UNPKG

@randajan/treelock

Version:

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

150 lines (145 loc) 4.44 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.js var index_exports = {}; __export(index_exports, { TreeLock: () => TreeLock, default: () => index_default }); module.exports = __toCommonJS(index_exports); // src/static.js var import_sleep = require("@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, (0, import_sleep.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); //# sourceMappingURL=index.js.map