footprintjs 9.9.0 → 9.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -9
- package/CLAUDE.md +82 -753
- package/README.md +51 -20
- package/dist/esm/advanced.d.ts +52 -0
- package/dist/esm/detach.d.ts +59 -0
- package/dist/esm/index.d.ts +202 -0
- package/dist/esm/lib/builder/FlowChartBuilder.d.ts +475 -0
- package/dist/esm/lib/builder/FlowChartBuilder.js +4 -3
- package/dist/esm/lib/builder/index.d.ts +11 -0
- package/dist/esm/lib/builder/structure/StructureRecorder.d.ts +349 -0
- package/dist/esm/lib/builder/structure/StructureRecorderDispatcher.d.ts +77 -0
- package/dist/esm/lib/builder/typedFlowChart.d.ts +25 -0
- package/dist/esm/lib/builder/types.d.ts +154 -0
- package/dist/esm/lib/capture/envelope.d.ts +169 -0
- package/dist/esm/lib/capture/index.d.ts +16 -0
- package/dist/esm/lib/capture/policies.d.ts +42 -0
- package/dist/esm/lib/capture/summarize.d.ts +65 -0
- package/dist/esm/lib/contract/defineContract.d.ts +18 -0
- package/dist/esm/lib/contract/index.d.ts +14 -0
- package/dist/esm/lib/contract/openapi.d.ts +18 -0
- package/dist/esm/lib/contract/schema.d.ts +13 -0
- package/dist/esm/lib/contract/types.d.ts +105 -0
- package/dist/esm/lib/decide/decide.d.ts +47 -0
- package/dist/esm/lib/decide/evaluator.d.ts +39 -0
- package/dist/esm/lib/decide/evidence.d.ts +22 -0
- package/dist/esm/lib/decide/index.d.ts +12 -0
- package/dist/esm/lib/decide/types.d.ts +111 -0
- package/dist/esm/lib/detach/drivers/immediate.d.ts +39 -0
- package/dist/esm/lib/detach/drivers/microtaskBatch.d.ts +57 -0
- package/dist/esm/lib/detach/drivers/sendBeacon.d.ts +38 -0
- package/dist/esm/lib/detach/drivers/setImmediate.d.ts +32 -0
- package/dist/esm/lib/detach/drivers/setTimeout.d.ts +34 -0
- package/dist/esm/lib/detach/drivers/workerThread.d.ts +50 -0
- package/dist/esm/lib/detach/flush.d.ts +62 -0
- package/dist/esm/lib/detach/handle.d.ts +83 -0
- package/dist/esm/lib/detach/registry.d.ts +82 -0
- package/dist/esm/lib/detach/runChild.d.ts +41 -0
- package/dist/esm/lib/detach/spawn.d.ts +64 -0
- package/dist/esm/lib/detach/types.d.ts +200 -0
- package/dist/esm/lib/engine/errors/errorInfo.d.ts +46 -0
- package/dist/esm/lib/engine/errors/index.d.ts +2 -0
- package/dist/esm/lib/engine/graph/StageNode.d.ts +108 -0
- package/dist/esm/lib/engine/graph/index.d.ts +2 -0
- package/dist/esm/lib/engine/handlers/ChildrenExecutor.d.ts +32 -0
- package/dist/esm/lib/engine/handlers/ContinuationResolver.d.ts +95 -0
- package/dist/esm/lib/engine/handlers/DeciderHandler.d.ts +56 -0
- package/dist/esm/lib/engine/handlers/NodeResolver.d.ts +41 -0
- package/dist/esm/lib/engine/handlers/RuntimeStructureManager.d.ts +37 -0
- package/dist/esm/lib/engine/handlers/SelectorHandler.d.ts +27 -0
- package/dist/esm/lib/engine/handlers/StageRunner.d.ts +17 -0
- package/dist/esm/lib/engine/handlers/SubflowExecutor.d.ts +34 -0
- package/dist/esm/lib/engine/handlers/SubflowExecutor.js +8 -1
- package/dist/esm/lib/engine/handlers/SubflowInputMapper.d.ts +40 -0
- package/dist/esm/lib/engine/handlers/index.d.ts +13 -0
- package/dist/esm/lib/engine/handlers/types.d.ts +32 -0
- package/dist/esm/lib/engine/index.d.ts +28 -0
- package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +139 -0
- package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.d.ts +47 -0
- package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.d.ts +34 -0
- package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +27 -0
- package/dist/esm/lib/engine/narrative/index.d.ts +15 -0
- package/dist/esm/lib/engine/narrative/narrativeTypes.d.ts +196 -0
- package/dist/esm/lib/engine/narrative/recorders/AdaptiveNarrativeFlowRecorder.d.ts +25 -0
- package/dist/esm/lib/engine/narrative/recorders/ManifestFlowRecorder.d.ts +69 -0
- package/dist/esm/lib/engine/narrative/recorders/MilestoneNarrativeFlowRecorder.d.ts +24 -0
- package/dist/esm/lib/engine/narrative/recorders/ProgressiveNarrativeFlowRecorder.d.ts +30 -0
- package/dist/esm/lib/engine/narrative/recorders/RLENarrativeFlowRecorder.d.ts +25 -0
- package/dist/esm/lib/engine/narrative/recorders/SeparateNarrativeFlowRecorder.d.ts +32 -0
- package/dist/esm/lib/engine/narrative/recorders/SilentNarrativeFlowRecorder.d.ts +25 -0
- package/dist/esm/lib/engine/narrative/recorders/WindowedNarrativeFlowRecorder.d.ts +29 -0
- package/dist/esm/lib/engine/narrative/recorders/index.d.ts +9 -0
- package/dist/esm/lib/engine/narrative/types.d.ts +384 -0
- package/dist/esm/lib/engine/runtimeStageId.d.ts +103 -0
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.d.ts +376 -0
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +1 -1
- package/dist/esm/lib/engine/traversal/index.d.ts +2 -0
- package/dist/esm/lib/engine/types.d.ts +417 -0
- package/dist/esm/lib/engine/walkSubflowSpec.d.ts +95 -0
- package/dist/esm/lib/memory/DiagnosticCollector.d.ts +33 -0
- package/dist/esm/lib/memory/EventLog.d.ts +27 -0
- package/dist/esm/lib/memory/SharedMemory.d.ts +31 -0
- package/dist/esm/lib/memory/StageContext.d.ts +368 -0
- package/dist/esm/lib/memory/StageContext.js +44 -2
- package/dist/esm/lib/memory/TransactionBuffer.d.ts +183 -0
- package/dist/esm/lib/memory/TransactionBuffer.js +35 -13
- package/dist/esm/lib/memory/backtrack.d.ts +252 -0
- package/dist/esm/lib/memory/backtrack.js +79 -8
- package/dist/esm/lib/memory/commitLogUtils.d.ts +41 -0
- package/dist/esm/lib/memory/index.d.ts +14 -0
- package/dist/esm/lib/memory/pathOps.d.ts +54 -0
- package/dist/esm/lib/memory/types.d.ts +230 -0
- package/dist/esm/lib/memory/types.js +1 -1
- package/dist/esm/lib/memory/utils.d.ts +124 -0
- package/dist/esm/lib/observer-queue/deferredDispatcher.d.ts +169 -0
- package/dist/esm/lib/observer-queue/flushDriver.d.ts +124 -0
- package/dist/esm/lib/observer-queue/index.d.ts +25 -0
- package/dist/esm/lib/observer-queue/mergedQueue.d.ts +85 -0
- package/dist/esm/lib/observer-queue/ring.d.ts +99 -0
- package/dist/esm/lib/pause/index.d.ts +2 -0
- package/dist/esm/lib/pause/types.d.ts +238 -0
- package/dist/esm/lib/pause/types.js +2 -2
- package/dist/esm/lib/reactive/allowlist.d.ts +30 -0
- package/dist/esm/lib/reactive/arrayTraps.d.ts +23 -0
- package/dist/esm/lib/reactive/createTypedScope.d.ts +24 -0
- package/dist/esm/lib/reactive/index.d.ts +14 -0
- package/dist/esm/lib/reactive/pathBuilder.d.ts +31 -0
- package/dist/esm/lib/reactive/types.d.ts +159 -0
- package/dist/esm/lib/reactive/types.js +1 -1
- package/dist/esm/lib/recorder/BoundaryStateStore.d.ts +115 -0
- package/dist/esm/lib/recorder/CombinedRecorder.d.ts +215 -0
- package/dist/esm/lib/recorder/CombinedRecorder.js +2 -2
- package/dist/esm/lib/recorder/CommitRangeIndex.d.ts +147 -0
- package/dist/esm/lib/recorder/CompositeRecorder.d.ts +95 -0
- package/dist/esm/lib/recorder/ControlDepRecorder.d.ts +133 -0
- package/dist/esm/lib/recorder/EmitRecorder.d.ts +135 -0
- package/dist/esm/lib/recorder/InOutRecorder.d.ts +176 -0
- package/dist/esm/lib/recorder/KeyedStore.d.ts +70 -0
- package/dist/esm/lib/recorder/QualityRecorder.d.ts +122 -0
- package/dist/esm/lib/recorder/RecorderOperation.d.ts +35 -0
- package/dist/esm/lib/recorder/SequenceStore.d.ts +120 -0
- package/dist/esm/lib/recorder/TopologyRecorder.d.ts +165 -0
- package/dist/esm/lib/recorder/TopologyRecorder.js +1 -1
- package/dist/esm/lib/recorder/index.d.ts +9 -0
- package/dist/esm/lib/recorder/invokeHook.d.ts +32 -0
- package/dist/esm/lib/recorder/qualityTrace.d.ts +54 -0
- package/dist/esm/lib/runner/ComposableRunner.d.ts +33 -0
- package/dist/esm/lib/runner/DeferredObserverTier.d.ts +204 -0
- package/dist/esm/lib/runner/ExecutionRuntime.d.ts +162 -0
- package/dist/esm/lib/runner/ExecutionRuntime.js +16 -1
- package/dist/esm/lib/runner/FlowChartExecutor.d.ts +660 -0
- package/dist/esm/lib/runner/FlowChartExecutor.js +35 -3
- package/dist/esm/lib/runner/RunContext.d.ts +42 -0
- package/dist/esm/lib/runner/RunnableChart.d.ts +57 -0
- package/dist/esm/lib/runner/checkpointSanitize.d.ts +44 -0
- package/dist/esm/lib/runner/getSubtreeSnapshot.d.ts +70 -0
- package/dist/esm/lib/runner/index.d.ts +11 -0
- package/dist/esm/lib/runner/runId.d.ts +45 -0
- package/dist/esm/lib/runner/validateInput.d.ts +11 -0
- package/dist/esm/lib/schema/detect.d.ts +32 -0
- package/dist/esm/lib/schema/errors.d.ts +36 -0
- package/dist/esm/lib/schema/index.d.ts +14 -0
- package/dist/esm/lib/schema/validate.d.ts +33 -0
- package/dist/esm/lib/scope/ScopeFacade.d.ts +250 -0
- package/dist/esm/lib/scope/detectCircular.d.ts +47 -0
- package/dist/esm/lib/scope/index.d.ts +16 -0
- package/dist/esm/lib/scope/protection/createProtectedScope.d.ts +9 -0
- package/dist/esm/lib/scope/protection/index.d.ts +2 -0
- package/dist/esm/lib/scope/protection/readonlyInput.d.ts +26 -0
- package/dist/esm/lib/scope/protection/types.d.ts +13 -0
- package/dist/esm/lib/scope/providers/baseStateCompatible.d.ts +24 -0
- package/dist/esm/lib/scope/providers/guards.d.ts +14 -0
- package/dist/esm/lib/scope/providers/index.d.ts +6 -0
- package/dist/esm/lib/scope/providers/providers.d.ts +8 -0
- package/dist/esm/lib/scope/providers/registry.d.ts +11 -0
- package/dist/esm/lib/scope/providers/resolve.d.ts +8 -0
- package/dist/esm/lib/scope/providers/types.d.ts +40 -0
- package/dist/esm/lib/scope/recorders/DebugRecorder.d.ts +63 -0
- package/dist/esm/lib/scope/recorders/MetricRecorder.d.ts +125 -0
- package/dist/esm/lib/scope/recorders/index.d.ts +5 -0
- package/dist/esm/lib/scope/recorders/summarizeValue.d.ts +7 -0
- package/dist/esm/lib/scope/recorders/summarizeValue.js +3 -3
- package/dist/esm/lib/scope/state/installResolvers.d.ts +4 -0
- package/dist/esm/lib/scope/state/zod/defineScopeFromZod.d.ts +9 -0
- package/dist/esm/lib/scope/state/zod/index.d.ts +5 -0
- package/dist/esm/lib/scope/state/zod/resolver.d.ts +5 -0
- package/dist/esm/lib/scope/state/zod/schema/builder.d.ts +12 -0
- package/dist/esm/lib/scope/state/zod/scopeFactory.d.ts +9 -0
- package/dist/esm/lib/scope/state/zod/utils/validateHelper.d.ts +25 -0
- package/dist/esm/lib/scope/types.d.ts +142 -0
- package/dist/esm/lib/slice/elementProvenance.d.ts +74 -0
- package/dist/esm/lib/slice/elementProvenance.js +181 -0
- package/dist/esm/lib/slice/index.d.ts +16 -0
- package/dist/esm/lib/slice/index.js +16 -0
- package/dist/esm/lib/slice/keysReadSources.d.ts +51 -0
- package/dist/esm/lib/slice/keysReadSources.js +96 -0
- package/dist/esm/lib/slice/serialize.d.ts +40 -0
- package/dist/esm/lib/slice/serialize.js +97 -0
- package/dist/esm/lib/slice/sliceForKey.d.ts +47 -0
- package/dist/esm/lib/slice/sliceForKey.js +72 -0
- package/dist/esm/lib/slice/types.d.ts +240 -0
- package/dist/esm/lib/slice/types.js +20 -0
- package/dist/esm/recorders.d.ts +70 -0
- package/dist/esm/trace.d.ts +57 -0
- package/dist/esm/trace.js +2 -1
- package/dist/esm/zod.d.ts +18 -0
- package/dist/lib/builder/FlowChartBuilder.js +4 -3
- package/dist/lib/engine/handlers/SubflowExecutor.js +8 -1
- package/dist/lib/engine/traversal/FlowchartTraverser.js +1 -1
- package/dist/lib/memory/StageContext.js +44 -2
- package/dist/lib/memory/TransactionBuffer.js +35 -13
- package/dist/lib/memory/backtrack.js +79 -8
- package/dist/lib/memory/types.js +1 -1
- package/dist/lib/pause/types.js +2 -2
- package/dist/lib/reactive/types.js +1 -1
- package/dist/lib/recorder/CombinedRecorder.js +2 -2
- package/dist/lib/recorder/TopologyRecorder.js +1 -1
- package/dist/lib/runner/ExecutionRuntime.js +16 -1
- package/dist/lib/runner/FlowChartExecutor.js +35 -3
- package/dist/lib/scope/recorders/summarizeValue.js +3 -3
- package/dist/lib/slice/elementProvenance.js +186 -0
- package/dist/lib/slice/index.js +28 -0
- package/dist/lib/slice/keysReadSources.js +102 -0
- package/dist/lib/slice/serialize.js +102 -0
- package/dist/lib/slice/sliceForKey.js +77 -0
- package/dist/lib/slice/types.js +21 -0
- package/dist/trace.js +12 -2
- package/dist/types/lib/builder/FlowChartBuilder.d.ts +25 -2
- package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +1 -1
- package/dist/types/lib/memory/StageContext.d.ts +28 -1
- package/dist/types/lib/memory/TransactionBuffer.d.ts +10 -1
- package/dist/types/lib/memory/backtrack.d.ts +26 -0
- package/dist/types/lib/memory/types.d.ts +26 -0
- package/dist/types/lib/pause/types.d.ts +4 -3
- package/dist/types/lib/reactive/types.d.ts +1 -1
- package/dist/types/lib/recorder/CombinedRecorder.d.ts +1 -1
- package/dist/types/lib/recorder/TopologyRecorder.d.ts +1 -1
- package/dist/types/lib/runner/ExecutionRuntime.d.ts +15 -1
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +34 -3
- package/dist/types/lib/scope/recorders/summarizeValue.d.ts +2 -2
- package/dist/types/lib/slice/elementProvenance.d.ts +74 -0
- package/dist/types/lib/slice/index.d.ts +16 -0
- package/dist/types/lib/slice/keysReadSources.d.ts +51 -0
- package/dist/types/lib/slice/serialize.d.ts +40 -0
- package/dist/types/lib/slice/sliceForKey.d.ts +47 -0
- package/dist/types/lib/slice/types.d.ts +240 -0
- package/dist/types/trace.d.ts +3 -0
- package/package.json +75 -21
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* capture/envelope.ts — RFC-001 Block 1: capture envelopes + payload summarizer.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: Point-in-time capture. An observer event is snapshotted into a
|
|
5
|
+
* self-contained, immutable envelope at the moment it happens, so
|
|
6
|
+
* DELIVERY can be deferred (RFC-001 "one beat behind") without the
|
|
7
|
+
* payload drifting under later engine mutations.
|
|
8
|
+
* Role: The capture tier of the deferred-observer pipeline
|
|
9
|
+
* (`src/lib/observer-queue/`). Pure module — ZERO engine imports
|
|
10
|
+
* (only `capture/` internals); the engine wiring is RFC-001
|
|
11
|
+
* Blocks 6–10.
|
|
12
|
+
*
|
|
13
|
+
* Capture policies (RFC-001 §5):
|
|
14
|
+
* - `'summary'` — bounded, reference-free summarization via
|
|
15
|
+
* {@link summarizePayload}. Built on the SAME classification path as the
|
|
16
|
+
* retention markers in `summarize.ts` (#14 / #13c-A), extended with
|
|
17
|
+
* bounded structural descent. Structured-clone-safe by construction.
|
|
18
|
+
* - `'clone'` — `structuredClone` at capture time (the capture-tier
|
|
19
|
+
* spelling of retention `'full'` — see the mapping notes in
|
|
20
|
+
* `policies.ts`). If the payload is not clonable (functions, symbols,
|
|
21
|
+
* live handles), capture DEGRADES to `'summary'` and reports through the
|
|
22
|
+
* `warn` hook — a capture must never throw into the producer.
|
|
23
|
+
* - `'ref'` — pass-through of the live reference. The CALLER asserts
|
|
24
|
+
* immutability for the delivery window (safe e.g. for committed-state
|
|
25
|
+
* reads, proven immutable-after-swap in #13/#13b). Exempt from the
|
|
26
|
+
* clone-safety guarantee — see the dev-warn seam below.
|
|
27
|
+
*
|
|
28
|
+
* Dev-warn seam (resolves the isDevMode-would-be-an-engine-import problem):
|
|
29
|
+
* This module must not import `scope/detectCircular` (engine territory).
|
|
30
|
+
* Instead, `capture()` accepts {@link CaptureHooks} with an optional
|
|
31
|
+
* `warn` callback and invokes it on every `'ref'` capture and every
|
|
32
|
+
* `'clone'` degradation. The WIRING layer (Block 6) binds `warn` to an
|
|
33
|
+
* `isDevMode()`-gated, deduplicated console warner; the pure module stays
|
|
34
|
+
* engine-free and silent by default (no hooks ⇒ no warning, zero cost).
|
|
35
|
+
*
|
|
36
|
+
* Summarizer bounds (documented contract, exported as constants):
|
|
37
|
+
* - depth ≤ {@link PAYLOAD_SUMMARY_MAX_DEPTH} (3) — deeper structure
|
|
38
|
+
* collapses to a classified leaf with `depthClipped: true`.
|
|
39
|
+
* - breadth ≤ {@link PAYLOAD_SUMMARY_MAX_ENTRIES} (16) per object/array —
|
|
40
|
+
* the remainder is dropped and flagged `truncated: true` (the honest
|
|
41
|
+
* `size` still reports the real count).
|
|
42
|
+
* - total ≤ {@link PAYLOAD_SUMMARY_MAX_NODES} (128) summary nodes per
|
|
43
|
+
* payload — a global budget so wide×deep payloads stay O(1)-bounded.
|
|
44
|
+
* - string previews ≤ `SUMMARY_PREVIEW_LENGTH` (80) chars.
|
|
45
|
+
* Cycles are detected (ancestor set) and flagged `circular: true`;
|
|
46
|
+
* throwing getters yield a `'unreadable'` leaf; symbol-keyed properties
|
|
47
|
+
* are ignored (same as JSON / `Object.keys`); `Map`/`Set` are leaves with
|
|
48
|
+
* their REAL entry count (no descent — matches `summarize.ts`).
|
|
49
|
+
*/
|
|
50
|
+
import { type SummaryValueType } from './summarize.js';
|
|
51
|
+
/** Which observer channel produced a captured event (RFC-001 §5). */
|
|
52
|
+
export type CaptureChannel = 'scope' | 'flow' | 'emit';
|
|
53
|
+
/**
|
|
54
|
+
* How an event payload is materialized into the envelope (RFC-001 §5).
|
|
55
|
+
* See the module header for the full per-policy contract.
|
|
56
|
+
*/
|
|
57
|
+
export type CapturePolicy = 'summary' | 'clone' | 'ref';
|
|
58
|
+
/**
|
|
59
|
+
* A captured observer event — self-contained and immutable (shallow-frozen
|
|
60
|
+
* at creation). `seq` is the arrival stamp assigned at capture under the
|
|
61
|
+
* single JS thread: it totally orders events ACROSS all three channels, is
|
|
62
|
+
* monotonic, and is gap-detectable (a dropped event leaves a visible hole
|
|
63
|
+
* in the delivered `seq` sequence — honest loss accounting).
|
|
64
|
+
*/
|
|
65
|
+
export interface CaptureEnvelope {
|
|
66
|
+
/** Arrival stamp — total order across channels; gaps reveal drops. */
|
|
67
|
+
readonly seq: number;
|
|
68
|
+
/** Producing observer channel. */
|
|
69
|
+
readonly channel: CaptureChannel;
|
|
70
|
+
/** Producing hook name — `'onWrite'`, `'onStageExecuted'`, `'onEmit'`, ... */
|
|
71
|
+
readonly method: string;
|
|
72
|
+
/** The execution step that produced the event (`stageId#executionIndex`). */
|
|
73
|
+
readonly runtimeStageId: string;
|
|
74
|
+
/** The run that produced the event (Convention 4 per-run scoping). */
|
|
75
|
+
readonly runId: string;
|
|
76
|
+
/**
|
|
77
|
+
* Per {@link CapturePolicy} — NEVER a live engine reference under
|
|
78
|
+
* `'summary'` / `'clone'`; under `'ref'` the caller asserted immutability.
|
|
79
|
+
*/
|
|
80
|
+
readonly payload: unknown;
|
|
81
|
+
/** Capture wall-clock (ms epoch by default; injectable via hooks.now). */
|
|
82
|
+
readonly capturedAt: number;
|
|
83
|
+
}
|
|
84
|
+
/** The raw event description handed to {@link capture}. */
|
|
85
|
+
export interface CaptureRequest {
|
|
86
|
+
/** Arrival stamp — assigned by the merged queue's counter (Block 3). */
|
|
87
|
+
readonly seq: number;
|
|
88
|
+
readonly channel: CaptureChannel;
|
|
89
|
+
readonly method: string;
|
|
90
|
+
readonly runtimeStageId: string;
|
|
91
|
+
readonly runId: string;
|
|
92
|
+
/** The LIVE payload — `capture()` materializes it per policy. */
|
|
93
|
+
readonly payload: unknown;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Engine-free seams injected by the wiring layer (Block 6). The pure module
|
|
97
|
+
* never imports dev-mode or clock infrastructure.
|
|
98
|
+
*/
|
|
99
|
+
export interface CaptureHooks {
|
|
100
|
+
/**
|
|
101
|
+
* Diagnostic sink — invoked on every `'ref'` capture (caller-asserted
|
|
102
|
+
* immutability) and every `'clone'` → `'summary'` degradation. Block 6
|
|
103
|
+
* binds this to an `isDevMode()`-gated, deduplicated warner.
|
|
104
|
+
*/
|
|
105
|
+
readonly warn?: (message: string) => void;
|
|
106
|
+
/** Clock for `capturedAt` — defaults to `Date.now`. Injectable for tests. */
|
|
107
|
+
readonly now?: () => number;
|
|
108
|
+
}
|
|
109
|
+
/** Max nesting depth a payload summary descends before clipping. */
|
|
110
|
+
export declare const PAYLOAD_SUMMARY_MAX_DEPTH = 3;
|
|
111
|
+
/** Max entries summarized per object/array level before truncating. */
|
|
112
|
+
export declare const PAYLOAD_SUMMARY_MAX_ENTRIES = 16;
|
|
113
|
+
/** Global per-payload budget of summary nodes (wide×deep hard bound). */
|
|
114
|
+
export declare const PAYLOAD_SUMMARY_MAX_NODES = 128;
|
|
115
|
+
/**
|
|
116
|
+
* Leaf classification for a summary node — the `summarize.ts` family plus
|
|
117
|
+
* `'unreadable'` for properties whose getter threw during capture.
|
|
118
|
+
*/
|
|
119
|
+
export type PayloadSummaryType = SummaryValueType | 'unreadable';
|
|
120
|
+
/**
|
|
121
|
+
* One node of a payload summary tree. Every field is a primitive, a plain
|
|
122
|
+
* object, or a plain array — structured-clone-safe by construction.
|
|
123
|
+
*/
|
|
124
|
+
export interface PayloadSummaryNode {
|
|
125
|
+
/** Classification — same rules as the retention markers (one code path). */
|
|
126
|
+
readonly type: PayloadSummaryType;
|
|
127
|
+
/** Honest size proxy: string length, array length, or key/entry count. */
|
|
128
|
+
readonly size?: number;
|
|
129
|
+
/** First `SUMMARY_PREVIEW_LENGTH` chars — primitives/strings only. */
|
|
130
|
+
readonly preview?: string;
|
|
131
|
+
/** Summarized own enumerable string-keyed properties (objects). */
|
|
132
|
+
readonly fields?: Readonly<Record<string, PayloadSummaryNode>>;
|
|
133
|
+
/** Summarized leading items (arrays). */
|
|
134
|
+
readonly items?: readonly PayloadSummaryNode[];
|
|
135
|
+
/** Entries were omitted here (breadth cap or node budget). */
|
|
136
|
+
readonly truncated?: boolean;
|
|
137
|
+
/** This value is an ancestor of itself — descent stopped. */
|
|
138
|
+
readonly circular?: true;
|
|
139
|
+
/** {@link PAYLOAD_SUMMARY_MAX_DEPTH} reached — children not descended. */
|
|
140
|
+
readonly depthClipped?: true;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Root of a payload summary — branded so consumers (and tests) can detect
|
|
144
|
+
* that a payload was summarized rather than cloned. Sibling of the
|
|
145
|
+
* `__readSummary` / `__writeSummary` retention markers.
|
|
146
|
+
*/
|
|
147
|
+
export interface PayloadSummary extends PayloadSummaryNode {
|
|
148
|
+
/** Discriminant — `'summary'`-policy envelope payloads carry this. */
|
|
149
|
+
readonly __payloadSummary: true;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Produce a bounded, reference-free, structured-clone-safe summary of an
|
|
153
|
+
* arbitrary payload. Never throws; never holds a reference into the source
|
|
154
|
+
* value (every node is a fresh object whose fields are primitives). Bounds
|
|
155
|
+
* are documented in the module header.
|
|
156
|
+
*/
|
|
157
|
+
export declare function summarizePayload(payload: unknown): PayloadSummary;
|
|
158
|
+
/**
|
|
159
|
+
* Capture one observer event into an immutable {@link CaptureEnvelope}.
|
|
160
|
+
*
|
|
161
|
+
* Guarantees:
|
|
162
|
+
* - Never throws into the producer (`'clone'` degradation, summarizer
|
|
163
|
+
* never-throws contract).
|
|
164
|
+
* - The returned envelope is shallow-frozen — `seq`/`channel`/... cannot
|
|
165
|
+
* be reassigned. Under `'summary'`/`'clone'` the payload holds no live
|
|
166
|
+
* reference into the source; under `'ref'` it intentionally does.
|
|
167
|
+
* - Default policy is `'summary'` (cheapest safe tier).
|
|
168
|
+
*/
|
|
169
|
+
export declare function capture(request: CaptureRequest, policy?: CapturePolicy, hooks?: CaptureHooks): CaptureEnvelope;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* capture/ — Shared value-capture / retention primitives (zero deps, leaf
|
|
3
|
+
* module — `memory/` imports it, never the reverse).
|
|
4
|
+
*
|
|
5
|
+
* One home for "what do we keep about a tracked operation's value?":
|
|
6
|
+
* - `RetentionPolicy` — the `'full' | 'summary' | 'off'` family behind the
|
|
7
|
+
* #14 `readTracking` and #13c-A `writeTracking` dials.
|
|
8
|
+
* - `summarizeReadValue` / `summarizeWriteValue` — the parameterized
|
|
9
|
+
* summary-marker builders sharing one classification path.
|
|
10
|
+
*
|
|
11
|
+
* RFC-001 (deferred observer delivery) builds its capture tier on this
|
|
12
|
+
* module — see the mapping notes in `policies.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export type { RetentionPolicy } from './policies.js';
|
|
15
|
+
export type { ReadSummaryMarker, SummaryValueType, WriteSummaryMarker } from './summarize.js';
|
|
16
|
+
export { READ_PREVIEW_LENGTH, summarizeReadValue, summarizeWriteValue, SUMMARY_PREVIEW_LENGTH } from './summarize.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* policies.ts — The retention policy family shared by every snapshot-tracking
|
|
3
|
+
* dial (#14 `readTracking`, #13c-A `writeTracking`).
|
|
4
|
+
*
|
|
5
|
+
* RETENTION answers "what does the engine KEEP in its own snapshot state
|
|
6
|
+
* (`StageSnapshot.stageReads` / `stageWrites`, and therefore the commit
|
|
7
|
+
* observer's mutations payload) after the moment of the operation?" It is
|
|
8
|
+
* distinct from DELIVERY — what an observer receives at event time
|
|
9
|
+
* (`ScopeRecorder.onRead`/`onWrite` always deliver the live value, in every
|
|
10
|
+
* retention mode).
|
|
11
|
+
*
|
|
12
|
+
* ── RFC-001 (deferred observer delivery) mapping ──────────────────────────
|
|
13
|
+
* RFC-001's capture tier uses the vocabulary `'clone' | 'summary' | 'ref'`.
|
|
14
|
+
* When its Block 1 lands it builds on THIS module:
|
|
15
|
+
*
|
|
16
|
+
* - RFC capture `'clone'` ≈ retention `'full'` (alias at the module
|
|
17
|
+
* boundary — same semantics: structuredClone at capture time).
|
|
18
|
+
* - RFC capture `'summary'` ≈ retention `'summary'` (same marker shapes —
|
|
19
|
+
* see `summarize.ts`).
|
|
20
|
+
* - RFC capture `'ref'` is DELIVERY-tier only and is NOT implemented here
|
|
21
|
+
* — reserved. Retention must never hold live references into engine
|
|
22
|
+
* state: retained entries outlive the stage (the execution tree keeps
|
|
23
|
+
* them for the whole run), while a `'ref'` is only safe within the
|
|
24
|
+
* immutability window of the captured value. Holding one in retention
|
|
25
|
+
* would either pin state generations (the #18 leak) or expose
|
|
26
|
+
* later-mutated values as if they were point-in-time captures.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* How a tracked operation's value is retained in the per-stage snapshot view.
|
|
30
|
+
*
|
|
31
|
+
* - `'full'` — `structuredClone` the value at the moment of the operation
|
|
32
|
+
* (the historical default for both dials; point-in-time, detached copy).
|
|
33
|
+
* - `'summary'` — retain a cheap marker (type + size proxy + short preview)
|
|
34
|
+
* instead of the value. O(1)-ish per operation; no value clone.
|
|
35
|
+
* - `'off'` — retain nothing. The operation itself is unaffected (reads
|
|
36
|
+
* still return values, writes still commit); only the snapshot bookkeeping
|
|
37
|
+
* is skipped.
|
|
38
|
+
*
|
|
39
|
+
* `ReadTrackingMode` (#14) and `WriteTrackingMode` (#13c-A) are public
|
|
40
|
+
* aliases of this type — see `memory/types.ts`.
|
|
41
|
+
*/
|
|
42
|
+
export type RetentionPolicy = 'full' | 'summary' | 'off';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* summarize.ts — Cheap value summarization for `'summary'` retention.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `StageContext` (#14's `summarizeReadValue`) into a shared,
|
|
5
|
+
* brand-parameterized helper so both snapshot-tracking dials (#14
|
|
6
|
+
* `readTracking`, #13c-A `writeTracking`) — and later RFC-001's deferred
|
|
7
|
+
* observer capture tier — produce identical summaries from ONE code path.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately avoids every O(value) operation: no clone, no serialization.
|
|
10
|
+
* See {@link ReadSummaryMarker} for the honest-cost contract.
|
|
11
|
+
*/
|
|
12
|
+
/** Max characters captured in a summary marker's `preview`. */
|
|
13
|
+
export declare const SUMMARY_PREVIEW_LENGTH = 80;
|
|
14
|
+
/**
|
|
15
|
+
* Compat alias for {@link SUMMARY_PREVIEW_LENGTH} — the name shipped with
|
|
16
|
+
* #14, kept so existing import paths (`memory/types`, the memory barrel)
|
|
17
|
+
* stay valid. Same constant; reads and writes share one preview cap.
|
|
18
|
+
*/
|
|
19
|
+
export declare const READ_PREVIEW_LENGTH = 80;
|
|
20
|
+
/** `typeof` result, refined to 'array' / 'null' for objects. */
|
|
21
|
+
export type SummaryValueType = 'string' | 'number' | 'boolean' | 'bigint' | 'symbol' | 'function' | 'object' | 'array' | 'null';
|
|
22
|
+
/** Brand-free summary fields shared by both marker shapes. */
|
|
23
|
+
interface ValueSummary {
|
|
24
|
+
/** `typeof` result, refined to 'array' / 'null' for objects. */
|
|
25
|
+
type: SummaryValueType;
|
|
26
|
+
/** Size proxy: string length, array length, or object key count. */
|
|
27
|
+
size?: number;
|
|
28
|
+
/** First {@link SUMMARY_PREVIEW_LENGTH} chars — primitives and strings only. */
|
|
29
|
+
preview?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Marker recorded in `StageSnapshot.stageReads` under `readTracking: 'summary'`.
|
|
33
|
+
*
|
|
34
|
+
* Honest cost note: `size` is a cheap proxy (string length / array length /
|
|
35
|
+
* object key count), NOT a serialized byte count — computing real byte size
|
|
36
|
+
* would require an O(value) serialization, which is exactly the cost the
|
|
37
|
+
* summary mode removes. `preview` is only produced for primitives and strings
|
|
38
|
+
* (first {@link SUMMARY_PREVIEW_LENGTH} characters); objects and arrays carry
|
|
39
|
+
* no preview for the same reason.
|
|
40
|
+
*/
|
|
41
|
+
export interface ReadSummaryMarker extends ValueSummary {
|
|
42
|
+
/** Discriminant — lets snapshot consumers detect marker entries. */
|
|
43
|
+
__readSummary: true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Marker recorded in `StageSnapshot.stageWrites` (and the commit observer's
|
|
47
|
+
* mutations payload) under `writeTracking: 'summary'` (#13c-A). Same fields
|
|
48
|
+
* and cost contract as {@link ReadSummaryMarker}; distinct brand so consumers
|
|
49
|
+
* can tell which dial produced an entry.
|
|
50
|
+
*/
|
|
51
|
+
export interface WriteSummaryMarker extends ValueSummary {
|
|
52
|
+
/** Discriminant — lets snapshot consumers detect marker entries. */
|
|
53
|
+
__writeSummary: true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Summarize a tracked READ for `readTracking: 'summary'` (#14). Byte-identical
|
|
57
|
+
* output to the pre-extraction `StageContext`-local implementation.
|
|
58
|
+
*/
|
|
59
|
+
export declare function summarizeReadValue(value: unknown): ReadSummaryMarker;
|
|
60
|
+
/**
|
|
61
|
+
* Summarize a tracked WRITE for `writeTracking: 'summary'` (#13c-A). Sibling
|
|
62
|
+
* of {@link summarizeReadValue} — same classification, distinct brand.
|
|
63
|
+
*/
|
|
64
|
+
export declare function summarizeWriteValue(value: unknown): WriteSummaryMarker;
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* contract/defineContract.ts — Factory for creating a FlowChartContract.
|
|
3
|
+
*
|
|
4
|
+
* Wraps a compiled FlowChart with I/O schemas and an output mapper,
|
|
5
|
+
* using the same pattern as SubflowMountOptions (inputMapper/outputMapper).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const contract = defineContract(chart, {
|
|
9
|
+
* inputSchema: z.object({ name: z.string() }),
|
|
10
|
+
* outputSchema: z.object({ greeting: z.string() }),
|
|
11
|
+
* outputMapper: (scope) => ({ greeting: scope.message as string }),
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* const openapi = contract.toOpenAPI();
|
|
15
|
+
*/
|
|
16
|
+
import type { FlowChart } from '../builder/types.js';
|
|
17
|
+
import type { FlowChartContract, FlowChartContractOptions } from './types.js';
|
|
18
|
+
export declare function defineContract<TInput = unknown, TOutput = unknown>(chart: FlowChart, options: FlowChartContractOptions<TInput, TOutput>): FlowChartContract<TInput, TOutput>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* contract/ — FlowChart I/O contract and OpenAPI generation layer.
|
|
3
|
+
*
|
|
4
|
+
* Standalone library: wraps a compiled FlowChart with input/output schemas
|
|
5
|
+
* and generates OpenAPI 3.1 specs. Uses the same inputMapper/outputMapper
|
|
6
|
+
* pattern as subflow mounting.
|
|
7
|
+
*
|
|
8
|
+
* Zero runtime deps on Zod — Zod schemas detected via duck-typing and
|
|
9
|
+
* converted to JSON Schema at contract creation time.
|
|
10
|
+
*/
|
|
11
|
+
export { defineContract } from './defineContract.js';
|
|
12
|
+
export { normalizeSchema, zodToJsonSchema } from './schema.js';
|
|
13
|
+
export { generateOpenAPI } from './openapi.js';
|
|
14
|
+
export type { FlowChartContract, FlowChartContractOptions, JsonSchema, OpenAPIOptions, OpenAPISpec, SchemaInput, } from './types.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* contract/openapi.ts — OpenAPI 3.1 spec generator.
|
|
3
|
+
*
|
|
4
|
+
* Generates an OpenAPI spec from a FlowChartContract by combining:
|
|
5
|
+
* - chart.description → operation description (built incrementally during FlowChartBuilder.build())
|
|
6
|
+
* - inputSchema → requestBody
|
|
7
|
+
* - outputSchema → response
|
|
8
|
+
*
|
|
9
|
+
* chart.description is assembled by FlowChartBuilder as each stage is added —
|
|
10
|
+
* no post-processing walk of buildTimeStructure is needed or performed here.
|
|
11
|
+
*/
|
|
12
|
+
import type { FlowChartContract, OpenAPIOptions, OpenAPISpec } from './types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Generates an OpenAPI 3.1 spec from a FlowChartContract.
|
|
15
|
+
* Uses `chart.description` which FlowChartBuilder assembles at build time —
|
|
16
|
+
* no post-processing walk of buildTimeStructure is performed here.
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateOpenAPI(contract: FlowChartContract, options?: OpenAPIOptions): OpenAPISpec;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* contract/schema.ts — Schema normalization utilities.
|
|
3
|
+
*
|
|
4
|
+
* Converts Zod schemas or raw JSON Schema objects into a normalized
|
|
5
|
+
* JsonSchema format. Detection delegated to schema/detect.ts (single source of truth).
|
|
6
|
+
*
|
|
7
|
+
* Standalone: no dependency on Zod at import time.
|
|
8
|
+
* Compatible with Zod v4 internals.
|
|
9
|
+
*/
|
|
10
|
+
import type { JsonSchema, SchemaInput } from './types.js';
|
|
11
|
+
/** Convert a Zod schema object to JSON Schema. */
|
|
12
|
+
export declare function zodToJsonSchema(zodSchema: Record<string, unknown>): JsonSchema;
|
|
13
|
+
export declare function normalizeSchema(input: SchemaInput): JsonSchema;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* contract/types.ts — Types for the FlowChart contract layer.
|
|
3
|
+
*
|
|
4
|
+
* Defines the I/O boundary for a flowchart: input schema, output schema,
|
|
5
|
+
* and output mapper. Uses the same pattern as SubflowMountOptions
|
|
6
|
+
* (inputMapper/outputMapper) but at the top-level flowchart boundary.
|
|
7
|
+
*/
|
|
8
|
+
import type { FlowChart } from '../builder/types.js';
|
|
9
|
+
export type JsonSchema = {
|
|
10
|
+
type?: string | string[];
|
|
11
|
+
properties?: Record<string, JsonSchema>;
|
|
12
|
+
required?: string[];
|
|
13
|
+
items?: JsonSchema;
|
|
14
|
+
enum?: unknown[];
|
|
15
|
+
description?: string;
|
|
16
|
+
default?: unknown;
|
|
17
|
+
format?: string;
|
|
18
|
+
additionalProperties?: boolean | JsonSchema;
|
|
19
|
+
oneOf?: JsonSchema[];
|
|
20
|
+
anyOf?: JsonSchema[];
|
|
21
|
+
allOf?: JsonSchema[];
|
|
22
|
+
$ref?: string;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
/** Anything with a `def` (Zod v4) or `_def` (Zod v3) property is treated as a Zod schema. */
|
|
26
|
+
export type SchemaInput = JsonSchema | {
|
|
27
|
+
def: unknown;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
};
|
|
30
|
+
export interface FlowChartContractOptions<_TInput = unknown, TOutput = unknown> {
|
|
31
|
+
/** Schema describing the input (readOnlyContext) shape. Zod or JSON Schema. */
|
|
32
|
+
inputSchema?: SchemaInput;
|
|
33
|
+
/** Schema describing the output shape. Zod or JSON Schema. */
|
|
34
|
+
outputSchema?: SchemaInput;
|
|
35
|
+
/** Maps the final scope state into the response shape. */
|
|
36
|
+
outputMapper?: (finalScope: Record<string, unknown>) => TOutput;
|
|
37
|
+
}
|
|
38
|
+
export interface FlowChartContract<_TInput = unknown, TOutput = unknown> {
|
|
39
|
+
/**
|
|
40
|
+
* Prototype-linked view of the original compiled FlowChart.
|
|
41
|
+
*
|
|
42
|
+
* Own properties (inputSchema, outputSchema, outputMapper) are shadowed here
|
|
43
|
+
* so that the contract's values take precedence over builder-set values.
|
|
44
|
+
* All other fields (root, stageMap, methods) are inherited via the prototype
|
|
45
|
+
* chain with zero copying. The original chart is never mutated.
|
|
46
|
+
*
|
|
47
|
+
* ⚠️ Do NOT use Object.keys(), spread ({...chart}), or JSON.stringify() on
|
|
48
|
+
* this object — it will only see own (shadowed) properties. Use named
|
|
49
|
+
* property access or chart.toSpec() instead.
|
|
50
|
+
*/
|
|
51
|
+
chart: FlowChart;
|
|
52
|
+
/** JSON Schema for the input (normalized from Zod or raw). */
|
|
53
|
+
inputSchema?: JsonSchema;
|
|
54
|
+
/** JSON Schema for the output (normalized from Zod or raw). */
|
|
55
|
+
outputSchema?: JsonSchema;
|
|
56
|
+
/** Maps the final scope state into the response shape. */
|
|
57
|
+
outputMapper?: (finalScope: Record<string, unknown>) => TOutput;
|
|
58
|
+
/** Auto-generated OpenAPI spec. Description is read from `chart.description` (pre-built at build time). */
|
|
59
|
+
toOpenAPI(options?: OpenAPIOptions): OpenAPISpec;
|
|
60
|
+
}
|
|
61
|
+
export interface OpenAPIOptions {
|
|
62
|
+
/** API version string (default: "1.0.0"). */
|
|
63
|
+
version?: string;
|
|
64
|
+
/** Base path prefix (default: "/"). */
|
|
65
|
+
basePath?: string;
|
|
66
|
+
/** HTTP method for the execute endpoint (default: "post"). */
|
|
67
|
+
method?: string;
|
|
68
|
+
}
|
|
69
|
+
export interface OpenAPISpec {
|
|
70
|
+
openapi: '3.1.0';
|
|
71
|
+
info: {
|
|
72
|
+
title: string;
|
|
73
|
+
description: string;
|
|
74
|
+
version: string;
|
|
75
|
+
};
|
|
76
|
+
paths: Record<string, Record<string, OpenAPIOperation>>;
|
|
77
|
+
components?: {
|
|
78
|
+
schemas?: Record<string, JsonSchema>;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export interface OpenAPIOperation {
|
|
82
|
+
operationId: string;
|
|
83
|
+
summary: string;
|
|
84
|
+
description: string;
|
|
85
|
+
requestBody?: {
|
|
86
|
+
required: boolean;
|
|
87
|
+
content: {
|
|
88
|
+
'application/json': {
|
|
89
|
+
schema: JsonSchema | {
|
|
90
|
+
$ref: string;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
responses: Record<string, {
|
|
96
|
+
description: string;
|
|
97
|
+
content?: {
|
|
98
|
+
'application/json': {
|
|
99
|
+
schema: JsonSchema | {
|
|
100
|
+
$ref: string;
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
}>;
|
|
105
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* decide/decide -- Core decide() and select() helper functions.
|
|
3
|
+
*
|
|
4
|
+
* decide() evaluates rules in order (first-match) and returns a DecisionResult.
|
|
5
|
+
* select() evaluates ALL rules and returns a SelectionResult with all matches.
|
|
6
|
+
*
|
|
7
|
+
* Each rule's `when` can be:
|
|
8
|
+
* - A function: (s) => s.creditScore > 700 (auto-captures reads via temp recorder)
|
|
9
|
+
* - A filter: { creditScore: { gt: 700 } } (captures reads + operators + thresholds)
|
|
10
|
+
*/
|
|
11
|
+
import type { DecideRule, DecisionResult, SelectionResult } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Evaluates rules in order (first-match). Returns a branded DecisionResult.
|
|
14
|
+
*
|
|
15
|
+
* @param scope - TypedScope or ScopeFacade
|
|
16
|
+
* @param rules - Array of DecideRule (function or filter when clauses)
|
|
17
|
+
* @param defaultBranch - Branch ID if no rule matches
|
|
18
|
+
*
|
|
19
|
+
* **Error behavior:** If a `when` function throws during evaluation, the rule is
|
|
20
|
+
* treated as non-matching (`matched: false`) and the error message is captured in
|
|
21
|
+
* `matchError` on that rule's `RuleEvidence` entry. Execution continues with
|
|
22
|
+
* subsequent rules; errors do not propagate to the caller.
|
|
23
|
+
*
|
|
24
|
+
* **Empty-filter behavior (anti-vacuous-truth):** a filter rule whose `when` is
|
|
25
|
+
* `{}` (no evaluable conditions) NEVER matches. This deliberately inverts the
|
|
26
|
+
* Prisma/SQL `where: {}` intuition ("match everything") — a rule that asserts
|
|
27
|
+
* nothing must not win a branch on vacuous truth. Use `defaultBranch` for the
|
|
28
|
+
* catch-all instead. Unknown filter operators also never match (dev mode warns).
|
|
29
|
+
*/
|
|
30
|
+
export declare function decide<S extends object>(scope: S, rules: DecideRule<S>[], defaultBranch: string): DecisionResult;
|
|
31
|
+
/**
|
|
32
|
+
* Evaluates ALL rules (not first-match). Returns a branded SelectionResult.
|
|
33
|
+
*
|
|
34
|
+
* @param scope - TypedScope or ScopeFacade
|
|
35
|
+
* @param rules - Array of DecideRule (function or filter when clauses)
|
|
36
|
+
*
|
|
37
|
+
* **Error behavior:** If a `when` function throws during evaluation, the rule is
|
|
38
|
+
* treated as non-matching (`matched: false`) and the error message is captured in
|
|
39
|
+
* `matchError` on that rule's `RuleEvidence` entry. Evaluation continues with
|
|
40
|
+
* remaining rules; errors do not propagate to the caller.
|
|
41
|
+
*
|
|
42
|
+
* **Empty-filter behavior (anti-vacuous-truth):** a filter rule whose `when` is
|
|
43
|
+
* `{}` (no evaluable conditions) NEVER matches — same rule as `decide()`; an
|
|
44
|
+
* always-selected branch must be expressed explicitly, not via an empty filter.
|
|
45
|
+
* Unknown filter operators also never match (dev mode warns).
|
|
46
|
+
*/
|
|
47
|
+
export declare function select<S extends object>(scope: S, rules: DecideRule<S>[]): SelectionResult;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* decide/evaluator -- Prisma-style filter evaluator for decision rules.
|
|
3
|
+
*
|
|
4
|
+
* Pure function. Takes a WhereFilter, a value getter, and a redaction checker.
|
|
5
|
+
* Evaluates each condition, records the result, returns matched/conditions.
|
|
6
|
+
*
|
|
7
|
+
* All keys in the filter are ANDed (all must match for the rule to match).
|
|
8
|
+
* Decoupled from ScopeFacade — receives callbacks, not scope.
|
|
9
|
+
*/
|
|
10
|
+
import type { FilterCondition, WhereFilter } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Evaluates a Prisma-style filter against scope values.
|
|
13
|
+
*
|
|
14
|
+
* ## Empty filter → NO match (anti-vacuous-truth — inverts Prisma/SQL)
|
|
15
|
+
*
|
|
16
|
+
* A filter with no evaluable conditions (`{}`, or only denied/non-object
|
|
17
|
+
* keys) returns `matched: false`. This deliberately INVERTS the Prisma/SQL
|
|
18
|
+
* intuition where `where: {}` matches everything: in a decision rule, a rule
|
|
19
|
+
* that asserts nothing should never win a branch — "all zero conditions
|
|
20
|
+
* passed" is vacuous truth, and silently routing on it would fabricate
|
|
21
|
+
* decision evidence. Want a catch-all? Use the explicit `defaultBranch`
|
|
22
|
+
* argument of `decide()` instead of an empty `when`.
|
|
23
|
+
*
|
|
24
|
+
* ## Unknown operators → condition fails (+ dev-mode warning)
|
|
25
|
+
*
|
|
26
|
+
* An operator outside the supported set (`eq, ne, gt, gte, lt, lte, in,
|
|
27
|
+
* notIn`) records a failed condition — the rule can never spuriously match
|
|
28
|
+
* through a typo (`gte` misspelled `gle`). With dev mode enabled
|
|
29
|
+
* (`enableDevMode()`), a console warning names the unknown operator and key.
|
|
30
|
+
*
|
|
31
|
+
* @param getValueFn - Reads a value from scope by key (raw, for comparison)
|
|
32
|
+
* @param isRedactedFn - Checks if a key is redacted (for evidence display)
|
|
33
|
+
* @param filter - The WhereFilter to evaluate
|
|
34
|
+
* @returns { matched, conditions } — matched = all conditions passed
|
|
35
|
+
*/
|
|
36
|
+
export declare function evaluateFilter<T extends object>(getValueFn: (key: string) => unknown, isRedactedFn: (key: string) => boolean, filter: WhereFilter<T>): {
|
|
37
|
+
matched: boolean;
|
|
38
|
+
conditions: FilterCondition[];
|
|
39
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* decide/evidence -- Lightweight temp recorder for auto-capturing reads
|
|
3
|
+
* during a when() function call.
|
|
4
|
+
*
|
|
5
|
+
* Attached to scope before calling when(scope), detached after.
|
|
6
|
+
* Captures ReadEvent key + summarized value + redaction flag.
|
|
7
|
+
* Uses summarizeValue() at capture time (no raw object references held).
|
|
8
|
+
*/
|
|
9
|
+
import type { ReadEvent, ScopeRecorder } from '../scope/types.js';
|
|
10
|
+
import type { ReadInput } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Minimal ScopeRecorder that captures reads for decision evidence.
|
|
13
|
+
* Attach before when(), detach after. Collect via getInputs().
|
|
14
|
+
*/
|
|
15
|
+
export declare class EvidenceCollector implements ScopeRecorder {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
private inputs;
|
|
18
|
+
constructor();
|
|
19
|
+
onRead(event: ReadEvent): void;
|
|
20
|
+
/** Returns collected read inputs. */
|
|
21
|
+
getInputs(): ReadInput[];
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* decide/ -- Decision reasoning capture for footprintjs.
|
|
3
|
+
*
|
|
4
|
+
* decide() and select() auto-capture evidence from decider/selector functions:
|
|
5
|
+
* - Function when: (s) => s.creditScore > 700 (auto-captures reads)
|
|
6
|
+
* - Filter when: { creditScore: { gt: 700 } } (captures reads + operators + thresholds)
|
|
7
|
+
*/
|
|
8
|
+
export type { DecideRule, DecisionEvidence, DecisionResult, FilterCondition, FilterOps, FilterRuleEvidence, FunctionRuleEvidence, ReadInput, RuleEvidence, SelectionEvidence, SelectionResult, WhenClause, WhereFilter, } from './types.js';
|
|
9
|
+
export { DECISION_RESULT } from './types.js';
|
|
10
|
+
export { decide, select } from './decide.js';
|
|
11
|
+
export { evaluateFilter } from './evaluator.js';
|
|
12
|
+
export { EvidenceCollector } from './evidence.js';
|