@randajan/pulse 1.1.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/Pulse.js", "../../src/const.js", "../../src/tools.js", "../../src/metadata.js", "../../src/static.js", "../../src/index.js"],
4
- "sourcesContent": ["import { solid, solids, virtuals } from \"@randajan/props\";\r\nimport { blankFn, _pulses } from \"./const\";\r\nimport { valid, validRange } from \"./tools\";\r\nimport { createMetaData } from \"./metadata\";\r\n\r\n/**\r\n * Periodic scheduler that aligns each callback to exact multiples\r\n * of the given interval (optionally shifted by `offset`).\r\n */\r\nexport class Pulse {\r\n\r\n /**\r\n * Create a new Pulse instance.\r\n * @param {PulseOptions} [options={}] See {@link PulseOptions}.\r\n */\r\n constructor(options = {}) {\r\n const _p = {\r\n state: false,\r\n nextId:0\r\n }\r\n\r\n const getNow = valid(\"function\", options.getNow, false, \"options.getNow\") || (_ => Date.now());\r\n\r\n const interval = validRange(10, 2_147_483_647, options.interval, true, \"options.interval\");\r\n const offset = (validRange(0, interval, options.offset, false, \"options.offset\") || 0);\r\n\r\n const onPulse = valid(\"function\", options.onPulse, true, \"options.onPulse\");\r\n _p.onStart = valid(\"function\", options.onStart, false, \"options.onStart\") || blankFn;\r\n _p.onStop = valid(\"function\", options.onStop, false, \"options.onStop\") || blankFn;\r\n \r\n const onError = valid(\"function\", options.onError, false, \"options.onError\") || blankFn;\r\n \r\n const afterPulse = valid(\"function\", options.afterPulse, false, \"options.afterPulse\") || blankFn;\r\n const autoStart = valid(\"boolean\", options.autoStart, false, \"options.autoStart\") || false;\r\n const noMeta = valid(\"boolean\", options.noMeta, false, \"option.noMeta\") || false;\r\n\r\n _p.reset = _=>{\r\n _p.nextId = 0;\r\n }\r\n\r\n _p.plan = _ => {\r\n if (!_p.state || _p.metaData != null) { return; }\r\n _p.timeoutId = setTimeout(_p.run, this.countdown);\r\n }\r\n\r\n _p.run = async _ => {\r\n if (!_p.state || _p.metaData) { return; }\r\n\r\n const id = _p.nextId++;\r\n const md = _p.metaData = noMeta ? id : createMetaData(id, getNow);\r\n let errA, errB, result;\r\n\r\n try {\r\n result = await onPulse(this, md);\r\n if (!noMeta) { solid(md, \"result\", result); }\r\n }\r\n catch (err) {\r\n errA = err;\r\n if (!noMeta) { solid(md, \"error\", errA); }\r\n }\r\n\r\n if (!noMeta) { solid(md, \"ended\", getNow()); }\r\n\r\n if (errA != null) {\r\n if (noMeta) { onError(this, errA, md); }\r\n else { onError(this, md); }\r\n }\r\n\r\n try { await afterPulse(this, md); }\r\n catch(err) { errB = err; }\r\n\r\n _p.last = noMeta ? result : md;\r\n delete _p.metaData;\r\n\r\n if (errB != null) {\r\n _p.state = false;\r\n throw errB;\r\n }\r\n \r\n _p.plan();\r\n }\r\n\r\n virtuals(this, {\r\n state:_=>_p.state,\r\n last:_=>_p.last,\r\n countdown:_=>((interval - getNow() % interval) + offset)\r\n });\r\n\r\n solids(this, {\r\n interval,\r\n offset\r\n });\r\n\r\n _pulses.set(this, _p);\r\n\r\n if (autoStart) { this.start(); }\r\n }\r\n\r\n /**\r\n * Start the pulse loop (no-op if already running).\r\n * @returns {boolean} `true` if it started now, `false` if it was already running.\r\n */\r\n start(reset=false) {\r\n const { state } = this;\r\n if (state) { return false; }\r\n const _p = _pulses.get(this);\r\n _p.onStart(this);\r\n if (reset) { _p.reset(); }\r\n _p.state = true;\r\n _p.plan();\r\n return true;\r\n }\r\n\r\n /**\r\n * Stop the pulse loop (no-op if already stopped).\r\n * @returns {boolean} `true` if it stopped now, `false` if it was already stopped.\r\n */\r\n stop(reset=false) {\r\n const { state } = this;\r\n if (!state) { return false; }\r\n const _p = _pulses.get(this);\r\n clearTimeout(_p.timeoutId);\r\n _p.state = false;\r\n if (reset) { _p.reset(); }\r\n _p.onStop(this);\r\n return true;\r\n }\r\n\r\n reset() {\r\n _pulses.get(this).reset();\r\n return true;\r\n }\r\n\r\n restart(reset=false) {\r\n this.stop(reset);\r\n return this.start(reset);\r\n }\r\n\r\n\r\n}", "\r\n\r\n\r\nexport const _pulses = new Map();\r\n\r\nexport const blankFn = ()=>{};", "\r\n\r\nexport const is = (type, any)=>typeof any === type;\r\n\r\nexport const valid = (type, any, req=false, msg=\"argument\")=>{\r\n if (any == null) {\r\n if (!req) { return; }\r\n throw new Error(`${msg} require typeof '${type}'`);\r\n }\r\n if (is(type, any)) { return any; }\r\n throw new Error(`${msg} is not typeof '${type}'`);\r\n}\r\n\r\nexport const validRange = (min, max, any, req=false, msg=\"argument\")=>{\r\n const num = valid(\"number\", any, req, msg);\r\n if (num == null) { return; }\r\n if (num < min) { throw new Error(`${msg} must be greater than ${min}`); }\r\n if (num > max) { throw new Error(`${msg} must be less than ${max}`); }\r\n return num;\r\n}", "import { solid, virtual } from \"@randajan/props\";\r\n\r\n\r\n/**\r\n * Metadata object passed to every pulse callback.\r\n * @typedef {Object} PulseMeta\r\n * @property {Date} started Timestamp when this pulse began.\r\n * @property {string[]} warnings Snapshot of all warnings collected so far.\r\n * @property {number} runtime Milliseconds elapsed since `started`.\r\n * @property {(warning: string|Error) => void} warn Push a warning into the internal list.\r\n */\r\nexport const createMetaData = (id, getNow) => {\r\n const p = {};\r\n const w = [];\r\n solid(p, \"id\", id);\r\n solid(p, \"started\", getNow());\r\n virtual(p, \"warnings\", _ => [...w]);\r\n virtual(p, \"runtime\", _ => (p.ended ? p.ended : getNow()) - p.started);\r\n solid(p, \"warn\", warning => w.push(warning), false);\r\n return p;\r\n}\r\n\r\n/**\r\n * Configuration object for {@link Pulse}.\r\n * @typedef {Object} PulseOptions\r\n * @property {(meta: PulseMeta) => (void|Promise<void>)} onPulse **required** \u2013 user callback invoked on every pulse.\r\n * @property {number} interval **required** \u2013 period length in ms (50 \u2013 2 147 483 647).\r\n * @property {number} [offset=0] Shift in ms applied to the start of each interval (0 \u2264 offset < interval).\r\n * @property {() => number} [getNow=() => Date.now()] Custom time source, useful for tests or time travel.\r\n * @property {(err: unknown) => void} [onError=blankFn] Error handler for exceptions or rejected `onPulse` promises.\r\n * @property {boolean} [autoStart=false] If true, the loop starts immediately after construction.\r\n */", "import { _pulses } from \"./const\";\r\nimport { Pulse } from \"./Pulse.js\";\r\n\r\n\r\n/**\r\n * Factory helper \u2013 simply calls `new Pulse(options)`.\r\n * @param {PulseOptions} [options={}] See {@link PulseOptions}.\r\n * @returns {Pulse}\r\n */\r\nexport const createPulse = (options = {}) => new Pulse(options);\r\n\r\n/**\r\n * Process exit helper \u2013 stops all pending pulses.\r\n */\r\n\r\nexport const stopAllPulses = ()=>{\r\n for (const [pulse, _pulse] of _pulses.entries()) {\r\n pulse.stop();\r\n }\r\n}\r\n", "import { Pulse } from \"./Pulse\";\nimport { createPulse, stopAllPulses } from \"./static\";\n\nexport default createPulse;\n\nexport { createPulse, stopAllPulses, Pulse }\n"],
5
- "mappings": ";AAAA,SAAS,SAAAA,QAAO,QAAQ,gBAAgB;;;ACGjC,IAAM,UAAU,oBAAI,IAAI;AAExB,IAAM,UAAU,MAAI;AAAC;;;ACHrB,IAAM,KAAK,CAAC,MAAM,QAAM,OAAO,QAAQ;AAEvC,IAAM,QAAQ,CAAC,MAAM,KAAK,MAAI,OAAO,MAAI,eAAa;AACzD,MAAI,OAAO,MAAM;AACb,QAAI,CAAC,KAAK;AAAE;AAAA,IAAQ;AACpB,UAAM,IAAI,MAAM,GAAG,GAAG,oBAAoB,IAAI,GAAG;AAAA,EACrD;AACA,MAAI,GAAG,MAAM,GAAG,GAAG;AAAE,WAAO;AAAA,EAAK;AACjC,QAAM,IAAI,MAAM,GAAG,GAAG,mBAAmB,IAAI,GAAG;AACpD;AAEO,IAAM,aAAa,CAAC,KAAK,KAAK,KAAK,MAAI,OAAO,MAAI,eAAa;AAClE,QAAM,MAAM,MAAM,UAAU,KAAK,KAAK,GAAG;AACzC,MAAI,OAAO,MAAM;AAAE;AAAA,EAAQ;AAC3B,MAAI,MAAM,KAAK;AAAE,UAAM,IAAI,MAAM,GAAG,GAAG,yBAAyB,GAAG,EAAE;AAAA,EAAG;AACxE,MAAI,MAAM,KAAK;AAAE,UAAM,IAAI,MAAM,GAAG,GAAG,sBAAsB,GAAG,EAAE;AAAA,EAAG;AACrE,SAAO;AACX;;;ACnBA,SAAS,OAAO,eAAe;AAWxB,IAAM,iBAAiB,CAAC,IAAI,WAAW;AAC1C,QAAM,IAAI,CAAC;AACX,QAAM,IAAI,CAAC;AACX,QAAM,GAAG,MAAM,EAAE;AACjB,QAAM,GAAG,WAAW,OAAO,CAAC;AAC5B,UAAQ,GAAG,YAAY,OAAK,CAAC,GAAG,CAAC,CAAC;AAClC,UAAQ,GAAG,WAAW,QAAM,EAAE,QAAQ,EAAE,QAAQ,OAAO,KAAK,EAAE,OAAO;AACrE,QAAM,GAAG,QAAQ,aAAW,EAAE,KAAK,OAAO,GAAG,KAAK;AAClD,SAAO;AACX;;;AHXO,IAAM,QAAN,MAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,YAAY,UAAU,CAAC,GAAG;AACtB,UAAM,KAAK;AAAA,MACP,OAAO;AAAA,MACP,QAAO;AAAA,IACX;AAEA,UAAM,SAAS,MAAM,YAAY,QAAQ,QAAQ,OAAO,gBAAgB,MAAM,OAAK,KAAK,IAAI;AAE5F,UAAM,WAAW,WAAW,IAAI,YAAe,QAAQ,UAAU,MAAM,kBAAkB;AACzF,UAAM,SAAU,WAAW,GAAG,UAAU,QAAQ,QAAQ,OAAO,gBAAgB,KAAK;AAEpF,UAAM,UAAU,MAAM,YAAY,QAAQ,SAAS,MAAM,iBAAiB;AAC1E,OAAG,UAAU,MAAM,YAAY,QAAQ,SAAS,OAAO,iBAAiB,KAAK;AAC7E,OAAG,SAAS,MAAM,YAAY,QAAQ,QAAQ,OAAO,gBAAgB,KAAK;AAE1E,UAAM,UAAU,MAAM,YAAY,QAAQ,SAAS,OAAO,iBAAiB,KAAK;AAEhF,UAAM,aAAa,MAAM,YAAY,QAAQ,YAAY,OAAO,oBAAoB,KAAK;AACzF,UAAM,YAAY,MAAM,WAAW,QAAQ,WAAW,OAAO,mBAAmB,KAAK;AACrF,UAAM,SAAS,MAAM,WAAW,QAAQ,QAAQ,OAAO,eAAe,KAAK;AAE3E,OAAG,QAAQ,OAAG;AACV,SAAG,SAAS;AAAA,IAChB;AAEA,OAAG,OAAO,OAAK;AACX,UAAI,CAAC,GAAG,SAAS,GAAG,YAAY,MAAM;AAAE;AAAA,MAAQ;AAChD,SAAG,YAAY,WAAW,GAAG,KAAK,KAAK,SAAS;AAAA,IACpD;AAEA,OAAG,MAAM,OAAM,MAAK;AAChB,UAAI,CAAC,GAAG,SAAS,GAAG,UAAU;AAAE;AAAA,MAAQ;AAExC,YAAM,KAAK,GAAG;AACd,YAAM,KAAK,GAAG,WAAW,SAAS,KAAK,eAAe,IAAI,MAAM;AAChE,UAAI,MAAM,MAAM;AAEhB,UAAI;AACA,iBAAS,MAAM,QAAQ,MAAM,EAAE;AAC/B,YAAI,CAAC,QAAQ;AAAE,UAAAC,OAAM,IAAI,UAAU,MAAM;AAAA,QAAG;AAAA,MAChD,SACO,KAAK;AACR,eAAO;AACP,YAAI,CAAC,QAAQ;AAAE,UAAAA,OAAM,IAAI,SAAS,IAAI;AAAA,QAAG;AAAA,MAC7C;AAEA,UAAI,CAAC,QAAQ;AAAE,QAAAA,OAAM,IAAI,SAAS,OAAO,CAAC;AAAA,MAAG;AAE7C,UAAI,QAAQ,MAAM;AACd,YAAI,QAAQ;AAAE,kBAAQ,MAAM,MAAM,EAAE;AAAA,QAAG,OAClC;AAAE,kBAAQ,MAAM,EAAE;AAAA,QAAG;AAAA,MAC9B;AAEA,UAAI;AAAE,cAAM,WAAW,MAAM,EAAE;AAAA,MAAG,SAC5B,KAAK;AAAE,eAAO;AAAA,MAAK;AAEzB,SAAG,OAAO,SAAS,SAAS;AAC5B,aAAO,GAAG;AAEV,UAAI,QAAQ,MAAM;AACd,WAAG,QAAQ;AACX,cAAM;AAAA,MACV;AAEA,SAAG,KAAK;AAAA,IACZ;AAEA,aAAS,MAAM;AAAA,MACX,OAAM,OAAG,GAAG;AAAA,MACZ,MAAK,OAAG,GAAG;AAAA,MACX,WAAU,OAAK,WAAW,OAAO,IAAI,WAAY;AAAA,IACrD,CAAC;AAED,WAAO,MAAM;AAAA,MACT;AAAA,MACA;AAAA,IACJ,CAAC;AAED,YAAQ,IAAI,MAAM,EAAE;AAEpB,QAAI,WAAW;AAAE,WAAK,MAAM;AAAA,IAAG;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAM,OAAO;AACf,UAAM,EAAE,MAAM,IAAI;AAClB,QAAI,OAAO;AAAE,aAAO;AAAA,IAAO;AAC3B,UAAM,KAAK,QAAQ,IAAI,IAAI;AAC3B,OAAG,QAAQ,IAAI;AACf,QAAI,OAAO;AAAE,SAAG,MAAM;AAAA,IAAG;AACzB,OAAG,QAAQ;AACX,OAAG,KAAK;AACR,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,QAAM,OAAO;AACd,UAAM,EAAE,MAAM,IAAI;AAClB,QAAI,CAAC,OAAO;AAAE,aAAO;AAAA,IAAO;AAC5B,UAAM,KAAK,QAAQ,IAAI,IAAI;AAC3B,iBAAa,GAAG,SAAS;AACzB,OAAG,QAAQ;AACX,QAAI,OAAO;AAAE,SAAG,MAAM;AAAA,IAAG;AACzB,OAAG,OAAO,IAAI;AACd,WAAO;AAAA,EACX;AAAA,EAEA,QAAQ;AACJ,YAAQ,IAAI,IAAI,EAAE,MAAM;AACxB,WAAO;AAAA,EACX;AAAA,EAEA,QAAQ,QAAM,OAAO;AACjB,SAAK,KAAK,KAAK;AACf,WAAO,KAAK,MAAM,KAAK;AAAA,EAC3B;AAGJ;;;AIlIO,IAAM,cAAc,CAAC,UAAU,CAAC,MAAM,IAAI,MAAM,OAAO;AAMvD,IAAM,gBAAgB,MAAI;AAC7B,aAAW,CAAC,OAAO,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC7C,UAAM,KAAK;AAAA,EACf;AACJ;;;AChBA,IAAO,gBAAQ;",
6
- "names": ["solid", "solid"]
3
+ "sources": ["../../src/tools.js", "../../src/options.js", "../../src/errors.js", "../../src/class/PulseCycle.js", "../../src/class/PulseSession.js", "../../src/class/Pulse.js", "../../src/index.js"],
4
+ "sourcesContent": ["\n\nexport const is = (type, any)=>typeof any === type;\n\nexport const valid = (type, any, req=false, msg=\"argument\")=>{\n if (any == null) {\n if (!req) { return; }\n throw new Error(`[pulse] ${msg} require typeof '${type}'`);\n }\n if (is(type, any)) { return any; }\n throw new Error(`[pulse] ${msg} is not typeof '${type}'`);\n}\n\nexport const validRange = (min, max, any, req=false, msg=\"argument\")=>{\n const num = valid(\"number\", any, req, msg);\n if (num == null) { return; }\n if (min != null && num < min) { throw new Error(`[pulse] ${msg} must be >= ${min}`); }\n if (max != null && num > max) { throw new Error(`[pulse] ${msg} must be <= ${max}`); }\n return num;\n}\n\nexport const validRangeFinite = (min, any, req=false, msg=\"argument\")=>{\n const num = validRange(min, null, any, req, msg);\n if (num == null) { return; }\n if (!Number.isFinite(num)) { throw new Error(`[pulse] ${msg} must be finite`); }\n return num;\n}\n\nexport const validateGetNow = (options)=>{\n const custom = valid(\"function\", options.getNow, false, \"options.getNow\");\n if (!custom) { return _=>Date.now(); }\n const safe = _=>validRangeFinite(0, custom(), true, \"options.getNow result\");\n safe(); //test\n return safe;\n}", "\r\nimport { valid, validateGetNow, validRange } from \"./tools\";\r\n\r\nconst blankFn = ()=>{};\r\n\r\nexport const formatOptions = (options) => {\r\n const o = {};\r\n\r\n if (options.noMeta != null) { throw new Error(\"options.noMeta is obsolete\"); }\r\n if (options.maxRuns != null) { throw new Error(\"options.maxRuns is obsolete\"); }\r\n if (options.afterPulse != null) { throw new Error(\"options.afterPulse is obsolete\"); }\r\n\r\n o.getNow = validateGetNow(options);\r\n\r\n o.align = valid(\"boolean\", options.align, true, \"options.align\");\r\n\r\n const minInterval = 10;\r\n const maxInterval = 2_147_483_647;\r\n o.interval = validRange(minInterval, maxInterval, options.interval, true, \"options.interval\");\r\n\r\n o.runOnStart = valid(\"boolean\", options.runOnStart, false, \"options.runOnStart\") || false;\r\n\r\n const offsetMsg = `options.offset (align:${o.align})`;\r\n\r\n if (o.align) {\r\n o.offset = (validRange(0, o.interval, options.offset, false, offsetMsg) ?? 0);\r\n } else if (!o.runOnStart) {\r\n o.offset = (validRange(-o.interval, (maxInterval - o.interval), options.offset, false, offsetMsg) ?? 0);\r\n } else if (options.offset != null) {\r\n throw new Error(`${offsetMsg} can't be set if runOnStart:true`);\r\n } else {\r\n o.offset = -o.interval;\r\n }\r\n\r\n o.onPulse = valid(\"function\", options.onPulse, true, \"options.onPulse\");\r\n o.onStart = valid(\"function\", options.onStart, false, \"options.onStart\") || blankFn;\r\n o.onStop = valid(\"function\", options.onStop, false, \"options.onStop\") || blankFn;\r\n o.onError = valid(\"function\", options.onError, false, \"options.onError\");\r\n o.autoStart = valid(\"boolean\", options.autoStart, false, \"options.autoStart\") || false;\r\n\r\n return Object.freeze(o);\r\n}\r\n", "export class PulseError extends Error { \n constructor(type, name, cause) {\n super(`Pulse ${type} failed`, { cause });\n this.name = name;\n this.pulseErrType = type;\n }\n}\n\nexport class PulseStartError extends PulseError {\n constructor(cause) {\n super(\"start\", \"PulseStartError\", cause);\n }\n}\n\nexport class PulseStopError extends PulseError {\n constructor(cause) {\n super(\"stop\", \"PulseStopError\", cause);\n }\n}\n\nexport class PulseScheduleError extends PulseError {\n constructor(cause) {\n super(\"schedule\", \"PulseScheduleError\", cause);\n }\n}\n\nexport class PulseExecuteError extends PulseError {\n constructor(cause) {\n super(\"execute\", \"PulseExecuteError\", cause);\n }\n}\n\nexport class PulseRuntimeError extends PulseError {\n constructor(cause) {\n super(\"runtime\", \"PulseRuntimeError\", cause);\n }\n}\n\nconst getErrType = (err)=>{\n if (err instanceof PulseError) { return err.pulseErrType; }\n return \"unknown\";\n}\n\nexport const handleError = async (pulse, error, onError)=>{\n\n const type = getErrType(error);\n\n if (!onError) {\n console.error(`[pulse] ${type} error:`, error);\n } else {\n try { await onError(pulse, type, error); }\n catch (e2) { console.error(`[pulse] options.onError('${type}') failed: `, e2); }\n }\n}", "\n/** One pulse execution and its user-visible metadata (formerly createMetaData). */\nexport class PulseCycle {\n\n #pulse = null;\n #session = null;\n #opt = null;\n #warnings = [];\n \n #ok = true;\n #id = undefined;\n #done = undefined;\n #result = undefined;\n #error = undefined;\n #started = undefined;\n #ended = undefined;\n\n constructor(pulse, session, opt) {\n this.#pulse = pulse;\n this.#session = session;\n this.#opt = opt;\n }\n\n get pulse() { return this.#pulse; }\n get session() { return this.#session; }\n get signal() { return this.#session.signal; }\n\n get id() { return this.#id; }\n\n get ok() { return this.#ok; }\n get done() { return this.#done; }\n\n get started() { return this.#started; }\n get ended() { return this.#ended; }\n get runtime() { return (this.#done ? this.#ended : this.#opt.getNow()) - this.#started; }\n get lateBy() { return Math.max(0, this.runtime - this.#opt.interval); }\n \n get warnings() { return [...this.#warnings]; }\n get result() { return this.#result; }\n get error() { return this.#error; }\n\n warn(warning) { return this.#warnings.push(warning); }\n\n async _execute(id) {\n if (this.#id != null) { return false; }\n\n const { getNow, onPulse } = this.#opt;\n\n this.#id = id;\n this.#started = getNow();\n\n try {\n this.#result = await onPulse(this);\n } catch (err) {\n this.#error = err;\n this.#ok = false;\n }\n\n this.#done = true;\n this.#ended = getNow();\n\n return true;\n }\n\n toJSON() {\n const { id, ok, done, started, ended, runtime, lateBy, warnings, result, error } = this;\n return { id, ok, done, started, ended, runtime, lateBy, warnings, result, error };\n }\n}\n", "import { handleError, PulseExecuteError, PulseRuntimeError, PulseScheduleError, PulseStartError, PulseStopError } from \"../errors\";\nimport { PulseCycle } from \"./PulseCycle\";\n\n/** One start/stop lifetime. Owns timing, cancellation and its cycles. */\nexport class PulseSession {\n\n #pulse = null;\n #opt = null;\n #ctrl = new AbortController();\n\n #state = false;\n #timeoutId;\n #fireAt;\n\n #current = null;\n #last;\n\n #consumeNextId;\n\n constructor(pulse, opt, consumeNextId) {\n this.#pulse = pulse;\n this.#opt = opt;\n this.#consumeNextId = consumeNextId;\n }\n\n get pulse() { return this.#pulse; }\n get signal() { return this.#ctrl.signal; }\n\n get interval() { return this.#opt.interval; }\n get offset() { return this.#opt.offset; }\n get align() { return this.#opt.align; }\n\n get state() { return this.#state && !this.#ctrl.signal.aborted; }\n get current() { return this.#current; }\n get last() { return this.#last; }\n get countdown() { return this.calculateCountdown(); }\n get potential() { return this.#pulse.potential }\n get nextAt() { return this.state ? this.#pulse.potential + this.#opt.getNow() : undefined; }\n get nextId() { return this.#pulse.nextId; }\n\n calculateCountdown() {\n if (!this.state || this.#fireAt == null) { return; }\n return Math.max(0, this.#fireAt - this.#opt.getNow());\n }\n\n start() {\n if (this.#state) { return false; }\n this.#state = true;\n\n try {\n this.#opt.onStart(this);\n } catch (err) {\n // A failed start should not leave a live session behind.\n this.#state = false;\n this.#ctrl.abort(err);\n throw new PulseStartError(err);\n }\n\n return this.#schedule(true);\n }\n\n stop(reason) {\n if (!this.#state) { return false; }\n // A consumer stopping pulse.session should also detach it from Pulse.\n if (this.#pulse.session === this) { return this.#pulse.stop(reason); }\n\n this.#state = false;\n clearTimeout(this.#timeoutId);\n this.#fireAt = undefined;\n this.#current = null;\n this.#ctrl.abort(reason);\n\n if (reason != null) { this.#handleError(reason); }\n\n try { this.#opt.onStop(this, reason); }\n catch(err) { this.#handleError(new PulseStopError(err)); }\n\n return true;\n }\n\n #handleError(error) { return handleError(this.pulse, error, this.#opt.onError); }\n\n // Even a temporarily broken clock/configuration must not permanently stall scheduling.\n #schedule(isInit) {\n if (!this.state || this.#current != null) { return false; }\n\n try {\n const fireIn = this.#pulse.calculatePotential(isInit);\n this.#fireAt = this.#opt.getNow() + fireIn;\n this.#timeoutId = setTimeout(this.#tick, fireIn);\n this.#timeoutId.unref?.();\n return true;\n } catch (err) {\n this.stop(new PulseScheduleError(err));\n return false;\n }\n\n }\n\n #tick = async () => {\n if (!this.state || this.#current != null) { return; }\n\n let cycle;\n\n try {\n this.#current = cycle = new PulseCycle(this.#pulse, this, this.#opt);\n await cycle._execute(this.#consumeNextId());\n if (!cycle.ok) { this.#handleError(new PulseRuntimeError(cycle.error)); }\n } catch (err) {\n this.stop(new PulseExecuteError(err));\n }\n\n if (!this.state) { return; }\n\n if (cycle && this.#current === cycle) {\n this.#last = cycle;\n this.#current = null;\n }\n this.#schedule(false);\n }\n\n toJSON() {\n const { interval, offset, align, state, current, last, countdown, potential, nextAt, nextId } = this;\n return { interval, offset, align, state, current, last, countdown, potential, nextAt, nextId };\n }\n\n}\n", "import { formatOptions } from \"../options\";\nimport { PulseSession } from \"./PulseSession\";\nimport { handleError } from \"../errors\";\n\n/** Public API for a periodic scheduler. */\nexport class Pulse {\n #opt;\n #session = null;\n #nextId = 0;\n #last;\n\n constructor(options = {}) {\n this.#opt = formatOptions(options);\n if (this.#opt.autoStart) { this.start(); }\n }\n\n get session() { return this.#session; }\n get state() { return this.#session?.state ?? false; }\n get current() { return this.#session?.current; }\n get last() { return this.#session?.last ?? this.#last; }\n get countdown() { return this.#session?.countdown; }\n get potential() { return this.countdown ?? this.calculatePotential(); }\n get nextAt() { return this.state ? this.potential + this.#opt.getNow() : undefined; }\n get nextId() { return this.#nextId; }\n\n get interval() { return this.#opt.interval; }\n get offset() { return this.#opt.offset; }\n get align() { return this.#opt.align; }\n\n calculatePotential(isInit = true) {\n const { align, runOnStart, interval, offset, getNow } = this.#opt;\n if (isInit && runOnStart) { return 0; }\n if (!align) { return isInit ? interval + offset : interval; }\n const now = getNow() - offset;\n const rest = ((now % interval) + interval) % interval;\n return rest === 0 ? interval : interval - rest;\n }\n\n #handleError(error) { return handleError(this, error, this.#opt.onError); }\n\n #consumeNextId = () => this.#nextId++;\n\n /** Start a new session. Returns false if already running. */\n start() {\n if (this.state) { return false; }\n\n // These callbacks keep IDs and the public `last` on Pulse itself.\n const session = new PulseSession(this, this.#opt, this.#consumeNextId);\n\n // Attach before calling onStart: callbacks can safely stop/restart us.\n this.#session = session;\n try { return session.start(); }\n catch (err) {\n if (this.#session === session) { this.#session = null; }\n this.#handleError(err);\n throw err;\n }\n }\n\n /** Stop the current session, without cancelling a newer one. */\n stop(reason) {\n const session = this.#session;\n if (!session?.state) { return false; }\n\n // Detach first so callbacks cannot accidentally affect this session.\n this.#session = null;\n this.#last = session.last ?? this.#last;\n return session.stop(reason);\n }\n\n restart() {\n this.stop();\n return this.start();\n }\n\n toJSON() {\n const { interval, offset, align, state, current, last, countdown, potential, nextAt, nextId } = this;\n return { interval, offset, align, state, current, last, countdown, potential, nextAt, nextId };\n }\n\n reset() {\n throw new Error(\"[pulse] reset() is obsolete\");\n }\n}\n", "import { Pulse } from \"./class/Pulse\";\nimport { PulseCycle } from \"./class/PulseCycle\";\nimport { PulseSession } from \"./class/PulseSession\";\nexport * from \"./errors\";\n\nexport {\n Pulse,\n PulseSession,\n PulseCycle\n}\n\nexport const createPulse = (options = {}) => new Pulse(options);\n\n\nexport default createPulse;\n"],
5
+ "mappings": "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",
6
+ "names": []
7
7
  }
