rsxjs
Version:
Resilience Extensions for JS.
1,373 lines (1,327 loc) • 35.6 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __reExport = (target, module2, desc) => {
if (module2 && typeof module2 === "object" || typeof module2 === "function") {
for (let key of __getOwnPropNames(module2))
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
}
return target;
};
var __toModule = (module2) => {
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
};
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
// src/index.ts
__export(exports, {
Breaker: () => breaker_exports,
Deferral: () => deferral_exports,
Fallback: () => fallback_exports,
MemoryStore: () => MemoryStore,
Mutex: () => mutex_exports,
Pool: () => pool_exports,
RedisStore: () => RedisStore,
Semaphore: () => semaphore_exports,
Timeout: () => timeout_exports,
WaitGroup: () => WaitGroup,
co: () => co,
makeChan: () => makeChan,
select: () => select
});
// src/breaker/index.ts
var breaker_exports = {};
__export(breaker_exports, {
fromAsync: () => fromAsync,
fromCallback: () => fromCallback,
fromSync: () => fromSync
});
// src/breaker/types.ts
var import_uuid = __toModule(require("uuid"));
var import_debug4 = __toModule(require("debug"));
// src/channel/channel.ts
var import_debug2 = __toModule(require("debug"));
var import_check_types = __toModule(require("check-types"));
// src/types.ts
function defer() {
const d = {};
d.promise = new Promise((resolve, reject) => {
d.resolve = resolve;
d.reject = reject;
});
return d;
}
var Referred = class {
_ref;
ref() {
this._ref = setInterval(() => {
}, 2 ** 31 - 1);
}
unref() {
if (this._ref)
clearTimeout(this._ref);
}
};
var Lock = class extends Referred {
requests = [];
};
// src/utils.ts
var utils_exports = {};
__export(utils_exports, {
any: () => any,
debounce: () => debounce,
delay: () => delay,
isDefined: () => isDefined
});
var import_debug = __toModule(require("debug"));
var debug = (0, import_debug.default)("rsxjs:utils");
function isDefined(v) {
return v !== void 0 && v !== null;
}
function debounce(fn, timeout) {
const state = {};
return function debouncedFn(...args) {
const since = Date.now() - (state.lastCall || 0);
debug(`debounced function called =>`, {
since,
timeout
});
if (!state.lastCall || since < timeout) {
state.lastCall = Date.now();
if (state.timer) {
clearTimeout(state.timer);
}
state.timer = setTimeout(() => debouncedFn.apply(this, args), timeout + 1);
return;
}
return fn.apply(this, args);
};
}
function delay(timeout) {
return new Promise((resolve) => setTimeout(() => resolve(), timeout));
}
function any(p) {
const d = defer();
for (let i = 0; i < p.length; ++i) {
p[i].then((v) => d.resolve([i, v]), d.reject);
}
return d.promise;
}
// src/channel/channel.ts
var debug2 = (0, import_debug2.default)("rsxjs:channel");
var _Channel = class {
constructor(config = {
bufferSize: 0
}) {
this.config = config;
_Channel.chanMap.set(this.selectSymbol, this);
import_check_types.default.assert.object(config);
import_check_types.default.assert.maybe.number(config.bufferSize);
}
isOpen = true;
buffer = [];
putters = [];
takers = [];
selectSymbol = Symbol();
[Symbol.toPrimitive]() {
return this.selectSymbol;
}
static getChannel(sym) {
const chan5 = _Channel.chanMap.get(sym);
if (!chan5) {
throw new Error(`Failed to fetch closed channel`);
}
return chan5;
}
close() {
this.isOpen = false;
_Channel.chanMap.delete(this.selectSymbol);
}
async _put(dir, value, timeout) {
if (!this.isOpen) {
debug2(`refusing to put on a closed channel`);
return { ok: false };
}
const nextTaker = dir === "left" ? this.takers.pop() : this.takers.shift();
if (nextTaker) {
debug2(`found pending taker, forwarding value`);
nextTaker.resolve(value);
return { ok: true };
}
if (this.buffer.length === this.config.bufferSize) {
const signal = defer();
const putter = {
signal,
value
};
this.putters.push(putter);
debug2(`added background putter, waiting for signal`);
if (isDefined(timeout)) {
const [i] = await any([signal.promise, delay(timeout)]);
if (i === 0) {
debug2(`background signal received for putter, value was read`);
} else {
debug2(`background putter timed out`);
this.putters = this.putters.filter((putterInList) => {
return putterInList !== putter;
});
}
return {
ok: i === 0
};
}
await signal.promise;
return { ok: true };
}
if (dir === "right") {
debug2(`putter wrote value to the right of the buffer`);
this.buffer.push(value);
} else {
debug2(`putter wrote value to the left of the buffer`);
this.buffer.unshift(value);
}
return { ok: true };
}
put(value, timeout) {
return this._put("right", value, timeout);
}
lput(value, timeout) {
return this._put("left", value, timeout);
}
async _take(dir, timeout) {
const { value, ok } = dir === "left" ? this.select() : this.rselect();
if (ok) {
return { value, ok };
}
if (!this.isOpen) {
debug2(`channel is closed & empty, refusing to take`);
return { ok: false };
}
if (this.buffer.length === 0) {
const nextPutter = dir === "left" ? this.putters.shift() : this.putters.pop();
if (nextPutter) {
nextPutter.signal.resolve();
debug2(`taker found an empty buffer & a background putter, stealing value`);
return {
ok: true,
value: nextPutter.value
};
}
const p = defer();
this.takers.push(p);
debug2(`adding taker to background list`);
if (isDefined(timeout)) {
const [i, value3] = await any([p.promise, delay(timeout)]);
if (i === 0) {
debug2(`received value from background taker`);
return {
ok: true,
value: value3
};
}
debug2(`failed to received value from background taker, timed out`);
this.takers = this.takers.filter((takerFromList) => {
return takerFromList !== p;
});
return {
ok: false
};
}
const value2 = await p.promise;
debug2(`received value from background taker`);
return {
ok: true,
value: value2
};
}
const poppedValue = dir === "left" ? this.buffer.shift() : this.buffer.pop();
debug2(`read value from static buffer`);
return {
ok: !!poppedValue,
value: poppedValue
};
}
take(timeout) {
return this._take("left", timeout);
}
rtake(timeout) {
return this._take("right", timeout);
}
_select(dir) {
if (this.buffer.length > 0) {
debug2(`selected value from %s of buffer`, dir);
return {
value: dir === "left" ? this.buffer.shift() : this.buffer.pop(),
ok: true
};
}
const nextPutter = dir === "left" ? this.putters.shift() : this.putters.pop();
if (nextPutter) {
debug2(`selected value from background putter`);
nextPutter.signal.resolve();
return {
value: nextPutter.value,
ok: true
};
}
debug2(`failed to select a value`);
return {
ok: false
};
}
select() {
return this._select("left");
}
rselect() {
return this._select("right");
}
async *[Symbol.asyncIterator]() {
while (this.isOpen) {
const { value } = await this.take();
yield value;
}
while (true) {
const { value, ok } = this.select();
if (ok) {
yield value;
} else {
break;
}
}
}
readOnlyChan;
readOnly() {
return this.readOnlyChan = this.readOnlyChan || new ReadOnlyChannel(this);
}
writeOnlyChan;
writeOnly() {
return this.writeOnlyChan = this.writeOnlyChan || new WriteOnlyChannel(this);
}
};
var Channel = _Channel;
__publicField(Channel, "chanMap", new Map());
var ReadOnlyChannel = class {
constructor(chan5) {
this.chan = chan5;
}
take() {
return this.chan.take();
}
select() {
return this.chan.select();
}
[Symbol.asyncIterator]() {
return this.chan[Symbol.asyncIterator]();
}
[Symbol.toPrimitive]() {
return this.chan[Symbol.toPrimitive]();
}
};
var WriteOnlyChannel = class {
constructor(chan5) {
this.chan = chan5;
}
put(value, timeout) {
return this.chan.put(value, timeout);
}
};
function makeChan(opts) {
return new Channel(opts);
}
// src/channel/select.ts
function select(cases) {
const defaultCase = cases._;
const syms = Object.getOwnPropertySymbols(cases);
if (syms.length === 0) {
throw new Error("No channels given to select");
}
const chans = [];
for (const sym of syms) {
chans.push(Channel.getChannel(sym));
}
function poll() {
for (const chan5 of chans) {
const { value, ok } = chan5.select();
if (ok) {
const sym = chan5[Symbol.toPrimitive]();
return cases[sym](value);
}
}
if (defaultCase) {
return defaultCase();
}
return new Promise((resolve) => setTimeout(() => resolve(poll()), 0));
}
return poll();
}
// src/store/memory.ts
var map;
var MemoryStore = class {
map = map = map || new Map();
async incr(key) {
this.map.set(key, (this.map.get(key) | 0) + 1);
}
async decr(key) {
this.map.set(key, (this.map.get(key) | 0) - 1);
}
async get(key) {
return this.map.get(key);
}
async set(key, value, options) {
if (options) {
if (options.notExists && this.map.has(key)) {
return;
}
if (options.expires !== void 0) {
setTimeout(() => this.map.delete(key), options.expires);
}
}
this.map.set(key, value);
}
async del(key) {
this.map.delete(key);
}
async hget(namespace, key, defaultValue) {
const map2 = this.map.get(namespace);
if (!map2) {
return defaultValue;
}
return map2.get(key) || defaultValue;
}
async hset(namespace, key, value) {
const map2 = this.map.get(namespace) || new Map();
this.map.set(namespace, map2);
map2.set(key, value);
}
async hincr(namespace, key) {
return this.hset(namespace, key, 1 + await this.hget(namespace, key, 0));
}
async hdecr(namespace, key) {
return this.hset(namespace, key, -1 + await this.hget(namespace, key, 0));
}
async rpush(listName, value) {
if (!this.map.has(listName)) {
this.map.set(listName, makeChan({
bufferSize: 1e9
}));
}
const q = this.map.get(listName);
await q.put(value);
return this.map.size;
}
async lpush(listName, value) {
if (!this.map.has(listName)) {
this.map.set(listName, makeChan({
bufferSize: 1e9
}));
}
const q = this.map.get(listName);
await q.lput(value);
return this.map.size;
}
async rpop(listName) {
const q = this.map.get(listName);
if (q) {
return q.rselect().value;
}
}
async lpop(listName) {
const q = this.map.get(listName);
if (q) {
return q.select().value;
}
}
async brpop(listName, timeout) {
const q = this.map.get(listName);
if (q) {
return (await q.rtake(timeout)).value;
}
}
async blpop(listName, timeout) {
const q = this.map.get(listName);
if (q) {
return (await q.take(timeout)).value;
}
}
};
// src/store/redis.ts
var import_debug3 = __toModule(require("debug"));
var debug3 = (0, import_debug3.default)("rsxjs");
function parse(text) {
if (text) {
return JSON.parse(text);
}
}
var RedisStore = class {
redis;
constructor(options) {
this.redis = new (require("ioredis"))(options);
}
async get(key) {
return parse(await this.redis.get(key));
}
async set(key, value, options) {
if (options) {
if (options.notExists && options.expires) {
await this.redis.set(key, JSON.stringify(value), "NX", "EX", Math.floor(options.expires / 1e3));
return;
}
if (options.notExists) {
await this.redis.set(key, JSON.stringify(value), "NX");
return;
}
if (options.expires) {
await this.redis.set(key, JSON.stringify(value), "EX", Math.floor(options.expires / 1e3));
return;
}
}
await this.redis.set(key, JSON.stringify(value));
}
async incr(key) {
await this.redis.incr(key);
}
async decr(key) {
await this.redis.decr(key);
}
async hincr(namespace, key) {
await this.redis.hincrby(namespace, key, 1);
}
async hdecr(namespace, key) {
await this.redis.hincrby(namespace, key, -1);
}
async del(key) {
await this.redis.del(key);
}
async hset(namespace, key, value) {
await this.redis.hset(namespace, key, JSON.stringify(value));
}
async hget(namespace, key, defaultValue) {
try {
return parse(await this.redis.hget(namespace, key)) || defaultValue;
} catch (err) {
debug3(`failed to hget ${namespace} ${key} => ${err}`);
return defaultValue;
}
}
rpush(listName, value) {
return this.redis.rpush(listName, JSON.parse(value));
}
lpush(listName, value) {
return this.redis.lpush(listName, JSON.parse(value));
}
async rpop(listName) {
return JSON.parse(await this.redis.rpop(listName));
}
async lpop(listName) {
return JSON.parse(await this.redis.lpop(listName));
}
async brpop(listName, timeout) {
const [_, data] = await this.redis.brpop(listName, String(timeout));
return JSON.parse(data);
}
async blpop(listName, timeout) {
const [_, data] = await this.redis.blpop(listName, String(timeout));
return JSON.parse(data);
}
};
// src/store/state.ts
var State = class {
store;
namespace;
defaults;
constructor({
store,
namespace,
defaults: defaults5
}) {
if (!store) {
throw new Error(`Store is required to create a state`);
}
if (!namespace) {
throw new Error(`Namespace is required to create a new state`);
}
if (!defaults5) {
throw new Error(`Defaults are required to create a new state`);
}
this.store = store;
this.namespace = namespace;
this.defaults = defaults5;
}
get(key) {
return this.store.hget(this.namespace, key, this.defaults[key]);
}
set(key, value) {
return this.store.hset(this.namespace, key, value);
}
incr(key) {
return this.store.hincr(this.namespace, key);
}
decr(key) {
return this.store.hdecr(this.namespace, key);
}
reset() {
return this.store.del(this.namespace);
}
async dump() {
const state = {};
const keys = Object.keys(this.defaults);
for (const key of keys) {
state[key] = await this.get(key);
}
return state;
}
};
// src/breaker/types.ts
var debug4 = (0, import_debug4.default)("rsxjs");
function defaults(config) {
const updatedConfig = Object.assign({}, DefaultBreakerOptions);
if (!config) {
return updatedConfig;
}
if (config.maxErrors !== void 0) {
const { maxErrors } = config;
if (typeof maxErrors !== "number" || maxErrors < -1 || !isFinite(maxErrors) || isNaN(maxErrors)) {
throw new TypeError(`Unexpected value of type ${typeof maxErrors} given to 'maxErrors'`);
}
updatedConfig.maxErrors = maxErrors;
}
if (config.timeout !== void 0) {
const { timeout } = config;
if (typeof timeout !== "number" || timeout < -1 || !isFinite(timeout) || isNaN(timeout)) {
throw new TypeError(`Unexpected value of type ${typeof timeout} given to 'timeout'`);
}
updatedConfig.timeout = timeout;
}
if (config.store) {
updatedConfig.store = config.store;
}
if (config.name) {
updatedConfig.name = config.name;
}
return updatedConfig;
}
var DefaultBreakerOptions = {
maxErrors: 10,
timeout: 10 * 1e3,
store: new MemoryStore()
};
var BreakerState;
(function(BreakerState2) {
BreakerState2[BreakerState2["OPEN"] = 0] = "OPEN";
BreakerState2[BreakerState2["CLOSED"] = 1] = "CLOSED";
BreakerState2[BreakerState2["HALFOPEN"] = 2] = "HALFOPEN";
})(BreakerState || (BreakerState = {}));
var CircuitBreaker = class {
constructor(options) {
this.options = options;
this.namespace = `rsxjs:breaker:${options.name || (0, import_uuid.v4)()}`;
this.state = new State({
store: options.store,
namespace: this.namespace,
defaults: {
numErrors: 0,
lastError: "Unknown error",
lastErrorTime: 0
}
});
}
namespace;
state;
lastState;
lastStateTime;
lastStateIsFresh() {
return this.lastState !== void 0 && this.lastStateTime !== void 0 && Date.now() - this.lastStateTime < this.options.timeout;
}
async getFreshState() {
const numErrors = await this.state.get("numErrors");
const lastErrorTime = await this.state.get("lastErrorTime");
if (debug4.enabled) {
debug4(`breaker(${this.namespace}) =>`, { numErrors, lastErrorTime });
debug4(`breaker(${this.namespace}) => state dump`, await this.state.dump());
}
const state = getBreakerState({
numErrors,
lastErrorTime,
options: this.options
});
const stateObject = {
numErrors,
lastErrorTime,
state
};
if (state === 1) {
this.lastStateTime = Date.now();
this.lastState = stateObject;
}
return stateObject;
}
async getState() {
if (this.lastStateIsFresh() && this.lastState) {
return this.lastState;
}
return this.getFreshState();
}
async shouldAllowRequest() {
return (await this.getState()).state !== 1;
}
async attempt(fn) {
const { numErrors, state } = await this.getState();
switch (state) {
case 2:
case 0: {
try {
debug4(`breaker(${this.namespace}) => passing through`);
const result = await fn();
debug4(`breaker(${this.namespace}) => resetting state: %s`);
if (numErrors > 0) {
await this.state.reset();
}
return result;
} catch (err) {
await this.state.incr("numErrors");
await this.state.set("lastError", err.message || String(err));
await this.state.set("lastErrorTime", Date.now());
throw err;
}
}
default: {
throw new Error(await this.state.get("lastError"));
}
}
}
};
function getBreakerState({
numErrors,
lastErrorTime,
options
}) {
if (numErrors >= options.maxErrors) {
const elapsed = Date.now() - lastErrorTime;
if (elapsed >= options.timeout) {
return 2;
}
return 1;
}
return 0;
}
// src/breaker/from-sync.ts
function fromSync(originalFn, _options) {
const options = defaults(_options);
const state = {
numErrors: 0,
lastError: "Unknown error",
lastErrorTime: 0
};
if (!(options.store instanceof MemoryStore)) {
throw new Error(`Synchronous breakers cannot be distributed. Use an async breaker.`);
}
return function syncBreaker(...args) {
if (state.numErrors >= options.maxErrors && Date.now() - state.lastErrorTime < options.timeout) {
throw new Error(state.lastError);
}
try {
const result = originalFn.apply(this, args);
state.numErrors = 0;
state.lastErrorTime = 0;
return result;
} catch (err) {
++state.numErrors;
state.lastError = String(err.message || err).split("\n")[0];
state.lastErrorTime = Date.now();
throw new Error(state.lastError);
}
};
}
// src/breaker/from-async.ts
function fromAsync(originalFn, _options) {
const options = defaults(_options);
const breaker = new CircuitBreaker(options);
return Object.assign(function asyncBreaker(...args) {
return breaker.attempt(() => originalFn.apply(this, args));
}, {
shouldAllowRequest: () => breaker.shouldAllowRequest()
});
}
// src/breaker/from-callback.ts
function fromCallback(originalFn, _options) {
const options = defaults(_options);
const breaker = new CircuitBreaker(options);
return function callbackBreaker(...args) {
const callback = args.pop();
breaker.attempt(() => new Promise((resolve, reject) => {
originalFn.call(this, ...args, async function(err, result) {
if (err)
reject(err);
else
resolve(result);
});
})).then((result) => callback(null, result), (err) => callback(err));
};
}
// src/mutex/index.ts
var mutex_exports = {};
__export(mutex_exports, {
fromAsync: () => fromAsync2,
fromGenerator: () => fromGenerator,
lock: () => lock
});
// src/mutex/mutex.ts
var import_uuid2 = __toModule(require("uuid"));
// src/errors.ts
var COULD_NOT_LOCK = "Unable to obtain lock";
var TIMEOUT = "Operation timed out";
// src/mutex/mutex.ts
var TIME_BETWEEN_SPINS = 10;
var Mutex = class extends Lock {
name;
failFast;
timeout;
store;
constructor(options = {}) {
super();
this.name = options.name || (0, import_uuid2.v4)();
this.timeout = options.timeout;
this.failFast = options.failFast === true;
this.store = options.store || new MemoryStore();
}
async _tryLock() {
this.ref();
const lockId = (0, import_uuid2.v4)();
await this.store.set(this.name, lockId, {
notExists: true,
expires: this.timeout || 10 * 1e3
});
if (lockId === await this.store.get(this.name)) {
return () => {
return this.store.del(this.name).then((val) => {
this.unref();
return val;
}, () => this.unref());
};
}
if (this.failFast) {
throw new Error(COULD_NOT_LOCK);
}
}
async lock() {
const start = Date.now();
const timeout = this.timeout || Infinity;
while (Date.now() - start < timeout) {
const unlock = await this._tryLock();
if (unlock) {
return unlock;
}
await delay(TIME_BETWEEN_SPINS);
}
throw new Error(TIMEOUT);
}
};
// src/mutex/from-async.ts
function fromAsync2(fn, options) {
const m = new Mutex(options);
return async function mutexWrappedFunction(...args) {
const unlock = await m.lock();
try {
const result = await fn.apply(this, args);
unlock();
return result;
} catch (err) {
unlock();
throw err;
}
};
}
// src/deferral/types.ts
var import_debug5 = __toModule(require("debug"));
var debug5 = (0, import_debug5.default)("rsxjs");
var DeferredOperation = class {
deferred = [];
defer(cleanup) {
debug5(`registering deferred operation:`, cleanup);
this.deferred.push(cleanup);
}
async cleanup() {
for (const cleanup of this.deferred) {
debug5(`executing deferred operation:`, cleanup);
await cleanup();
}
}
};
// src/deferral/from-async.ts
var import_debug6 = __toModule(require("debug"));
var debug6 = (0, import_debug6.default)("rsxjs");
function fromAsync3(fn) {
async function deferralWrapper(...args) {
const deferral = new DeferredOperation();
try {
const retval = await fn.call(this, deferral.defer.bind(deferral), ...args);
debug6(`unwinding deferred operations`);
await deferral.cleanup();
return retval;
} catch (err) {
debug6(`unwinding deferred operations (failure)`);
Error.captureStackTrace(err, deferralWrapper);
await deferral.cleanup();
throw err;
}
}
return deferralWrapper;
}
// src/coroutine/co.ts
function wrap(fn) {
function go(...args) {
const state = {
isCanceled: false,
isDone: null,
onCancel: []
};
const ctx = this;
const p = fromAsync3(async function routine(defer2) {
const it = fn.call(ctx, defer2, ...args);
if (!it.throw) {
throw new Error(`Can only wrap a generator into a coroutine`);
}
let lastValue;
let lastError;
while (!state.isCanceled) {
const { done, value } = lastError ? it.throw(lastError) : it.next(lastValue);
try {
lastValue = await value;
lastError = void 0;
} catch (err) {
lastValue = void 0;
lastError = err;
}
if (done) {
state.isDone = true;
return lastValue;
}
}
state.isDone = false;
for (const resolve of state.onCancel) {
resolve(state.isDone);
}
})();
p.cancel = function() {
state.isCanceled = true;
return new Promise((resolve) => {
if (state.isDone !== null) {
return resolve(state.isDone);
}
state.onCancel.push(resolve);
});
};
return p;
}
return go;
}
function runRoutine(fn) {
return wrap(fn)();
}
var co = Object.assign(runRoutine, { wrap });
// src/mutex/from-generator.ts
function fromGenerator(fn, options) {
const m = new Mutex(options);
return async function mutexWrappedFunction(...args) {
const unlock = await m.lock();
try {
return await co(() => fn.apply(this, args));
} finally {
unlock();
}
};
}
// src/mutex/lock.ts
async function lock(options) {
return {
unlock: await new Mutex(options).lock()
};
}
// src/pool/index.ts
var pool_exports = {};
__export(pool_exports, {
fromAsyncIterable: () => fromAsyncIterable,
fromAsyncIterator: () => fromAsyncIterator,
fromIterable: () => fromIterable,
fromIterator: () => fromIterator
});
// src/pool/from-it.ts
var import_assert = __toModule(require("assert"));
// src/pool/types.ts
function defaults2(_) {
const config = {};
return config;
}
function isWorkerAvailable(worker) {
return worker && typeof worker.isAvailable === "function" && worker.isAvailable();
}
var GenericPool = class {
head;
tail;
addWorker(worker) {
if (!isWorkerAvailable(worker)) {
throw new Error(`Worker was given, but is not available.`);
}
if (!this.tail) {
this.head = this.tail = {
value: worker
};
return;
}
const node = {
prev: this.tail,
value: worker
};
this.tail.next = node;
this.tail = node;
}
getExistingWorker() {
for (let node = this.head; node; node = node.next) {
if (isWorkerAvailable(node.value)) {
return node.value;
}
}
}
};
var SyncPool = class extends GenericPool {
constructor(it, options) {
super();
this.it = it;
this.options = options;
}
getWorker() {
const worker = this.getExistingWorker();
if (worker)
return worker;
const result = this.it.next();
if (result.done) {
throw new Error("Pool has died - iterator is out of results");
}
this.addWorker(result.value);
return result.value;
}
};
var AsyncPool = class extends GenericPool {
constructor(it, options) {
super();
this.it = it;
this.options = options;
}
async getWorker() {
const worker = this.getExistingWorker();
if (worker)
return worker;
const result = await this.it.next();
if (result.done) {
throw new Error("Pool has died - iterator is out of results");
}
this.addWorker(result.value);
return result.value;
}
};
// src/pool/from-it.ts
function fromIterable(iterable, options) {
(0, import_assert.ok)(!iterable[Symbol.iterator], "Must provide a valid iterable");
const it = iterable[Symbol.iterator]();
return fromIterator(it, options);
}
function fromIterator(it, _options) {
(0, import_assert.ok)(it && typeof it.next === "function", "Must provide a valid iterator");
return new SyncPool(it, defaults2(_options));
}
// src/pool/from-async-it.ts
var import_assert2 = __toModule(require("assert"));
function fromAsyncIterable(iterable, options) {
(0, import_assert2.ok)(!iterable[Symbol.asyncIterator], "Must provide a valid iterable");
const it = iterable[Symbol.asyncIterator]();
return fromAsyncIterator(it, options);
}
function fromAsyncIterator(it, _options) {
(0, import_assert2.ok)(it && typeof it.next === "function", "Must provide a valid iterator");
return new AsyncPool(it, defaults2(_options));
}
// src/semaphore/index.ts
var semaphore_exports = {};
__export(semaphore_exports, {
fromAsync: () => fromAsync4
});
// src/semaphore/semaphore.ts
var import_uuid3 = __toModule(require("uuid"));
var Semaphore = class extends Lock {
size;
mutex;
state;
constructor(options) {
super();
const namespace = `rsxjs:semaphore(${options.name || (0, import_uuid3.v4)()})`;
this.size = options.size;
this.mutex = new Mutex({
name: `${namespace}:mux`,
store: options.store
});
this.state = new State({
namespace,
store: options.store,
defaults: {
tokensOut: 0
}
});
}
async lock(failWithoutLock = false) {
const unlock = async () => {
const req = this.requests.shift();
if (req) {
req.resolve(unlock);
return;
}
await this.state.decr("tokensOut");
};
const releaseMux = await this.mutex.lock();
if (await this.state.get("tokensOut") < this.size) {
await this.state.incr("tokensOut");
releaseMux();
return unlock;
}
releaseMux();
if (failWithoutLock) {
throw new Error(COULD_NOT_LOCK);
}
const d = defer();
this.requests.push({
...d,
unlock
});
return d.promise;
}
};
// src/semaphore/types.ts
var DefaultSemaphoreOptions = {
size: 0,
failFast: false,
store: new MemoryStore()
};
function defaults3(options) {
const config = DefaultSemaphoreOptions;
if (options) {
if (options.failFast !== void 0) {
config.failFast = !!options.failFast;
}
if (options.size !== void 0) {
config.size = options.size;
}
if (options.store) {
config.store = options.store;
}
if (options.name) {
config.name = options.name;
}
}
if (typeof config.size !== "number" || config.size < 1 || Math.round(config.size) !== config.size) {
throw new Error(`Invalid size given for semaphore: ${config.size}`);
}
return config;
}
// src/semaphore/from-async.ts
function fromAsync4(fn, _options) {
const options = defaults3(_options);
const s = new Semaphore(options);
return async function mutexWrappedFunction(...args) {
const unlock = await s.lock(options.failFast);
try {
const result = await fn.apply(this, args);
unlock();
return result;
} catch (err) {
unlock();
throw err;
}
};
}
// src/timeout/index.ts
var timeout_exports = {};
__export(timeout_exports, {
after: () => after,
factory: () => factory,
fromAsync: () => fromAsync5,
fromPromise: () => fromPromise
});
// src/timeout/types.ts
var DefaultTimeoutOptions = {
timeout: 1e3
};
function defaults4(options) {
const config = DefaultTimeoutOptions;
if (options) {
if (options.timeout !== void 0) {
if (typeof options.timeout !== "number" || options.timeout < 0) {
throw new Error(`Invalid value given for timeout: ${options.timeout}`);
}
config.timeout = options.timeout;
} else {
throw new Error("No timeout option provided! It is required.");
}
}
return config;
}
// src/timeout/from-promise.ts
function fromPromise(promise, _options) {
const options = defaults4(_options);
return new Promise(async (resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error(TIMEOUT));
}, options.timeout);
try {
const v = await promise;
clearTimeout(timeout);
resolve(v);
} catch (err) {
clearTimeout(timeout);
reject(err);
}
});
}
// src/timeout/from-async.ts
function fromAsync5(cb, options) {
return function asyncTimeout(...args) {
return fromPromise(cb.apply(this, args), options);
};
}
function factory(options) {
return function fromFactory(fn) {
return fromAsync5(fn, options);
};
}
// src/timeout/after.ts
function after(timeout) {
const quit = makeChan({ bufferSize: 1 });
setTimeout(() => {
quit.put(void 0);
quit.close();
}, timeout);
return quit;
}
// src/fallback/index.ts
var fallback_exports = {};
__export(fallback_exports, {
fromAsync: () => fromAsync6
});
// src/fallback/from-async.ts
function fromAsync6(fn, fallback) {
return async function fallbackFn(...args) {
try {
const ret = await fn.apply(this, args);
return ret;
} catch (err) {
const ret = await fallback.apply(this, args);
if (typeof ret === "object") {
return Object.assign({
isFallback: true,
error: err
}, ret);
}
return ret;
}
};
}
// src/deferral/index.ts
var deferral_exports = {};
__export(deferral_exports, {
fromAsync: () => fromAsync3,
fromGenerator: () => fromGenerator2
});
// src/deferral/from-generator.ts
function fromGenerator2(fn) {
return co.wrap(fn);
}
// src/waitgroup/waitgroup.ts
var WaitGroup = class {
numTasks = 0;
waiters = [];
add(ctr) {
if (typeof ctr === "number" || ctr === void 0 || ctr === null) {
this.numTasks += ctr;
return;
}
if (typeof ctr === "function") {
return this.add(ctr());
}
if ("then" in ctr && typeof ctr.then === "function") {
this.numTasks++;
ctr.then(() => {
this.done();
}, (err) => {
this.done(err);
});
return;
}
throw new Error(`Unexpected value given to add: ${typeof ctr}`);
}
done(err) {
this.add(-1);
if (this.numTasks < 0) {
throw new Error(`WaitGroup.done() called too many times`);
}
if (this.numTasks < 1) {
while (this.waiters.length > 0) {
const waiter = this.waiters.pop();
if (waiter) {
if (err) {
waiter.reject(err);
} else {
waiter.resolve();
}
}
}
}
}
wait(t) {
if (this.numTasks < 1) {
return Promise.resolve();
}
const d = defer();
this.waiters.push(d);
if (t) {
const timer = setTimeout(() => {
const i = this.waiters.indexOf(d);
this.waiters.splice(i, 1);
}, t);
d.promise.then(() => clearTimeout(timer));
}
return d.promise;
}
};
// src/index.ts
if (process.env.NODE_ENV === "test") {
Object.assign(exports, {
...utils_exports
});
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Breaker,
Deferral,
Fallback,
MemoryStore,
Mutex,
Pool,
RedisStore,
Semaphore,
Timeout,
WaitGroup,
co,
makeChan,
select
});