UNPKG

@randajan/treelock

Version:

A minimal async lock with timeout support and parent-child queueing.

128 lines (125 loc) 3.51 kB
// src/static.js import { timeout } from "@randajan/sleep"; var _privates = /* @__PURE__ */ new WeakMap(); var bfn = () => { }; var Timeout = class extends Error { }; var toInt = (num, min, max, errorName) => { if (num == null) { return; } const tp = typeof num; if (tp !== "number") { throw new Error(`Expects '${errorName}' to be a 'number' but received '${tp}'`); } if (min != null && num < min) { throw new Error(`Expects '${errorName}' to be greater than '${min}' but received '${num}'`); } if (max != null && num > max) { throw new Error(`Expects '${errorName}' to be lesser than '${max}' but received '${num}'`); } return Math.round(num); }; var toTimeoutMs = (num) => toInt(num, 0, 2147483647, "timeout"); var withTimeout = (prom, tms) => { if (!tms) { return prom; } return Promise.race([prom, timeout(tms, new Timeout(`Execution time exceeded ${tms / 1e3}s`))]); }; var toFn = (any, errorName) => { if (any == null) { return; } const tp = typeof any; if (tp !== "function") { throw new Error(`Expects '${errorName}' to be a 'number' but received '${tp}'`); } return any; }; // src/TreeLock.js var TreeLock = class _TreeLock { constructor(options = {}) { const { name, ttl, on, sup } = options; const _pp = _privates.get(sup); const _p = { sup, queue: Promise.resolve(), ram: 0, enqueue: !_pp ? (_) => _p.queue : (_) => Promise.all([_p.queue, _pp.queue]), ttl: toTimeoutMs(ttl) ?? _pp?.ttl, on: toFn(on) || _pp?.on || bfn }; _p.append = (sub) => { if (_p.subs) { _p.subs.push(sub); } else { _p.subs = [sub]; const enq = _p.enqueue; _p.enqueue = (_) => Promise.all([enq(), ..._p.subs.map((b) => b.queue)]); } }; const finish = () => _p.ram = Math.max(0, _p.ram - 1); const done = (r) => { finish(); _p.on(this, "done"); return r; }; const crash = (err) => { finish(); const status = err instanceof Timeout ? "timeout" : "error"; _p.on(this, status, err); }; _p.run = (fn, tms, args) => { _p.ram++; if (this.ram + this.ramSup > 1) { _p.on(this, "enter"); } const exec = () => { _p.on(this, "start"); return withTimeout(fn(...args), tms).then(done); }; const next = _p.enqueue().then(exec); _p.queue = next.catch(crash); return next; }; const enumerable = true; Object.defineProperties(this, { name: { value: name }, sup: { value: sup }, subs: { get: (_) => [..._p.subs] }, ram: { enumerable, get: (_) => _p.ram }, ramSup: { enumerable, get: !_pp ? (_) => 0 : (_) => sup.ram + sup.ramSup }, ramSub: { enumerable, get: (_) => _p.subs.reduce((r, s) => r + s.ram + s.ramSub, 0) }, queue: { get: (_) => _p.queue } }); _privates.set(this, _p); if (_pp) { _pp.append(this); } } run(fn, ttl, ...args) { const _p = _privates.get(this); ttl = toTimeoutMs(ttl) ?? _p.ttl; return _p.run(fn, ttl, args); } wrap(fn, ttl) { const _p = _privates.get(this); ttl = toTimeoutMs(ttl) ?? _p.ttl; return (...args) => _p.run(fn, ttl, args); } sub(options = {}) { return new _TreeLock({ ...options, sup: this }); } }; // src/index.js var index_default = (options = {}) => new TreeLock(options); export { TreeLock, index_default as default }; //# sourceMappingURL=index.js.map