@autonome-research/thread-phase 3.2.1 → 3.3.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.
- package/dist/agent/parse-json.d.ts +4 -0
- package/dist/agent/parse-json.d.ts.map +1 -1
- package/dist/agent/parse-json.js +65 -11
- package/dist/agent/parse-json.js.map +1 -1
- package/dist/agents/serialize-error.d.ts +10 -1
- package/dist/agents/serialize-error.d.ts.map +1 -1
- package/dist/agents/serialize-error.js +42 -3
- package/dist/agents/serialize-error.js.map +1 -1
- package/dist/cache.d.ts +13 -2
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +36 -6
- package/dist/cache.js.map +1 -1
- package/dist/internal/sleep.d.ts +17 -0
- package/dist/internal/sleep.d.ts.map +1 -0
- package/dist/internal/sleep.js +51 -0
- package/dist/internal/sleep.js.map +1 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +7 -1
- package/dist/orchestrator.js.map +1 -1
- package/dist/patterns/bounded-fanout-of.d.ts.map +1 -1
- package/dist/patterns/bounded-fanout-of.js +10 -4
- package/dist/patterns/bounded-fanout-of.js.map +1 -1
- package/dist/patterns/parallel-phases.d.ts +7 -2
- package/dist/patterns/parallel-phases.d.ts.map +1 -1
- package/dist/patterns/parallel-phases.js +70 -16
- package/dist/patterns/parallel-phases.js.map +1 -1
- package/dist/patterns/sub-pipeline.d.ts.map +1 -1
- package/dist/patterns/sub-pipeline.js +10 -6
- package/dist/patterns/sub-pipeline.js.map +1 -1
- package/dist/patterns/with-retry.d.ts.map +1 -1
- package/dist/patterns/with-retry.js +5 -2
- package/dist/patterns/with-retry.js.map +1 -1
- package/dist/triggers/run-trigger.d.ts.map +1 -1
- package/dist/triggers/run-trigger.js +4 -1
- package/dist/triggers/run-trigger.js.map +1 -1
- package/dist/triggers/timer-trigger.d.ts +13 -1
- package/dist/triggers/timer-trigger.d.ts.map +1 -1
- package/dist/triggers/timer-trigger.js +35 -3
- package/dist/triggers/timer-trigger.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* the first {...} object from surrounding prose. Returns the supplied fallback
|
|
4
4
|
* on parse failure.
|
|
5
5
|
*
|
|
6
|
+
* Contract: this function MUST NOT throw. Adversarial agent output (deeply
|
|
7
|
+
* nested structures, malformed prose, megabytes of text) returns the
|
|
8
|
+
* fallback and reports via `onError` (or `console.warn` if no handler).
|
|
9
|
+
*
|
|
6
10
|
* Note on the silent-fallback behavior: when the agent's output was truncated
|
|
7
11
|
* (i.e. `AgentRunResult.finishReason === 'length'`), this almost always
|
|
8
12
|
* fails to parse. Callers should branch on `finishReason` BEFORE trusting
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-json.d.ts","sourceRoot":"","sources":["../../src/agent/parse-json.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"parse-json.d.ts","sourceRoot":"","sources":["../../src/agent/parse-json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAYH,wBAAgB,SAAS,CAAC,CAAC,EACzB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,EACX,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,KAAK,IAAI,GAC9C,CAAC,CAsBH"}
|
package/dist/agent/parse-json.js
CHANGED
|
@@ -3,29 +3,83 @@
|
|
|
3
3
|
* the first {...} object from surrounding prose. Returns the supplied fallback
|
|
4
4
|
* on parse failure.
|
|
5
5
|
*
|
|
6
|
+
* Contract: this function MUST NOT throw. Adversarial agent output (deeply
|
|
7
|
+
* nested structures, malformed prose, megabytes of text) returns the
|
|
8
|
+
* fallback and reports via `onError` (or `console.warn` if no handler).
|
|
9
|
+
*
|
|
6
10
|
* Note on the silent-fallback behavior: when the agent's output was truncated
|
|
7
11
|
* (i.e. `AgentRunResult.finishReason === 'length'`), this almost always
|
|
8
12
|
* fails to parse. Callers should branch on `finishReason` BEFORE trusting
|
|
9
13
|
* the parsed value — otherwise truncation is invisible.
|
|
10
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* V8's `JSON.parse` is recursive — input nested past ~10k levels can blow
|
|
17
|
+
* the stack with a `RangeError` that is NOT reliably catchable from the
|
|
18
|
+
* surrounding try/catch (the throw happens at a depth where the catch
|
|
19
|
+
* frame itself can't run). We pre-check the candidate string's brace
|
|
20
|
+
* nesting and bail to fallback before calling `JSON.parse` when nesting
|
|
21
|
+
* is adversarially deep. Realistic agent output never approaches this.
|
|
22
|
+
*/
|
|
23
|
+
const MAX_PARSE_DEPTH = 1000;
|
|
11
24
|
export function parseJSON(text, fallback, onError) {
|
|
12
|
-
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
13
|
-
const braced = text.match(/(\{[\s\S]*\})/);
|
|
14
|
-
const jsonStr = fenced ? fenced[1] : braced ? braced[1] : text;
|
|
15
25
|
try {
|
|
16
|
-
|
|
26
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
27
|
+
const braced = text.match(/(\{[\s\S]*\})/);
|
|
28
|
+
const jsonStr = (fenced ? fenced[1] : braced ? braced[1] : text).trim();
|
|
29
|
+
if (exceedsMaxDepth(jsonStr, MAX_PARSE_DEPTH)) {
|
|
30
|
+
return report(text, new RangeError(`parseJSON: input nesting exceeds MAX_PARSE_DEPTH=${MAX_PARSE_DEPTH}`), fallback, onError);
|
|
31
|
+
}
|
|
32
|
+
return JSON.parse(jsonStr);
|
|
17
33
|
}
|
|
18
34
|
catch (err) {
|
|
19
|
-
const preview = text.slice(0, 200);
|
|
20
35
|
const errObj = err instanceof Error ? err : new Error(String(err));
|
|
21
|
-
|
|
22
|
-
|
|
36
|
+
return report(text, errObj, fallback, onError);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function report(text, err, fallback, onError) {
|
|
40
|
+
const preview = text.slice(0, 200);
|
|
41
|
+
if (onError) {
|
|
42
|
+
onError(preview, err);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// eslint-disable-next-line no-console
|
|
46
|
+
console.warn(`[parseJSON] failed to parse agent output, using fallback. Preview: "${preview}..."`, err);
|
|
47
|
+
}
|
|
48
|
+
return fallback;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* O(n) scan tracking string/escape state so braces inside string literals
|
|
52
|
+
* don't inflate the depth count. Returns true as soon as `max` is exceeded —
|
|
53
|
+
* does NOT walk the full input once the verdict is decided.
|
|
54
|
+
*/
|
|
55
|
+
function exceedsMaxDepth(s, max) {
|
|
56
|
+
let depth = 0;
|
|
57
|
+
let inString = false;
|
|
58
|
+
let escaped = false;
|
|
59
|
+
for (let i = 0; i < s.length; i++) {
|
|
60
|
+
const c = s.charCodeAt(i);
|
|
61
|
+
if (escaped) {
|
|
62
|
+
escaped = false;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (inString) {
|
|
66
|
+
if (c === 0x5c /* \ */)
|
|
67
|
+
escaped = true;
|
|
68
|
+
else if (c === 0x22 /* " */)
|
|
69
|
+
inString = false;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (c === 0x22 /* " */) {
|
|
73
|
+
inString = true;
|
|
74
|
+
}
|
|
75
|
+
else if (c === 0x7b /* { */ || c === 0x5b /* [ */) {
|
|
76
|
+
if (++depth > max)
|
|
77
|
+
return true;
|
|
23
78
|
}
|
|
24
|
-
else {
|
|
25
|
-
|
|
26
|
-
console.warn(`[parseJSON] failed to parse agent output, using fallback. Preview: "${preview}..."`, err);
|
|
79
|
+
else if (c === 0x7d /* } */ || c === 0x5d /* ] */) {
|
|
80
|
+
depth--;
|
|
27
81
|
}
|
|
28
|
-
return fallback;
|
|
29
82
|
}
|
|
83
|
+
return false;
|
|
30
84
|
}
|
|
31
85
|
//# sourceMappingURL=parse-json.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-json.js","sourceRoot":"","sources":["../../src/agent/parse-json.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"parse-json.js","sourceRoot":"","sources":["../../src/agent/parse-json.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,15 +3,24 @@
|
|
|
3
3
|
* that adapters emit on `error` events. Walks the `cause` chain so wrapped
|
|
4
4
|
* errors don't lose context across the subprocess boundary.
|
|
5
5
|
*
|
|
6
|
+
* Contract: MUST NOT throw — adapters call this on every error before
|
|
7
|
+
* emitting an `error` event, so a throw here would crash the adapter
|
|
8
|
+
* harness instead of producing the documented payload.
|
|
9
|
+
*
|
|
6
10
|
* @internal
|
|
7
11
|
*/
|
|
8
12
|
import type { SerializableError } from './protocol.js';
|
|
9
13
|
/**
|
|
10
14
|
* Normalize any thrown value to a `SerializableError`. Non-`Error` throws
|
|
11
|
-
* become `{ name: 'NonError', message:
|
|
15
|
+
* become `{ name: 'NonError', message: <stringified> }` so the field shape
|
|
12
16
|
* stays uniform — consumers can rely on `name` and `message` always being
|
|
13
17
|
* present.
|
|
14
18
|
*
|
|
19
|
+
* Cycles in `Error.cause` chains (which can occur in real code via wrapped
|
|
20
|
+
* errors that re-reference the original) are detected via a visited set
|
|
21
|
+
* and surface as `{ name: 'CycleDetected', ... }` instead of recursing
|
|
22
|
+
* past the JS stack limit.
|
|
23
|
+
*
|
|
15
24
|
* @internal
|
|
16
25
|
*/
|
|
17
26
|
export declare function serializeError(err: unknown): SerializableError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize-error.d.ts","sourceRoot":"","sources":["../../src/agents/serialize-error.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"serialize-error.d.ts","sourceRoot":"","sources":["../../src/agents/serialize-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,CAE9D"}
|
|
@@ -3,25 +3,64 @@
|
|
|
3
3
|
* that adapters emit on `error` events. Walks the `cause` chain so wrapped
|
|
4
4
|
* errors don't lose context across the subprocess boundary.
|
|
5
5
|
*
|
|
6
|
+
* Contract: MUST NOT throw — adapters call this on every error before
|
|
7
|
+
* emitting an `error` event, so a throw here would crash the adapter
|
|
8
|
+
* harness instead of producing the documented payload.
|
|
9
|
+
*
|
|
6
10
|
* @internal
|
|
7
11
|
*/
|
|
8
12
|
/**
|
|
9
13
|
* Normalize any thrown value to a `SerializableError`. Non-`Error` throws
|
|
10
|
-
* become `{ name: 'NonError', message:
|
|
14
|
+
* become `{ name: 'NonError', message: <stringified> }` so the field shape
|
|
11
15
|
* stays uniform — consumers can rely on `name` and `message` always being
|
|
12
16
|
* present.
|
|
13
17
|
*
|
|
18
|
+
* Cycles in `Error.cause` chains (which can occur in real code via wrapped
|
|
19
|
+
* errors that re-reference the original) are detected via a visited set
|
|
20
|
+
* and surface as `{ name: 'CycleDetected', ... }` instead of recursing
|
|
21
|
+
* past the JS stack limit.
|
|
22
|
+
*
|
|
14
23
|
* @internal
|
|
15
24
|
*/
|
|
16
25
|
export function serializeError(err) {
|
|
26
|
+
return serializeInner(err, new WeakSet());
|
|
27
|
+
}
|
|
28
|
+
function serializeInner(err, seen) {
|
|
17
29
|
if (err instanceof Error) {
|
|
30
|
+
if (seen.has(err)) {
|
|
31
|
+
return {
|
|
32
|
+
name: 'CycleDetected',
|
|
33
|
+
message: `cyclic Error.cause reference back to ${err.name}: ${err.message}`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
seen.add(err);
|
|
18
37
|
return {
|
|
19
38
|
name: err.name,
|
|
20
39
|
message: err.message,
|
|
21
40
|
stack: err.stack,
|
|
22
|
-
cause: err.cause !== undefined ?
|
|
41
|
+
cause: err.cause !== undefined ? serializeInner(err.cause, seen) : undefined,
|
|
23
42
|
};
|
|
24
43
|
}
|
|
25
|
-
return { name: 'NonError', message:
|
|
44
|
+
return { name: 'NonError', message: safeStringify(err) };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Stringify an arbitrary value without ever propagating an exception.
|
|
48
|
+
*
|
|
49
|
+
* Hostile inputs — Proxies whose traps throw, objects with throwing
|
|
50
|
+
* `Symbol.toPrimitive` or `toString`, getter chains that recurse — would
|
|
51
|
+
* otherwise crash this function and break the adapter safety-net invariant.
|
|
52
|
+
*/
|
|
53
|
+
function safeStringify(v) {
|
|
54
|
+
try {
|
|
55
|
+
return String(v);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
try {
|
|
59
|
+
return Object.prototype.toString.call(v);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return '<unserializable>';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
26
65
|
}
|
|
27
66
|
//# sourceMappingURL=serialize-error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize-error.js","sourceRoot":"","sources":["../../src/agents/serialize-error.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"serialize-error.js","sourceRoot":"","sources":["../../src/agents/serialize-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,OAAO,cAAc,CAAC,GAAG,EAAE,IAAI,OAAO,EAAU,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,cAAc,CACrB,GAAY,EACZ,IAAqB;IAErB,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClB,OAAO;gBACL,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,wCAAwC,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE;aAC5E,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,OAAO;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,KAAK,EACH,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SACxE,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,CAAU;IAC/B,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC;YACH,OAAO,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,kBAAkB,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/cache.d.ts
CHANGED
|
@@ -13,13 +13,24 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export declare class PipelineCache {
|
|
15
15
|
private readonly store;
|
|
16
|
+
private readonly pending;
|
|
16
17
|
private readonly prefix;
|
|
17
|
-
constructor(store?: Map<string, unknown>, prefix?: string);
|
|
18
|
+
constructor(store?: Map<string, unknown>, prefix?: string, pending?: Map<string, Promise<unknown>>);
|
|
18
19
|
private k;
|
|
19
20
|
get<T>(key: string): T | undefined;
|
|
20
21
|
set(key: string, value: unknown): void;
|
|
21
22
|
has(key: string): boolean;
|
|
22
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Cache-or-fetch.
|
|
25
|
+
*
|
|
26
|
+
* Concurrent callers on the same key share one in-flight fetch — the
|
|
27
|
+
* second-through-Nth caller awaits the first caller's promise instead of
|
|
28
|
+
* each running its own. This preserves the cache's central invariant
|
|
29
|
+
* (one fetch per key per pipeline run) under fanout.
|
|
30
|
+
*
|
|
31
|
+
* On fetcher rejection the pending entry is evicted so the next caller
|
|
32
|
+
* may retry; rejections are NOT cached.
|
|
33
|
+
*/
|
|
23
34
|
getOrFetch<T>(key: string, fetcher: () => Promise<T>): Promise<T>;
|
|
24
35
|
/**
|
|
25
36
|
* Clear cache entries.
|
package/dist/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAG9B,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,MAAM,GAAE,MAAW,EACnB,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAOzC,OAAO,CAAC,CAAC;IAIT,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAIlC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAItC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB;;;;;;;;;;OAUG;IACG,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsBvE;;;;;;;OAOG;IACH,KAAK,IAAI,IAAI;IAcb;;;OAGG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;;;OAMG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa;CAIvC"}
|
package/dist/cache.js
CHANGED
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export class PipelineCache {
|
|
15
15
|
store;
|
|
16
|
+
pending;
|
|
16
17
|
prefix;
|
|
17
|
-
constructor(store, prefix = '') {
|
|
18
|
+
constructor(store, prefix = '', pending) {
|
|
18
19
|
this.store = store ?? new Map();
|
|
20
|
+
this.pending = pending ?? new Map();
|
|
19
21
|
this.prefix = prefix;
|
|
20
22
|
}
|
|
21
23
|
k(key) {
|
|
@@ -30,15 +32,38 @@ export class PipelineCache {
|
|
|
30
32
|
has(key) {
|
|
31
33
|
return this.store.has(this.k(key));
|
|
32
34
|
}
|
|
33
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Cache-or-fetch.
|
|
37
|
+
*
|
|
38
|
+
* Concurrent callers on the same key share one in-flight fetch — the
|
|
39
|
+
* second-through-Nth caller awaits the first caller's promise instead of
|
|
40
|
+
* each running its own. This preserves the cache's central invariant
|
|
41
|
+
* (one fetch per key per pipeline run) under fanout.
|
|
42
|
+
*
|
|
43
|
+
* On fetcher rejection the pending entry is evicted so the next caller
|
|
44
|
+
* may retry; rejections are NOT cached.
|
|
45
|
+
*/
|
|
34
46
|
async getOrFetch(key, fetcher) {
|
|
35
47
|
const fullKey = this.k(key);
|
|
36
48
|
if (this.store.has(fullKey)) {
|
|
37
49
|
return this.store.get(fullKey);
|
|
38
50
|
}
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
const inflight = this.pending.get(fullKey);
|
|
52
|
+
if (inflight) {
|
|
53
|
+
return inflight;
|
|
54
|
+
}
|
|
55
|
+
const p = (async () => {
|
|
56
|
+
try {
|
|
57
|
+
const value = await fetcher();
|
|
58
|
+
this.store.set(fullKey, value);
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
this.pending.delete(fullKey);
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
this.pending.set(fullKey, p);
|
|
66
|
+
return p;
|
|
42
67
|
}
|
|
43
68
|
/**
|
|
44
69
|
* Clear cache entries.
|
|
@@ -51,12 +76,17 @@ export class PipelineCache {
|
|
|
51
76
|
clear() {
|
|
52
77
|
if (!this.prefix) {
|
|
53
78
|
this.store.clear();
|
|
79
|
+
this.pending.clear();
|
|
54
80
|
return;
|
|
55
81
|
}
|
|
56
82
|
for (const k of [...this.store.keys()]) {
|
|
57
83
|
if (k.startsWith(this.prefix))
|
|
58
84
|
this.store.delete(k);
|
|
59
85
|
}
|
|
86
|
+
for (const k of [...this.pending.keys()]) {
|
|
87
|
+
if (k.startsWith(this.prefix))
|
|
88
|
+
this.pending.delete(k);
|
|
89
|
+
}
|
|
60
90
|
}
|
|
61
91
|
/**
|
|
62
92
|
* Total entries in the underlying store, across all namespaces. This is
|
|
@@ -75,7 +105,7 @@ export class PipelineCache {
|
|
|
75
105
|
namespace(name) {
|
|
76
106
|
if (!name)
|
|
77
107
|
throw new Error('PipelineCache.namespace: name must be non-empty');
|
|
78
|
-
return new PipelineCache(this.store, `${this.prefix}${name}
|
|
108
|
+
return new PipelineCache(this.store, `${this.prefix}${name}:`, this.pending);
|
|
79
109
|
}
|
|
80
110
|
}
|
|
81
111
|
//# sourceMappingURL=cache.js.map
|
package/dist/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,OAAO,aAAa;IACP,KAAK,CAAuB;IAC5B,MAAM,CAAS;IAEhC,
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal — shared abortable-sleep helper.
|
|
3
|
+
*
|
|
4
|
+
* Replaces `new Promise(r => setTimeout(r, ms))` everywhere a delay needs to
|
|
5
|
+
* honor an `AbortSignal`. Without this, cancellation has to wait for the
|
|
6
|
+
* full delay before surfacing — which makes retry backoffs, timer polling,
|
|
7
|
+
* and structured backoff loops un-cancellable in practice.
|
|
8
|
+
*
|
|
9
|
+
* Rejects with a `DOMException(name: 'AbortError')` when the signal aborts.
|
|
10
|
+
* Resolves normally when the timer elapses. Cleans up its listener in both
|
|
11
|
+
* paths so signals attached to long-lived AbortControllers don't leak.
|
|
12
|
+
*
|
|
13
|
+
* Not exported from the package surface. Pattern wrappers (with-retry,
|
|
14
|
+
* timer-trigger, agent runner) consume it directly.
|
|
15
|
+
*/
|
|
16
|
+
export declare function abortableSleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=sleep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../src/internal/sleep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAef"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal — shared abortable-sleep helper.
|
|
3
|
+
*
|
|
4
|
+
* Replaces `new Promise(r => setTimeout(r, ms))` everywhere a delay needs to
|
|
5
|
+
* honor an `AbortSignal`. Without this, cancellation has to wait for the
|
|
6
|
+
* full delay before surfacing — which makes retry backoffs, timer polling,
|
|
7
|
+
* and structured backoff loops un-cancellable in practice.
|
|
8
|
+
*
|
|
9
|
+
* Rejects with a `DOMException(name: 'AbortError')` when the signal aborts.
|
|
10
|
+
* Resolves normally when the timer elapses. Cleans up its listener in both
|
|
11
|
+
* paths so signals attached to long-lived AbortControllers don't leak.
|
|
12
|
+
*
|
|
13
|
+
* Not exported from the package surface. Pattern wrappers (with-retry,
|
|
14
|
+
* timer-trigger, agent runner) consume it directly.
|
|
15
|
+
*/
|
|
16
|
+
export function abortableSleep(ms, signal) {
|
|
17
|
+
if (signal?.aborted) {
|
|
18
|
+
return Promise.reject(toAbortError(signal.reason));
|
|
19
|
+
}
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const timer = setTimeout(() => {
|
|
22
|
+
signal?.removeEventListener('abort', onAbort);
|
|
23
|
+
resolve();
|
|
24
|
+
}, ms);
|
|
25
|
+
const onAbort = () => {
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
reject(toAbortError(signal?.reason));
|
|
28
|
+
};
|
|
29
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function toAbortError(reason) {
|
|
33
|
+
// Prefer the native DOMException(AbortError) so callers using
|
|
34
|
+
// `e.name === 'AbortError'` (the platform idiom) keep working.
|
|
35
|
+
if (typeof DOMException === 'function') {
|
|
36
|
+
const message = typeof reason === 'string'
|
|
37
|
+
? reason
|
|
38
|
+
: reason instanceof Error
|
|
39
|
+
? reason.message
|
|
40
|
+
: 'aborted';
|
|
41
|
+
return new DOMException(message, 'AbortError');
|
|
42
|
+
}
|
|
43
|
+
const err = new Error(typeof reason === 'string'
|
|
44
|
+
? reason
|
|
45
|
+
: reason instanceof Error
|
|
46
|
+
? reason.message
|
|
47
|
+
: 'aborted');
|
|
48
|
+
err.name = 'AbortError';
|
|
49
|
+
return err;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=sleep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep.js","sourceRoot":"","sources":["../../src/internal/sleep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,UAAU,cAAc,CAC5B,EAAU,EACV,MAAoB;IAEpB,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,OAAO,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY,CAAC,MAAe;IACnC,8DAA8D;IAC9D,+DAA+D;IAC/D,IAAI,OAAO,YAAY,KAAK,UAAU,EAAE,CAAC;QACvC,MAAM,OAAO,GACX,OAAO,MAAM,KAAK,QAAQ;YACxB,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,MAAM,YAAY,KAAK;gBACvB,CAAC,CAAC,MAAM,CAAC,OAAO;gBAChB,CAAC,CAAC,SAAS,CAAC;QAClB,OAAO,IAAI,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,KAAK,CACnB,OAAO,MAAM,KAAK,QAAQ;QACxB,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,MAAM,YAAY,KAAK;YACvB,CAAC,CAAC,MAAM,CAAC,OAAO;YAChB,CAAC,CAAC,SAAS,CAChB,CAAC;IACF,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC;IACxB,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5E;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IACzC,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAuB,WAAW,CAChC,IAAI,SAAS,mBAAmB,EAChC,MAAM,GAAG,aAAa,EAEtB,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAC1C,GAAG,EAAE,IAAI,EACT,OAAO,CAAC,EAAE,kBAAkB,GAC3B,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5E;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IACzC,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAuB,WAAW,CAChC,IAAI,SAAS,mBAAmB,EAChC,MAAM,GAAG,aAAa,EAEtB,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAC1C,GAAG,EAAE,IAAI,EACT,OAAO,CAAC,EAAE,kBAAkB,GAC3B,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CA2B9B;AAED;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,SAAS,mBAAmB,EAChC,MAAM,SAAS,aAAa,GAAG,aAAa,EAE5C,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAC1C,GAAG,EAAE,IAAI,EACT,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,eAAe,CAAC,CAY1B"}
|
package/dist/orchestrator.js
CHANGED
|
@@ -15,7 +15,13 @@
|
|
|
15
15
|
* downstream pattern is `type MyEvent = PipelineEvent | { type: 'mine' };`.
|
|
16
16
|
*/
|
|
17
17
|
export async function* runPipeline(phases, ctx, options) {
|
|
18
|
-
|
|
18
|
+
// Signal resolution: options.signal wins if provided; otherwise honor a
|
|
19
|
+
// pre-set ctx.signal (callers that already populated ctx — including
|
|
20
|
+
// run-trigger and tests building ctx by hand). Whichever wins is assigned
|
|
21
|
+
// back to ctx so phases observing ctx.signal see the same value the
|
|
22
|
+
// orchestrator uses for its between-phase abort check.
|
|
23
|
+
const signal = options?.signal ?? ctx.signal;
|
|
24
|
+
ctx.signal = signal;
|
|
19
25
|
try {
|
|
20
26
|
for (const phase of phases) {
|
|
21
27
|
if (signal?.aborted) {
|
package/dist/orchestrator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAgCH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,WAAW,CAIhC,MAA0C,EAC1C,GAAS,EACT,OAA4B;IAE5B,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAgCH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,WAAW,CAIhC,MAA0C,EAC1C,GAAS,EACT,OAA4B;IAE5B,wEAAwE;IACxE,qEAAqE;IACrE,0EAA0E;IAC1E,oEAAoE;IACpE,uDAAuD;IACvD,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC;IAC7C,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,MAAM,MAAM,GACT,MAAM,CAAC,MAA6B,IAAI,SAAS,CAAC;gBACrD,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;gBACxD,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC;gBACxB,MAAM,GAAG,CAAC;YACZ,CAAC;YACD,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;gBACb,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,EAAuB,CAAC;gBACrE,OAAO;YACT,CAAC;QACH,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAuB,CAAC;IAC9C,CAAC;YAAS,CAAC;QACT,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAIxC,MAA0C,EAC1C,GAAS,EACT,OAA4B;IAE5B,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAA8B,CAAC;IACnC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,WAAW,CAAe,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC;QAC1E,UAAU,EAAE,CAAC;QACb,IAAK,KAAuB,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC7C,UAAU,GAAI,KAA6B,CAAC,MAAM,CAAC;QACrD,CAAC;IACH,CAAC;IACD,OAAO,UAAU,KAAK,SAAS;QAC7B,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE;QACvD,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;AAC1C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounded-fanout-of.d.ts","sourceRoot":"","sources":["../../src/patterns/bounded-fanout-of.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EAEb,cAAc,EACf,MAAM,uBAAuB,CAAC;AAE/B,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE1D,gBAAgB;AAChB,MAAM,WAAW,sBAAsB,CAAC,KAAK,EAAE,OAAO;IACpD,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACnC,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,oEAAoE;IACpE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,oFAAoF;IACpF,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;CAC5E;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,OAAO,EAClD,IAAI,EAAE,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,GAC3C,OAAO,CAAC,cAAc,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"bounded-fanout-of.d.ts","sourceRoot":"","sources":["../../src/patterns/bounded-fanout-of.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EAEb,cAAc,EACf,MAAM,uBAAuB,CAAC;AAE/B,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE1D,gBAAgB;AAChB,MAAM,WAAW,sBAAsB,CAAC,KAAK,EAAE,OAAO;IACpD,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACnC,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,oEAAoE;IACpE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,oFAAoF;IACpF,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;CAC5E;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,OAAO,EAClD,IAAI,EAAE,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,GAC3C,OAAO,CAAC,cAAc,EAAE,CAAC,CAsH3B;AAED,gBAAgB;AAChB,qBAAa,oBAAqB,SAAQ,KAAK;IAEpC,SAAS,EAAE,MAAM;IACjB,MAAM,EAAE,cAAc;gBADtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,cAAc;CAKhC"}
|
|
@@ -40,11 +40,17 @@ export async function boundedFanoutOf(opts) {
|
|
|
40
40
|
const inFlight = new Set();
|
|
41
41
|
let failed = null;
|
|
42
42
|
let cursor = 0;
|
|
43
|
-
const abortAllInFlight = () => {
|
|
44
|
-
|
|
43
|
+
const abortAllInFlight = async () => {
|
|
44
|
+
const snapshot = [...inFlight];
|
|
45
|
+
for (const entry of snapshot) {
|
|
45
46
|
entry.controller.abort();
|
|
46
47
|
entry.run.abort('boundedFanoutOf fail-fast');
|
|
47
48
|
}
|
|
49
|
+
// Wait for every aborted run's `result` to settle so adapter-side cleanup
|
|
50
|
+
// (timers, sockets, subprocesses) finishes before the fanout returns.
|
|
51
|
+
// We allSettled because the runs may reject as `aborted` and we don't
|
|
52
|
+
// want to mask the original failure with the abort-induced one.
|
|
53
|
+
await Promise.allSettled(snapshot.map((e) => e.result));
|
|
48
54
|
};
|
|
49
55
|
const worker = async () => {
|
|
50
56
|
while (true) {
|
|
@@ -66,7 +72,7 @@ export async function boundedFanoutOf(opts) {
|
|
|
66
72
|
eventBus: opts.eventBus,
|
|
67
73
|
traceId: opts.traceId,
|
|
68
74
|
});
|
|
69
|
-
const entry = { controller: itemController, run };
|
|
75
|
+
const entry = { controller: itemController, run, result: run.result };
|
|
70
76
|
inFlight.add(entry);
|
|
71
77
|
// The adapter's events iterable is intentionally NOT consumed here.
|
|
72
78
|
// The event bus is the multi-subscriber seam; double-iteration would
|
|
@@ -84,7 +90,7 @@ export async function boundedFanoutOf(opts) {
|
|
|
84
90
|
if (!failed) {
|
|
85
91
|
failed = { index: i, result };
|
|
86
92
|
opts.onItemError?.(item, i, result);
|
|
87
|
-
abortAllInFlight();
|
|
93
|
+
await abortAllInFlight();
|
|
88
94
|
}
|
|
89
95
|
return;
|
|
90
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounded-fanout-of.js","sourceRoot":"","sources":["../../src/patterns/bounded-fanout-of.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA+BH;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAA4C;IAE5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1E,MAAM,IAAI,GAAwB,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC;IAC3D,MAAM,OAAO,GAAsC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"bounded-fanout-of.js","sourceRoot":"","sources":["../../src/patterns/bounded-fanout-of.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,8 +12,13 @@
|
|
|
12
12
|
* same field, last-write-wins. Keep branches' ctx writes disjoint.
|
|
13
13
|
* - Events from all branches interleave into the composite phase's
|
|
14
14
|
* output stream in arrival order.
|
|
15
|
-
* - If a sub-phase throws,
|
|
16
|
-
*
|
|
15
|
+
* - If a sub-phase throws, sibling branches are cooperatively cancelled
|
|
16
|
+
* via an internal `AbortSignal` composed onto each branch's `ctx.signal`.
|
|
17
|
+
* Siblings still need to OBSERVE their signal to short-circuit awaits —
|
|
18
|
+
* between yields they bail immediately via the queue's error flag.
|
|
19
|
+
* - If the outer `ctx.signal` aborts, every branch's composed signal
|
|
20
|
+
* aborts too. The composite then re-throws the first error encountered
|
|
21
|
+
* (or the outer abort).
|
|
17
22
|
* - If a sub-phase sets `ctx.stop`, sibling branches still run to
|
|
18
23
|
* completion. The orchestrator's stop check fires AFTER the composite
|
|
19
24
|
* phase returns, halting subsequent top-level phases.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parallel-phases.d.ts","sourceRoot":"","sources":["../../src/patterns/parallel-phases.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"parallel-phases.d.ts","sourceRoot":"","sources":["../../src/patterns/parallel-phases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE7E,wBAAgB,cAAc,CAC5B,IAAI,SAAS,mBAAmB,EAChC,MAAM,GAAG,aAAa,EAEtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,GACzC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAiFrB"}
|
|
@@ -12,8 +12,13 @@
|
|
|
12
12
|
* same field, last-write-wins. Keep branches' ctx writes disjoint.
|
|
13
13
|
* - Events from all branches interleave into the composite phase's
|
|
14
14
|
* output stream in arrival order.
|
|
15
|
-
* - If a sub-phase throws,
|
|
16
|
-
*
|
|
15
|
+
* - If a sub-phase throws, sibling branches are cooperatively cancelled
|
|
16
|
+
* via an internal `AbortSignal` composed onto each branch's `ctx.signal`.
|
|
17
|
+
* Siblings still need to OBSERVE their signal to short-circuit awaits —
|
|
18
|
+
* between yields they bail immediately via the queue's error flag.
|
|
19
|
+
* - If the outer `ctx.signal` aborts, every branch's composed signal
|
|
20
|
+
* aborts too. The composite then re-throws the first error encountered
|
|
21
|
+
* (or the outer abort).
|
|
17
22
|
* - If a sub-phase sets `ctx.stop`, sibling branches still run to
|
|
18
23
|
* completion. The orchestrator's stop check fires AFTER the composite
|
|
19
24
|
* phase returns, halting subsequent top-level phases.
|
|
@@ -28,6 +33,22 @@ export function parallelPhases(phaseName, phases) {
|
|
|
28
33
|
async *run(ctx) {
|
|
29
34
|
if (phases.length === 0)
|
|
30
35
|
return;
|
|
36
|
+
// Internal controller fans cancellation across branches. When one
|
|
37
|
+
// branch throws (or the outer ctx.signal aborts), every other branch
|
|
38
|
+
// sees its composed ctx.signal flip to aborted — they can observe it
|
|
39
|
+
// in their inner await loops to bail cooperatively.
|
|
40
|
+
const internal = new AbortController();
|
|
41
|
+
const outerSignal = ctx.signal;
|
|
42
|
+
const composedSignal = outerSignal
|
|
43
|
+
? AbortSignal.any([outerSignal, internal.signal])
|
|
44
|
+
: internal.signal;
|
|
45
|
+
const onOuterAbort = () => internal.abort(outerSignal.reason);
|
|
46
|
+
if (outerSignal && !outerSignal.aborted) {
|
|
47
|
+
outerSignal.addEventListener('abort', onOuterAbort, { once: true });
|
|
48
|
+
}
|
|
49
|
+
else if (outerSignal?.aborted) {
|
|
50
|
+
internal.abort(outerSignal.reason);
|
|
51
|
+
}
|
|
31
52
|
// Producer/consumer: each sub-phase pushes events into a shared queue;
|
|
32
53
|
// the composite generator drains the queue and yields downstream.
|
|
33
54
|
const queue = [];
|
|
@@ -41,17 +62,26 @@ export function parallelPhases(phaseName, phases) {
|
|
|
41
62
|
let errored = null;
|
|
42
63
|
let running = phases.length;
|
|
43
64
|
const drain = async (phase) => {
|
|
65
|
+
// Each branch sees the composed signal, not the outer's directly,
|
|
66
|
+
// so sibling errors propagate as cancellation through ctx.signal.
|
|
67
|
+
const branchCtx = withSignal(ctx, composedSignal);
|
|
44
68
|
try {
|
|
45
|
-
for await (const ev of phase.run(
|
|
69
|
+
for await (const ev of phase.run(branchCtx)) {
|
|
46
70
|
queue.push(ev);
|
|
47
71
|
wake();
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
// Do not short-circuit on `errored` — that would close the
|
|
73
|
+
// branch's generator before it had a chance to observe
|
|
74
|
+
// composedSignal cooperatively. Branches that check c.signal
|
|
75
|
+
// bail themselves; branches that don't run to completion (the
|
|
76
|
+
// documented cooperative-cancellation contract).
|
|
50
77
|
}
|
|
51
78
|
}
|
|
52
79
|
catch (err) {
|
|
53
|
-
if (!errored)
|
|
80
|
+
if (!errored) {
|
|
54
81
|
errored = err;
|
|
82
|
+
// First error fires the internal abort so siblings cancel too.
|
|
83
|
+
internal.abort(err);
|
|
84
|
+
}
|
|
55
85
|
}
|
|
56
86
|
finally {
|
|
57
87
|
running--;
|
|
@@ -59,19 +89,43 @@ export function parallelPhases(phaseName, phases) {
|
|
|
59
89
|
}
|
|
60
90
|
};
|
|
61
91
|
const allDone = Promise.all(phases.map(drain));
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
92
|
+
try {
|
|
93
|
+
while (running > 0 || queue.length > 0) {
|
|
94
|
+
if (queue.length === 0) {
|
|
95
|
+
await Promise.race([wait(), allDone]);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
yield queue.shift();
|
|
68
99
|
}
|
|
69
|
-
|
|
100
|
+
await allDone;
|
|
101
|
+
if (errored)
|
|
102
|
+
throw errored;
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
if (outerSignal)
|
|
106
|
+
outerSignal.removeEventListener('abort', onOuterAbort);
|
|
70
107
|
}
|
|
71
|
-
await allDone;
|
|
72
|
-
if (errored)
|
|
73
|
-
throw errored;
|
|
74
108
|
},
|
|
75
109
|
};
|
|
76
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Build a per-branch ctx view whose `signal` is the composed signal.
|
|
113
|
+
* Other fields share the parent ctx (last-write-wins semantics preserved).
|
|
114
|
+
*/
|
|
115
|
+
function withSignal(ctx, signal) {
|
|
116
|
+
// A Proxy keeps writes to ctx visible across all branches (the documented
|
|
117
|
+
// last-write-wins model) while masking `signal` to the composed value.
|
|
118
|
+
return new Proxy(ctx, {
|
|
119
|
+
get(target, prop, receiver) {
|
|
120
|
+
if (prop === 'signal')
|
|
121
|
+
return signal;
|
|
122
|
+
return Reflect.get(target, prop, receiver);
|
|
123
|
+
},
|
|
124
|
+
set(target, prop, value, receiver) {
|
|
125
|
+
if (prop === 'signal')
|
|
126
|
+
return true; // ignore writes to branch-local signal
|
|
127
|
+
return Reflect.set(target, prop, value, receiver);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
77
131
|
//# sourceMappingURL=parallel-phases.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parallel-phases.js","sourceRoot":"","sources":["../../src/patterns/parallel-phases.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"parallel-phases.js","sourceRoot":"","sources":["../../src/patterns/parallel-phases.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sub-pipeline.d.ts","sourceRoot":"","sources":["../../src/patterns/sub-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EACV,mBAAmB,EACnB,KAAK,EAEN,MAAM,aAAa,CAAC;AAGrB;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,CAAC,SAAS,SAAS,mBAAmB,IAC/D;IAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAAC,GAAG,EAAE,SAAS,CAAA;CAAE,GAC3D,CAAC,MAAM;IAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAAC,GAAG,EAAE,SAAS,CAAA;CAAE,GAAG,SAAS,CAAC,CAAC;AAEpF,MAAM,WAAW,kBAAkB,CACjC,SAAS,SAAS,mBAAmB,EACrC,SAAS,SAAS,mBAAmB;IAErC,6DAA6D;IAC7D,QAAQ,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,SAAS,CAAC;IAC3C;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CAC1D;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CACzB,SAAS,SAAS,mBAAmB,EACrC,SAAS,SAAS,mBAAmB,EAErC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,kBAAkB,CAAC,SAAS,EAAE,SAAS,CAAC,GAChD,KAAK,CAAC,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"sub-pipeline.d.ts","sourceRoot":"","sources":["../../src/patterns/sub-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EACV,mBAAmB,EACnB,KAAK,EAEN,MAAM,aAAa,CAAC;AAGrB;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,CAAC,SAAS,SAAS,mBAAmB,IAC/D;IAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAAC,GAAG,EAAE,SAAS,CAAA;CAAE,GAC3D,CAAC,MAAM;IAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAAC,GAAG,EAAE,SAAS,CAAA;CAAE,GAAG,SAAS,CAAC,CAAC;AAEpF,MAAM,WAAW,kBAAkB,CACjC,SAAS,SAAS,mBAAmB,EACrC,SAAS,SAAS,mBAAmB;IAErC,6DAA6D;IAC7D,QAAQ,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,SAAS,CAAC;IAC3C;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CAC1D;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CACzB,SAAS,SAAS,mBAAmB,EACrC,SAAS,SAAS,mBAAmB,EAErC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,kBAAkB,CAAC,SAAS,EAAE,SAAS,CAAC,GAChD,KAAK,CAAC,SAAS,CAAC,CA+BlB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,SAAS,SAAS,mBAAmB,EACrC,SAAS,SAAS,mBAAmB,GAAG,mBAAmB,EAE3D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE;IAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAAC,GAAG,EAAE,SAAS,CAAA;CAAE,EACnE,OAAO,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,UAAU,CAAC,GACnE,KAAK,CAAC,SAAS,CAAC,CAKlB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,SAAS,SAAS,mBAAmB,EAErC,MAAM,EAAE,iBAAiB,CAAC,SAAS,CAAC,EACpC,OAAO,GAAE;IACP,wEAAwE;IACxE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,SAAS,CAAC;CACZ,GACL,OAAO,CAAC;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE;QAAE,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,CA6BhH"}
|
|
@@ -33,10 +33,14 @@ export function subPipeline(name, options) {
|
|
|
33
33
|
const inner = options.mapInput
|
|
34
34
|
? options.mapInput(outer)
|
|
35
35
|
: source.ctx;
|
|
36
|
-
// Isolate the inner pipeline's cache
|
|
36
|
+
// Isolate the inner pipeline's cache so writes can't bleed across
|
|
37
|
+
// nesting levels. Signal propagation is via runPipeline's options —
|
|
38
|
+
// the orchestrator assigns ctx.signal canonically, which avoids the
|
|
39
|
+
// pre-set-then-overwritten footgun at arbitrary depth.
|
|
37
40
|
inner.cache = new PipelineCache();
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
yield* runPipeline(source.phases, inner, {
|
|
42
|
+
signal: outer.signal,
|
|
43
|
+
});
|
|
40
44
|
options.mapOutput?.(outer, inner);
|
|
41
45
|
},
|
|
42
46
|
};
|
|
@@ -70,11 +74,11 @@ export async function runSubPipeline(source, options = {}) {
|
|
|
70
74
|
}
|
|
71
75
|
const inner = options.ctx ?? resolved.ctx;
|
|
72
76
|
inner.cache = new PipelineCache();
|
|
73
|
-
if (options.signal)
|
|
74
|
-
inner.signal = options.signal;
|
|
75
77
|
let eventCount = 0;
|
|
76
78
|
let stopReason;
|
|
77
|
-
for await (const event of runPipeline(resolved.phases, inner
|
|
79
|
+
for await (const event of runPipeline(resolved.phases, inner, {
|
|
80
|
+
signal: options.signal,
|
|
81
|
+
})) {
|
|
78
82
|
eventCount++;
|
|
79
83
|
if (event.type === 'done') {
|
|
80
84
|
stopReason = event.reason;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sub-pipeline.js","sourceRoot":"","sources":["../../src/patterns/sub-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAMjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA+B5C,6DAA6D;AAC7D,MAAM,UAAU,WAAW,CAIzB,IAAY,EACZ,OAAiD;IAEjD,OAAO;QACL,IAAI;QACJ,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK;YACd,MAAM,MAAM,GACV,OAAO,OAAO,CAAC,QAAQ,KAAK,UAAU;gBACpC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE;gBACpB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;YACvB,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CACb,gBAAgB,IAAI,yCAAyC,CAC9D,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ;gBAC5B,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gBACzB,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;YAEf,oEAAoE;
|
|
1
|
+
{"version":3,"file":"sub-pipeline.js","sourceRoot":"","sources":["../../src/patterns/sub-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAMjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA+B5C,6DAA6D;AAC7D,MAAM,UAAU,WAAW,CAIzB,IAAY,EACZ,OAAiD;IAEjD,OAAO;QACL,IAAI;QACJ,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK;YACd,MAAM,MAAM,GACV,OAAO,OAAO,CAAC,QAAQ,KAAK,UAAU;gBACpC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE;gBACpB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;YACvB,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CACb,gBAAgB,IAAI,yCAAyC,CAC9D,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ;gBAC5B,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gBACzB,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;YAEf,kEAAkE;YAClE,oEAAoE;YACpE,oEAAoE;YACpE,uDAAuD;YACtD,KAAkC,CAAC,KAAK,GAAG,IAAI,aAAa,EAAE,CAAC;YAEhE,KAAK,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE;gBACvC,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB,CAAwC,CAAC;YAE1C,OAAO,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAI3B,IAAY,EACZ,MAAmE,EACnE,OAAoE;IAEpE,OAAO,WAAW,CAAuB,IAAI,EAAE;QAC7C,QAAQ,EAAE,MAAM;QAChB,GAAG,OAAO;KACX,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAGlC,MAAoC,EACpC,UAKI,EAAE;IAEN,MAAM,QAAQ,GACZ,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IACnD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC;IACzC,KAAkC,CAAC,KAAK,GAAG,IAAI,aAAa,EAAE,CAAC;IAEhE,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAA8B,CAAC;IAEnC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE;QAC5D,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,EAAE,CAAC;QACH,UAAU,EAAE,CAAC;QACb,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,UAAU,GAAI,KAA6B,CAAC,MAAM,CAAC;QACrD,CAAC;IACH,CAAC;IAED,OAAO;QACL,GAAG,EAAE,KAAK;QACV,OAAO,EACL,UAAU,KAAK,SAAS;YACtB,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE;YACvD,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE;KAC1C,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-retry.d.ts","sourceRoot":"","sources":["../../src/patterns/with-retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAiB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"with-retry.d.ts","sourceRoot":"","sources":["../../src/patterns/with-retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAiB,MAAM,aAAa,CAAC;AAG7E,MAAM,WAAW,gBAAgB,CAAC,IAAI,SAAS,mBAAmB;IAChE,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mHAAmH;IACnH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC;IACpD,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAChE,sFAAsF;IACtF,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,SAAS,CAAC,IAAI,SAAS,mBAAmB,EACxD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,GAAE,gBAAgB,CAAC,IAAI,CAAM,GACnC,KAAK,CAAC,IAAI,CAAC,CA2Db"}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Events emitted by the inner phase pass through unmodified. The wrapper
|
|
21
21
|
* additionally emits `data` events with key `${phase.name}.attempt`.
|
|
22
22
|
*/
|
|
23
|
-
|
|
23
|
+
import { abortableSleep } from '../internal/sleep.js';
|
|
24
24
|
export function withRetry(phase, options = {}) {
|
|
25
25
|
const maxAttempts = options.maxAttempts ?? 3;
|
|
26
26
|
const baseDelay = options.baseDelayMs ?? 1000;
|
|
@@ -65,7 +65,10 @@ export function withRetry(phase, options = {}) {
|
|
|
65
65
|
ctx.stop = stopBefore; // restore upstream stop, if any
|
|
66
66
|
options.resetState?.(ctx);
|
|
67
67
|
const delay = baseDelay * 2 ** (attempt - 1);
|
|
68
|
-
|
|
68
|
+
// ctx.signal is wired by runPipeline; if the caller aborts during a
|
|
69
|
+
// backoff window, surface it immediately instead of waiting out the
|
|
70
|
+
// schedule. AbortError propagates up through runPipeline.
|
|
71
|
+
await abortableSleep(delay, ctx.signal);
|
|
69
72
|
}
|
|
70
73
|
},
|
|
71
74
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-retry.js","sourceRoot":"","sources":["../../src/patterns/with-retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;
|
|
1
|
+
{"version":3,"file":"with-retry.js","sourceRoot":"","sources":["../../src/patterns/with-retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAetD,MAAM,UAAU,SAAS,CACvB,KAAkB,EAClB,UAAkC,EAAE;IAEpC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;IAC9C,MAAM,SAAS,GACb,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IAEvF,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG;YACZ,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;gBACxD,MAAM;oBACJ,IAAI,EAAE,MAAM;oBACZ,GAAG,EAAE,GAAG,KAAK,CAAC,IAAI,UAAU;oBAC5B,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE;iBAChC,CAAC;gBAEF,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC;gBAC5B,IAAI,MAAM,GAAY,SAAS,CAAC;gBAEhC,IAAI,CAAC;oBACH,kEAAkE;oBAClE,mEAAmE;oBACnE,oBAAoB;oBACpB,GAAG,CAAC,IAAI,GAAG,SAAS,CAAC;oBACrB,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACxB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,GAAG,GAAG,CAAC;gBACf,CAAC;gBAED,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBAEtC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,iEAAiE;oBACjE,gEAAgE;oBAChE,mCAAmC;oBACnC,OAAO;gBACT,CAAC;gBAED,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;oBAC5B,+DAA+D;oBAC/D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;wBACzB,MAAM,MAAM,CAAC;oBACf,CAAC;oBACD,wDAAwD;oBACxD,OAAO;gBACT,CAAC;gBAED,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;gBACxC,GAAG,CAAC,IAAI,GAAG,UAAU,CAAC,CAAC,gCAAgC;gBACvD,OAAO,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;gBAE1B,MAAM,KAAK,GAAG,SAAS,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;gBAC7C,oEAAoE;gBACpE,oEAAoE;gBACpE,0DAA0D;gBAC1D,MAAM,cAAc,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-trigger.d.ts","sourceRoot":"","sources":["../../src/triggers/run-trigger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EACV,mBAAmB,EACnB,KAAK,EACN,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,iBAAiB,CAAC,MAAM,EAAE,IAAI,SAAS,mBAAmB;IACzE;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,qDAAqD;IACrD,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9E;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACvD,2EAA2E;IAC3E,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,WAAW,gBAAgB;IAC/B,mGAAmG;IACnG,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,+EAA+E;IAC/E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;CACzC;AAQD,wBAAgB,UAAU,CAAC,MAAM,EAAE,IAAI,SAAS,mBAAmB,EACjE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,EACxB,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK;IAC/D,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,GAAG,EAAE,IAAI,CAAC;CACX,EACD,OAAO,GAAE,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAM,GAC5C,gBAAgB,
|
|
1
|
+
{"version":3,"file":"run-trigger.d.ts","sourceRoot":"","sources":["../../src/triggers/run-trigger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EACV,mBAAmB,EACnB,KAAK,EACN,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,iBAAiB,CAAC,MAAM,EAAE,IAAI,SAAS,mBAAmB;IACzE;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,qDAAqD;IACrD,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9E;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACvD,2EAA2E;IAC3E,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,WAAW,gBAAgB;IAC/B,mGAAmG;IACnG,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,+EAA+E;IAC/E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;CACzC;AAQD,wBAAgB,UAAU,CAAC,MAAM,EAAE,IAAI,SAAS,mBAAmB,EACjE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,EACxB,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK;IAC/D,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,GAAG,EAAE,IAAI,CAAC;CACX,EACD,OAAO,GAAE,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAM,GAC5C,gBAAgB,CAkIlB"}
|
|
@@ -33,7 +33,10 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import { runPipelineToSummary } from '../orchestrator.js';
|
|
35
35
|
export function runTrigger(trigger, pipelineFactory, options = {}) {
|
|
36
|
-
|
|
36
|
+
// Clamp to >=1: 0 or negative would deadlock the backpressure loop on an
|
|
37
|
+
// empty inflight map (Promise.race([]) never settles). Mirrors the clamp
|
|
38
|
+
// at bounded-fanout.ts:121.
|
|
39
|
+
const maxConcurrency = Math.max(1, options.maxConcurrency ?? 1);
|
|
37
40
|
const pipelineName = options.pipelineName ?? trigger.name;
|
|
38
41
|
const onError = options.onError ??
|
|
39
42
|
((event, err) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-trigger.js","sourceRoot":"","sources":["../../src/triggers/run-trigger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AA4D1D,MAAM,UAAU,UAAU,CACxB,OAAwB,EACxB,eAGC,EACD,UAA2C,EAAE;IAE7C,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"run-trigger.js","sourceRoot":"","sources":["../../src/triggers/run-trigger.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* No cron expression support in core — keep the impl tiny. For cron, wrap
|
|
9
9
|
* a cron parser (e.g. `croner`) and produce events on its schedule;
|
|
10
10
|
* `examples/triggers/timer-with-cron.ts` shows the shape.
|
|
11
|
+
*
|
|
12
|
+
* Cancellation contract: `stop()` resolves immediately and aborts the
|
|
13
|
+
* trigger's internal `AbortSignal`. Any in-flight payload factory that
|
|
14
|
+
* accepts the signal can short-circuit. Even payloads that ignore the
|
|
15
|
+
* signal don't block `stop()` — `makeEvent` races the payload promise
|
|
16
|
+
* against the abort so `start()` returns promptly.
|
|
11
17
|
*/
|
|
12
18
|
import type { Trigger, TriggerEvent } from './types.js';
|
|
13
19
|
export interface TimerTriggerOptions<TInput = void> {
|
|
@@ -17,8 +23,12 @@ export interface TimerTriggerOptions<TInput = void> {
|
|
|
17
23
|
* Payload to attach to each event. Defaults to `undefined`. If a
|
|
18
24
|
* function, called each fire to produce a fresh payload (e.g. the
|
|
19
25
|
* current time, a counter, a snapshot from somewhere).
|
|
26
|
+
*
|
|
27
|
+
* Async factories receive the trigger's internal `AbortSignal` so they
|
|
28
|
+
* may honor `stop()` cooperatively. The signature `(signal?) => ...`
|
|
29
|
+
* keeps existing callers working unchanged.
|
|
20
30
|
*/
|
|
21
|
-
payload?: TInput | (() => TInput) | (() => Promise<TInput>);
|
|
31
|
+
payload?: TInput | ((signal?: AbortSignal) => TInput) | ((signal?: AbortSignal) => Promise<TInput>);
|
|
22
32
|
/**
|
|
23
33
|
* If true, fires immediately on `start()` before the first interval
|
|
24
34
|
* elapses. Default: false (first event arrives after one interval).
|
|
@@ -36,9 +46,11 @@ export declare class TimerTrigger<TInput = void> implements Trigger<TInput> {
|
|
|
36
46
|
private stopped;
|
|
37
47
|
private pendingTimer;
|
|
38
48
|
private notifyStop;
|
|
49
|
+
private readonly aborter;
|
|
39
50
|
constructor(options: TimerTriggerOptions<TInput>);
|
|
40
51
|
start(): AsyncGenerator<TriggerEvent<TInput>, void>;
|
|
41
52
|
stop(): Promise<void>;
|
|
53
|
+
private makeEventOrStop;
|
|
42
54
|
private makeEvent;
|
|
43
55
|
/** Returns true if the interval elapsed normally, false if stop() fired first. */
|
|
44
56
|
private waitOrStop;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timer-trigger.d.ts","sourceRoot":"","sources":["../../src/triggers/timer-trigger.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"timer-trigger.d.ts","sourceRoot":"","sources":["../../src/triggers/timer-trigger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,mBAAmB,CAAC,MAAM,GAAG,IAAI;IAChD,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,OAAO,CAAC,EACJ,MAAM,GACN,CAAC,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,GAClC,CAAC,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAChD;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,qBAAa,YAAY,CAAC,MAAM,GAAG,IAAI,CAAE,YAAW,OAAO,CAAC,MAAM,CAAC;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IACjE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAE1C,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;gBAErC,OAAO,EAAE,mBAAmB,CAAC,MAAM,CAAC;IAOzC,KAAK,IAAI,cAAc,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC;IAmBpD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAgBb,eAAe;YAaf,SAAS;IAevB,kFAAkF;IAClF,OAAO,CAAC,UAAU;CAUnB"}
|
|
@@ -8,7 +8,14 @@
|
|
|
8
8
|
* No cron expression support in core — keep the impl tiny. For cron, wrap
|
|
9
9
|
* a cron parser (e.g. `croner`) and produce events on its schedule;
|
|
10
10
|
* `examples/triggers/timer-with-cron.ts` shows the shape.
|
|
11
|
+
*
|
|
12
|
+
* Cancellation contract: `stop()` resolves immediately and aborts the
|
|
13
|
+
* trigger's internal `AbortSignal`. Any in-flight payload factory that
|
|
14
|
+
* accepts the signal can short-circuit. Even payloads that ignore the
|
|
15
|
+
* signal don't block `stop()` — `makeEvent` races the payload promise
|
|
16
|
+
* against the abort so `start()` returns promptly.
|
|
11
17
|
*/
|
|
18
|
+
const STOP_SENTINEL = Symbol('TimerTrigger.stop');
|
|
12
19
|
export class TimerTrigger {
|
|
13
20
|
name;
|
|
14
21
|
intervalMs;
|
|
@@ -18,6 +25,7 @@ export class TimerTrigger {
|
|
|
18
25
|
stopped = false;
|
|
19
26
|
pendingTimer = null;
|
|
20
27
|
notifyStop = null;
|
|
28
|
+
aborter = new AbortController();
|
|
21
29
|
constructor(options) {
|
|
22
30
|
this.intervalMs = options.intervalMs;
|
|
23
31
|
this.payload = options.payload;
|
|
@@ -28,7 +36,10 @@ export class TimerTrigger {
|
|
|
28
36
|
if (this.stopped)
|
|
29
37
|
return;
|
|
30
38
|
if (this.fireImmediately) {
|
|
31
|
-
|
|
39
|
+
const ev = await this.makeEventOrStop();
|
|
40
|
+
if (ev === STOP_SENTINEL)
|
|
41
|
+
return;
|
|
42
|
+
yield ev;
|
|
32
43
|
if (this.stopped)
|
|
33
44
|
return;
|
|
34
45
|
}
|
|
@@ -36,7 +47,10 @@ export class TimerTrigger {
|
|
|
36
47
|
const waited = await this.waitOrStop();
|
|
37
48
|
if (!waited)
|
|
38
49
|
return;
|
|
39
|
-
|
|
50
|
+
const ev = await this.makeEventOrStop();
|
|
51
|
+
if (ev === STOP_SENTINEL)
|
|
52
|
+
return;
|
|
53
|
+
yield ev;
|
|
40
54
|
}
|
|
41
55
|
}
|
|
42
56
|
async stop() {
|
|
@@ -49,10 +63,28 @@ export class TimerTrigger {
|
|
|
49
63
|
}
|
|
50
64
|
this.notifyStop?.();
|
|
51
65
|
this.notifyStop = null;
|
|
66
|
+
// Aborting unblocks makeEventOrStop's race and lets cooperative payload
|
|
67
|
+
// factories cancel any in-flight work they own.
|
|
68
|
+
if (!this.aborter.signal.aborted) {
|
|
69
|
+
this.aborter.abort('TimerTrigger.stop');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async makeEventOrStop() {
|
|
73
|
+
const payloadPromise = this.makeEvent();
|
|
74
|
+
if (this.aborter.signal.aborted)
|
|
75
|
+
return STOP_SENTINEL;
|
|
76
|
+
const stopPromise = new Promise((resolve) => {
|
|
77
|
+
const onAbort = () => resolve(STOP_SENTINEL);
|
|
78
|
+
this.aborter.signal.addEventListener('abort', onAbort, { once: true });
|
|
79
|
+
});
|
|
80
|
+
const winner = await Promise.race([payloadPromise, stopPromise]);
|
|
81
|
+
// If stop won, drop the payload result on the floor (it may still resolve
|
|
82
|
+
// later, but no consumer is waiting and the trigger has shut down).
|
|
83
|
+
return winner;
|
|
52
84
|
}
|
|
53
85
|
async makeEvent() {
|
|
54
86
|
const input = typeof this.payload === 'function'
|
|
55
|
-
? await this.payload()
|
|
87
|
+
? await this.payload(this.aborter.signal)
|
|
56
88
|
: this.payload;
|
|
57
89
|
return {
|
|
58
90
|
id: ++this.seq,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timer-trigger.js","sourceRoot":"","sources":["../../src/triggers/timer-trigger.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"timer-trigger.js","sourceRoot":"","sources":["../../src/triggers/timer-trigger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autonome-research/thread-phase",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "TypeScript substrate for building automation workflows that coordinate AI agents. Phase ordering, typed shared state, persistence, fanout, event flow.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|