@graphrefly/graphrefly 0.46.0 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base/composition/index.cjs +69 -15
- package/dist/base/composition/index.cjs.map +1 -1
- package/dist/base/composition/index.d.cts +1 -2
- package/dist/base/composition/index.d.ts +1 -2
- package/dist/base/composition/index.js +1 -1
- package/dist/base/index.cjs +69 -15
- package/dist/base/index.cjs.map +1 -1
- package/dist/base/index.d.cts +1 -2
- package/dist/base/index.d.ts +1 -2
- package/dist/base/index.js +1 -1
- package/dist/{chunk-WKSWLSCX.js → chunk-22SG74BD.js} +2 -2
- package/dist/{chunk-OO5BM6CJ.js → chunk-5IMMNARC.js} +2 -2
- package/dist/{chunk-KIIXR252.js → chunk-EHRRQ4IC.js} +2 -2
- package/dist/{chunk-HULCUY35.js → chunk-GWRNLJNW.js} +2 -2
- package/dist/{chunk-5THCXDWY.js → chunk-JKTC747G.js} +2 -2
- package/dist/{chunk-RGL53X5G.js → chunk-TNX5ZGDJ.js} +3 -3
- package/dist/{chunk-LBAJK24K.js → chunk-VLAGJZSL.js} +11 -3
- package/dist/chunk-VLAGJZSL.js.map +1 -0
- package/dist/chunk-Z65DVDEQ.js +146 -0
- package/dist/chunk-Z65DVDEQ.js.map +1 -0
- package/dist/{chunk-GBCENOLN.js → chunk-ZT4WMQW4.js} +3 -3
- package/dist/{chunk-FR6RGA3B.js → chunk-ZVXXDWIB.js} +19 -14
- package/dist/{chunk-FR6RGA3B.js.map → chunk-ZVXXDWIB.js.map} +1 -1
- package/dist/compat/index.cjs +156 -93
- package/dist/compat/index.cjs.map +1 -1
- package/dist/compat/index.d.cts +3 -3
- package/dist/compat/index.d.ts +3 -3
- package/dist/compat/index.js +2 -2
- package/dist/compat/nestjs/index.cjs +156 -93
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +4 -4
- package/dist/compat/nestjs/index.d.ts +4 -4
- package/dist/compat/nestjs/index.js +4 -5
- package/dist/{index-5SU_O78r.d.cts → index-B_p8tnvf.d.cts} +19 -3
- package/dist/{index-CEXCtYYJ.d.ts → index-_HDSmPyp.d.ts} +19 -3
- package/dist/index.cjs +77 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +9 -9
- package/dist/observable-B25XqCbZ.d.cts +59 -0
- package/dist/observable-B25XqCbZ.d.ts +59 -0
- package/dist/presets/ai/index.js +5 -5
- package/dist/presets/harness/index.js +7 -7
- package/dist/presets/index.js +8 -8
- package/dist/solutions/index.js +6 -6
- package/dist/utils/ai/index.js +3 -3
- package/dist/utils/index.cjs +8 -3
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +3 -3
- package/dist/utils/memory/index.cjs +8 -3
- package/dist/utils/memory/index.cjs.map +1 -1
- package/dist/utils/memory/index.d.cts +78 -0
- package/dist/utils/memory/index.d.ts +78 -0
- package/dist/utils/memory/index.js +1 -1
- package/package.json +1 -5
- package/dist/chunk-3QZY5BI7.js +0 -92
- package/dist/chunk-3QZY5BI7.js.map +0 -1
- package/dist/chunk-LBAJK24K.js.map +0 -1
- package/dist/observable-BXQoW1P-.d.cts +0 -36
- package/dist/observable-BXQoW1P-.d.ts +0 -36
- /package/dist/{chunk-WKSWLSCX.js.map → chunk-22SG74BD.js.map} +0 -0
- /package/dist/{chunk-OO5BM6CJ.js.map → chunk-5IMMNARC.js.map} +0 -0
- /package/dist/{chunk-KIIXR252.js.map → chunk-EHRRQ4IC.js.map} +0 -0
- /package/dist/{chunk-HULCUY35.js.map → chunk-GWRNLJNW.js.map} +0 -0
- /package/dist/{chunk-5THCXDWY.js.map → chunk-JKTC747G.js.map} +0 -0
- /package/dist/{chunk-RGL53X5G.js.map → chunk-TNX5ZGDJ.js.map} +0 -0
- /package/dist/{chunk-GBCENOLN.js.map → chunk-ZT4WMQW4.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/base/composition/index.ts","../../../src/base/composition/backpressure.ts","../../../src/base/composition/distill.ts","../../../src/base/sources/async.ts","../../../src/base/composition/external-register.ts","../../../src/base/composition/materialize.ts","../../../src/base/composition/observable.ts","../../../src/base/composition/pubsub.ts","../../../src/base/composition/single-from-any.ts","../../../src/base/sources/settled.ts","../../../src/base/composition/verifiable.ts"],"sourcesContent":["/**\n * Base composition — domain-agnostic composition helpers.\n *\n * @module\n */\n\nexport * from \"./backpressure.js\";\nexport * from \"./distill.js\";\nexport * from \"./external-register.js\";\nexport * from \"./materialize.js\";\nexport * from \"./observable.js\";\nexport * from \"./pubsub.js\";\nexport * from \"./single-from-any.js\";\nexport * from \"./verifiable.js\";\n// topology-diff is substrate (pure-ts); consumers import from @graphrefly/pure-ts/extra\n","/**\n * Watermark-based backpressure controller — reactive PAUSE/RESUME flow control.\n *\n * Purely synchronous, event-driven. No timers, no polling, no Promises.\n * Each controller instance uses a unique lockId so multiple controllers\n * on the same upstream node do not collide.\n *\n * @module\n */\n\nimport { type Messages, PAUSE, RESUME } from \"@graphrefly/pure-ts/core\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type WatermarkOptions = {\n\t/** Pending count at which PAUSE is sent upstream. */\n\thighWaterMark: number;\n\t/** Pending count at which RESUME is sent upstream (after being paused). */\n\tlowWaterMark: number;\n};\n\nexport type WatermarkController = {\n\t/** Call when a DATA message is buffered/enqueued. Returns `true` if PAUSE was just sent. */\n\tonEnqueue(): boolean;\n\t/** Call when a buffered item is consumed. Returns `true` if RESUME was just sent. */\n\tonDequeue(): boolean;\n\t/** Current un-consumed item count. */\n\treadonly pending: number;\n\t/** Whether upstream is currently paused by this controller. */\n\treadonly paused: boolean;\n\t/** Dispose: if paused, sends RESUME to unblock upstream. */\n\tdispose(): void;\n};\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nlet nextLockId = 0;\n\n/**\n * Creates a watermark-based backpressure controller.\n *\n * @param sendUp - Callback that delivers messages upstream (typically `handle.up`).\n * @param opts - High/low watermark thresholds (item counts).\n * @returns A {@link WatermarkController}.\n *\n * @example\n * ```ts\n * const handle = graph.observe(\"fast-source\");\n * const wm = createWatermarkController(\n * (msgs) => handle.up(msgs),\n * { highWaterMark: 64, lowWaterMark: 16 },\n * );\n *\n * // In sink callback:\n * handle.subscribe((msgs) => {\n * for (const msg of msgs) {\n * if (msg[0] === DATA) {\n * buffer.push(msg[1]);\n * wm.onEnqueue();\n * }\n * }\n * });\n *\n * // When consumer drains:\n * const item = buffer.shift();\n * wm.onDequeue();\n * ```\n *\n * @category extra\n */\nexport function createWatermarkController(\n\tsendUp: (messages: Messages) => void,\n\topts: WatermarkOptions,\n): WatermarkController {\n\tif (opts.highWaterMark < 1) throw new RangeError(\"highWaterMark must be >= 1\");\n\tif (opts.lowWaterMark < 0) throw new RangeError(\"lowWaterMark must be >= 0\");\n\tif (opts.lowWaterMark >= opts.highWaterMark)\n\t\tthrow new RangeError(\"lowWaterMark must be < highWaterMark\");\n\tconst lockId = Symbol(`bp-${++nextLockId}`);\n\tlet pending = 0;\n\tlet paused = false;\n\n\treturn {\n\t\tonEnqueue(): boolean {\n\t\t\tpending += 1;\n\t\t\tif (!paused && pending >= opts.highWaterMark) {\n\t\t\t\tpaused = true;\n\t\t\t\tsendUp([[PAUSE, lockId]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\tonDequeue(): boolean {\n\t\t\tif (pending > 0) pending -= 1;\n\t\t\tif (paused && pending <= opts.lowWaterMark) {\n\t\t\t\tpaused = false;\n\t\t\t\tsendUp([[RESUME, lockId]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\tget pending() {\n\t\t\treturn pending;\n\t\t},\n\t\tget paused() {\n\t\t\treturn paused;\n\t\t},\n\t\tdispose() {\n\t\t\tif (paused) {\n\t\t\t\tpaused = false;\n\t\t\t\tsendUp([[RESUME, lockId]]);\n\t\t\t}\n\t\t},\n\t};\n}\n","/**\n * Budget-constrained reactive memory composition (roadmap §3.2b).\n *\n * Moved to base/composition/distill.ts during cleave A2.\n */\n\nimport { batch, factoryTag, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport {\n\tfromAny,\n\ttype NodeInput,\n\ttype ReactiveMapBundle,\n\ttype ReactiveMapOptions,\n\treactiveMap,\n\tswitchMap,\n\twithLatestFrom,\n} from \"@graphrefly/pure-ts/extra\";\nimport { forEach } from \"../sources/async.js\";\n\nfunction isNodeLike<T>(value: unknown): value is Node<T> {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"cache\" in (value as Node<T>) &&\n\t\ttypeof (value as Node<T>).subscribe === \"function\"\n\t);\n}\n\nexport type Extraction<TMem> = {\n\tupsert: Array<{ key: string; value: TMem }>;\n\tremove?: string[];\n};\n\nexport type DistillOptions<TMem> = {\n\tscore: (mem: TMem, context: unknown) => number;\n\tcost: (mem: TMem) => number;\n\tbudget?: number;\n\tevict?: (key: string, mem: TMem) => boolean | Node<boolean>;\n\tconsolidate?: (entries: ReadonlyMap<string, TMem>) => NodeInput<Extraction<TMem>>;\n\tconsolidateTrigger?: NodeInput<unknown>;\n\tcontext?: NodeInput<unknown>;\n\tmapOptions?: ReactiveMapOptions<string, TMem>;\n};\n\nexport type DistillBundle<TMem> = {\n\tstore: ReactiveMapBundle<string, TMem>;\n\tcompact: Node<Array<{ key: string; value: TMem; score: number }>>;\n\tsize: Node<number>;\n};\n\nfunction keepalive(node: Node): void {\n\tnode.subscribe(() => undefined);\n}\n\n/**\n * Defensive snapshot → ReadonlyMap coercion (D2 /qa lock, Tier 9.1).\n *\n * `ReactiveMapBundle.entries` always emits a real `Map` on the live emit\n * path. The non-Map case happens on snapshot **restore**: the default\n * `JsonGraphCodec` serializes a `Map` to `null`/`{}`/`[]` depending on the\n * codec configuration, and `Graph.restore` writes that decoded value back\n * to the cache. A naive `(snapshot as ReadonlyMap) ?? new Map()` would\n * pass a plain object through and then `.entries()` / `.size` access would\n * silently yield wrong results (or throw). The runtime `instanceof Map`\n * check below restores the safety net the previous `mapFromSnapshot` helper\n * provided before its initial deletion in Tier 10.1.\n */\nfunction mapFromSnapshot<TMem>(snapshot: unknown): ReadonlyMap<string, TMem> {\n\tif (snapshot instanceof Map) return snapshot as ReadonlyMap<string, TMem>;\n\treturn new Map<string, TMem>();\n}\n\nfunction applyExtraction<TMem>(\n\tstore: ReactiveMapBundle<string, TMem>,\n\textraction: Extraction<TMem>,\n): void {\n\tif (!Array.isArray(extraction.upsert)) {\n\t\tthrow new TypeError(\"distill extraction requires upsert: Array<{ key, value }>\");\n\t}\n\tbatch(() => {\n\t\tfor (const { key, value } of extraction.upsert) {\n\t\t\tstore.set(key, value);\n\t\t}\n\t\tfor (const key of extraction.remove ?? []) {\n\t\t\tstore.delete(key);\n\t\t}\n\t});\n}\n\n/**\n * Budget-constrained reactive memory composition.\n *\n * **Tier 1.5.4 (Session A.5 lock, 2026-04-27):** `extractFn` receives the\n * source and existing-store as `Node`s. Distill calls `extractFn` ONCE at\n * wiring time and consumes the returned stream of extractions. The user\n * controls reactive composition — wrap with `switchMap` for cancel-on-new-input,\n * `mergeMap` for parallel, `derived` for sync transforms. See COMPOSITION-GUIDE\n * §40 for the recipe.\n */\nexport function distill<TRaw, TMem>(\n\tsource: NodeInput<TRaw>,\n\textractFn: (\n\t\traw: Node<TRaw>,\n\t\texisting: Node<ReadonlyMap<string, TMem>>,\n\t) => NodeInput<Extraction<TMem>>,\n\topts: DistillOptions<TMem>,\n): DistillBundle<TMem> {\n\tconst sourceNode = fromAny(source);\n\tconst store = reactiveMap<string, TMem>(opts.mapOptions ?? {});\n\tconst budget = opts.budget ?? 2000;\n\tconst hasContext = opts.context !== undefined && opts.context !== null;\n\tconst contextNode = hasContext ? fromAny(opts.context) : node<unknown>([], { initial: null });\n\n\t// `latestStore` (formerly a §28 closure-mirror) is no longer needed —\n\t// Phase 10.5 (`withLatestFrom` flipped to `partial: false`) fixed the\n\t// W1 initial-pair drop. `consolidate` now uses\n\t// `withLatestFrom(trigger, store.entries)` below to pair each trigger\n\t// with the latest store snapshot via a real reactive edge (visible in\n\t// `describe()`). The `mapFromSnapshot` transform runs inside the\n\t// switchMap fn body.\n\n\t// Tier 1.5.4: one-shot wire. User's `extractFn` returns the reactive\n\t// extraction stream — distill just `forEach`s and applies. No internal\n\t// switchMap; user picks the cancellation / queueing semantics.\n\tconst extractionStream = fromAny(\n\t\textractFn(sourceNode, store.entries as Node<ReadonlyMap<string, TMem>>),\n\t);\n\tforEach(extractionStream, (extraction) => {\n\t\tapplyExtraction(store, extraction);\n\t});\n\n\tif (opts.evict) {\n\t\t// Track active verdict-node subscriptions so we can react to Node<boolean> changes.\n\t\tconst verdictUnsubs = new Map<string, () => void>();\n\n\t\tconst evictionKeys = node<string[]>(\n\t\t\t[store.entries],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst batch0 = batchData[0];\n\t\t\t\tconst snapshot = batch0 != null && batch0.length > 0 ? batch0.at(-1) : ctx.prevData[0];\n\t\t\t\tconst out: string[] = [];\n\t\t\t\tconst entries = mapFromSnapshot<TMem>(snapshot);\n\t\t\t\t// Clean up verdict subscriptions for removed keys.\n\t\t\t\tfor (const key of verdictUnsubs.keys()) {\n\t\t\t\t\tif (!entries.has(key)) {\n\t\t\t\t\t\tverdictUnsubs.get(key)!();\n\t\t\t\t\t\tverdictUnsubs.delete(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const [key, mem] of entries) {\n\t\t\t\t\tconst verdict = opts.evict!(key, mem);\n\t\t\t\t\tif (isNodeLike<boolean>(verdict)) {\n\t\t\t\t\t\t// Subscribe if not already — push-on-subscribe fires with\n\t\t\t\t\t\t// the verdict's current value on first subscribe, so an\n\t\t\t\t\t\t// already-true verdict deletes via the callback without\n\t\t\t\t\t\t// needing a `verdict.cache` read (closes P3 audit #3).\n\t\t\t\t\t\t// Future transitions to `true` flow through the same path.\n\t\t\t\t\t\tif (!verdictUnsubs.has(key)) {\n\t\t\t\t\t\t\tconst unsub = forEach(verdict, (val) => {\n\t\t\t\t\t\t\t\tif (val === true && store.has(key)) {\n\t\t\t\t\t\t\t\t\tstore.delete(key);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tverdictUnsubs.set(key, unsub);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof verdict === \"boolean\") {\n\t\t\t\t\t\tif (verdict) out.push(key);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthrow new TypeError(\"distill evict() must return boolean or Node<boolean>\");\n\t\t\t\t}\n\t\t\t\tactions.emit(out);\n\t\t\t},\n\t\t\t{ describeKind: \"derived\" },\n\t\t);\n\t\tforEach(evictionKeys, (keys) => {\n\t\t\tfor (const key of keys) store.delete(key);\n\t\t});\n\t}\n\n\tconst hasConsolidateTrigger =\n\t\topts.consolidateTrigger !== undefined && opts.consolidateTrigger !== null;\n\tif (opts.consolidate && hasConsolidateTrigger) {\n\t\tconst consolidateTriggerNode = fromAny(opts.consolidateTrigger);\n\t\tconst consolidatePaired = withLatestFrom(\n\t\t\tconsolidateTriggerNode,\n\t\t\tstore.entries as Node<unknown>,\n\t\t);\n\t\tconst consolidationStream = switchMap(consolidatePaired, ([, entries]) =>\n\t\t\topts.consolidate!(mapFromSnapshot<TMem>(entries)),\n\t\t);\n\t\tforEach(consolidationStream, (extraction) => {\n\t\t\tapplyExtraction(store, extraction);\n\t\t});\n\t}\n\n\tconst compact = node<Array<{ key: string; value: TMem; score: number }>>(\n\t\t[store.entries, contextNode],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst snapshot = data[0];\n\t\t\tconst context = data[1];\n\t\t\tconst map = mapFromSnapshot<TMem>(snapshot);\n\t\t\tconst entries = [...map.entries()].map(([key, value]) => ({\n\t\t\t\tkey,\n\t\t\t\tvalue,\n\t\t\t\tscore: opts.score(value, context),\n\t\t\t\tcost: opts.cost(value),\n\t\t\t}));\n\t\t\tentries.sort((a, b) => b.score - a.score);\n\n\t\t\tconst packed: Array<{ key: string; value: TMem; score: number }> = [];\n\t\t\tlet remaining = budget;\n\t\t\tfor (const item of entries) {\n\t\t\t\tif (item.cost <= remaining) {\n\t\t\t\t\tpacked.push({ key: item.key, value: item.value, score: item.score });\n\t\t\t\t\tremaining -= item.cost;\n\t\t\t\t}\n\t\t\t}\n\t\t\tactions.emit(packed);\n\t\t},\n\t\t{ describeKind: \"derived\", meta: { ...factoryTag(\"distill\", { budget }) } },\n\t);\n\n\tconst size = node<number>(\n\t\t[store.entries],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst batch0 = batchData[0];\n\t\t\tconst snapshot = batch0 != null && batch0.length > 0 ? batch0.at(-1) : ctx.prevData[0];\n\t\t\tactions.emit(mapFromSnapshot<TMem>(snapshot).size);\n\t\t},\n\t\t{ describeKind: \"derived\" },\n\t);\n\tkeepalive(compact);\n\tkeepalive(size);\n\n\treturn { store, compact, size };\n}\n","/**\n * Async sources, sinks, and multicast — presentation layer.\n *\n * `fromPromise`, `fromAsyncIter`, `fromAny` are substrate primitives; they are\n * re-exported here from `@graphrefly/pure-ts` for ergonomic single-import use.\n * This file owns the presentation-only async utilities: `defer`, `forEach`,\n * `toArray`, `share`, `replay`, `cached`, `shareReplay`.\n *\n * `singleFromAny` and `singleNodeFromAny` (keyed singleflight) live in\n * `base/composition/single-from-any.ts`.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\tRESOLVED,\n\tSTART,\n} from \"@graphrefly/pure-ts/core\";\nimport { type AsyncSourceOpts, type NodeInput, sourceOpts } from \"@graphrefly/pure-ts/extra\";\n\n/** Options for presentation-layer async operators: NodeOptions without `describeKind`. */\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\n// Import fromAny from substrate — used internally by defer. The three async\n// substrate sources (fromAny, fromAsyncIter, fromPromise) are already\n// re-exported from @graphrefly/pure-ts; do NOT re-export here to avoid\n// duplicate-export conflicts at the root barrel level.\nimport { fromAny } from \"@graphrefly/pure-ts/extra\";\n\n/**\n * Lazily constructs a {@link Node} from a thunk that runs at **activation\n * time** (first subscriber after a teardown to zero sinks), not factory time.\n *\n * **Resubscribable by default.** Diverges from `fromPromise` / `fromIter` /\n * `fromAsyncIter` (which are single-shot — second subscriber sees the cached\n * terminal value). `defer`'s contract matches RxJS `defer`: every fresh\n * activation cycle re-runs the thunk. To opt out and get one-shot semantics,\n * pass `{ resubscribable: false }`.\n *\n * **Sharing across overlapping subscribers.** The thunk only re-runs on a\n * fresh activation cycle (zero → one sink). Overlapping subscribers share\n * the single activation; the thunk does NOT re-run for each subscriber. If\n * the thunk returns an existing `Node`, that Node is shared across activations\n * — `defer` will subscribe to it on each activation but does not isolate state\n * across subscribers. For per-subscriber isolation, the thunk must construct\n * a fresh source (`state(...)`, `fromPromise(fetch(...))`, etc.) on each call.\n *\n * **Use cases:**\n * - Lazy upstream construction (avoid eager evaluation of expensive factories\n * at module load — the thunk runs only when something subscribes).\n * - Per-activation resource construction (open a connection / file handle on\n * subscribe, when paired with full teardown between sessions).\n * - Bridging non-Node inputs (Promise, AsyncIterable, Iterable, scalar) into\n * the graph behind a lazy boundary.\n *\n * The thunk's return value is bridged via {@link fromAny}. Errors thrown by\n * the thunk surface as a single `[[ERROR, err]]` on the output (with `err`\n * coerced to a non-`undefined` value to satisfy spec §1.3 — bare `throw` and\n * `throw undefined` are wrapped in a `defer: thunk threw undefined` Error).\n *\n * Upstream messages are forwarded transparently (DIRTY / DATA / RESOLVED /\n * COMPLETE / ERROR / INVALIDATE / PAUSE / RESUME / TEARDOWN), preserving\n * batch boundaries. The producer's own `START` handshake is delivered to\n * subscribers automatically; the upstream's `START` is filtered.\n *\n * @param thunk - Called on each activation; returns the upstream input.\n * @param opts - Forwarded to `fromAny` (e.g. `signal` for async inputs).\n * `signal` is only consumed by `fromAny` for async input shapes (Promise,\n * AsyncIterable); it does NOT abort a Node-input or scalar-input defer.\n * @returns `Node<T>` — lazy upstream-on-activation.\n *\n * @example\n * ```ts\n * import { defer } from \"@graphrefly/graphrefly-ts\";\n *\n * // Lazy fetch — runs on the first activation, NOT at factory time.\n * // Each fresh activation cycle (after teardown) re-runs the thunk →\n * // a new fetch. Overlapping subscribers share the single activation.\n * const live = defer(() => fetch(\"/api/feed\").then((r) => r.json()));\n * ```\n *\n * @category extra\n */\nexport function defer<T>(thunk: () => NodeInput<T>, opts?: AsyncSourceOpts): Node<T> {\n\t// A4: strip `signal` before forwarding to NodeOptions — sibling sources\n\t// (fromTimer / fromPromise / fromAsyncIter) destructure first; signal\n\t// continues to flow into fromAny(input, opts) for async input shapes.\n\tconst { signal: _sig, ...nodeOpts } = (opts ?? {}) as AsyncSourceOpts;\n\tconst sOpts = sourceOpts<T>(nodeOpts);\n\tconst merged = sOpts.resubscribable === undefined ? { ...sOpts, resubscribable: true } : sOpts;\n\treturn node<T>((_data, a) => {\n\t\tlet unsub: (() => void) | undefined;\n\t\tlet stopped = false;\n\t\ttry {\n\t\t\tconst input = thunk();\n\t\t\t// `iter: true` preserves defer's RxJS-aligned per-element\n\t\t\t// streaming for sync iterable thunk returns (post DS-13.5\n\t\t\t// fromAny default flip; defer's documented contract is\n\t\t\t// \"forwards iterable values\" per-element).\n\t\t\tconst src = fromAny(input, { ...opts, iter: true });\n\t\t\tunsub = src.subscribe((msgs) => {\n\t\t\t\tif (stopped) return;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tconst t = m[0];\n\t\t\t\t\tif (t === START) continue; // producer's own START is delivered separately\n\t\t\t\t\tif (t === DATA) {\n\t\t\t\t\t\ta.emit(m[1] as T);\n\t\t\t\t\t} else if (t === COMPLETE) {\n\t\t\t\t\t\tstopped = true;\n\t\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t\t\tbreak; // A2: don't forward post-terminal messages in the same batch\n\t\t\t\t\t} else if (t === ERROR) {\n\t\t\t\t\t\tstopped = true;\n\t\t\t\t\t\ta.down([[ERROR, m[1]]]);\n\t\t\t\t\t\tbreak; // A2\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Forward DIRTY / RESOLVED / INVALIDATE / PAUSE / RESUME /\n\t\t\t\t\t\t// TEARDOWN, plus any unknown types (spec §1.3.6 forward-compat).\n\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (err) {\n\t\t\t// A5: spec §1.3 — ERROR payload must not be undefined. Wrap a\n\t\t\t// `throw` or `throw undefined` so dispatch doesn't reject the emit.\n\t\t\tconst safe = err === undefined ? new Error(\"defer: thunk threw undefined\") : err;\n\t\t\ta.down([[ERROR, safe]]);\n\t\t}\n\t\treturn () => {\n\t\t\tstopped = true;\n\t\t\tunsub?.();\n\t\t};\n\t}, merged);\n}\n\n/**\n * Subscribes immediately and runs `fn` for each upstream `DATA`; returns unsubscribe.\n *\n * @param source - Upstream node.\n * @param fn - Side effect per value.\n * @param opts - Effect node options.\n * @returns Unsubscribe function (idempotent).\n *\n * @example\n * ```ts\n * import { forEach, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const u = forEach(state(1), (v) => console.log(v));\n * u();\n * ```\n *\n * @category extra\n */\nexport function forEach<T>(source: Node<T>, fn: (value: T) => void, opts?: ExtraOpts): () => void {\n\tconst inner = node(\n\t\t[source as Node],\n\t\t(data, _actions) => {\n\t\t\tconst batch0 = data[0];\n\t\t\tif (batch0 != null && batch0.length > 0) {\n\t\t\t\tfor (const v of batch0) fn(v as T);\n\t\t\t}\n\t\t},\n\t\t{ describeKind: \"effect\", ...opts } as NodeOptions,\n\t);\n\treturn inner.subscribe(() => {});\n}\n\n/**\n * Buffers every `DATA`; on upstream `COMPLETE` emits one `DATA` with the full array then `COMPLETE`.\n *\n * @param source - Upstream node.\n * @param opts - Optional node options (derived describe kind).\n * @returns `Node<T[]>` — single array emission before completion.\n *\n * @example\n * ```ts\n * import { of, toArray } from \"@graphrefly/graphrefly-ts\";\n *\n * toArray(of(1, 2, 3));\n * ```\n *\n * @category extra\n */\nexport function toArray<T>(source: Node<T>, opts?: ExtraOpts): Node<T[]> {\n\t// Lock 6.D (Phase 13.6.B): clear the accumulator buffer on\n\t// deactivation so a resubscribable toArray restarts with an empty\n\t// array on the next cycle — pre-flip this came for free via\n\t// `_deactivate`'s store wipe.\n\tlet cleanup: { onDeactivation: () => void } | undefined;\n\treturn node<T[]>(\n\t\t[source as Node],\n\t\t(data, actions, ctx) => {\n\t\t\tif (cleanup === undefined) {\n\t\t\t\tconst store = ctx.store;\n\t\t\t\tcleanup = {\n\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\tdelete store.buf;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (!ctx.store.buf) ctx.store.buf = [];\n\t\t\tconst buf = ctx.store.buf as T[];\n\t\t\t// Accumulate DATA first — must happen before the COMPLETE check so\n\t\t\t// that a same-wave DATA+COMPLETE batch (e.g. fromTimer one-shot,\n\t\t\t// fromIter last item) is included in the emitted array.\n\t\t\tconst batch0 = data[0];\n\t\t\tif (batch0 != null && batch0.length > 0) {\n\t\t\t\tfor (const v of batch0) buf.push(v as T);\n\t\t\t}\n\t\t\t// COMPLETE: emit accumulated array then complete.\n\t\t\t// ERROR: autoError propagates; do NOT emit the partial buffer.\n\t\t\tif (ctx.terminalDeps[0] === true) {\n\t\t\t\tactions.emit([...buf]);\n\t\t\t\tactions.down([[COMPLETE]]);\n\t\t\t\treturn cleanup;\n\t\t\t}\n\t\t\t// RESOLVED wave: propagate RESOLVED. Covers first-wave case; after first\n\t\t\t// call the pre-fn skip handles this automatically.\n\t\t\tif (batch0 == null || batch0.length === 0) {\n\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t}\n\t\t\treturn cleanup;\n\t\t},\n\t\t{\n\t\t\tdescribeKind: \"derived\",\n\t\t\tcompleteWhenDepsComplete: false,\n\t\t\t...opts,\n\t\t} as NodeOptions<T[]>,\n\t);\n}\n\n/**\n * Multicasts upstream: one subscription to `source` while this wrapper has subscribers (via {@link producer}).\n *\n * @param source - Upstream node to share.\n * @param opts - Producer options; `initial` seeds from `source.cache` when set by factory.\n * @returns `Node<T>` — hot ref-counted bridge.\n *\n * @example\n * ```ts\n * import { share, state } from \"@graphrefly/graphrefly-ts\";\n *\n * share(state(0));\n * ```\n *\n * @category extra\n */\nexport function share<T>(source: Node<T>, opts?: ExtraOpts): Node<T> {\n\treturn node<T>(\n\t\t(_data, a) =>\n\t\t\tsource.subscribe((msgs) => {\n\t\t\t\ta.down(msgs);\n\t\t\t}),\n\t\t{ ...sourceOpts<T>(opts), initial: source.cache },\n\t);\n}\n\n/**\n * Like {@link share} with a bounded replay buffer: new subscribers receive the last `bufferSize`\n * `DATA` payloads (as separate batches) before live updates.\n *\n * @param source - Upstream node.\n * @param bufferSize - Maximum past values to replay (≥ 1).\n * @param opts - Producer options.\n * @returns `Node<T>` — multicast with replay on subscribe.\n *\n * @example\n * ```ts\n * import { replay, state } from \"@graphrefly/graphrefly-ts\";\n *\n * replay(state(0), 3);\n * ```\n *\n * @category extra\n */\nexport function replay<T>(source: Node<T>, bufferSize: number, opts?: ExtraOpts): Node<T> {\n\tif (bufferSize < 1) throw new RangeError(\"replay expects bufferSize >= 1\");\n\t// Spec §2.5 / Lock 6.G: the built-in `replayBuffer` NodeOption retains the\n\t// last-N outgoing DATA and `defaultOnSubscribe` delivers them to a late\n\t// subscriber INSTEAD of the cache-DATA push — so there is no double-deliver\n\t// of the most-recent value. Supersedes the old `wrapSubscribeHook` +\n\t// manual-buffer pattern (which flushed the buffer AND then push-on-\n\t// subscribed the cache, double-delivering the last value).\n\treturn node<T>(\n\t\t(_data, a) =>\n\t\t\tsource.subscribe((msgs) => {\n\t\t\t\ta.down(msgs);\n\t\t\t}),\n\t\t{ ...sourceOpts<T>(opts), initial: source.cache, replayBuffer: bufferSize },\n\t);\n}\n\n/**\n * {@link replay} with `bufferSize === 1` — replays the latest `DATA` to new subscribers.\n *\n * @param source - Upstream node.\n * @param opts - Producer options.\n * @returns `Node<T>` — share + last-value replay.\n *\n * @example\n * ```ts\n * import { cached, state } from \"@graphrefly/graphrefly-ts\";\n *\n * cached(state(0));\n * ```\n *\n * @category extra\n */\nexport function cached<T>(source: Node<T>, opts?: ExtraOpts): Node<T> {\n\treturn replay(source, 1, opts);\n}\n\n// ——————————————————————————————————————————————————————————————\n// RxJS-compatible aliases\n// ——————————————————————————————————————————————————————————————\n\n/**\n * RxJS-named alias for {@link replay} — multicast with a replay buffer of size `bufferSize`.\n *\n * @param source - Upstream node.\n * @param bufferSize - Replay depth (≥ 1).\n * @param opts - Producer options.\n * @returns Same behavior as `replay`.\n *\n * @example\n * ```ts\n * import { shareReplay, state } from \"@graphrefly/graphrefly-ts\";\n *\n * shareReplay(state(0), 5);\n * ```\n *\n * @category extra\n */\nexport const shareReplay = replay;\n","/**\n * External-register helpers — the common `register({emit, error, complete})`\n * contract shared by webhook, MCP, syslog, StatsD, OTel and other callback-\n * based integrations. Absorbs the `active` flag that every such adapter needs\n * to guard against emits after teardown (§5.10 boundary pattern).\n *\n * Two shapes:\n *\n * - {@link externalProducer} — single channel. Lazy activation: the register\n * fn runs when the node gains its first subscriber; its returned cleanup\n * runs on deactivation.\n *\n * - {@link externalBundle} — multiple named channels. Eager activation: the\n * register fn runs at bundle construction time so externally-owned servers\n * (HTTP endpoints, UDP sockets) start accepting traffic immediately. A\n * shared refcount fires the returned cleanup once every channel has fully\n * torn down.\n */\n\nimport {\n\tbatch,\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n} from \"@graphrefly/pure-ts/core\";\n\ntype ExtraOpts = Omit<NodeOptions<unknown>, \"describeKind\">;\n\nfunction sourceOpts<T>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\n/**\n * Standard emit-triad passed to a single-channel external registrar.\n *\n * Post-teardown calls on any of these are automatically no-ops — the\n * registrar does not need its own guard flag.\n *\n * @category extra\n */\nexport type EmitTriad<T> = {\n\t/** Emit a value as `DATA`. */\n\temit: (value: T) => void;\n\t/** Terminate with `ERROR`. Subsequent `emit` / `error` / `complete` are ignored. */\n\terror: (err: unknown) => void;\n\t/** Terminate with `COMPLETE`. Subsequent `emit` / `error` / `complete` are ignored. */\n\tcomplete: () => void;\n};\n\n/**\n * Multi-channel emit bundle. Each declared channel name maps to an emit fn;\n * `error` and `complete` terminate every channel atomically.\n *\n * @category extra\n */\nexport type BundleTriad<TChannels extends Record<string, unknown>> = {\n\t[K in keyof TChannels]: (value: TChannels[K]) => void;\n} & {\n\t/** Terminate every channel with `ERROR`. */\n\terror: (err: unknown) => void;\n\t/** Terminate every channel with `COMPLETE`. */\n\tcomplete: () => void;\n};\n\n/**\n * Generic external registrator contract. The caller installs handlers into a\n * third-party library / framework / server and optionally returns a cleanup\n * callback. Returning `undefined` / `void` is equivalent to a no-op cleanup.\n *\n * @category extra\n */\nexport type ExternalRegister<H> = (handlers: H) => (() => void) | undefined;\n\n/**\n * Wraps a callback-style external integration as a reactive source.\n *\n * The registrar installs the supplied `emit` / `error` / `complete` handlers\n * into the external SDK; post-teardown calls are silently dropped. Synchronous\n * exceptions thrown by the registrar surface as terminal `ERROR`.\n *\n * @param register - Installs handlers. Optionally returns a cleanup fn.\n * @param opts - Node options (name, equals, resubscribable, ...).\n *\n * @example\n * ```ts\n * import { externalProducer } from \"@graphrefly/graphrefly-ts\";\n *\n * const hook$ = externalProducer<Payload>(({ emit, error }) => {\n * const id = transport.onMessage((raw) => {\n * try { emit(parse(raw)); } catch (e) { error(e); }\n * });\n * return () => transport.off(id);\n * });\n * ```\n *\n * @category extra\n */\nexport function externalProducer<T = unknown>(\n\tregister: ExternalRegister<EmitTriad<T>>,\n\topts?: ExtraOpts,\n): Node<T> {\n\treturn node<T>((_data, a) => {\n\t\tlet active = true;\n\t\tconst triad: EmitTriad<T> = {\n\t\t\temit(value) {\n\t\t\t\tif (!active) return;\n\t\t\t\ta.emit(value);\n\t\t\t},\n\t\t\terror(err) {\n\t\t\t\tif (!active) return;\n\t\t\t\tactive = false;\n\t\t\t\ta.down([[ERROR, err]]);\n\t\t\t},\n\t\t\tcomplete() {\n\t\t\t\tif (!active) return;\n\t\t\t\tactive = false;\n\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t},\n\t\t};\n\t\tlet cleanup: (() => void) | undefined;\n\t\ttry {\n\t\t\tconst ret = register(triad);\n\t\t\tcleanup = typeof ret === \"function\" ? ret : undefined;\n\t\t} catch (err) {\n\t\t\ttriad.error(err);\n\t\t\treturn () => {\n\t\t\t\tactive = false;\n\t\t\t};\n\t\t}\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\ttry {\n\t\t\t\tcleanup?.();\n\t\t\t} catch {\n\t\t\t\t/* registrar cleanup failure is not a reactive signal */\n\t\t\t}\n\t\t};\n\t}, sourceOpts(opts));\n}\n\n/**\n * Options for {@link externalBundle}.\n *\n * @category extra\n */\nexport type ExternalBundleOptions<TChannels extends Record<string, unknown>> = {\n\t/** Base name prefix for channel nodes; each node is named `${name}::${channel}`. */\n\tname?: string;\n\t/** Per-channel node options (equals, resubscribable, ...). */\n\tchannelOpts?: { [K in keyof TChannels]?: ExtraOpts };\n};\n\n/**\n * Multi-channel variant — one `Node<T>` per named channel, sharing a single\n * registrar. Activation is eager: the registrar runs at construction time so\n * externally-owned servers (HTTP, UDP, queue consumers) can start accepting\n * traffic immediately. The returned cleanup fires once every channel has been\n * subscribed and then fully deactivated (refcount-on-teardown).\n *\n * Any call to `error` or `complete` propagates to every channel atomically.\n *\n * @param register - Installs handlers for each channel plus shared error/complete.\n * @param channels - Ordered channel names; determines the returned object shape.\n * @param opts - Optional name prefix and per-channel node options.\n *\n * @example\n * ```ts\n * import { externalBundle } from \"@graphrefly/graphrefly-ts\";\n *\n * type OTelChannels = { traces: Span; metrics: Metric; logs: LogRec };\n * const otel = externalBundle<OTelChannels>(\n * ({ traces, metrics, logs, error }) => {\n * app.post(\"/v1/traces\", (req, res) => { traces(req.body); res.sendStatus(200); });\n * app.post(\"/v1/metrics\", (req, res) => { metrics(req.body); res.sendStatus(200); });\n * app.post(\"/v1/logs\", (req, res) => { logs(req.body); res.sendStatus(200); });\n * server.on(\"error\", error);\n * return () => server.close();\n * },\n * [\"traces\", \"metrics\", \"logs\"],\n * );\n * otel.traces.subscribe(...);\n * ```\n *\n * @category extra\n */\nexport function externalBundle<TChannels extends Record<string, unknown>>(\n\tregister: ExternalRegister<BundleTriad<TChannels>>,\n\tchannels: readonly (keyof TChannels & string)[],\n\topts?: ExternalBundleOptions<TChannels>,\n): { [K in keyof TChannels]: Node<TChannels[K]> } & { dispose(): void } {\n\tlet active = true;\n\tlet cleanup: (() => void) | undefined;\n\tlet activatedCount = 0;\n\tlet teardownCount = 0;\n\n\tconst nodes = {} as { [K in keyof TChannels]: Node<TChannels[K]> };\n\tconst channelNodes: Array<Node<unknown>> = [];\n\n\tconst finishCleanup = () => {\n\t\tconst fn = cleanup;\n\t\tcleanup = undefined;\n\t\ttry {\n\t\t\tfn?.();\n\t\t} catch {\n\t\t\t/* registrar cleanup failure is not a reactive signal */\n\t\t}\n\t};\n\n\tfor (const ch of channels) {\n\t\tconst name = opts?.name ? `${opts.name}::${ch}` : ch;\n\t\tconst chOpts = opts?.channelOpts?.[ch];\n\t\tconst n = node<TChannels[typeof ch]>(\n\t\t\t(_data, _a) => {\n\t\t\t\tactivatedCount++;\n\t\t\t\treturn () => {\n\t\t\t\t\tteardownCount++;\n\t\t\t\t\t// Cleanup fires once every channel has activated at least once\n\t\t\t\t\t// and then deactivated. Channels that never subscribe do not\n\t\t\t\t\t// gate cleanup — use the explicit `.dispose()` method for\n\t\t\t\t\t// unconditional teardown.\n\t\t\t\t\tif (\n\t\t\t\t\t\tactivatedCount > 0 &&\n\t\t\t\t\t\tteardownCount >= activatedCount &&\n\t\t\t\t\t\tteardownCount >= channels.length\n\t\t\t\t\t) {\n\t\t\t\t\t\tfinishCleanup();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t},\n\t\t\tsourceOpts({ ...chOpts, name }),\n\t\t);\n\t\tnodes[ch as keyof TChannels] = n as Node<TChannels[typeof ch]>;\n\t\tchannelNodes.push(n as Node<unknown>);\n\t}\n\n\tconst bundle = {} as BundleTriad<TChannels>;\n\tfor (const ch of channels) {\n\t\t(bundle as Record<string, unknown>)[ch] = (value: unknown) => {\n\t\t\tif (!active) return;\n\t\t\t(nodes[ch as keyof TChannels] as Node<unknown>).down([[DATA, value]]);\n\t\t};\n\t}\n\tbundle.error = (err: unknown) => {\n\t\tif (!active) return;\n\t\tactive = false;\n\t\tbatch(() => {\n\t\t\tfor (const n of channelNodes) n.down([[ERROR, err]]);\n\t\t});\n\t\tfinishCleanup();\n\t};\n\tbundle.complete = () => {\n\t\tif (!active) return;\n\t\tactive = false;\n\t\tbatch(() => {\n\t\t\tfor (const n of channelNodes) n.down([[COMPLETE]]);\n\t\t});\n\t\tfinishCleanup();\n\t};\n\n\t// Eager activation — register fires at construction time so externally-\n\t// owned servers can start accepting traffic immediately. Synchronous throws\n\t// propagate to the caller (no subscribers exist yet, so there is no\n\t// reactive ERROR path to deliver to). This matches the existing `fromOTel`\n\t// contract.\n\tconst ret = register(bundle);\n\tcleanup = typeof ret === \"function\" ? ret : undefined;\n\n\tconst dispose = () => {\n\t\tif (!active) return;\n\t\tactive = false;\n\t\t// Fire COMPLETE on every channel so downstream sees a clean terminal.\n\t\tbatch(() => {\n\t\t\tfor (const n of channelNodes) {\n\t\t\t\ttry {\n\t\t\t\t\tn.down([[COMPLETE]]);\n\t\t\t\t} catch {\n\t\t\t\t\t/* terminal filter / re-entrance — swallow */\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tfinishCleanup();\n\t};\n\n\treturn Object.assign(nodes, { dispose });\n}\n","/**\n * Phase 13.C — `selector` + `materialize` composers (DS-13.C / G2 lock C).\n *\n * Two paired primitives for dynamic-mount routing:\n *\n * - {@link selector} — projects an input value to a routing key, deduped.\n * Equivalent to `map + distinctUntilChanged`, but the dedup is the\n * semantic point: the key changes ONLY when the routed-to slot should\n * change. Fires `materialize` re-mounts efficiently.\n *\n * - {@link materialize} — given a reactive `key` and a reactive map of\n * `key → factory` thunks, mounts the matching factory's Graph under\n * `parent` at a stable slot name. When `key` changes, unmounts the old\n * slot and mounts the new factory's output. When `factories` mutates but\n * `key` stays the same, the current slot continues to run on the OLD\n * factory (\"current sessions complete on old factory; new sessions use\n * new factory\" — full hot-swap correctness deferred to G10, parked).\n *\n * Reusable beyond the agent layer:\n * - `harnessLoop` strategy routing — the strategy node IS a `selector`.\n * - `pipelineGraph` dynamic stage selection.\n * - `refineLoop` strategy swap.\n * - Phase 13.I `spawnable()` mounts agent slots via `materialize`.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\tfactoryTag,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\tRESOLVED,\n} from \"@graphrefly/pure-ts/core\";\nimport type { Graph } from \"@graphrefly/pure-ts/graph\";\n\n/** Options for operator nodes: NodeOptions without `describeKind` (set internally). */\nexport type ExtraOpts = Omit<NodeOptions<unknown>, \"describeKind\">;\n\nfunction operatorOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"derived\", ...opts } as NodeOptions<T>;\n}\n\n// ---------------------------------------------------------------------------\n// selector\n// ---------------------------------------------------------------------------\n\n/** Options for {@link selector}. */\nexport type SelectorOpts<TKey> = ExtraOpts & {\n\t/**\n\t * Equality comparator for the projected key. Defaults to {@link Object.is}.\n\t * Used to suppress re-emits when the input changes but the projected key\n\t * does not — this is the load-bearing behavior that lets downstream\n\t * `materialize` skip unnecessary unmount/remount cycles.\n\t */\n\tequals?: (a: TKey, b: TKey) => boolean;\n};\n\n/**\n * Projects each upstream value to a routing key, deduped on the key. The\n * output node emits a key only when the projected key actually changes —\n * pairs cleanly with {@link materialize}, which re-mounts only on key\n * change.\n *\n * **Differs from `map`:** `map(input, fn)` fires on every upstream wave\n * regardless of output value. `selector(input, fn)` fires only when the\n * projected key CHANGES (under `equals`), so downstream re-mount logic is\n * stable.\n *\n * @param input - Upstream node carrying the value to project.\n * @param fn - Synchronous projection function.\n * @param opts - Optional {@link SelectorOpts}.\n * @returns `Node<TKey>` carrying the latest projected key (deduped).\n *\n * @example\n * ```ts\n * import { selector, materialize, state } from \"@graphrefly/graphrefly-ts\";\n *\n * type Request = { kind: \"research\" | \"summarize\" | \"code\"; payload: unknown };\n * const requestNode = state<Request>({ kind: \"research\", payload: {} });\n *\n * const presetId = selector(requestNode, (req) => req.kind);\n * // presetId is `Node<\"research\" | \"summarize\" | \"code\">`, deduped.\n * // Downstream materialize re-mounts ONLY when the kind axis changes.\n * ```\n *\n * @category extra\n */\nexport function selector<TIn, TKey>(\n\tinput: Node<TIn>,\n\tfn: (input: TIn) => TKey,\n\topts?: SelectorOpts<TKey>,\n): Node<TKey> {\n\tconst equals = opts?.equals ?? Object.is;\n\t// Lock 6.D (Phase 13.6.B): clear prev/hasPrev on deactivation so a\n\t// resubscribable selector doesn't dedupe the next cycle's first\n\t// projected key against a stale prev from the prior cycle.\n\tlet cleanup: { onDeactivation: () => void } | undefined;\n\treturn node<TKey>(\n\t\t[input as Node],\n\t\t(data, a, ctx) => {\n\t\t\tif (cleanup === undefined) {\n\t\t\t\tconst store = ctx.store;\n\t\t\t\tcleanup = {\n\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\tdelete store.prev;\n\t\t\t\t\t\tdelete store.hasPrev;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst batch0 = data[0];\n\t\t\tif (batch0 == null || batch0.length === 0) {\n\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\treturn cleanup;\n\t\t\t}\n\t\t\t// A11 (QA fix 2026-05-01): pre-pass — compute every projected\n\t\t\t// key + dedup decision FIRST, surface any user `equals` throw\n\t\t\t// as ERROR before any DATA goes out. The previous in-loop\n\t\t\t// emission interleaved partial DATA with ERROR mid-batch; that\n\t\t\t// left subscribers inconsistent (some had read the early DATA,\n\t\t\t// some treated ERROR as \"discard everything since RESOLVED\")\n\t\t\t// and left `ctx.store.prev` mutated to the last successful key,\n\t\t\t// which made selector \"stuck\" until the next throw-free batch.\n\t\t\tconst toEmit: TKey[] = [];\n\t\t\tlet prev: TKey | undefined = ctx.store.hasPrev ? (ctx.store.prev as TKey) : undefined;\n\t\t\tlet hasPrev = ctx.store.hasPrev;\n\t\t\tfor (const v of batch0 as TIn[]) {\n\t\t\t\tconst key = fn(v);\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\tlet same: boolean;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsame = equals(prev as TKey, key);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t// Pre-pass throw — abandon the whole batch (no DATA emits)\n\t\t\t\t\t\t// and surface ERROR. ctx.store stays at its pre-batch\n\t\t\t\t\t\t// state so the next batch starts from a known-good prev.\n\t\t\t\t\t\ta.down([[ERROR, err]]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (same) continue;\n\t\t\t\t}\n\t\t\t\tprev = key;\n\t\t\t\thasPrev = true;\n\t\t\t\ttoEmit.push(key);\n\t\t\t}\n\t\t\tif (toEmit.length === 0) {\n\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\treturn cleanup;\n\t\t\t}\n\t\t\tctx.store.prev = prev as TKey;\n\t\t\tctx.store.hasPrev = true;\n\t\t\tfor (const k of toEmit) a.emit(k);\n\t\t\treturn cleanup;\n\t\t},\n\t\t{\n\t\t\t...operatorOpts(opts),\n\t\t\tmeta: { ...factoryTag(\"selector\"), ...(opts?.meta ?? {}) },\n\t\t},\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// materialize\n// ---------------------------------------------------------------------------\n\n/**\n * Factory thunk for a {@link materialize} slot. Called once per mount cycle\n * to mint a fresh `TGraph` instance. The instance is mounted under\n * `parent.mount(slotName, ...)` and unmounted via `parent.remove(slotName)`\n * when `key` next changes.\n *\n * Each invocation MUST return a fresh, never-before-mounted Graph instance —\n * `Graph.mount` rejects re-mounting an instance that is already mounted\n * elsewhere in the tree. Caching factory output is unsafe.\n */\nexport type GraphFactory<TGraph extends Graph> = () => TGraph;\n\n/** Options for {@link materialize}. */\nexport type MaterializeOpts = ExtraOpts & {\n\t/**\n\t * Local mount name on the parent graph. Default `\"materialized\"`.\n\t *\n\t * Two materialize calls on the SAME parent must use distinct `slotName`\n\t * values, otherwise the second mount throws \"mount already exists\".\n\t * For a hub mounting many slots (e.g. {@link spawnable}'s preset\n\t * registry), use `slotName: \\`preset-\\${id}\\`` or similar.\n\t */\n\tslotName?: string;\n};\n\n/**\n * Reactive dynamic mount: mounts the Graph instance for `key` under\n * `parent.mount(slotName, ...)`, and re-mounts when `key` changes.\n *\n * **Lifecycle.** First DATA on `key` triggers a mount: look up\n * `factories.get(key)`, call the factory thunk, mount the result under\n * `parent`. Each subsequent `key` change unmounts the previous slot and\n * mounts the new one. When this materialize node terminates (subscriber\n * teardown, `COMPLETE` from `key`, `Graph.destroy`), the active slot is\n * unmounted via `parent.remove(slotName)`.\n *\n * **Hot-swap policy (G10 deferred).** When `factories` mutates but `key`\n * stays the same, the currently-mounted slot is NOT re-instantiated —\n * \"current sessions complete on old factory; new sessions use new\n * factory.\" Atomic disconnect/reconnect of an in-flight slot to a new\n * factory is parked under G10 (see `optimizations.md` \"G10 atomic\n * registry hot-swap\").\n *\n * **Output.** The returned `Node<TGraph>` emits the currently-mounted\n * Graph reference whenever a mount occurs. Consumers can subscribe to\n * watch slot changes, or read `.cache` for the active mount. SENTINEL\n * (no DATA) when no slot is currently mounted (e.g. `key` has no matching\n * factory).\n *\n * **Spec compliance.**\n * - No polling: mount transitions are reactive on `key` / `factories`.\n * - No raw async: factory invocation is synchronous; if a factory needs\n * async setup, it returns a Graph that handles its own setup internally.\n * - Mount/unmount happens as side-effects inside the reactive `subscribe`\n * handler — sanctioned per spec §5.9 (Graph topology mutations are\n * imperative writes at the system boundary, not in-flight reactive\n * triggers).\n *\n * @param key - Reactive routing key. Re-mounts on each key change (deduped\n * by reference; pair with {@link selector} for projection-based dedup).\n * @param factories - Reactive map of `key → factory thunk`. Factory map\n * mutations don't disturb the active slot until the next key change.\n * @param parent - Graph to mount slots under. The `slotName` (default\n * `\"materialized\"`) must be free on `parent` at construction time.\n * @param opts - Optional {@link MaterializeOpts}.\n * @returns `Node<TGraph>` carrying the active mount.\n *\n * @example\n * ```ts\n * import { materialize, selector, state, Graph } from \"@graphrefly/graphrefly-ts\";\n *\n * const parent = new Graph(\"parent\");\n * const factories = state<ReadonlyMap<string, () => Graph>>(new Map([\n * [\"researcher\", () => new ResearchAgentGraph()],\n * [\"coder\", () => new CoderAgentGraph()],\n * ]));\n * const key = selector(requestNode, (r) => r.kind);\n * const slot = materialize(key, factories, parent, { slotName: \"agent\" });\n * // `slot.cache` is the active agent graph; `parent.node(\"agent::out\")`\n * // resolves into whichever agent is currently mounted.\n * ```\n *\n * @category extra\n */\nexport function materialize<TKey, TGraph extends Graph>(\n\tkey: Node<TKey>,\n\tfactories: Node<ReadonlyMap<TKey, GraphFactory<TGraph>>>,\n\tparent: Graph,\n\topts?: MaterializeOpts,\n): Node<TGraph> {\n\tconst slotName = opts?.slotName ?? \"materialized\";\n\treturn node<TGraph>(\n\t\t(_data, a) => {\n\t\t\tlet currentKey: TKey | undefined;\n\t\t\tlet hasCurrentKey = false;\n\t\t\tlet currentGraph: TGraph | undefined;\n\t\t\tlet latestFactories: ReadonlyMap<TKey, GraphFactory<TGraph>> | undefined;\n\t\t\tlet terminated = false;\n\n\t\t\tfunction unmountCurrent(): void {\n\t\t\t\tif (currentGraph === undefined) return;\n\t\t\t\ttry {\n\t\t\t\t\tparent.remove(slotName);\n\t\t\t\t} catch {\n\t\t\t\t\t// Slot already gone (parent destroyed, or external `remove`).\n\t\t\t\t}\n\t\t\t\tcurrentGraph = undefined;\n\t\t\t}\n\n\t\t\t// Closure mirror for the factories map. Subscribed FIRST so\n\t\t\t// `latestFactories` is populated by the time the first `key` DATA\n\t\t\t// arrives. Same §28 factory-time-seed pattern used elsewhere.\n\t\t\tconst facUnsub = factories.subscribe((msgs) => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\tlatestFactories = m[1] as ReadonlyMap<TKey, GraphFactory<TGraph>>;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Primary trigger: key DATA drives mount transitions.\n\t\t\tconst keyUnsub = key.subscribe((msgs) => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\tconst newKey = m[1] as TKey;\n\t\t\t\t\t\tconst keyChanged = !hasCurrentKey || newKey !== currentKey;\n\t\t\t\t\t\tif (keyChanged) {\n\t\t\t\t\t\t\tunmountCurrent();\n\t\t\t\t\t\t\tif (latestFactories !== undefined) {\n\t\t\t\t\t\t\t\tconst factory = latestFactories.get(newKey);\n\t\t\t\t\t\t\t\tif (factory !== undefined) {\n\t\t\t\t\t\t\t\t\tcurrentGraph = factory();\n\t\t\t\t\t\t\t\t\tparent.mount(slotName, currentGraph);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcurrentKey = newKey;\n\t\t\t\t\t\t\thasCurrentKey = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (currentGraph !== undefined) {\n\t\t\t\t\t\t\ta.emit(currentGraph);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (m[0] === COMPLETE) {\n\t\t\t\t\t\tterminated = true;\n\t\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t\t} else if (m[0] === ERROR) {\n\t\t\t\t\t\tterminated = true;\n\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tterminated = true;\n\t\t\t\tkeyUnsub();\n\t\t\t\tfacUnsub();\n\t\t\t\tunmountCurrent();\n\t\t\t};\n\t\t},\n\t\t{\n\t\t\t...operatorOpts(opts),\n\t\t\tmeta: { ...factoryTag(\"materialize\"), slotName, ...(opts?.meta ?? {}) },\n\t\t},\n\t);\n}\n","// ---------------------------------------------------------------------------\n// RxJS bridge — reactive interop between GraphReFly nodes and RxJS Observables.\n// ---------------------------------------------------------------------------\n// Usage:\n// import { toObservable } from '@graphrefly/graphrefly-ts/extra';\n// const values$ = toObservable(myNode); // Observable<T>\n// const msgs$ = toObservable(myNode, { raw: true }); // Observable<Messages>\n// ---------------------------------------------------------------------------\n\nimport type { Node } from \"@graphrefly/pure-ts/core\";\nimport { COMPLETE, DATA, ERROR, type Messages } from \"@graphrefly/pure-ts/core\";\nimport { Observable } from \"rxjs\";\n\n/** Options for {@link toObservable}. */\nexport type ToObservableOptions = {\n\t/**\n\t * When `true`, emit raw `Messages` batches instead of extracted `DATA` values.\n\t * Terminal batches are still emitted as the final `next()` before the\n\t * Observable signal (error/complete).\n\t */\n\traw?: boolean;\n};\n\n/**\n * Bridge a `Node<T>` to an RxJS `Observable`.\n *\n * Default mode emits the node's value on each `DATA` message. Maps `ERROR` to\n * `subscriber.error()` and `COMPLETE` to `subscriber.complete()`.\n * Protocol-internal signals (DIRTY, RESOLVED, PAUSE, etc.) are skipped.\n *\n * With `{ raw: true }`, emits full `[[Type, Data?], ...]` message batches.\n * The Observable terminates on ERROR or COMPLETE (the terminal batch is still\n * emitted as the final `next()` before the Observable signal).\n *\n * For graph-level observation, use `toObservable(graph.resolve(path))` or\n * subscribe to `graph.observe()` directly.\n *\n * Unsubscribing the Observable unsubscribes the node.\n */\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions?: ToObservableOptions & { raw?: false },\n): Observable<T>;\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions: ToObservableOptions & { raw: true },\n): Observable<Messages>;\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions?: ToObservableOptions,\n): Observable<T | Messages> {\n\tif (options?.raw) {\n\t\treturn new Observable<Messages>((subscriber) => {\n\t\t\tconst unsub = node.subscribe((msgs) => {\n\t\t\t\tif (subscriber.closed) return;\n\t\t\t\tsubscriber.next(msgs);\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\t\tsubscriber.error(m[1]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\t\tsubscriber.complete();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn unsub;\n\t\t});\n\t}\n\n\treturn new Observable<T>((subscriber) => {\n\t\tconst unsub = node.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (subscriber.closed) return;\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tsubscriber.next(m[1] as T);\n\t\t\t\t} else if (m[0] === ERROR) {\n\t\t\t\t\tsubscriber.error(m[1]);\n\t\t\t\t\treturn;\n\t\t\t\t} else if (m[0] === COMPLETE) {\n\t\t\t\t\tsubscriber.complete();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn unsub;\n\t});\n}\n","/**\n * Lazy per-topic state hub (roadmap §3.2) — lightweight last-value broadcasts.\n *\n * Each topic is a sentinel `node<unknown>()` with push-on-subscribe replay of\n * the most recent published value (no push until the first `publish`). For\n * Pulsar-inspired retained message logs,\n * cursor-based subscriptions, and job-queue semantics, use `messagingHub()` in\n * `utils/messaging` — built on `TopicGraph` / `SubscriptionGraph` with\n * retention policies, absolute cursor tracking, and per-subscriber state.\n *\n * Presentation layer (base/composition). Moved from pure-ts during cleave A3\n * (no substrate core/graph dependency on pubsub found).\n */\n\nimport { batch, type Node, node, TEARDOWN, wallClockNs } from \"@graphrefly/pure-ts/core\";\nimport {\n\ttype PubSubChange,\n\ttype PubSubChangePayload,\n\ttype ReactiveLogBundle,\n\treactiveLog,\n} from \"@graphrefly/pure-ts/extra\";\n\n// ── Backend interface ─────────────────────────────────────────────────────\n\n/**\n * Storage contract for {@link pubsub} — registry only.\n *\n * Tracks the set of topic names plus a monotonic `version` counter that\n * advances on topic create/remove. Does NOT own per-topic message storage —\n * per-topic cached last values live in the topic nodes themselves (sentinel\n * until the first publish).\n *\n * For distributed / persistent per-topic storage, use `messagingHub()` in\n * `utils/messaging`, which composes `TopicGraph` under a lazy registry.\n *\n * @category base\n */\nexport interface PubSubBackend {\n\t/** Monotonic counter; advances on topic create/remove. */\n\treadonly version: number;\n\treadonly topicCount: number;\n\thasTopic(name: string): boolean;\n\ttopicNames(): IterableIterator<string>;\n\t/** Records topic creation. Returns `true` if newly added (advances `version`). */\n\tcreateTopic(name: string): boolean;\n\t/** Records topic removal. Returns `true` if it existed (advances `version`). */\n\tremoveTopic(name: string): boolean;\n}\n\n/**\n * Default in-memory registry backend.\n *\n * @category base\n */\nexport class NativePubSubBackend implements PubSubBackend {\n\tprivate _version = 0;\n\tprivate readonly _topics = new Set<string>();\n\n\tget version(): number {\n\t\treturn this._version;\n\t}\n\n\tget topicCount(): number {\n\t\treturn this._topics.size;\n\t}\n\n\thasTopic(name: string): boolean {\n\t\treturn this._topics.has(name);\n\t}\n\n\ttopicNames(): IterableIterator<string> {\n\t\treturn this._topics.values();\n\t}\n\n\tcreateTopic(name: string): boolean {\n\t\tif (this._topics.has(name)) return false;\n\t\tthis._topics.add(name);\n\t\tthis._version += 1;\n\t\treturn true;\n\t}\n\n\tremoveTopic(name: string): boolean {\n\t\tconst had = this._topics.delete(name);\n\t\tif (had) this._version += 1;\n\t\treturn had;\n\t}\n}\n\n// ── Hub ───────────────────────────────────────────────────────────────────\n\nexport type PubSubHubOptions = {\n\t/**\n\t * Storage backend. Defaults to `NativePubSubBackend`. Pluggable for audit /\n\t * monitoring / mirror-to-external-broker use cases.\n\t */\n\tbackend?: PubSubBackend;\n\t/**\n\t * DS-14 / DS14R2 — opt-in delta companion. When set, the hub appends a\n\t * typed {@link PubSubChange} record in the **same batch frame** as the\n\t * topic emission / teardown (same-wave consistency — subscribers reading\n\t * both a topic and `mutationLog` never see torn state).\n\t *\n\t * Records the locked `PubSubChange` verbs that apply to this last-value\n\t * hub: `publish` (per `publish` / `publishMany`) and `remove` (per\n\t * `removeTopic`). The `ack` verb is a cursor concern of `messagingHub()`\n\t * and does not apply here. **Note:** the locked `publish` payload carries\n\t * `value` only (no topic name) — callers needing per-topic delta\n\t * correlation should embed identity in the value or use `messagingHub()`.\n\t *\n\t * `true` = defaults; object form forwards `maxSize` / `name` to the inner\n\t * `reactiveLog`.\n\t */\n\tmutationLog?: true | { maxSize?: number; name?: string };\n};\n\n/**\n * Lazy per-topic state hub. Topics are single-value sentinel nodes\n * with push-on-subscribe replay of the most recent publish.\n *\n * @category base\n */\nexport interface PubSubHub {\n\t/**\n\t * Returns the topic node, creating it on first use.\n\t *\n\t * @param name - Topic key.\n\t * @returns `Node` whose value is the last published payload. Starts in\n\t * sentinel state — no push-on-subscribe until the first publish.\n\t */\n\ttopic(name: string): Node<unknown>;\n\t/** Publishes a value to the topic (lazily creating the topic if missing). */\n\tpublish(name: string, value: unknown): void;\n\t/**\n\t * Bulk publish — single outer batch for all entries. No-op if empty.\n\t *\n\t * **Iterable consumption (F6):** `entries` is consumed once (single-pass).\n\t * Pass an array or `Set` for multi-shot callers. Iteration happens INSIDE\n\t * the batch frame — if the iterator throws mid-way, the batch is discarded\n\t * and NO publishes are visible to subscribers (all-or-nothing within one\n\t * call).\n\t */\n\tpublishMany(entries: Iterable<[string, unknown]>): void;\n\t/** Removes a topic; sends `TEARDOWN` to its node. Returns `true` if it existed. */\n\tremoveTopic(name: string): boolean;\n\t/** Checks topic existence without creating. O(1). */\n\thas(name: string): boolean;\n\t/** Number of topics currently registered. O(1). */\n\treadonly size: number;\n\t/** Iterator over topic names. */\n\ttopicNames(): IterableIterator<string>;\n\t/**\n\t * DS14R2 — present iff `mutationLog` was configured. Append-only log of\n\t * `publish` / `remove` deltas, same-wave-consistent with topic emissions.\n\t */\n\treadonly mutationLog?: ReactiveLogBundle<PubSubChange>;\n}\n\n/**\n * Creates a lazy per-topic state hub.\n *\n * @param options - Optional pluggable `backend` (defaults to `NativePubSubBackend`).\n * @returns Hub with lazy `topic()` / `publish()` / `publishMany()` / `removeTopic()` /\n * `has()` / `size` / `topicNames()`.\n *\n * @remarks\n * **Scope:** Each topic is a sentinel node — retains only the last published\n * value (no push-on-subscribe before the first publish). For Pulsar-inspired\n * retention + cursor reading, use `messagingHub()` in `utils/messaging`.\n *\n * **`removeTopic`:** Sends `TEARDOWN` to the topic node; all subscribers receive\n * the TEARDOWN message. Subsequent `publish(name, value)` silently recreates the\n * topic with a fresh node — existing subscribers to the old node do NOT reconnect.\n *\n * @example\n * ```ts\n * import { pubsub } from \"@graphrefly/graphrefly\";\n *\n * const hub = pubsub();\n * const t = hub.topic(\"events\");\n * t.subscribe((msgs) => console.log(msgs));\n * hub.publish(\"events\", { ok: true });\n * hub.publishMany([[\"events\", 1], [\"status\", \"ready\"]]);\n * ```\n *\n * @category base\n */\nexport function pubsub(options: PubSubHubOptions = {}): PubSubHub {\n\tconst { backend: userBackend, mutationLog: mutLogOpt } = options;\n\tconst backend: PubSubBackend = userBackend ?? new NativePubSubBackend();\n\tconst nodes = new Map<string, Node<unknown>>();\n\n\t// ── DS14R2 — mutation log companion ──────────────────────────────────────\n\tconst mutLog: ReactiveLogBundle<PubSubChange> | undefined = mutLogOpt\n\t\t? reactiveLog<PubSubChange>(undefined, {\n\t\t\t\tname: mutLogOpt === true ? \"pubsub.mutationLog\" : (mutLogOpt.name ?? \"pubsub.mutationLog\"),\n\t\t\t\tmaxSize: mutLogOpt === true ? undefined : mutLogOpt.maxSize,\n\t\t\t})\n\t\t: undefined;\n\tlet mutVersion = 0;\n\tfunction recordChange(change: PubSubChangePayload): void {\n\t\tif (!mutLog) return;\n\t\tmutLog.append({\n\t\t\tstructure: \"pubsub\",\n\t\t\tversion: ++mutVersion,\n\t\t\tt_ns: wallClockNs(),\n\t\t\tlifecycle: \"data\",\n\t\t\tchange,\n\t\t});\n\t}\n\n\tfunction ensureTopic(name: string): Node<unknown> {\n\t\tlet n = nodes.get(name);\n\t\tif (n === undefined) {\n\t\t\tn = node<unknown>({ describeKind: \"state\" });\n\t\t\tnodes.set(name, n);\n\t\t\tbackend.createTopic(name);\n\t\t}\n\t\treturn n;\n\t}\n\n\treturn {\n\t\ttopic(name: string): Node<unknown> {\n\t\t\treturn ensureTopic(name);\n\t\t},\n\n\t\tpublish(name: string, value: unknown): void {\n\t\t\tif (!mutLog) {\n\t\t\t\tensureTopic(name).emit(value);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Same-wave: topic emit + change record in one batch frame.\n\t\t\tbatch(() => {\n\t\t\t\tensureTopic(name).emit(value);\n\t\t\t\trecordChange({ kind: \"publish\", value });\n\t\t\t});\n\t\t},\n\n\t\tpublishMany(entries: Iterable<[string, unknown]>): void {\n\t\t\tbatch(() => {\n\t\t\t\tfor (const [name, value] of entries) {\n\t\t\t\t\tensureTopic(name).emit(value);\n\t\t\t\t\trecordChange({ kind: \"publish\", value });\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tremoveTopic(name: string): boolean {\n\t\t\tconst n = nodes.get(name);\n\t\t\tif (n === undefined) return false;\n\t\t\tnodes.delete(name);\n\t\t\tbackend.removeTopic(name);\n\t\t\tif (!mutLog) {\n\t\t\t\tn.down([[TEARDOWN]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbatch(() => {\n\t\t\t\t// QA P3: record BEFORE TEARDOWN. A subscriber wired to both a\n\t\t\t\t// topic and `mutationLog` that self-detaches on TEARDOWN would\n\t\t\t\t// otherwise miss the `remove` delta (same-wave consistency).\n\t\t\t\trecordChange({ kind: \"remove\", name });\n\t\t\t\tn.down([[TEARDOWN]]);\n\t\t\t});\n\t\t\treturn true;\n\t\t},\n\n\t\thas(name: string): boolean {\n\t\t\treturn backend.hasTopic(name);\n\t\t},\n\n\t\tget size(): number {\n\t\t\treturn backend.topicCount;\n\t\t},\n\n\t\ttopicNames(): IterableIterator<string> {\n\t\t\treturn backend.topicNames();\n\t\t},\n\n\t\tmutationLog: mutLog,\n\t};\n}\n","/**\n * `singleFromAny` — keyed promise/Node de-duplication (\"singleflight\").\n *\n * Given a `factory: (key) => NodeInput<T>`, returns a callable that dedupes\n * concurrent invocations sharing the same key — all callers with the same\n * key while a request is in-flight receive the same `Promise<T>`. Once the\n * underlying source settles (DATA, ERROR, or COMPLETE), the cache entry is\n * cleared so the next call re-invokes the factory.\n *\n * This is the classic \"singleflight\" pattern from Go, generalised over the\n * library's `NodeInput<T>` bridge so callers can pass Promise-returning\n * factories, Node-returning factories, or plain value factories with\n * identical semantics.\n *\n * Use cases:\n * - `withReplayCache` cache-miss thundering-herd dedup\n * - Shared HTTP fetches keyed by URL\n * - Expensive compute keyed by request fingerprint\n *\n * @example\n * ```ts\n * const fetchUser = singleFromAny<string, User>((id) => fetch(`/users/${id}`).then(r => r.json()));\n * // Two concurrent callers with id=\"42\" → one underlying fetch, two Promises resolving to the same User.\n * const [a, b] = await Promise.all([fetchUser(\"42\"), fetchUser(\"42\")]);\n * ```\n *\n * @category extra\n */\n\nimport type { Node } from \"@graphrefly/pure-ts/core\";\nimport { COMPLETE, ERROR } from \"@graphrefly/pure-ts/core\";\n// Import directly from the source sub-files (rather than the `./sources.js`\n// barrel) so the `single-from-any` module is NOT part of any cycle that runs\n// through `extra/sources/index.ts` — eager re-exports through the barrel were\n// observed to leave `firstValueFrom` / `keepalive` unresolved during nested\n// import chains under vite-node.\nimport { fromAny, type NodeInput } from \"@graphrefly/pure-ts/extra\";\nimport { firstValueFrom } from \"../sources/settled.js\";\n\nexport interface SingleFromAnyOptions<K> {\n\t/**\n\t * Convert a typed key into a cache-string. Defaults to `String(key)`, which\n\t * works for primitive keys; callers with object keys should provide a\n\t * stable serializer (e.g., canonical JSON).\n\t */\n\tkeyFn?: (key: K) => string;\n}\n\n/**\n * Dedupe concurrent `factory(key)` invocations. Returns a bound callable.\n *\n * @param factory - Produces a `NodeInput<T>` for each unique key.\n * @param opts - Optional key-stringification.\n * @returns A function `(key: K) => Promise<T>` whose inflight results are shared per key.\n */\nexport function singleFromAny<K, T>(\n\tfactory: (key: K) => NodeInput<T>,\n\topts: SingleFromAnyOptions<K> = {},\n): (key: K) => Promise<T> {\n\tconst keyFn = opts.keyFn ?? ((k: K) => String(k));\n\tconst inFlight = new Map<string, Promise<T>>();\n\n\treturn (key: K): Promise<T> => {\n\t\tconst k = keyFn(key);\n\t\tconst existing = inFlight.get(k);\n\t\tif (existing) return existing;\n\n\t\tconst input = factory(key);\n\n\t\t// Resolve the NodeInput to a Promise<T>. Different input shapes need\n\t\t// different bridges — Promise/Node/AsyncIterable/Iterable/plain value.\n\t\tlet rawPromise: Promise<T>;\n\t\tif (input != null && typeof (input as PromiseLike<T>).then === \"function\") {\n\t\t\trawPromise = Promise.resolve(input as PromiseLike<T>);\n\t\t} else if (\n\t\t\tinput != null &&\n\t\t\ttypeof input === \"object\" &&\n\t\t\t\"subscribe\" in (input as object) &&\n\t\t\t\"cache\" in (input as object)\n\t\t) {\n\t\t\t// Node: bridge via firstValueFrom.\n\t\t\trawPromise = firstValueFrom(input as Node<T>);\n\t\t} else if (\n\t\t\tinput != null &&\n\t\t\ttypeof input === \"object\" &&\n\t\t\tSymbol.asyncIterator in (input as object)\n\t\t) {\n\t\t\t// AsyncIterable — take the first value, then close the iterator so\n\t\t\t// any owned resources (HTTP body, subscription, timer) are released.\n\t\t\trawPromise = (async () => {\n\t\t\t\tconst iter = (input as AsyncIterable<T>)[Symbol.asyncIterator]();\n\t\t\t\ttry {\n\t\t\t\t\tconst { value, done } = await iter.next();\n\t\t\t\t\tif (done) throw new Error(\"singleFromAny: factory returned empty async iterable\");\n\t\t\t\t\treturn value as T;\n\t\t\t\t} finally {\n\t\t\t\t\tawait iter.return?.();\n\t\t\t\t}\n\t\t\t})();\n\t\t} else if (input != null && typeof input === \"object\" && Symbol.iterator in (input as object)) {\n\t\t\t// Iterable — take the first value, close the iterator.\n\t\t\trawPromise = (async () => {\n\t\t\t\tconst iter = (input as Iterable<T>)[Symbol.iterator]();\n\t\t\t\ttry {\n\t\t\t\t\tconst { value, done } = iter.next();\n\t\t\t\t\tif (done) throw new Error(\"singleFromAny: factory returned empty iterable\");\n\t\t\t\t\treturn value as T;\n\t\t\t\t} finally {\n\t\t\t\t\titer.return?.();\n\t\t\t\t}\n\t\t\t})();\n\t\t} else {\n\t\t\t// Plain value.\n\t\t\trawPromise = Promise.resolve(input as T);\n\t\t}\n\n\t\t// Install the cache entry BEFORE attaching `.finally`. Otherwise a\n\t\t// sync-resolved Promise's finally microtask could run before the\n\t\t// `inFlight.set` below, leaving a stale entry installed afterwards.\n\t\t// We wrap in a holder whose reference we capture *before* chaining.\n\t\tlet tracked!: Promise<T>;\n\t\tconst cleanup = (): void => {\n\t\t\tif (inFlight.get(k) === tracked) inFlight.delete(k);\n\t\t};\n\t\ttracked = rawPromise.then(\n\t\t\t(v) => {\n\t\t\t\tcleanup();\n\t\t\t\treturn v;\n\t\t\t},\n\t\t\t(e) => {\n\t\t\t\tcleanup();\n\t\t\t\tthrow e;\n\t\t\t},\n\t\t);\n\t\tinFlight.set(k, tracked);\n\t\treturn tracked;\n\t};\n}\n\n/**\n * Reactive variant: returns a bound callable that hands out `Node<T>` values.\n * All concurrent callers with the same key during an in-flight source share\n * the same Node. When the underlying source **terminally** settles (ERROR\n * or COMPLETE), the Node is removed from the cache so the next call\n * re-invokes `factory`. DATA is NOT terminal — callers subscribing after\n * the first DATA still receive the shared Node (and push-on-subscribe per\n * the spec's cached-DATA contract).\n *\n * Use when downstream wants reactive subscription (not a one-shot Promise).\n *\n * @category extra\n */\nexport function singleNodeFromAny<K, T>(\n\tfactory: (key: K) => NodeInput<T>,\n\topts: SingleFromAnyOptions<K> = {},\n): (key: K) => Node<T> {\n\tconst keyFn = opts.keyFn ?? ((k: K) => String(k));\n\tconst inFlight = new Map<string, Node<T>>();\n\n\treturn (key: K): Node<T> => {\n\t\tconst k = keyFn(key);\n\t\tconst existing = inFlight.get(k);\n\t\tif (existing) return existing;\n\n\t\tconst node = fromAny(factory(key));\n\t\tinFlight.set(k, node);\n\n\t\t// Evict on terminal settle only — ERROR or COMPLETE. DATA is a value\n\t\t// emission, not a lifecycle transition; multi-emitting Nodes should\n\t\t// continue to share across subscribers after the first value.\n\t\tconst unsub = node.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === ERROR || m[0] === COMPLETE) {\n\t\t\t\t\tif (inFlight.get(k) === node) inFlight.delete(k);\n\t\t\t\t\tunsub();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn node;\n\t};\n}\n","/**\n * Settled/signal helpers.\n *\n * Moved from extra/sources/settled.ts during cleave A2.\n * `keepalive` is substrate — it lives at `@graphrefly/pure-ts`\n * (`packages/pure-ts/src/extra/sources/_keepalive.ts`), not here.\n */\n\n/**\n * Settled / signal helpers — boundary primitives for converting reactive\n * sources into Promise/AbortSignal endpoints.\n *\n * - {@link firstValueFrom} / {@link firstWhere} — Promise of the first\n * matching DATA.\n * - {@link awaitSettled} — composition over `firstWhere` + reactive\n * `timeout` from `extra/resilience` (lazy import to avoid a\n * resilience → sources cycle).\n * - {@link nodeSignal} — `Node<boolean>` → `AbortSignal` bridge.\n * - {@link reactiveCounter} — capped counter exposed as a `Node<number>`.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\ttype Messages,\n\ttype Node,\n\tnode,\n} from \"@graphrefly/pure-ts/core\";\n\n/**\n * Converts the first `DATA` on `source` into a Promise; rejects on `ERROR` or `COMPLETE` without data.\n *\n * **Important:** This subscribes and waits for a **future** emission. Data that\n * has already flowed is gone and will not be seen. Call this *before* the upstream\n * emits, or use `source.cache` / `source.status` for already-cached state.\n * See COMPOSITION-GUIDE §2 (subscription ordering).\n *\n * @param source - Node to read once.\n * @returns Promise of the first value.\n *\n * @example\n * ```ts\n * import { firstValueFrom, of } from \"@graphrefly/graphrefly-ts\";\n *\n * await firstValueFrom(of(42));\n * ```\n *\n * @category extra\n */\nexport function firstValueFrom<T>(source: Node<T>): Promise<T> {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet shouldUnsub = false;\n\t\tlet unsub: (() => void) | undefined;\n\t\tunsub = source.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (settled) return;\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tresolve(m[1] as T);\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\treject(m[1]);\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\treject(new Error(\"completed without DATA\"));\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tif (shouldUnsub) {\n\t\t\tunsub?.();\n\t\t\tunsub = undefined;\n\t\t}\n\t});\n}\n\n/**\n * Wait for the first DATA value from `source` that satisfies `predicate`.\n *\n * Subscribes directly and resolves on the first DATA value where\n * `predicate` returns true. Reactive, no polling. Use in tests and\n * bridging code where you need a single matching value as a Promise.\n *\n * **Important:** This only captures **future** emissions — data that has\n * already flowed through the node is gone. Call this *before* the upstream\n * emits. For already-cached values, use `source.cache` / `source.status`.\n * See COMPOSITION-GUIDE §2 (subscription ordering).\n *\n * ```ts\n * const val = await firstWhere(strategy.snapshot, snap => snap.size > 0);\n * ```\n *\n * @param source - Upstream node to observe.\n * @param predicate - Returns `true` for the value to resolve on.\n * @param opts - `{ skipCurrent?: boolean }`. When `skipCurrent: true`, any DATA\n * delivered during the synchronous `subscribe()` call (push-on-subscribe §2.2\n * replay of the cached value) is ignored — the promise resolves only on the\n * next future emission. Useful when the caller wants to await the next\n * settlement event after an imperative action (e.g. `run()` minting a new\n * runVersion, where the currently-cached value belongs to the previous run).\n *\n * @category extra\n */\nexport function firstWhere<T>(\n\tsource: Node<T>,\n\tpredicate: (value: T) => boolean,\n\topts?: { skipCurrent?: boolean; kick?: () => void },\n): Promise<T> {\n\t// Lock 3.A (Phase 13.6.B): subscribe synchronously inside the function\n\t// body — NOT inside the Promise executor. Subscribing inside the\n\t// executor would defer the subscription past any synchronous `kick()`\n\t// the caller fires after the call returns, race-losing the very wave\n\t// the caller wants to observe.\n\t//\n\t// To bridge sync-subscribe with the async Promise contract, we record\n\t// any settlement that fires *before* the Promise constructor runs, and\n\t// the executor immediately resolves/rejects with the recorded value.\n\t// Settlements after the executor runs go straight through resolve/reject.\n\ttype Pending =\n\t\t| { kind: \"data\"; value: T }\n\t\t| { kind: \"error\"; err: unknown }\n\t\t| { kind: \"complete\" };\n\tlet pending: Pending | undefined;\n\tlet resolveFn: ((value: T) => void) | undefined;\n\tlet rejectFn: ((err: unknown) => void) | undefined;\n\tlet settled = false;\n\tlet shouldUnsub = false;\n\tlet unsub: (() => void) | undefined;\n\tlet inInitialSyncPhase = opts?.skipCurrent === true;\n\n\t// QA P1: every settler short-circuits when `settled === true` so a\n\t// later settle attempt (e.g. `kick()` throwing AFTER it synchronously\n\t// fired matching DATA, OR a `[DATA matched, ERROR]` wave during\n\t// push-on-subscribe) cannot overwrite an earlier `pending` outcome.\n\t// Without this gate, a kick that races sink-callback settlement could\n\t// reject a Promise the user already has resolved-DATA for.\n\tconst settleData = (v: T): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tif (resolveFn != null) resolveFn(v);\n\t\telse pending = { kind: \"data\", value: v };\n\t};\n\tconst settleError = (err: unknown): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tif (rejectFn != null) rejectFn(err);\n\t\telse pending = { kind: \"error\", err };\n\t};\n\tconst settleComplete = (): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tconst err = new Error(\"completed without matching value\");\n\t\tif (rejectFn != null) rejectFn(err);\n\t\telse pending = { kind: \"complete\" };\n\t};\n\tconst detach = (): void => {\n\t\tif (unsub) {\n\t\t\tunsub();\n\t\t\tunsub = undefined;\n\t\t} else shouldUnsub = true;\n\t};\n\n\tconst sink: (msgs: Messages) => void = (msgs) => {\n\t\tif (settled) return;\n\t\tfor (const m of msgs) {\n\t\t\tif (settled) return;\n\t\t\t// During the initial sync phase, swallow only cached DATA\n\t\t\t// (push-on-subscribe §2.2). Terminal ERROR / COMPLETE must\n\t\t\t// still reject the promise — otherwise an already-terminated\n\t\t\t// source synchronously delivering `[[ERROR, ...]]` or\n\t\t\t// `[[COMPLETE]]` during `subscribe()` would hang forever\n\t\t\t// under `skipCurrent: true`.\n\t\t\tif (inInitialSyncPhase && m[0] === DATA) continue;\n\t\t\tif (m[0] === DATA) {\n\t\t\t\tconst v = m[1] as T;\n\t\t\t\tif (predicate(v)) {\n\t\t\t\t\tsettleData(v);\n\t\t\t\t\tdetach();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (m[0] === ERROR) {\n\t\t\t\tsettleError(m[1]);\n\t\t\t\tdetach();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\tsettleComplete();\n\t\t\t\tdetach();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t};\n\tunsub = source.subscribe(sink);\n\tinInitialSyncPhase = false;\n\t// Lock 3.A: fire `kick` AFTER subscribe is in place. With sync\n\t// subscribe + sync kick, the resulting wave reaches `sink` before\n\t// control returns — making subscribe-before-kick ordering structurally\n\t// impossible to misuse.\n\tif (opts?.kick != null && !settled) {\n\t\ttry {\n\t\t\topts.kick();\n\t\t} catch (err) {\n\t\t\tsettleError(err);\n\t\t\tdetach();\n\t\t}\n\t}\n\tif (shouldUnsub) {\n\t\tunsub?.();\n\t\tunsub = undefined;\n\t}\n\n\treturn new Promise<T>((resolve, reject) => {\n\t\t// If a settlement landed synchronously before the executor runs,\n\t\t// flush it through immediately.\n\t\tif (pending != null) {\n\t\t\tif (pending.kind === \"data\") resolve(pending.value);\n\t\t\telse if (pending.kind === \"error\") reject(pending.err);\n\t\t\telse reject(new Error(\"completed without matching value\"));\n\t\t\treturn;\n\t\t}\n\t\tresolveFn = resolve;\n\t\trejectFn = reject;\n\t});\n}\n\n/**\n * Await the first non-nullish DATA value from `source`, with optional\n * timeout. Composition sugar over `firstWhere` + reactive timeout.\n *\n * Designed as the CLI/boundary sink for reactive pipelines that end in a\n * nullable node (e.g. `promptNode` — per COMPOSITION-GUIDE §8, it emits\n * `null` before it settles with a real value). Replaces the common pattern\n * `firstValueFrom(filter(source, v => v != null))` with a deadline.\n *\n * - Rejects with `TimeoutError` (from `extra/resilience`) if no matching\n * value arrives within `timeoutMs`. Omit `timeoutMs` for unbounded wait.\n * - `predicate` defaults to `v => v != null`. Pass a custom predicate to\n * gate on a stronger condition (e.g. `v => typeof v === \"string\"`).\n * - Pass `skipCurrent: true` to ignore the currently-cached value delivered\n * synchronously via push-on-subscribe and resolve only on the *next*\n * matching emission. Useful after an imperative action that should produce\n * a fresh settlement (e.g. `run()` minting a new version — the stale\n * cached value from the previous run must not resolve the new caller).\n *\n * ```ts\n * const brief = await awaitSettled(briefNode, { timeoutMs: 120_000 });\n * // or with a predicate:\n * const rich = await awaitSettled(node, {\n * predicate: (v): v is MyShape => typeof v === \"object\" && v != null && \"key\" in v,\n * timeoutMs: 60_000,\n * });\n * // or after kicking off a fresh run:\n * kickOff();\n * const fresh = await awaitSettled(resultNode, { skipCurrent: true });\n * ```\n *\n * Reactive inside, sync propagation — the one async boundary is the\n * returned `Promise<T>` (spec §5.10: async belongs at sources and\n * boundaries, not in the graph).\n *\n * @param source - Upstream node to observe.\n * @param opts - `{ predicate?, timeoutMs?, skipCurrent? }`.\n * @returns Promise that resolves with the first matching value, or rejects on timeout / ERROR / COMPLETE-without-DATA.\n *\n * @category extra\n */\n// Lazy module-cache for the `withTimeout` resilience operator. The dynamic\n// import keeps `settled` free of an eager edge into the resilience family;\n// first call pays the one-shot import, subsequent calls hit cached refs.\nlet _timeoutOp: typeof import(\"../resilience/timeout.js\").withTimeout | undefined;\nlet _nsPerMs: number | undefined;\n\nexport async function awaitSettled<T>(\n\tsource: Node<T>,\n\topts?: {\n\t\tpredicate?: (value: T) => boolean;\n\t\ttimeoutMs?: number;\n\t\tskipCurrent?: boolean;\n\t\t/**\n\t\t * Lock 3.A (Phase 13.6.B): fired AFTER subscribe is in place but\n\t\t * BEFORE the helper's async boundary is exposed to the caller.\n\t\t * Subscribe-before-kick is structurally enforced — the kick's\n\t\t * synchronous wave reaches `sink` before control returns. Replaces\n\t\t * the prior load-bearing-comment pattern (M.20-load-bearing) with\n\t\t * a misuse-impossible API shape.\n\t\t *\n\t\t * Common pattern:\n\t\t * await awaitSettled(node, {\n\t\t * skipCurrent: true,\n\t\t * kick: () => producer.emit(value),\n\t\t * });\n\t\t *\n\t\t * Omit `kick` for external-trigger cases where the wave is fired\n\t\t * by code outside the helper's caller; subscribe still lands\n\t\t * synchronously inside the helper body so the next external wave\n\t\t * is not lost.\n\t\t */\n\t\tkick?: () => void;\n\t},\n): Promise<NonNullable<T>> {\n\tconst predicate = opts?.predicate ?? ((v: T) => v != null);\n\tconst skipCurrent = opts?.skipCurrent;\n\tconst kick = opts?.kick;\n\tif (opts?.timeoutMs == null || opts.timeoutMs <= 0) {\n\t\treturn (await firstWhere(source, predicate, { skipCurrent, kick })) as NonNullable<T>;\n\t}\n\t// Reactive composition: `timeout()` wraps the source as a Node that\n\t// emits ERROR(TimeoutError) on deadline. `firstWhere` then resolves on\n\t// the first matching DATA or rejects on that ERROR. One async boundary\n\t// (the returned Promise), everything inside is sync reactive.\n\tif (_timeoutOp === undefined) {\n\t\tconst [timeoutMod, backoff] = await Promise.all([\n\t\t\timport(\"../resilience/timeout.js\"),\n\t\t\timport(\"../resilience/backoff.js\"),\n\t\t]);\n\t\t_timeoutOp = timeoutMod.withTimeout;\n\t\t_nsPerMs = backoff.NS_PER_MS;\n\t}\n\tconst guarded = _timeoutOp(source, { ns: opts.timeoutMs * (_nsPerMs as number) }).node;\n\treturn (await firstWhere(guarded, predicate, { skipCurrent, kick })) as NonNullable<T>;\n}\n\n/**\n * Converts a reactive `Node<boolean>` into a browser-standard `AbortSignal`\n * that fires when the node settles on `true`. Useful for threading a reactive\n * \"cancel\" flag into any async boundary that accepts a signal (fetch, LLM SDK\n * calls, child-process APIs, timers).\n *\n * **Contract.**\n * - `signal.abort(reason)` fires exactly once, on the first DATA emission with\n * a truthy value. Subsequent emissions are ignored (AbortSignal is\n * single-shot).\n * - Null / `false` / sentinel values are ignored. Push-on-subscribe will\n * check the currently-cached value on subscribe and abort immediately if\n * it's already `true`.\n * - `reason` defaults to `\"cancelled via nodeSignal\"`; pass `opts.reason` to\n * override (`DOMException`, `Error`, or any value accepted by\n * `AbortController.abort`).\n *\n * **Lifecycle.**\n * - Returns a `{signal, dispose}` bundle. Call `dispose()` when you're done\n * with the signal (e.g. in a `finally` after the async operation completes).\n * `dispose()` unsubscribes from the node and is a no-op once the signal has\n * fired.\n * - **Memory note:** without `dispose()` the subscription keeps the reactive\n * node alive for the lifetime of the process. For bridge calls inside a\n * `switchMap` project fn, the switchMap supersede tears the inner subgraph\n * down, which is usually the right lifetime — but still call `dispose()`\n * from the caller's `finally` for clarity.\n *\n * @example\n * ```ts\n * const aborted = state(false);\n * const { signal, dispose } = nodeSignal(aborted);\n * try {\n * const resp = await adapter.invoke(msgs, { signal });\n * return resp;\n * } finally {\n * dispose();\n * }\n * ```\n *\n * @category extra\n */\nexport function nodeSignal(\n\tsource: Node<boolean>,\n\topts?: { reason?: unknown },\n): { signal: AbortSignal; dispose: () => void } {\n\tconst ctrl = new AbortController();\n\tconst reason = opts?.reason ?? new Error(\"cancelled via nodeSignal\");\n\tlet unsub: (() => void) | undefined;\n\tlet shouldUnsub = false;\n\tconst done = () => {\n\t\tif (unsub) {\n\t\t\tunsub();\n\t\t\tunsub = undefined;\n\t\t} else shouldUnsub = true;\n\t};\n\tunsub = source.subscribe((msgs) => {\n\t\tif (ctrl.signal.aborted) return;\n\t\tfor (const m of msgs) {\n\t\t\tif (m[0] === DATA && m[1] === true) {\n\t\t\t\tctrl.abort(reason);\n\t\t\t\tdone();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (m[0] === ERROR) {\n\t\t\t\t// Treat an ERROR on the abort source as a cancel signal too —\n\t\t\t\t// a broken control channel should fail closed, not leak the\n\t\t\t\t// in-flight call. Use the error as the abort reason.\n\t\t\t\tctrl.abort(m[1]);\n\t\t\t\tdone();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t// Source completed without aborting — no-op. `done()` already\n\t\t\t\t// released the subscription here, so a later `dispose()` call\n\t\t\t\t// from the caller is a no-op (safe / idempotent).\n\t\t\t\tdone();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t});\n\tif (shouldUnsub) {\n\t\tunsub?.();\n\t\tunsub = undefined;\n\t}\n\treturn {\n\t\tsignal: ctrl.signal,\n\t\tdispose: () => {\n\t\t\tif (unsub) {\n\t\t\t\tunsub();\n\t\t\t\tunsub = undefined;\n\t\t\t}\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// reactiveCounter\n// ---------------------------------------------------------------------------\n\n/** Bundle returned by {@link reactiveCounter}. */\nexport type ReactiveCounterBundle = {\n\t/** Reactive node holding the current count. */\n\treadonly node: Node<number>;\n\t/** Increment by 1. Returns `false` if cap would be exceeded. */\n\tincrement(): boolean;\n\t/** Current count (synchronous read). */\n\tget(): number;\n\t/** Whether the counter has reached its cap. */\n\tatCap(): boolean;\n};\n\n/**\n * Reactive counter with a cap — the building block for circuit breakers.\n *\n * Wraps a `state(0)` node with `increment()` that respects a maximum.\n * The `node` is subscribable and composable like any reactive node. When\n * the cap is reached, `increment()` returns `false`.\n *\n * ```ts\n * const retries = reactiveCounter(10);\n * retries.increment(); // true — count is now 1\n * retries.node.subscribe(...); // reactive updates\n * retries.atCap(); // false\n * ```\n *\n * @param cap - Maximum value (inclusive). 0 = no increments allowed.\n * @category extra\n */\nexport function reactiveCounter(cap: number): ReactiveCounterBundle {\n\tconst counter = node([], { initial: 0 });\n\treturn {\n\t\tnode: counter,\n\t\tincrement() {\n\t\t\tconst current = counter.cache ?? 0;\n\t\t\tif (current >= cap) return false;\n\t\t\tcounter.down([[DIRTY], [DATA, current + 1]]);\n\t\t\treturn true;\n\t\t},\n\t\tget() {\n\t\t\treturn counter.cache ?? 0;\n\t\t},\n\t\tatCap() {\n\t\t\treturn (counter.cache ?? 0) >= cap;\n\t\t},\n\t};\n}\n","/**\n * Composite data patterns (roadmap §3.2b).\n *\n * These helpers compose existing primitives (`node`, `switchMap`, `reactiveMap`,\n * `dynamicNode`, `fromAny`) without introducing new protocol semantics.\n */\n\nimport {\n\tbatch,\n\tDATA,\n\tfactoryTag,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n} from \"@graphrefly/pure-ts/core\";\nimport {\n\tfromAny,\n\tmerge,\n\ttype NodeInput,\n\tswitchMap,\n\twithLatestFrom,\n} from \"@graphrefly/pure-ts/extra\";\nimport { forEach } from \"../sources/async.js\";\n\n// Re-export distill from its canonical module (co-located here pre-split;\n// moved to distill.ts to avoid duplicate-export conflict at the barrel level).\nexport {\n\ttype DistillBundle,\n\ttype DistillOptions,\n\tdistill,\n\ttype Extraction,\n} from \"./distill.js\";\n\n/**\n * Verification payload shape is intentionally user-defined.\n */\nexport type VerifyValue = unknown;\n\nexport type VerifiableOptions<TVerify = VerifyValue> = Omit<\n\tNodeOptions,\n\t\"describeKind\" | \"initial\"\n> & {\n\t/** Reactive re-verification trigger. */\n\ttrigger?: NodeInput<unknown>;\n\t/** Re-run verification whenever `source` settles. */\n\tautoVerify?: boolean;\n\t/** Initial verification companion value. */\n\tinitialVerified?: TVerify | null;\n};\n\nexport type VerifiableBundle<T, TVerify = VerifyValue> = {\n\t/** Coerced source node. */\n\tnode: Node<T>;\n\t/** Latest verification result (`null` before first verification). */\n\tverified: Node<TVerify | null>;\n\t/** Effective trigger node used for verification, if any. */\n\ttrigger: Node<unknown> | null;\n};\n\n/**\n * Composes a value node with a reactive verification companion.\n *\n * Uses `switchMap` so newer triggers cancel stale in-flight verification work.\n */\nexport function verifiable<T, TVerify = VerifyValue>(\n\tsource: NodeInput<T>,\n\tverifyFn: (value: T) => NodeInput<TVerify>,\n\topts?: VerifiableOptions<TVerify>,\n): VerifiableBundle<T, TVerify> {\n\tconst sourceNode = fromAny(source);\n\tconst hasSourceVersioning = sourceNode.v != null;\n\tconst verified = node<TVerify | null>([], {\n\t\tinitial: opts?.initialVerified ?? null,\n\t\tmeta: {\n\t\t\t...factoryTag(\"verifiable\"),\n\t\t\t...(hasSourceVersioning ? { sourceVersion: null } : {}),\n\t\t},\n\t});\n\tconst hasTrigger = opts?.trigger !== undefined && opts.trigger !== null;\n\n\tlet triggerNode: Node<unknown> | null = null;\n\tif (hasTrigger && opts?.autoVerify) {\n\t\ttriggerNode = merge(fromAny(opts.trigger) as Node<unknown>, sourceNode as Node<unknown>);\n\t} else if (hasTrigger) {\n\t\ttriggerNode = fromAny(opts.trigger);\n\t} else if (opts?.autoVerify) {\n\t\ttriggerNode = sourceNode as Node<unknown>;\n\t}\n\n\tif (triggerNode !== null) {\n\t\t// Two patterns depending on trigger shape:\n\t\t// - autoVerify-only (triggerNode === sourceNode): the projected\n\t\t// switchMap value IS the source DATA, pass it directly.\n\t\t// - explicit trigger: `withLatestFrom(trigger, source)` pairs each\n\t\t// trigger emission with the latest source value. Phase 10.5\n\t\t// (`withLatestFrom` flipped to `partial: false`) fixed the W1\n\t\t// initial-pair drop — both deps settle before fn fires, so the\n\t\t// first trigger correctly pairs with the seeded source cache.\n\t\t// Replaces the §28 closure-mirror that was canonical pre-10.5.\n\t\tlet verifyStream: Node<TVerify>;\n\t\tif (triggerNode === (sourceNode as Node<unknown>)) {\n\t\t\tverifyStream = switchMap(sourceNode, (src) => verifyFn(src as T));\n\t\t} else {\n\t\t\tconst paired = withLatestFrom(triggerNode, sourceNode);\n\t\t\tverifyStream = switchMap(paired, ([, source]) => verifyFn(source as T));\n\t\t}\n\t\tforEach(verifyStream, (value) => {\n\t\t\tbatch(() => {\n\t\t\t\tverified.down([[DATA, value]]);\n\t\t\t\t// V0 backfill: stamp which source version was verified (§6.0b).\n\t\t\t\tif (hasSourceVersioning) {\n\t\t\t\t\tconst sv = sourceNode.v;\n\t\t\t\t\tif (sv != null) {\n\t\t\t\t\t\tverified.meta.sourceVersion.down([[DATA, { id: sv.id, version: sv.version }]]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\treturn { node: sourceNode, verified, trigger: triggerNode };\n}\n"],"mappings":"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","names":["import_core","import_extra","import_core","import_extra","node","batch","import_core","sourceOpts","import_core","import_core","node","import_core","import_extra","import_core","import_extra","import_core","node","import_core","import_extra","source"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/base/composition/index.ts","../../../src/base/composition/backpressure.ts","../../../src/base/composition/distill.ts","../../../src/base/sources/async.ts","../../../src/base/composition/external-register.ts","../../../src/base/composition/materialize.ts","../../../src/base/composition/observable.ts","../../../src/base/composition/pubsub.ts","../../../src/base/composition/single-from-any.ts","../../../src/base/sources/settled.ts","../../../src/base/composition/verifiable.ts"],"sourcesContent":["/**\n * Base composition — domain-agnostic composition helpers.\n *\n * @module\n */\n\nexport * from \"./backpressure.js\";\nexport * from \"./distill.js\";\nexport * from \"./external-register.js\";\nexport * from \"./materialize.js\";\nexport * from \"./observable.js\";\nexport * from \"./pubsub.js\";\nexport * from \"./single-from-any.js\";\nexport * from \"./verifiable.js\";\n// topology-diff is substrate (pure-ts); consumers import from @graphrefly/pure-ts/extra\n","/**\n * Watermark-based backpressure controller — reactive PAUSE/RESUME flow control.\n *\n * Purely synchronous, event-driven. No timers, no polling, no Promises.\n * Each controller instance uses a unique lockId so multiple controllers\n * on the same upstream node do not collide.\n *\n * @module\n */\n\nimport { type Messages, PAUSE, RESUME } from \"@graphrefly/pure-ts/core\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type WatermarkOptions = {\n\t/** Pending count at which PAUSE is sent upstream. */\n\thighWaterMark: number;\n\t/** Pending count at which RESUME is sent upstream (after being paused). */\n\tlowWaterMark: number;\n};\n\nexport type WatermarkController = {\n\t/** Call when a DATA message is buffered/enqueued. Returns `true` if PAUSE was just sent. */\n\tonEnqueue(): boolean;\n\t/** Call when a buffered item is consumed. Returns `true` if RESUME was just sent. */\n\tonDequeue(): boolean;\n\t/** Current un-consumed item count. */\n\treadonly pending: number;\n\t/** Whether upstream is currently paused by this controller. */\n\treadonly paused: boolean;\n\t/** Dispose: if paused, sends RESUME to unblock upstream. */\n\tdispose(): void;\n};\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nlet nextLockId = 0;\n\n/**\n * Creates a watermark-based backpressure controller.\n *\n * @param sendUp - Callback that delivers messages upstream (typically `handle.up`).\n * @param opts - High/low watermark thresholds (item counts).\n * @returns A {@link WatermarkController}.\n *\n * @example\n * ```ts\n * const handle = graph.observe(\"fast-source\");\n * const wm = createWatermarkController(\n * (msgs) => handle.up(msgs),\n * { highWaterMark: 64, lowWaterMark: 16 },\n * );\n *\n * // In sink callback:\n * handle.subscribe((msgs) => {\n * for (const msg of msgs) {\n * if (msg[0] === DATA) {\n * buffer.push(msg[1]);\n * wm.onEnqueue();\n * }\n * }\n * });\n *\n * // When consumer drains:\n * const item = buffer.shift();\n * wm.onDequeue();\n * ```\n *\n * @category extra\n */\nexport function createWatermarkController(\n\tsendUp: (messages: Messages) => void,\n\topts: WatermarkOptions,\n): WatermarkController {\n\tif (opts.highWaterMark < 1) throw new RangeError(\"highWaterMark must be >= 1\");\n\tif (opts.lowWaterMark < 0) throw new RangeError(\"lowWaterMark must be >= 0\");\n\tif (opts.lowWaterMark >= opts.highWaterMark)\n\t\tthrow new RangeError(\"lowWaterMark must be < highWaterMark\");\n\tconst lockId = Symbol(`bp-${++nextLockId}`);\n\tlet pending = 0;\n\tlet paused = false;\n\n\treturn {\n\t\tonEnqueue(): boolean {\n\t\t\tpending += 1;\n\t\t\tif (!paused && pending >= opts.highWaterMark) {\n\t\t\t\tpaused = true;\n\t\t\t\tsendUp([[PAUSE, lockId]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\tonDequeue(): boolean {\n\t\t\tif (pending > 0) pending -= 1;\n\t\t\tif (paused && pending <= opts.lowWaterMark) {\n\t\t\t\tpaused = false;\n\t\t\t\tsendUp([[RESUME, lockId]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\tget pending() {\n\t\t\treturn pending;\n\t\t},\n\t\tget paused() {\n\t\t\treturn paused;\n\t\t},\n\t\tdispose() {\n\t\t\tif (paused) {\n\t\t\t\tpaused = false;\n\t\t\t\tsendUp([[RESUME, lockId]]);\n\t\t\t}\n\t\t},\n\t};\n}\n","/**\n * Budget-constrained reactive memory composition (roadmap §3.2b).\n *\n * Moved to base/composition/distill.ts during cleave A2.\n */\n\nimport { batch, factoryTag, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport {\n\tfromAny,\n\ttype NodeInput,\n\ttype ReactiveMapBundle,\n\ttype ReactiveMapOptions,\n\treactiveMap,\n\tswitchMap,\n\twithLatestFrom,\n} from \"@graphrefly/pure-ts/extra\";\nimport { forEach } from \"../sources/async.js\";\n\nfunction isNodeLike<T>(value: unknown): value is Node<T> {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"cache\" in (value as Node<T>) &&\n\t\ttypeof (value as Node<T>).subscribe === \"function\"\n\t);\n}\n\nexport type Extraction<TMem> = {\n\tupsert: Array<{ key: string; value: TMem }>;\n\tremove?: string[];\n};\n\nexport type DistillOptions<TMem> = {\n\tscore: (mem: TMem, context: unknown) => number;\n\tcost: (mem: TMem) => number;\n\tbudget?: number;\n\tevict?: (key: string, mem: TMem) => boolean | Node<boolean>;\n\tconsolidate?: (entries: ReadonlyMap<string, TMem>) => NodeInput<Extraction<TMem>>;\n\tconsolidateTrigger?: NodeInput<unknown>;\n\tcontext?: NodeInput<unknown>;\n\tmapOptions?: ReactiveMapOptions<string, TMem>;\n};\n\nexport type DistillBundle<TMem> = {\n\tstore: ReactiveMapBundle<string, TMem>;\n\tcompact: Node<Array<{ key: string; value: TMem; score: number }>>;\n\tsize: Node<number>;\n};\n\nfunction keepalive(node: Node): void {\n\tnode.subscribe(() => undefined);\n}\n\n/**\n * Defensive snapshot → ReadonlyMap coercion (D2 /qa lock, Tier 9.1).\n *\n * `ReactiveMapBundle.entries` always emits a real `Map` on the live emit\n * path. The non-Map case happens on snapshot **restore**: the default\n * `JsonGraphCodec` serializes a `Map` to `null`/`{}`/`[]` depending on the\n * codec configuration, and `Graph.restore` writes that decoded value back\n * to the cache. A naive `(snapshot as ReadonlyMap) ?? new Map()` would\n * pass a plain object through and then `.entries()` / `.size` access would\n * silently yield wrong results (or throw). The runtime `instanceof Map`\n * check below restores the safety net the previous `mapFromSnapshot` helper\n * provided before its initial deletion in Tier 10.1.\n */\nfunction mapFromSnapshot<TMem>(snapshot: unknown): ReadonlyMap<string, TMem> {\n\tif (snapshot instanceof Map) return snapshot as ReadonlyMap<string, TMem>;\n\treturn new Map<string, TMem>();\n}\n\nfunction applyExtraction<TMem>(\n\tstore: ReactiveMapBundle<string, TMem>,\n\textraction: Extraction<TMem>,\n): void {\n\tif (!Array.isArray(extraction.upsert)) {\n\t\tthrow new TypeError(\"distill extraction requires upsert: Array<{ key, value }>\");\n\t}\n\tbatch(() => {\n\t\tfor (const { key, value } of extraction.upsert) {\n\t\t\tstore.set(key, value);\n\t\t}\n\t\tfor (const key of extraction.remove ?? []) {\n\t\t\tstore.delete(key);\n\t\t}\n\t});\n}\n\n/**\n * Budget-constrained reactive memory composition.\n *\n * **Tier 1.5.4 (Session A.5 lock, 2026-04-27):** `extractFn` receives the\n * source and existing-store as `Node`s. Distill calls `extractFn` ONCE at\n * wiring time and consumes the returned stream of extractions. The user\n * controls reactive composition — wrap with `switchMap` for cancel-on-new-input,\n * `mergeMap` for parallel, `derived` for sync transforms. See COMPOSITION-GUIDE\n * §40 for the recipe.\n */\nexport function distill<TRaw, TMem>(\n\tsource: NodeInput<TRaw>,\n\textractFn: (\n\t\traw: Node<TRaw>,\n\t\texisting: Node<ReadonlyMap<string, TMem>>,\n\t) => NodeInput<Extraction<TMem>>,\n\topts: DistillOptions<TMem>,\n): DistillBundle<TMem> {\n\tconst sourceNode = fromAny(source);\n\tconst store = reactiveMap<string, TMem>(opts.mapOptions ?? {});\n\tconst budget = opts.budget ?? 2000;\n\tconst hasContext = opts.context !== undefined && opts.context !== null;\n\tconst contextNode = hasContext ? fromAny(opts.context) : node<unknown>([], { initial: null });\n\n\t// `latestStore` (formerly a §28 closure-mirror) is no longer needed —\n\t// Phase 10.5 (`withLatestFrom` flipped to `partial: false`) fixed the\n\t// W1 initial-pair drop. `consolidate` now uses\n\t// `withLatestFrom(trigger, store.entries)` below to pair each trigger\n\t// with the latest store snapshot via a real reactive edge (visible in\n\t// `describe()`). The `mapFromSnapshot` transform runs inside the\n\t// switchMap fn body.\n\n\t// Tier 1.5.4: one-shot wire. User's `extractFn` returns the reactive\n\t// extraction stream — distill just `forEach`s and applies. No internal\n\t// switchMap; user picks the cancellation / queueing semantics.\n\tconst extractionStream = fromAny(\n\t\textractFn(sourceNode, store.entries as Node<ReadonlyMap<string, TMem>>),\n\t);\n\tforEach(extractionStream, (extraction) => {\n\t\tapplyExtraction(store, extraction);\n\t});\n\n\tif (opts.evict) {\n\t\t// Track active verdict-node subscriptions so we can react to Node<boolean> changes.\n\t\tconst verdictUnsubs = new Map<string, () => void>();\n\n\t\tconst evictionKeys = node<string[]>(\n\t\t\t[store.entries],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst batch0 = batchData[0];\n\t\t\t\tconst snapshot = batch0 != null && batch0.length > 0 ? batch0.at(-1) : ctx.prevData[0];\n\t\t\t\tconst out: string[] = [];\n\t\t\t\tconst entries = mapFromSnapshot<TMem>(snapshot);\n\t\t\t\t// Clean up verdict subscriptions for removed keys.\n\t\t\t\tfor (const key of verdictUnsubs.keys()) {\n\t\t\t\t\tif (!entries.has(key)) {\n\t\t\t\t\t\tverdictUnsubs.get(key)!();\n\t\t\t\t\t\tverdictUnsubs.delete(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const [key, mem] of entries) {\n\t\t\t\t\tconst verdict = opts.evict!(key, mem);\n\t\t\t\t\tif (isNodeLike<boolean>(verdict)) {\n\t\t\t\t\t\t// Subscribe if not already — push-on-subscribe fires with\n\t\t\t\t\t\t// the verdict's current value on first subscribe, so an\n\t\t\t\t\t\t// already-true verdict deletes via the callback without\n\t\t\t\t\t\t// needing a `verdict.cache` read (closes P3 audit #3).\n\t\t\t\t\t\t// Future transitions to `true` flow through the same path.\n\t\t\t\t\t\tif (!verdictUnsubs.has(key)) {\n\t\t\t\t\t\t\tconst unsub = forEach(verdict, (val) => {\n\t\t\t\t\t\t\t\tif (val === true && store.has(key)) {\n\t\t\t\t\t\t\t\t\tstore.delete(key);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tverdictUnsubs.set(key, unsub);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof verdict === \"boolean\") {\n\t\t\t\t\t\tif (verdict) out.push(key);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthrow new TypeError(\"distill evict() must return boolean or Node<boolean>\");\n\t\t\t\t}\n\t\t\t\tactions.emit(out);\n\t\t\t},\n\t\t\t{ describeKind: \"derived\" },\n\t\t);\n\t\tforEach(evictionKeys, (keys) => {\n\t\t\tfor (const key of keys) store.delete(key);\n\t\t});\n\t}\n\n\tconst hasConsolidateTrigger =\n\t\topts.consolidateTrigger !== undefined && opts.consolidateTrigger !== null;\n\tif (opts.consolidate && hasConsolidateTrigger) {\n\t\tconst consolidateTriggerNode = fromAny(opts.consolidateTrigger);\n\t\tconst consolidatePaired = withLatestFrom(\n\t\t\tconsolidateTriggerNode,\n\t\t\tstore.entries as Node<unknown>,\n\t\t);\n\t\tconst consolidationStream = switchMap(consolidatePaired, ([, entries]) =>\n\t\t\topts.consolidate!(mapFromSnapshot<TMem>(entries)),\n\t\t);\n\t\tforEach(consolidationStream, (extraction) => {\n\t\t\tapplyExtraction(store, extraction);\n\t\t});\n\t}\n\n\tconst compact = node<Array<{ key: string; value: TMem; score: number }>>(\n\t\t[store.entries, contextNode],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst snapshot = data[0];\n\t\t\tconst context = data[1];\n\t\t\tconst map = mapFromSnapshot<TMem>(snapshot);\n\t\t\tconst entries = [...map.entries()].map(([key, value]) => ({\n\t\t\t\tkey,\n\t\t\t\tvalue,\n\t\t\t\tscore: opts.score(value, context),\n\t\t\t\tcost: opts.cost(value),\n\t\t\t}));\n\t\t\tentries.sort((a, b) => b.score - a.score);\n\n\t\t\tconst packed: Array<{ key: string; value: TMem; score: number }> = [];\n\t\t\tlet remaining = budget;\n\t\t\tfor (const item of entries) {\n\t\t\t\tif (item.cost <= remaining) {\n\t\t\t\t\tpacked.push({ key: item.key, value: item.value, score: item.score });\n\t\t\t\t\tremaining -= item.cost;\n\t\t\t\t}\n\t\t\t}\n\t\t\tactions.emit(packed);\n\t\t},\n\t\t{ describeKind: \"derived\", meta: { ...factoryTag(\"distill\", { budget }) } },\n\t);\n\n\tconst size = node<number>(\n\t\t[store.entries],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst batch0 = batchData[0];\n\t\t\tconst snapshot = batch0 != null && batch0.length > 0 ? batch0.at(-1) : ctx.prevData[0];\n\t\t\tactions.emit(mapFromSnapshot<TMem>(snapshot).size);\n\t\t},\n\t\t{ describeKind: \"derived\" },\n\t);\n\tkeepalive(compact);\n\tkeepalive(size);\n\n\treturn { store, compact, size };\n}\n","/**\n * Async sources, sinks, and multicast — presentation layer.\n *\n * `fromPromise`, `fromAsyncIter`, `fromAny` are substrate primitives; they are\n * re-exported here from `@graphrefly/pure-ts` for ergonomic single-import use.\n * This file owns the presentation-only async utilities: `defer`, `forEach`,\n * `toArray`, `share`, `replay`, `cached`, `shareReplay`.\n *\n * `singleFromAny` and `singleNodeFromAny` (keyed singleflight) live in\n * `base/composition/single-from-any.ts`.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\tRESOLVED,\n\tSTART,\n} from \"@graphrefly/pure-ts/core\";\nimport { type AsyncSourceOpts, type NodeInput, sourceOpts } from \"@graphrefly/pure-ts/extra\";\n\n/** Options for presentation-layer async operators: NodeOptions without `describeKind`. */\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\n// Import fromAny from substrate — used internally by defer. The three async\n// substrate sources (fromAny, fromAsyncIter, fromPromise) are already\n// re-exported from @graphrefly/pure-ts; do NOT re-export here to avoid\n// duplicate-export conflicts at the root barrel level.\nimport { fromAny } from \"@graphrefly/pure-ts/extra\";\n\n/**\n * Lazily constructs a {@link Node} from a thunk that runs at **activation\n * time** (first subscriber after a teardown to zero sinks), not factory time.\n *\n * **Resubscribable by default.** Diverges from `fromPromise` / `fromIter` /\n * `fromAsyncIter` (which are single-shot — second subscriber sees the cached\n * terminal value). `defer`'s contract matches RxJS `defer`: every fresh\n * activation cycle re-runs the thunk. To opt out and get one-shot semantics,\n * pass `{ resubscribable: false }`.\n *\n * **Sharing across overlapping subscribers.** The thunk only re-runs on a\n * fresh activation cycle (zero → one sink). Overlapping subscribers share\n * the single activation; the thunk does NOT re-run for each subscriber. If\n * the thunk returns an existing `Node`, that Node is shared across activations\n * — `defer` will subscribe to it on each activation but does not isolate state\n * across subscribers. For per-subscriber isolation, the thunk must construct\n * a fresh source (`state(...)`, `fromPromise(fetch(...))`, etc.) on each call.\n *\n * **Use cases:**\n * - Lazy upstream construction (avoid eager evaluation of expensive factories\n * at module load — the thunk runs only when something subscribes).\n * - Per-activation resource construction (open a connection / file handle on\n * subscribe, when paired with full teardown between sessions).\n * - Bridging non-Node inputs (Promise, AsyncIterable, Iterable, scalar) into\n * the graph behind a lazy boundary.\n *\n * The thunk's return value is bridged via {@link fromAny}. Errors thrown by\n * the thunk surface as a single `[[ERROR, err]]` on the output (with `err`\n * coerced to a non-`undefined` value to satisfy spec §1.3 — bare `throw` and\n * `throw undefined` are wrapped in a `defer: thunk threw undefined` Error).\n *\n * Upstream messages are forwarded transparently (DIRTY / DATA / RESOLVED /\n * COMPLETE / ERROR / INVALIDATE / PAUSE / RESUME / TEARDOWN), preserving\n * batch boundaries. The producer's own `START` handshake is delivered to\n * subscribers automatically; the upstream's `START` is filtered.\n *\n * @param thunk - Called on each activation; returns the upstream input.\n * @param opts - Forwarded to `fromAny` (e.g. `signal` for async inputs).\n * `signal` is only consumed by `fromAny` for async input shapes (Promise,\n * AsyncIterable); it does NOT abort a Node-input or scalar-input defer.\n * @returns `Node<T>` — lazy upstream-on-activation.\n *\n * @example\n * ```ts\n * import { defer } from \"@graphrefly/graphrefly-ts\";\n *\n * // Lazy fetch — runs on the first activation, NOT at factory time.\n * // Each fresh activation cycle (after teardown) re-runs the thunk →\n * // a new fetch. Overlapping subscribers share the single activation.\n * const live = defer(() => fetch(\"/api/feed\").then((r) => r.json()));\n * ```\n *\n * @category extra\n */\nexport function defer<T>(thunk: () => NodeInput<T>, opts?: AsyncSourceOpts): Node<T> {\n\t// A4: strip `signal` before forwarding to NodeOptions — sibling sources\n\t// (fromTimer / fromPromise / fromAsyncIter) destructure first; signal\n\t// continues to flow into fromAny(input, opts) for async input shapes.\n\tconst { signal: _sig, ...nodeOpts } = (opts ?? {}) as AsyncSourceOpts;\n\tconst sOpts = sourceOpts<T>(nodeOpts);\n\tconst merged = sOpts.resubscribable === undefined ? { ...sOpts, resubscribable: true } : sOpts;\n\treturn node<T>((_data, a) => {\n\t\tlet unsub: (() => void) | undefined;\n\t\tlet stopped = false;\n\t\ttry {\n\t\t\tconst input = thunk();\n\t\t\t// `iter: true` preserves defer's RxJS-aligned per-element\n\t\t\t// streaming for sync iterable thunk returns (post DS-13.5\n\t\t\t// fromAny default flip; defer's documented contract is\n\t\t\t// \"forwards iterable values\" per-element).\n\t\t\tconst src = fromAny(input, { ...opts, iter: true });\n\t\t\tunsub = src.subscribe((msgs) => {\n\t\t\t\tif (stopped) return;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tconst t = m[0];\n\t\t\t\t\tif (t === START) continue; // producer's own START is delivered separately\n\t\t\t\t\tif (t === DATA) {\n\t\t\t\t\t\ta.emit(m[1] as T);\n\t\t\t\t\t} else if (t === COMPLETE) {\n\t\t\t\t\t\tstopped = true;\n\t\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t\t\tbreak; // A2: don't forward post-terminal messages in the same batch\n\t\t\t\t\t} else if (t === ERROR) {\n\t\t\t\t\t\tstopped = true;\n\t\t\t\t\t\ta.down([[ERROR, m[1]]]);\n\t\t\t\t\t\tbreak; // A2\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Forward DIRTY / RESOLVED / INVALIDATE / PAUSE / RESUME /\n\t\t\t\t\t\t// TEARDOWN, plus any unknown types (spec §1.3.6 forward-compat).\n\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (err) {\n\t\t\t// A5: spec §1.3 — ERROR payload must not be undefined. Wrap a\n\t\t\t// `throw` or `throw undefined` so dispatch doesn't reject the emit.\n\t\t\tconst safe = err === undefined ? new Error(\"defer: thunk threw undefined\") : err;\n\t\t\ta.down([[ERROR, safe]]);\n\t\t}\n\t\treturn () => {\n\t\t\tstopped = true;\n\t\t\tunsub?.();\n\t\t};\n\t}, merged);\n}\n\n/**\n * Subscribes immediately and runs `fn` for each upstream `DATA`; returns unsubscribe.\n *\n * @param source - Upstream node.\n * @param fn - Side effect per value.\n * @param opts - Effect node options.\n * @returns Unsubscribe function (idempotent).\n *\n * @example\n * ```ts\n * import { forEach, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const u = forEach(state(1), (v) => console.log(v));\n * u();\n * ```\n *\n * @category extra\n */\nexport function forEach<T>(source: Node<T>, fn: (value: T) => void, opts?: ExtraOpts): () => void {\n\tconst inner = node(\n\t\t[source as Node],\n\t\t(data, _actions) => {\n\t\t\tconst batch0 = data[0];\n\t\t\tif (batch0 != null && batch0.length > 0) {\n\t\t\t\tfor (const v of batch0) fn(v as T);\n\t\t\t}\n\t\t},\n\t\t{ describeKind: \"effect\", ...opts } as NodeOptions,\n\t);\n\treturn inner.subscribe(() => {});\n}\n\n/**\n * Buffers every `DATA`; on upstream `COMPLETE` emits one `DATA` with the full array then `COMPLETE`.\n *\n * @param source - Upstream node.\n * @param opts - Optional node options (derived describe kind).\n * @returns `Node<T[]>` — single array emission before completion.\n *\n * @example\n * ```ts\n * import { of, toArray } from \"@graphrefly/graphrefly-ts\";\n *\n * toArray(of(1, 2, 3));\n * ```\n *\n * @category extra\n */\nexport function toArray<T>(source: Node<T>, opts?: ExtraOpts): Node<T[]> {\n\t// Lock 6.D (Phase 13.6.B): clear the accumulator buffer on\n\t// deactivation so a resubscribable toArray restarts with an empty\n\t// array on the next cycle — pre-flip this came for free via\n\t// `_deactivate`'s store wipe.\n\tlet cleanup: { onDeactivation: () => void } | undefined;\n\treturn node<T[]>(\n\t\t[source as Node],\n\t\t(data, actions, ctx) => {\n\t\t\tif (cleanup === undefined) {\n\t\t\t\tconst store = ctx.store;\n\t\t\t\tcleanup = {\n\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\tdelete store.buf;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (!ctx.store.buf) ctx.store.buf = [];\n\t\t\tconst buf = ctx.store.buf as T[];\n\t\t\t// Accumulate DATA first — must happen before the COMPLETE check so\n\t\t\t// that a same-wave DATA+COMPLETE batch (e.g. fromTimer one-shot,\n\t\t\t// fromIter last item) is included in the emitted array.\n\t\t\tconst batch0 = data[0];\n\t\t\tif (batch0 != null && batch0.length > 0) {\n\t\t\t\tfor (const v of batch0) buf.push(v as T);\n\t\t\t}\n\t\t\t// COMPLETE: emit accumulated array then complete.\n\t\t\t// ERROR: autoError propagates; do NOT emit the partial buffer.\n\t\t\tif (ctx.terminalDeps[0] === true) {\n\t\t\t\tactions.emit([...buf]);\n\t\t\t\tactions.down([[COMPLETE]]);\n\t\t\t\treturn cleanup;\n\t\t\t}\n\t\t\t// RESOLVED wave: propagate RESOLVED. Covers first-wave case; after first\n\t\t\t// call the pre-fn skip handles this automatically.\n\t\t\tif (batch0 == null || batch0.length === 0) {\n\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t}\n\t\t\treturn cleanup;\n\t\t},\n\t\t{\n\t\t\tdescribeKind: \"derived\",\n\t\t\tcompleteWhenDepsComplete: false,\n\t\t\t...opts,\n\t\t} as NodeOptions<T[]>,\n\t);\n}\n\n/**\n * Multicasts upstream: one subscription to `source` while this wrapper has subscribers (via {@link producer}).\n *\n * @param source - Upstream node to share.\n * @param opts - Producer options; `initial` seeds from `source.cache` when set by factory.\n * @returns `Node<T>` — hot ref-counted bridge.\n *\n * @example\n * ```ts\n * import { share, state } from \"@graphrefly/graphrefly-ts\";\n *\n * share(state(0));\n * ```\n *\n * @category extra\n */\nexport function share<T>(source: Node<T>, opts?: ExtraOpts): Node<T> {\n\treturn node<T>(\n\t\t(_data, a) =>\n\t\t\tsource.subscribe((msgs) => {\n\t\t\t\ta.down(msgs);\n\t\t\t}),\n\t\t{ ...sourceOpts<T>(opts), initial: source.cache },\n\t);\n}\n\n/**\n * Like {@link share} with a bounded replay buffer: new subscribers receive the last `bufferSize`\n * `DATA` payloads (as separate batches) before live updates.\n *\n * @param source - Upstream node.\n * @param bufferSize - Maximum past values to replay (≥ 1).\n * @param opts - Producer options.\n * @returns `Node<T>` — multicast with replay on subscribe.\n *\n * @example\n * ```ts\n * import { replay, state } from \"@graphrefly/graphrefly-ts\";\n *\n * replay(state(0), 3);\n * ```\n *\n * @category extra\n */\nexport function replay<T>(source: Node<T>, bufferSize: number, opts?: ExtraOpts): Node<T> {\n\tif (bufferSize < 1) throw new RangeError(\"replay expects bufferSize >= 1\");\n\t// Spec §2.5 / Lock 6.G: the built-in `replayBuffer` NodeOption retains the\n\t// last-N outgoing DATA and `defaultOnSubscribe` delivers them to a late\n\t// subscriber INSTEAD of the cache-DATA push — so there is no double-deliver\n\t// of the most-recent value. Supersedes the old `wrapSubscribeHook` +\n\t// manual-buffer pattern (which flushed the buffer AND then push-on-\n\t// subscribed the cache, double-delivering the last value).\n\treturn node<T>(\n\t\t(_data, a) =>\n\t\t\tsource.subscribe((msgs) => {\n\t\t\t\ta.down(msgs);\n\t\t\t}),\n\t\t{ ...sourceOpts<T>(opts), initial: source.cache, replayBuffer: bufferSize },\n\t);\n}\n\n/**\n * {@link replay} with `bufferSize === 1` — replays the latest `DATA` to new subscribers.\n *\n * @param source - Upstream node.\n * @param opts - Producer options.\n * @returns `Node<T>` — share + last-value replay.\n *\n * @example\n * ```ts\n * import { cached, state } from \"@graphrefly/graphrefly-ts\";\n *\n * cached(state(0));\n * ```\n *\n * @category extra\n */\nexport function cached<T>(source: Node<T>, opts?: ExtraOpts): Node<T> {\n\treturn replay(source, 1, opts);\n}\n\n// ——————————————————————————————————————————————————————————————\n// RxJS-compatible aliases\n// ——————————————————————————————————————————————————————————————\n\n/**\n * RxJS-named alias for {@link replay} — multicast with a replay buffer of size `bufferSize`.\n *\n * @param source - Upstream node.\n * @param bufferSize - Replay depth (≥ 1).\n * @param opts - Producer options.\n * @returns Same behavior as `replay`.\n *\n * @example\n * ```ts\n * import { shareReplay, state } from \"@graphrefly/graphrefly-ts\";\n *\n * shareReplay(state(0), 5);\n * ```\n *\n * @category extra\n */\nexport const shareReplay = replay;\n","/**\n * External-register helpers — the common `register({emit, error, complete})`\n * contract shared by webhook, MCP, syslog, StatsD, OTel and other callback-\n * based integrations. Absorbs the `active` flag that every such adapter needs\n * to guard against emits after teardown (§5.10 boundary pattern).\n *\n * Two shapes:\n *\n * - {@link externalProducer} — single channel. Lazy activation: the register\n * fn runs when the node gains its first subscriber; its returned cleanup\n * runs on deactivation.\n *\n * - {@link externalBundle} — multiple named channels. Eager activation: the\n * register fn runs at bundle construction time so externally-owned servers\n * (HTTP endpoints, UDP sockets) start accepting traffic immediately. A\n * shared refcount fires the returned cleanup once every channel has fully\n * torn down.\n */\n\nimport {\n\tbatch,\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n} from \"@graphrefly/pure-ts/core\";\n\ntype ExtraOpts = Omit<NodeOptions<unknown>, \"describeKind\">;\n\nfunction sourceOpts<T>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\n/**\n * Standard emit-triad passed to a single-channel external registrar.\n *\n * Post-teardown calls on any of these are automatically no-ops — the\n * registrar does not need its own guard flag.\n *\n * @category extra\n */\nexport type EmitTriad<T> = {\n\t/** Emit a value as `DATA`. */\n\temit: (value: T) => void;\n\t/** Terminate with `ERROR`. Subsequent `emit` / `error` / `complete` are ignored. */\n\terror: (err: unknown) => void;\n\t/** Terminate with `COMPLETE`. Subsequent `emit` / `error` / `complete` are ignored. */\n\tcomplete: () => void;\n};\n\n/**\n * Multi-channel emit bundle. Each declared channel name maps to an emit fn;\n * `error` and `complete` terminate every channel atomically.\n *\n * @category extra\n */\nexport type BundleTriad<TChannels extends Record<string, unknown>> = {\n\t[K in keyof TChannels]: (value: TChannels[K]) => void;\n} & {\n\t/** Terminate every channel with `ERROR`. */\n\terror: (err: unknown) => void;\n\t/** Terminate every channel with `COMPLETE`. */\n\tcomplete: () => void;\n};\n\n/**\n * Generic external registrator contract. The caller installs handlers into a\n * third-party library / framework / server and optionally returns a cleanup\n * callback. Returning `undefined` / `void` is equivalent to a no-op cleanup.\n *\n * @category extra\n */\nexport type ExternalRegister<H> = (handlers: H) => (() => void) | undefined;\n\n/**\n * Wraps a callback-style external integration as a reactive source.\n *\n * The registrar installs the supplied `emit` / `error` / `complete` handlers\n * into the external SDK; post-teardown calls are silently dropped. Synchronous\n * exceptions thrown by the registrar surface as terminal `ERROR`.\n *\n * @param register - Installs handlers. Optionally returns a cleanup fn.\n * @param opts - Node options (name, equals, resubscribable, ...).\n *\n * @example\n * ```ts\n * import { externalProducer } from \"@graphrefly/graphrefly-ts\";\n *\n * const hook$ = externalProducer<Payload>(({ emit, error }) => {\n * const id = transport.onMessage((raw) => {\n * try { emit(parse(raw)); } catch (e) { error(e); }\n * });\n * return () => transport.off(id);\n * });\n * ```\n *\n * @category extra\n */\nexport function externalProducer<T = unknown>(\n\tregister: ExternalRegister<EmitTriad<T>>,\n\topts?: ExtraOpts,\n): Node<T> {\n\treturn node<T>((_data, a) => {\n\t\tlet active = true;\n\t\tconst triad: EmitTriad<T> = {\n\t\t\temit(value) {\n\t\t\t\tif (!active) return;\n\t\t\t\ta.emit(value);\n\t\t\t},\n\t\t\terror(err) {\n\t\t\t\tif (!active) return;\n\t\t\t\tactive = false;\n\t\t\t\ta.down([[ERROR, err]]);\n\t\t\t},\n\t\t\tcomplete() {\n\t\t\t\tif (!active) return;\n\t\t\t\tactive = false;\n\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t},\n\t\t};\n\t\tlet cleanup: (() => void) | undefined;\n\t\ttry {\n\t\t\tconst ret = register(triad);\n\t\t\tcleanup = typeof ret === \"function\" ? ret : undefined;\n\t\t} catch (err) {\n\t\t\ttriad.error(err);\n\t\t\treturn () => {\n\t\t\t\tactive = false;\n\t\t\t};\n\t\t}\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\ttry {\n\t\t\t\tcleanup?.();\n\t\t\t} catch {\n\t\t\t\t/* registrar cleanup failure is not a reactive signal */\n\t\t\t}\n\t\t};\n\t}, sourceOpts(opts));\n}\n\n/**\n * Options for {@link externalBundle}.\n *\n * @category extra\n */\nexport type ExternalBundleOptions<TChannels extends Record<string, unknown>> = {\n\t/** Base name prefix for channel nodes; each node is named `${name}::${channel}`. */\n\tname?: string;\n\t/** Per-channel node options (equals, resubscribable, ...). */\n\tchannelOpts?: { [K in keyof TChannels]?: ExtraOpts };\n};\n\n/**\n * Multi-channel variant — one `Node<T>` per named channel, sharing a single\n * registrar. Activation is eager: the registrar runs at construction time so\n * externally-owned servers (HTTP, UDP, queue consumers) can start accepting\n * traffic immediately. The returned cleanup fires once every channel has been\n * subscribed and then fully deactivated (refcount-on-teardown).\n *\n * Any call to `error` or `complete` propagates to every channel atomically.\n *\n * @param register - Installs handlers for each channel plus shared error/complete.\n * @param channels - Ordered channel names; determines the returned object shape.\n * @param opts - Optional name prefix and per-channel node options.\n *\n * @example\n * ```ts\n * import { externalBundle } from \"@graphrefly/graphrefly-ts\";\n *\n * type OTelChannels = { traces: Span; metrics: Metric; logs: LogRec };\n * const otel = externalBundle<OTelChannels>(\n * ({ traces, metrics, logs, error }) => {\n * app.post(\"/v1/traces\", (req, res) => { traces(req.body); res.sendStatus(200); });\n * app.post(\"/v1/metrics\", (req, res) => { metrics(req.body); res.sendStatus(200); });\n * app.post(\"/v1/logs\", (req, res) => { logs(req.body); res.sendStatus(200); });\n * server.on(\"error\", error);\n * return () => server.close();\n * },\n * [\"traces\", \"metrics\", \"logs\"],\n * );\n * otel.traces.subscribe(...);\n * ```\n *\n * @category extra\n */\nexport function externalBundle<TChannels extends Record<string, unknown>>(\n\tregister: ExternalRegister<BundleTriad<TChannels>>,\n\tchannels: readonly (keyof TChannels & string)[],\n\topts?: ExternalBundleOptions<TChannels>,\n): { [K in keyof TChannels]: Node<TChannels[K]> } & { dispose(): void } {\n\tlet active = true;\n\tlet cleanup: (() => void) | undefined;\n\tlet activatedCount = 0;\n\tlet teardownCount = 0;\n\n\tconst nodes = {} as { [K in keyof TChannels]: Node<TChannels[K]> };\n\tconst channelNodes: Array<Node<unknown>> = [];\n\n\tconst finishCleanup = () => {\n\t\tconst fn = cleanup;\n\t\tcleanup = undefined;\n\t\ttry {\n\t\t\tfn?.();\n\t\t} catch {\n\t\t\t/* registrar cleanup failure is not a reactive signal */\n\t\t}\n\t};\n\n\tfor (const ch of channels) {\n\t\tconst name = opts?.name ? `${opts.name}::${ch}` : ch;\n\t\tconst chOpts = opts?.channelOpts?.[ch];\n\t\tconst n = node<TChannels[typeof ch]>(\n\t\t\t(_data, _a) => {\n\t\t\t\tactivatedCount++;\n\t\t\t\treturn () => {\n\t\t\t\t\tteardownCount++;\n\t\t\t\t\t// Cleanup fires once every channel has activated at least once\n\t\t\t\t\t// and then deactivated. Channels that never subscribe do not\n\t\t\t\t\t// gate cleanup — use the explicit `.dispose()` method for\n\t\t\t\t\t// unconditional teardown.\n\t\t\t\t\tif (\n\t\t\t\t\t\tactivatedCount > 0 &&\n\t\t\t\t\t\tteardownCount >= activatedCount &&\n\t\t\t\t\t\tteardownCount >= channels.length\n\t\t\t\t\t) {\n\t\t\t\t\t\tfinishCleanup();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t},\n\t\t\tsourceOpts({ ...chOpts, name }),\n\t\t);\n\t\tnodes[ch as keyof TChannels] = n as Node<TChannels[typeof ch]>;\n\t\tchannelNodes.push(n as Node<unknown>);\n\t}\n\n\tconst bundle = {} as BundleTriad<TChannels>;\n\tfor (const ch of channels) {\n\t\t(bundle as Record<string, unknown>)[ch] = (value: unknown) => {\n\t\t\tif (!active) return;\n\t\t\t(nodes[ch as keyof TChannels] as Node<unknown>).down([[DATA, value]]);\n\t\t};\n\t}\n\tbundle.error = (err: unknown) => {\n\t\tif (!active) return;\n\t\tactive = false;\n\t\tbatch(() => {\n\t\t\tfor (const n of channelNodes) n.down([[ERROR, err]]);\n\t\t});\n\t\tfinishCleanup();\n\t};\n\tbundle.complete = () => {\n\t\tif (!active) return;\n\t\tactive = false;\n\t\tbatch(() => {\n\t\t\tfor (const n of channelNodes) n.down([[COMPLETE]]);\n\t\t});\n\t\tfinishCleanup();\n\t};\n\n\t// Eager activation — register fires at construction time so externally-\n\t// owned servers can start accepting traffic immediately. Synchronous throws\n\t// propagate to the caller (no subscribers exist yet, so there is no\n\t// reactive ERROR path to deliver to). This matches the existing `fromOTel`\n\t// contract.\n\tconst ret = register(bundle);\n\tcleanup = typeof ret === \"function\" ? ret : undefined;\n\n\tconst dispose = () => {\n\t\tif (!active) return;\n\t\tactive = false;\n\t\t// Fire COMPLETE on every channel so downstream sees a clean terminal.\n\t\tbatch(() => {\n\t\t\tfor (const n of channelNodes) {\n\t\t\t\ttry {\n\t\t\t\t\tn.down([[COMPLETE]]);\n\t\t\t\t} catch {\n\t\t\t\t\t/* terminal filter / re-entrance — swallow */\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tfinishCleanup();\n\t};\n\n\treturn Object.assign(nodes, { dispose });\n}\n","/**\n * Phase 13.C — `selector` + `materialize` composers (DS-13.C / G2 lock C).\n *\n * Two paired primitives for dynamic-mount routing:\n *\n * - {@link selector} — projects an input value to a routing key, deduped.\n * Equivalent to `map + distinctUntilChanged`, but the dedup is the\n * semantic point: the key changes ONLY when the routed-to slot should\n * change. Fires `materialize` re-mounts efficiently.\n *\n * - {@link materialize} — given a reactive `key` and a reactive map of\n * `key → factory` thunks, mounts the matching factory's Graph under\n * `parent` at a stable slot name. When `key` changes, unmounts the old\n * slot and mounts the new factory's output. When `factories` mutates but\n * `key` stays the same, the current slot continues to run on the OLD\n * factory (\"current sessions complete on old factory; new sessions use\n * new factory\" — full hot-swap correctness deferred to G10, parked).\n *\n * Reusable beyond the agent layer:\n * - `harnessLoop` strategy routing — the strategy node IS a `selector`.\n * - `pipelineGraph` dynamic stage selection.\n * - `refineLoop` strategy swap.\n * - Phase 13.I `spawnable()` mounts agent slots via `materialize`.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\tfactoryTag,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\tRESOLVED,\n} from \"@graphrefly/pure-ts/core\";\nimport type { Graph } from \"@graphrefly/pure-ts/graph\";\n\n/** Options for operator nodes: NodeOptions without `describeKind` (set internally). */\nexport type ExtraOpts = Omit<NodeOptions<unknown>, \"describeKind\">;\n\nfunction operatorOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"derived\", ...opts } as NodeOptions<T>;\n}\n\n// ---------------------------------------------------------------------------\n// selector\n// ---------------------------------------------------------------------------\n\n/** Options for {@link selector}. */\nexport type SelectorOpts<TKey> = ExtraOpts & {\n\t/**\n\t * Equality comparator for the projected key. Defaults to {@link Object.is}.\n\t * Used to suppress re-emits when the input changes but the projected key\n\t * does not — this is the load-bearing behavior that lets downstream\n\t * `materialize` skip unnecessary unmount/remount cycles.\n\t */\n\tequals?: (a: TKey, b: TKey) => boolean;\n};\n\n/**\n * Projects each upstream value to a routing key, deduped on the key. The\n * output node emits a key only when the projected key actually changes —\n * pairs cleanly with {@link materialize}, which re-mounts only on key\n * change.\n *\n * **Differs from `map`:** `map(input, fn)` fires on every upstream wave\n * regardless of output value. `selector(input, fn)` fires only when the\n * projected key CHANGES (under `equals`), so downstream re-mount logic is\n * stable.\n *\n * @param input - Upstream node carrying the value to project.\n * @param fn - Synchronous projection function.\n * @param opts - Optional {@link SelectorOpts}.\n * @returns `Node<TKey>` carrying the latest projected key (deduped).\n *\n * @example\n * ```ts\n * import { selector, materialize, state } from \"@graphrefly/graphrefly-ts\";\n *\n * type Request = { kind: \"research\" | \"summarize\" | \"code\"; payload: unknown };\n * const requestNode = state<Request>({ kind: \"research\", payload: {} });\n *\n * const presetId = selector(requestNode, (req) => req.kind);\n * // presetId is `Node<\"research\" | \"summarize\" | \"code\">`, deduped.\n * // Downstream materialize re-mounts ONLY when the kind axis changes.\n * ```\n *\n * @category extra\n */\nexport function selector<TIn, TKey>(\n\tinput: Node<TIn>,\n\tfn: (input: TIn) => TKey,\n\topts?: SelectorOpts<TKey>,\n): Node<TKey> {\n\tconst equals = opts?.equals ?? Object.is;\n\t// Lock 6.D (Phase 13.6.B): clear prev/hasPrev on deactivation so a\n\t// resubscribable selector doesn't dedupe the next cycle's first\n\t// projected key against a stale prev from the prior cycle.\n\tlet cleanup: { onDeactivation: () => void } | undefined;\n\treturn node<TKey>(\n\t\t[input as Node],\n\t\t(data, a, ctx) => {\n\t\t\tif (cleanup === undefined) {\n\t\t\t\tconst store = ctx.store;\n\t\t\t\tcleanup = {\n\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\tdelete store.prev;\n\t\t\t\t\t\tdelete store.hasPrev;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst batch0 = data[0];\n\t\t\tif (batch0 == null || batch0.length === 0) {\n\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\treturn cleanup;\n\t\t\t}\n\t\t\t// A11 (QA fix 2026-05-01): pre-pass — compute every projected\n\t\t\t// key + dedup decision FIRST, surface any user `equals` throw\n\t\t\t// as ERROR before any DATA goes out. The previous in-loop\n\t\t\t// emission interleaved partial DATA with ERROR mid-batch; that\n\t\t\t// left subscribers inconsistent (some had read the early DATA,\n\t\t\t// some treated ERROR as \"discard everything since RESOLVED\")\n\t\t\t// and left `ctx.store.prev` mutated to the last successful key,\n\t\t\t// which made selector \"stuck\" until the next throw-free batch.\n\t\t\tconst toEmit: TKey[] = [];\n\t\t\tlet prev: TKey | undefined = ctx.store.hasPrev ? (ctx.store.prev as TKey) : undefined;\n\t\t\tlet hasPrev = ctx.store.hasPrev;\n\t\t\tfor (const v of batch0 as TIn[]) {\n\t\t\t\tconst key = fn(v);\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\tlet same: boolean;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsame = equals(prev as TKey, key);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t// Pre-pass throw — abandon the whole batch (no DATA emits)\n\t\t\t\t\t\t// and surface ERROR. ctx.store stays at its pre-batch\n\t\t\t\t\t\t// state so the next batch starts from a known-good prev.\n\t\t\t\t\t\ta.down([[ERROR, err]]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (same) continue;\n\t\t\t\t}\n\t\t\t\tprev = key;\n\t\t\t\thasPrev = true;\n\t\t\t\ttoEmit.push(key);\n\t\t\t}\n\t\t\tif (toEmit.length === 0) {\n\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\treturn cleanup;\n\t\t\t}\n\t\t\tctx.store.prev = prev as TKey;\n\t\t\tctx.store.hasPrev = true;\n\t\t\tfor (const k of toEmit) a.emit(k);\n\t\t\treturn cleanup;\n\t\t},\n\t\t{\n\t\t\t...operatorOpts(opts),\n\t\t\tmeta: { ...factoryTag(\"selector\"), ...(opts?.meta ?? {}) },\n\t\t},\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// materialize\n// ---------------------------------------------------------------------------\n\n/**\n * Factory thunk for a {@link materialize} slot. Called once per mount cycle\n * to mint a fresh `TGraph` instance. The instance is mounted under\n * `parent.mount(slotName, ...)` and unmounted via `parent.remove(slotName)`\n * when `key` next changes.\n *\n * Each invocation MUST return a fresh, never-before-mounted Graph instance —\n * `Graph.mount` rejects re-mounting an instance that is already mounted\n * elsewhere in the tree. Caching factory output is unsafe.\n */\nexport type GraphFactory<TGraph extends Graph> = () => TGraph;\n\n/** Options for {@link materialize}. */\nexport type MaterializeOpts = ExtraOpts & {\n\t/**\n\t * Local mount name on the parent graph. Default `\"materialized\"`.\n\t *\n\t * Two materialize calls on the SAME parent must use distinct `slotName`\n\t * values, otherwise the second mount throws \"mount already exists\".\n\t * For a hub mounting many slots (e.g. {@link spawnable}'s preset\n\t * registry), use `slotName: \\`preset-\\${id}\\`` or similar.\n\t */\n\tslotName?: string;\n};\n\n/**\n * Reactive dynamic mount: mounts the Graph instance for `key` under\n * `parent.mount(slotName, ...)`, and re-mounts when `key` changes.\n *\n * **Lifecycle.** First DATA on `key` triggers a mount: look up\n * `factories.get(key)`, call the factory thunk, mount the result under\n * `parent`. Each subsequent `key` change unmounts the previous slot and\n * mounts the new one. When this materialize node terminates (subscriber\n * teardown, `COMPLETE` from `key`, `Graph.destroy`), the active slot is\n * unmounted via `parent.remove(slotName)`.\n *\n * **Hot-swap policy (G10 deferred).** When `factories` mutates but `key`\n * stays the same, the currently-mounted slot is NOT re-instantiated —\n * \"current sessions complete on old factory; new sessions use new\n * factory.\" Atomic disconnect/reconnect of an in-flight slot to a new\n * factory is parked under G10 (see `optimizations.md` \"G10 atomic\n * registry hot-swap\").\n *\n * **Output.** The returned `Node<TGraph>` emits the currently-mounted\n * Graph reference whenever a mount occurs. Consumers can subscribe to\n * watch slot changes, or read `.cache` for the active mount. SENTINEL\n * (no DATA) when no slot is currently mounted (e.g. `key` has no matching\n * factory).\n *\n * **Spec compliance.**\n * - No polling: mount transitions are reactive on `key` / `factories`.\n * - No raw async: factory invocation is synchronous; if a factory needs\n * async setup, it returns a Graph that handles its own setup internally.\n * - Mount/unmount happens as side-effects inside the reactive `subscribe`\n * handler — sanctioned per spec §5.9 (Graph topology mutations are\n * imperative writes at the system boundary, not in-flight reactive\n * triggers).\n *\n * @param key - Reactive routing key. Re-mounts on each key change (deduped\n * by reference; pair with {@link selector} for projection-based dedup).\n * @param factories - Reactive map of `key → factory thunk`. Factory map\n * mutations don't disturb the active slot until the next key change.\n * @param parent - Graph to mount slots under. The `slotName` (default\n * `\"materialized\"`) must be free on `parent` at construction time.\n * @param opts - Optional {@link MaterializeOpts}.\n * @returns `Node<TGraph>` carrying the active mount.\n *\n * @example\n * ```ts\n * import { materialize, selector, state, Graph } from \"@graphrefly/graphrefly-ts\";\n *\n * const parent = new Graph(\"parent\");\n * const factories = state<ReadonlyMap<string, () => Graph>>(new Map([\n * [\"researcher\", () => new ResearchAgentGraph()],\n * [\"coder\", () => new CoderAgentGraph()],\n * ]));\n * const key = selector(requestNode, (r) => r.kind);\n * const slot = materialize(key, factories, parent, { slotName: \"agent\" });\n * // `slot.cache` is the active agent graph; `parent.node(\"agent::out\")`\n * // resolves into whichever agent is currently mounted.\n * ```\n *\n * @category extra\n */\nexport function materialize<TKey, TGraph extends Graph>(\n\tkey: Node<TKey>,\n\tfactories: Node<ReadonlyMap<TKey, GraphFactory<TGraph>>>,\n\tparent: Graph,\n\topts?: MaterializeOpts,\n): Node<TGraph> {\n\tconst slotName = opts?.slotName ?? \"materialized\";\n\treturn node<TGraph>(\n\t\t(_data, a) => {\n\t\t\tlet currentKey: TKey | undefined;\n\t\t\tlet hasCurrentKey = false;\n\t\t\tlet currentGraph: TGraph | undefined;\n\t\t\tlet latestFactories: ReadonlyMap<TKey, GraphFactory<TGraph>> | undefined;\n\t\t\tlet terminated = false;\n\n\t\t\tfunction unmountCurrent(): void {\n\t\t\t\tif (currentGraph === undefined) return;\n\t\t\t\ttry {\n\t\t\t\t\tparent.remove(slotName);\n\t\t\t\t} catch {\n\t\t\t\t\t// Slot already gone (parent destroyed, or external `remove`).\n\t\t\t\t}\n\t\t\t\tcurrentGraph = undefined;\n\t\t\t}\n\n\t\t\t// Closure mirror for the factories map. Subscribed FIRST so\n\t\t\t// `latestFactories` is populated by the time the first `key` DATA\n\t\t\t// arrives. Same §28 factory-time-seed pattern used elsewhere.\n\t\t\tconst facUnsub = factories.subscribe((msgs) => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\tlatestFactories = m[1] as ReadonlyMap<TKey, GraphFactory<TGraph>>;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Primary trigger: key DATA drives mount transitions.\n\t\t\tconst keyUnsub = key.subscribe((msgs) => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\tconst newKey = m[1] as TKey;\n\t\t\t\t\t\tconst keyChanged = !hasCurrentKey || newKey !== currentKey;\n\t\t\t\t\t\tif (keyChanged) {\n\t\t\t\t\t\t\tunmountCurrent();\n\t\t\t\t\t\t\tif (latestFactories !== undefined) {\n\t\t\t\t\t\t\t\tconst factory = latestFactories.get(newKey);\n\t\t\t\t\t\t\t\tif (factory !== undefined) {\n\t\t\t\t\t\t\t\t\tcurrentGraph = factory();\n\t\t\t\t\t\t\t\t\tparent.mount(slotName, currentGraph);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcurrentKey = newKey;\n\t\t\t\t\t\t\thasCurrentKey = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (currentGraph !== undefined) {\n\t\t\t\t\t\t\ta.emit(currentGraph);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (m[0] === COMPLETE) {\n\t\t\t\t\t\tterminated = true;\n\t\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t\t} else if (m[0] === ERROR) {\n\t\t\t\t\t\tterminated = true;\n\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tterminated = true;\n\t\t\t\tkeyUnsub();\n\t\t\t\tfacUnsub();\n\t\t\t\tunmountCurrent();\n\t\t\t};\n\t\t},\n\t\t{\n\t\t\t...operatorOpts(opts),\n\t\t\tmeta: { ...factoryTag(\"materialize\"), slotName, ...(opts?.meta ?? {}) },\n\t\t},\n\t);\n}\n","// ---------------------------------------------------------------------------\n// Observable bridge — reactive interop between GraphReFly nodes and the TC39\n// Observable contract (the well-known `Symbol.observable` / \"@@observable\"\n// method). **Zero runtime dependency on rxjs**: the returned value is a\n// spec-interop observable that rxjs `from()`, Angular, the NestJS compat\n// layer, and any `Symbol.observable` consumer can adopt. Consumers wanting\n// rxjs operators do `from(toObservable(node))`.\n//\n// Usage:\n// import { toObservable } from '@graphrefly/graphrefly/base';\n// import { from } from 'rxjs';\n// const values$ = from(toObservable(myNode)); // Observable<T>\n// const msgs$ = from(toObservable(myNode, { raw: true })); // Observable<Messages>\n// ---------------------------------------------------------------------------\n\nimport type { Node } from \"@graphrefly/pure-ts/core\";\nimport { COMPLETE, DATA, ERROR, type Messages } from \"@graphrefly/pure-ts/core\";\n\n/** Observer passed to {@link InteropObservable.subscribe}. */\nexport interface InteropObserver<T> {\n\tnext?(value: T): void;\n\terror?(err: unknown): void;\n\tcomplete?(): void;\n\t/** rxjs `Subscriber` sets this; we short-circuit delivery when closed. */\n\tclosed?: boolean;\n}\n\n/** Teardown handle returned by {@link InteropObservable.subscribe}. */\nexport interface InteropSubscription {\n\tunsubscribe(): void;\n}\n\n/**\n * Minimal TC39 Observable. rxjs `from()` (and any `Symbol.observable`\n * consumer) adopts it at runtime via the well-known interop method attached\n * by {@link toObservable}. Pass the result through `from(...)` to get a\n * pipeable rxjs `Observable`.\n */\nexport interface InteropObservable<T> {\n\tsubscribe(observer: InteropObserver<T> | ((value: T) => void)): InteropSubscription;\n}\n\n/** Options for {@link toObservable}. */\nexport type ToObservableOptions = {\n\t/**\n\t * When `true`, emit raw `Messages` batches instead of extracted `DATA`\n\t * values. Terminal batches are still emitted as the final `next()` before\n\t * the error/complete signal.\n\t */\n\traw?: boolean;\n};\n\n// Well-known Observable interop key. Mirrors the rxjs / `symbol-observable`\n// resolution (the global `Symbol.observable` when the runtime or a polyfill\n// provides it, otherwise the `\"@@observable\"` string) so rxjs `from()` adopts\n// our object regardless of polyfill state.\nconst OBSERVABLE_KEY: PropertyKey =\n\t(typeof Symbol === \"function\" && (Symbol as unknown as { observable?: symbol }).observable) ||\n\t\"@@observable\";\n\nfunction normalizeObserver<T>(\n\tobserver: InteropObserver<T> | ((value: T) => void),\n): InteropObserver<T> {\n\treturn typeof observer === \"function\" ? { next: observer } : observer;\n}\n\nfunction makeInterop<T>(\n\tonSubscribe: (observer: InteropObserver<T>) => () => void,\n): InteropObservable<T> {\n\tconst obs: InteropObservable<T> = {\n\t\tsubscribe(rawObserver): InteropSubscription {\n\t\t\tconst observer = normalizeObserver(rawObserver);\n\t\t\tlet closed = false;\n\t\t\tlet teardown: (() => void) | undefined;\n\t\t\tlet teardownPending = false;\n\t\t\tconst runTeardown = (): void => {\n\t\t\t\tif (teardown) teardown();\n\t\t\t\telse teardownPending = true; // sync push-on-subscribe terminal\n\t\t\t};\n\t\t\t// Guarded observer: latch `closed` and auto-unsubscribe the node on\n\t\t\t// terminal. The prior rxjs-backed impl got this from rxjs's\n\t\t\t// `Subscriber` (closed flag + teardown-on-terminal); a plain TC39\n\t\t\t// consumer has no such machinery, so without this a post-terminal\n\t\t\t// node wave would re-fire next/error/complete and the node\n\t\t\t// subscription would leak until a manual unsubscribe(). `closed` is\n\t\t\t// also read by toObservable's per-message loop to short-circuit.\n\t\t\tconst guarded: InteropObserver<T> = {\n\t\t\t\tget closed() {\n\t\t\t\t\treturn closed;\n\t\t\t\t},\n\t\t\t\tnext(value) {\n\t\t\t\t\tif (!closed) observer.next?.(value);\n\t\t\t\t},\n\t\t\t\terror(err) {\n\t\t\t\t\tif (closed) return;\n\t\t\t\t\tclosed = true;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tobserver.error?.(err);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\trunTeardown();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tcomplete() {\n\t\t\t\t\tif (closed) return;\n\t\t\t\t\tclosed = true;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tobserver.complete?.();\n\t\t\t\t\t} finally {\n\t\t\t\t\t\trunTeardown();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t\tteardown = onSubscribe(guarded);\n\t\t\tif (teardownPending) teardown(); // terminal fired before assignment\n\t\t\treturn {\n\t\t\t\tunsubscribe() {\n\t\t\t\t\tif (closed) return;\n\t\t\t\t\tclosed = true;\n\t\t\t\t\tteardown?.();\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n\t// TC39 interop: `x[Symbol.observable]()` returns the observable itself.\n\t(obs as unknown as Record<PropertyKey, unknown>)[OBSERVABLE_KEY] = function (\n\t\tthis: InteropObservable<T>,\n\t) {\n\t\treturn this;\n\t};\n\treturn obs;\n}\n\n/**\n * Bridge a `Node<T>` to a TC39 interop observable (no rxjs dependency).\n *\n * Default mode emits the node's value on each `DATA` message. Maps `ERROR` to\n * `observer.error()` and `COMPLETE` to `observer.complete()`.\n * Protocol-internal signals (DIRTY, RESOLVED, PAUSE, etc.) are skipped.\n *\n * With `{ raw: true }`, emits full `[[Type, Data?], ...]` message batches.\n * The stream terminates on ERROR or COMPLETE (the terminal batch is still\n * emitted as the final `next()` before the error/complete signal).\n *\n * The returned value is a spec-interop observable, **not** a concrete rxjs\n * `Observable`. Wrap with `from(toObservable(node))` for rxjs operators, or\n * use the NestJS compat layer's `toObservable` which returns a real rxjs\n * `Observable`. For graph-level observation, use\n * `toObservable(graph.resolve(path))` or subscribe to `graph.observe()`.\n *\n * Unsubscribing unsubscribes the node.\n */\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions?: ToObservableOptions & { raw?: false },\n): InteropObservable<T>;\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions: ToObservableOptions & { raw: true },\n): InteropObservable<Messages>;\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions?: ToObservableOptions,\n): InteropObservable<T | Messages> {\n\tif (options?.raw) {\n\t\treturn makeInterop<Messages>((observer) => {\n\t\t\treturn node.subscribe((msgs) => {\n\t\t\t\tif (observer.closed) return;\n\t\t\t\tobserver.next?.(msgs);\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\t\tobserver.error?.(m[1]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\t\tobserver.complete?.();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\treturn makeInterop<T>((observer) => {\n\t\treturn node.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (observer.closed) return;\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tobserver.next?.(m[1] as T);\n\t\t\t\t} else if (m[0] === ERROR) {\n\t\t\t\t\tobserver.error?.(m[1]);\n\t\t\t\t\treturn;\n\t\t\t\t} else if (m[0] === COMPLETE) {\n\t\t\t\t\tobserver.complete?.();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t});\n}\n","/**\n * Lazy per-topic state hub (roadmap §3.2) — lightweight last-value broadcasts.\n *\n * Each topic is a sentinel `node<unknown>()` with push-on-subscribe replay of\n * the most recent published value (no push until the first `publish`). For\n * Pulsar-inspired retained message logs,\n * cursor-based subscriptions, and job-queue semantics, use `messagingHub()` in\n * `utils/messaging` — built on `TopicGraph` / `SubscriptionGraph` with\n * retention policies, absolute cursor tracking, and per-subscriber state.\n *\n * Presentation layer (base/composition). Moved from pure-ts during cleave A3\n * (no substrate core/graph dependency on pubsub found).\n */\n\nimport { batch, type Node, node, TEARDOWN, wallClockNs } from \"@graphrefly/pure-ts/core\";\nimport {\n\ttype PubSubChange,\n\ttype PubSubChangePayload,\n\ttype ReactiveLogBundle,\n\treactiveLog,\n} from \"@graphrefly/pure-ts/extra\";\n\n// ── Backend interface ─────────────────────────────────────────────────────\n\n/**\n * Storage contract for {@link pubsub} — registry only.\n *\n * Tracks the set of topic names plus a monotonic `version` counter that\n * advances on topic create/remove. Does NOT own per-topic message storage —\n * per-topic cached last values live in the topic nodes themselves (sentinel\n * until the first publish).\n *\n * For distributed / persistent per-topic storage, use `messagingHub()` in\n * `utils/messaging`, which composes `TopicGraph` under a lazy registry.\n *\n * @category base\n */\nexport interface PubSubBackend {\n\t/** Monotonic counter; advances on topic create/remove. */\n\treadonly version: number;\n\treadonly topicCount: number;\n\thasTopic(name: string): boolean;\n\ttopicNames(): IterableIterator<string>;\n\t/** Records topic creation. Returns `true` if newly added (advances `version`). */\n\tcreateTopic(name: string): boolean;\n\t/** Records topic removal. Returns `true` if it existed (advances `version`). */\n\tremoveTopic(name: string): boolean;\n}\n\n/**\n * Default in-memory registry backend.\n *\n * @category base\n */\nexport class NativePubSubBackend implements PubSubBackend {\n\tprivate _version = 0;\n\tprivate readonly _topics = new Set<string>();\n\n\tget version(): number {\n\t\treturn this._version;\n\t}\n\n\tget topicCount(): number {\n\t\treturn this._topics.size;\n\t}\n\n\thasTopic(name: string): boolean {\n\t\treturn this._topics.has(name);\n\t}\n\n\ttopicNames(): IterableIterator<string> {\n\t\treturn this._topics.values();\n\t}\n\n\tcreateTopic(name: string): boolean {\n\t\tif (this._topics.has(name)) return false;\n\t\tthis._topics.add(name);\n\t\tthis._version += 1;\n\t\treturn true;\n\t}\n\n\tremoveTopic(name: string): boolean {\n\t\tconst had = this._topics.delete(name);\n\t\tif (had) this._version += 1;\n\t\treturn had;\n\t}\n}\n\n// ── Hub ───────────────────────────────────────────────────────────────────\n\nexport type PubSubHubOptions = {\n\t/**\n\t * Storage backend. Defaults to `NativePubSubBackend`. Pluggable for audit /\n\t * monitoring / mirror-to-external-broker use cases.\n\t */\n\tbackend?: PubSubBackend;\n\t/**\n\t * DS-14 / DS14R2 — opt-in delta companion. When set, the hub appends a\n\t * typed {@link PubSubChange} record in the **same batch frame** as the\n\t * topic emission / teardown (same-wave consistency — subscribers reading\n\t * both a topic and `mutationLog` never see torn state).\n\t *\n\t * Records the locked `PubSubChange` verbs that apply to this last-value\n\t * hub: `publish` (per `publish` / `publishMany`) and `remove` (per\n\t * `removeTopic`). The `ack` verb is a cursor concern of `messagingHub()`\n\t * and does not apply here. **Note:** the locked `publish` payload carries\n\t * `value` only (no topic name) — callers needing per-topic delta\n\t * correlation should embed identity in the value or use `messagingHub()`.\n\t *\n\t * `true` = defaults; object form forwards `maxSize` / `name` to the inner\n\t * `reactiveLog`.\n\t */\n\tmutationLog?: true | { maxSize?: number; name?: string };\n};\n\n/**\n * Lazy per-topic state hub. Topics are single-value sentinel nodes\n * with push-on-subscribe replay of the most recent publish.\n *\n * @category base\n */\nexport interface PubSubHub {\n\t/**\n\t * Returns the topic node, creating it on first use.\n\t *\n\t * @param name - Topic key.\n\t * @returns `Node` whose value is the last published payload. Starts in\n\t * sentinel state — no push-on-subscribe until the first publish.\n\t */\n\ttopic(name: string): Node<unknown>;\n\t/** Publishes a value to the topic (lazily creating the topic if missing). */\n\tpublish(name: string, value: unknown): void;\n\t/**\n\t * Bulk publish — single outer batch for all entries. No-op if empty.\n\t *\n\t * **Iterable consumption (F6):** `entries` is consumed once (single-pass).\n\t * Pass an array or `Set` for multi-shot callers. Iteration happens INSIDE\n\t * the batch frame — if the iterator throws mid-way, the batch is discarded\n\t * and NO publishes are visible to subscribers (all-or-nothing within one\n\t * call).\n\t */\n\tpublishMany(entries: Iterable<[string, unknown]>): void;\n\t/** Removes a topic; sends `TEARDOWN` to its node. Returns `true` if it existed. */\n\tremoveTopic(name: string): boolean;\n\t/** Checks topic existence without creating. O(1). */\n\thas(name: string): boolean;\n\t/** Number of topics currently registered. O(1). */\n\treadonly size: number;\n\t/** Iterator over topic names. */\n\ttopicNames(): IterableIterator<string>;\n\t/**\n\t * DS14R2 — present iff `mutationLog` was configured. Append-only log of\n\t * `publish` / `remove` deltas, same-wave-consistent with topic emissions.\n\t */\n\treadonly mutationLog?: ReactiveLogBundle<PubSubChange>;\n}\n\n/**\n * Creates a lazy per-topic state hub.\n *\n * @param options - Optional pluggable `backend` (defaults to `NativePubSubBackend`).\n * @returns Hub with lazy `topic()` / `publish()` / `publishMany()` / `removeTopic()` /\n * `has()` / `size` / `topicNames()`.\n *\n * @remarks\n * **Scope:** Each topic is a sentinel node — retains only the last published\n * value (no push-on-subscribe before the first publish). For Pulsar-inspired\n * retention + cursor reading, use `messagingHub()` in `utils/messaging`.\n *\n * **`removeTopic`:** Sends `TEARDOWN` to the topic node; all subscribers receive\n * the TEARDOWN message. Subsequent `publish(name, value)` silently recreates the\n * topic with a fresh node — existing subscribers to the old node do NOT reconnect.\n *\n * @example\n * ```ts\n * import { pubsub } from \"@graphrefly/graphrefly\";\n *\n * const hub = pubsub();\n * const t = hub.topic(\"events\");\n * t.subscribe((msgs) => console.log(msgs));\n * hub.publish(\"events\", { ok: true });\n * hub.publishMany([[\"events\", 1], [\"status\", \"ready\"]]);\n * ```\n *\n * @category base\n */\nexport function pubsub(options: PubSubHubOptions = {}): PubSubHub {\n\tconst { backend: userBackend, mutationLog: mutLogOpt } = options;\n\tconst backend: PubSubBackend = userBackend ?? new NativePubSubBackend();\n\tconst nodes = new Map<string, Node<unknown>>();\n\n\t// ── DS14R2 — mutation log companion ──────────────────────────────────────\n\tconst mutLog: ReactiveLogBundle<PubSubChange> | undefined = mutLogOpt\n\t\t? reactiveLog<PubSubChange>(undefined, {\n\t\t\t\tname: mutLogOpt === true ? \"pubsub.mutationLog\" : (mutLogOpt.name ?? \"pubsub.mutationLog\"),\n\t\t\t\tmaxSize: mutLogOpt === true ? undefined : mutLogOpt.maxSize,\n\t\t\t})\n\t\t: undefined;\n\tlet mutVersion = 0;\n\tfunction recordChange(change: PubSubChangePayload): void {\n\t\tif (!mutLog) return;\n\t\tmutLog.append({\n\t\t\tstructure: \"pubsub\",\n\t\t\tversion: ++mutVersion,\n\t\t\tt_ns: wallClockNs(),\n\t\t\tlifecycle: \"data\",\n\t\t\tchange,\n\t\t});\n\t}\n\n\tfunction ensureTopic(name: string): Node<unknown> {\n\t\tlet n = nodes.get(name);\n\t\tif (n === undefined) {\n\t\t\tn = node<unknown>({ describeKind: \"state\" });\n\t\t\tnodes.set(name, n);\n\t\t\tbackend.createTopic(name);\n\t\t}\n\t\treturn n;\n\t}\n\n\treturn {\n\t\ttopic(name: string): Node<unknown> {\n\t\t\treturn ensureTopic(name);\n\t\t},\n\n\t\tpublish(name: string, value: unknown): void {\n\t\t\tif (!mutLog) {\n\t\t\t\tensureTopic(name).emit(value);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Same-wave: topic emit + change record in one batch frame.\n\t\t\tbatch(() => {\n\t\t\t\tensureTopic(name).emit(value);\n\t\t\t\trecordChange({ kind: \"publish\", value });\n\t\t\t});\n\t\t},\n\n\t\tpublishMany(entries: Iterable<[string, unknown]>): void {\n\t\t\tbatch(() => {\n\t\t\t\tfor (const [name, value] of entries) {\n\t\t\t\t\tensureTopic(name).emit(value);\n\t\t\t\t\trecordChange({ kind: \"publish\", value });\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tremoveTopic(name: string): boolean {\n\t\t\tconst n = nodes.get(name);\n\t\t\tif (n === undefined) return false;\n\t\t\tnodes.delete(name);\n\t\t\tbackend.removeTopic(name);\n\t\t\tif (!mutLog) {\n\t\t\t\tn.down([[TEARDOWN]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbatch(() => {\n\t\t\t\t// QA P3: record BEFORE TEARDOWN. A subscriber wired to both a\n\t\t\t\t// topic and `mutationLog` that self-detaches on TEARDOWN would\n\t\t\t\t// otherwise miss the `remove` delta (same-wave consistency).\n\t\t\t\trecordChange({ kind: \"remove\", name });\n\t\t\t\tn.down([[TEARDOWN]]);\n\t\t\t});\n\t\t\treturn true;\n\t\t},\n\n\t\thas(name: string): boolean {\n\t\t\treturn backend.hasTopic(name);\n\t\t},\n\n\t\tget size(): number {\n\t\t\treturn backend.topicCount;\n\t\t},\n\n\t\ttopicNames(): IterableIterator<string> {\n\t\t\treturn backend.topicNames();\n\t\t},\n\n\t\tmutationLog: mutLog,\n\t};\n}\n","/**\n * `singleFromAny` — keyed promise/Node de-duplication (\"singleflight\").\n *\n * Given a `factory: (key) => NodeInput<T>`, returns a callable that dedupes\n * concurrent invocations sharing the same key — all callers with the same\n * key while a request is in-flight receive the same `Promise<T>`. Once the\n * underlying source settles (DATA, ERROR, or COMPLETE), the cache entry is\n * cleared so the next call re-invokes the factory.\n *\n * This is the classic \"singleflight\" pattern from Go, generalised over the\n * library's `NodeInput<T>` bridge so callers can pass Promise-returning\n * factories, Node-returning factories, or plain value factories with\n * identical semantics.\n *\n * Use cases:\n * - `withReplayCache` cache-miss thundering-herd dedup\n * - Shared HTTP fetches keyed by URL\n * - Expensive compute keyed by request fingerprint\n *\n * @example\n * ```ts\n * const fetchUser = singleFromAny<string, User>((id) => fetch(`/users/${id}`).then(r => r.json()));\n * // Two concurrent callers with id=\"42\" → one underlying fetch, two Promises resolving to the same User.\n * const [a, b] = await Promise.all([fetchUser(\"42\"), fetchUser(\"42\")]);\n * ```\n *\n * @category extra\n */\n\nimport type { Node } from \"@graphrefly/pure-ts/core\";\nimport { COMPLETE, ERROR } from \"@graphrefly/pure-ts/core\";\n// Import directly from the source sub-files (rather than the `./sources.js`\n// barrel) so the `single-from-any` module is NOT part of any cycle that runs\n// through `extra/sources/index.ts` — eager re-exports through the barrel were\n// observed to leave `firstValueFrom` / `keepalive` unresolved during nested\n// import chains under vite-node.\nimport { fromAny, type NodeInput } from \"@graphrefly/pure-ts/extra\";\nimport { firstValueFrom } from \"../sources/settled.js\";\n\nexport interface SingleFromAnyOptions<K> {\n\t/**\n\t * Convert a typed key into a cache-string. Defaults to `String(key)`, which\n\t * works for primitive keys; callers with object keys should provide a\n\t * stable serializer (e.g., canonical JSON).\n\t */\n\tkeyFn?: (key: K) => string;\n}\n\n/**\n * Dedupe concurrent `factory(key)` invocations. Returns a bound callable.\n *\n * @param factory - Produces a `NodeInput<T>` for each unique key.\n * @param opts - Optional key-stringification.\n * @returns A function `(key: K) => Promise<T>` whose inflight results are shared per key.\n */\nexport function singleFromAny<K, T>(\n\tfactory: (key: K) => NodeInput<T>,\n\topts: SingleFromAnyOptions<K> = {},\n): (key: K) => Promise<T> {\n\tconst keyFn = opts.keyFn ?? ((k: K) => String(k));\n\tconst inFlight = new Map<string, Promise<T>>();\n\n\treturn (key: K): Promise<T> => {\n\t\tconst k = keyFn(key);\n\t\tconst existing = inFlight.get(k);\n\t\tif (existing) return existing;\n\n\t\tconst input = factory(key);\n\n\t\t// Resolve the NodeInput to a Promise<T>. Different input shapes need\n\t\t// different bridges — Promise/Node/AsyncIterable/Iterable/plain value.\n\t\tlet rawPromise: Promise<T>;\n\t\tif (input != null && typeof (input as PromiseLike<T>).then === \"function\") {\n\t\t\trawPromise = Promise.resolve(input as PromiseLike<T>);\n\t\t} else if (\n\t\t\tinput != null &&\n\t\t\ttypeof input === \"object\" &&\n\t\t\t\"subscribe\" in (input as object) &&\n\t\t\t\"cache\" in (input as object)\n\t\t) {\n\t\t\t// Node: bridge via firstValueFrom.\n\t\t\trawPromise = firstValueFrom(input as Node<T>);\n\t\t} else if (\n\t\t\tinput != null &&\n\t\t\ttypeof input === \"object\" &&\n\t\t\tSymbol.asyncIterator in (input as object)\n\t\t) {\n\t\t\t// AsyncIterable — take the first value, then close the iterator so\n\t\t\t// any owned resources (HTTP body, subscription, timer) are released.\n\t\t\trawPromise = (async () => {\n\t\t\t\tconst iter = (input as AsyncIterable<T>)[Symbol.asyncIterator]();\n\t\t\t\ttry {\n\t\t\t\t\tconst { value, done } = await iter.next();\n\t\t\t\t\tif (done) throw new Error(\"singleFromAny: factory returned empty async iterable\");\n\t\t\t\t\treturn value as T;\n\t\t\t\t} finally {\n\t\t\t\t\tawait iter.return?.();\n\t\t\t\t}\n\t\t\t})();\n\t\t} else if (input != null && typeof input === \"object\" && Symbol.iterator in (input as object)) {\n\t\t\t// Iterable — take the first value, close the iterator.\n\t\t\trawPromise = (async () => {\n\t\t\t\tconst iter = (input as Iterable<T>)[Symbol.iterator]();\n\t\t\t\ttry {\n\t\t\t\t\tconst { value, done } = iter.next();\n\t\t\t\t\tif (done) throw new Error(\"singleFromAny: factory returned empty iterable\");\n\t\t\t\t\treturn value as T;\n\t\t\t\t} finally {\n\t\t\t\t\titer.return?.();\n\t\t\t\t}\n\t\t\t})();\n\t\t} else {\n\t\t\t// Plain value.\n\t\t\trawPromise = Promise.resolve(input as T);\n\t\t}\n\n\t\t// Install the cache entry BEFORE attaching `.finally`. Otherwise a\n\t\t// sync-resolved Promise's finally microtask could run before the\n\t\t// `inFlight.set` below, leaving a stale entry installed afterwards.\n\t\t// We wrap in a holder whose reference we capture *before* chaining.\n\t\tlet tracked!: Promise<T>;\n\t\tconst cleanup = (): void => {\n\t\t\tif (inFlight.get(k) === tracked) inFlight.delete(k);\n\t\t};\n\t\ttracked = rawPromise.then(\n\t\t\t(v) => {\n\t\t\t\tcleanup();\n\t\t\t\treturn v;\n\t\t\t},\n\t\t\t(e) => {\n\t\t\t\tcleanup();\n\t\t\t\tthrow e;\n\t\t\t},\n\t\t);\n\t\tinFlight.set(k, tracked);\n\t\treturn tracked;\n\t};\n}\n\n/**\n * Reactive variant: returns a bound callable that hands out `Node<T>` values.\n * All concurrent callers with the same key during an in-flight source share\n * the same Node. When the underlying source **terminally** settles (ERROR\n * or COMPLETE), the Node is removed from the cache so the next call\n * re-invokes `factory`. DATA is NOT terminal — callers subscribing after\n * the first DATA still receive the shared Node (and push-on-subscribe per\n * the spec's cached-DATA contract).\n *\n * Use when downstream wants reactive subscription (not a one-shot Promise).\n *\n * @category extra\n */\nexport function singleNodeFromAny<K, T>(\n\tfactory: (key: K) => NodeInput<T>,\n\topts: SingleFromAnyOptions<K> = {},\n): (key: K) => Node<T> {\n\tconst keyFn = opts.keyFn ?? ((k: K) => String(k));\n\tconst inFlight = new Map<string, Node<T>>();\n\n\treturn (key: K): Node<T> => {\n\t\tconst k = keyFn(key);\n\t\tconst existing = inFlight.get(k);\n\t\tif (existing) return existing;\n\n\t\tconst node = fromAny(factory(key));\n\t\tinFlight.set(k, node);\n\n\t\t// Evict on terminal settle only — ERROR or COMPLETE. DATA is a value\n\t\t// emission, not a lifecycle transition; multi-emitting Nodes should\n\t\t// continue to share across subscribers after the first value.\n\t\tconst unsub = node.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === ERROR || m[0] === COMPLETE) {\n\t\t\t\t\tif (inFlight.get(k) === node) inFlight.delete(k);\n\t\t\t\t\tunsub();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn node;\n\t};\n}\n","/**\n * Settled/signal helpers.\n *\n * Moved from extra/sources/settled.ts during cleave A2.\n * `keepalive` is substrate — it lives at `@graphrefly/pure-ts`\n * (`packages/pure-ts/src/extra/sources/_keepalive.ts`), not here.\n */\n\n/**\n * Settled / signal helpers — boundary primitives for converting reactive\n * sources into Promise/AbortSignal endpoints.\n *\n * - {@link firstValueFrom} / {@link firstWhere} — Promise of the first\n * matching DATA.\n * - {@link awaitSettled} — composition over `firstWhere` + reactive\n * `timeout` from `extra/resilience` (lazy import to avoid a\n * resilience → sources cycle).\n * - {@link nodeSignal} — `Node<boolean>` → `AbortSignal` bridge.\n * - {@link reactiveCounter} — capped counter exposed as a `Node<number>`.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\ttype Messages,\n\ttype Node,\n\tnode,\n} from \"@graphrefly/pure-ts/core\";\n\n/**\n * Converts the first `DATA` on `source` into a Promise; rejects on `ERROR` or `COMPLETE` without data.\n *\n * **Important:** This subscribes and waits for a **future** emission. Data that\n * has already flowed is gone and will not be seen. Call this *before* the upstream\n * emits, or use `source.cache` / `source.status` for already-cached state.\n * See COMPOSITION-GUIDE §2 (subscription ordering).\n *\n * @param source - Node to read once.\n * @returns Promise of the first value.\n *\n * @example\n * ```ts\n * import { firstValueFrom, of } from \"@graphrefly/graphrefly-ts\";\n *\n * await firstValueFrom(of(42));\n * ```\n *\n * @category extra\n */\nexport function firstValueFrom<T>(source: Node<T>): Promise<T> {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet shouldUnsub = false;\n\t\tlet unsub: (() => void) | undefined;\n\t\tunsub = source.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (settled) return;\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tresolve(m[1] as T);\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\treject(m[1]);\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\treject(new Error(\"completed without DATA\"));\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tif (shouldUnsub) {\n\t\t\tunsub?.();\n\t\t\tunsub = undefined;\n\t\t}\n\t});\n}\n\n/**\n * Wait for the first DATA value from `source` that satisfies `predicate`.\n *\n * Subscribes directly and resolves on the first DATA value where\n * `predicate` returns true. Reactive, no polling. Use in tests and\n * bridging code where you need a single matching value as a Promise.\n *\n * **Important:** This only captures **future** emissions — data that has\n * already flowed through the node is gone. Call this *before* the upstream\n * emits. For already-cached values, use `source.cache` / `source.status`.\n * See COMPOSITION-GUIDE §2 (subscription ordering).\n *\n * ```ts\n * const val = await firstWhere(strategy.snapshot, snap => snap.size > 0);\n * ```\n *\n * @param source - Upstream node to observe.\n * @param predicate - Returns `true` for the value to resolve on.\n * @param opts - `{ skipCurrent?: boolean }`. When `skipCurrent: true`, any DATA\n * delivered during the synchronous `subscribe()` call (push-on-subscribe §2.2\n * replay of the cached value) is ignored — the promise resolves only on the\n * next future emission. Useful when the caller wants to await the next\n * settlement event after an imperative action (e.g. `run()` minting a new\n * runVersion, where the currently-cached value belongs to the previous run).\n *\n * @category extra\n */\nexport function firstWhere<T>(\n\tsource: Node<T>,\n\tpredicate: (value: T) => boolean,\n\topts?: { skipCurrent?: boolean; kick?: () => void },\n): Promise<T> {\n\t// Lock 3.A (Phase 13.6.B): subscribe synchronously inside the function\n\t// body — NOT inside the Promise executor. Subscribing inside the\n\t// executor would defer the subscription past any synchronous `kick()`\n\t// the caller fires after the call returns, race-losing the very wave\n\t// the caller wants to observe.\n\t//\n\t// To bridge sync-subscribe with the async Promise contract, we record\n\t// any settlement that fires *before* the Promise constructor runs, and\n\t// the executor immediately resolves/rejects with the recorded value.\n\t// Settlements after the executor runs go straight through resolve/reject.\n\ttype Pending =\n\t\t| { kind: \"data\"; value: T }\n\t\t| { kind: \"error\"; err: unknown }\n\t\t| { kind: \"complete\" };\n\tlet pending: Pending | undefined;\n\tlet resolveFn: ((value: T) => void) | undefined;\n\tlet rejectFn: ((err: unknown) => void) | undefined;\n\tlet settled = false;\n\tlet shouldUnsub = false;\n\tlet unsub: (() => void) | undefined;\n\tlet inInitialSyncPhase = opts?.skipCurrent === true;\n\n\t// QA P1: every settler short-circuits when `settled === true` so a\n\t// later settle attempt (e.g. `kick()` throwing AFTER it synchronously\n\t// fired matching DATA, OR a `[DATA matched, ERROR]` wave during\n\t// push-on-subscribe) cannot overwrite an earlier `pending` outcome.\n\t// Without this gate, a kick that races sink-callback settlement could\n\t// reject a Promise the user already has resolved-DATA for.\n\tconst settleData = (v: T): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tif (resolveFn != null) resolveFn(v);\n\t\telse pending = { kind: \"data\", value: v };\n\t};\n\tconst settleError = (err: unknown): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tif (rejectFn != null) rejectFn(err);\n\t\telse pending = { kind: \"error\", err };\n\t};\n\tconst settleComplete = (): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tconst err = new Error(\"completed without matching value\");\n\t\tif (rejectFn != null) rejectFn(err);\n\t\telse pending = { kind: \"complete\" };\n\t};\n\tconst detach = (): void => {\n\t\tif (unsub) {\n\t\t\tunsub();\n\t\t\tunsub = undefined;\n\t\t} else shouldUnsub = true;\n\t};\n\n\tconst sink: (msgs: Messages) => void = (msgs) => {\n\t\tif (settled) return;\n\t\tfor (const m of msgs) {\n\t\t\tif (settled) return;\n\t\t\t// During the initial sync phase, swallow only cached DATA\n\t\t\t// (push-on-subscribe §2.2). Terminal ERROR / COMPLETE must\n\t\t\t// still reject the promise — otherwise an already-terminated\n\t\t\t// source synchronously delivering `[[ERROR, ...]]` or\n\t\t\t// `[[COMPLETE]]` during `subscribe()` would hang forever\n\t\t\t// under `skipCurrent: true`.\n\t\t\tif (inInitialSyncPhase && m[0] === DATA) continue;\n\t\t\tif (m[0] === DATA) {\n\t\t\t\tconst v = m[1] as T;\n\t\t\t\tif (predicate(v)) {\n\t\t\t\t\tsettleData(v);\n\t\t\t\t\tdetach();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (m[0] === ERROR) {\n\t\t\t\tsettleError(m[1]);\n\t\t\t\tdetach();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\tsettleComplete();\n\t\t\t\tdetach();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t};\n\tunsub = source.subscribe(sink);\n\tinInitialSyncPhase = false;\n\t// Lock 3.A: fire `kick` AFTER subscribe is in place. With sync\n\t// subscribe + sync kick, the resulting wave reaches `sink` before\n\t// control returns — making subscribe-before-kick ordering structurally\n\t// impossible to misuse.\n\tif (opts?.kick != null && !settled) {\n\t\ttry {\n\t\t\topts.kick();\n\t\t} catch (err) {\n\t\t\tsettleError(err);\n\t\t\tdetach();\n\t\t}\n\t}\n\tif (shouldUnsub) {\n\t\tunsub?.();\n\t\tunsub = undefined;\n\t}\n\n\treturn new Promise<T>((resolve, reject) => {\n\t\t// If a settlement landed synchronously before the executor runs,\n\t\t// flush it through immediately.\n\t\tif (pending != null) {\n\t\t\tif (pending.kind === \"data\") resolve(pending.value);\n\t\t\telse if (pending.kind === \"error\") reject(pending.err);\n\t\t\telse reject(new Error(\"completed without matching value\"));\n\t\t\treturn;\n\t\t}\n\t\tresolveFn = resolve;\n\t\trejectFn = reject;\n\t});\n}\n\n/**\n * Await the first non-nullish DATA value from `source`, with optional\n * timeout. Composition sugar over `firstWhere` + reactive timeout.\n *\n * Designed as the CLI/boundary sink for reactive pipelines that end in a\n * nullable node (e.g. `promptNode` — per COMPOSITION-GUIDE §8, it emits\n * `null` before it settles with a real value). Replaces the common pattern\n * `firstValueFrom(filter(source, v => v != null))` with a deadline.\n *\n * - Rejects with `TimeoutError` (from `extra/resilience`) if no matching\n * value arrives within `timeoutMs`. Omit `timeoutMs` for unbounded wait.\n * - `predicate` defaults to `v => v != null`. Pass a custom predicate to\n * gate on a stronger condition (e.g. `v => typeof v === \"string\"`).\n * - Pass `skipCurrent: true` to ignore the currently-cached value delivered\n * synchronously via push-on-subscribe and resolve only on the *next*\n * matching emission. Useful after an imperative action that should produce\n * a fresh settlement (e.g. `run()` minting a new version — the stale\n * cached value from the previous run must not resolve the new caller).\n *\n * ```ts\n * const brief = await awaitSettled(briefNode, { timeoutMs: 120_000 });\n * // or with a predicate:\n * const rich = await awaitSettled(node, {\n * predicate: (v): v is MyShape => typeof v === \"object\" && v != null && \"key\" in v,\n * timeoutMs: 60_000,\n * });\n * // or after kicking off a fresh run:\n * kickOff();\n * const fresh = await awaitSettled(resultNode, { skipCurrent: true });\n * ```\n *\n * Reactive inside, sync propagation — the one async boundary is the\n * returned `Promise<T>` (spec §5.10: async belongs at sources and\n * boundaries, not in the graph).\n *\n * @param source - Upstream node to observe.\n * @param opts - `{ predicate?, timeoutMs?, skipCurrent? }`.\n * @returns Promise that resolves with the first matching value, or rejects on timeout / ERROR / COMPLETE-without-DATA.\n *\n * @category extra\n */\n// Lazy module-cache for the `withTimeout` resilience operator. The dynamic\n// import keeps `settled` free of an eager edge into the resilience family;\n// first call pays the one-shot import, subsequent calls hit cached refs.\nlet _timeoutOp: typeof import(\"../resilience/timeout.js\").withTimeout | undefined;\nlet _nsPerMs: number | undefined;\n\nexport async function awaitSettled<T>(\n\tsource: Node<T>,\n\topts?: {\n\t\tpredicate?: (value: T) => boolean;\n\t\ttimeoutMs?: number;\n\t\tskipCurrent?: boolean;\n\t\t/**\n\t\t * Lock 3.A (Phase 13.6.B): fired AFTER subscribe is in place but\n\t\t * BEFORE the helper's async boundary is exposed to the caller.\n\t\t * Subscribe-before-kick is structurally enforced — the kick's\n\t\t * synchronous wave reaches `sink` before control returns. Replaces\n\t\t * the prior load-bearing-comment pattern (M.20-load-bearing) with\n\t\t * a misuse-impossible API shape.\n\t\t *\n\t\t * Common pattern:\n\t\t * await awaitSettled(node, {\n\t\t * skipCurrent: true,\n\t\t * kick: () => producer.emit(value),\n\t\t * });\n\t\t *\n\t\t * Omit `kick` for external-trigger cases where the wave is fired\n\t\t * by code outside the helper's caller; subscribe still lands\n\t\t * synchronously inside the helper body so the next external wave\n\t\t * is not lost.\n\t\t */\n\t\tkick?: () => void;\n\t},\n): Promise<NonNullable<T>> {\n\tconst predicate = opts?.predicate ?? ((v: T) => v != null);\n\tconst skipCurrent = opts?.skipCurrent;\n\tconst kick = opts?.kick;\n\tif (opts?.timeoutMs == null || opts.timeoutMs <= 0) {\n\t\treturn (await firstWhere(source, predicate, { skipCurrent, kick })) as NonNullable<T>;\n\t}\n\t// Reactive composition: `timeout()` wraps the source as a Node that\n\t// emits ERROR(TimeoutError) on deadline. `firstWhere` then resolves on\n\t// the first matching DATA or rejects on that ERROR. One async boundary\n\t// (the returned Promise), everything inside is sync reactive.\n\tif (_timeoutOp === undefined) {\n\t\tconst [timeoutMod, backoff] = await Promise.all([\n\t\t\timport(\"../resilience/timeout.js\"),\n\t\t\timport(\"../resilience/backoff.js\"),\n\t\t]);\n\t\t_timeoutOp = timeoutMod.withTimeout;\n\t\t_nsPerMs = backoff.NS_PER_MS;\n\t}\n\tconst guarded = _timeoutOp(source, { ns: opts.timeoutMs * (_nsPerMs as number) }).node;\n\treturn (await firstWhere(guarded, predicate, { skipCurrent, kick })) as NonNullable<T>;\n}\n\n/**\n * Converts a reactive `Node<boolean>` into a browser-standard `AbortSignal`\n * that fires when the node settles on `true`. Useful for threading a reactive\n * \"cancel\" flag into any async boundary that accepts a signal (fetch, LLM SDK\n * calls, child-process APIs, timers).\n *\n * **Contract.**\n * - `signal.abort(reason)` fires exactly once, on the first DATA emission with\n * a truthy value. Subsequent emissions are ignored (AbortSignal is\n * single-shot).\n * - Null / `false` / sentinel values are ignored. Push-on-subscribe will\n * check the currently-cached value on subscribe and abort immediately if\n * it's already `true`.\n * - `reason` defaults to `\"cancelled via nodeSignal\"`; pass `opts.reason` to\n * override (`DOMException`, `Error`, or any value accepted by\n * `AbortController.abort`).\n *\n * **Lifecycle.**\n * - Returns a `{signal, dispose}` bundle. Call `dispose()` when you're done\n * with the signal (e.g. in a `finally` after the async operation completes).\n * `dispose()` unsubscribes from the node and is a no-op once the signal has\n * fired.\n * - **Memory note:** without `dispose()` the subscription keeps the reactive\n * node alive for the lifetime of the process. For bridge calls inside a\n * `switchMap` project fn, the switchMap supersede tears the inner subgraph\n * down, which is usually the right lifetime — but still call `dispose()`\n * from the caller's `finally` for clarity.\n *\n * @example\n * ```ts\n * const aborted = state(false);\n * const { signal, dispose } = nodeSignal(aborted);\n * try {\n * const resp = await adapter.invoke(msgs, { signal });\n * return resp;\n * } finally {\n * dispose();\n * }\n * ```\n *\n * @category extra\n */\nexport function nodeSignal(\n\tsource: Node<boolean>,\n\topts?: { reason?: unknown },\n): { signal: AbortSignal; dispose: () => void } {\n\tconst ctrl = new AbortController();\n\tconst reason = opts?.reason ?? new Error(\"cancelled via nodeSignal\");\n\tlet unsub: (() => void) | undefined;\n\tlet shouldUnsub = false;\n\tconst done = () => {\n\t\tif (unsub) {\n\t\t\tunsub();\n\t\t\tunsub = undefined;\n\t\t} else shouldUnsub = true;\n\t};\n\tunsub = source.subscribe((msgs) => {\n\t\tif (ctrl.signal.aborted) return;\n\t\tfor (const m of msgs) {\n\t\t\tif (m[0] === DATA && m[1] === true) {\n\t\t\t\tctrl.abort(reason);\n\t\t\t\tdone();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (m[0] === ERROR) {\n\t\t\t\t// Treat an ERROR on the abort source as a cancel signal too —\n\t\t\t\t// a broken control channel should fail closed, not leak the\n\t\t\t\t// in-flight call. Use the error as the abort reason.\n\t\t\t\tctrl.abort(m[1]);\n\t\t\t\tdone();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t// Source completed without aborting — no-op. `done()` already\n\t\t\t\t// released the subscription here, so a later `dispose()` call\n\t\t\t\t// from the caller is a no-op (safe / idempotent).\n\t\t\t\tdone();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t});\n\tif (shouldUnsub) {\n\t\tunsub?.();\n\t\tunsub = undefined;\n\t}\n\treturn {\n\t\tsignal: ctrl.signal,\n\t\tdispose: () => {\n\t\t\tif (unsub) {\n\t\t\t\tunsub();\n\t\t\t\tunsub = undefined;\n\t\t\t}\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// reactiveCounter\n// ---------------------------------------------------------------------------\n\n/** Bundle returned by {@link reactiveCounter}. */\nexport type ReactiveCounterBundle = {\n\t/** Reactive node holding the current count. */\n\treadonly node: Node<number>;\n\t/** Increment by 1. Returns `false` if cap would be exceeded. */\n\tincrement(): boolean;\n\t/** Current count (synchronous read). */\n\tget(): number;\n\t/** Whether the counter has reached its cap. */\n\tatCap(): boolean;\n};\n\n/**\n * Reactive counter with a cap — the building block for circuit breakers.\n *\n * Wraps a `state(0)` node with `increment()` that respects a maximum.\n * The `node` is subscribable and composable like any reactive node. When\n * the cap is reached, `increment()` returns `false`.\n *\n * ```ts\n * const retries = reactiveCounter(10);\n * retries.increment(); // true — count is now 1\n * retries.node.subscribe(...); // reactive updates\n * retries.atCap(); // false\n * ```\n *\n * @param cap - Maximum value (inclusive). 0 = no increments allowed.\n * @category extra\n */\nexport function reactiveCounter(cap: number): ReactiveCounterBundle {\n\tconst counter = node([], { initial: 0 });\n\treturn {\n\t\tnode: counter,\n\t\tincrement() {\n\t\t\tconst current = counter.cache ?? 0;\n\t\t\tif (current >= cap) return false;\n\t\t\tcounter.down([[DIRTY], [DATA, current + 1]]);\n\t\t\treturn true;\n\t\t},\n\t\tget() {\n\t\t\treturn counter.cache ?? 0;\n\t\t},\n\t\tatCap() {\n\t\t\treturn (counter.cache ?? 0) >= cap;\n\t\t},\n\t};\n}\n","/**\n * Composite data patterns (roadmap §3.2b).\n *\n * These helpers compose existing primitives (`node`, `switchMap`, `reactiveMap`,\n * `dynamicNode`, `fromAny`) without introducing new protocol semantics.\n */\n\nimport {\n\tbatch,\n\tDATA,\n\tfactoryTag,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n} from \"@graphrefly/pure-ts/core\";\nimport {\n\tfromAny,\n\tmerge,\n\ttype NodeInput,\n\tswitchMap,\n\twithLatestFrom,\n} from \"@graphrefly/pure-ts/extra\";\nimport { forEach } from \"../sources/async.js\";\n\n// Re-export distill from its canonical module (co-located here pre-split;\n// moved to distill.ts to avoid duplicate-export conflict at the barrel level).\nexport {\n\ttype DistillBundle,\n\ttype DistillOptions,\n\tdistill,\n\ttype Extraction,\n} from \"./distill.js\";\n\n/**\n * Verification payload shape is intentionally user-defined.\n */\nexport type VerifyValue = unknown;\n\nexport type VerifiableOptions<TVerify = VerifyValue> = Omit<\n\tNodeOptions,\n\t\"describeKind\" | \"initial\"\n> & {\n\t/** Reactive re-verification trigger. */\n\ttrigger?: NodeInput<unknown>;\n\t/** Re-run verification whenever `source` settles. */\n\tautoVerify?: boolean;\n\t/** Initial verification companion value. */\n\tinitialVerified?: TVerify | null;\n};\n\nexport type VerifiableBundle<T, TVerify = VerifyValue> = {\n\t/** Coerced source node. */\n\tnode: Node<T>;\n\t/** Latest verification result (`null` before first verification). */\n\tverified: Node<TVerify | null>;\n\t/** Effective trigger node used for verification, if any. */\n\ttrigger: Node<unknown> | null;\n};\n\n/**\n * Composes a value node with a reactive verification companion.\n *\n * Uses `switchMap` so newer triggers cancel stale in-flight verification work.\n */\nexport function verifiable<T, TVerify = VerifyValue>(\n\tsource: NodeInput<T>,\n\tverifyFn: (value: T) => NodeInput<TVerify>,\n\topts?: VerifiableOptions<TVerify>,\n): VerifiableBundle<T, TVerify> {\n\tconst sourceNode = fromAny(source);\n\tconst hasSourceVersioning = sourceNode.v != null;\n\tconst verified = node<TVerify | null>([], {\n\t\tinitial: opts?.initialVerified ?? null,\n\t\tmeta: {\n\t\t\t...factoryTag(\"verifiable\"),\n\t\t\t...(hasSourceVersioning ? { sourceVersion: null } : {}),\n\t\t},\n\t});\n\tconst hasTrigger = opts?.trigger !== undefined && opts.trigger !== null;\n\n\tlet triggerNode: Node<unknown> | null = null;\n\tif (hasTrigger && opts?.autoVerify) {\n\t\ttriggerNode = merge(fromAny(opts.trigger) as Node<unknown>, sourceNode as Node<unknown>);\n\t} else if (hasTrigger) {\n\t\ttriggerNode = fromAny(opts.trigger);\n\t} else if (opts?.autoVerify) {\n\t\ttriggerNode = sourceNode as Node<unknown>;\n\t}\n\n\tif (triggerNode !== null) {\n\t\t// Two patterns depending on trigger shape:\n\t\t// - autoVerify-only (triggerNode === sourceNode): the projected\n\t\t// switchMap value IS the source DATA, pass it directly.\n\t\t// - explicit trigger: `withLatestFrom(trigger, source)` pairs each\n\t\t// trigger emission with the latest source value. Phase 10.5\n\t\t// (`withLatestFrom` flipped to `partial: false`) fixed the W1\n\t\t// initial-pair drop — both deps settle before fn fires, so the\n\t\t// first trigger correctly pairs with the seeded source cache.\n\t\t// Replaces the §28 closure-mirror that was canonical pre-10.5.\n\t\tlet verifyStream: Node<TVerify>;\n\t\tif (triggerNode === (sourceNode as Node<unknown>)) {\n\t\t\tverifyStream = switchMap(sourceNode, (src) => verifyFn(src as T));\n\t\t} else {\n\t\t\tconst paired = withLatestFrom(triggerNode, sourceNode);\n\t\t\tverifyStream = switchMap(paired, ([, source]) => verifyFn(source as T));\n\t\t}\n\t\tforEach(verifyStream, (value) => {\n\t\t\tbatch(() => {\n\t\t\t\tverified.down([[DATA, value]]);\n\t\t\t\t// V0 backfill: stamp which source version was verified (§6.0b).\n\t\t\t\tif (hasSourceVersioning) {\n\t\t\t\t\tconst sv = sourceNode.v;\n\t\t\t\t\tif (sv != null) {\n\t\t\t\t\t\tverified.meta.sourceVersion.down([[DATA, { id: sv.id, version: sv.version }]]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\treturn { node: sourceNode, verified, trigger: triggerNode };\n}\n"],"mappings":"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","names":["import_core","import_extra","import_core","import_extra","node","batch","import_core","sourceOpts","import_core","import_core","node","import_core","import_extra","import_core","import_extra","import_core","node","import_core","import_extra","source"]}
|
|
@@ -2,9 +2,8 @@ import { Messages, NodeOptions, Node } from '@graphrefly/pure-ts/core';
|
|
|
2
2
|
export { D as DistillBundle, a as DistillOptions, E as Extraction, d as distill } from '../../distill-De6Rnn15.cjs';
|
|
3
3
|
export { B as BundleTriad, E as EmitTriad, a as ExternalBundleOptions, b as ExternalRegister, e as externalBundle, c as externalProducer } from '../../external-register-CWyroXb_.cjs';
|
|
4
4
|
import { Graph } from '@graphrefly/pure-ts/graph';
|
|
5
|
-
export { T as ToObservableOptions, t as toObservable } from '../../observable-
|
|
5
|
+
export { I as InteropObservable, a as InteropObserver, b as InteropSubscription, T as ToObservableOptions, t as toObservable } from '../../observable-B25XqCbZ.cjs';
|
|
6
6
|
import { ReactiveLogBundle, PubSubChange, NodeInput } from '@graphrefly/pure-ts/extra';
|
|
7
|
-
import 'rxjs';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Watermark-based backpressure controller — reactive PAUSE/RESUME flow control.
|
|
@@ -2,9 +2,8 @@ import { Messages, NodeOptions, Node } from '@graphrefly/pure-ts/core';
|
|
|
2
2
|
export { D as DistillBundle, a as DistillOptions, E as Extraction, d as distill } from '../../distill-De6Rnn15.js';
|
|
3
3
|
export { B as BundleTriad, E as EmitTriad, a as ExternalBundleOptions, b as ExternalRegister, e as externalBundle, c as externalProducer } from '../../external-register-CWyroXb_.js';
|
|
4
4
|
import { Graph } from '@graphrefly/pure-ts/graph';
|
|
5
|
-
export { T as ToObservableOptions, t as toObservable } from '../../observable-
|
|
5
|
+
export { I as InteropObservable, a as InteropObserver, b as InteropSubscription, T as ToObservableOptions, t as toObservable } from '../../observable-B25XqCbZ.js';
|
|
6
6
|
import { ReactiveLogBundle, PubSubChange, NodeInput } from '@graphrefly/pure-ts/extra';
|
|
7
|
-
import 'rxjs';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Watermark-based backpressure controller — reactive PAUSE/RESUME flow control.
|