footprintjs 4.16.0 → 4.17.2
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/AGENTS.md +567 -82
- package/CLAUDE.md +57 -0
- package/README.md +2 -0
- package/dist/advanced.js +4 -2
- package/dist/detach.js +78 -0
- package/dist/esm/advanced.js +2 -1
- package/dist/esm/detach.js +57 -0
- package/dist/esm/lib/builder/FlowChartBuilder.js +171 -61
- package/dist/esm/lib/contract/openapi.js +4 -5
- package/dist/esm/lib/contract/schema.js +115 -4
- package/dist/esm/lib/decide/decide.js +4 -5
- package/dist/esm/lib/decide/evidence.js +3 -2
- package/dist/esm/lib/detach/drivers/immediate.js +66 -0
- package/dist/esm/lib/detach/drivers/microtaskBatch.js +113 -0
- package/dist/esm/lib/detach/drivers/sendBeacon.js +78 -0
- package/dist/esm/lib/detach/drivers/setImmediate.js +81 -0
- package/dist/esm/lib/detach/drivers/setTimeout.js +69 -0
- package/dist/esm/lib/detach/drivers/workerThread.js +117 -0
- package/dist/esm/lib/detach/flush.js +91 -0
- package/dist/esm/lib/detach/handle.js +134 -0
- package/dist/esm/lib/detach/registry.js +97 -0
- package/dist/esm/lib/detach/runChild.js +40 -0
- package/dist/esm/lib/detach/spawn.js +86 -0
- package/dist/esm/lib/detach/types.js +37 -0
- package/dist/esm/lib/engine/errors/errorInfo.js +4 -4
- package/dist/esm/lib/engine/graph/StageNode.js +2 -2
- package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +9 -8
- package/dist/esm/lib/engine/handlers/ContinuationResolver.js +12 -9
- package/dist/esm/lib/engine/handlers/DeciderHandler.js +7 -8
- package/dist/esm/lib/engine/handlers/ExtractorRunner.js +14 -8
- package/dist/esm/lib/engine/handlers/NodeResolver.js +5 -3
- package/dist/esm/lib/engine/handlers/RuntimeStructureManager.js +11 -14
- package/dist/esm/lib/engine/handlers/SelectorHandler.js +9 -9
- package/dist/esm/lib/engine/handlers/StageRunner.js +9 -11
- package/dist/esm/lib/engine/handlers/SubflowExecutor.js +13 -12
- package/dist/esm/lib/engine/handlers/SubflowInputMapper.js +4 -4
- package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +85 -96
- package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +18 -36
- package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +6 -5
- package/dist/esm/lib/engine/narrative/recorders/AdaptiveNarrativeFlowRecorder.js +7 -6
- package/dist/esm/lib/engine/narrative/recorders/ManifestFlowRecorder.js +10 -10
- package/dist/esm/lib/engine/narrative/recorders/MilestoneNarrativeFlowRecorder.js +5 -3
- package/dist/esm/lib/engine/narrative/recorders/ProgressiveNarrativeFlowRecorder.js +4 -3
- package/dist/esm/lib/engine/narrative/recorders/RLENarrativeFlowRecorder.js +4 -4
- package/dist/esm/lib/engine/narrative/recorders/SeparateNarrativeFlowRecorder.js +5 -6
- package/dist/esm/lib/engine/narrative/recorders/SilentNarrativeFlowRecorder.js +5 -6
- package/dist/esm/lib/engine/narrative/recorders/WindowedNarrativeFlowRecorder.js +5 -3
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +97 -71
- package/dist/esm/lib/memory/DiagnosticCollector.js +6 -8
- package/dist/esm/lib/memory/EventLog.js +5 -3
- package/dist/esm/lib/memory/SharedMemory.js +3 -2
- package/dist/esm/lib/memory/StageContext.js +44 -14
- package/dist/esm/lib/memory/TransactionBuffer.js +9 -8
- package/dist/esm/lib/memory/backtrack.js +3 -4
- package/dist/esm/lib/memory/commitLogUtils.js +2 -2
- package/dist/esm/lib/memory/utils.js +2 -3
- package/dist/esm/lib/pause/types.js +33 -14
- package/dist/esm/lib/reactive/createTypedScope.js +10 -8
- package/dist/esm/lib/reactive/types.js +3 -1
- package/dist/esm/lib/recorder/BoundaryStateTracker.js +263 -0
- package/dist/esm/lib/recorder/CompositeRecorder.js +3 -1
- package/dist/esm/lib/recorder/InOutRecorder.js +5 -6
- package/dist/esm/lib/recorder/KeyedRecorder.js +2 -4
- package/dist/esm/lib/recorder/QualityRecorder.js +15 -14
- package/dist/esm/lib/recorder/SequenceRecorder.js +11 -12
- package/dist/esm/lib/recorder/TopologyRecorder.js +36 -40
- package/dist/esm/lib/recorder/index.js +2 -1
- package/dist/esm/lib/recorder/qualityTrace.js +4 -5
- package/dist/esm/lib/runner/ExecutionRuntime.js +20 -4
- package/dist/esm/lib/runner/FlowChartExecutor.js +99 -55
- package/dist/esm/lib/runner/RunContext.js +5 -3
- package/dist/esm/lib/runner/RunnableChart.js +7 -9
- package/dist/esm/lib/runner/getSubtreeSnapshot.js +4 -5
- package/dist/esm/lib/schema/errors.js +4 -3
- package/dist/esm/lib/schema/validate.js +4 -5
- package/dist/esm/lib/scope/ScopeFacade.js +52 -35
- package/dist/esm/lib/scope/providers/baseStateCompatible.js +9 -9
- package/dist/esm/lib/scope/providers/guards.js +2 -2
- package/dist/esm/lib/scope/recorders/DebugRecorder.js +9 -7
- package/dist/esm/lib/scope/recorders/MetricRecorder.js +10 -8
- package/dist/esm/lib/scope/state/zod/defineScopeFromZod.js +2 -3
- package/dist/esm/lib/scope/state/zod/resolver.js +2 -3
- package/dist/esm/lib/scope/state/zod/scopeFactory.js +16 -20
- package/dist/esm/lib/scope/state/zod/utils/validateHelper.js +57 -14
- package/dist/esm/trace.js +4 -1
- package/dist/lib/builder/FlowChartBuilder.js +171 -61
- package/dist/lib/contract/openapi.js +4 -5
- package/dist/lib/contract/schema.js +115 -4
- package/dist/lib/decide/decide.js +4 -5
- package/dist/lib/decide/evidence.js +3 -2
- package/dist/lib/detach/drivers/immediate.js +70 -0
- package/dist/lib/detach/drivers/microtaskBatch.js +117 -0
- package/dist/lib/detach/drivers/sendBeacon.js +82 -0
- package/dist/lib/detach/drivers/setImmediate.js +85 -0
- package/dist/lib/detach/drivers/setTimeout.js +73 -0
- package/dist/lib/detach/drivers/workerThread.js +121 -0
- package/dist/lib/detach/flush.js +95 -0
- package/dist/lib/detach/handle.js +140 -0
- package/dist/lib/detach/registry.js +106 -0
- package/dist/lib/detach/runChild.js +67 -0
- package/dist/lib/detach/spawn.js +92 -0
- package/dist/lib/detach/types.js +38 -0
- package/dist/lib/engine/errors/errorInfo.js +4 -4
- package/dist/lib/engine/graph/StageNode.js +2 -2
- package/dist/lib/engine/handlers/ChildrenExecutor.js +9 -8
- package/dist/lib/engine/handlers/ContinuationResolver.js +12 -9
- package/dist/lib/engine/handlers/DeciderHandler.js +7 -8
- package/dist/lib/engine/handlers/ExtractorRunner.js +14 -8
- package/dist/lib/engine/handlers/NodeResolver.js +5 -3
- package/dist/lib/engine/handlers/RuntimeStructureManager.js +11 -14
- package/dist/lib/engine/handlers/SelectorHandler.js +9 -9
- package/dist/lib/engine/handlers/StageRunner.js +9 -11
- package/dist/lib/engine/handlers/SubflowExecutor.js +13 -12
- package/dist/lib/engine/handlers/SubflowInputMapper.js +4 -4
- package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +85 -96
- package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +18 -36
- package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +6 -5
- package/dist/lib/engine/narrative/recorders/AdaptiveNarrativeFlowRecorder.js +7 -6
- package/dist/lib/engine/narrative/recorders/ManifestFlowRecorder.js +10 -10
- package/dist/lib/engine/narrative/recorders/MilestoneNarrativeFlowRecorder.js +5 -3
- package/dist/lib/engine/narrative/recorders/ProgressiveNarrativeFlowRecorder.js +4 -3
- package/dist/lib/engine/narrative/recorders/RLENarrativeFlowRecorder.js +4 -4
- package/dist/lib/engine/narrative/recorders/SeparateNarrativeFlowRecorder.js +5 -6
- package/dist/lib/engine/narrative/recorders/SilentNarrativeFlowRecorder.js +5 -6
- package/dist/lib/engine/narrative/recorders/WindowedNarrativeFlowRecorder.js +5 -3
- package/dist/lib/engine/traversal/FlowchartTraverser.js +97 -71
- package/dist/lib/memory/DiagnosticCollector.js +6 -8
- package/dist/lib/memory/EventLog.js +5 -3
- package/dist/lib/memory/SharedMemory.js +3 -2
- package/dist/lib/memory/StageContext.js +44 -14
- package/dist/lib/memory/TransactionBuffer.js +9 -8
- package/dist/lib/memory/backtrack.js +3 -4
- package/dist/lib/memory/commitLogUtils.js +2 -2
- package/dist/lib/memory/utils.js +2 -3
- package/dist/lib/pause/types.js +33 -14
- package/dist/lib/reactive/createTypedScope.js +10 -8
- package/dist/lib/reactive/types.js +3 -1
- package/dist/lib/recorder/BoundaryStateTracker.js +267 -0
- package/dist/lib/recorder/CompositeRecorder.js +3 -1
- package/dist/lib/recorder/InOutRecorder.js +5 -6
- package/dist/lib/recorder/KeyedRecorder.js +2 -4
- package/dist/lib/recorder/QualityRecorder.js +15 -14
- package/dist/lib/recorder/SequenceRecorder.js +11 -12
- package/dist/lib/recorder/TopologyRecorder.js +36 -40
- package/dist/lib/recorder/index.js +4 -2
- package/dist/lib/recorder/qualityTrace.js +4 -5
- package/dist/lib/runner/ExecutionRuntime.js +20 -4
- package/dist/lib/runner/FlowChartExecutor.js +99 -55
- package/dist/lib/runner/RunContext.js +5 -3
- package/dist/lib/runner/RunnableChart.js +7 -9
- package/dist/lib/runner/getSubtreeSnapshot.js +4 -5
- package/dist/lib/schema/errors.js +4 -3
- package/dist/lib/schema/validate.js +4 -5
- package/dist/lib/scope/ScopeFacade.js +52 -35
- package/dist/lib/scope/providers/baseStateCompatible.js +9 -9
- package/dist/lib/scope/providers/guards.js +2 -2
- package/dist/lib/scope/recorders/DebugRecorder.js +9 -7
- package/dist/lib/scope/recorders/MetricRecorder.js +10 -8
- package/dist/lib/scope/state/zod/defineScopeFromZod.js +2 -3
- package/dist/lib/scope/state/zod/resolver.js +2 -3
- package/dist/lib/scope/state/zod/scopeFactory.js +16 -20
- package/dist/lib/scope/state/zod/utils/validateHelper.js +57 -14
- package/dist/trace.js +6 -2
- package/dist/types/advanced.d.ts +1 -0
- package/dist/types/detach.d.ts +59 -0
- package/dist/types/lib/builder/FlowChartBuilder.d.ts +81 -0
- package/dist/types/lib/detach/drivers/immediate.d.ts +39 -0
- package/dist/types/lib/detach/drivers/microtaskBatch.d.ts +57 -0
- package/dist/types/lib/detach/drivers/sendBeacon.d.ts +38 -0
- package/dist/types/lib/detach/drivers/setImmediate.d.ts +32 -0
- package/dist/types/lib/detach/drivers/setTimeout.d.ts +34 -0
- package/dist/types/lib/detach/drivers/workerThread.d.ts +50 -0
- package/dist/types/lib/detach/flush.d.ts +62 -0
- package/dist/types/lib/detach/handle.d.ts +83 -0
- package/dist/types/lib/detach/registry.d.ts +82 -0
- package/dist/types/lib/detach/runChild.d.ts +41 -0
- package/dist/types/lib/detach/spawn.d.ts +64 -0
- package/dist/types/lib/detach/types.d.ts +200 -0
- package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +0 -1
- package/dist/types/lib/engine/types.d.ts +0 -1
- package/dist/types/lib/reactive/types.d.ts +4 -0
- package/dist/types/lib/recorder/BoundaryStateTracker.d.ts +215 -0
- package/dist/types/lib/recorder/index.d.ts +1 -0
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +28 -0
- package/dist/types/lib/scope/ScopeFacade.d.ts +4 -0
- package/dist/types/lib/scope/state/zod/utils/validateHelper.d.ts +13 -1
- package/dist/types/trace.d.ts +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detach/drivers/microtaskBatch.ts — Batch detached work into ONE microtask.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: Producer-consumer with batched flush. Same shape as
|
|
5
|
+
* agentfootprint's `EventDispatcher` flush queue and the React
|
|
6
|
+
* reconciler's microtask scheduling — accumulate during the
|
|
7
|
+
* current sync slice, drain at the next microtask boundary.
|
|
8
|
+
* Role: Default driver for in-process detach. Cheapest scheduling
|
|
9
|
+
* primitive on V8/JSC: one `queueMicrotask` per batch
|
|
10
|
+
* regardless of how many work items, so the perf budget
|
|
11
|
+
* amortizes. Suitable for browser AND node AND edge runtimes
|
|
12
|
+
* (queueMicrotask is universal since 2018).
|
|
13
|
+
*
|
|
14
|
+
* Lifecycle:
|
|
15
|
+
*
|
|
16
|
+
* schedule(child, input, refId) ← driver entry
|
|
17
|
+
* └─ create handle (queued)
|
|
18
|
+
* └─ register in detachRegistry
|
|
19
|
+
* └─ push work item onto local queue
|
|
20
|
+
* └─ if no microtask scheduled yet → queueMicrotask(flush)
|
|
21
|
+
* └─ return handle (sync — passive recorder rule)
|
|
22
|
+
*
|
|
23
|
+
* flush() (microtask) ← deferred
|
|
24
|
+
* └─ swap out queue (drain races safely)
|
|
25
|
+
* └─ for each item: _markRunning, await runChild, _markDone/_markFailed
|
|
26
|
+
* └─ unregister handle from detachRegistry
|
|
27
|
+
*
|
|
28
|
+
* Why microtask (and not setImmediate / setTimeout):
|
|
29
|
+
* - Microtasks run BEFORE returning to the event loop — guarantees
|
|
30
|
+
* the work finishes within the current "tick" if the runtime allows
|
|
31
|
+
* - Lowest possible deferral cost (~50ns on modern V8)
|
|
32
|
+
* - Works in EVERY JS runtime (browser, node, deno, bun, edge)
|
|
33
|
+
* - Doesn't require any timer infrastructure → no GC pressure
|
|
34
|
+
*
|
|
35
|
+
* Re-entrancy:
|
|
36
|
+
* - If `runChild` calls `schedule()` for nested detach, the new item
|
|
37
|
+
* lands on the SAME queue. Because `scheduled` flips back to false
|
|
38
|
+
* at the start of `flush`, the new item triggers a fresh microtask.
|
|
39
|
+
* - Worst-case: O(n) microtasks for n nested levels. Acceptable —
|
|
40
|
+
* real-world detach trees are shallow.
|
|
41
|
+
*/
|
|
42
|
+
import { asImpl, createHandle } from '../handle.js';
|
|
43
|
+
import { register, unregister } from '../registry.js';
|
|
44
|
+
import { defaultRunChild } from '../runChild.js';
|
|
45
|
+
/**
|
|
46
|
+
* Build a microtask-batch driver wired to a custom child runner. Most
|
|
47
|
+
* consumers want the default singleton `microtaskBatchDriver` instead;
|
|
48
|
+
* this factory exists for tests and for advanced consumers who want to
|
|
49
|
+
* inject their own runner (e.g., a runner that wraps the child in a
|
|
50
|
+
* tracing context).
|
|
51
|
+
*/
|
|
52
|
+
export function createMicrotaskBatchDriver(runChild = defaultRunChild) {
|
|
53
|
+
// Per-driver-instance queue and flush guard. Closed over by `schedule`
|
|
54
|
+
// and `flush` so each call to `createMicrotaskBatchDriver` gets its
|
|
55
|
+
// own isolated batch (test isolation, multi-tenant scenarios).
|
|
56
|
+
const queue = [];
|
|
57
|
+
let scheduled = false;
|
|
58
|
+
function flush() {
|
|
59
|
+
// Reset BEFORE draining so re-entrant schedule()s during runChild
|
|
60
|
+
// queue a fresh microtask instead of joining the in-flight drain.
|
|
61
|
+
scheduled = false;
|
|
62
|
+
const items = queue.splice(0);
|
|
63
|
+
for (const item of items) {
|
|
64
|
+
// Each item runs concurrently — no awaits here, so the outer
|
|
65
|
+
// for-loop completes within this microtask. Errors inside
|
|
66
|
+
// `executeOne` are routed to the handle, not thrown. The promise
|
|
67
|
+
// is intentionally not awaited; ignore-promise-returned via the
|
|
68
|
+
// explicit no-op .then() pattern that the project's lint config
|
|
69
|
+
// accepts (vs `void`, which `no-void` rejects).
|
|
70
|
+
executeOne(item, runChild).then(undefined, undefined);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
name: 'microtask-batch',
|
|
75
|
+
capabilities: { browserSafe: true, nodeSafe: true, edgeSafe: true },
|
|
76
|
+
schedule(child, input, refId) {
|
|
77
|
+
const handle = createHandle(refId);
|
|
78
|
+
register(handle);
|
|
79
|
+
queue.push({ child, input, handle });
|
|
80
|
+
if (!scheduled) {
|
|
81
|
+
scheduled = true;
|
|
82
|
+
queueMicrotask(flush);
|
|
83
|
+
}
|
|
84
|
+
return handle;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Per-item execution. Marks the handle running, awaits the runner,
|
|
90
|
+
* routes outcome to the handle, cleans up the registry entry. Never
|
|
91
|
+
* throws — errors land on the handle (passive recorder rule).
|
|
92
|
+
*/
|
|
93
|
+
async function executeOne(item, runChild) {
|
|
94
|
+
const impl = asImpl(item.handle);
|
|
95
|
+
impl._markRunning();
|
|
96
|
+
try {
|
|
97
|
+
const result = await runChild(item.child, item.input);
|
|
98
|
+
impl._markDone(result);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
impl._markFailed(err instanceof Error ? err : new Error(String(err)));
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
unregister(impl.id);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Default singleton. Most consumers import this and pass it to
|
|
109
|
+
* `executor.detachAndJoinLater(child, input, { driver: microtaskBatchDriver })`
|
|
110
|
+
* (or rely on it being the executor's default driver, set in T5b).
|
|
111
|
+
*/
|
|
112
|
+
export const microtaskBatchDriver = createMicrotaskBatchDriver();
|
|
113
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWljcm90YXNrQmF0Y2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2RldGFjaC9kcml2ZXJzL21pY3JvdGFza0JhdGNoLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0NHO0FBR0gsT0FBTyxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDcEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN0RCxPQUFPLEVBQW9CLGVBQWUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBU25FOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSwwQkFBMEIsQ0FBQyxXQUF3QixlQUFlO0lBQ2hGLHVFQUF1RTtJQUN2RSxvRUFBb0U7SUFDcEUsK0RBQStEO0lBQy9ELE1BQU0sS0FBSyxHQUFlLEVBQUUsQ0FBQztJQUM3QixJQUFJLFNBQVMsR0FBRyxLQUFLLENBQUM7SUFFdEIsU0FBUyxLQUFLO1FBQ1osa0VBQWtFO1FBQ2xFLGtFQUFrRTtRQUNsRSxTQUFTLEdBQUcsS0FBSyxDQUFDO1FBQ2xCLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDOUIsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUN6Qiw2REFBNkQ7WUFDN0QsMERBQTBEO1lBQzFELGlFQUFpRTtZQUNqRSxnRUFBZ0U7WUFDaEUsZ0VBQWdFO1lBQ2hFLGdEQUFnRDtZQUNoRCxVQUFVLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDeEQsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPO1FBQ0wsSUFBSSxFQUFFLGlCQUFpQjtRQUN2QixZQUFZLEVBQUUsRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRTtRQUNuRSxRQUFRLENBQUMsS0FBZ0IsRUFBRSxLQUFjLEVBQUUsS0FBYTtZQUN0RCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDbkMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ2pCLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7WUFDckMsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO2dCQUNmLFNBQVMsR0FBRyxJQUFJLENBQUM7Z0JBQ2pCLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUN4QixDQUFDO1lBQ0QsT0FBTyxNQUFNLENBQUM7UUFDaEIsQ0FBQztLQUNGLENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILEtBQUssVUFBVSxVQUFVLENBQUMsSUFBYyxFQUFFLFFBQXFCO0lBQzdELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDakMsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ3BCLElBQUksQ0FBQztRQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sUUFBUSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3RELElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDekIsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN4RSxDQUFDO1lBQVMsQ0FBQztRQUNULFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDdEIsQ0FBQztBQUNILENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sb0JBQW9CLEdBQWlCLDBCQUEwQixFQUFFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGRldGFjaC9kcml2ZXJzL21pY3JvdGFza0JhdGNoLnRzIOKAlCBCYXRjaCBkZXRhY2hlZCB3b3JrIGludG8gT05FIG1pY3JvdGFzay5cbiAqXG4gKiBQYXR0ZXJuOiAgUHJvZHVjZXItY29uc3VtZXIgd2l0aCBiYXRjaGVkIGZsdXNoLiBTYW1lIHNoYXBlIGFzXG4gKiAgICAgICAgICAgYWdlbnRmb290cHJpbnQncyBgRXZlbnREaXNwYXRjaGVyYCBmbHVzaCBxdWV1ZSBhbmQgdGhlIFJlYWN0XG4gKiAgICAgICAgICAgcmVjb25jaWxlcidzIG1pY3JvdGFzayBzY2hlZHVsaW5nIOKAlCBhY2N1bXVsYXRlIGR1cmluZyB0aGVcbiAqICAgICAgICAgICBjdXJyZW50IHN5bmMgc2xpY2UsIGRyYWluIGF0IHRoZSBuZXh0IG1pY3JvdGFzayBib3VuZGFyeS5cbiAqIFJvbGU6ICAgICBEZWZhdWx0IGRyaXZlciBmb3IgaW4tcHJvY2VzcyBkZXRhY2guIENoZWFwZXN0IHNjaGVkdWxpbmdcbiAqICAgICAgICAgICBwcmltaXRpdmUgb24gVjgvSlNDOiBvbmUgYHF1ZXVlTWljcm90YXNrYCBwZXIgYmF0Y2hcbiAqICAgICAgICAgICByZWdhcmRsZXNzIG9mIGhvdyBtYW55IHdvcmsgaXRlbXMsIHNvIHRoZSBwZXJmIGJ1ZGdldFxuICogICAgICAgICAgIGFtb3J0aXplcy4gU3VpdGFibGUgZm9yIGJyb3dzZXIgQU5EIG5vZGUgQU5EIGVkZ2UgcnVudGltZXNcbiAqICAgICAgICAgICAocXVldWVNaWNyb3Rhc2sgaXMgdW5pdmVyc2FsIHNpbmNlIDIwMTgpLlxuICpcbiAqIExpZmVjeWNsZTpcbiAqXG4gKiAgIHNjaGVkdWxlKGNoaWxkLCBpbnB1dCwgcmVmSWQpICAgICAgICAgICAg4oaQIGRyaXZlciBlbnRyeVxuICogICAgIOKUlOKUgCBjcmVhdGUgaGFuZGxlIChxdWV1ZWQpXG4gKiAgICAg4pSU4pSAIHJlZ2lzdGVyIGluIGRldGFjaFJlZ2lzdHJ5XG4gKiAgICAg4pSU4pSAIHB1c2ggd29yayBpdGVtIG9udG8gbG9jYWwgcXVldWVcbiAqICAgICDilJTilIAgaWYgbm8gbWljcm90YXNrIHNjaGVkdWxlZCB5ZXQg4oaSIHF1ZXVlTWljcm90YXNrKGZsdXNoKVxuICogICAgIOKUlOKUgCByZXR1cm4gaGFuZGxlIChzeW5jIOKAlCBwYXNzaXZlIHJlY29yZGVyIHJ1bGUpXG4gKlxuICogICBmbHVzaCgpIChtaWNyb3Rhc2spICAgICAgICAgICAgICAgICAgICAgICDihpAgZGVmZXJyZWRcbiAqICAgICDilJTilIAgc3dhcCBvdXQgcXVldWUgKGRyYWluIHJhY2VzIHNhZmVseSlcbiAqICAgICDilJTilIAgZm9yIGVhY2ggaXRlbTogX21hcmtSdW5uaW5nLCBhd2FpdCBydW5DaGlsZCwgX21hcmtEb25lL19tYXJrRmFpbGVkXG4gKiAgICAg4pSU4pSAIHVucmVnaXN0ZXIgaGFuZGxlIGZyb20gZGV0YWNoUmVnaXN0cnlcbiAqXG4gKiBXaHkgbWljcm90YXNrIChhbmQgbm90IHNldEltbWVkaWF0ZSAvIHNldFRpbWVvdXQpOlxuICogICAtIE1pY3JvdGFza3MgcnVuIEJFRk9SRSByZXR1cm5pbmcgdG8gdGhlIGV2ZW50IGxvb3Ag4oCUIGd1YXJhbnRlZXNcbiAqICAgICB0aGUgd29yayBmaW5pc2hlcyB3aXRoaW4gdGhlIGN1cnJlbnQgXCJ0aWNrXCIgaWYgdGhlIHJ1bnRpbWUgYWxsb3dzXG4gKiAgIC0gTG93ZXN0IHBvc3NpYmxlIGRlZmVycmFsIGNvc3QgKH41MG5zIG9uIG1vZGVybiBWOClcbiAqICAgLSBXb3JrcyBpbiBFVkVSWSBKUyBydW50aW1lIChicm93c2VyLCBub2RlLCBkZW5vLCBidW4sIGVkZ2UpXG4gKiAgIC0gRG9lc24ndCByZXF1aXJlIGFueSB0aW1lciBpbmZyYXN0cnVjdHVyZSDihpIgbm8gR0MgcHJlc3N1cmVcbiAqXG4gKiBSZS1lbnRyYW5jeTpcbiAqICAgLSBJZiBgcnVuQ2hpbGRgIGNhbGxzIGBzY2hlZHVsZSgpYCBmb3IgbmVzdGVkIGRldGFjaCwgdGhlIG5ldyBpdGVtXG4gKiAgICAgbGFuZHMgb24gdGhlIFNBTUUgcXVldWUuIEJlY2F1c2UgYHNjaGVkdWxlZGAgZmxpcHMgYmFjayB0byBmYWxzZVxuICogICAgIGF0IHRoZSBzdGFydCBvZiBgZmx1c2hgLCB0aGUgbmV3IGl0ZW0gdHJpZ2dlcnMgYSBmcmVzaCBtaWNyb3Rhc2suXG4gKiAgIC0gV29yc3QtY2FzZTogTyhuKSBtaWNyb3Rhc2tzIGZvciBuIG5lc3RlZCBsZXZlbHMuIEFjY2VwdGFibGUg4oCUXG4gKiAgICAgcmVhbC13b3JsZCBkZXRhY2ggdHJlZXMgYXJlIHNoYWxsb3cuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBGbG93Q2hhcnQgfSBmcm9tICcuLi8uLi9idWlsZGVyL3R5cGVzLmpzJztcbmltcG9ydCB7IGFzSW1wbCwgY3JlYXRlSGFuZGxlIH0gZnJvbSAnLi4vaGFuZGxlLmpzJztcbmltcG9ydCB7IHJlZ2lzdGVyLCB1bnJlZ2lzdGVyIH0gZnJvbSAnLi4vcmVnaXN0cnkuanMnO1xuaW1wb3J0IHsgdHlwZSBDaGlsZFJ1bm5lciwgZGVmYXVsdFJ1bkNoaWxkIH0gZnJvbSAnLi4vcnVuQ2hpbGQuanMnO1xuaW1wb3J0IHR5cGUgeyBEZXRhY2hEcml2ZXIsIERldGFjaEhhbmRsZSB9IGZyb20gJy4uL3R5cGVzLmpzJztcblxuaW50ZXJmYWNlIFdvcmtJdGVtIHtcbiAgcmVhZG9ubHkgY2hpbGQ6IEZsb3dDaGFydDtcbiAgcmVhZG9ubHkgaW5wdXQ6IHVua25vd247XG4gIHJlYWRvbmx5IGhhbmRsZTogRGV0YWNoSGFuZGxlO1xufVxuXG4vKipcbiAqIEJ1aWxkIGEgbWljcm90YXNrLWJhdGNoIGRyaXZlciB3aXJlZCB0byBhIGN1c3RvbSBjaGlsZCBydW5uZXIuIE1vc3RcbiAqIGNvbnN1bWVycyB3YW50IHRoZSBkZWZhdWx0IHNpbmdsZXRvbiBgbWljcm90YXNrQmF0Y2hEcml2ZXJgIGluc3RlYWQ7XG4gKiB0aGlzIGZhY3RvcnkgZXhpc3RzIGZvciB0ZXN0cyBhbmQgZm9yIGFkdmFuY2VkIGNvbnN1bWVycyB3aG8gd2FudCB0b1xuICogaW5qZWN0IHRoZWlyIG93biBydW5uZXIgKGUuZy4sIGEgcnVubmVyIHRoYXQgd3JhcHMgdGhlIGNoaWxkIGluIGFcbiAqIHRyYWNpbmcgY29udGV4dCkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVNaWNyb3Rhc2tCYXRjaERyaXZlcihydW5DaGlsZDogQ2hpbGRSdW5uZXIgPSBkZWZhdWx0UnVuQ2hpbGQpOiBEZXRhY2hEcml2ZXIge1xuICAvLyBQZXItZHJpdmVyLWluc3RhbmNlIHF1ZXVlIGFuZCBmbHVzaCBndWFyZC4gQ2xvc2VkIG92ZXIgYnkgYHNjaGVkdWxlYFxuICAvLyBhbmQgYGZsdXNoYCBzbyBlYWNoIGNhbGwgdG8gYGNyZWF0ZU1pY3JvdGFza0JhdGNoRHJpdmVyYCBnZXRzIGl0c1xuICAvLyBvd24gaXNvbGF0ZWQgYmF0Y2ggKHRlc3QgaXNvbGF0aW9uLCBtdWx0aS10ZW5hbnQgc2NlbmFyaW9zKS5cbiAgY29uc3QgcXVldWU6IFdvcmtJdGVtW10gPSBbXTtcbiAgbGV0IHNjaGVkdWxlZCA9IGZhbHNlO1xuXG4gIGZ1bmN0aW9uIGZsdXNoKCk6IHZvaWQge1xuICAgIC8vIFJlc2V0IEJFRk9SRSBkcmFpbmluZyBzbyByZS1lbnRyYW50IHNjaGVkdWxlKClzIGR1cmluZyBydW5DaGlsZFxuICAgIC8vIHF1ZXVlIGEgZnJlc2ggbWljcm90YXNrIGluc3RlYWQgb2Ygam9pbmluZyB0aGUgaW4tZmxpZ2h0IGRyYWluLlxuICAgIHNjaGVkdWxlZCA9IGZhbHNlO1xuICAgIGNvbnN0IGl0ZW1zID0gcXVldWUuc3BsaWNlKDApO1xuICAgIGZvciAoY29uc3QgaXRlbSBvZiBpdGVtcykge1xuICAgICAgLy8gRWFjaCBpdGVtIHJ1bnMgY29uY3VycmVudGx5IOKAlCBubyBhd2FpdHMgaGVyZSwgc28gdGhlIG91dGVyXG4gICAgICAvLyBmb3ItbG9vcCBjb21wbGV0ZXMgd2l0aGluIHRoaXMgbWljcm90YXNrLiBFcnJvcnMgaW5zaWRlXG4gICAgICAvLyBgZXhlY3V0ZU9uZWAgYXJlIHJvdXRlZCB0byB0aGUgaGFuZGxlLCBub3QgdGhyb3duLiBUaGUgcHJvbWlzZVxuICAgICAgLy8gaXMgaW50ZW50aW9uYWxseSBub3QgYXdhaXRlZDsgaWdub3JlLXByb21pc2UtcmV0dXJuZWQgdmlhIHRoZVxuICAgICAgLy8gZXhwbGljaXQgbm8tb3AgLnRoZW4oKSBwYXR0ZXJuIHRoYXQgdGhlIHByb2plY3QncyBsaW50IGNvbmZpZ1xuICAgICAgLy8gYWNjZXB0cyAodnMgYHZvaWRgLCB3aGljaCBgbm8tdm9pZGAgcmVqZWN0cykuXG4gICAgICBleGVjdXRlT25lKGl0ZW0sIHJ1bkNoaWxkKS50aGVuKHVuZGVmaW5lZCwgdW5kZWZpbmVkKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG5hbWU6ICdtaWNyb3Rhc2stYmF0Y2gnLFxuICAgIGNhcGFiaWxpdGllczogeyBicm93c2VyU2FmZTogdHJ1ZSwgbm9kZVNhZmU6IHRydWUsIGVkZ2VTYWZlOiB0cnVlIH0sXG4gICAgc2NoZWR1bGUoY2hpbGQ6IEZsb3dDaGFydCwgaW5wdXQ6IHVua25vd24sIHJlZklkOiBzdHJpbmcpOiBEZXRhY2hIYW5kbGUge1xuICAgICAgY29uc3QgaGFuZGxlID0gY3JlYXRlSGFuZGxlKHJlZklkKTtcbiAgICAgIHJlZ2lzdGVyKGhhbmRsZSk7XG4gICAgICBxdWV1ZS5wdXNoKHsgY2hpbGQsIGlucHV0LCBoYW5kbGUgfSk7XG4gICAgICBpZiAoIXNjaGVkdWxlZCkge1xuICAgICAgICBzY2hlZHVsZWQgPSB0cnVlO1xuICAgICAgICBxdWV1ZU1pY3JvdGFzayhmbHVzaCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gaGFuZGxlO1xuICAgIH0sXG4gIH07XG59XG5cbi8qKlxuICogUGVyLWl0ZW0gZXhlY3V0aW9uLiBNYXJrcyB0aGUgaGFuZGxlIHJ1bm5pbmcsIGF3YWl0cyB0aGUgcnVubmVyLFxuICogcm91dGVzIG91dGNvbWUgdG8gdGhlIGhhbmRsZSwgY2xlYW5zIHVwIHRoZSByZWdpc3RyeSBlbnRyeS4gTmV2ZXJcbiAqIHRocm93cyDigJQgZXJyb3JzIGxhbmQgb24gdGhlIGhhbmRsZSAocGFzc2l2ZSByZWNvcmRlciBydWxlKS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZU9uZShpdGVtOiBXb3JrSXRlbSwgcnVuQ2hpbGQ6IENoaWxkUnVubmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGltcGwgPSBhc0ltcGwoaXRlbS5oYW5kbGUpO1xuICBpbXBsLl9tYXJrUnVubmluZygpO1xuICB0cnkge1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHJ1bkNoaWxkKGl0ZW0uY2hpbGQsIGl0ZW0uaW5wdXQpO1xuICAgIGltcGwuX21hcmtEb25lKHJlc3VsdCk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGltcGwuX21hcmtGYWlsZWQoZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIgOiBuZXcgRXJyb3IoU3RyaW5nKGVycikpKTtcbiAgfSBmaW5hbGx5IHtcbiAgICB1bnJlZ2lzdGVyKGltcGwuaWQpO1xuICB9XG59XG5cbi8qKlxuICogRGVmYXVsdCBzaW5nbGV0b24uIE1vc3QgY29uc3VtZXJzIGltcG9ydCB0aGlzIGFuZCBwYXNzIGl0IHRvXG4gKiBgZXhlY3V0b3IuZGV0YWNoQW5kSm9pbkxhdGVyKGNoaWxkLCBpbnB1dCwgeyBkcml2ZXI6IG1pY3JvdGFza0JhdGNoRHJpdmVyIH0pYFxuICogKG9yIHJlbHkgb24gaXQgYmVpbmcgdGhlIGV4ZWN1dG9yJ3MgZGVmYXVsdCBkcml2ZXIsIHNldCBpbiBUNWIpLlxuICovXG5leHBvcnQgY29uc3QgbWljcm90YXNrQmF0Y2hEcml2ZXI6IERldGFjaERyaXZlciA9IGNyZWF0ZU1pY3JvdGFza0JhdGNoRHJpdmVyKCk7XG4iXX0=
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detach/drivers/sendBeacon.ts — Browser-only driver that ships work
|
|
3
|
+
* via `navigator.sendBeacon` so it
|
|
4
|
+
* survives page-unload.
|
|
5
|
+
*
|
|
6
|
+
* Pattern: Strategy / Adapter — translates the consumer's child
|
|
7
|
+
* flowchart into a sendBeacon POST. The "child" is expected
|
|
8
|
+
* to produce a JSON-serializable payload via its
|
|
9
|
+
* `inputMapper`; the URL endpoint is set at driver creation.
|
|
10
|
+
* Role: The narrow but high-value driver for analytics / error
|
|
11
|
+
* reporting / page-leave telemetry — the one case where
|
|
12
|
+
* "fire-and-forget" must really mean "ships even if the
|
|
13
|
+
* user closes the tab right after."
|
|
14
|
+
*
|
|
15
|
+
* `navigator.sendBeacon` semantics:
|
|
16
|
+
* - Browser queues the POST in the OS network stack BEFORE returning
|
|
17
|
+
* control. Survives page-unload, navigation, refresh.
|
|
18
|
+
* - Limited to ~64 KB per call (per HTML5 spec).
|
|
19
|
+
* - Fire-and-forget — no response observable.
|
|
20
|
+
*
|
|
21
|
+
* Caveats:
|
|
22
|
+
* - Browser-only (`browserSafe: true, survivesUnload: true`).
|
|
23
|
+
* `validate()` throws helpfully if `navigator.sendBeacon` isn't a
|
|
24
|
+
* function (e.g., when imported in Node).
|
|
25
|
+
* - The driver does NOT run the child flowchart through a
|
|
26
|
+
* `FlowChartExecutor` — it serializes the input and POSTs. This
|
|
27
|
+
* is an intentional simplification: sendBeacon's semantics
|
|
28
|
+
* wouldn't survive an executor's async stages anyway.
|
|
29
|
+
*/
|
|
30
|
+
import { asImpl, createHandle } from '../handle.js';
|
|
31
|
+
import { register, unregister } from '../registry.js';
|
|
32
|
+
export function createSendBeaconDriver(opts) {
|
|
33
|
+
if (!opts.url) {
|
|
34
|
+
throw new TypeError('[detach] createSendBeaconDriver requires a `url` option.');
|
|
35
|
+
}
|
|
36
|
+
function serialize(input) {
|
|
37
|
+
if (opts.serialize)
|
|
38
|
+
return opts.serialize(input);
|
|
39
|
+
return new Blob([JSON.stringify(input ?? null)], { type: 'application/json' });
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
name: 'send-beacon',
|
|
43
|
+
capabilities: {
|
|
44
|
+
browserSafe: true,
|
|
45
|
+
survivesUnload: true,
|
|
46
|
+
},
|
|
47
|
+
validate() {
|
|
48
|
+
if (typeof navigator === 'undefined' || typeof navigator.sendBeacon !== 'function') {
|
|
49
|
+
throw new Error('[detach] sendBeaconDriver requires a browser environment with `navigator.sendBeacon`. ' +
|
|
50
|
+
'Use `microtaskBatchDriver` for in-process detach, or `setImmediateDriver` for Node.js.');
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
schedule(_child, input, refId) {
|
|
54
|
+
const handle = createHandle(refId);
|
|
55
|
+
register(handle);
|
|
56
|
+
const impl = asImpl(handle);
|
|
57
|
+
impl._markRunning();
|
|
58
|
+
try {
|
|
59
|
+
const payload = serialize(input);
|
|
60
|
+
const accepted = navigator.sendBeacon(opts.url, payload);
|
|
61
|
+
if (accepted) {
|
|
62
|
+
impl._markDone({ accepted: true, url: opts.url });
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
impl._markFailed(new Error('[detach] navigator.sendBeacon refused the payload (likely over the ~64 KB limit).'));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
impl._markFailed(err instanceof Error ? err : new Error(String(err)));
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
unregister(impl.id);
|
|
73
|
+
}
|
|
74
|
+
return handle;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VuZEJlYWNvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvZGV0YWNoL2RyaXZlcnMvc2VuZEJlYWNvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTRCRztBQUdILE9BQU8sRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3BELE9BQU8sRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFXdEQsTUFBTSxVQUFVLHNCQUFzQixDQUFDLElBQTZCO0lBQ2xFLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDZCxNQUFNLElBQUksU0FBUyxDQUFDLDBEQUEwRCxDQUFDLENBQUM7SUFDbEYsQ0FBQztJQUVELFNBQVMsU0FBUyxDQUFDLEtBQWM7UUFDL0IsSUFBSSxJQUFJLENBQUMsU0FBUztZQUFFLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNqRCxPQUFPLElBQUksSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLENBQUMsRUFBRSxFQUFFLElBQUksRUFBRSxrQkFBa0IsRUFBRSxDQUFDLENBQUM7SUFDakYsQ0FBQztJQUVELE9BQU87UUFDTCxJQUFJLEVBQUUsYUFBYTtRQUNuQixZQUFZLEVBQUU7WUFDWixXQUFXLEVBQUUsSUFBSTtZQUNqQixjQUFjLEVBQUUsSUFBSTtTQUNyQjtRQUNELFFBQVE7WUFDTixJQUFJLE9BQU8sU0FBUyxLQUFLLFdBQVcsSUFBSSxPQUFPLFNBQVMsQ0FBQyxVQUFVLEtBQUssVUFBVSxFQUFFLENBQUM7Z0JBQ25GLE1BQU0sSUFBSSxLQUFLLENBQ2Isd0ZBQXdGO29CQUN0Rix3RkFBd0YsQ0FDM0YsQ0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO1FBQ0QsUUFBUSxDQUFDLE1BQWlCLEVBQUUsS0FBYyxFQUFFLEtBQWE7WUFDdkQsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ25DLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUNqQixNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDNUIsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3BCLElBQUksQ0FBQztnQkFDSCxNQUFNLE9BQU8sR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ2pDLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxPQUFtQixDQUFDLENBQUM7Z0JBQ3JFLElBQUksUUFBUSxFQUFFLENBQUM7b0JBQ2IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2dCQUNwRCxDQUFDO3FCQUFNLENBQUM7b0JBQ04sSUFBSSxDQUFDLFdBQVcsQ0FDZCxJQUFJLEtBQUssQ0FBQyxtRkFBbUYsQ0FBQyxDQUMvRixDQUFDO2dCQUNKLENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDYixJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN4RSxDQUFDO29CQUFTLENBQUM7Z0JBQ1QsVUFBVSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUN0QixDQUFDO1lBQ0QsT0FBTyxNQUFNLENBQUM7UUFDaEIsQ0FBQztLQUNGLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBkZXRhY2gvZHJpdmVycy9zZW5kQmVhY29uLnRzIOKAlCBCcm93c2VyLW9ubHkgZHJpdmVyIHRoYXQgc2hpcHMgd29ya1xuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpYSBgbmF2aWdhdG9yLnNlbmRCZWFjb25gIHNvIGl0XG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vydml2ZXMgcGFnZS11bmxvYWQuXG4gKlxuICogUGF0dGVybjogIFN0cmF0ZWd5IC8gQWRhcHRlciDigJQgdHJhbnNsYXRlcyB0aGUgY29uc3VtZXIncyBjaGlsZFxuICogICAgICAgICAgIGZsb3djaGFydCBpbnRvIGEgc2VuZEJlYWNvbiBQT1NULiBUaGUgXCJjaGlsZFwiIGlzIGV4cGVjdGVkXG4gKiAgICAgICAgICAgdG8gcHJvZHVjZSBhIEpTT04tc2VyaWFsaXphYmxlIHBheWxvYWQgdmlhIGl0c1xuICogICAgICAgICAgIGBpbnB1dE1hcHBlcmA7IHRoZSBVUkwgZW5kcG9pbnQgaXMgc2V0IGF0IGRyaXZlciBjcmVhdGlvbi5cbiAqIFJvbGU6ICAgICBUaGUgbmFycm93IGJ1dCBoaWdoLXZhbHVlIGRyaXZlciBmb3IgYW5hbHl0aWNzIC8gZXJyb3JcbiAqICAgICAgICAgICByZXBvcnRpbmcgLyBwYWdlLWxlYXZlIHRlbGVtZXRyeSDigJQgdGhlIG9uZSBjYXNlIHdoZXJlXG4gKiAgICAgICAgICAgXCJmaXJlLWFuZC1mb3JnZXRcIiBtdXN0IHJlYWxseSBtZWFuIFwic2hpcHMgZXZlbiBpZiB0aGVcbiAqICAgICAgICAgICB1c2VyIGNsb3NlcyB0aGUgdGFiIHJpZ2h0IGFmdGVyLlwiXG4gKlxuICogYG5hdmlnYXRvci5zZW5kQmVhY29uYCBzZW1hbnRpY3M6XG4gKiAgIC0gQnJvd3NlciBxdWV1ZXMgdGhlIFBPU1QgaW4gdGhlIE9TIG5ldHdvcmsgc3RhY2sgQkVGT1JFIHJldHVybmluZ1xuICogICAgIGNvbnRyb2wuIFN1cnZpdmVzIHBhZ2UtdW5sb2FkLCBuYXZpZ2F0aW9uLCByZWZyZXNoLlxuICogICAtIExpbWl0ZWQgdG8gfjY0IEtCIHBlciBjYWxsIChwZXIgSFRNTDUgc3BlYykuXG4gKiAgIC0gRmlyZS1hbmQtZm9yZ2V0IOKAlCBubyByZXNwb25zZSBvYnNlcnZhYmxlLlxuICpcbiAqIENhdmVhdHM6XG4gKiAgIC0gQnJvd3Nlci1vbmx5IChgYnJvd3NlclNhZmU6IHRydWUsIHN1cnZpdmVzVW5sb2FkOiB0cnVlYCkuXG4gKiAgICAgYHZhbGlkYXRlKClgIHRocm93cyBoZWxwZnVsbHkgaWYgYG5hdmlnYXRvci5zZW5kQmVhY29uYCBpc24ndCBhXG4gKiAgICAgZnVuY3Rpb24gKGUuZy4sIHdoZW4gaW1wb3J0ZWQgaW4gTm9kZSkuXG4gKiAgIC0gVGhlIGRyaXZlciBkb2VzIE5PVCBydW4gdGhlIGNoaWxkIGZsb3djaGFydCB0aHJvdWdoIGFcbiAqICAgICBgRmxvd0NoYXJ0RXhlY3V0b3JgIOKAlCBpdCBzZXJpYWxpemVzIHRoZSBpbnB1dCBhbmQgUE9TVHMuIFRoaXNcbiAqICAgICBpcyBhbiBpbnRlbnRpb25hbCBzaW1wbGlmaWNhdGlvbjogc2VuZEJlYWNvbidzIHNlbWFudGljc1xuICogICAgIHdvdWxkbid0IHN1cnZpdmUgYW4gZXhlY3V0b3IncyBhc3luYyBzdGFnZXMgYW55d2F5LlxuICovXG5cbmltcG9ydCB0eXBlIHsgRmxvd0NoYXJ0IH0gZnJvbSAnLi4vLi4vYnVpbGRlci90eXBlcy5qcyc7XG5pbXBvcnQgeyBhc0ltcGwsIGNyZWF0ZUhhbmRsZSB9IGZyb20gJy4uL2hhbmRsZS5qcyc7XG5pbXBvcnQgeyByZWdpc3RlciwgdW5yZWdpc3RlciB9IGZyb20gJy4uL3JlZ2lzdHJ5LmpzJztcbmltcG9ydCB0eXBlIHsgRGV0YWNoRHJpdmVyLCBEZXRhY2hIYW5kbGUgfSBmcm9tICcuLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2VuZEJlYWNvbkRyaXZlck9wdGlvbnMge1xuICAvKiogRW5kcG9pbnQgVVJMIOKAlCByZXF1aXJlZC4gZS5nLiwgYCdodHRwczovL3RlbGVtZXRyeS5leGFtcGxlLmNvbS9pbmdlc3QnYC4gKi9cbiAgcmVhZG9ubHkgdXJsOiBzdHJpbmc7XG4gIC8qKiBDdXN0b20gc2VyaWFsaXplci4gRGVmYXVsdHMgdG8gYEpTT04uc3RyaW5naWZ5KGlucHV0KWAgd2l0aFxuICAgKiAgYGFwcGxpY2F0aW9uL2pzb25gIGNvbnRlbnQgdHlwZS4gKi9cbiAgcmVhZG9ubHkgc2VyaWFsaXplPzogKGlucHV0OiB1bmtub3duKSA9PiBCbG9iIHwgc3RyaW5nIHwgRm9ybURhdGE7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTZW5kQmVhY29uRHJpdmVyKG9wdHM6IFNlbmRCZWFjb25Ecml2ZXJPcHRpb25zKTogRGV0YWNoRHJpdmVyIHtcbiAgaWYgKCFvcHRzLnVybCkge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1tkZXRhY2hdIGNyZWF0ZVNlbmRCZWFjb25Ecml2ZXIgcmVxdWlyZXMgYSBgdXJsYCBvcHRpb24uJyk7XG4gIH1cblxuICBmdW5jdGlvbiBzZXJpYWxpemUoaW5wdXQ6IHVua25vd24pOiBCbG9iIHwgc3RyaW5nIHwgRm9ybURhdGEge1xuICAgIGlmIChvcHRzLnNlcmlhbGl6ZSkgcmV0dXJuIG9wdHMuc2VyaWFsaXplKGlucHV0KTtcbiAgICByZXR1cm4gbmV3IEJsb2IoW0pTT04uc3RyaW5naWZ5KGlucHV0ID8/IG51bGwpXSwgeyB0eXBlOiAnYXBwbGljYXRpb24vanNvbicgfSk7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG5hbWU6ICdzZW5kLWJlYWNvbicsXG4gICAgY2FwYWJpbGl0aWVzOiB7XG4gICAgICBicm93c2VyU2FmZTogdHJ1ZSxcbiAgICAgIHN1cnZpdmVzVW5sb2FkOiB0cnVlLFxuICAgIH0sXG4gICAgdmFsaWRhdGUoKTogdm9pZCB7XG4gICAgICBpZiAodHlwZW9mIG5hdmlnYXRvciA9PT0gJ3VuZGVmaW5lZCcgfHwgdHlwZW9mIG5hdmlnYXRvci5zZW5kQmVhY29uICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnW2RldGFjaF0gc2VuZEJlYWNvbkRyaXZlciByZXF1aXJlcyBhIGJyb3dzZXIgZW52aXJvbm1lbnQgd2l0aCBgbmF2aWdhdG9yLnNlbmRCZWFjb25gLiAnICtcbiAgICAgICAgICAgICdVc2UgYG1pY3JvdGFza0JhdGNoRHJpdmVyYCBmb3IgaW4tcHJvY2VzcyBkZXRhY2gsIG9yIGBzZXRJbW1lZGlhdGVEcml2ZXJgIGZvciBOb2RlLmpzLicsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfSxcbiAgICBzY2hlZHVsZShfY2hpbGQ6IEZsb3dDaGFydCwgaW5wdXQ6IHVua25vd24sIHJlZklkOiBzdHJpbmcpOiBEZXRhY2hIYW5kbGUge1xuICAgICAgY29uc3QgaGFuZGxlID0gY3JlYXRlSGFuZGxlKHJlZklkKTtcbiAgICAgIHJlZ2lzdGVyKGhhbmRsZSk7XG4gICAgICBjb25zdCBpbXBsID0gYXNJbXBsKGhhbmRsZSk7XG4gICAgICBpbXBsLl9tYXJrUnVubmluZygpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcGF5bG9hZCA9IHNlcmlhbGl6ZShpbnB1dCk7XG4gICAgICAgIGNvbnN0IGFjY2VwdGVkID0gbmF2aWdhdG9yLnNlbmRCZWFjb24ob3B0cy51cmwsIHBheWxvYWQgYXMgQm9keUluaXQpO1xuICAgICAgICBpZiAoYWNjZXB0ZWQpIHtcbiAgICAgICAgICBpbXBsLl9tYXJrRG9uZSh7IGFjY2VwdGVkOiB0cnVlLCB1cmw6IG9wdHMudXJsIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGltcGwuX21hcmtGYWlsZWQoXG4gICAgICAgICAgICBuZXcgRXJyb3IoJ1tkZXRhY2hdIG5hdmlnYXRvci5zZW5kQmVhY29uIHJlZnVzZWQgdGhlIHBheWxvYWQgKGxpa2VseSBvdmVyIHRoZSB+NjQgS0IgbGltaXQpLicpLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBpbXBsLl9tYXJrRmFpbGVkKGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyIDogbmV3IEVycm9yKFN0cmluZyhlcnIpKSk7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICB1bnJlZ2lzdGVyKGltcGwuaWQpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGhhbmRsZTtcbiAgICB9LFxuICB9O1xufVxuIl19
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detach/drivers/setImmediate.ts — Defer detached work to a Node.js
|
|
3
|
+
* `setImmediate` boundary.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: Same producer-consumer batch flush as `microtaskBatch`,
|
|
6
|
+
* but the deferral is `setImmediate` instead of
|
|
7
|
+
* `queueMicrotask`. Yields control back to the event loop
|
|
8
|
+
* BEFORE running — allows pending I/O callbacks to drain
|
|
9
|
+
* first, which microtasks would block.
|
|
10
|
+
* Role: Node-specific driver for "fire-and-forget after the
|
|
11
|
+
* current I/O tick." Use when the parent stage handles
|
|
12
|
+
* latency-sensitive work and you don't want detached work
|
|
13
|
+
* to compete for the synchronous slice.
|
|
14
|
+
*
|
|
15
|
+
* When to pick this over microtaskBatch:
|
|
16
|
+
* - You're shipping logs / metrics in a hot HTTP path and don't
|
|
17
|
+
* want them blocking the response from being flushed
|
|
18
|
+
* - The detached work itself is CPU-heavy enough that running it on
|
|
19
|
+
* the same microtask cycle would delay other microtasks
|
|
20
|
+
* - You explicitly want "next event-loop tick" semantics — useful
|
|
21
|
+
* when interacting with third-party libraries that expect at
|
|
22
|
+
* least one I/O tick between schedule and execution
|
|
23
|
+
*
|
|
24
|
+
* Capability:
|
|
25
|
+
* - `nodeSafe: true` — relies on Node's `setImmediate`, NOT
|
|
26
|
+
* available in browsers / Deno / Cloudflare Workers (use
|
|
27
|
+
* `setTimeoutDriver` for cross-runtime alternative)
|
|
28
|
+
*/
|
|
29
|
+
import { asImpl, createHandle } from '../handle.js';
|
|
30
|
+
import { register, unregister } from '../registry.js';
|
|
31
|
+
import { defaultRunChild } from '../runChild.js';
|
|
32
|
+
export function createSetImmediateDriver(runChild = defaultRunChild) {
|
|
33
|
+
const queue = [];
|
|
34
|
+
let scheduled = false;
|
|
35
|
+
function flush() {
|
|
36
|
+
scheduled = false;
|
|
37
|
+
const items = queue.splice(0);
|
|
38
|
+
for (const item of items) {
|
|
39
|
+
executeOne(item, runChild).then(undefined, undefined);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
name: 'set-immediate',
|
|
44
|
+
capabilities: { nodeSafe: true },
|
|
45
|
+
validate() {
|
|
46
|
+
if (typeof setImmediate !== 'function') {
|
|
47
|
+
throw new Error('[detach] setImmediateDriver requires Node.js — global `setImmediate` is not defined ' +
|
|
48
|
+
'in this runtime. Use `microtaskBatchDriver` for cross-runtime use, or `setTimeoutDriver` ' +
|
|
49
|
+
'for browser/edge environments.');
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
schedule(child, input, refId) {
|
|
53
|
+
const handle = createHandle(refId);
|
|
54
|
+
register(handle);
|
|
55
|
+
queue.push({ child, input, handle });
|
|
56
|
+
if (!scheduled) {
|
|
57
|
+
scheduled = true;
|
|
58
|
+
// `setImmediate` is non-undefined here in Node; runtime guard
|
|
59
|
+
// is in `validate()`. The `!` is a deliberate assertion.
|
|
60
|
+
setImmediate(flush);
|
|
61
|
+
}
|
|
62
|
+
return handle;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async function executeOne(item, runChild) {
|
|
67
|
+
const impl = asImpl(item.handle);
|
|
68
|
+
impl._markRunning();
|
|
69
|
+
try {
|
|
70
|
+
const result = await runChild(item.child, item.input);
|
|
71
|
+
impl._markDone(result);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
impl._markFailed(err instanceof Error ? err : new Error(String(err)));
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
unregister(impl.id);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export const setImmediateDriver = createSetImmediateDriver();
|
|
81
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0SW1tZWRpYXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9kZXRhY2gvZHJpdmVycy9zZXRJbW1lZGlhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTJCRztBQUdILE9BQU8sRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3BELE9BQU8sRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDdEQsT0FBTyxFQUFvQixlQUFlLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQWVuRSxNQUFNLFVBQVUsd0JBQXdCLENBQUMsV0FBd0IsZUFBZTtJQUM5RSxNQUFNLEtBQUssR0FBZSxFQUFFLENBQUM7SUFDN0IsSUFBSSxTQUFTLEdBQUcsS0FBSyxDQUFDO0lBRXRCLFNBQVMsS0FBSztRQUNaLFNBQVMsR0FBRyxLQUFLLENBQUM7UUFDbEIsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QixLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ3pCLFVBQVUsQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQztRQUN4RCxDQUFDO0lBQ0gsQ0FBQztJQUVELE9BQU87UUFDTCxJQUFJLEVBQUUsZUFBZTtRQUNyQixZQUFZLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFO1FBQ2hDLFFBQVE7WUFDTixJQUFJLE9BQU8sWUFBWSxLQUFLLFVBQVUsRUFBRSxDQUFDO2dCQUN2QyxNQUFNLElBQUksS0FBSyxDQUNiLHNGQUFzRjtvQkFDcEYsMkZBQTJGO29CQUMzRixnQ0FBZ0MsQ0FDbkMsQ0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO1FBQ0QsUUFBUSxDQUFDLEtBQWdCLEVBQUUsS0FBYyxFQUFFLEtBQWE7WUFDdEQsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ25DLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUNqQixLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQ3JDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDZixTQUFTLEdBQUcsSUFBSSxDQUFDO2dCQUNqQiw4REFBOEQ7Z0JBQzlELHlEQUF5RDtnQkFDekQsWUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ3ZCLENBQUM7WUFDRCxPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRCxLQUFLLFVBQVUsVUFBVSxDQUFDLElBQWMsRUFBRSxRQUFxQjtJQUM3RCxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2pDLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUNwQixJQUFJLENBQUM7UUFDSCxNQUFNLE1BQU0sR0FBRyxNQUFNLFFBQVEsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN0RCxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3pCLENBQUM7SUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ2IsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDeEUsQ0FBQztZQUFTLENBQUM7UUFDVCxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3RCLENBQUM7QUFDSCxDQUFDO0FBRUQsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQWlCLHdCQUF3QixFQUFFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGRldGFjaC9kcml2ZXJzL3NldEltbWVkaWF0ZS50cyDigJQgRGVmZXIgZGV0YWNoZWQgd29yayB0byBhIE5vZGUuanNcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBzZXRJbW1lZGlhdGVgIGJvdW5kYXJ5LlxuICpcbiAqIFBhdHRlcm46ICBTYW1lIHByb2R1Y2VyLWNvbnN1bWVyIGJhdGNoIGZsdXNoIGFzIGBtaWNyb3Rhc2tCYXRjaGAsXG4gKiAgICAgICAgICAgYnV0IHRoZSBkZWZlcnJhbCBpcyBgc2V0SW1tZWRpYXRlYCBpbnN0ZWFkIG9mXG4gKiAgICAgICAgICAgYHF1ZXVlTWljcm90YXNrYC4gWWllbGRzIGNvbnRyb2wgYmFjayB0byB0aGUgZXZlbnQgbG9vcFxuICogICAgICAgICAgIEJFRk9SRSBydW5uaW5nIOKAlCBhbGxvd3MgcGVuZGluZyBJL08gY2FsbGJhY2tzIHRvIGRyYWluXG4gKiAgICAgICAgICAgZmlyc3QsIHdoaWNoIG1pY3JvdGFza3Mgd291bGQgYmxvY2suXG4gKiBSb2xlOiAgICAgTm9kZS1zcGVjaWZpYyBkcml2ZXIgZm9yIFwiZmlyZS1hbmQtZm9yZ2V0IGFmdGVyIHRoZVxuICogICAgICAgICAgIGN1cnJlbnQgSS9PIHRpY2suXCIgVXNlIHdoZW4gdGhlIHBhcmVudCBzdGFnZSBoYW5kbGVzXG4gKiAgICAgICAgICAgbGF0ZW5jeS1zZW5zaXRpdmUgd29yayBhbmQgeW91IGRvbid0IHdhbnQgZGV0YWNoZWQgd29ya1xuICogICAgICAgICAgIHRvIGNvbXBldGUgZm9yIHRoZSBzeW5jaHJvbm91cyBzbGljZS5cbiAqXG4gKiBXaGVuIHRvIHBpY2sgdGhpcyBvdmVyIG1pY3JvdGFza0JhdGNoOlxuICogICAtIFlvdSdyZSBzaGlwcGluZyBsb2dzIC8gbWV0cmljcyBpbiBhIGhvdCBIVFRQIHBhdGggYW5kIGRvbid0XG4gKiAgICAgd2FudCB0aGVtIGJsb2NraW5nIHRoZSByZXNwb25zZSBmcm9tIGJlaW5nIGZsdXNoZWRcbiAqICAgLSBUaGUgZGV0YWNoZWQgd29yayBpdHNlbGYgaXMgQ1BVLWhlYXZ5IGVub3VnaCB0aGF0IHJ1bm5pbmcgaXQgb25cbiAqICAgICB0aGUgc2FtZSBtaWNyb3Rhc2sgY3ljbGUgd291bGQgZGVsYXkgb3RoZXIgbWljcm90YXNrc1xuICogICAtIFlvdSBleHBsaWNpdGx5IHdhbnQgXCJuZXh0IGV2ZW50LWxvb3AgdGlja1wiIHNlbWFudGljcyDigJQgdXNlZnVsXG4gKiAgICAgd2hlbiBpbnRlcmFjdGluZyB3aXRoIHRoaXJkLXBhcnR5IGxpYnJhcmllcyB0aGF0IGV4cGVjdCBhdFxuICogICAgIGxlYXN0IG9uZSBJL08gdGljayBiZXR3ZWVuIHNjaGVkdWxlIGFuZCBleGVjdXRpb25cbiAqXG4gKiBDYXBhYmlsaXR5OlxuICogICAtIGBub2RlU2FmZTogdHJ1ZWAg4oCUIHJlbGllcyBvbiBOb2RlJ3MgYHNldEltbWVkaWF0ZWAsIE5PVFxuICogICAgIGF2YWlsYWJsZSBpbiBicm93c2VycyAvIERlbm8gLyBDbG91ZGZsYXJlIFdvcmtlcnMgKHVzZVxuICogICAgIGBzZXRUaW1lb3V0RHJpdmVyYCBmb3IgY3Jvc3MtcnVudGltZSBhbHRlcm5hdGl2ZSlcbiAqL1xuXG5pbXBvcnQgdHlwZSB7IEZsb3dDaGFydCB9IGZyb20gJy4uLy4uL2J1aWxkZXIvdHlwZXMuanMnO1xuaW1wb3J0IHsgYXNJbXBsLCBjcmVhdGVIYW5kbGUgfSBmcm9tICcuLi9oYW5kbGUuanMnO1xuaW1wb3J0IHsgcmVnaXN0ZXIsIHVucmVnaXN0ZXIgfSBmcm9tICcuLi9yZWdpc3RyeS5qcyc7XG5pbXBvcnQgeyB0eXBlIENoaWxkUnVubmVyLCBkZWZhdWx0UnVuQ2hpbGQgfSBmcm9tICcuLi9ydW5DaGlsZC5qcyc7XG5pbXBvcnQgdHlwZSB7IERldGFjaERyaXZlciwgRGV0YWNoSGFuZGxlIH0gZnJvbSAnLi4vdHlwZXMuanMnO1xuXG4vLyBOb2RlLW9ubHkgZ2xvYmFsLiBXZSBkb24ndCBzaGlwIEB0eXBlcy9ub2RlLCBzbyBkZWNsYXJlIHRoZSBtaW5pbWFsXG4vLyBzaGFwZSBoZXJlLiBgc2V0SW1tZWRpYXRlRHJpdmVyYCBhZHZlcnRpc2VzIGBub2RlU2FmZTogdHJ1ZWAgYW5kXG4vLyBgdmFsaWRhdGUoKWAgdGhyb3dzIGhlbHBmdWxseSBpZiBgc2V0SW1tZWRpYXRlYCBpcyB1bmRlZmluZWQgYXQgdXNlXG4vLyB0aW1lIChlLmcuLCBicm93c2VyIGJ1bmRsZSkuXG5kZWNsYXJlIGNvbnN0IHNldEltbWVkaWF0ZTogKChjYjogKCkgPT4gdm9pZCkgPT4gdW5rbm93bikgfCB1bmRlZmluZWQ7XG5cbmludGVyZmFjZSBXb3JrSXRlbSB7XG4gIHJlYWRvbmx5IGNoaWxkOiBGbG93Q2hhcnQ7XG4gIHJlYWRvbmx5IGlucHV0OiB1bmtub3duO1xuICByZWFkb25seSBoYW5kbGU6IERldGFjaEhhbmRsZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVNldEltbWVkaWF0ZURyaXZlcihydW5DaGlsZDogQ2hpbGRSdW5uZXIgPSBkZWZhdWx0UnVuQ2hpbGQpOiBEZXRhY2hEcml2ZXIge1xuICBjb25zdCBxdWV1ZTogV29ya0l0ZW1bXSA9IFtdO1xuICBsZXQgc2NoZWR1bGVkID0gZmFsc2U7XG5cbiAgZnVuY3Rpb24gZmx1c2goKTogdm9pZCB7XG4gICAgc2NoZWR1bGVkID0gZmFsc2U7XG4gICAgY29uc3QgaXRlbXMgPSBxdWV1ZS5zcGxpY2UoMCk7XG4gICAgZm9yIChjb25zdCBpdGVtIG9mIGl0ZW1zKSB7XG4gICAgICBleGVjdXRlT25lKGl0ZW0sIHJ1bkNoaWxkKS50aGVuKHVuZGVmaW5lZCwgdW5kZWZpbmVkKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG5hbWU6ICdzZXQtaW1tZWRpYXRlJyxcbiAgICBjYXBhYmlsaXRpZXM6IHsgbm9kZVNhZmU6IHRydWUgfSxcbiAgICB2YWxpZGF0ZSgpOiB2b2lkIHtcbiAgICAgIGlmICh0eXBlb2Ygc2V0SW1tZWRpYXRlICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnW2RldGFjaF0gc2V0SW1tZWRpYXRlRHJpdmVyIHJlcXVpcmVzIE5vZGUuanMg4oCUIGdsb2JhbCBgc2V0SW1tZWRpYXRlYCBpcyBub3QgZGVmaW5lZCAnICtcbiAgICAgICAgICAgICdpbiB0aGlzIHJ1bnRpbWUuIFVzZSBgbWljcm90YXNrQmF0Y2hEcml2ZXJgIGZvciBjcm9zcy1ydW50aW1lIHVzZSwgb3IgYHNldFRpbWVvdXREcml2ZXJgICcgK1xuICAgICAgICAgICAgJ2ZvciBicm93c2VyL2VkZ2UgZW52aXJvbm1lbnRzLicsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfSxcbiAgICBzY2hlZHVsZShjaGlsZDogRmxvd0NoYXJ0LCBpbnB1dDogdW5rbm93biwgcmVmSWQ6IHN0cmluZyk6IERldGFjaEhhbmRsZSB7XG4gICAgICBjb25zdCBoYW5kbGUgPSBjcmVhdGVIYW5kbGUocmVmSWQpO1xuICAgICAgcmVnaXN0ZXIoaGFuZGxlKTtcbiAgICAgIHF1ZXVlLnB1c2goeyBjaGlsZCwgaW5wdXQsIGhhbmRsZSB9KTtcbiAgICAgIGlmICghc2NoZWR1bGVkKSB7XG4gICAgICAgIHNjaGVkdWxlZCA9IHRydWU7XG4gICAgICAgIC8vIGBzZXRJbW1lZGlhdGVgIGlzIG5vbi11bmRlZmluZWQgaGVyZSBpbiBOb2RlOyBydW50aW1lIGd1YXJkXG4gICAgICAgIC8vIGlzIGluIGB2YWxpZGF0ZSgpYC4gVGhlIGAhYCBpcyBhIGRlbGliZXJhdGUgYXNzZXJ0aW9uLlxuICAgICAgICBzZXRJbW1lZGlhdGUhKGZsdXNoKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBoYW5kbGU7XG4gICAgfSxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZU9uZShpdGVtOiBXb3JrSXRlbSwgcnVuQ2hpbGQ6IENoaWxkUnVubmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGltcGwgPSBhc0ltcGwoaXRlbS5oYW5kbGUpO1xuICBpbXBsLl9tYXJrUnVubmluZygpO1xuICB0cnkge1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHJ1bkNoaWxkKGl0ZW0uY2hpbGQsIGl0ZW0uaW5wdXQpO1xuICAgIGltcGwuX21hcmtEb25lKHJlc3VsdCk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGltcGwuX21hcmtGYWlsZWQoZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIgOiBuZXcgRXJyb3IoU3RyaW5nKGVycikpKTtcbiAgfSBmaW5hbGx5IHtcbiAgICB1bnJlZ2lzdGVyKGltcGwuaWQpO1xuICB9XG59XG5cbmV4cG9ydCBjb25zdCBzZXRJbW1lZGlhdGVEcml2ZXI6IERldGFjaERyaXZlciA9IGNyZWF0ZVNldEltbWVkaWF0ZURyaXZlcigpO1xuIl19
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detach/drivers/setTimeout.ts — Defer detached work via `setTimeout(..., delayMs)`.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: Producer-consumer batch flush; deferral mechanism is
|
|
5
|
+
* `setTimeout` with a configurable delay (default `0`).
|
|
6
|
+
* Role: Cross-runtime "next macrotask" driver. Works in browsers,
|
|
7
|
+
* Node.js, Deno, Cloudflare Workers, Bun, etc.
|
|
8
|
+
*
|
|
9
|
+
* When to pick this:
|
|
10
|
+
* - Consumer wants a SPECIFIC delay (e.g. "ship telemetry in 5
|
|
11
|
+
* seconds, batched") — pass `createSetTimeoutDriver({ delayMs: 5000 })`
|
|
12
|
+
* - Cross-runtime detach where `setImmediate` isn't available
|
|
13
|
+
* - Coalescing high-frequency events into a low-frequency flush
|
|
14
|
+
*
|
|
15
|
+
* Caveats:
|
|
16
|
+
* - Not for low-latency hot paths — minimum delay is ~4ms in
|
|
17
|
+
* browsers per the HTML5 spec, ~1ms in Node. Use
|
|
18
|
+
* `microtaskBatchDriver` for sub-ms scheduling.
|
|
19
|
+
* - Browser tab freezing / throttling can extend the delay
|
|
20
|
+
* significantly. Don't rely on precise timing.
|
|
21
|
+
*/
|
|
22
|
+
import { asImpl, createHandle } from '../handle.js';
|
|
23
|
+
import { register, unregister } from '../registry.js';
|
|
24
|
+
import { defaultRunChild } from '../runChild.js';
|
|
25
|
+
export function createSetTimeoutDriver(opts = {}) {
|
|
26
|
+
const delayMs = opts.delayMs ?? 0;
|
|
27
|
+
const runChild = opts.runChild ?? defaultRunChild;
|
|
28
|
+
const queue = [];
|
|
29
|
+
let scheduled = false;
|
|
30
|
+
function flush() {
|
|
31
|
+
scheduled = false;
|
|
32
|
+
const items = queue.splice(0);
|
|
33
|
+
for (const item of items) {
|
|
34
|
+
executeOne(item, runChild).then(undefined, undefined);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
name: delayMs === 0 ? 'set-timeout' : `set-timeout-${delayMs}ms`,
|
|
39
|
+
capabilities: { browserSafe: true, nodeSafe: true, edgeSafe: true },
|
|
40
|
+
schedule(child, input, refId) {
|
|
41
|
+
const handle = createHandle(refId);
|
|
42
|
+
register(handle);
|
|
43
|
+
queue.push({ child, input, handle });
|
|
44
|
+
if (!scheduled) {
|
|
45
|
+
scheduled = true;
|
|
46
|
+
setTimeout(flush, delayMs);
|
|
47
|
+
}
|
|
48
|
+
return handle;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async function executeOne(item, runChild) {
|
|
53
|
+
const impl = asImpl(item.handle);
|
|
54
|
+
impl._markRunning();
|
|
55
|
+
try {
|
|
56
|
+
const result = await runChild(item.child, item.input);
|
|
57
|
+
impl._markDone(result);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
impl._markFailed(err instanceof Error ? err : new Error(String(err)));
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
unregister(impl.id);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Default singleton — zero-delay (next macrotask). For configurable
|
|
67
|
+
* delays, use `createSetTimeoutDriver({ delayMs })`. */
|
|
68
|
+
export const setTimeoutDriver = createSetTimeoutDriver();
|
|
69
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0VGltZW91dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvZGV0YWNoL2RyaXZlcnMvc2V0VGltZW91dC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQkc7QUFHSCxPQUFPLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNwRCxPQUFPLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3RELE9BQU8sRUFBb0IsZUFBZSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFpQm5FLE1BQU0sVUFBVSxzQkFBc0IsQ0FBQyxPQUFnQyxFQUFFO0lBQ3ZFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxDQUFDO0lBQ2xDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLElBQUksZUFBZSxDQUFDO0lBQ2xELE1BQU0sS0FBSyxHQUFlLEVBQUUsQ0FBQztJQUM3QixJQUFJLFNBQVMsR0FBRyxLQUFLLENBQUM7SUFFdEIsU0FBUyxLQUFLO1FBQ1osU0FBUyxHQUFHLEtBQUssQ0FBQztRQUNsQixNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzlCLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFLENBQUM7WUFDekIsVUFBVSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ3hELENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTztRQUNMLElBQUksRUFBRSxPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLGVBQWUsT0FBTyxJQUFJO1FBQ2hFLFlBQVksRUFBRSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFO1FBQ25FLFFBQVEsQ0FBQyxLQUFnQixFQUFFLEtBQWMsRUFBRSxLQUFhO1lBQ3RELE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNuQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDakIsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUNyQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ2YsU0FBUyxHQUFHLElBQUksQ0FBQztnQkFDakIsVUFBVSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztZQUM3QixDQUFDO1lBQ0QsT0FBTyxNQUFNLENBQUM7UUFDaEIsQ0FBQztLQUNGLENBQUM7QUFDSixDQUFDO0FBRUQsS0FBSyxVQUFVLFVBQVUsQ0FBQyxJQUFjLEVBQUUsUUFBcUI7SUFDN0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNqQyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDcEIsSUFBSSxDQUFDO1FBQ0gsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDdEQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUN6QixDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3hFLENBQUM7WUFBUyxDQUFDO1FBQ1QsVUFBVSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUN0QixDQUFDO0FBQ0gsQ0FBQztBQUVEO3lEQUN5RDtBQUN6RCxNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBaUIsc0JBQXNCLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogZGV0YWNoL2RyaXZlcnMvc2V0VGltZW91dC50cyDigJQgRGVmZXIgZGV0YWNoZWQgd29yayB2aWEgYHNldFRpbWVvdXQoLi4uLCBkZWxheU1zKWAuXG4gKlxuICogUGF0dGVybjogIFByb2R1Y2VyLWNvbnN1bWVyIGJhdGNoIGZsdXNoOyBkZWZlcnJhbCBtZWNoYW5pc20gaXNcbiAqICAgICAgICAgICBgc2V0VGltZW91dGAgd2l0aCBhIGNvbmZpZ3VyYWJsZSBkZWxheSAoZGVmYXVsdCBgMGApLlxuICogUm9sZTogICAgIENyb3NzLXJ1bnRpbWUgXCJuZXh0IG1hY3JvdGFza1wiIGRyaXZlci4gV29ya3MgaW4gYnJvd3NlcnMsXG4gKiAgICAgICAgICAgTm9kZS5qcywgRGVubywgQ2xvdWRmbGFyZSBXb3JrZXJzLCBCdW4sIGV0Yy5cbiAqXG4gKiBXaGVuIHRvIHBpY2sgdGhpczpcbiAqICAgLSBDb25zdW1lciB3YW50cyBhIFNQRUNJRklDIGRlbGF5IChlLmcuIFwic2hpcCB0ZWxlbWV0cnkgaW4gNVxuICogICAgIHNlY29uZHMsIGJhdGNoZWRcIikg4oCUIHBhc3MgYGNyZWF0ZVNldFRpbWVvdXREcml2ZXIoeyBkZWxheU1zOiA1MDAwIH0pYFxuICogICAtIENyb3NzLXJ1bnRpbWUgZGV0YWNoIHdoZXJlIGBzZXRJbW1lZGlhdGVgIGlzbid0IGF2YWlsYWJsZVxuICogICAtIENvYWxlc2NpbmcgaGlnaC1mcmVxdWVuY3kgZXZlbnRzIGludG8gYSBsb3ctZnJlcXVlbmN5IGZsdXNoXG4gKlxuICogQ2F2ZWF0czpcbiAqICAgLSBOb3QgZm9yIGxvdy1sYXRlbmN5IGhvdCBwYXRocyDigJQgbWluaW11bSBkZWxheSBpcyB+NG1zIGluXG4gKiAgICAgYnJvd3NlcnMgcGVyIHRoZSBIVE1MNSBzcGVjLCB+MW1zIGluIE5vZGUuIFVzZVxuICogICAgIGBtaWNyb3Rhc2tCYXRjaERyaXZlcmAgZm9yIHN1Yi1tcyBzY2hlZHVsaW5nLlxuICogICAtIEJyb3dzZXIgdGFiIGZyZWV6aW5nIC8gdGhyb3R0bGluZyBjYW4gZXh0ZW5kIHRoZSBkZWxheVxuICogICAgIHNpZ25pZmljYW50bHkuIERvbid0IHJlbHkgb24gcHJlY2lzZSB0aW1pbmcuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBGbG93Q2hhcnQgfSBmcm9tICcuLi8uLi9idWlsZGVyL3R5cGVzLmpzJztcbmltcG9ydCB7IGFzSW1wbCwgY3JlYXRlSGFuZGxlIH0gZnJvbSAnLi4vaGFuZGxlLmpzJztcbmltcG9ydCB7IHJlZ2lzdGVyLCB1bnJlZ2lzdGVyIH0gZnJvbSAnLi4vcmVnaXN0cnkuanMnO1xuaW1wb3J0IHsgdHlwZSBDaGlsZFJ1bm5lciwgZGVmYXVsdFJ1bkNoaWxkIH0gZnJvbSAnLi4vcnVuQ2hpbGQuanMnO1xuaW1wb3J0IHR5cGUgeyBEZXRhY2hEcml2ZXIsIERldGFjaEhhbmRsZSB9IGZyb20gJy4uL3R5cGVzLmpzJztcblxuaW50ZXJmYWNlIFdvcmtJdGVtIHtcbiAgcmVhZG9ubHkgY2hpbGQ6IEZsb3dDaGFydDtcbiAgcmVhZG9ubHkgaW5wdXQ6IHVua25vd247XG4gIHJlYWRvbmx5IGhhbmRsZTogRGV0YWNoSGFuZGxlO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFNldFRpbWVvdXREcml2ZXJPcHRpb25zIHtcbiAgLyoqIE1pbGxpc2Vjb25kcyB0byB3YWl0IGJlZm9yZSBmbHVzaGluZyB0aGUgYmF0Y2guIERlZmF1bHQgMFxuICAgKiAgKG5leHQgbWFjcm90YXNrKS4gKi9cbiAgcmVhZG9ubHkgZGVsYXlNcz86IG51bWJlcjtcbiAgLyoqIEN1c3RvbSBgcnVuQ2hpbGRgLiBEZWZhdWx0cyB0byBzcGF3bmluZyBhIGBGbG93Q2hhcnRFeGVjdXRvcmAuICovXG4gIHJlYWRvbmx5IHJ1bkNoaWxkPzogQ2hpbGRSdW5uZXI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTZXRUaW1lb3V0RHJpdmVyKG9wdHM6IFNldFRpbWVvdXREcml2ZXJPcHRpb25zID0ge30pOiBEZXRhY2hEcml2ZXIge1xuICBjb25zdCBkZWxheU1zID0gb3B0cy5kZWxheU1zID8/IDA7XG4gIGNvbnN0IHJ1bkNoaWxkID0gb3B0cy5ydW5DaGlsZCA/PyBkZWZhdWx0UnVuQ2hpbGQ7XG4gIGNvbnN0IHF1ZXVlOiBXb3JrSXRlbVtdID0gW107XG4gIGxldCBzY2hlZHVsZWQgPSBmYWxzZTtcblxuICBmdW5jdGlvbiBmbHVzaCgpOiB2b2lkIHtcbiAgICBzY2hlZHVsZWQgPSBmYWxzZTtcbiAgICBjb25zdCBpdGVtcyA9IHF1ZXVlLnNwbGljZSgwKTtcbiAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgaXRlbXMpIHtcbiAgICAgIGV4ZWN1dGVPbmUoaXRlbSwgcnVuQ2hpbGQpLnRoZW4odW5kZWZpbmVkLCB1bmRlZmluZWQpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgbmFtZTogZGVsYXlNcyA9PT0gMCA/ICdzZXQtdGltZW91dCcgOiBgc2V0LXRpbWVvdXQtJHtkZWxheU1zfW1zYCxcbiAgICBjYXBhYmlsaXRpZXM6IHsgYnJvd3NlclNhZmU6IHRydWUsIG5vZGVTYWZlOiB0cnVlLCBlZGdlU2FmZTogdHJ1ZSB9LFxuICAgIHNjaGVkdWxlKGNoaWxkOiBGbG93Q2hhcnQsIGlucHV0OiB1bmtub3duLCByZWZJZDogc3RyaW5nKTogRGV0YWNoSGFuZGxlIHtcbiAgICAgIGNvbnN0IGhhbmRsZSA9IGNyZWF0ZUhhbmRsZShyZWZJZCk7XG4gICAgICByZWdpc3RlcihoYW5kbGUpO1xuICAgICAgcXVldWUucHVzaCh7IGNoaWxkLCBpbnB1dCwgaGFuZGxlIH0pO1xuICAgICAgaWYgKCFzY2hlZHVsZWQpIHtcbiAgICAgICAgc2NoZWR1bGVkID0gdHJ1ZTtcbiAgICAgICAgc2V0VGltZW91dChmbHVzaCwgZGVsYXlNcyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gaGFuZGxlO1xuICAgIH0sXG4gIH07XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGV4ZWN1dGVPbmUoaXRlbTogV29ya0l0ZW0sIHJ1bkNoaWxkOiBDaGlsZFJ1bm5lcik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBpbXBsID0gYXNJbXBsKGl0ZW0uaGFuZGxlKTtcbiAgaW1wbC5fbWFya1J1bm5pbmcoKTtcbiAgdHJ5IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBydW5DaGlsZChpdGVtLmNoaWxkLCBpdGVtLmlucHV0KTtcbiAgICBpbXBsLl9tYXJrRG9uZShyZXN1bHQpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBpbXBsLl9tYXJrRmFpbGVkKGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyIDogbmV3IEVycm9yKFN0cmluZyhlcnIpKSk7XG4gIH0gZmluYWxseSB7XG4gICAgdW5yZWdpc3RlcihpbXBsLmlkKTtcbiAgfVxufVxuXG4vKiogRGVmYXVsdCBzaW5nbGV0b24g4oCUIHplcm8tZGVsYXkgKG5leHQgbWFjcm90YXNrKS4gRm9yIGNvbmZpZ3VyYWJsZVxuICogIGRlbGF5cywgdXNlIGBjcmVhdGVTZXRUaW1lb3V0RHJpdmVyKHsgZGVsYXlNcyB9KWAuICovXG5leHBvcnQgY29uc3Qgc2V0VGltZW91dERyaXZlcjogRGV0YWNoRHJpdmVyID0gY3JlYXRlU2V0VGltZW91dERyaXZlcigpO1xuIl19
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detach/drivers/workerThread.ts — Run detached work in a Node.js
|
|
3
|
+
* Worker Thread (or browser Web Worker).
|
|
4
|
+
*
|
|
5
|
+
* Pattern: Adapter — translates the consumer's child flowchart into
|
|
6
|
+
* a worker message + lifecycle handoff. The worker is owned
|
|
7
|
+
* by the driver instance; restarted on crash.
|
|
8
|
+
* Role: CPU-isolation driver — when detached work is genuinely
|
|
9
|
+
* expensive (heavy parsing, hashing, image processing) and
|
|
10
|
+
* you don't want it blocking the main thread's event loop
|
|
11
|
+
* even for a microtask burst.
|
|
12
|
+
*
|
|
13
|
+
* Caveats / IMPORTANT v1 limitations:
|
|
14
|
+
* - The worker entry point is a CONSUMER-PROVIDED file path / URL —
|
|
15
|
+
* this driver does NOT auto-spawn FlowChartExecutor in a worker.
|
|
16
|
+
* Workers can't `import('footprintjs')` portably without setup,
|
|
17
|
+
* and the worker file's lifecycle differs by runtime
|
|
18
|
+
* (Node Worker vs Web Worker vs Bun). Consumer writes the worker
|
|
19
|
+
* code; this driver just hands them a uniform `(input, handle)`
|
|
20
|
+
* API.
|
|
21
|
+
* - The "child flowchart" parameter is IGNORED in v1 (we only ship
|
|
22
|
+
* the input). The chart shape doesn't survive structuredClone +
|
|
23
|
+
* postMessage anyway. v2 may add a serialization protocol.
|
|
24
|
+
*
|
|
25
|
+
* Two ways to consume:
|
|
26
|
+
*
|
|
27
|
+
* 1. Node.js: pass a file path
|
|
28
|
+
* `createWorkerThreadDriver({ workerScript: '/path/to/worker.js' })`
|
|
29
|
+
*
|
|
30
|
+
* 2. Browser: pass a URL or pre-built Worker instance
|
|
31
|
+
* `createWorkerThreadDriver({ worker: new Worker(url) })`
|
|
32
|
+
*/
|
|
33
|
+
import { asImpl, createHandle } from '../handle.js';
|
|
34
|
+
import { register, unregister } from '../registry.js';
|
|
35
|
+
let nextMessageId = 0;
|
|
36
|
+
export function createWorkerThreadDriver(opts) {
|
|
37
|
+
let worker;
|
|
38
|
+
const inFlight = new Map();
|
|
39
|
+
// If consumer provided a Worker at construction time, bind its
|
|
40
|
+
// 'message' handler eagerly so replies are routed back to handles.
|
|
41
|
+
// Lazy construction (via `workerScript`) defers binding to first use.
|
|
42
|
+
if (opts.worker) {
|
|
43
|
+
worker = opts.worker;
|
|
44
|
+
bindWorker(worker, inFlight);
|
|
45
|
+
}
|
|
46
|
+
function ensureWorker() {
|
|
47
|
+
if (worker)
|
|
48
|
+
return worker;
|
|
49
|
+
if (!opts.workerScript) {
|
|
50
|
+
throw new Error('[detach] workerThreadDriver: provide either `worker` (a constructed Worker) ' +
|
|
51
|
+
'or `workerScript` (a path/URL) at driver creation.');
|
|
52
|
+
}
|
|
53
|
+
// Lazy-import Node's worker_threads — keeps browser bundles clean.
|
|
54
|
+
if (typeof require !== 'function') {
|
|
55
|
+
throw new Error('[detach] workerThreadDriver: `workerScript` requires Node.js (CommonJS `require`).');
|
|
56
|
+
}
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
58
|
+
const { Worker } = require('worker_threads');
|
|
59
|
+
worker = new Worker(opts.workerScript);
|
|
60
|
+
bindWorker(worker, inFlight);
|
|
61
|
+
return worker;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
name: 'worker-thread',
|
|
65
|
+
capabilities: { nodeSafe: true, cpuIsolated: true },
|
|
66
|
+
validate() {
|
|
67
|
+
if (!opts.worker && !opts.workerScript) {
|
|
68
|
+
throw new Error('[detach] workerThreadDriver requires either a pre-built `worker` or a `workerScript` path.');
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
schedule(_child, input, refId) {
|
|
72
|
+
const handle = createHandle(refId);
|
|
73
|
+
register(handle);
|
|
74
|
+
const impl = asImpl(handle);
|
|
75
|
+
impl._markRunning();
|
|
76
|
+
const messageId = nextMessageId++;
|
|
77
|
+
inFlight.set(messageId, { handle });
|
|
78
|
+
try {
|
|
79
|
+
const w = ensureWorker();
|
|
80
|
+
w.postMessage({ messageId, refId, input });
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
impl._markFailed(err instanceof Error ? err : new Error(String(err)));
|
|
84
|
+
unregister(impl.id);
|
|
85
|
+
inFlight.delete(messageId);
|
|
86
|
+
}
|
|
87
|
+
return handle;
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function bindWorker(worker, inFlight) {
|
|
92
|
+
const handler = (msg) => {
|
|
93
|
+
const m = msg;
|
|
94
|
+
if (!m || typeof m.messageId !== 'number')
|
|
95
|
+
return;
|
|
96
|
+
const slot = inFlight.get(m.messageId);
|
|
97
|
+
if (!slot)
|
|
98
|
+
return;
|
|
99
|
+
inFlight.delete(m.messageId);
|
|
100
|
+
const impl = asImpl(slot.handle);
|
|
101
|
+
if (m.ok) {
|
|
102
|
+
impl._markDone(m.result);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
impl._markFailed(new Error(m.error ?? 'worker reported failure'));
|
|
106
|
+
}
|
|
107
|
+
unregister(impl.id);
|
|
108
|
+
};
|
|
109
|
+
// Node Worker (worker_threads): EventEmitter-shape (`on('message', ...)`).
|
|
110
|
+
if (typeof worker.on === 'function')
|
|
111
|
+
worker.on('message', handler);
|
|
112
|
+
// Browser Worker / Web Worker: EventTarget-shape (`addEventListener`).
|
|
113
|
+
else if (typeof worker.addEventListener === 'function') {
|
|
114
|
+
worker.addEventListener('message', (evt) => handler(evt?.data));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ya2VyVGhyZWFkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9kZXRhY2gvZHJpdmVycy93b3JrZXJUaHJlYWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0ErQkc7QUFHSCxPQUFPLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNwRCxPQUFPLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBNkJ0RCxJQUFJLGFBQWEsR0FBRyxDQUFDLENBQUM7QUFFdEIsTUFBTSxVQUFVLHdCQUF3QixDQUFDLElBQStCO0lBQ3RFLElBQUksTUFBOEIsQ0FBQztJQUNuQyxNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsRUFBb0IsQ0FBQztJQUU3QywrREFBK0Q7SUFDL0QsbUVBQW1FO0lBQ25FLHNFQUFzRTtJQUN0RSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNoQixNQUFNLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUNyQixVQUFVLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQy9CLENBQUM7SUFFRCxTQUFTLFlBQVk7UUFDbkIsSUFBSSxNQUFNO1lBQUUsT0FBTyxNQUFNLENBQUM7UUFDMUIsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUN2QixNQUFNLElBQUksS0FBSyxDQUNiLDhFQUE4RTtnQkFDNUUsb0RBQW9ELENBQ3ZELENBQUM7UUFDSixDQUFDO1FBQ0QsbUVBQW1FO1FBQ25FLElBQUksT0FBTyxPQUFPLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDbEMsTUFBTSxJQUFJLEtBQUssQ0FBQyxvRkFBb0YsQ0FBQyxDQUFDO1FBQ3hHLENBQUM7UUFDRCxpRUFBaUU7UUFDakUsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBOEMsQ0FBQztRQUMxRixNQUFNLEdBQUcsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ3ZDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDN0IsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUVELE9BQU87UUFDTCxJQUFJLEVBQUUsZUFBZTtRQUNyQixZQUFZLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUU7UUFDbkQsUUFBUTtZQUNOLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO2dCQUN2QyxNQUFNLElBQUksS0FBSyxDQUFDLDRGQUE0RixDQUFDLENBQUM7WUFDaEgsQ0FBQztRQUNILENBQUM7UUFDRCxRQUFRLENBQUMsTUFBaUIsRUFBRSxLQUFjLEVBQUUsS0FBYTtZQUN2RCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDbkMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ2pCLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUM1QixJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFFcEIsTUFBTSxTQUFTLEdBQUcsYUFBYSxFQUFFLENBQUM7WUFDbEMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBRXBDLElBQUksQ0FBQztnQkFDSCxNQUFNLENBQUMsR0FBRyxZQUFZLEVBQUUsQ0FBQztnQkFDekIsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUM3QyxDQUFDO1lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDYixJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDdEUsVUFBVSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztnQkFDcEIsUUFBUSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM3QixDQUFDO1lBRUQsT0FBTyxNQUFNLENBQUM7UUFDaEIsQ0FBQztLQUNGLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBUyxVQUFVLENBQUMsTUFBa0IsRUFBRSxRQUErQjtJQUNyRSxNQUFNLE9BQU8sR0FBRyxDQUFDLEdBQVksRUFBUSxFQUFFO1FBQ3JDLE1BQU0sQ0FBQyxHQUFHLEdBQXlGLENBQUM7UUFDcEcsSUFBSSxDQUFDLENBQUMsSUFBSSxPQUFPLENBQUMsQ0FBQyxTQUFTLEtBQUssUUFBUTtZQUFFLE9BQU87UUFDbEQsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDdkMsSUFBSSxDQUFDLElBQUk7WUFBRSxPQUFPO1FBQ2xCLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRTdCLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDakMsSUFBSSxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDVCxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMzQixDQUFDO2FBQU0sQ0FBQztZQUNOLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSSx5QkFBeUIsQ0FBQyxDQUFDLENBQUM7UUFDcEUsQ0FBQztRQUNELFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDdEIsQ0FBQyxDQUFDO0lBRUYsMkVBQTJFO0lBQzNFLElBQUksT0FBTyxNQUFNLENBQUMsRUFBRSxLQUFLLFVBQVU7UUFBRSxNQUFNLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNuRSx1RUFBdUU7U0FDbEUsSUFBSSxPQUFPLE1BQU0sQ0FBQyxnQkFBZ0IsS0FBSyxVQUFVLEVBQUUsQ0FBQztRQUN2RCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLENBQUMsR0FBWSxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUUsR0FBMEIsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ25HLENBQUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBkZXRhY2gvZHJpdmVycy93b3JrZXJUaHJlYWQudHMg4oCUIFJ1biBkZXRhY2hlZCB3b3JrIGluIGEgTm9kZS5qc1xuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV29ya2VyIFRocmVhZCAob3IgYnJvd3NlciBXZWIgV29ya2VyKS5cbiAqXG4gKiBQYXR0ZXJuOiAgQWRhcHRlciDigJQgdHJhbnNsYXRlcyB0aGUgY29uc3VtZXIncyBjaGlsZCBmbG93Y2hhcnQgaW50b1xuICogICAgICAgICAgIGEgd29ya2VyIG1lc3NhZ2UgKyBsaWZlY3ljbGUgaGFuZG9mZi4gVGhlIHdvcmtlciBpcyBvd25lZFxuICogICAgICAgICAgIGJ5IHRoZSBkcml2ZXIgaW5zdGFuY2U7IHJlc3RhcnRlZCBvbiBjcmFzaC5cbiAqIFJvbGU6ICAgICBDUFUtaXNvbGF0aW9uIGRyaXZlciDigJQgd2hlbiBkZXRhY2hlZCB3b3JrIGlzIGdlbnVpbmVseVxuICogICAgICAgICAgIGV4cGVuc2l2ZSAoaGVhdnkgcGFyc2luZywgaGFzaGluZywgaW1hZ2UgcHJvY2Vzc2luZykgYW5kXG4gKiAgICAgICAgICAgeW91IGRvbid0IHdhbnQgaXQgYmxvY2tpbmcgdGhlIG1haW4gdGhyZWFkJ3MgZXZlbnQgbG9vcFxuICogICAgICAgICAgIGV2ZW4gZm9yIGEgbWljcm90YXNrIGJ1cnN0LlxuICpcbiAqIENhdmVhdHMgLyBJTVBPUlRBTlQgdjEgbGltaXRhdGlvbnM6XG4gKiAgIC0gVGhlIHdvcmtlciBlbnRyeSBwb2ludCBpcyBhIENPTlNVTUVSLVBST1ZJREVEIGZpbGUgcGF0aCAvIFVSTCDigJRcbiAqICAgICB0aGlzIGRyaXZlciBkb2VzIE5PVCBhdXRvLXNwYXduIEZsb3dDaGFydEV4ZWN1dG9yIGluIGEgd29ya2VyLlxuICogICAgIFdvcmtlcnMgY2FuJ3QgYGltcG9ydCgnZm9vdHByaW50anMnKWAgcG9ydGFibHkgd2l0aG91dCBzZXR1cCxcbiAqICAgICBhbmQgdGhlIHdvcmtlciBmaWxlJ3MgbGlmZWN5Y2xlIGRpZmZlcnMgYnkgcnVudGltZVxuICogICAgIChOb2RlIFdvcmtlciB2cyBXZWIgV29ya2VyIHZzIEJ1bikuIENvbnN1bWVyIHdyaXRlcyB0aGUgd29ya2VyXG4gKiAgICAgY29kZTsgdGhpcyBkcml2ZXIganVzdCBoYW5kcyB0aGVtIGEgdW5pZm9ybSBgKGlucHV0LCBoYW5kbGUpYFxuICogICAgIEFQSS5cbiAqICAgLSBUaGUgXCJjaGlsZCBmbG93Y2hhcnRcIiBwYXJhbWV0ZXIgaXMgSUdOT1JFRCBpbiB2MSAod2Ugb25seSBzaGlwXG4gKiAgICAgdGhlIGlucHV0KS4gVGhlIGNoYXJ0IHNoYXBlIGRvZXNuJ3Qgc3Vydml2ZSBzdHJ1Y3R1cmVkQ2xvbmUgK1xuICogICAgIHBvc3RNZXNzYWdlIGFueXdheS4gdjIgbWF5IGFkZCBhIHNlcmlhbGl6YXRpb24gcHJvdG9jb2wuXG4gKlxuICogVHdvIHdheXMgdG8gY29uc3VtZTpcbiAqXG4gKiAgIDEuIE5vZGUuanM6IHBhc3MgYSBmaWxlIHBhdGhcbiAqICAgICAgYGNyZWF0ZVdvcmtlclRocmVhZERyaXZlcih7IHdvcmtlclNjcmlwdDogJy9wYXRoL3RvL3dvcmtlci5qcycgfSlgXG4gKlxuICogICAyLiBCcm93c2VyOiBwYXNzIGEgVVJMIG9yIHByZS1idWlsdCBXb3JrZXIgaW5zdGFuY2VcbiAqICAgICAgYGNyZWF0ZVdvcmtlclRocmVhZERyaXZlcih7IHdvcmtlcjogbmV3IFdvcmtlcih1cmwpIH0pYFxuICovXG5cbmltcG9ydCB0eXBlIHsgRmxvd0NoYXJ0IH0gZnJvbSAnLi4vLi4vYnVpbGRlci90eXBlcy5qcyc7XG5pbXBvcnQgeyBhc0ltcGwsIGNyZWF0ZUhhbmRsZSB9IGZyb20gJy4uL2hhbmRsZS5qcyc7XG5pbXBvcnQgeyByZWdpc3RlciwgdW5yZWdpc3RlciB9IGZyb20gJy4uL3JlZ2lzdHJ5LmpzJztcbmltcG9ydCB0eXBlIHsgRGV0YWNoRHJpdmVyLCBEZXRhY2hIYW5kbGUgfSBmcm9tICcuLi90eXBlcy5qcyc7XG5cbi8vIE5vZGUtb25seSBDb21tb25KUyBgcmVxdWlyZWAuIFdlIGRvbid0IHNoaXAgQHR5cGVzL25vZGUsIHNvIGRlY2xhcmVcbi8vIHRoZSBtaW5pbWFsIHNoYXBlIGhlcmUuIFVzZWQgb25seSBpbiB0aGUgbGF6eSBgd29ya2VyU2NyaXB0YCBwYXRoO1xuLy8gYnJvd3NlciBjb25zdW1lcnMgcGFzcyBhIHByZS1jb25zdHJ1Y3RlZCBgV29ya2VyYCBhbmQgbmV2ZXIgaGl0IGl0LlxuZGVjbGFyZSBjb25zdCByZXF1aXJlOiAoKG1vZDogc3RyaW5nKSA9PiB1bmtub3duKSB8IHVuZGVmaW5lZDtcblxuaW50ZXJmYWNlIFdvcmtlckxpa2Uge1xuICBwb3N0TWVzc2FnZShtZXNzYWdlOiB1bmtub3duLCB0cmFuc2Zlcj86IFRyYW5zZmVyYWJsZVtdKTogdm9pZDtcbiAgdGVybWluYXRlPygpOiB1bmtub3duO1xuICBvbj8oZXZlbnQ6IHN0cmluZywgbGlzdGVuZXI6IChtc2c6IHVua25vd24pID0+IHZvaWQpOiB2b2lkO1xuICBhZGRFdmVudExpc3RlbmVyPyhldmVudDogc3RyaW5nLCBsaXN0ZW5lcjogKG1zZzogdW5rbm93bikgPT4gdm9pZCk6IHZvaWQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgV29ya2VyVGhyZWFkRHJpdmVyT3B0aW9ucyB7XG4gIC8qKiBQcmUtY29uc3RydWN0ZWQgV29ya2VyIGluc3RhbmNlLiBQYXNzIGVpdGhlciB0aGlzIE9SXG4gICAqICBgd29ya2VyU2NyaXB0YCDigJQgbm90IGJvdGguICovXG4gIHJlYWRvbmx5IHdvcmtlcj86IFdvcmtlckxpa2U7XG4gIC8qKiBQYXRoIC8gVVJMIHRvIHRoZSB3b3JrZXIgc2NyaXB0LiBVc2VkIG9ubHkgd2hlbiBgd29ya2VyYCBpc1xuICAgKiAgbm90IHByb3ZpZGVkOyB0aGUgZHJpdmVyIGNvbnN0cnVjdHMgYSBXb3JrZXIgZnJvbSB0aGlzIG9uIGRlbWFuZFxuICAgKiAgKE5vZGUgYHdvcmtlcl90aHJlYWRzYCBBUEkpLiAqL1xuICByZWFkb25seSB3b3JrZXJTY3JpcHQ/OiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBJbkZsaWdodCB7XG4gIHJlYWRvbmx5IGhhbmRsZTogRGV0YWNoSGFuZGxlO1xufVxuXG5sZXQgbmV4dE1lc3NhZ2VJZCA9IDA7XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVXb3JrZXJUaHJlYWREcml2ZXIob3B0czogV29ya2VyVGhyZWFkRHJpdmVyT3B0aW9ucyk6IERldGFjaERyaXZlciB7XG4gIGxldCB3b3JrZXI6IFdvcmtlckxpa2UgfCB1bmRlZmluZWQ7XG4gIGNvbnN0IGluRmxpZ2h0ID0gbmV3IE1hcDxudW1iZXIsIEluRmxpZ2h0PigpO1xuXG4gIC8vIElmIGNvbnN1bWVyIHByb3ZpZGVkIGEgV29ya2VyIGF0IGNvbnN0cnVjdGlvbiB0aW1lLCBiaW5kIGl0c1xuICAvLyAnbWVzc2FnZScgaGFuZGxlciBlYWdlcmx5IHNvIHJlcGxpZXMgYXJlIHJvdXRlZCBiYWNrIHRvIGhhbmRsZXMuXG4gIC8vIExhenkgY29uc3RydWN0aW9uICh2aWEgYHdvcmtlclNjcmlwdGApIGRlZmVycyBiaW5kaW5nIHRvIGZpcnN0IHVzZS5cbiAgaWYgKG9wdHMud29ya2VyKSB7XG4gICAgd29ya2VyID0gb3B0cy53b3JrZXI7XG4gICAgYmluZFdvcmtlcih3b3JrZXIsIGluRmxpZ2h0KTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGVuc3VyZVdvcmtlcigpOiBXb3JrZXJMaWtlIHtcbiAgICBpZiAod29ya2VyKSByZXR1cm4gd29ya2VyO1xuICAgIGlmICghb3B0cy53b3JrZXJTY3JpcHQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ1tkZXRhY2hdIHdvcmtlclRocmVhZERyaXZlcjogcHJvdmlkZSBlaXRoZXIgYHdvcmtlcmAgKGEgY29uc3RydWN0ZWQgV29ya2VyKSAnICtcbiAgICAgICAgICAnb3IgYHdvcmtlclNjcmlwdGAgKGEgcGF0aC9VUkwpIGF0IGRyaXZlciBjcmVhdGlvbi4nLFxuICAgICAgKTtcbiAgICB9XG4gICAgLy8gTGF6eS1pbXBvcnQgTm9kZSdzIHdvcmtlcl90aHJlYWRzIOKAlCBrZWVwcyBicm93c2VyIGJ1bmRsZXMgY2xlYW4uXG4gICAgaWYgKHR5cGVvZiByZXF1aXJlICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1tkZXRhY2hdIHdvcmtlclRocmVhZERyaXZlcjogYHdvcmtlclNjcmlwdGAgcmVxdWlyZXMgTm9kZS5qcyAoQ29tbW9uSlMgYHJlcXVpcmVgKS4nKTtcbiAgICB9XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB7IFdvcmtlciB9ID0gcmVxdWlyZSgnd29ya2VyX3RocmVhZHMnKSBhcyB7IFdvcmtlcjogbmV3IChzOiBzdHJpbmcpID0+IFdvcmtlckxpa2UgfTtcbiAgICB3b3JrZXIgPSBuZXcgV29ya2VyKG9wdHMud29ya2VyU2NyaXB0KTtcbiAgICBiaW5kV29ya2VyKHdvcmtlciwgaW5GbGlnaHQpO1xuICAgIHJldHVybiB3b3JrZXI7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG5hbWU6ICd3b3JrZXItdGhyZWFkJyxcbiAgICBjYXBhYmlsaXRpZXM6IHsgbm9kZVNhZmU6IHRydWUsIGNwdUlzb2xhdGVkOiB0cnVlIH0sXG4gICAgdmFsaWRhdGUoKTogdm9pZCB7XG4gICAgICBpZiAoIW9wdHMud29ya2VyICYmICFvcHRzLndvcmtlclNjcmlwdCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1tkZXRhY2hdIHdvcmtlclRocmVhZERyaXZlciByZXF1aXJlcyBlaXRoZXIgYSBwcmUtYnVpbHQgYHdvcmtlcmAgb3IgYSBgd29ya2VyU2NyaXB0YCBwYXRoLicpO1xuICAgICAgfVxuICAgIH0sXG4gICAgc2NoZWR1bGUoX2NoaWxkOiBGbG93Q2hhcnQsIGlucHV0OiB1bmtub3duLCByZWZJZDogc3RyaW5nKTogRGV0YWNoSGFuZGxlIHtcbiAgICAgIGNvbnN0IGhhbmRsZSA9IGNyZWF0ZUhhbmRsZShyZWZJZCk7XG4gICAgICByZWdpc3RlcihoYW5kbGUpO1xuICAgICAgY29uc3QgaW1wbCA9IGFzSW1wbChoYW5kbGUpO1xuICAgICAgaW1wbC5fbWFya1J1bm5pbmcoKTtcblxuICAgICAgY29uc3QgbWVzc2FnZUlkID0gbmV4dE1lc3NhZ2VJZCsrO1xuICAgICAgaW5GbGlnaHQuc2V0KG1lc3NhZ2VJZCwgeyBoYW5kbGUgfSk7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHcgPSBlbnN1cmVXb3JrZXIoKTtcbiAgICAgICAgdy5wb3N0TWVzc2FnZSh7IG1lc3NhZ2VJZCwgcmVmSWQsIGlucHV0IH0pO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGltcGwuX21hcmtGYWlsZWQoZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIgOiBuZXcgRXJyb3IoU3RyaW5nKGVycikpKTtcbiAgICAgICAgdW5yZWdpc3RlcihpbXBsLmlkKTtcbiAgICAgICAgaW5GbGlnaHQuZGVsZXRlKG1lc3NhZ2VJZCk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBoYW5kbGU7XG4gICAgfSxcbiAgfTtcbn1cblxuZnVuY3Rpb24gYmluZFdvcmtlcih3b3JrZXI6IFdvcmtlckxpa2UsIGluRmxpZ2h0OiBNYXA8bnVtYmVyLCBJbkZsaWdodD4pOiB2b2lkIHtcbiAgY29uc3QgaGFuZGxlciA9IChtc2c6IHVua25vd24pOiB2b2lkID0+IHtcbiAgICBjb25zdCBtID0gbXNnIGFzIHsgbWVzc2FnZUlkPzogbnVtYmVyOyBvaz86IGJvb2xlYW47IHJlc3VsdD86IHVua25vd247IGVycm9yPzogc3RyaW5nIH0gfCB1bmRlZmluZWQ7XG4gICAgaWYgKCFtIHx8IHR5cGVvZiBtLm1lc3NhZ2VJZCAhPT0gJ251bWJlcicpIHJldHVybjtcbiAgICBjb25zdCBzbG90ID0gaW5GbGlnaHQuZ2V0KG0ubWVzc2FnZUlkKTtcbiAgICBpZiAoIXNsb3QpIHJldHVybjtcbiAgICBpbkZsaWdodC5kZWxldGUobS5tZXNzYWdlSWQpO1xuXG4gICAgY29uc3QgaW1wbCA9IGFzSW1wbChzbG90LmhhbmRsZSk7XG4gICAgaWYgKG0ub2spIHtcbiAgICAgIGltcGwuX21hcmtEb25lKG0ucmVzdWx0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgaW1wbC5fbWFya0ZhaWxlZChuZXcgRXJyb3IobS5lcnJvciA/PyAnd29ya2VyIHJlcG9ydGVkIGZhaWx1cmUnKSk7XG4gICAgfVxuICAgIHVucmVnaXN0ZXIoaW1wbC5pZCk7XG4gIH07XG5cbiAgLy8gTm9kZSBXb3JrZXIgKHdvcmtlcl90aHJlYWRzKTogRXZlbnRFbWl0dGVyLXNoYXBlIChgb24oJ21lc3NhZ2UnLCAuLi4pYCkuXG4gIGlmICh0eXBlb2Ygd29ya2VyLm9uID09PSAnZnVuY3Rpb24nKSB3b3JrZXIub24oJ21lc3NhZ2UnLCBoYW5kbGVyKTtcbiAgLy8gQnJvd3NlciBXb3JrZXIgLyBXZWIgV29ya2VyOiBFdmVudFRhcmdldC1zaGFwZSAoYGFkZEV2ZW50TGlzdGVuZXJgKS5cbiAgZWxzZSBpZiAodHlwZW9mIHdvcmtlci5hZGRFdmVudExpc3RlbmVyID09PSAnZnVuY3Rpb24nKSB7XG4gICAgd29ya2VyLmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCAoZXZ0OiB1bmtub3duKSA9PiBoYW5kbGVyKChldnQgYXMgeyBkYXRhPzogdW5rbm93biB9KT8uZGF0YSkpO1xuICB9XG59XG4iXX0=
|