@orkestrel/workflow 0.0.6 → 0.0.7
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.
- package/dist/src/browser/index.d.ts +6 -6
- package/dist/src/browser/index.js +40 -30
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +63 -68
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +7 -5
- package/dist/src/core/index.d.ts +7 -5
- package/dist/src/core/index.js +63 -68
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +10 -8
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +4 -4
- package/dist/src/server/index.d.ts +4 -4
- package/dist/src/server/index.js +10 -8
- package/dist/src/server/index.js.map +1 -1
- package/package.json +23 -20
|
@@ -63,31 +63,33 @@ var NodeScheduler = class {
|
|
|
63
63
|
#immediate(signal) {
|
|
64
64
|
if (signal?.aborted === true) return Promise.reject(signal.reason);
|
|
65
65
|
return new Promise((resolve, reject) => {
|
|
66
|
-
const onAbort = () => {
|
|
67
|
-
clearImmediate(handle);
|
|
68
|
-
reject(signal?.reason);
|
|
69
|
-
};
|
|
70
66
|
const handle = setImmediate(() => {
|
|
71
67
|
signal?.removeEventListener("abort", onAbort);
|
|
72
68
|
resolve();
|
|
73
69
|
});
|
|
70
|
+
const onAbort = this.#abortImmediate.bind(this, handle, reject, signal);
|
|
74
71
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
75
72
|
});
|
|
76
73
|
}
|
|
77
74
|
#sleep(ms, signal) {
|
|
78
75
|
if (signal?.aborted === true) return Promise.reject(signal.reason);
|
|
79
76
|
return new Promise((resolve, reject) => {
|
|
80
|
-
const onAbort = () => {
|
|
81
|
-
clearTimeout(handle);
|
|
82
|
-
reject(signal?.reason);
|
|
83
|
-
};
|
|
84
77
|
const handle = setTimeout(() => {
|
|
85
78
|
signal?.removeEventListener("abort", onAbort);
|
|
86
79
|
resolve();
|
|
87
80
|
}, ms);
|
|
81
|
+
const onAbort = this.#abortTimeout.bind(this, handle, reject, signal);
|
|
88
82
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
89
83
|
});
|
|
90
84
|
}
|
|
85
|
+
#abortImmediate(handle, reject, signal) {
|
|
86
|
+
clearImmediate(handle);
|
|
87
|
+
reject(signal?.reason);
|
|
88
|
+
}
|
|
89
|
+
#abortTimeout(handle, reject, signal) {
|
|
90
|
+
clearTimeout(handle);
|
|
91
|
+
reject(signal?.reason);
|
|
92
|
+
}
|
|
91
93
|
};
|
|
92
94
|
//#endregion
|
|
93
95
|
//#region src/server/factories.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["#immediate","#sleep"],"sources":["../../../src/server/NodeScheduler.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { SchedulerInterface, SchedulerOptions } from '@src/core'\n\n/**\n * The Node {@link SchedulerInterface} — the server-native cooperative-yield backend.\n *\n * @remarks\n * - **`yield` is a `setImmediate` host-turn.** `yield()` waits on `setImmediate`, the\n * canonical Node \"give the host a turn\" — it runs AFTER the current operation and\n * any pending I/O callbacks, so the event loop genuinely regains control before\n * resuming (unlike a microtask, which drains within the current task). `delay(ms)`\n * waits on a real `setTimeout`.\n * - **Abort fidelity is verbatim.** A pending `yield` / `delay` rejects with\n * `signal.reason` exactly — the value the caller passed, never wrapped or replaced.\n * The discipline mirrors the cross-environment default's `#sleep`: an already-aborted\n * signal rejects immediately WITHOUT arming a timer; otherwise the timer is armed and\n * a `{ once: true }` abort listener attached, and the two settle paths are mutually\n * exclusive — the timer path removes the listener before resolving (so a later abort\n * cannot reach `reject`), and the abort path clears the timer before rejecting (so the\n * macrotask cannot reach `resolve`). The promise settles exactly once, with no leaked\n * timer and no leaked listener. It deliberately does NOT use `node:timers/promises`,\n * whose `{ signal }` option rejects with a Node `AbortError` (`code: 'ABORT_ERR'`)\n * rather than the caller's `signal.reason` — that would break reason fidelity, so the\n * timer and listener are hand-rolled to match the contract.\n * - **Priority is accepted but a no-op.** Node has no priority primitive (no equivalent\n * of the browser's `scheduler.postTask` priorities), so `options.priority` is accepted\n * for contract compliance and ignored — every yield/delay is uniform.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@src/core'\n * import { NodeScheduler } from '@src/server'\n *\n * const abort = createAbort()\n * const scheduler = new NodeScheduler()\n * while (!abort.signal.aborted) {\n * \tdoSomeWork()\n * \tawait scheduler.yield({ signal: abort.signal }) // a setImmediate host-turn\n * }\n * ```\n */\nexport class NodeScheduler implements SchedulerInterface {\n\t/**\n\t * Yield control back to the event loop via `setImmediate` so pending I/O and timers\n\t * can run, then resume; abort rejects with `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#immediate(options?.signal)\n\t}\n\n\t/**\n\t * Resume after at least `ms` milliseconds via `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * `ms` should be a non-negative finite number. The primitive does no validation: it\n\t * passes `ms` straight to the host `setTimeout`, which clamps a negative value or\n\t * `NaN` to ~0 — so an out-of-domain `ms` resolves on the next host turn rather than\n\t * throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#sleep(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// The `setImmediate` host-turn for `yield`. Resolves after the immediate callback\n\t// fires (after the current operation and pending I/O); rejects with `signal.reason`\n\t// if the signal is already aborted (no immediate armed) or aborts while pending.\n\t// The two settle paths disarm each other: the immediate path removes the abort\n\t// listener before resolving; the abort path clears the immediate before rejecting —\n\t// so it settles exactly once, with no leaked handle and no leaked listener.\n\t#immediate(signal?: AbortSignal): Promise<void> {\n\t\tif (signal?.aborted === true) return Promise.reject(signal.reason)\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["#immediate","#sleep","#abortImmediate","#abortTimeout"],"sources":["../../../src/server/NodeScheduler.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { SchedulerInterface, SchedulerOptions } from '@src/core'\n\n/**\n * The Node {@link SchedulerInterface} — the server-native cooperative-yield backend.\n *\n * @remarks\n * - **`yield` is a `setImmediate` host-turn.** `yield()` waits on `setImmediate`, the\n * canonical Node \"give the host a turn\" — it runs AFTER the current operation and\n * any pending I/O callbacks, so the event loop genuinely regains control before\n * resuming (unlike a microtask, which drains within the current task). `delay(ms)`\n * waits on a real `setTimeout`.\n * - **Abort fidelity is verbatim.** A pending `yield` / `delay` rejects with\n * `signal.reason` exactly — the value the caller passed, never wrapped or replaced.\n * The discipline mirrors the cross-environment default's `#sleep`: an already-aborted\n * signal rejects immediately WITHOUT arming a timer; otherwise the timer is armed and\n * a `{ once: true }` abort listener attached, and the two settle paths are mutually\n * exclusive — the timer path removes the listener before resolving (so a later abort\n * cannot reach `reject`), and the abort path clears the timer before rejecting (so the\n * macrotask cannot reach `resolve`). The promise settles exactly once, with no leaked\n * timer and no leaked listener. It deliberately does NOT use `node:timers/promises`,\n * whose `{ signal }` option rejects with a Node `AbortError` (`code: 'ABORT_ERR'`)\n * rather than the caller's `signal.reason` — that would break reason fidelity, so the\n * timer and listener are hand-rolled to match the contract.\n * - **Priority is accepted but a no-op.** Node has no priority primitive (no equivalent\n * of the browser's `scheduler.postTask` priorities), so `options.priority` is accepted\n * for contract compliance and ignored — every yield/delay is uniform.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@src/core'\n * import { NodeScheduler } from '@src/server'\n *\n * const abort = createAbort()\n * const scheduler = new NodeScheduler()\n * while (!abort.signal.aborted) {\n * \tdoSomeWork()\n * \tawait scheduler.yield({ signal: abort.signal }) // a setImmediate host-turn\n * }\n * ```\n */\nexport class NodeScheduler implements SchedulerInterface {\n\t/**\n\t * Yield control back to the event loop via `setImmediate` so pending I/O and timers\n\t * can run, then resume; abort rejects with `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#immediate(options?.signal)\n\t}\n\n\t/**\n\t * Resume after at least `ms` milliseconds via `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * `ms` should be a non-negative finite number. The primitive does no validation: it\n\t * passes `ms` straight to the host `setTimeout`, which clamps a negative value or\n\t * `NaN` to ~0 — so an out-of-domain `ms` resolves on the next host turn rather than\n\t * throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#sleep(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// The `setImmediate` host-turn for `yield`. Resolves after the immediate callback\n\t// fires (after the current operation and pending I/O); rejects with `signal.reason`\n\t// if the signal is already aborted (no immediate armed) or aborts while pending.\n\t// The two settle paths disarm each other: the immediate path removes the abort\n\t// listener before resolving; the abort path clears the immediate before rejecting —\n\t// so it settles exactly once, with no leaked handle and no leaked listener.\n\t#immediate(signal?: AbortSignal): Promise<void> {\n\t\tif (signal?.aborted === true) return Promise.reject(signal.reason)\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst handle = setImmediate(() => {\n\t\t\t\tsignal?.removeEventListener('abort', onAbort) // load-bearing: prevents a post-resolve reject\n\t\t\t\tresolve()\n\t\t\t})\n\t\t\tconst onAbort = this.#abortImmediate.bind(this, handle, reject, signal)\n\t\t\tsignal?.addEventListener('abort', onAbort, { once: true })\n\t\t})\n\t}\n\n\t// The abort-aware `setTimeout` sleep for `delay`. Same settle-once discipline as\n\t// `#immediate`, with a timer in place of the immediate: an already-aborted signal\n\t// rejects without arming; otherwise the timer path removes the listener before\n\t// resolving and the abort path clears the timer before rejecting with `signal.reason`.\n\t#sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\t\tif (signal?.aborted === true) return Promise.reject(signal.reason)\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst handle = setTimeout(() => {\n\t\t\t\tsignal?.removeEventListener('abort', onAbort) // load-bearing: prevents a post-resolve reject\n\t\t\t\tresolve()\n\t\t\t}, ms)\n\t\t\tconst onAbort = this.#abortTimeout.bind(this, handle, reject, signal)\n\t\t\tsignal?.addEventListener('abort', onAbort, { once: true })\n\t\t})\n\t}\n\n\t#abortImmediate(\n\t\thandle: ReturnType<typeof setImmediate>,\n\t\treject: (reason?: unknown) => void,\n\t\tsignal?: AbortSignal,\n\t): void {\n\t\tclearImmediate(handle)\n\t\treject(signal?.reason)\n\t}\n\n\t#abortTimeout(\n\t\thandle: ReturnType<typeof setTimeout>,\n\t\treject: (reason?: unknown) => void,\n\t\tsignal?: AbortSignal,\n\t): void {\n\t\tclearTimeout(handle)\n\t\treject(signal?.reason)\n\t}\n}\n","import type { SchedulerInterface } from '@src/core'\nimport { NodeScheduler } from './NodeScheduler.js'\n\n/**\n * Create the Node-native cooperative-yield {@link SchedulerInterface} — `yield()` is a\n * `setImmediate` host-turn (the canonical Node \"give the event loop a turn\"), `delay(ms)`\n * a real `setTimeout`.\n *\n * @remarks\n * Use it on a server instead of the cross-environment `createScheduler` when a yield\n * should hand the event loop a full turn (after pending I/O) via `setImmediate` rather\n * than a zero-delay timer. Both methods are abort-aware: pass `options.signal` and a\n * pending yield/delay rejects with the signal's `reason` (verbatim, with full\n * timer/listener cleanup). `options.priority` is accepted for contract compliance but a\n * no-op — Node has no priority primitive.\n *\n * @returns A {@link SchedulerInterface} backed by Node's `setImmediate` / `setTimeout`\n *\n * @example\n * ```ts\n * import { createAbort } from '@src/core'\n * import { createNodeScheduler } from '@src/server'\n *\n * const abort = createAbort()\n * const scheduler = createNodeScheduler()\n * while (!abort.signal.aborted) {\n * \tdoSomeWork()\n * \tawait scheduler.yield({ signal: abort.signal })\n * }\n * ```\n */\nexport function createNodeScheduler(): SchedulerInterface {\n\treturn new NodeScheduler()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,IAAa,gBAAb,MAAyD;;;;;CAKxD,MAAM,SAA2C;EAChD,OAAO,KAAKA,WAAW,SAAS,MAAM;CACvC;;;;;;;;;;;CAYA,MAAM,IAAY,SAA2C;EAC5D,OAAO,KAAKC,OAAO,IAAI,SAAS,MAAM;CACvC;CAUA,WAAW,QAAqC;EAC/C,IAAI,QAAQ,YAAY,MAAM,OAAO,QAAQ,OAAO,OAAO,MAAM;EACjE,OAAO,IAAI,SAAe,SAAS,WAAW;GAC7C,MAAM,SAAS,mBAAmB;IACjC,QAAQ,oBAAoB,SAAS,OAAO;IAC5C,QAAQ;GACT,CAAC;GACD,MAAM,UAAU,KAAKC,gBAAgB,KAAK,MAAM,QAAQ,QAAQ,MAAM;GACtE,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EAC1D,CAAC;CACF;CAMA,OAAO,IAAY,QAAqC;EACvD,IAAI,QAAQ,YAAY,MAAM,OAAO,QAAQ,OAAO,OAAO,MAAM;EACjE,OAAO,IAAI,SAAe,SAAS,WAAW;GAC7C,MAAM,SAAS,iBAAiB;IAC/B,QAAQ,oBAAoB,SAAS,OAAO;IAC5C,QAAQ;GACT,GAAG,EAAE;GACL,MAAM,UAAU,KAAKC,cAAc,KAAK,MAAM,QAAQ,QAAQ,MAAM;GACpE,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EAC1D,CAAC;CACF;CAEA,gBACC,QACA,QACA,QACO;EACP,eAAe,MAAM;EACrB,OAAO,QAAQ,MAAM;CACtB;CAEA,cACC,QACA,QACA,QACO;EACP,aAAa,MAAM;EACnB,OAAO,QAAQ,MAAM;CACtB;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtFA,SAAgB,sBAA0C;CACzD,OAAO,IAAI,cAAc;AAC1B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SchedulerInterface } from '../core/index.
|
|
2
|
-
import { SchedulerOptions } from '../core/index.
|
|
1
|
+
import { SchedulerInterface } from '../core/index.ts';
|
|
2
|
+
import { SchedulerOptions } from '../core/index.ts';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Create the Node-native cooperative-yield {@link SchedulerInterface} — `yield()` is a
|
|
@@ -18,7 +18,7 @@ import { SchedulerOptions } from '../core/index.js';
|
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
20
|
* ```ts
|
|
21
|
-
* import { createAbort } from '
|
|
21
|
+
* import { createAbort } from '@src/core'
|
|
22
22
|
* import { createNodeScheduler } from '@src/server'
|
|
23
23
|
*
|
|
24
24
|
* const abort = createAbort()
|
|
@@ -59,7 +59,7 @@ export declare function createNodeScheduler(): SchedulerInterface;
|
|
|
59
59
|
*
|
|
60
60
|
* @example
|
|
61
61
|
* ```ts
|
|
62
|
-
* import { createAbort } from '
|
|
62
|
+
* import { createAbort } from '@src/core'
|
|
63
63
|
* import { NodeScheduler } from '@src/server'
|
|
64
64
|
*
|
|
65
65
|
* const abort = createAbort()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SchedulerInterface } from '../core/index.
|
|
2
|
-
import { SchedulerOptions } from '../core/index.
|
|
1
|
+
import { SchedulerInterface } from '../core/index.ts';
|
|
2
|
+
import { SchedulerOptions } from '../core/index.ts';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Create the Node-native cooperative-yield {@link SchedulerInterface} — `yield()` is a
|
|
@@ -18,7 +18,7 @@ import { SchedulerOptions } from '../core/index.js';
|
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
20
|
* ```ts
|
|
21
|
-
* import { createAbort } from '
|
|
21
|
+
* import { createAbort } from '@src/core'
|
|
22
22
|
* import { createNodeScheduler } from '@src/server'
|
|
23
23
|
*
|
|
24
24
|
* const abort = createAbort()
|
|
@@ -59,7 +59,7 @@ export declare function createNodeScheduler(): SchedulerInterface;
|
|
|
59
59
|
*
|
|
60
60
|
* @example
|
|
61
61
|
* ```ts
|
|
62
|
-
* import { createAbort } from '
|
|
62
|
+
* import { createAbort } from '@src/core'
|
|
63
63
|
* import { NodeScheduler } from '@src/server'
|
|
64
64
|
*
|
|
65
65
|
* const abort = createAbort()
|
package/dist/src/server/index.js
CHANGED
|
@@ -62,31 +62,33 @@ var NodeScheduler = class {
|
|
|
62
62
|
#immediate(signal) {
|
|
63
63
|
if (signal?.aborted === true) return Promise.reject(signal.reason);
|
|
64
64
|
return new Promise((resolve, reject) => {
|
|
65
|
-
const onAbort = () => {
|
|
66
|
-
clearImmediate(handle);
|
|
67
|
-
reject(signal?.reason);
|
|
68
|
-
};
|
|
69
65
|
const handle = setImmediate(() => {
|
|
70
66
|
signal?.removeEventListener("abort", onAbort);
|
|
71
67
|
resolve();
|
|
72
68
|
});
|
|
69
|
+
const onAbort = this.#abortImmediate.bind(this, handle, reject, signal);
|
|
73
70
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
74
71
|
});
|
|
75
72
|
}
|
|
76
73
|
#sleep(ms, signal) {
|
|
77
74
|
if (signal?.aborted === true) return Promise.reject(signal.reason);
|
|
78
75
|
return new Promise((resolve, reject) => {
|
|
79
|
-
const onAbort = () => {
|
|
80
|
-
clearTimeout(handle);
|
|
81
|
-
reject(signal?.reason);
|
|
82
|
-
};
|
|
83
76
|
const handle = setTimeout(() => {
|
|
84
77
|
signal?.removeEventListener("abort", onAbort);
|
|
85
78
|
resolve();
|
|
86
79
|
}, ms);
|
|
80
|
+
const onAbort = this.#abortTimeout.bind(this, handle, reject, signal);
|
|
87
81
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
88
82
|
});
|
|
89
83
|
}
|
|
84
|
+
#abortImmediate(handle, reject, signal) {
|
|
85
|
+
clearImmediate(handle);
|
|
86
|
+
reject(signal?.reason);
|
|
87
|
+
}
|
|
88
|
+
#abortTimeout(handle, reject, signal) {
|
|
89
|
+
clearTimeout(handle);
|
|
90
|
+
reject(signal?.reason);
|
|
91
|
+
}
|
|
90
92
|
};
|
|
91
93
|
//#endregion
|
|
92
94
|
//#region src/server/factories.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#immediate","#sleep"],"sources":["../../../src/server/NodeScheduler.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { SchedulerInterface, SchedulerOptions } from '@src/core'\n\n/**\n * The Node {@link SchedulerInterface} — the server-native cooperative-yield backend.\n *\n * @remarks\n * - **`yield` is a `setImmediate` host-turn.** `yield()` waits on `setImmediate`, the\n * canonical Node \"give the host a turn\" — it runs AFTER the current operation and\n * any pending I/O callbacks, so the event loop genuinely regains control before\n * resuming (unlike a microtask, which drains within the current task). `delay(ms)`\n * waits on a real `setTimeout`.\n * - **Abort fidelity is verbatim.** A pending `yield` / `delay` rejects with\n * `signal.reason` exactly — the value the caller passed, never wrapped or replaced.\n * The discipline mirrors the cross-environment default's `#sleep`: an already-aborted\n * signal rejects immediately WITHOUT arming a timer; otherwise the timer is armed and\n * a `{ once: true }` abort listener attached, and the two settle paths are mutually\n * exclusive — the timer path removes the listener before resolving (so a later abort\n * cannot reach `reject`), and the abort path clears the timer before rejecting (so the\n * macrotask cannot reach `resolve`). The promise settles exactly once, with no leaked\n * timer and no leaked listener. It deliberately does NOT use `node:timers/promises`,\n * whose `{ signal }` option rejects with a Node `AbortError` (`code: 'ABORT_ERR'`)\n * rather than the caller's `signal.reason` — that would break reason fidelity, so the\n * timer and listener are hand-rolled to match the contract.\n * - **Priority is accepted but a no-op.** Node has no priority primitive (no equivalent\n * of the browser's `scheduler.postTask` priorities), so `options.priority` is accepted\n * for contract compliance and ignored — every yield/delay is uniform.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@src/core'\n * import { NodeScheduler } from '@src/server'\n *\n * const abort = createAbort()\n * const scheduler = new NodeScheduler()\n * while (!abort.signal.aborted) {\n * \tdoSomeWork()\n * \tawait scheduler.yield({ signal: abort.signal }) // a setImmediate host-turn\n * }\n * ```\n */\nexport class NodeScheduler implements SchedulerInterface {\n\t/**\n\t * Yield control back to the event loop via `setImmediate` so pending I/O and timers\n\t * can run, then resume; abort rejects with `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#immediate(options?.signal)\n\t}\n\n\t/**\n\t * Resume after at least `ms` milliseconds via `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * `ms` should be a non-negative finite number. The primitive does no validation: it\n\t * passes `ms` straight to the host `setTimeout`, which clamps a negative value or\n\t * `NaN` to ~0 — so an out-of-domain `ms` resolves on the next host turn rather than\n\t * throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#sleep(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// The `setImmediate` host-turn for `yield`. Resolves after the immediate callback\n\t// fires (after the current operation and pending I/O); rejects with `signal.reason`\n\t// if the signal is already aborted (no immediate armed) or aborts while pending.\n\t// The two settle paths disarm each other: the immediate path removes the abort\n\t// listener before resolving; the abort path clears the immediate before rejecting —\n\t// so it settles exactly once, with no leaked handle and no leaked listener.\n\t#immediate(signal?: AbortSignal): Promise<void> {\n\t\tif (signal?.aborted === true) return Promise.reject(signal.reason)\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst
|
|
1
|
+
{"version":3,"file":"index.js","names":["#immediate","#sleep","#abortImmediate","#abortTimeout"],"sources":["../../../src/server/NodeScheduler.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { SchedulerInterface, SchedulerOptions } from '@src/core'\n\n/**\n * The Node {@link SchedulerInterface} — the server-native cooperative-yield backend.\n *\n * @remarks\n * - **`yield` is a `setImmediate` host-turn.** `yield()` waits on `setImmediate`, the\n * canonical Node \"give the host a turn\" — it runs AFTER the current operation and\n * any pending I/O callbacks, so the event loop genuinely regains control before\n * resuming (unlike a microtask, which drains within the current task). `delay(ms)`\n * waits on a real `setTimeout`.\n * - **Abort fidelity is verbatim.** A pending `yield` / `delay` rejects with\n * `signal.reason` exactly — the value the caller passed, never wrapped or replaced.\n * The discipline mirrors the cross-environment default's `#sleep`: an already-aborted\n * signal rejects immediately WITHOUT arming a timer; otherwise the timer is armed and\n * a `{ once: true }` abort listener attached, and the two settle paths are mutually\n * exclusive — the timer path removes the listener before resolving (so a later abort\n * cannot reach `reject`), and the abort path clears the timer before rejecting (so the\n * macrotask cannot reach `resolve`). The promise settles exactly once, with no leaked\n * timer and no leaked listener. It deliberately does NOT use `node:timers/promises`,\n * whose `{ signal }` option rejects with a Node `AbortError` (`code: 'ABORT_ERR'`)\n * rather than the caller's `signal.reason` — that would break reason fidelity, so the\n * timer and listener are hand-rolled to match the contract.\n * - **Priority is accepted but a no-op.** Node has no priority primitive (no equivalent\n * of the browser's `scheduler.postTask` priorities), so `options.priority` is accepted\n * for contract compliance and ignored — every yield/delay is uniform.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@src/core'\n * import { NodeScheduler } from '@src/server'\n *\n * const abort = createAbort()\n * const scheduler = new NodeScheduler()\n * while (!abort.signal.aborted) {\n * \tdoSomeWork()\n * \tawait scheduler.yield({ signal: abort.signal }) // a setImmediate host-turn\n * }\n * ```\n */\nexport class NodeScheduler implements SchedulerInterface {\n\t/**\n\t * Yield control back to the event loop via `setImmediate` so pending I/O and timers\n\t * can run, then resume; abort rejects with `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#immediate(options?.signal)\n\t}\n\n\t/**\n\t * Resume after at least `ms` milliseconds via `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * `ms` should be a non-negative finite number. The primitive does no validation: it\n\t * passes `ms` straight to the host `setTimeout`, which clamps a negative value or\n\t * `NaN` to ~0 — so an out-of-domain `ms` resolves on the next host turn rather than\n\t * throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#sleep(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// The `setImmediate` host-turn for `yield`. Resolves after the immediate callback\n\t// fires (after the current operation and pending I/O); rejects with `signal.reason`\n\t// if the signal is already aborted (no immediate armed) or aborts while pending.\n\t// The two settle paths disarm each other: the immediate path removes the abort\n\t// listener before resolving; the abort path clears the immediate before rejecting —\n\t// so it settles exactly once, with no leaked handle and no leaked listener.\n\t#immediate(signal?: AbortSignal): Promise<void> {\n\t\tif (signal?.aborted === true) return Promise.reject(signal.reason)\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst handle = setImmediate(() => {\n\t\t\t\tsignal?.removeEventListener('abort', onAbort) // load-bearing: prevents a post-resolve reject\n\t\t\t\tresolve()\n\t\t\t})\n\t\t\tconst onAbort = this.#abortImmediate.bind(this, handle, reject, signal)\n\t\t\tsignal?.addEventListener('abort', onAbort, { once: true })\n\t\t})\n\t}\n\n\t// The abort-aware `setTimeout` sleep for `delay`. Same settle-once discipline as\n\t// `#immediate`, with a timer in place of the immediate: an already-aborted signal\n\t// rejects without arming; otherwise the timer path removes the listener before\n\t// resolving and the abort path clears the timer before rejecting with `signal.reason`.\n\t#sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\t\tif (signal?.aborted === true) return Promise.reject(signal.reason)\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst handle = setTimeout(() => {\n\t\t\t\tsignal?.removeEventListener('abort', onAbort) // load-bearing: prevents a post-resolve reject\n\t\t\t\tresolve()\n\t\t\t}, ms)\n\t\t\tconst onAbort = this.#abortTimeout.bind(this, handle, reject, signal)\n\t\t\tsignal?.addEventListener('abort', onAbort, { once: true })\n\t\t})\n\t}\n\n\t#abortImmediate(\n\t\thandle: ReturnType<typeof setImmediate>,\n\t\treject: (reason?: unknown) => void,\n\t\tsignal?: AbortSignal,\n\t): void {\n\t\tclearImmediate(handle)\n\t\treject(signal?.reason)\n\t}\n\n\t#abortTimeout(\n\t\thandle: ReturnType<typeof setTimeout>,\n\t\treject: (reason?: unknown) => void,\n\t\tsignal?: AbortSignal,\n\t): void {\n\t\tclearTimeout(handle)\n\t\treject(signal?.reason)\n\t}\n}\n","import type { SchedulerInterface } from '@src/core'\nimport { NodeScheduler } from './NodeScheduler.js'\n\n/**\n * Create the Node-native cooperative-yield {@link SchedulerInterface} — `yield()` is a\n * `setImmediate` host-turn (the canonical Node \"give the event loop a turn\"), `delay(ms)`\n * a real `setTimeout`.\n *\n * @remarks\n * Use it on a server instead of the cross-environment `createScheduler` when a yield\n * should hand the event loop a full turn (after pending I/O) via `setImmediate` rather\n * than a zero-delay timer. Both methods are abort-aware: pass `options.signal` and a\n * pending yield/delay rejects with the signal's `reason` (verbatim, with full\n * timer/listener cleanup). `options.priority` is accepted for contract compliance but a\n * no-op — Node has no priority primitive.\n *\n * @returns A {@link SchedulerInterface} backed by Node's `setImmediate` / `setTimeout`\n *\n * @example\n * ```ts\n * import { createAbort } from '@src/core'\n * import { createNodeScheduler } from '@src/server'\n *\n * const abort = createAbort()\n * const scheduler = createNodeScheduler()\n * while (!abort.signal.aborted) {\n * \tdoSomeWork()\n * \tawait scheduler.yield({ signal: abort.signal })\n * }\n * ```\n */\nexport function createNodeScheduler(): SchedulerInterface {\n\treturn new NodeScheduler()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,IAAa,gBAAb,MAAyD;;;;;CAKxD,MAAM,SAA2C;EAChD,OAAO,KAAKA,WAAW,SAAS,MAAM;CACvC;;;;;;;;;;;CAYA,MAAM,IAAY,SAA2C;EAC5D,OAAO,KAAKC,OAAO,IAAI,SAAS,MAAM;CACvC;CAUA,WAAW,QAAqC;EAC/C,IAAI,QAAQ,YAAY,MAAM,OAAO,QAAQ,OAAO,OAAO,MAAM;EACjE,OAAO,IAAI,SAAe,SAAS,WAAW;GAC7C,MAAM,SAAS,mBAAmB;IACjC,QAAQ,oBAAoB,SAAS,OAAO;IAC5C,QAAQ;GACT,CAAC;GACD,MAAM,UAAU,KAAKC,gBAAgB,KAAK,MAAM,QAAQ,QAAQ,MAAM;GACtE,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EAC1D,CAAC;CACF;CAMA,OAAO,IAAY,QAAqC;EACvD,IAAI,QAAQ,YAAY,MAAM,OAAO,QAAQ,OAAO,OAAO,MAAM;EACjE,OAAO,IAAI,SAAe,SAAS,WAAW;GAC7C,MAAM,SAAS,iBAAiB;IAC/B,QAAQ,oBAAoB,SAAS,OAAO;IAC5C,QAAQ;GACT,GAAG,EAAE;GACL,MAAM,UAAU,KAAKC,cAAc,KAAK,MAAM,QAAQ,QAAQ,MAAM;GACpE,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EAC1D,CAAC;CACF;CAEA,gBACC,QACA,QACA,QACO;EACP,eAAe,MAAM;EACrB,OAAO,QAAQ,MAAM;CACtB;CAEA,cACC,QACA,QACA,QACO;EACP,aAAa,MAAM;EACnB,OAAO,QAAQ,MAAM;CACtB;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtFA,SAAgB,sBAA0C;CACzD,OAAO,IAAI,cAAc;AAC1B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkestrel/workflow",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "A typed workflow engine for the @orkestrel line — a serializable Workflow → Phase → Task tree run by a composed runner on a cooperative scheduler. Part of the @orkestrel line.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"orchestration",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"url": "git+https://github.com/orkestrel/workflow.git"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
|
-
"dist",
|
|
21
|
+
"dist/src",
|
|
22
22
|
"README.md"
|
|
23
23
|
],
|
|
24
24
|
"type": "module",
|
|
@@ -58,10 +58,10 @@
|
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
|
-
"clean": "node -e \"
|
|
61
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
62
62
|
"copy": "node -e \"const fs=require('node:fs'),p=require('node:path'),a=process.argv[1],b=process.argv[2];fs.mkdirSync(p.dirname(b),{recursive:true});fs.cpSync(a,b,{force:true});console.log('Copied: '+a+' to '+b)\"",
|
|
63
|
-
"
|
|
64
|
-
"lint": "oxlint --config .oxlintrc.json --fix .",
|
|
63
|
+
"scaffold": "scaffold",
|
|
64
|
+
"lint": "oxlint --config .oxlintrc.json --fix --deny-warnings .",
|
|
65
65
|
"check": "tsc --noEmit --project tsconfig.json && npm run check:src",
|
|
66
66
|
"check:src": "npm run check:src:core && npm run check:src:browser && npm run check:src:server",
|
|
67
67
|
"check:src:core": "tsc --noEmit -p configs/src/tsconfig.core.json",
|
|
@@ -69,12 +69,13 @@
|
|
|
69
69
|
"check:src:server": "tsc --noEmit -p configs/src/tsconfig.server.json",
|
|
70
70
|
"format": "oxfmt --config .oxfmtrc.json --write .",
|
|
71
71
|
"format:check": "oxfmt --config .oxfmtrc.json --check .",
|
|
72
|
-
"lint:check": "oxlint --config .oxlintrc.json .",
|
|
73
|
-
"test": "npm run test:src && npm run test:guides",
|
|
72
|
+
"lint:check": "oxlint --config .oxlintrc.json --deny-warnings .",
|
|
73
|
+
"test": "npm run test:src && npm run test:policy && npm run test:guides",
|
|
74
74
|
"test:src": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:core --project src:browser --project src:server",
|
|
75
75
|
"test:src:core": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:core",
|
|
76
76
|
"test:src:browser": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:browser",
|
|
77
77
|
"test:src:server": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:server",
|
|
78
|
+
"test:policy": "vitest run --config vite.config.ts --no-cache --reporter=dot --project policy",
|
|
78
79
|
"test:guides": "vitest run --config vite.config.ts --reporter=dot --project guides",
|
|
79
80
|
"build": "npm run clean && npm run build:src",
|
|
80
81
|
"build:src": "npm run build:src:core && npm run build:src:browser && npm run build:src:server",
|
|
@@ -84,27 +85,29 @@
|
|
|
84
85
|
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test"
|
|
85
86
|
},
|
|
86
87
|
"dependencies": {
|
|
87
|
-
"@orkestrel/abort": "^0.0.
|
|
88
|
-
"@orkestrel/budget": "^0.0.
|
|
89
|
-
"@orkestrel/contract": "^0.0.
|
|
90
|
-
"@orkestrel/database": "^0.0.
|
|
91
|
-
"@orkestrel/emitter": "^0.0.
|
|
92
|
-
"@orkestrel/queue": "^0.0.
|
|
93
|
-
"@orkestrel/timeout": "^0.0.
|
|
88
|
+
"@orkestrel/abort": "^0.0.4",
|
|
89
|
+
"@orkestrel/budget": "^0.0.4",
|
|
90
|
+
"@orkestrel/contract": "^0.0.8",
|
|
91
|
+
"@orkestrel/database": "^0.0.6",
|
|
92
|
+
"@orkestrel/emitter": "^0.0.4",
|
|
93
|
+
"@orkestrel/queue": "^0.0.4",
|
|
94
|
+
"@orkestrel/timeout": "^0.0.4"
|
|
94
95
|
},
|
|
95
96
|
"devDependencies": {
|
|
96
|
-
"@microsoft/api-extractor": "^7.58.
|
|
97
|
-
"@orkestrel/guide": "^0.0.
|
|
98
|
-
"@
|
|
97
|
+
"@microsoft/api-extractor": "^7.58.12",
|
|
98
|
+
"@orkestrel/guide": "^0.0.7",
|
|
99
|
+
"@orkestrel/scaffold": "^0.0.7",
|
|
100
|
+
"@types/node": "^26.1.2",
|
|
99
101
|
"@vitest/browser-playwright": "^4.1.10",
|
|
100
|
-
"oxfmt": "^0.
|
|
101
|
-
"oxlint": "^1.
|
|
102
|
+
"oxfmt": "^0.61.0",
|
|
103
|
+
"oxlint": "^1.76.0",
|
|
104
|
+
"playwright": "^1.62.0",
|
|
102
105
|
"typescript": "^6.0.3",
|
|
103
106
|
"vite": "^8.1.5",
|
|
104
107
|
"vite-plugin-dts": "^5.0.3",
|
|
105
108
|
"vitest": "^4.1.10"
|
|
106
109
|
},
|
|
107
110
|
"engines": {
|
|
108
|
-
"node": ">=22"
|
|
111
|
+
"node": ">=22.12.0"
|
|
109
112
|
}
|
|
110
113
|
}
|