@@ -0,0 +1,174 @@
1
+ /** One of the failure categories reported by Pulse. */
2
+ export type PulseErrorType = "start" | "stop" | "schedule" | "execute" | "runtime";
3
+ export type PulseErrorKind = PulseErrorType | "unknown";
4
+ export type PulseWarning = string | Error;
5
+
6
+ /** Called when Pulse reports an error. Async handlers are not awaited by the scheduler. */
7
+ export type PulseErrorHandler = (pulse: Pulse, type: PulseErrorKind, error: unknown) => void | Promise<void>;
8
+
9
+ /** Scheduler configuration. */
10
+ export interface PulseOptions<Result = unknown> {
11
+ /** Callback executed for each cycle; receives the cycle itself, not Pulse or session. */
12
+ onPulse: (cycle: PulseCycle<Result>) => Result | PromiseLike<Result>;
13
+ /** Align executions to clock boundaries. */
14
+ align: boolean;
15
+ /** Interval in milliseconds, from 10 to 2_147_483_647. */
16
+ interval: number;
17
+ /** Time shift. Constraints depend on align and runOnStart. */
18
+ offset?: number;
19
+ /** Schedule the first execution immediately. Default: false. */
20
+ runOnStart?: boolean;
21
+ /** Custom finite, nonnegative clock in milliseconds. Default: Date.now. */
22
+ getNow?: () => number;
23
+ /** Start automatically during construction. Default: false. */
24
+ autoStart?: boolean;
25
+ /** Called before scheduling a newly started session. Can prevent start by throwing. */
26
+ onStart?: (session: PulseSession<Result>) => void;
27
+ /** Called after a session has been stopped, with the stop reason when present. */
28
+ onStop?: (session: PulseSession<Result>, reason?: unknown) => void;
29
+ /** Error notification; does not block further cycles. */
30
+ onError?: PulseErrorHandler;
31
+ }
32
+
33
+ /** Normalized options used internally by PulseSession and PulseCycle. */
34
+ interface ResolvedPulseOptions<Result> {
35
+ readonly onPulse: (cycle: PulseCycle<Result>) => Result | PromiseLike<Result>;
36
+ readonly align: boolean;
37
+ readonly interval: number;
38
+ readonly offset: number;
39
+ readonly runOnStart: boolean;
40
+ readonly getNow: () => number;
41
+ readonly autoStart: boolean;
42
+ readonly onStart: (session: PulseSession<Result>) => void;
43
+ readonly onStop: (session: PulseSession<Result>, reason?: unknown) => void;
44
+ readonly onError?: PulseErrorHandler;
45
+ }
46
+
47
+ /** Snapshot returned by Pulse.toJSON(). */
48
+ export interface PulseSnapshot<Result = unknown> {
49
+ interval: number;
50
+ offset: number;
51
+ align: boolean;
52
+ state: boolean;
53
+ current: PulseCycle<Result> | null | undefined;
54
+ last: PulseCycle<Result> | undefined;
55
+ countdown: number | undefined;
56
+ potential: number;
57
+ nextAt: number | undefined;
58
+ nextId: number;
59
+ }
60
+
61
+ /** Snapshot returned by PulseSession.toJSON(). */
62
+ export interface PulseSessionSnapshot<Result = unknown> extends PulseSnapshot<Result> {}
63
+
64
+ /** Snapshot returned by PulseCycle.toJSON(). */
65
+ export interface PulseCycleSnapshot<Result = unknown> {
66
+ id: number | undefined;
67
+ ok: boolean;
68
+ done: boolean | undefined;
69
+ started: number | undefined;
70
+ ended: number | undefined;
71
+ runtime: number;
72
+ lateBy: number;
73
+ warnings: PulseWarning[];
74
+ result: Result | undefined;
75
+ error: unknown;
76
+ }
77
+
78
+ /** Public API for a periodic scheduler. */
79
+ export class Pulse<Result = unknown> {
80
+ constructor(options: PulseOptions<Result>);
81
+ readonly session: PulseSession<Result> | null;
82
+ readonly state: boolean;
83
+ readonly current: PulseCycle<Result> | null | undefined;
84
+ readonly last: PulseCycle<Result> | undefined;
85
+ readonly countdown: number | undefined;
86
+ readonly potential: number;
87
+ readonly nextAt: number | undefined;
88
+ readonly nextId: number;
89
+ readonly interval: number;
90
+ readonly offset: number;
91
+ readonly align: boolean;
92
+ calculatePotential(isInit?: boolean): number;
93
+ start(): boolean;
94
+ stop(reason?: unknown): boolean;
95
+ restart(): boolean;
96
+ toJSON(): PulseSnapshot<Result>;
97
+ }
98
+
99
+ /** One start/stop lifetime, including its timer and cancellation signal. */
100
+ export class PulseSession<Result = unknown> {
101
+ constructor(pulse: Pulse<Result>, opt: ResolvedPulseOptions<Result>, consumeNextId: () => number);
102
+ readonly pulse: Pulse<Result>;
103
+ readonly signal: AbortSignal;
104
+ readonly interval: number;
105
+ readonly offset: number;
106
+ readonly align: boolean;
107
+ readonly state: boolean;
108
+ readonly current: PulseCycle<Result> | null;
109
+ readonly last: PulseCycle<Result> | undefined;
110
+ readonly countdown: number | undefined;
111
+ readonly potential: number;
112
+ readonly nextAt: number | undefined;
113
+ readonly nextId: number;
114
+ calculateCountdown(): number | undefined;
115
+ start(): boolean;
116
+ stop(reason?: unknown): boolean;
117
+ toJSON(): PulseSessionSnapshot<Result>;
118
+ }
119
+
120
+ /** One execution and the metadata made available to onPulse. */
121
+ export class PulseCycle<Result = unknown> {
122
+ constructor(pulse: Pulse<Result>, session: PulseSession<Result>, opt: ResolvedPulseOptions<Result>);
123
+ readonly pulse: Pulse<Result>;
124
+ readonly session: PulseSession<Result>;
125
+ readonly signal: AbortSignal;
126
+ readonly id: number | undefined;
127
+ readonly ok: boolean;
128
+ readonly done: boolean | undefined;
129
+ readonly started: number | undefined;
130
+ readonly ended: number | undefined;
131
+ readonly runtime: number;
132
+ readonly lateBy: number;
133
+ readonly warnings: PulseWarning[];
134
+ readonly result: Result | undefined;
135
+ readonly error: unknown;
136
+ warn(warning: PulseWarning): number;
137
+ /** Internal execution entry point. */
138
+ _execute(id: number): Promise<boolean>;
139
+ toJSON(): PulseCycleSnapshot<Result>;
140
+ }
141
+
142
+ /** Base for errors produced by Pulse. The original error is available as `cause`. */
143
+ export class PulseError extends Error {
144
+ constructor(type: PulseErrorType | (string & {}), name: string, cause?: unknown);
145
+ readonly pulseErrType: string;
146
+ readonly cause: unknown;
147
+ }
148
+ export class PulseStartError extends PulseError {
149
+ constructor(cause?: unknown);
150
+ readonly pulseErrType: "start";
151
+ }
152
+ export class PulseStopError extends PulseError {
153
+ constructor(cause?: unknown);
154
+ readonly pulseErrType: "stop";
155
+ }
156
+ export class PulseScheduleError extends PulseError {
157
+ constructor(cause?: unknown);
158
+ readonly pulseErrType: "schedule";
159
+ }
160
+ export class PulseExecuteError extends PulseError {
161
+ constructor(cause?: unknown);
162
+ readonly pulseErrType: "execute";
163
+ }
164
+ export class PulseRuntimeError extends PulseError {
165
+ constructor(cause?: unknown);
166
+ readonly pulseErrType: "runtime";
167
+ }
168
+
169
+ /** Report an error; the promise settles after the handler completes. */
170
+ export function handleError(error: unknown, onError?: PulseErrorHandler): Promise<void>;
171
+
172
+ /** Create a Pulse using the same options as the Pulse constructor. */
173
+ export function createPulse<Result = unknown>(options: PulseOptions<Result>): Pulse<Result>;
174
+ export default createPulse;
package/package.json CHANGED
@@ -1,20 +1,25 @@
1
1
  {
2
2
  "name": "@randajan/pulse",
3
- "version": "1.1.4",
4
- "description": "Lightweight, promise-friendly scheduler that outperforms classic cron for any interval shorter than one month.",
3
+ "version": "3.0.0",
4
+ "description": "Reliable, promise-friendly scheduler with precise timing, lifecycle management and graceful error handling.",
5
5
  "repository": "randajan/pulse",
6
6
  "type": "module",
7
- "main": "dist/index.js",
8
- "module": "dist/index.js",
7
+ "main": "./dist/cjs/index.cjs",
8
+ "module": "./dist/esm/index.mjs",
9
+ "types": "./dist/types/index.d.ts",
9
10
  "exports": {
10
11
  ".": {
12
+ "types": "./dist/types/index.d.ts",
11
13
  "import": "./dist/esm/index.mjs",
12
14
  "require": "./dist/cjs/index.cjs"
13
15
  }
14
16
  },
17
+ "scripts": {
18
+ "build": "node index.js isBuild=true"
19
+ },
15
20
  "license": "MIT",
16
21
  "devDependencies": {
17
- "@randajan/simple-lib": "^3.3.0"
22
+ "@randajan/simple-lib": "^5.0.2"
18
23
  },
19
24
  "files": [
20
25
  "dist",
@@ -42,7 +47,5 @@
42
47
  "email": "jnranda@gmail.com",
43
48
  "url": "https://www.linkedin.com/in/randajan/"
44
49
  },
45
- "dependencies": {
46
- "@randajan/props": "^0.1.4"
47
- }
50
+ "dependencies": {}
48
51
  }