footprintjs 4.15.0 → 4.17.1
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/CLAUDE.md +133 -1
- package/README.md +1 -0
- package/dist/detach.js +78 -0
- 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 +69 -18
- package/dist/esm/lib/engine/handlers/SubflowInputMapper.js +4 -4
- package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +85 -102
- package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +44 -32
- package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +6 -5
- package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +3 -1
- 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/narrative/types.js +1 -1
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +121 -71
- package/dist/esm/lib/engine/types.js +1 -1
- 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 +54 -1
- package/dist/esm/lib/reactive/createTypedScope.js +10 -8
- package/dist/esm/lib/reactive/types.js +3 -1
- package/dist/esm/lib/recorder/CompositeRecorder.js +3 -1
- package/dist/esm/lib/recorder/InOutRecorder.js +205 -0
- 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 +77 -27
- package/dist/esm/lib/recorder/qualityTrace.js +4 -5
- package/dist/esm/lib/runner/ComposableRunner.js +1 -1
- package/dist/esm/lib/runner/ExecutionRuntime.js +20 -4
- package/dist/esm/lib/runner/FlowChartExecutor.js +233 -84
- package/dist/esm/lib/runner/RunContext.js +5 -4
- 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/recorders.js +2 -9
- package/dist/esm/trace.js +2 -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 +69 -18
- package/dist/lib/engine/handlers/SubflowInputMapper.js +4 -4
- package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +85 -102
- package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +44 -32
- package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +6 -5
- package/dist/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +3 -1
- 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/narrative/types.js +1 -1
- package/dist/lib/engine/traversal/FlowchartTraverser.js +121 -71
- package/dist/lib/engine/types.js +1 -1
- 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 +54 -1
- package/dist/lib/reactive/createTypedScope.js +10 -8
- package/dist/lib/reactive/types.js +3 -1
- package/dist/lib/recorder/CompositeRecorder.js +3 -1
- package/dist/lib/recorder/InOutRecorder.js +210 -0
- 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 +77 -27
- package/dist/lib/recorder/qualityTrace.js +4 -5
- package/dist/lib/runner/ComposableRunner.js +1 -1
- package/dist/lib/runner/ExecutionRuntime.js +20 -4
- package/dist/lib/runner/FlowChartExecutor.js +233 -84
- package/dist/lib/runner/RunContext.js +5 -4
- 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/recorders.js +2 -9
- package/dist/trace.js +7 -2
- 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/narrative/CombinedNarrativeRecorder.d.ts +0 -2
- package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +2 -0
- package/dist/types/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +2 -0
- package/dist/types/lib/engine/narrative/types.d.ts +41 -0
- package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +11 -1
- package/dist/types/lib/engine/types.d.ts +13 -17
- package/dist/types/lib/pause/types.d.ts +38 -0
- package/dist/types/lib/reactive/types.d.ts +4 -0
- package/dist/types/lib/recorder/InOutRecorder.d.ts +165 -0
- package/dist/types/lib/recorder/TopologyRecorder.d.ts +14 -0
- package/dist/types/lib/runner/ComposableRunner.d.ts +2 -1
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +62 -26
- package/dist/types/lib/runner/RunContext.d.ts +2 -3
- 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/recorders.d.ts +9 -5
- package/dist/types/trace.d.ts +2 -0
- package/package.json +8 -2
package/CLAUDE.md
CHANGED
|
@@ -269,6 +269,7 @@ const llmCommit = findCommit(commitLog, 'call-llm', 'adapterRawResponse');
|
|
|
269
269
|
| `KeyedRecorder<T>` | abstract class | Base for 1:1 Map-based recorders |
|
|
270
270
|
| `SequenceRecorder<T>` | abstract class | Base for 1:N ordered sequence recorders (has `getEntryRanges()` for O(1) time-travel) |
|
|
271
271
|
| `topologyRecorder()` / `TopologyRecorder` | factory / class | Live composition graph for streaming consumers (subflow nodes + control-flow edges) |
|
|
272
|
+
| `inOutRecorder()` / `InOutRecorder` | factory / class | Chart in/out stream — `entry`/`exit` pairs at every chart boundary (top-level run + every subflow) |
|
|
272
273
|
|
|
273
274
|
### TopologyRecorder — Composition Graph for Streaming Consumers
|
|
274
275
|
|
|
@@ -344,7 +345,81 @@ When a fork-branch or decision-branch target is also a subflow, the subsequent `
|
|
|
344
345
|
|
|
345
346
|
**For downstream libraries:** compose, don't duplicate. An agent-shaped recorder should wrap a `topologyRecorder()` internally and translate topology nodes into agent semantics — not re-implement subflow-stack + fork + decision tracking.
|
|
346
347
|
|
|
347
|
-
Example: [examples/flow-
|
|
348
|
+
Example: [examples/runtime-features/flow-recorder/06-topology.ts](examples/runtime-features/flow-recorder/06-topology.ts)
|
|
349
|
+
|
|
350
|
+
### InOutRecorder — Chart In/Out Stream (every chart boundary, root + subflows)
|
|
351
|
+
|
|
352
|
+
**One-liner:** captures every chart execution (top-level run AND every subflow) as an `entry`/`exit` boundary pair, with the `inputMapper`/`outputMapper` payloads attached. Combined with `TopologyRecorder` (composition shape) this gives downstream layers the universal "step" primitive — `runtimeStageId` binds them.
|
|
353
|
+
|
|
354
|
+
**Mental model:**
|
|
355
|
+
|
|
356
|
+
```
|
|
357
|
+
user input ─►┌───────────────── run ─────────────────┐ ◄─ user output
|
|
358
|
+
│ __root__#0 onRunStart / onRunEnd │
|
|
359
|
+
│ │
|
|
360
|
+
│ inputMapper outputMapper │
|
|
361
|
+
│ │ │ │
|
|
362
|
+
│ parent ──►┤ subflow ├──► parent │
|
|
363
|
+
│ │ │ │
|
|
364
|
+
│ └── runtimeStageId ───┘ │
|
|
365
|
+
│ │
|
|
366
|
+
└────────────────────────────────────────┘
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Each chart execution → 2 boundaries:
|
|
370
|
+
- **Root** — `onRunStart` / `onRunEnd` fire ONCE per `executor.run()`. `subflowId: '__root__'`, `depth: 0`, `isRoot: true`.
|
|
371
|
+
- **Subflow** — `onSubflowEntry` / `onSubflowExit` fire once per mounted subflow. Nested under root in the path tree (`['__root__', 'sf-x']`, depth 1+).
|
|
372
|
+
|
|
373
|
+
Loop re-entry produces distinct pairs because the parent stage's executionIndex increments.
|
|
374
|
+
|
|
375
|
+
**What it IS:**
|
|
376
|
+
- `SequenceRecorder<InOutEntry>` — flat ordered list + per-`runtimeStageId` index
|
|
377
|
+
- Captures the **payloads** at every chart boundary (what flowed IN and OUT)
|
|
378
|
+
- Path-aware: `subflowPath` is decomposed from the engine's path-prefixed `subflowId` and rooted under `__root__`
|
|
379
|
+
- Domain-agnostic — knows nothing about LLMs, tools, agents
|
|
380
|
+
|
|
381
|
+
**What it ISN'T:**
|
|
382
|
+
- Not a composition graph — that's `TopologyRecorder` (shape) vs this (data crossing each boundary)
|
|
383
|
+
- Not a full execution tree — that's `StageContext`
|
|
384
|
+
- Not agent-specific — domain libraries (e.g. agentfootprint) compose it; footprintjs owns it
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import { inOutRecorder, ROOT_SUBFLOW_ID } from 'footprintjs/trace';
|
|
388
|
+
|
|
389
|
+
const inOut = inOutRecorder();
|
|
390
|
+
executor.attachCombinedRecorder(inOut);
|
|
391
|
+
|
|
392
|
+
await executor.run({ input });
|
|
393
|
+
|
|
394
|
+
inOut.getSteps(); // entry boundaries (timeline; root is first step)
|
|
395
|
+
inOut.getBoundary(runtimeStageId); // { entry, exit } pair for one execution
|
|
396
|
+
inOut.getRootBoundary(); // { entry, exit } for the top-level run
|
|
397
|
+
inOut.getBoundaries(); // flat list (entry+exit interleaved)
|
|
398
|
+
inOut.getEntryRanges(); // O(1) per-step range index for time-travel
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**`InOutEntry` shape:**
|
|
402
|
+
|
|
403
|
+
| Field | Description |
|
|
404
|
+
|---|---|
|
|
405
|
+
| `runtimeStageId` | Same value for the entry/exit pair of one execution. Top-level run uses `'__root__#0'`. |
|
|
406
|
+
| `subflowId` | Path-prefixed engine id. Top-level → `'__root__'`. Subflow → `'sf-outer'` or `'sf-outer/sf-inner'`. |
|
|
407
|
+
| `localSubflowId` | Last segment of `subflowId` |
|
|
408
|
+
| `subflowName` | Human-readable display name (`'Run'` for the top-level run) |
|
|
409
|
+
| `description` | Build-time description (carries taxonomy markers like `'Agent: ReAct loop'`). Undefined for root. |
|
|
410
|
+
| `subflowPath` | Decomposition of `subflowId` rooted under `__root__`: `['__root__']` for root, `['__root__', 'sf-x']` for top-level subflow |
|
|
411
|
+
| `depth` | Root → 0. First-level subflow → 1. |
|
|
412
|
+
| `phase` | `'entry'` or `'exit'` |
|
|
413
|
+
| `payload` | `entry`: `inputMapper` result (subflow) or `run({input})` (root); `exit`: shared state at exit (subflow) or chart return value (root) |
|
|
414
|
+
| `isRoot` | True only for the synthetic root pair from `onRunStart` / `onRunEnd` |
|
|
415
|
+
|
|
416
|
+
**Pause semantics:** when a stage pauses inside a subflow, the engine re-throws without firing `onSubflowExit` (or `onRunEnd`). The chart has an `entry` with no matching `exit` until resume completes. `getBoundary()` returns `{ entry, exit: undefined }` in that case.
|
|
417
|
+
|
|
418
|
+
**Engine events:** `FlowRecorder.onRunStart(event)` and `onRunEnd(event)` carry `event.payload` (the run's input or output). Fire ONCE per top-level `executor.run()` — not for subflow traversers (those fire `onSubflowEntry`/`onSubflowExit` instead). Available on the `IControlFlowNarrative` interface and the `FlowRecorderDispatcher`.
|
|
419
|
+
|
|
420
|
+
**For downstream libraries:** compose, don't duplicate. A domain-flavored step graph (e.g., agentfootprint's `StepGraph`) should consume `InOutRecorder` output and label each entry by inspecting the payload through domain semantics — not re-walk subflow events.
|
|
421
|
+
|
|
422
|
+
Example: [examples/runtime-features/flow-recorder/07-inout.ts](examples/runtime-features/flow-recorder/07-inout.ts)
|
|
348
423
|
|
|
349
424
|
**Two recorder base classes** — choose based on data shape:
|
|
350
425
|
|
|
@@ -473,6 +548,63 @@ This closes the long-standing gap where `$metric` / `$debug` went to side bags n
|
|
|
473
548
|
|
|
474
549
|
Example: [examples/runtime-features/emit/01-custom-events.ts](examples/runtime-features/emit/01-custom-events.ts).
|
|
475
550
|
|
|
551
|
+
## Detach — Fire-and-Forget Child Flowcharts (`footprintjs/detach`)
|
|
552
|
+
|
|
553
|
+
Schedule a child chart on a chosen **driver** without blocking the parent stage. Two semantics + two surfaces:
|
|
554
|
+
|
|
555
|
+
| Method | Returns | Caller |
|
|
556
|
+
|---------------------------------------|----------------|------------------------------------------|
|
|
557
|
+
| `scope.$detachAndJoinLater(d, c, i)` | `DetachHandle` | Inside a stage (refId = stage's runtimeStageId) |
|
|
558
|
+
| `scope.$detachAndForget(d, c, i)` | `void` | Inside a stage (handle discarded) |
|
|
559
|
+
| `executor.detachAndJoinLater(d, c, i)`| `DetachHandle` | Outside any chart (refId prefix `__executor__`) |
|
|
560
|
+
| `executor.detachAndForget(d, c, i)` | `void` | Outside any chart |
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
import { microtaskBatchDriver } from 'footprintjs/detach';
|
|
564
|
+
|
|
565
|
+
flowChart('process', async (scope) => {
|
|
566
|
+
scope.result = await heavyWork();
|
|
567
|
+
scope.$detachAndForget(microtaskBatchDriver, telemetryChart, { event: 'done' });
|
|
568
|
+
}, 'process').build();
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
**Built-in drivers** (more in v4.17.1):
|
|
572
|
+
- `microtaskBatchDriver` — coalesces N detaches into one `queueMicrotask` flush. Default for in-process.
|
|
573
|
+
- `immediateDriver` — runs sync inside `schedule()`. Test fixture / debugging aid.
|
|
574
|
+
|
|
575
|
+
**Custom drivers**: `createMicrotaskBatchDriver(runChild)` / `createImmediateDriver(runChild)` accept a custom `ChildRunner` so consumers can wrap the executor (e.g., for tracing context). Drivers are **passed explicitly** as the first arg — no library-default to keep the engine free of driver imports.
|
|
576
|
+
|
|
577
|
+
**Handle**: `{ id, status: 'queued'|'running'|'done'|'failed', result?, error?, wait() }`. NOT Promise-shaped (no `.then()` — defeats fire-and-forget). Status is sync property; `wait()` returns a CACHED Promise on every call.
|
|
578
|
+
|
|
579
|
+
**Graceful shutdown**: `flushAllDetached({ timeoutMs })` drains every in-flight handle to terminal. Use in SIGTERM handlers / test cleanup. Returns `{ done, failed, pending }` — `pending === 0` means full drain.
|
|
580
|
+
|
|
581
|
+
**Gotcha**: don't store handles in shared state — `StageContext.setValue` calls `structuredClone`, which drops the handle's class prototype (and `.wait()` method). Keep handles in closure-local variables. The builder-native `addDetachAndJoinLater` enforces this by delivering the handle to a consumer-supplied `onHandle` callback rather than to a shared-state key.
|
|
582
|
+
|
|
583
|
+
**Builder-native composition** — make detach a labeled chart stage (visible in narrative + visualizations):
|
|
584
|
+
|
|
585
|
+
```ts
|
|
586
|
+
const handles: DetachHandle[] = [];
|
|
587
|
+
const chart = flowChart('process', processFn, 'process')
|
|
588
|
+
.addDetachAndForget('telemetry', telemetryChart, {
|
|
589
|
+
driver: microtaskBatchDriver,
|
|
590
|
+
inputMapper: (scope) => ({ event: 'processed', orderId: scope.orderId }),
|
|
591
|
+
})
|
|
592
|
+
.addDetachAndJoinLater('eval', evalChart, {
|
|
593
|
+
driver: microtaskBatchDriver,
|
|
594
|
+
inputMapper: (s) => s.input,
|
|
595
|
+
onHandle: (h) => handles.push(h),
|
|
596
|
+
})
|
|
597
|
+
.addFunction('join', async (scope) => {
|
|
598
|
+
const settled = await Promise.all(handles.map((h) => h.wait()));
|
|
599
|
+
scope.results = settled.map((r) => r.result);
|
|
600
|
+
}, 'join')
|
|
601
|
+
.build();
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
Pure sugar over `addFunction` — zero engine changes. For server-side concurrent runs, allocate a fresh `handles` closure per run (factory-build the chart) so handles don't bleed across requests.
|
|
605
|
+
|
|
606
|
+
Examples: [examples/runtime-features/detach/](examples/runtime-features/detach/) — 7 scenarios (telemetry, fan-out, bare-executor, immediate driver, error handling, status polling, graceful shutdown).
|
|
607
|
+
|
|
476
608
|
## Combined Recorder
|
|
477
609
|
|
|
478
610
|
A `CombinedRecorder` is an observer that hooks into multiple event streams (scope data-flow, control-flow, AND emit — all three channels). One object, one `id`, one `attachCombinedRecorder()` call — the library routes to the right channels via runtime method-shape detection.
|
package/README.md
CHANGED
|
@@ -164,6 +164,7 @@ The LLM calls the tool, gets back the decision and causal trace, and explains th
|
|
|
164
164
|
| **Cancellation** | AbortSignal, timeout, early termination via `scope.$break(reason?)` with optional reason |
|
|
165
165
|
| **Subflow break propagation** | Mount a subflow with `propagateBreak: true` — inner `$break` terminates the parent loop, with drill-down preserved |
|
|
166
166
|
| **Emit channel** | `scope.$emit(name, payload)` — user-authored structured events to `EmitRecorder`, pass-through, zero-allocation when no recorder attached, redactable via `emitPatterns` |
|
|
167
|
+
| **Detach** *(new in 4.17)* | `scope.$detachAndForget(driver, child, input)` and `scope.$detachAndJoinLater(driver, child, input)` — fire-and-forget child flowcharts via the `footprintjs/detach` subpath. Six built-in drivers (microtask / immediate / setImmediate / setTimeout / sendBeacon / workerThread) + custom-driver protocol. Builder-native composition (`addDetachAndForget` / `addDetachAndJoinLater`) makes detach a labeled chart stage. `flushAllDetached()` for graceful shutdown. [Guide →](https://footprintjs.github.io/footPrint/guides/patterns/detach/) |
|
|
167
168
|
|
|
168
169
|
---
|
|
169
170
|
|
package/dist/detach.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* footprintjs/detach — Fire-and-forget child flowchart execution.
|
|
4
|
+
*
|
|
5
|
+
* A library of scheduling drivers that lets you detach work from the
|
|
6
|
+
* parent stage's hot path. The parent stage returns immediately; the
|
|
7
|
+
* child runs on whichever driver you pick (microtask, immediate, plus
|
|
8
|
+
* setImmediate / setTimeout / sendBeacon / worker-thread in v4.17.1+).
|
|
9
|
+
*
|
|
10
|
+
* Two shapes:
|
|
11
|
+
*
|
|
12
|
+
* - `detachAndJoinLater(driver, child, input)` — returns a `DetachHandle`
|
|
13
|
+
* you can `wait()` on (Promise) or read `.status` from (sync).
|
|
14
|
+
* - `detachAndForget(driver, child, input)` — discards the handle.
|
|
15
|
+
* Use for fire-and-forget telemetry where the caller never needs to
|
|
16
|
+
* know how the child finished.
|
|
17
|
+
*
|
|
18
|
+
* Two entry points:
|
|
19
|
+
*
|
|
20
|
+
* - `scope.$detachAndJoinLater(driver, child, input)` — from inside a
|
|
21
|
+
* stage. refIds are minted from the calling stage's runtimeStageId
|
|
22
|
+
* for diagnostic correlation.
|
|
23
|
+
* - `executor.detachAndJoinLater(driver, child, input)` — from outside
|
|
24
|
+
* any chart (consumer code). refIds use the synthetic `__executor__`
|
|
25
|
+
* prefix.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { microtaskBatchDriver } from 'footprintjs/detach';
|
|
30
|
+
* import { flowChart, FlowChartExecutor } from 'footprintjs';
|
|
31
|
+
*
|
|
32
|
+
* const telemetry = flowChart('telemetry', async (scope) => {
|
|
33
|
+
* await fetch('/log', { method: 'POST', body: JSON.stringify(scope.$getArgs()) });
|
|
34
|
+
* }, 'telemetry').build();
|
|
35
|
+
*
|
|
36
|
+
* const main = flowChart('process', async (scope) => {
|
|
37
|
+
* scope.result = await heavyWork();
|
|
38
|
+
* // Fire telemetry without blocking the parent.
|
|
39
|
+
* scope.$detachAndForget(microtaskBatchDriver, telemetry, { event: 'processed' });
|
|
40
|
+
* }, 'process').build();
|
|
41
|
+
*
|
|
42
|
+
* await new FlowChartExecutor(main).run();
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.flushAllDetached = exports.lookupDetachedHandle = exports.listDetachedIds = exports.detachedCount = exports.defaultRunChild = exports.HandleImpl = exports.createHandle = exports.asImpl = exports.createWorkerThreadDriver = exports.setTimeoutDriver = exports.createSetTimeoutDriver = exports.setImmediateDriver = exports.createSetImmediateDriver = exports.createSendBeaconDriver = exports.microtaskBatchDriver = exports.createMicrotaskBatchDriver = exports.immediateDriver = exports.createImmediateDriver = void 0;
|
|
47
|
+
// ─── Driver factories + default singletons ───────────────────────────
|
|
48
|
+
var immediate_js_1 = require("./lib/detach/drivers/immediate.js");
|
|
49
|
+
Object.defineProperty(exports, "createImmediateDriver", { enumerable: true, get: function () { return immediate_js_1.createImmediateDriver; } });
|
|
50
|
+
Object.defineProperty(exports, "immediateDriver", { enumerable: true, get: function () { return immediate_js_1.immediateDriver; } });
|
|
51
|
+
var microtaskBatch_js_1 = require("./lib/detach/drivers/microtaskBatch.js");
|
|
52
|
+
Object.defineProperty(exports, "createMicrotaskBatchDriver", { enumerable: true, get: function () { return microtaskBatch_js_1.createMicrotaskBatchDriver; } });
|
|
53
|
+
Object.defineProperty(exports, "microtaskBatchDriver", { enumerable: true, get: function () { return microtaskBatch_js_1.microtaskBatchDriver; } });
|
|
54
|
+
var sendBeacon_js_1 = require("./lib/detach/drivers/sendBeacon.js");
|
|
55
|
+
Object.defineProperty(exports, "createSendBeaconDriver", { enumerable: true, get: function () { return sendBeacon_js_1.createSendBeaconDriver; } });
|
|
56
|
+
var setImmediate_js_1 = require("./lib/detach/drivers/setImmediate.js");
|
|
57
|
+
Object.defineProperty(exports, "createSetImmediateDriver", { enumerable: true, get: function () { return setImmediate_js_1.createSetImmediateDriver; } });
|
|
58
|
+
Object.defineProperty(exports, "setImmediateDriver", { enumerable: true, get: function () { return setImmediate_js_1.setImmediateDriver; } });
|
|
59
|
+
var setTimeout_js_1 = require("./lib/detach/drivers/setTimeout.js");
|
|
60
|
+
Object.defineProperty(exports, "createSetTimeoutDriver", { enumerable: true, get: function () { return setTimeout_js_1.createSetTimeoutDriver; } });
|
|
61
|
+
Object.defineProperty(exports, "setTimeoutDriver", { enumerable: true, get: function () { return setTimeout_js_1.setTimeoutDriver; } });
|
|
62
|
+
var workerThread_js_1 = require("./lib/detach/drivers/workerThread.js");
|
|
63
|
+
Object.defineProperty(exports, "createWorkerThreadDriver", { enumerable: true, get: function () { return workerThread_js_1.createWorkerThreadDriver; } });
|
|
64
|
+
// ─── Handle factory + helpers (for custom-driver authors) ────────────
|
|
65
|
+
var handle_js_1 = require("./lib/detach/handle.js");
|
|
66
|
+
Object.defineProperty(exports, "asImpl", { enumerable: true, get: function () { return handle_js_1.asImpl; } });
|
|
67
|
+
Object.defineProperty(exports, "createHandle", { enumerable: true, get: function () { return handle_js_1.createHandle; } });
|
|
68
|
+
Object.defineProperty(exports, "HandleImpl", { enumerable: true, get: function () { return handle_js_1.HandleImpl; } });
|
|
69
|
+
var runChild_js_1 = require("./lib/detach/runChild.js");
|
|
70
|
+
Object.defineProperty(exports, "defaultRunChild", { enumerable: true, get: function () { return runChild_js_1.defaultRunChild; } });
|
|
71
|
+
// ─── Registry — diagnostic surface ───────────────────────────────────
|
|
72
|
+
var registry_js_1 = require("./lib/detach/registry.js");
|
|
73
|
+
Object.defineProperty(exports, "detachedCount", { enumerable: true, get: function () { return registry_js_1.size; } });
|
|
74
|
+
Object.defineProperty(exports, "listDetachedIds", { enumerable: true, get: function () { return registry_js_1.ids; } });
|
|
75
|
+
Object.defineProperty(exports, "lookupDetachedHandle", { enumerable: true, get: function () { return registry_js_1.lookup; } });
|
|
76
|
+
var flush_js_1 = require("./lib/detach/flush.js");
|
|
77
|
+
Object.defineProperty(exports, "flushAllDetached", { enumerable: true, get: function () { return flush_js_1.flushAllDetached; } });
|
|
78
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV0YWNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2RldGFjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTBDRzs7O0FBV0gsd0VBQXdFO0FBQ3hFLGtFQUEyRjtBQUFsRixxSEFBQSxxQkFBcUIsT0FBQTtBQUFFLCtHQUFBLGVBQWUsT0FBQTtBQUMvQyw0RUFBMEc7QUFBakcsK0hBQUEsMEJBQTBCLE9BQUE7QUFBRSx5SEFBQSxvQkFBb0IsT0FBQTtBQUV6RCxvRUFBNEU7QUFBbkUsdUhBQUEsc0JBQXNCLE9BQUE7QUFDL0Isd0VBQW9HO0FBQTNGLDJIQUFBLHdCQUF3QixPQUFBO0FBQUUscUhBQUEsa0JBQWtCLE9BQUE7QUFFckQsb0VBQThGO0FBQXJGLHVIQUFBLHNCQUFzQixPQUFBO0FBQUUsaUhBQUEsZ0JBQWdCLE9BQUE7QUFFakQsd0VBQWdGO0FBQXZFLDJIQUFBLHdCQUF3QixPQUFBO0FBRWpDLHdFQUF3RTtBQUN4RSxvREFBMEU7QUFBakUsbUdBQUEsTUFBTSxPQUFBO0FBQUUseUdBQUEsWUFBWSxPQUFBO0FBQUUsdUdBQUEsVUFBVSxPQUFBO0FBRXpDLHdEQUEyRDtBQUFsRCw4R0FBQSxlQUFlLE9BQUE7QUFFeEIsd0VBQXdFO0FBQ3hFLHdEQUlrQztBQUhoQyw0R0FBQSxJQUFJLE9BQWlCO0FBQ3JCLDhHQUFBLEdBQUcsT0FBbUI7QUFDdEIsbUhBQUEsTUFBTSxPQUF3QjtBQUtoQyxrREFBeUQ7QUFBaEQsNEdBQUEsZ0JBQWdCLE9BQUEiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGZvb3RwcmludGpzL2RldGFjaCDigJQgRmlyZS1hbmQtZm9yZ2V0IGNoaWxkIGZsb3djaGFydCBleGVjdXRpb24uXG4gKlxuICogQSBsaWJyYXJ5IG9mIHNjaGVkdWxpbmcgZHJpdmVycyB0aGF0IGxldHMgeW91IGRldGFjaCB3b3JrIGZyb20gdGhlXG4gKiBwYXJlbnQgc3RhZ2UncyBob3QgcGF0aC4gVGhlIHBhcmVudCBzdGFnZSByZXR1cm5zIGltbWVkaWF0ZWx5OyB0aGVcbiAqIGNoaWxkIHJ1bnMgb24gd2hpY2hldmVyIGRyaXZlciB5b3UgcGljayAobWljcm90YXNrLCBpbW1lZGlhdGUsIHBsdXNcbiAqIHNldEltbWVkaWF0ZSAvIHNldFRpbWVvdXQgLyBzZW5kQmVhY29uIC8gd29ya2VyLXRocmVhZCBpbiB2NC4xNy4xKykuXG4gKlxuICogVHdvIHNoYXBlczpcbiAqXG4gKiAgIC0gYGRldGFjaEFuZEpvaW5MYXRlcihkcml2ZXIsIGNoaWxkLCBpbnB1dClgIOKAlCByZXR1cm5zIGEgYERldGFjaEhhbmRsZWBcbiAqICAgICB5b3UgY2FuIGB3YWl0KClgIG9uIChQcm9taXNlKSBvciByZWFkIGAuc3RhdHVzYCBmcm9tIChzeW5jKS5cbiAqICAgLSBgZGV0YWNoQW5kRm9yZ2V0KGRyaXZlciwgY2hpbGQsIGlucHV0KWAg4oCUIGRpc2NhcmRzIHRoZSBoYW5kbGUuXG4gKiAgICAgVXNlIGZvciBmaXJlLWFuZC1mb3JnZXQgdGVsZW1ldHJ5IHdoZXJlIHRoZSBjYWxsZXIgbmV2ZXIgbmVlZHMgdG9cbiAqICAgICBrbm93IGhvdyB0aGUgY2hpbGQgZmluaXNoZWQuXG4gKlxuICogVHdvIGVudHJ5IHBvaW50czpcbiAqXG4gKiAgIC0gYHNjb3BlLiRkZXRhY2hBbmRKb2luTGF0ZXIoZHJpdmVyLCBjaGlsZCwgaW5wdXQpYCDigJQgZnJvbSBpbnNpZGUgYVxuICogICAgIHN0YWdlLiByZWZJZHMgYXJlIG1pbnRlZCBmcm9tIHRoZSBjYWxsaW5nIHN0YWdlJ3MgcnVudGltZVN0YWdlSWRcbiAqICAgICBmb3IgZGlhZ25vc3RpYyBjb3JyZWxhdGlvbi5cbiAqICAgLSBgZXhlY3V0b3IuZGV0YWNoQW5kSm9pbkxhdGVyKGRyaXZlciwgY2hpbGQsIGlucHV0KWAg4oCUIGZyb20gb3V0c2lkZVxuICogICAgIGFueSBjaGFydCAoY29uc3VtZXIgY29kZSkuIHJlZklkcyB1c2UgdGhlIHN5bnRoZXRpYyBgX19leGVjdXRvcl9fYFxuICogICAgIHByZWZpeC5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgbWljcm90YXNrQmF0Y2hEcml2ZXIgfSBmcm9tICdmb290cHJpbnRqcy9kZXRhY2gnO1xuICogaW1wb3J0IHsgZmxvd0NoYXJ0LCBGbG93Q2hhcnRFeGVjdXRvciB9IGZyb20gJ2Zvb3RwcmludGpzJztcbiAqXG4gKiBjb25zdCB0ZWxlbWV0cnkgPSBmbG93Q2hhcnQoJ3RlbGVtZXRyeScsIGFzeW5jIChzY29wZSkgPT4ge1xuICogICBhd2FpdCBmZXRjaCgnL2xvZycsIHsgbWV0aG9kOiAnUE9TVCcsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHNjb3BlLiRnZXRBcmdzKCkpIH0pO1xuICogfSwgJ3RlbGVtZXRyeScpLmJ1aWxkKCk7XG4gKlxuICogY29uc3QgbWFpbiA9IGZsb3dDaGFydCgncHJvY2VzcycsIGFzeW5jIChzY29wZSkgPT4ge1xuICogICBzY29wZS5yZXN1bHQgPSBhd2FpdCBoZWF2eVdvcmsoKTtcbiAqICAgLy8gRmlyZSB0ZWxlbWV0cnkgd2l0aG91dCBibG9ja2luZyB0aGUgcGFyZW50LlxuICogICBzY29wZS4kZGV0YWNoQW5kRm9yZ2V0KG1pY3JvdGFza0JhdGNoRHJpdmVyLCB0ZWxlbWV0cnksIHsgZXZlbnQ6ICdwcm9jZXNzZWQnIH0pO1xuICogfSwgJ3Byb2Nlc3MnKS5idWlsZCgpO1xuICpcbiAqIGF3YWl0IG5ldyBGbG93Q2hhcnRFeGVjdXRvcihtYWluKS5ydW4oKTtcbiAqIGBgYFxuICovXG5cbi8vIOKUgOKUgOKUgCBUeXBlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbmV4cG9ydCB0eXBlIHtcbiAgRGV0YWNoRHJpdmVyLFxuICBEZXRhY2hIYW5kbGUsXG4gIERldGFjaFBvbGxSZXN1bHQsXG4gIERldGFjaFdhaXRSZXN1bHQsXG4gIERyaXZlckNhcGFiaWxpdGllcyxcbn0gZnJvbSAnLi9saWIvZGV0YWNoL3R5cGVzLmpzJztcblxuLy8g4pSA4pSA4pSAIERyaXZlciBmYWN0b3JpZXMgKyBkZWZhdWx0IHNpbmdsZXRvbnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5leHBvcnQgeyBjcmVhdGVJbW1lZGlhdGVEcml2ZXIsIGltbWVkaWF0ZURyaXZlciB9IGZyb20gJy4vbGliL2RldGFjaC9kcml2ZXJzL2ltbWVkaWF0ZS5qcyc7XG5leHBvcnQgeyBjcmVhdGVNaWNyb3Rhc2tCYXRjaERyaXZlciwgbWljcm90YXNrQmF0Y2hEcml2ZXIgfSBmcm9tICcuL2xpYi9kZXRhY2gvZHJpdmVycy9taWNyb3Rhc2tCYXRjaC5qcyc7XG5leHBvcnQgdHlwZSB7IFNlbmRCZWFjb25Ecml2ZXJPcHRpb25zIH0gZnJvbSAnLi9saWIvZGV0YWNoL2RyaXZlcnMvc2VuZEJlYWNvbi5qcyc7XG5leHBvcnQgeyBjcmVhdGVTZW5kQmVhY29uRHJpdmVyIH0gZnJvbSAnLi9saWIvZGV0YWNoL2RyaXZlcnMvc2VuZEJlYWNvbi5qcyc7XG5leHBvcnQgeyBjcmVhdGVTZXRJbW1lZGlhdGVEcml2ZXIsIHNldEltbWVkaWF0ZURyaXZlciB9IGZyb20gJy4vbGliL2RldGFjaC9kcml2ZXJzL3NldEltbWVkaWF0ZS5qcyc7XG5leHBvcnQgdHlwZSB7IFNldFRpbWVvdXREcml2ZXJPcHRpb25zIH0gZnJvbSAnLi9saWIvZGV0YWNoL2RyaXZlcnMvc2V0VGltZW91dC5qcyc7XG5leHBvcnQgeyBjcmVhdGVTZXRUaW1lb3V0RHJpdmVyLCBzZXRUaW1lb3V0RHJpdmVyIH0gZnJvbSAnLi9saWIvZGV0YWNoL2RyaXZlcnMvc2V0VGltZW91dC5qcyc7XG5leHBvcnQgdHlwZSB7IFdvcmtlclRocmVhZERyaXZlck9wdGlvbnMgfSBmcm9tICcuL2xpYi9kZXRhY2gvZHJpdmVycy93b3JrZXJUaHJlYWQuanMnO1xuZXhwb3J0IHsgY3JlYXRlV29ya2VyVGhyZWFkRHJpdmVyIH0gZnJvbSAnLi9saWIvZGV0YWNoL2RyaXZlcnMvd29ya2VyVGhyZWFkLmpzJztcblxuLy8g4pSA4pSA4pSAIEhhbmRsZSBmYWN0b3J5ICsgaGVscGVycyAoZm9yIGN1c3RvbS1kcml2ZXIgYXV0aG9ycykg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5leHBvcnQgeyBhc0ltcGwsIGNyZWF0ZUhhbmRsZSwgSGFuZGxlSW1wbCB9IGZyb20gJy4vbGliL2RldGFjaC9oYW5kbGUuanMnO1xuZXhwb3J0IHR5cGUgeyBDaGlsZFJ1bm5lciB9IGZyb20gJy4vbGliL2RldGFjaC9ydW5DaGlsZC5qcyc7XG5leHBvcnQgeyBkZWZhdWx0UnVuQ2hpbGQgfSBmcm9tICcuL2xpYi9kZXRhY2gvcnVuQ2hpbGQuanMnO1xuXG4vLyDilIDilIDilIAgUmVnaXN0cnkg4oCUIGRpYWdub3N0aWMgc3VyZmFjZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbmV4cG9ydCB7XG4gIHNpemUgYXMgZGV0YWNoZWRDb3VudCxcbiAgaWRzIGFzIGxpc3REZXRhY2hlZElkcyxcbiAgbG9va3VwIGFzIGxvb2t1cERldGFjaGVkSGFuZGxlLFxufSBmcm9tICcuL2xpYi9kZXRhY2gvcmVnaXN0cnkuanMnO1xuXG4vLyDilIDilIDilIAgRmx1c2gg4oCUIGdyYWNlZnVsLXNodXRkb3duIGhlbHBlciDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbmV4cG9ydCB0eXBlIHsgRmx1c2hPcHRpb25zLCBGbHVzaFJlc3VsdCB9IGZyb20gJy4vbGliL2RldGFjaC9mbHVzaC5qcyc7XG5leHBvcnQgeyBmbHVzaEFsbERldGFjaGVkIH0gZnJvbSAnLi9saWIvZGV0YWNoL2ZsdXNoLmpzJztcbiJdfQ==
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* footprintjs/detach — Fire-and-forget child flowchart execution.
|
|
3
|
+
*
|
|
4
|
+
* A library of scheduling drivers that lets you detach work from the
|
|
5
|
+
* parent stage's hot path. The parent stage returns immediately; the
|
|
6
|
+
* child runs on whichever driver you pick (microtask, immediate, plus
|
|
7
|
+
* setImmediate / setTimeout / sendBeacon / worker-thread in v4.17.1+).
|
|
8
|
+
*
|
|
9
|
+
* Two shapes:
|
|
10
|
+
*
|
|
11
|
+
* - `detachAndJoinLater(driver, child, input)` — returns a `DetachHandle`
|
|
12
|
+
* you can `wait()` on (Promise) or read `.status` from (sync).
|
|
13
|
+
* - `detachAndForget(driver, child, input)` — discards the handle.
|
|
14
|
+
* Use for fire-and-forget telemetry where the caller never needs to
|
|
15
|
+
* know how the child finished.
|
|
16
|
+
*
|
|
17
|
+
* Two entry points:
|
|
18
|
+
*
|
|
19
|
+
* - `scope.$detachAndJoinLater(driver, child, input)` — from inside a
|
|
20
|
+
* stage. refIds are minted from the calling stage's runtimeStageId
|
|
21
|
+
* for diagnostic correlation.
|
|
22
|
+
* - `executor.detachAndJoinLater(driver, child, input)` — from outside
|
|
23
|
+
* any chart (consumer code). refIds use the synthetic `__executor__`
|
|
24
|
+
* prefix.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { microtaskBatchDriver } from 'footprintjs/detach';
|
|
29
|
+
* import { flowChart, FlowChartExecutor } from 'footprintjs';
|
|
30
|
+
*
|
|
31
|
+
* const telemetry = flowChart('telemetry', async (scope) => {
|
|
32
|
+
* await fetch('/log', { method: 'POST', body: JSON.stringify(scope.$getArgs()) });
|
|
33
|
+
* }, 'telemetry').build();
|
|
34
|
+
*
|
|
35
|
+
* const main = flowChart('process', async (scope) => {
|
|
36
|
+
* scope.result = await heavyWork();
|
|
37
|
+
* // Fire telemetry without blocking the parent.
|
|
38
|
+
* scope.$detachAndForget(microtaskBatchDriver, telemetry, { event: 'processed' });
|
|
39
|
+
* }, 'process').build();
|
|
40
|
+
*
|
|
41
|
+
* await new FlowChartExecutor(main).run();
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
// ─── Driver factories + default singletons ───────────────────────────
|
|
45
|
+
export { createImmediateDriver, immediateDriver } from './lib/detach/drivers/immediate.js';
|
|
46
|
+
export { createMicrotaskBatchDriver, microtaskBatchDriver } from './lib/detach/drivers/microtaskBatch.js';
|
|
47
|
+
export { createSendBeaconDriver } from './lib/detach/drivers/sendBeacon.js';
|
|
48
|
+
export { createSetImmediateDriver, setImmediateDriver } from './lib/detach/drivers/setImmediate.js';
|
|
49
|
+
export { createSetTimeoutDriver, setTimeoutDriver } from './lib/detach/drivers/setTimeout.js';
|
|
50
|
+
export { createWorkerThreadDriver } from './lib/detach/drivers/workerThread.js';
|
|
51
|
+
// ─── Handle factory + helpers (for custom-driver authors) ────────────
|
|
52
|
+
export { asImpl, createHandle, HandleImpl } from './lib/detach/handle.js';
|
|
53
|
+
export { defaultRunChild } from './lib/detach/runChild.js';
|
|
54
|
+
// ─── Registry — diagnostic surface ───────────────────────────────────
|
|
55
|
+
export { size as detachedCount, ids as listDetachedIds, lookup as lookupDetachedHandle, } from './lib/detach/registry.js';
|
|
56
|
+
export { flushAllDetached } from './lib/detach/flush.js';
|
|
57
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV0YWNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RldGFjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMENHO0FBV0gsd0VBQXdFO0FBQ3hFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUMzRixPQUFPLEVBQUUsMEJBQTBCLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUUxRyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUM1RSxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUVwRyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUU5RixPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUVoRix3RUFBd0U7QUFDeEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFMUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRTNELHdFQUF3RTtBQUN4RSxPQUFPLEVBQ0wsSUFBSSxJQUFJLGFBQWEsRUFDckIsR0FBRyxJQUFJLGVBQWUsRUFDdEIsTUFBTSxJQUFJLG9CQUFvQixHQUMvQixNQUFNLDBCQUEwQixDQUFDO0FBSWxDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBmb290cHJpbnRqcy9kZXRhY2gg4oCUIEZpcmUtYW5kLWZvcmdldCBjaGlsZCBmbG93Y2hhcnQgZXhlY3V0aW9uLlxuICpcbiAqIEEgbGlicmFyeSBvZiBzY2hlZHVsaW5nIGRyaXZlcnMgdGhhdCBsZXRzIHlvdSBkZXRhY2ggd29yayBmcm9tIHRoZVxuICogcGFyZW50IHN0YWdlJ3MgaG90IHBhdGguIFRoZSBwYXJlbnQgc3RhZ2UgcmV0dXJucyBpbW1lZGlhdGVseTsgdGhlXG4gKiBjaGlsZCBydW5zIG9uIHdoaWNoZXZlciBkcml2ZXIgeW91IHBpY2sgKG1pY3JvdGFzaywgaW1tZWRpYXRlLCBwbHVzXG4gKiBzZXRJbW1lZGlhdGUgLyBzZXRUaW1lb3V0IC8gc2VuZEJlYWNvbiAvIHdvcmtlci10aHJlYWQgaW4gdjQuMTcuMSspLlxuICpcbiAqIFR3byBzaGFwZXM6XG4gKlxuICogICAtIGBkZXRhY2hBbmRKb2luTGF0ZXIoZHJpdmVyLCBjaGlsZCwgaW5wdXQpYCDigJQgcmV0dXJucyBhIGBEZXRhY2hIYW5kbGVgXG4gKiAgICAgeW91IGNhbiBgd2FpdCgpYCBvbiAoUHJvbWlzZSkgb3IgcmVhZCBgLnN0YXR1c2AgZnJvbSAoc3luYykuXG4gKiAgIC0gYGRldGFjaEFuZEZvcmdldChkcml2ZXIsIGNoaWxkLCBpbnB1dClgIOKAlCBkaXNjYXJkcyB0aGUgaGFuZGxlLlxuICogICAgIFVzZSBmb3IgZmlyZS1hbmQtZm9yZ2V0IHRlbGVtZXRyeSB3aGVyZSB0aGUgY2FsbGVyIG5ldmVyIG5lZWRzIHRvXG4gKiAgICAga25vdyBob3cgdGhlIGNoaWxkIGZpbmlzaGVkLlxuICpcbiAqIFR3byBlbnRyeSBwb2ludHM6XG4gKlxuICogICAtIGBzY29wZS4kZGV0YWNoQW5kSm9pbkxhdGVyKGRyaXZlciwgY2hpbGQsIGlucHV0KWAg4oCUIGZyb20gaW5zaWRlIGFcbiAqICAgICBzdGFnZS4gcmVmSWRzIGFyZSBtaW50ZWQgZnJvbSB0aGUgY2FsbGluZyBzdGFnZSdzIHJ1bnRpbWVTdGFnZUlkXG4gKiAgICAgZm9yIGRpYWdub3N0aWMgY29ycmVsYXRpb24uXG4gKiAgIC0gYGV4ZWN1dG9yLmRldGFjaEFuZEpvaW5MYXRlcihkcml2ZXIsIGNoaWxkLCBpbnB1dClgIOKAlCBmcm9tIG91dHNpZGVcbiAqICAgICBhbnkgY2hhcnQgKGNvbnN1bWVyIGNvZGUpLiByZWZJZHMgdXNlIHRoZSBzeW50aGV0aWMgYF9fZXhlY3V0b3JfX2BcbiAqICAgICBwcmVmaXguXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGltcG9ydCB7IG1pY3JvdGFza0JhdGNoRHJpdmVyIH0gZnJvbSAnZm9vdHByaW50anMvZGV0YWNoJztcbiAqIGltcG9ydCB7IGZsb3dDaGFydCwgRmxvd0NoYXJ0RXhlY3V0b3IgfSBmcm9tICdmb290cHJpbnRqcyc7XG4gKlxuICogY29uc3QgdGVsZW1ldHJ5ID0gZmxvd0NoYXJ0KCd0ZWxlbWV0cnknLCBhc3luYyAoc2NvcGUpID0+IHtcbiAqICAgYXdhaXQgZmV0Y2goJy9sb2cnLCB7IG1ldGhvZDogJ1BPU1QnLCBib2R5OiBKU09OLnN0cmluZ2lmeShzY29wZS4kZ2V0QXJncygpKSB9KTtcbiAqIH0sICd0ZWxlbWV0cnknKS5idWlsZCgpO1xuICpcbiAqIGNvbnN0IG1haW4gPSBmbG93Q2hhcnQoJ3Byb2Nlc3MnLCBhc3luYyAoc2NvcGUpID0+IHtcbiAqICAgc2NvcGUucmVzdWx0ID0gYXdhaXQgaGVhdnlXb3JrKCk7XG4gKiAgIC8vIEZpcmUgdGVsZW1ldHJ5IHdpdGhvdXQgYmxvY2tpbmcgdGhlIHBhcmVudC5cbiAqICAgc2NvcGUuJGRldGFjaEFuZEZvcmdldChtaWNyb3Rhc2tCYXRjaERyaXZlciwgdGVsZW1ldHJ5LCB7IGV2ZW50OiAncHJvY2Vzc2VkJyB9KTtcbiAqIH0sICdwcm9jZXNzJykuYnVpbGQoKTtcbiAqXG4gKiBhd2FpdCBuZXcgRmxvd0NoYXJ0RXhlY3V0b3IobWFpbikucnVuKCk7XG4gKiBgYGBcbiAqL1xuXG4vLyDilIDilIDilIAgVHlwZXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5leHBvcnQgdHlwZSB7XG4gIERldGFjaERyaXZlcixcbiAgRGV0YWNoSGFuZGxlLFxuICBEZXRhY2hQb2xsUmVzdWx0LFxuICBEZXRhY2hXYWl0UmVzdWx0LFxuICBEcml2ZXJDYXBhYmlsaXRpZXMsXG59IGZyb20gJy4vbGliL2RldGFjaC90eXBlcy5qcyc7XG5cbi8vIOKUgOKUgOKUgCBEcml2ZXIgZmFjdG9yaWVzICsgZGVmYXVsdCBzaW5nbGV0b25zIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuZXhwb3J0IHsgY3JlYXRlSW1tZWRpYXRlRHJpdmVyLCBpbW1lZGlhdGVEcml2ZXIgfSBmcm9tICcuL2xpYi9kZXRhY2gvZHJpdmVycy9pbW1lZGlhdGUuanMnO1xuZXhwb3J0IHsgY3JlYXRlTWljcm90YXNrQmF0Y2hEcml2ZXIsIG1pY3JvdGFza0JhdGNoRHJpdmVyIH0gZnJvbSAnLi9saWIvZGV0YWNoL2RyaXZlcnMvbWljcm90YXNrQmF0Y2guanMnO1xuZXhwb3J0IHR5cGUgeyBTZW5kQmVhY29uRHJpdmVyT3B0aW9ucyB9IGZyb20gJy4vbGliL2RldGFjaC9kcml2ZXJzL3NlbmRCZWFjb24uanMnO1xuZXhwb3J0IHsgY3JlYXRlU2VuZEJlYWNvbkRyaXZlciB9IGZyb20gJy4vbGliL2RldGFjaC9kcml2ZXJzL3NlbmRCZWFjb24uanMnO1xuZXhwb3J0IHsgY3JlYXRlU2V0SW1tZWRpYXRlRHJpdmVyLCBzZXRJbW1lZGlhdGVEcml2ZXIgfSBmcm9tICcuL2xpYi9kZXRhY2gvZHJpdmVycy9zZXRJbW1lZGlhdGUuanMnO1xuZXhwb3J0IHR5cGUgeyBTZXRUaW1lb3V0RHJpdmVyT3B0aW9ucyB9IGZyb20gJy4vbGliL2RldGFjaC9kcml2ZXJzL3NldFRpbWVvdXQuanMnO1xuZXhwb3J0IHsgY3JlYXRlU2V0VGltZW91dERyaXZlciwgc2V0VGltZW91dERyaXZlciB9IGZyb20gJy4vbGliL2RldGFjaC9kcml2ZXJzL3NldFRpbWVvdXQuanMnO1xuZXhwb3J0IHR5cGUgeyBXb3JrZXJUaHJlYWREcml2ZXJPcHRpb25zIH0gZnJvbSAnLi9saWIvZGV0YWNoL2RyaXZlcnMvd29ya2VyVGhyZWFkLmpzJztcbmV4cG9ydCB7IGNyZWF0ZVdvcmtlclRocmVhZERyaXZlciB9IGZyb20gJy4vbGliL2RldGFjaC9kcml2ZXJzL3dvcmtlclRocmVhZC5qcyc7XG5cbi8vIOKUgOKUgOKUgCBIYW5kbGUgZmFjdG9yeSArIGhlbHBlcnMgKGZvciBjdXN0b20tZHJpdmVyIGF1dGhvcnMpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuZXhwb3J0IHsgYXNJbXBsLCBjcmVhdGVIYW5kbGUsIEhhbmRsZUltcGwgfSBmcm9tICcuL2xpYi9kZXRhY2gvaGFuZGxlLmpzJztcbmV4cG9ydCB0eXBlIHsgQ2hpbGRSdW5uZXIgfSBmcm9tICcuL2xpYi9kZXRhY2gvcnVuQ2hpbGQuanMnO1xuZXhwb3J0IHsgZGVmYXVsdFJ1bkNoaWxkIH0gZnJvbSAnLi9saWIvZGV0YWNoL3J1bkNoaWxkLmpzJztcblxuLy8g4pSA4pSA4pSAIFJlZ2lzdHJ5IOKAlCBkaWFnbm9zdGljIHN1cmZhY2Ug4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5leHBvcnQge1xuICBzaXplIGFzIGRldGFjaGVkQ291bnQsXG4gIGlkcyBhcyBsaXN0RGV0YWNoZWRJZHMsXG4gIGxvb2t1cCBhcyBsb29rdXBEZXRhY2hlZEhhbmRsZSxcbn0gZnJvbSAnLi9saWIvZGV0YWNoL3JlZ2lzdHJ5LmpzJztcblxuLy8g4pSA4pSA4pSAIEZsdXNoIOKAlCBncmFjZWZ1bC1zaHV0ZG93biBoZWxwZXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5leHBvcnQgdHlwZSB7IEZsdXNoT3B0aW9ucywgRmx1c2hSZXN1bHQgfSBmcm9tICcuL2xpYi9kZXRhY2gvZmx1c2guanMnO1xuZXhwb3J0IHsgZmx1c2hBbGxEZXRhY2hlZCB9IGZyb20gJy4vbGliL2RldGFjaC9mbHVzaC5qcyc7XG4iXX0=
|