@graphrefly/graphrefly 0.47.1 → 0.47.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/dist/base/composition/index.cjs +24 -16
  2. package/dist/base/composition/index.cjs.map +1 -1
  3. package/dist/base/composition/index.js +6 -6
  4. package/dist/base/index.cjs +142 -86
  5. package/dist/base/index.cjs.map +1 -1
  6. package/dist/base/index.js +11 -11
  7. package/dist/base/io/index.cjs +114 -68
  8. package/dist/base/io/index.cjs.map +1 -1
  9. package/dist/base/io/index.js +5 -5
  10. package/dist/base/sources/browser/index.cjs +13 -9
  11. package/dist/base/sources/browser/index.cjs.map +1 -1
  12. package/dist/base/sources/browser/index.js +13 -9
  13. package/dist/base/sources/browser/index.js.map +1 -1
  14. package/dist/base/sources/event/index.cjs +1 -1
  15. package/dist/base/sources/event/index.cjs.map +1 -1
  16. package/dist/base/sources/event/index.js +1 -1
  17. package/dist/base/sources/index.cjs +21 -13
  18. package/dist/base/sources/index.cjs.map +1 -1
  19. package/dist/base/sources/index.js +3 -3
  20. package/dist/base/sources/node/index.cjs +43 -37
  21. package/dist/base/sources/node/index.cjs.map +1 -1
  22. package/dist/base/sources/node/index.js +43 -37
  23. package/dist/base/sources/node/index.js.map +1 -1
  24. package/dist/{chunk-VLAGJZSL.js → chunk-3O3NKZJW.js} +2 -2
  25. package/dist/{chunk-YJ4U2D2C.js → chunk-446I4EGD.js} +9 -7
  26. package/dist/chunk-446I4EGD.js.map +1 -0
  27. package/dist/{chunk-DKNHAICT.js → chunk-5GVURVIG.js} +14 -8
  28. package/dist/chunk-5GVURVIG.js.map +1 -0
  29. package/dist/{chunk-2OB3CEJS.js → chunk-6MRSX3YK.js} +2 -2
  30. package/dist/{chunk-EVYY4X5A.js → chunk-6ZLCPUXS.js} +2 -2
  31. package/dist/{chunk-U225SKB4.js → chunk-7AVQIGF6.js} +61 -10
  32. package/dist/chunk-7AVQIGF6.js.map +1 -0
  33. package/dist/{chunk-7EGRP2VX.js → chunk-7BULJTL6.js} +2 -2
  34. package/dist/{chunk-7EGRP2VX.js.map → chunk-7BULJTL6.js.map} +1 -1
  35. package/dist/{chunk-7ADWWI2T.js → chunk-DDTS7F5O.js} +6 -4
  36. package/dist/chunk-DDTS7F5O.js.map +1 -0
  37. package/dist/{chunk-OCUDSN63.js → chunk-EL5VHUGK.js} +79 -47
  38. package/dist/chunk-EL5VHUGK.js.map +1 -0
  39. package/dist/{chunk-4GYMCUDZ.js → chunk-EP4WVQLX.js} +5 -5
  40. package/dist/{chunk-SOOKUYVM.js → chunk-F7EKHR32.js} +13 -9
  41. package/dist/chunk-F7EKHR32.js.map +1 -0
  42. package/dist/{chunk-RGMTUZCL.js → chunk-FQSQONOU.js} +3 -3
  43. package/dist/{chunk-RAGGHLCV.js → chunk-GUNIRPEJ.js} +8 -6
  44. package/dist/{chunk-RAGGHLCV.js.map → chunk-GUNIRPEJ.js.map} +1 -1
  45. package/dist/{chunk-BU3SEFA5.js → chunk-IOJDYUA7.js} +2 -2
  46. package/dist/{chunk-Y52CS6YA.js → chunk-JA67ZQG2.js} +2 -2
  47. package/dist/{chunk-Y52CS6YA.js.map → chunk-JA67ZQG2.js.map} +1 -1
  48. package/dist/{chunk-DM4OMPWK.js → chunk-KNU73RZW.js} +2 -2
  49. package/dist/{chunk-K7PDZYQE.js → chunk-KRFGO5QH.js} +18 -14
  50. package/dist/{chunk-K7PDZYQE.js.map → chunk-KRFGO5QH.js.map} +1 -1
  51. package/dist/{chunk-Z4YXAUDN.js → chunk-KUFXLAEY.js} +11 -7
  52. package/dist/{chunk-Z4YXAUDN.js.map → chunk-KUFXLAEY.js.map} +1 -1
  53. package/dist/{chunk-YXCPV26R.js → chunk-MS3WPRJR.js} +37 -25
  54. package/dist/chunk-MS3WPRJR.js.map +1 -0
  55. package/dist/{chunk-CXANAIZU.js → chunk-N65E26UL.js} +3 -3
  56. package/dist/{chunk-O3MT7DYI.js → chunk-N6MNJNHB.js} +2 -2
  57. package/dist/{chunk-A7KV5UK4.js → chunk-OXD5LFQP.js} +2 -2
  58. package/dist/{chunk-V4Y3TM7U.js → chunk-PTWADEH3.js} +8 -6
  59. package/dist/chunk-PTWADEH3.js.map +1 -0
  60. package/dist/{chunk-IHTWQEDR.js → chunk-QFE5BQH7.js} +2 -2
  61. package/dist/{chunk-IHTWQEDR.js.map → chunk-QFE5BQH7.js.map} +1 -1
  62. package/dist/{chunk-J5WFUEO4.js → chunk-R6ZCSXKX.js} +3 -3
  63. package/dist/{chunk-PZWISPIQ.js → chunk-S7HN5FHL.js} +17 -11
  64. package/dist/chunk-S7HN5FHL.js.map +1 -0
  65. package/dist/{chunk-RJOG4IJU.js → chunk-T7SP3EYR.js} +18 -12
  66. package/dist/chunk-T7SP3EYR.js.map +1 -0
  67. package/dist/{chunk-4S53H2KR.js → chunk-VAZXUK6G.js} +2 -2
  68. package/dist/{chunk-IJRR6YAI.js → chunk-VLDRAMP7.js} +18 -12
  69. package/dist/chunk-VLDRAMP7.js.map +1 -0
  70. package/dist/{chunk-B4AKFXGE.js → chunk-VNXAF2KE.js} +3 -3
  71. package/dist/{chunk-MTTRCEJT.js → chunk-VP3TIUDF.js} +2 -2
  72. package/dist/{chunk-6XZYT4SW.js → chunk-WGDEBIP4.js} +5 -5
  73. package/dist/{chunk-E5OZPDIW.js → chunk-X7BA5PWG.js} +7 -5
  74. package/dist/chunk-X7BA5PWG.js.map +1 -0
  75. package/dist/compat/index.cjs +1 -1
  76. package/dist/compat/index.cjs.map +1 -1
  77. package/dist/compat/index.js +2 -2
  78. package/dist/compat/nestjs/index.cjs +1 -1
  79. package/dist/compat/nestjs/index.cjs.map +1 -1
  80. package/dist/compat/nestjs/index.js +2 -2
  81. package/dist/index.cjs +283 -142
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.js +32 -32
  84. package/dist/presets/ai/index.cjs +42 -26
  85. package/dist/presets/ai/index.cjs.map +1 -1
  86. package/dist/presets/ai/index.js +10 -10
  87. package/dist/presets/harness/index.cjs +53 -33
  88. package/dist/presets/harness/index.cjs.map +1 -1
  89. package/dist/presets/harness/index.js +21 -21
  90. package/dist/presets/index.cjs +76 -48
  91. package/dist/presets/index.cjs.map +1 -1
  92. package/dist/presets/index.js +24 -24
  93. package/dist/presets/resilience/index.cjs +35 -23
  94. package/dist/presets/resilience/index.cjs.map +1 -1
  95. package/dist/presets/resilience/index.js +5 -5
  96. package/dist/solutions/index.cjs +71 -45
  97. package/dist/solutions/index.cjs.map +1 -1
  98. package/dist/solutions/index.js +21 -21
  99. package/dist/{timeout-U5O4ESK3.js → timeout-BEABACRP.js} +2 -2
  100. package/dist/utils/ai/browser.cjs.map +1 -1
  101. package/dist/utils/ai/browser.js +9 -9
  102. package/dist/utils/ai/index.cjs +41 -25
  103. package/dist/utils/ai/index.cjs.map +1 -1
  104. package/dist/utils/ai/index.js +17 -17
  105. package/dist/utils/ai/node.js +3 -3
  106. package/dist/utils/domain-templates/index.cjs +1 -1
  107. package/dist/utils/domain-templates/index.cjs.map +1 -1
  108. package/dist/utils/domain-templates/index.js +2 -2
  109. package/dist/utils/graphspec/index.cjs +1 -1
  110. package/dist/utils/graphspec/index.cjs.map +1 -1
  111. package/dist/utils/graphspec/index.js +2 -2
  112. package/dist/utils/harness/index.cjs +16 -10
  113. package/dist/utils/harness/index.cjs.map +1 -1
  114. package/dist/utils/harness/index.js +1 -1
  115. package/dist/utils/index.cjs +138 -55
  116. package/dist/utils/index.cjs.map +1 -1
  117. package/dist/utils/index.js +21 -21
  118. package/dist/utils/memory/index.cjs +51 -0
  119. package/dist/utils/memory/index.cjs.map +1 -1
  120. package/dist/utils/memory/index.d.cts +58 -9
  121. package/dist/utils/memory/index.d.ts +58 -9
  122. package/dist/utils/memory/index.js +1 -1
  123. package/dist/utils/orchestration/index.cjs +5 -3
  124. package/dist/utils/orchestration/index.cjs.map +1 -1
  125. package/dist/utils/orchestration/index.js +1 -1
  126. package/dist/utils/process/index.js +2 -2
  127. package/dist/utils/reduction/index.cjs +1 -1
  128. package/dist/utils/reduction/index.cjs.map +1 -1
  129. package/dist/utils/reduction/index.js +1 -1
  130. package/dist/utils/resilience/index.cjs +35 -23
  131. package/dist/utils/resilience/index.cjs.map +1 -1
  132. package/dist/utils/resilience/index.js +4 -4
  133. package/dist/utils/surface/index.cjs +1 -1
  134. package/dist/utils/surface/index.cjs.map +1 -1
  135. package/dist/utils/surface/index.js +3 -3
  136. package/package.json +1 -1
  137. package/dist/chunk-7ADWWI2T.js.map +0 -1
  138. package/dist/chunk-DKNHAICT.js.map +0 -1
  139. package/dist/chunk-E5OZPDIW.js.map +0 -1
  140. package/dist/chunk-IJRR6YAI.js.map +0 -1
  141. package/dist/chunk-OCUDSN63.js.map +0 -1
  142. package/dist/chunk-PZWISPIQ.js.map +0 -1
  143. package/dist/chunk-RJOG4IJU.js.map +0 -1
  144. package/dist/chunk-SOOKUYVM.js.map +0 -1
  145. package/dist/chunk-U225SKB4.js.map +0 -1
  146. package/dist/chunk-V4Y3TM7U.js.map +0 -1
  147. package/dist/chunk-YJ4U2D2C.js.map +0 -1
  148. package/dist/chunk-YXCPV26R.js.map +0 -1
  149. /package/dist/{chunk-VLAGJZSL.js.map → chunk-3O3NKZJW.js.map} +0 -0
  150. /package/dist/{chunk-2OB3CEJS.js.map → chunk-6MRSX3YK.js.map} +0 -0
  151. /package/dist/{chunk-EVYY4X5A.js.map → chunk-6ZLCPUXS.js.map} +0 -0
  152. /package/dist/{chunk-4GYMCUDZ.js.map → chunk-EP4WVQLX.js.map} +0 -0
  153. /package/dist/{chunk-RGMTUZCL.js.map → chunk-FQSQONOU.js.map} +0 -0
  154. /package/dist/{chunk-BU3SEFA5.js.map → chunk-IOJDYUA7.js.map} +0 -0
  155. /package/dist/{chunk-DM4OMPWK.js.map → chunk-KNU73RZW.js.map} +0 -0
  156. /package/dist/{chunk-CXANAIZU.js.map → chunk-N65E26UL.js.map} +0 -0
  157. /package/dist/{chunk-O3MT7DYI.js.map → chunk-N6MNJNHB.js.map} +0 -0
  158. /package/dist/{chunk-A7KV5UK4.js.map → chunk-OXD5LFQP.js.map} +0 -0
  159. /package/dist/{chunk-J5WFUEO4.js.map → chunk-R6ZCSXKX.js.map} +0 -0
  160. /package/dist/{chunk-4S53H2KR.js.map → chunk-VAZXUK6G.js.map} +0 -0
  161. /package/dist/{chunk-B4AKFXGE.js.map → chunk-VNXAF2KE.js.map} +0 -0
  162. /package/dist/{chunk-MTTRCEJT.js.map → chunk-VP3TIUDF.js.map} +0 -0
  163. /package/dist/{chunk-6XZYT4SW.js.map → chunk-WGDEBIP4.js.map} +0 -0
  164. /package/dist/{timeout-U5O4ESK3.js.map → timeout-BEABACRP.js.map} +0 -0
@@ -4,7 +4,7 @@ import {
4
4
  humanInput,
5
5
  pipelineGraph,
6
6
  tracker
7
- } from "../../chunk-7ADWWI2T.js";
7
+ } from "../../chunk-DDTS7F5O.js";
8
8
  import "../../chunk-NPRP3MCV.js";
9
9
  import "../../chunk-FMPF42Q4.js";
10
10
  import "../../chunk-BXGZFGZ4.js";
@@ -2,9 +2,9 @@ import {
2
2
  processInstanceKeyOf,
3
3
  processManager,
4
4
  processStateKeyOf
5
- } from "../../chunk-EVYY4X5A.js";
5
+ } from "../../chunk-6ZLCPUXS.js";
6
6
  import "../../chunk-BXGZFGZ4.js";
7
- import "../../chunk-O3MT7DYI.js";
7
+ import "../../chunk-N6MNJNHB.js";
8
8
  import "../../chunk-AZDQPQ3V.js";
9
9
  export {
10
10
  processInstanceKeyOf,
@@ -139,7 +139,7 @@ function feedback(graph, condition, reentry, opts) {
139
139
  }
140
140
  }
141
141
  });
142
- return () => unsub();
142
+ return { onDeactivation: () => unsub() };
143
143
  },
144
144
  {
145
145
  name: feedbackEffectName,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/reduction/index.ts","../../../src/base/meta/domain-meta.ts","../../../src/base/mutation/index.ts"],"sourcesContent":["/**\n * Reduction primitives (roadmap §8.1).\n *\n * Composable building blocks for taking heterogeneous massive inputs and producing\n * prioritized, auditable, human-actionable output. Each primitive is either a Graph\n * factory or a Node factory, built on top of core + extra primitives.\n *\n * @module\n */\n\nimport {\n\tbatch,\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\ttype Message,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n} from \"@graphrefly/pure-ts/core\";\n\nimport { merge } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\n\n// ---------------------------------------------------------------------------\n// Shared helpers (same pattern as orchestration.ts)\n// ---------------------------------------------------------------------------\n\nexport type { StepRef } from \"../orchestration/pipeline-graph.js\";\n\nimport { keepalive } from \"@graphrefly/pure-ts/extra\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport { tryIncrementBounded } from \"../../base/mutation/index.js\";\nimport type { StepRef } from \"../orchestration/pipeline-graph.js\";\n\nfunction baseMeta(kind: string, meta?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"reduction\", kind, meta);\n}\n\n// stratify moved to `src/extra/stratify.ts` (protocol-level primitive).\n\n// ---------------------------------------------------------------------------\n// funnel\n// ---------------------------------------------------------------------------\n\n/** A named stage for {@link funnel}. */\nexport type FunnelStage = {\n\t/** Stage name (mounted as subgraph). */\n\tname: string;\n\t/** Builder: receives a sub-graph, should add an `\"input\"` and `\"output\"` node. */\n\tbuild: (sub: Graph) => void;\n};\n\n/** Options for {@link funnel}. */\nexport type FunnelOptions = GraphOptions & {\n\tmeta?: Record<string, unknown>;\n};\n\n/**\n * Multi-source merge with sequential reduction stages.\n *\n * Sources are merged into a single stream. Each stage is a named subgraph\n * (mounted via `graph.mount()`). Stages connect linearly:\n * `merged → stage[0].input → stage[0].output → stage[1].input → ...`\n *\n * @param name - Graph name.\n * @param sources - Input nodes to merge.\n * @param stages - Sequential reduction stages.\n * @param opts - Optional graph/meta options.\n * @returns Graph with `\"merged\"` and mounted stage subgraphs.\n *\n * @category patterns\n */\nexport function funnel<T>(\n\tname: string,\n\tsources: ReadonlyArray<Node<T>>,\n\tstages: ReadonlyArray<FunnelStage>,\n\topts?: FunnelOptions,\n): Graph {\n\tif (sources.length === 0) throw new RangeError(\"funnel requires at least one source\");\n\tif (stages.length === 0) throw new RangeError(\"funnel requires at least one stage\");\n\n\tconst g = new Graph(name, opts);\n\n\t// Merge all sources\n\tconst merged = sources.length === 1 ? sources[0] : merge(...(sources as unknown as Node<T>[]));\n\tg.add(merged as Node<unknown>, { name: \"merged\" });\n\n\t// Build and mount each stage linearly.\n\t// Stage inputs are standalone state nodes, so we bridge via subscribe\n\t// (connect() requires constructor deps). Bridge effects forward DATA\n\t// from the previous output to the next stage's input.\n\tlet prevOutputPath = \"merged\";\n\tfor (let i = 0; i < stages.length; i++) {\n\t\tconst stage = stages[i];\n\t\tconst sub = new Graph(stage.name);\n\t\tstage.build(sub);\n\n\t\t// Validate that the stage has input and output nodes\n\t\ttry {\n\t\t\tsub.resolve(\"input\");\n\t\t} catch {\n\t\t\tthrow new Error(`funnel stage \"${stage.name}\" must define an \"input\" node`);\n\t\t}\n\t\ttry {\n\t\t\tsub.resolve(\"output\");\n\t\t} catch {\n\t\t\tthrow new Error(`funnel stage \"${stage.name}\" must define an \"output\" node`);\n\t\t}\n\n\t\tg.mount(stage.name, sub);\n\n\t\t// Bridge replacement: effect that forwards DATA from previous output\n\t\t// to the next stage's input. TEARDOWN excluded because stage lifecycle\n\t\t// is managed by the parent graph. Shows up in describe().\n\t\tconst prevNode = g.resolve(prevOutputPath);\n\t\tconst stageInputPath = `${stage.name}::input`;\n\t\tconst stageInput = g.resolve(stageInputPath);\n\t\tconst bridgeName = `__bridge_${prevOutputPath}→${stage.name}_input`;\n\t\tconst br = node(\n\t\t\t[prevNode],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tstageInput.emit(data[0]);\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t\t{ describeKind: \"effect\", name: bridgeName },\n\t\t);\n\t\tg.add(br as Node<unknown>, { name: bridgeName });\n\t\tg.addDisposer(keepalive(br));\n\n\t\tprevOutputPath = `${stage.name}::output`;\n\t}\n\n\treturn g;\n}\n\n// ---------------------------------------------------------------------------\n// feedback\n// ---------------------------------------------------------------------------\n\n/** Options for {@link feedback}. */\nexport type FeedbackOptions = {\n\t/** Maximum feedback iterations before stopping (default: 10). */\n\tmaxIterations?: number;\n\t/** Optional budget gate node path for cost-bounded iteration. */\n\tbudgetNode?: StepRef;\n\tmeta?: Record<string, unknown>;\n};\n\n/**\n * Introduce a bounded reactive cycle into an existing graph.\n *\n * When `condition` emits a non-null DATA value, the feedback effect routes it\n * back to the `reentry` state node — creating a cycle. Bounded by\n * `maxIterations` (default 10). The counter node (`__feedback_<condition>`)\n * is the source of truth — reset it to 0 to allow more iterations.\n *\n * To remove the feedback cycle, call `graph.remove(\"__feedback_<condition>\")`.\n *\n * @param graph - Existing graph to augment with a feedback cycle.\n * @param condition - Path to a node whose DATA triggers feedback.\n * @param reentry - Path to a state node that receives the feedback value.\n * @param opts - Iteration bounds and metadata.\n * @returns The same graph (mutated with feedback nodes added).\n *\n * @category patterns\n */\nexport function feedback(\n\tgraph: Graph,\n\tcondition: string,\n\treentry: string,\n\topts?: FeedbackOptions,\n): Graph {\n\tconst maxIter = opts?.maxIterations ?? 10;\n\n\t// Internal counter node — source of truth for iteration bound.\n\t// Reset to 0 to allow more iterations.\n\tconst counterName = `__feedback_${condition}`;\n\tconst counter = node<number>([], {\n\t\t...{\n\t\t\tmeta: baseMeta(\"feedback_counter\", {\n\t\t\t\tmaxIterations: maxIter,\n\t\t\t\tfeedbackFrom: condition,\n\t\t\t\tfeedbackTo: reentry,\n\t\t\t}),\n\t\t},\n\t\tinitial: 0,\n\t});\n\tgraph.add(counter as Node<unknown>, { name: counterName });\n\n\t// Resolve the condition and reentry nodes\n\tconst condNode = graph.resolve(condition);\n\tconst reentryNode = graph.resolve(reentry);\n\n\t// Graph-visible feedback effect: intercepts condition DATA, routes back to\n\t// reentry with iteration counting. Registered in the graph so it shows up\n\t// in describe() and cleans up on graph.destroy().\n\t// Feedback effect: subscribe to condition node for per-message interception\n\t// (onMessage removed in v5 — use producer+subscribe instead)\n\tconst feedbackEffectName = `__feedback_effect_${condition}`;\n\tconst feedbackEffect = node(\n\t\t[],\n\t\t(_data, _feedbackActions) => {\n\t\t\tconst unsub = condNode.subscribe((msgs) => {\n\t\t\t\tfor (const msg of msgs) {\n\t\t\t\t\tconst t = msg[0];\n\t\t\t\t\tif (t === DATA) {\n\t\t\t\t\t\tconst condValue = msg[1];\n\t\t\t\t\t\tif (condValue == null) return;\n\t\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\t\tif (tryIncrementBounded(counter, maxIter)) {\n\t\t\t\t\t\t\t\treentryNode.emit(condValue);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (t === COMPLETE || t === ERROR) {\n\t\t\t\t\t\tconst terminal: Message = t === ERROR && msg.length > 1 ? [ERROR, msg[1]] : [t];\n\t\t\t\t\t\tcounter.down([terminal]);\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\t{\n\t\t\tname: feedbackEffectName,\n\t\t\tdescribeKind: \"effect\",\n\t\t\tmeta: {\n\t\t\t\t...baseMeta(\"feedback_effect\", {\n\t\t\t\t\tfeedbackFrom: condition,\n\t\t\t\t\tfeedbackTo: reentry,\n\t\t\t\t}),\n\t\t\t\t_internal: true,\n\t\t\t},\n\t\t},\n\t);\n\tgraph.add(feedbackEffect as Node<unknown>, { name: feedbackEffectName });\n\tgraph.addDisposer(keepalive(feedbackEffect));\n\n\treturn graph;\n}\n\n// `budgetGate` was promoted to `extra/resilience/budget-gate.ts` per Tier 2.2.\n// Import from `@graphrefly/graphrefly/extra` (or `../../extra/resilience/budget-gate.js`\n// internally) instead. See the resilience family for sibling primitives:\n// `retry`, `circuitBreaker`, `rateLimiter`, `tokenBucket`, `fallback`, `withStatus`.\n\n// ---------------------------------------------------------------------------\n// scorer\n// ---------------------------------------------------------------------------\n\n/** A scored item with full breakdown. */\nexport type ScoredItem<T = unknown> = {\n\t/** Original value. */\n\tvalue: T;\n\t/** Final weighted score. */\n\tscore: number;\n\t/** Per-signal breakdown: signal index → weighted contribution. */\n\tbreakdown: number[];\n};\n\n/** Options for {@link scorer}. */\nexport type ScorerOptions = Omit<NodeOptions<unknown>, \"describeKind\" | \"name\" | \"meta\"> & {\n\tmeta?: Record<string, unknown>;\n\t/** Custom scoring function per signal. Default: identity (signal value IS the score). */\n\tscoreFns?: ReadonlyArray<(value: unknown) => number>;\n};\n\n/**\n * Reactive multi-signal scoring with live weights.\n *\n * Each source emits items to score. Weights are reactive state nodes that\n * LLM or human can adjust live. Output is sorted scored items with full\n * breakdown.\n *\n * @param sources - Signal nodes (each emits a numeric score dimension).\n * @param weights - Reactive weight nodes (one per source).\n * @param opts - Optional node/meta options.\n * @returns Node emitting scored output.\n *\n * @category patterns\n */\nexport function scorer(\n\tsources: ReadonlyArray<Node<number>>,\n\tweights: ReadonlyArray<Node<number>>,\n\topts?: ScorerOptions,\n): Node<ScoredItem<number[]>> {\n\tif (sources.length === 0) throw new RangeError(\"scorer requires at least one source\");\n\tif (sources.length !== weights.length) {\n\t\tthrow new RangeError(\"scorer requires the same number of sources and weights\");\n\t}\n\n\tconst allDeps = [...(sources as unknown as Node[]), ...(weights as unknown as Node[])];\n\tconst n = sources.length;\n\tconst scoreFns = opts?.scoreFns;\n\n\treturn node<ScoredItem<number[]>>(\n\t\tallDeps,\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst vals = 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 signals = vals.slice(0, n) as number[];\n\t\t\tconst weightValues = vals.slice(n) as number[];\n\n\t\t\tconst breakdown: number[] = [];\n\t\t\tlet totalScore = 0;\n\n\t\t\tfor (let i = 0; i < n; i++) {\n\t\t\t\tconst sig = signals[i] ?? 0;\n\t\t\t\tconst wt = weightValues[i] ?? 0;\n\t\t\t\tconst rawScore = scoreFns?.[i] ? scoreFns[i](sig) : sig;\n\t\t\t\tconst weighted = (rawScore as number) * wt;\n\t\t\t\tbreakdown.push(weighted);\n\t\t\t\ttotalScore += weighted;\n\t\t\t}\n\n\t\t\tactions.emit({\n\t\t\t\tvalue: signals,\n\t\t\t\tscore: totalScore,\n\t\t\t\tbreakdown,\n\t\t\t});\n\t\t},\n\t\t{\n\t\t\t...(opts\n\t\t\t\t? {\n\t\t\t\t\t\tequals: opts.equals,\n\t\t\t\t\t\tresubscribable: opts.resubscribable,\n\t\t\t\t\t\tresetOnTeardown: opts.resetOnTeardown,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\tdescribeKind: \"derived\",\n\t\t\tmeta: baseMeta(\"scorer\", opts?.meta),\n\t\t},\n\t);\n}\n\n// `effectivenessTracker` was deleted per Class B audit Alt E (2026-04-30).\n// The shared substrate now lives in `extra/composition/audited-success-tracker.ts`\n// — re-exported via `@graphrefly/graphrefly-ts/extra` for general use.\n","/**\n * Metadata helpers for pattern-layer nodes (Tier 2.2 promotion from\n * `patterns/_internal/`).\n *\n * Each domain (orchestration, messaging, reduction, ai, cqrs, domain_template,\n * memory, lens, audit, harness) shares the same metadata convention. Promoted\n * to `extra/` so non-patterns code (and downstream consumers building their\n * own domain primitives) can use the same shape.\n *\n * @module\n */\n\n/**\n * Build a domain metadata object for pattern-layer nodes.\n *\n * Each domain follows the same shape: `{ [domain]: true, [domain]_type: kind, ...extra }`.\n *\n * @param domain - The domain tag (e.g. `\"orchestration\"`, `\"ai\"`, `\"cqrs\"`).\n * @param kind - The specific type within the domain (e.g. `\"gate\"`, `\"prompt\"`).\n * @param extra - Additional metadata to merge.\n * @returns Metadata object.\n */\nexport function domainMeta(\n\tdomain: string,\n\tkind: string,\n\textra?: Record<string, unknown>,\n): Record<string, unknown> {\n\treturn {\n\t\t[domain]: true,\n\t\t[`${domain}_type`]: kind,\n\t\t...(extra ?? {}),\n\t};\n}\n","/**\n * Universal mutation framework (Phase 14 — DS-14 locked 2026-05-05).\n *\n * Single `mutate(act, opts)` factory replaces the prior `lightMutation` +\n * `wrapMutation` two-tier split (pre-1.0 break per Q-O2).\n *\n * Two frames:\n * - `\"inline\"` — no batch; up() runs raw. Seq bumps before action; persists\n * on throw. Hot-path-friendly for atomic single-write mutations.\n * - `\"transactional\"` — opens `batch(() => up(...))`. On throw: batch discards\n * deferred deliveries, then `down()` runs (if provided), then failure record.\n *\n * Phase-4 primitives share the same shape: imperative mutation methods +\n * closure state + reactive audit log + freeze-at-entry + rollback-on-throw.\n * This module factors out the common machinery so each primitive becomes\n * declarative wiring over typed audit records.\n */\n\nimport {\n\tbatch,\n\tDATA,\n\tDIRTY,\n\ttype Node,\n\ttype NodeGuard,\n\tnode,\n\tpolicy,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport {\n\ttype ReactiveLogBundle,\n\ttype ReactiveLogOptions,\n\treactiveLog,\n} from \"@graphrefly/pure-ts/extra\";\nimport { Graph } from \"@graphrefly/pure-ts/graph\";\n\n// ── tryIncrementBounded ──────────────────────────────────────────────────\n\n/**\n * Bounded increment for a self-owned counter state node.\n *\n * Reads `counter.cache`, bumps by `by` (default 1) if `cur + by <= cap`,\n * writes back. Returns `false` when the cap would be exceeded (no-op write).\n * Documented P3 exception: the counter is not a declared dep of the caller —\n * it's a private budget read+written from a single call site. This helper\n * keeps the `.cache` access in one named place so caller bodies (which may\n * be inside reactive fn execution paths) stay free of cross-node `.cache`\n * reads.\n *\n * **Safety today:**\n * 1. Single-threaded JS runner never invokes the caller concurrently.\n * 2. `counter.down` writes the cache synchronously before returning, so\n * synchronous re-entry through a downstream publish reads the\n * freshly-incremented value — no double-count.\n *\n * **Future risk:** under a free-threaded runner (PY no-GIL or hypothetical\n * concurrent TS runner), two concurrent firings could still race. Revisit\n * when that surfaces.\n *\n * @param counter - Self-owned counter Node. Caller is the sole writer.\n * @param cap - Upper bound (inclusive). Pass `Number.MAX_SAFE_INTEGER` for\n * \"effectively unbounded\" use cases (e.g. token meters).\n * @param by - Delta to add (default `1`). Must be a finite non-negative\n * number; callers should pre-validate. Overflow-safe via\n * `by > cap - cur` check rather than `cur + by >= cap`.\n */\nexport function tryIncrementBounded(counter: Node<number>, cap: number, by = 1): boolean {\n\tconst cur = (counter.cache as number | undefined) ?? 0;\n\tif (by > cap - cur) return false;\n\tcounter.down([[DIRTY], [DATA, cur + by]]);\n\treturn true;\n}\n\n// ── Audit record schema ──────────────────────────────────────────────────\n\n/** Shared base shape for every audit record. Per-primitive types extend this. */\nexport interface BaseAuditRecord {\n\treadonly t_ns: number;\n\treadonly seq?: number;\n\treadonly handlerVersion?: { id: string; version: string | number };\n}\n\n// ── Default audit guard ──────────────────────────────────────────────────\n\n/**\n * Allow `observe` and `signal`; deny external `write` on the audit log so\n * consumers can subscribe + signal-bridge but cannot inject fake records.\n */\nexport const DEFAULT_AUDIT_GUARD: NodeGuard = policy((allow, deny) => {\n\tallow(\"observe\");\n\tallow(\"signal\");\n\tdeny(\"write\");\n});\n\n// ── createAuditLog ───────────────────────────────────────────────────────\n\nexport type AuditLogOpts<R extends BaseAuditRecord> = {\n\tname: string;\n\t/** Bounded retention; default 1024 per Audit 2 / cross-cutting bounded-default policy. */\n\tretainedLimit?: number;\n\t/** Override the default audit guard. */\n\tguard?: NodeGuard;\n\t/** Mount the audit `entries` Node under this graph (and activate withLatest). */\n\tgraph?: Graph;\n\t/** Pass-through to {@link reactiveLog}. */\n\tversioning?: ReactiveLogOptions<R>[\"versioning\"];\n};\n\n/**\n * Build a reactive audit log with sane defaults: bounded retention, deny-write\n * guard, `withLatest()` companions activated. Returns the {@link ReactiveLogBundle}\n * directly — primitives expose this as `<primitive>.events` / `.decisions` /\n * `.dispatches` / `.invocations` and alias it as `.audit`.\n *\n * @category internal\n */\nexport function createAuditLog<R extends BaseAuditRecord>(\n\topts: AuditLogOpts<R>,\n): ReactiveLogBundle<R> {\n\tconst log = reactiveLog<R>([], {\n\t\tname: opts.name,\n\t\tmaxSize: opts.retainedLimit ?? 1024,\n\t\tguard: opts.guard ?? DEFAULT_AUDIT_GUARD,\n\t\t...(opts.versioning != null ? { versioning: opts.versioning } : {}),\n\t});\n\t// Lazy companion activation up-front so `bundle.lastValue` / `hasLatest`\n\t// are queryable without an explicit `withLatest()` call.\n\tlog.withLatest();\n\tif (opts.graph) {\n\t\topts.graph.add(log.entries, { name: opts.name });\n\t}\n\treturn log;\n}\n\n// ── Universal mutation factory (Phase 14 — DS-14 lock Q-O2/Q-O3) ────────\n//\n// Single `mutate(act, opts)` factory. Two frames:\n//\n// - `\"inline\"` — no batch frame; up() runs raw. Seq bumps before action;\n// persists on throw. Hot-path-friendly for atomic single-write mutations.\n//\n// - `\"transactional\"` — opens `batch(() => up(...))`. On throw: batch discards\n// deferred deliveries, then `down()` runs, then failure record persists.\n//\n// **Heuristic:** if your imperative method's body is one or two lines (mutate\n// state, emit), use `frame: \"inline\"`. If it runs a user-supplied handler or\n// has multiple steps that could leave inconsistent state mid-throw, use\n// `frame: \"transactional\"`.\n\nexport type FailureMeta = {\n\tt_ns: number;\n\tseq?: number;\n\terrorType: string;\n};\n\nexport type SuccessMeta = {\n\tt_ns: number;\n\tseq?: number;\n};\n\n/**\n * Mutation action shape. Plain function shorthand auto-wraps as `{ up: fn }`.\n *\n * - `up` — the mutation action (the \"up migration\").\n * - `down` — optional rollback for closure mutations that `batch()` can't\n * reach. Receives the SAME frozen args as `up`. Runs AFTER batch reactive\n * rollback, BEFORE the failure record. Throws inside `down` are\n * console.error'd without masking the original error. Only meaningful\n * with `frame: \"transactional\"`.\n */\nexport type MutationAct<TArgs extends readonly unknown[], TResult> = {\n\tup: (...args: TArgs) => TResult;\n\tdown?: (...args: TArgs) => void;\n};\n\nexport type MutationFrame = \"inline\" | \"transactional\";\n\nexport type MutateOpts<TArgs extends readonly unknown[], TResult, R extends BaseAuditRecord> = {\n\t/** Frame mode. `\"inline\"` = no batch; `\"transactional\"` = batch + rollback. */\n\tframe: MutationFrame;\n\t/**\n\t * Optional log to append records to. When omitted, the wrapper still\n\t * provides freeze / seq-advance / rollback-on-throw but skips record\n\t * emission — useful for primitives that want centralized mutation\n\t * semantics without a dedicated log surface (e.g. `Topic.publish`).\n\t */\n\tlog?: ReactiveLogBundle<R>;\n\t/** Build the success record from the action's args + result + meta. */\n\tonSuccessRecord?: (args: TArgs, result: TResult, meta: SuccessMeta) => R | undefined;\n\t/** Build the failure record from the args + error + meta. */\n\tonFailureRecord?: (args: TArgs, error: unknown, meta: FailureMeta) => R | undefined;\n\t/** Deep-freeze args at entry (default `true`). Opt out for hot paths. */\n\tfreeze?: boolean;\n\t/** Optional sequence cursor — auto-advanced and stamped onto records. */\n\tseq?: Node<number>;\n\t/** Optional handler version — stamped per Audit 5. */\n\thandlerVersion?: { id: string; version: string | number };\n};\n\nfunction deepFreeze<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || Object.isFrozen(value)) return value;\n\tfor (const k of Object.keys(value as Record<string, unknown>)) {\n\t\tdeepFreeze((value as Record<string, unknown>)[k]);\n\t}\n\treturn Object.freeze(value);\n}\n\n/**\n * Universal mutation factory (Phase 14 — DS-14 Q-O2).\n *\n * Replaces the prior `lightMutation` + `wrapMutation` two-tier split.\n * Single factory with `frame: \"inline\" | \"transactional\"` discriminant.\n *\n * @param act - The mutation action. Either a plain function (auto-wrapped as\n * `{ up: fn }`) or a `{ up, down? }` object for explicit rollback.\n * @param opts - Configuration: frame, log, record builders, freeze, seq.\n * @returns A typed wrapper function with the same signature as `act.up`.\n */\nexport function mutate<TArgs extends readonly unknown[], TResult, R extends BaseAuditRecord>(\n\tact: MutationAct<TArgs, TResult> | ((...args: TArgs) => TResult),\n\topts: MutateOpts<TArgs, TResult, R>,\n): (...args: TArgs) => TResult {\n\tconst { up, down } = typeof act === \"function\" ? { up: act, down: undefined } : act;\n\tconst freeze = opts.freeze ?? true;\n\n\tif (opts.frame === \"inline\") {\n\t\treturn function wrapped(...args: TArgs): TResult {\n\t\t\tconst sealed = freeze ? (args.map(deepFreeze) as unknown as TArgs) : args;\n\t\t\tconst t_ns = wallClockNs();\n\t\t\tconst seq = opts.seq ? bumpCursor(opts.seq) : undefined;\n\t\t\ttry {\n\t\t\t\tconst result = up(...sealed);\n\t\t\t\tif (opts.log && opts.onSuccessRecord) {\n\t\t\t\t\tappendAudit<TArgs, TResult, R, SuccessMeta>(\n\t\t\t\t\t\topts.log,\n\t\t\t\t\t\topts.onSuccessRecord,\n\t\t\t\t\t\tsealed,\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\t{ t_ns, seq },\n\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tif (opts.log && opts.onFailureRecord) {\n\t\t\t\t\tconst errorType = err instanceof Error ? err.name : typeof err;\n\t\t\t\t\tappendAudit<TArgs, unknown, R, FailureMeta>(\n\t\t\t\t\t\topts.log,\n\t\t\t\t\t\topts.onFailureRecord,\n\t\t\t\t\t\tsealed,\n\t\t\t\t\t\terr,\n\t\t\t\t\t\t{ t_ns, seq, errorType },\n\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t};\n\t}\n\n\t// frame === \"transactional\"\n\treturn function wrapped(...args: TArgs): TResult {\n\t\tconst sealed = freeze ? (args.map(deepFreeze) as unknown as TArgs) : args;\n\t\tconst t_ns = wallClockNs();\n\t\tlet result: TResult;\n\t\tlet captured: unknown;\n\t\tlet captureSet = false;\n\t\tlet seq: number | undefined;\n\t\ttry {\n\t\t\tbatch(() => {\n\t\t\t\tif (opts.seq) seq = bumpCursor(opts.seq);\n\t\t\t\ttry {\n\t\t\t\t\tresult = up(...sealed);\n\t\t\t\t\tif (opts.log && opts.onSuccessRecord) {\n\t\t\t\t\t\tappendAudit<TArgs, TResult, R, SuccessMeta>(\n\t\t\t\t\t\t\topts.log,\n\t\t\t\t\t\t\topts.onSuccessRecord,\n\t\t\t\t\t\t\tsealed,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\t{ t_ns, seq },\n\t\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tcaptured = err;\n\t\t\t\t\tcaptureSet = true;\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (outerErr) {\n\t\t\t// Fire `down` AFTER batch's reactive rollback, BEFORE failure record.\n\t\t\t// Gate on `captureSet` — if the throw came from outside the inner try\n\t\t\t// (framework-level batch error before action ran), don't fire down.\n\t\t\tif (captureSet && down) {\n\t\t\t\ttry {\n\t\t\t\t\tdown(...sealed);\n\t\t\t\t} catch (downErr) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`mutate: down hook threw — original action error preserved (${\n\t\t\t\t\t\t\tcaptured instanceof Error ? captured.name : typeof captured\n\t\t\t\t\t\t}). Down error:`,\n\t\t\t\t\t\tdownErr,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (captureSet && opts.log && opts.onFailureRecord) {\n\t\t\t\tconst errorType = captured instanceof Error ? captured.name : typeof captured;\n\t\t\t\tappendAudit<TArgs, unknown, R, FailureMeta>(\n\t\t\t\t\topts.log,\n\t\t\t\t\topts.onFailureRecord,\n\t\t\t\t\tsealed,\n\t\t\t\t\tcaptured,\n\t\t\t\t\t{ t_ns, seq, errorType },\n\t\t\t\t\topts.handlerVersion,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow captureSet ? captured : outerErr;\n\t\t}\n\t\treturn result!;\n\t};\n}\n\n/**\n * Advance a cursor node and return the new value. Emits `[DIRTY], [DATA, next]`\n * directly on the cursor — atomic outside a batch, rollback-discardable inside.\n *\n * Resets to `0` if the cursor cache is missing, non-numeric, `NaN`, or\n * non-finite (e.g. corrupted by `restore()` from a malformed snapshot, or\n * by a misbehaving codec). `??` alone would let `NaN` and `\"\"` pass through\n * and silently corrupt audit ordering downstream.\n *\n * **Silent reset diagnostic (EH-12).** When the cache holds a non-numeric\n * value at bump time, the cursor restarts at 0 and the next bump returns 1\n * — colliding with the seq stamped on the very first record after construct.\n * To make seq-monotonicity violations after a restore visible to operators,\n * the helper emits a one-shot `console.warn` per cursor instance describing\n * the offending value. The cursor is identified by a `WeakSet<Node<number>>`\n * so the warning fires exactly once per node — repeat malformed bumps stay\n * quiet to avoid log spam. Production callers wanting to suppress can swap\n * the global `console` (universal-safe code path; no Node-only API used).\n *\n * Works whether or not the cursor has any subscribers — `down` updates the\n * cache regardless, so primitives that bump before consumers attach (e.g.\n * `JobQueueGraph.enqueue`) still see a coherent sequence.\n *\n * @category internal\n */\nconst _bumpCursorWarned = new WeakSet<Node<number>>();\nexport function bumpCursor(seq: Node<number>): number {\n\tconst raw = seq.cache;\n\tconst valid = typeof raw === \"number\" && Number.isFinite(raw);\n\tif (!valid && raw !== undefined && !_bumpCursorWarned.has(seq)) {\n\t\t_bumpCursorWarned.add(seq);\n\t\tconsole.warn(\n\t\t\t`bumpCursor: cursor cache held a non-numeric value (${String(raw)}); resetting to 0. ` +\n\t\t\t\t\"Causes include: a snapshot codec round-tripping the cursor as a string / null / NaN, \" +\n\t\t\t\t\"OR a malformed initial seed (e.g. state<number>(NaN)). \" +\n\t\t\t\t\"Audit consumers may see colliding seq values after this point.\",\n\t\t);\n\t}\n\tconst cur = valid ? raw : 0;\n\tconst next = cur + 1;\n\tseq.down([[DIRTY], [DATA, next]]);\n\treturn next;\n}\n\n/**\n * Build a record via the supplied builder, stamp `handlerVersion` if present,\n * and append it to the audit log. `undefined` records are skipped (callers\n * pass an `onSuccess` / `onFailure` that returns `undefined` to opt out per\n * call).\n *\n * @category internal\n */\nexport function appendAudit<\n\tTArgs extends readonly unknown[],\n\tTValue,\n\tR extends BaseAuditRecord,\n\tM extends SuccessMeta | FailureMeta,\n>(\n\taudit: ReactiveLogBundle<R>,\n\tbuilder: (args: TArgs, value: TValue, meta: M) => R | undefined,\n\targs: TArgs,\n\tvalue: TValue,\n\tmeta: M,\n\thandlerVersion?: { id: string; version: string | number },\n): void {\n\tconst record = builder(args, value, meta);\n\tif (record === undefined) return;\n\tconst stamped = handlerVersion != null ? ({ ...record, handlerVersion } as R) : record;\n\taudit.append(stamped);\n}\n\n// ── registerCursor / registerCursorMap ───────────────────────────────────\n\n/**\n * Promote a closure counter to a state node mounted under `graph`.\n * Replaces ad-hoc `let _seq = 0` patterns with a node observable in\n * `describe()` and persistable via storage tiers.\n *\n * @category internal\n */\nexport function registerCursor(graph: Graph, name: string, initial = 0): Node<number> {\n\tconst cursor = node<number>([], { initial, name, describeKind: \"state\" });\n\tgraph.add(cursor, { name });\n\treturn cursor;\n}\n\n/**\n * Promote a closure `Map<K, number>` to N state nodes (one per key) mounted\n * under `<graph>::<name>::<key>`. Used by saga (per-event-type cursor).\n *\n * @category internal\n */\nexport function registerCursorMap<K extends string>(\n\tgraph: Graph,\n\tname: string,\n\tkeys: readonly K[],\n\tinitial = 0,\n): { readonly [P in K]: Node<number> } {\n\tconst out = {} as { [P in K]: Node<number> };\n\t// Mount cursors under a child plain-Graph so per-key node names stay flat\n\t// (path-separator `::` is reserved by Graph.add). Using `Graph` directly\n\t// rather than `graph.constructor` avoids spawning a typed subclass with\n\t// an incompatible constructor signature (e.g., CqrsGraph(name, opts)).\n\tconst sub = new Graph(name);\n\tfor (const k of keys) {\n\t\tconst cursor = node<number>([], {\n\t\t\tinitial,\n\t\t\tname: k,\n\t\t\tdescribeKind: \"state\",\n\t\t});\n\t\tsub.add(cursor, { name: k });\n\t\tout[k] = cursor;\n\t}\n\tgraph.mount(name, sub);\n\treturn out;\n}\n"],"mappings":"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","names":["import_core","import_extra","import_graph","batch","i"]}
1
+ {"version":3,"sources":["../../../src/utils/reduction/index.ts","../../../src/base/meta/domain-meta.ts","../../../src/base/mutation/index.ts"],"sourcesContent":["/**\n * Reduction primitives (roadmap §8.1).\n *\n * Composable building blocks for taking heterogeneous massive inputs and producing\n * prioritized, auditable, human-actionable output. Each primitive is either a Graph\n * factory or a Node factory, built on top of core + extra primitives.\n *\n * @module\n */\n\nimport {\n\tbatch,\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\ttype Message,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n} from \"@graphrefly/pure-ts/core\";\n\nimport { merge } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\n\n// ---------------------------------------------------------------------------\n// Shared helpers (same pattern as orchestration.ts)\n// ---------------------------------------------------------------------------\n\nexport type { StepRef } from \"../orchestration/pipeline-graph.js\";\n\nimport { keepalive } from \"@graphrefly/pure-ts/extra\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport { tryIncrementBounded } from \"../../base/mutation/index.js\";\nimport type { StepRef } from \"../orchestration/pipeline-graph.js\";\n\nfunction baseMeta(kind: string, meta?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"reduction\", kind, meta);\n}\n\n// stratify moved to `src/extra/stratify.ts` (protocol-level primitive).\n\n// ---------------------------------------------------------------------------\n// funnel\n// ---------------------------------------------------------------------------\n\n/** A named stage for {@link funnel}. */\nexport type FunnelStage = {\n\t/** Stage name (mounted as subgraph). */\n\tname: string;\n\t/** Builder: receives a sub-graph, should add an `\"input\"` and `\"output\"` node. */\n\tbuild: (sub: Graph) => void;\n};\n\n/** Options for {@link funnel}. */\nexport type FunnelOptions = GraphOptions & {\n\tmeta?: Record<string, unknown>;\n};\n\n/**\n * Multi-source merge with sequential reduction stages.\n *\n * Sources are merged into a single stream. Each stage is a named subgraph\n * (mounted via `graph.mount()`). Stages connect linearly:\n * `merged → stage[0].input → stage[0].output → stage[1].input → ...`\n *\n * @param name - Graph name.\n * @param sources - Input nodes to merge.\n * @param stages - Sequential reduction stages.\n * @param opts - Optional graph/meta options.\n * @returns Graph with `\"merged\"` and mounted stage subgraphs.\n *\n * @category patterns\n */\nexport function funnel<T>(\n\tname: string,\n\tsources: ReadonlyArray<Node<T>>,\n\tstages: ReadonlyArray<FunnelStage>,\n\topts?: FunnelOptions,\n): Graph {\n\tif (sources.length === 0) throw new RangeError(\"funnel requires at least one source\");\n\tif (stages.length === 0) throw new RangeError(\"funnel requires at least one stage\");\n\n\tconst g = new Graph(name, opts);\n\n\t// Merge all sources\n\tconst merged = sources.length === 1 ? sources[0] : merge(...(sources as unknown as Node<T>[]));\n\tg.add(merged as Node<unknown>, { name: \"merged\" });\n\n\t// Build and mount each stage linearly.\n\t// Stage inputs are standalone state nodes, so we bridge via subscribe\n\t// (connect() requires constructor deps). Bridge effects forward DATA\n\t// from the previous output to the next stage's input.\n\tlet prevOutputPath = \"merged\";\n\tfor (let i = 0; i < stages.length; i++) {\n\t\tconst stage = stages[i];\n\t\tconst sub = new Graph(stage.name);\n\t\tstage.build(sub);\n\n\t\t// Validate that the stage has input and output nodes\n\t\ttry {\n\t\t\tsub.resolve(\"input\");\n\t\t} catch {\n\t\t\tthrow new Error(`funnel stage \"${stage.name}\" must define an \"input\" node`);\n\t\t}\n\t\ttry {\n\t\t\tsub.resolve(\"output\");\n\t\t} catch {\n\t\t\tthrow new Error(`funnel stage \"${stage.name}\" must define an \"output\" node`);\n\t\t}\n\n\t\tg.mount(stage.name, sub);\n\n\t\t// Bridge replacement: effect that forwards DATA from previous output\n\t\t// to the next stage's input. TEARDOWN excluded because stage lifecycle\n\t\t// is managed by the parent graph. Shows up in describe().\n\t\tconst prevNode = g.resolve(prevOutputPath);\n\t\tconst stageInputPath = `${stage.name}::input`;\n\t\tconst stageInput = g.resolve(stageInputPath);\n\t\tconst bridgeName = `__bridge_${prevOutputPath}→${stage.name}_input`;\n\t\tconst br = node(\n\t\t\t[prevNode],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tstageInput.emit(data[0]);\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t\t{ describeKind: \"effect\", name: bridgeName },\n\t\t);\n\t\tg.add(br as Node<unknown>, { name: bridgeName });\n\t\tg.addDisposer(keepalive(br));\n\n\t\tprevOutputPath = `${stage.name}::output`;\n\t}\n\n\treturn g;\n}\n\n// ---------------------------------------------------------------------------\n// feedback\n// ---------------------------------------------------------------------------\n\n/** Options for {@link feedback}. */\nexport type FeedbackOptions = {\n\t/** Maximum feedback iterations before stopping (default: 10). */\n\tmaxIterations?: number;\n\t/** Optional budget gate node path for cost-bounded iteration. */\n\tbudgetNode?: StepRef;\n\tmeta?: Record<string, unknown>;\n};\n\n/**\n * Introduce a bounded reactive cycle into an existing graph.\n *\n * When `condition` emits a non-null DATA value, the feedback effect routes it\n * back to the `reentry` state node — creating a cycle. Bounded by\n * `maxIterations` (default 10). The counter node (`__feedback_<condition>`)\n * is the source of truth — reset it to 0 to allow more iterations.\n *\n * To remove the feedback cycle, call `graph.remove(\"__feedback_<condition>\")`.\n *\n * @param graph - Existing graph to augment with a feedback cycle.\n * @param condition - Path to a node whose DATA triggers feedback.\n * @param reentry - Path to a state node that receives the feedback value.\n * @param opts - Iteration bounds and metadata.\n * @returns The same graph (mutated with feedback nodes added).\n *\n * @category patterns\n */\nexport function feedback(\n\tgraph: Graph,\n\tcondition: string,\n\treentry: string,\n\topts?: FeedbackOptions,\n): Graph {\n\tconst maxIter = opts?.maxIterations ?? 10;\n\n\t// Internal counter node — source of truth for iteration bound.\n\t// Reset to 0 to allow more iterations.\n\tconst counterName = `__feedback_${condition}`;\n\tconst counter = node<number>([], {\n\t\t...{\n\t\t\tmeta: baseMeta(\"feedback_counter\", {\n\t\t\t\tmaxIterations: maxIter,\n\t\t\t\tfeedbackFrom: condition,\n\t\t\t\tfeedbackTo: reentry,\n\t\t\t}),\n\t\t},\n\t\tinitial: 0,\n\t});\n\tgraph.add(counter as Node<unknown>, { name: counterName });\n\n\t// Resolve the condition and reentry nodes\n\tconst condNode = graph.resolve(condition);\n\tconst reentryNode = graph.resolve(reentry);\n\n\t// Graph-visible feedback effect: intercepts condition DATA, routes back to\n\t// reentry with iteration counting. Registered in the graph so it shows up\n\t// in describe() and cleans up on graph.destroy().\n\t// Feedback effect: subscribe to condition node for per-message interception\n\t// (onMessage removed in v5 — use producer+subscribe instead)\n\tconst feedbackEffectName = `__feedback_effect_${condition}`;\n\tconst feedbackEffect = node(\n\t\t[],\n\t\t(_data, _feedbackActions) => {\n\t\t\tconst unsub = condNode.subscribe((msgs) => {\n\t\t\t\tfor (const msg of msgs) {\n\t\t\t\t\tconst t = msg[0];\n\t\t\t\t\tif (t === DATA) {\n\t\t\t\t\t\tconst condValue = msg[1];\n\t\t\t\t\t\tif (condValue == null) return;\n\t\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\t\tif (tryIncrementBounded(counter, maxIter)) {\n\t\t\t\t\t\t\t\treentryNode.emit(condValue);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (t === COMPLETE || t === ERROR) {\n\t\t\t\t\t\tconst terminal: Message = t === ERROR && msg.length > 1 ? [ERROR, msg[1]] : [t];\n\t\t\t\t\t\tcounter.down([terminal]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn { onDeactivation: () => unsub() };\n\t\t},\n\t\t{\n\t\t\tname: feedbackEffectName,\n\t\t\tdescribeKind: \"effect\",\n\t\t\tmeta: {\n\t\t\t\t...baseMeta(\"feedback_effect\", {\n\t\t\t\t\tfeedbackFrom: condition,\n\t\t\t\t\tfeedbackTo: reentry,\n\t\t\t\t}),\n\t\t\t\t_internal: true,\n\t\t\t},\n\t\t},\n\t);\n\tgraph.add(feedbackEffect as Node<unknown>, { name: feedbackEffectName });\n\tgraph.addDisposer(keepalive(feedbackEffect));\n\n\treturn graph;\n}\n\n// `budgetGate` was promoted to `extra/resilience/budget-gate.ts` per Tier 2.2.\n// Import from `@graphrefly/graphrefly/extra` (or `../../extra/resilience/budget-gate.js`\n// internally) instead. See the resilience family for sibling primitives:\n// `retry`, `circuitBreaker`, `rateLimiter`, `tokenBucket`, `fallback`, `withStatus`.\n\n// ---------------------------------------------------------------------------\n// scorer\n// ---------------------------------------------------------------------------\n\n/** A scored item with full breakdown. */\nexport type ScoredItem<T = unknown> = {\n\t/** Original value. */\n\tvalue: T;\n\t/** Final weighted score. */\n\tscore: number;\n\t/** Per-signal breakdown: signal index → weighted contribution. */\n\tbreakdown: number[];\n};\n\n/** Options for {@link scorer}. */\nexport type ScorerOptions = Omit<NodeOptions<unknown>, \"describeKind\" | \"name\" | \"meta\"> & {\n\tmeta?: Record<string, unknown>;\n\t/** Custom scoring function per signal. Default: identity (signal value IS the score). */\n\tscoreFns?: ReadonlyArray<(value: unknown) => number>;\n};\n\n/**\n * Reactive multi-signal scoring with live weights.\n *\n * Each source emits items to score. Weights are reactive state nodes that\n * LLM or human can adjust live. Output is sorted scored items with full\n * breakdown.\n *\n * @param sources - Signal nodes (each emits a numeric score dimension).\n * @param weights - Reactive weight nodes (one per source).\n * @param opts - Optional node/meta options.\n * @returns Node emitting scored output.\n *\n * @category patterns\n */\nexport function scorer(\n\tsources: ReadonlyArray<Node<number>>,\n\tweights: ReadonlyArray<Node<number>>,\n\topts?: ScorerOptions,\n): Node<ScoredItem<number[]>> {\n\tif (sources.length === 0) throw new RangeError(\"scorer requires at least one source\");\n\tif (sources.length !== weights.length) {\n\t\tthrow new RangeError(\"scorer requires the same number of sources and weights\");\n\t}\n\n\tconst allDeps = [...(sources as unknown as Node[]), ...(weights as unknown as Node[])];\n\tconst n = sources.length;\n\tconst scoreFns = opts?.scoreFns;\n\n\treturn node<ScoredItem<number[]>>(\n\t\tallDeps,\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst vals = 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 signals = vals.slice(0, n) as number[];\n\t\t\tconst weightValues = vals.slice(n) as number[];\n\n\t\t\tconst breakdown: number[] = [];\n\t\t\tlet totalScore = 0;\n\n\t\t\tfor (let i = 0; i < n; i++) {\n\t\t\t\tconst sig = signals[i] ?? 0;\n\t\t\t\tconst wt = weightValues[i] ?? 0;\n\t\t\t\tconst rawScore = scoreFns?.[i] ? scoreFns[i](sig) : sig;\n\t\t\t\tconst weighted = (rawScore as number) * wt;\n\t\t\t\tbreakdown.push(weighted);\n\t\t\t\ttotalScore += weighted;\n\t\t\t}\n\n\t\t\tactions.emit({\n\t\t\t\tvalue: signals,\n\t\t\t\tscore: totalScore,\n\t\t\t\tbreakdown,\n\t\t\t});\n\t\t},\n\t\t{\n\t\t\t...(opts\n\t\t\t\t? {\n\t\t\t\t\t\tequals: opts.equals,\n\t\t\t\t\t\tresubscribable: opts.resubscribable,\n\t\t\t\t\t\tresetOnTeardown: opts.resetOnTeardown,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\tdescribeKind: \"derived\",\n\t\t\tmeta: baseMeta(\"scorer\", opts?.meta),\n\t\t},\n\t);\n}\n\n// `effectivenessTracker` was deleted per Class B audit Alt E (2026-04-30).\n// The shared substrate now lives in `extra/composition/audited-success-tracker.ts`\n// — re-exported via `@graphrefly/graphrefly-ts/extra` for general use.\n","/**\n * Metadata helpers for pattern-layer nodes (Tier 2.2 promotion from\n * `patterns/_internal/`).\n *\n * Each domain (orchestration, messaging, reduction, ai, cqrs, domain_template,\n * memory, lens, audit, harness) shares the same metadata convention. Promoted\n * to `extra/` so non-patterns code (and downstream consumers building their\n * own domain primitives) can use the same shape.\n *\n * @module\n */\n\n/**\n * Build a domain metadata object for pattern-layer nodes.\n *\n * Each domain follows the same shape: `{ [domain]: true, [domain]_type: kind, ...extra }`.\n *\n * @param domain - The domain tag (e.g. `\"orchestration\"`, `\"ai\"`, `\"cqrs\"`).\n * @param kind - The specific type within the domain (e.g. `\"gate\"`, `\"prompt\"`).\n * @param extra - Additional metadata to merge.\n * @returns Metadata object.\n */\nexport function domainMeta(\n\tdomain: string,\n\tkind: string,\n\textra?: Record<string, unknown>,\n): Record<string, unknown> {\n\treturn {\n\t\t[domain]: true,\n\t\t[`${domain}_type`]: kind,\n\t\t...(extra ?? {}),\n\t};\n}\n","/**\n * Universal mutation framework (Phase 14 — DS-14 locked 2026-05-05).\n *\n * Single `mutate(act, opts)` factory replaces the prior `lightMutation` +\n * `wrapMutation` two-tier split (pre-1.0 break per Q-O2).\n *\n * Two frames:\n * - `\"inline\"` — no batch; up() runs raw. Seq bumps before action; persists\n * on throw. Hot-path-friendly for atomic single-write mutations.\n * - `\"transactional\"` — opens `batch(() => up(...))`. On throw: batch discards\n * deferred deliveries, then `down()` runs (if provided), then failure record.\n *\n * Phase-4 primitives share the same shape: imperative mutation methods +\n * closure state + reactive audit log + freeze-at-entry + rollback-on-throw.\n * This module factors out the common machinery so each primitive becomes\n * declarative wiring over typed audit records.\n */\n\nimport {\n\tbatch,\n\tDATA,\n\tDIRTY,\n\ttype Node,\n\ttype NodeGuard,\n\tnode,\n\tpolicy,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport {\n\ttype ReactiveLogBundle,\n\ttype ReactiveLogOptions,\n\treactiveLog,\n} from \"@graphrefly/pure-ts/extra\";\nimport { Graph } from \"@graphrefly/pure-ts/graph\";\n\n// ── tryIncrementBounded ──────────────────────────────────────────────────\n\n/**\n * Bounded increment for a self-owned counter state node.\n *\n * Reads `counter.cache`, bumps by `by` (default 1) if `cur + by <= cap`,\n * writes back. Returns `false` when the cap would be exceeded (no-op write).\n * Documented P3 exception: the counter is not a declared dep of the caller —\n * it's a private budget read+written from a single call site. This helper\n * keeps the `.cache` access in one named place so caller bodies (which may\n * be inside reactive fn execution paths) stay free of cross-node `.cache`\n * reads.\n *\n * **Safety today:**\n * 1. Single-threaded JS runner never invokes the caller concurrently.\n * 2. `counter.down` writes the cache synchronously before returning, so\n * synchronous re-entry through a downstream publish reads the\n * freshly-incremented value — no double-count.\n *\n * **Future risk:** under a free-threaded runner (PY no-GIL or hypothetical\n * concurrent TS runner), two concurrent firings could still race. Revisit\n * when that surfaces.\n *\n * @param counter - Self-owned counter Node. Caller is the sole writer.\n * @param cap - Upper bound (inclusive). Pass `Number.MAX_SAFE_INTEGER` for\n * \"effectively unbounded\" use cases (e.g. token meters).\n * @param by - Delta to add (default `1`). Must be a finite non-negative\n * number; callers should pre-validate. Overflow-safe via\n * `by > cap - cur` check rather than `cur + by >= cap`.\n */\nexport function tryIncrementBounded(counter: Node<number>, cap: number, by = 1): boolean {\n\tconst cur = (counter.cache as number | undefined) ?? 0;\n\tif (by > cap - cur) return false;\n\tcounter.down([[DIRTY], [DATA, cur + by]]);\n\treturn true;\n}\n\n// ── Audit record schema ──────────────────────────────────────────────────\n\n/** Shared base shape for every audit record. Per-primitive types extend this. */\nexport interface BaseAuditRecord {\n\treadonly t_ns: number;\n\treadonly seq?: number;\n\treadonly handlerVersion?: { id: string; version: string | number };\n}\n\n// ── Default audit guard ──────────────────────────────────────────────────\n\n/**\n * Allow `observe` and `signal`; deny external `write` on the audit log so\n * consumers can subscribe + signal-bridge but cannot inject fake records.\n */\nexport const DEFAULT_AUDIT_GUARD: NodeGuard = policy((allow, deny) => {\n\tallow(\"observe\");\n\tallow(\"signal\");\n\tdeny(\"write\");\n});\n\n// ── createAuditLog ───────────────────────────────────────────────────────\n\nexport type AuditLogOpts<R extends BaseAuditRecord> = {\n\tname: string;\n\t/** Bounded retention; default 1024 per Audit 2 / cross-cutting bounded-default policy. */\n\tretainedLimit?: number;\n\t/** Override the default audit guard. */\n\tguard?: NodeGuard;\n\t/** Mount the audit `entries` Node under this graph (and activate withLatest). */\n\tgraph?: Graph;\n\t/** Pass-through to {@link reactiveLog}. */\n\tversioning?: ReactiveLogOptions<R>[\"versioning\"];\n};\n\n/**\n * Build a reactive audit log with sane defaults: bounded retention, deny-write\n * guard, `withLatest()` companions activated. Returns the {@link ReactiveLogBundle}\n * directly — primitives expose this as `<primitive>.events` / `.decisions` /\n * `.dispatches` / `.invocations` and alias it as `.audit`.\n *\n * @category internal\n */\nexport function createAuditLog<R extends BaseAuditRecord>(\n\topts: AuditLogOpts<R>,\n): ReactiveLogBundle<R> {\n\tconst log = reactiveLog<R>([], {\n\t\tname: opts.name,\n\t\tmaxSize: opts.retainedLimit ?? 1024,\n\t\tguard: opts.guard ?? DEFAULT_AUDIT_GUARD,\n\t\t...(opts.versioning != null ? { versioning: opts.versioning } : {}),\n\t});\n\t// Lazy companion activation up-front so `bundle.lastValue` / `hasLatest`\n\t// are queryable without an explicit `withLatest()` call.\n\tlog.withLatest();\n\tif (opts.graph) {\n\t\topts.graph.add(log.entries, { name: opts.name });\n\t}\n\treturn log;\n}\n\n// ── Universal mutation factory (Phase 14 — DS-14 lock Q-O2/Q-O3) ────────\n//\n// Single `mutate(act, opts)` factory. Two frames:\n//\n// - `\"inline\"` — no batch frame; up() runs raw. Seq bumps before action;\n// persists on throw. Hot-path-friendly for atomic single-write mutations.\n//\n// - `\"transactional\"` — opens `batch(() => up(...))`. On throw: batch discards\n// deferred deliveries, then `down()` runs, then failure record persists.\n//\n// **Heuristic:** if your imperative method's body is one or two lines (mutate\n// state, emit), use `frame: \"inline\"`. If it runs a user-supplied handler or\n// has multiple steps that could leave inconsistent state mid-throw, use\n// `frame: \"transactional\"`.\n\nexport type FailureMeta = {\n\tt_ns: number;\n\tseq?: number;\n\terrorType: string;\n};\n\nexport type SuccessMeta = {\n\tt_ns: number;\n\tseq?: number;\n};\n\n/**\n * Mutation action shape. Plain function shorthand auto-wraps as `{ up: fn }`.\n *\n * - `up` — the mutation action (the \"up migration\").\n * - `down` — optional rollback for closure mutations that `batch()` can't\n * reach. Receives the SAME frozen args as `up`. Runs AFTER batch reactive\n * rollback, BEFORE the failure record. Throws inside `down` are\n * console.error'd without masking the original error. Only meaningful\n * with `frame: \"transactional\"`.\n */\nexport type MutationAct<TArgs extends readonly unknown[], TResult> = {\n\tup: (...args: TArgs) => TResult;\n\tdown?: (...args: TArgs) => void;\n};\n\nexport type MutationFrame = \"inline\" | \"transactional\";\n\nexport type MutateOpts<TArgs extends readonly unknown[], TResult, R extends BaseAuditRecord> = {\n\t/** Frame mode. `\"inline\"` = no batch; `\"transactional\"` = batch + rollback. */\n\tframe: MutationFrame;\n\t/**\n\t * Optional log to append records to. When omitted, the wrapper still\n\t * provides freeze / seq-advance / rollback-on-throw but skips record\n\t * emission — useful for primitives that want centralized mutation\n\t * semantics without a dedicated log surface (e.g. `Topic.publish`).\n\t */\n\tlog?: ReactiveLogBundle<R>;\n\t/** Build the success record from the action's args + result + meta. */\n\tonSuccessRecord?: (args: TArgs, result: TResult, meta: SuccessMeta) => R | undefined;\n\t/** Build the failure record from the args + error + meta. */\n\tonFailureRecord?: (args: TArgs, error: unknown, meta: FailureMeta) => R | undefined;\n\t/** Deep-freeze args at entry (default `true`). Opt out for hot paths. */\n\tfreeze?: boolean;\n\t/** Optional sequence cursor — auto-advanced and stamped onto records. */\n\tseq?: Node<number>;\n\t/** Optional handler version — stamped per Audit 5. */\n\thandlerVersion?: { id: string; version: string | number };\n};\n\nfunction deepFreeze<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || Object.isFrozen(value)) return value;\n\tfor (const k of Object.keys(value as Record<string, unknown>)) {\n\t\tdeepFreeze((value as Record<string, unknown>)[k]);\n\t}\n\treturn Object.freeze(value);\n}\n\n/**\n * Universal mutation factory (Phase 14 — DS-14 Q-O2).\n *\n * Replaces the prior `lightMutation` + `wrapMutation` two-tier split.\n * Single factory with `frame: \"inline\" | \"transactional\"` discriminant.\n *\n * @param act - The mutation action. Either a plain function (auto-wrapped as\n * `{ up: fn }`) or a `{ up, down? }` object for explicit rollback.\n * @param opts - Configuration: frame, log, record builders, freeze, seq.\n * @returns A typed wrapper function with the same signature as `act.up`.\n */\nexport function mutate<TArgs extends readonly unknown[], TResult, R extends BaseAuditRecord>(\n\tact: MutationAct<TArgs, TResult> | ((...args: TArgs) => TResult),\n\topts: MutateOpts<TArgs, TResult, R>,\n): (...args: TArgs) => TResult {\n\tconst { up, down } = typeof act === \"function\" ? { up: act, down: undefined } : act;\n\tconst freeze = opts.freeze ?? true;\n\n\tif (opts.frame === \"inline\") {\n\t\treturn function wrapped(...args: TArgs): TResult {\n\t\t\tconst sealed = freeze ? (args.map(deepFreeze) as unknown as TArgs) : args;\n\t\t\tconst t_ns = wallClockNs();\n\t\t\tconst seq = opts.seq ? bumpCursor(opts.seq) : undefined;\n\t\t\ttry {\n\t\t\t\tconst result = up(...sealed);\n\t\t\t\tif (opts.log && opts.onSuccessRecord) {\n\t\t\t\t\tappendAudit<TArgs, TResult, R, SuccessMeta>(\n\t\t\t\t\t\topts.log,\n\t\t\t\t\t\topts.onSuccessRecord,\n\t\t\t\t\t\tsealed,\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\t{ t_ns, seq },\n\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tif (opts.log && opts.onFailureRecord) {\n\t\t\t\t\tconst errorType = err instanceof Error ? err.name : typeof err;\n\t\t\t\t\tappendAudit<TArgs, unknown, R, FailureMeta>(\n\t\t\t\t\t\topts.log,\n\t\t\t\t\t\topts.onFailureRecord,\n\t\t\t\t\t\tsealed,\n\t\t\t\t\t\terr,\n\t\t\t\t\t\t{ t_ns, seq, errorType },\n\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t};\n\t}\n\n\t// frame === \"transactional\"\n\treturn function wrapped(...args: TArgs): TResult {\n\t\tconst sealed = freeze ? (args.map(deepFreeze) as unknown as TArgs) : args;\n\t\tconst t_ns = wallClockNs();\n\t\tlet result: TResult;\n\t\tlet captured: unknown;\n\t\tlet captureSet = false;\n\t\tlet seq: number | undefined;\n\t\ttry {\n\t\t\tbatch(() => {\n\t\t\t\tif (opts.seq) seq = bumpCursor(opts.seq);\n\t\t\t\ttry {\n\t\t\t\t\tresult = up(...sealed);\n\t\t\t\t\tif (opts.log && opts.onSuccessRecord) {\n\t\t\t\t\t\tappendAudit<TArgs, TResult, R, SuccessMeta>(\n\t\t\t\t\t\t\topts.log,\n\t\t\t\t\t\t\topts.onSuccessRecord,\n\t\t\t\t\t\t\tsealed,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\t{ t_ns, seq },\n\t\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tcaptured = err;\n\t\t\t\t\tcaptureSet = true;\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (outerErr) {\n\t\t\t// Fire `down` AFTER batch's reactive rollback, BEFORE failure record.\n\t\t\t// Gate on `captureSet` — if the throw came from outside the inner try\n\t\t\t// (framework-level batch error before action ran), don't fire down.\n\t\t\tif (captureSet && down) {\n\t\t\t\ttry {\n\t\t\t\t\tdown(...sealed);\n\t\t\t\t} catch (downErr) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`mutate: down hook threw — original action error preserved (${\n\t\t\t\t\t\t\tcaptured instanceof Error ? captured.name : typeof captured\n\t\t\t\t\t\t}). Down error:`,\n\t\t\t\t\t\tdownErr,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (captureSet && opts.log && opts.onFailureRecord) {\n\t\t\t\tconst errorType = captured instanceof Error ? captured.name : typeof captured;\n\t\t\t\tappendAudit<TArgs, unknown, R, FailureMeta>(\n\t\t\t\t\topts.log,\n\t\t\t\t\topts.onFailureRecord,\n\t\t\t\t\tsealed,\n\t\t\t\t\tcaptured,\n\t\t\t\t\t{ t_ns, seq, errorType },\n\t\t\t\t\topts.handlerVersion,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow captureSet ? captured : outerErr;\n\t\t}\n\t\treturn result!;\n\t};\n}\n\n/**\n * Advance a cursor node and return the new value. Emits `[DIRTY], [DATA, next]`\n * directly on the cursor — atomic outside a batch, rollback-discardable inside.\n *\n * Resets to `0` if the cursor cache is missing, non-numeric, `NaN`, or\n * non-finite (e.g. corrupted by `restore()` from a malformed snapshot, or\n * by a misbehaving codec). `??` alone would let `NaN` and `\"\"` pass through\n * and silently corrupt audit ordering downstream.\n *\n * **Silent reset diagnostic (EH-12).** When the cache holds a non-numeric\n * value at bump time, the cursor restarts at 0 and the next bump returns 1\n * — colliding with the seq stamped on the very first record after construct.\n * To make seq-monotonicity violations after a restore visible to operators,\n * the helper emits a one-shot `console.warn` per cursor instance describing\n * the offending value. The cursor is identified by a `WeakSet<Node<number>>`\n * so the warning fires exactly once per node — repeat malformed bumps stay\n * quiet to avoid log spam. Production callers wanting to suppress can swap\n * the global `console` (universal-safe code path; no Node-only API used).\n *\n * Works whether or not the cursor has any subscribers — `down` updates the\n * cache regardless, so primitives that bump before consumers attach (e.g.\n * `JobQueueGraph.enqueue`) still see a coherent sequence.\n *\n * @category internal\n */\nconst _bumpCursorWarned = new WeakSet<Node<number>>();\nexport function bumpCursor(seq: Node<number>): number {\n\tconst raw = seq.cache;\n\tconst valid = typeof raw === \"number\" && Number.isFinite(raw);\n\tif (!valid && raw !== undefined && !_bumpCursorWarned.has(seq)) {\n\t\t_bumpCursorWarned.add(seq);\n\t\tconsole.warn(\n\t\t\t`bumpCursor: cursor cache held a non-numeric value (${String(raw)}); resetting to 0. ` +\n\t\t\t\t\"Causes include: a snapshot codec round-tripping the cursor as a string / null / NaN, \" +\n\t\t\t\t\"OR a malformed initial seed (e.g. state<number>(NaN)). \" +\n\t\t\t\t\"Audit consumers may see colliding seq values after this point.\",\n\t\t);\n\t}\n\tconst cur = valid ? raw : 0;\n\tconst next = cur + 1;\n\tseq.down([[DIRTY], [DATA, next]]);\n\treturn next;\n}\n\n/**\n * Build a record via the supplied builder, stamp `handlerVersion` if present,\n * and append it to the audit log. `undefined` records are skipped (callers\n * pass an `onSuccess` / `onFailure` that returns `undefined` to opt out per\n * call).\n *\n * @category internal\n */\nexport function appendAudit<\n\tTArgs extends readonly unknown[],\n\tTValue,\n\tR extends BaseAuditRecord,\n\tM extends SuccessMeta | FailureMeta,\n>(\n\taudit: ReactiveLogBundle<R>,\n\tbuilder: (args: TArgs, value: TValue, meta: M) => R | undefined,\n\targs: TArgs,\n\tvalue: TValue,\n\tmeta: M,\n\thandlerVersion?: { id: string; version: string | number },\n): void {\n\tconst record = builder(args, value, meta);\n\tif (record === undefined) return;\n\tconst stamped = handlerVersion != null ? ({ ...record, handlerVersion } as R) : record;\n\taudit.append(stamped);\n}\n\n// ── registerCursor / registerCursorMap ───────────────────────────────────\n\n/**\n * Promote a closure counter to a state node mounted under `graph`.\n * Replaces ad-hoc `let _seq = 0` patterns with a node observable in\n * `describe()` and persistable via storage tiers.\n *\n * @category internal\n */\nexport function registerCursor(graph: Graph, name: string, initial = 0): Node<number> {\n\tconst cursor = node<number>([], { initial, name, describeKind: \"state\" });\n\tgraph.add(cursor, { name });\n\treturn cursor;\n}\n\n/**\n * Promote a closure `Map<K, number>` to N state nodes (one per key) mounted\n * under `<graph>::<name>::<key>`. Used by saga (per-event-type cursor).\n *\n * @category internal\n */\nexport function registerCursorMap<K extends string>(\n\tgraph: Graph,\n\tname: string,\n\tkeys: readonly K[],\n\tinitial = 0,\n): { readonly [P in K]: Node<number> } {\n\tconst out = {} as { [P in K]: Node<number> };\n\t// Mount cursors under a child plain-Graph so per-key node names stay flat\n\t// (path-separator `::` is reserved by Graph.add). Using `Graph` directly\n\t// rather than `graph.constructor` avoids spawning a typed subclass with\n\t// an incompatible constructor signature (e.g., CqrsGraph(name, opts)).\n\tconst sub = new Graph(name);\n\tfor (const k of keys) {\n\t\tconst cursor = node<number>([], {\n\t\t\tinitial,\n\t\t\tname: k,\n\t\t\tdescribeKind: \"state\",\n\t\t});\n\t\tsub.add(cursor, { name: k });\n\t\tout[k] = cursor;\n\t}\n\tgraph.mount(name, sub);\n\treturn out;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,IAAAA,eASO;AAEP,IAAAC,gBAAsB;AACtB,IAAAC,gBAAyC;AAQzC,IAAAD,gBAA0B;;;ACRnB,SAAS,WACf,QACA,MACA,OAC0B;AAC1B,SAAO;AAAA,IACN,CAAC,MAAM,GAAG;AAAA,IACV,CAAC,GAAG,MAAM,OAAO,GAAG;AAAA,IACpB,GAAI,SAAS,CAAC;AAAA,EACf;AACD;;;ACdA,kBASO;AACP,mBAIO;AACP,mBAAsB;AAgCf,SAAS,oBAAoB,SAAuB,KAAa,KAAK,GAAY;AACxF,QAAM,MAAO,QAAQ,SAAgC;AACrD,MAAI,KAAK,MAAM,IAAK,QAAO;AAC3B,UAAQ,KAAK,CAAC,CAAC,iBAAK,GAAG,CAAC,kBAAM,MAAM,EAAE,CAAC,CAAC;AACxC,SAAO;AACR;AAiBO,IAAM,0BAAiC,oBAAO,CAAC,OAAO,SAAS;AACrE,QAAM,SAAS;AACf,QAAM,QAAQ;AACd,OAAK,OAAO;AACb,CAAC;;;AFxDD,SAAS,SAAS,MAAc,MAAyD;AACxF,SAAO,WAAW,aAAa,MAAM,IAAI;AAC1C;AAoCO,SAAS,OACf,MACA,SACA,QACA,MACQ;AACR,MAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,WAAW,qCAAqC;AACpF,MAAI,OAAO,WAAW,EAAG,OAAM,IAAI,WAAW,oCAAoC;AAElF,QAAM,IAAI,IAAI,oBAAM,MAAM,IAAI;AAG9B,QAAM,SAAS,QAAQ,WAAW,IAAI,QAAQ,CAAC,QAAI,qBAAM,GAAI,OAAgC;AAC7F,IAAE,IAAI,QAAyB,EAAE,MAAM,SAAS,CAAC;AAMjD,MAAI,iBAAiB;AACrB,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACvC,UAAM,QAAQ,OAAO,CAAC;AACtB,UAAM,MAAM,IAAI,oBAAM,MAAM,IAAI;AAChC,UAAM,MAAM,GAAG;AAGf,QAAI;AACH,UAAI,QAAQ,OAAO;AAAA,IACpB,QAAQ;AACP,YAAM,IAAI,MAAM,iBAAiB,MAAM,IAAI,+BAA+B;AAAA,IAC3E;AACA,QAAI;AACH,UAAI,QAAQ,QAAQ;AAAA,IACrB,QAAQ;AACP,YAAM,IAAI,MAAM,iBAAiB,MAAM,IAAI,gCAAgC;AAAA,IAC5E;AAEA,MAAE,MAAM,MAAM,MAAM,GAAG;AAKvB,UAAM,WAAW,EAAE,QAAQ,cAAc;AACzC,UAAM,iBAAiB,GAAG,MAAM,IAAI;AACpC,UAAM,aAAa,EAAE,QAAQ,cAAc;AAC3C,UAAM,aAAa,YAAY,cAAc,SAAI,MAAM,IAAI;AAC3D,UAAM,SAAK;AAAA,MACV,CAAC,QAAQ;AAAA,MACT,CAAC,WAAW,UAAU,QAAQ;AAC7B,cAAM,OAAO,UAAU;AAAA,UAAI,CAACE,QAAOC,OAClCD,UAAS,QAAQA,OAAM,SAAS,IAAIA,OAAM,GAAG,EAAE,IAAI,IAAI,SAASC,EAAC;AAAA,QAClE;AACA,mBAAW,KAAK,KAAK,CAAC,CAAC;AACvB,eAAO;AAAA,MACR;AAAA,MACA,EAAE,cAAc,UAAU,MAAM,WAAW;AAAA,IAC5C;AACA,MAAE,IAAI,IAAqB,EAAE,MAAM,WAAW,CAAC;AAC/C,MAAE,gBAAY,yBAAU,EAAE,CAAC;AAE3B,qBAAiB,GAAG,MAAM,IAAI;AAAA,EAC/B;AAEA,SAAO;AACR;AAiCO,SAAS,SACf,OACA,WACA,SACA,MACQ;AACR,QAAM,UAAU,MAAM,iBAAiB;AAIvC,QAAM,cAAc,cAAc,SAAS;AAC3C,QAAM,cAAU,mBAAa,CAAC,GAAG;AAAA,IAChC,GAAG;AAAA,MACF,MAAM,SAAS,oBAAoB;AAAA,QAClC,eAAe;AAAA,QACf,cAAc;AAAA,QACd,YAAY;AAAA,MACb,CAAC;AAAA,IACF;AAAA,IACA,SAAS;AAAA,EACV,CAAC;AACD,QAAM,IAAI,SAA0B,EAAE,MAAM,YAAY,CAAC;AAGzD,QAAM,WAAW,MAAM,QAAQ,SAAS;AACxC,QAAM,cAAc,MAAM,QAAQ,OAAO;AAOzC,QAAM,qBAAqB,qBAAqB,SAAS;AACzD,QAAM,qBAAiB;AAAA,IACtB,CAAC;AAAA,IACD,CAAC,OAAO,qBAAqB;AAC5B,YAAM,QAAQ,SAAS,UAAU,CAAC,SAAS;AAC1C,mBAAW,OAAO,MAAM;AACvB,gBAAM,IAAI,IAAI,CAAC;AACf,cAAI,MAAM,mBAAM;AACf,kBAAM,YAAY,IAAI,CAAC;AACvB,gBAAI,aAAa,KAAM;AACvB,oCAAM,MAAM;AACX,kBAAI,oBAAoB,SAAS,OAAO,GAAG;AAC1C,4BAAY,KAAK,SAAS;AAAA,cAC3B;AAAA,YACD,CAAC;AAAA,UACF,WAAW,MAAM,yBAAY,MAAM,oBAAO;AACzC,kBAAM,WAAoB,MAAM,sBAAS,IAAI,SAAS,IAAI,CAAC,oBAAO,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;AAC9E,oBAAQ,KAAK,CAAC,QAAQ,CAAC;AAAA,UACxB;AAAA,QACD;AAAA,MACD,CAAC;AACD,aAAO,EAAE,gBAAgB,MAAM,MAAM,EAAE;AAAA,IACxC;AAAA,IACA;AAAA,MACC,MAAM;AAAA,MACN,cAAc;AAAA,MACd,MAAM;AAAA,QACL,GAAG,SAAS,mBAAmB;AAAA,UAC9B,cAAc;AAAA,UACd,YAAY;AAAA,QACb,CAAC;AAAA,QACD,WAAW;AAAA,MACZ;AAAA,IACD;AAAA,EACD;AACA,QAAM,IAAI,gBAAiC,EAAE,MAAM,mBAAmB,CAAC;AACvE,QAAM,gBAAY,yBAAU,cAAc,CAAC;AAE3C,SAAO;AACR;AA0CO,SAAS,OACf,SACA,SACA,MAC6B;AAC7B,MAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,WAAW,qCAAqC;AACpF,MAAI,QAAQ,WAAW,QAAQ,QAAQ;AACtC,UAAM,IAAI,WAAW,wDAAwD;AAAA,EAC9E;AAEA,QAAM,UAAU,CAAC,GAAI,SAA+B,GAAI,OAA6B;AACrF,QAAM,IAAI,QAAQ;AAClB,QAAM,WAAW,MAAM;AAEvB,aAAO;AAAA,IACN;AAAA,IACA,CAAC,WAAW,SAAS,QAAQ;AAC5B,YAAM,OAAO,UAAU;AAAA,QAAI,CAACD,QAAO,MAClCA,UAAS,QAAQA,OAAM,SAAS,IAAIA,OAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,UAAU,KAAK,MAAM,GAAG,CAAC;AAC/B,YAAM,eAAe,KAAK,MAAM,CAAC;AAEjC,YAAM,YAAsB,CAAC;AAC7B,UAAI,aAAa;AAEjB,eAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC3B,cAAM,MAAM,QAAQ,CAAC,KAAK;AAC1B,cAAM,KAAK,aAAa,CAAC,KAAK;AAC9B,cAAM,WAAW,WAAW,CAAC,IAAI,SAAS,CAAC,EAAE,GAAG,IAAI;AACpD,cAAM,WAAY,WAAsB;AACxC,kBAAU,KAAK,QAAQ;AACvB,sBAAc;AAAA,MACf;AAEA,cAAQ,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,OAAO;AAAA,QACP;AAAA,MACD,CAAC;AAAA,IACF;AAAA,IACA;AAAA,MACC,GAAI,OACD;AAAA,QACA,QAAQ,KAAK;AAAA,QACb,gBAAgB,KAAK;AAAA,QACrB,iBAAiB,KAAK;AAAA,MACvB,IACC,CAAC;AAAA,MACJ,cAAc;AAAA,MACd,MAAM,SAAS,UAAU,MAAM,IAAI;AAAA,IACpC;AAAA,EACD;AACD;","names":["import_core","import_extra","import_graph","batch","i"]}
@@ -2,7 +2,7 @@ import {
2
2
  feedback,
3
3
  funnel,
4
4
  scorer
5
- } from "../../chunk-IHTWQEDR.js";
5
+ } from "../../chunk-QFE5BQH7.js";
6
6
  import "../../chunk-FMPF42Q4.js";
7
7
  import "../../chunk-BXGZFGZ4.js";
8
8
  import "../../chunk-AZDQPQ3V.js";
@@ -342,9 +342,11 @@ function _retrySource(source, opts, emitState) {
342
342
  const merged = makeMergedOptsMirror(opts);
343
343
  const getCfg = () => resolveRetryConfig(merged.current());
344
344
  const inner = _runRetryStateMachine(getCfg, () => source, a, emitState);
345
- return () => {
346
- inner();
347
- merged.unsub();
345
+ return {
346
+ onDeactivation: () => {
347
+ inner();
348
+ merged.unsub();
349
+ }
348
350
  };
349
351
  },
350
352
  {
@@ -368,9 +370,11 @@ function _retryFactory(factory, opts, emitState) {
368
370
  const merged = makeMergedOptsMirror(opts);
369
371
  const getCfg = () => resolveRetryConfig(merged.current());
370
372
  const inner = _runRetryStateMachine(getCfg, factory, a, emitState);
371
- return () => {
372
- inner();
373
- merged.unsub();
373
+ return {
374
+ onDeactivation: () => {
375
+ inner();
376
+ merged.unsub();
377
+ }
374
378
  };
375
379
  },
376
380
  {
@@ -435,7 +439,7 @@ function withStatus(src, options) {
435
439
  } else a.down([m]);
436
440
  }
437
441
  });
438
- return unsub;
442
+ return { onDeactivation: unsub };
439
443
  },
440
444
  {
441
445
  ...operatorOpts(),
@@ -617,11 +621,13 @@ function withTimeout(source, opts, extraOpts) {
617
621
  if (latestOpts != null) {
618
622
  attachSource();
619
623
  }
620
- return () => {
621
- stopped = true;
622
- timer.cancel();
623
- if (srcUnsub) srcUnsub();
624
- if (optsUnsub) optsUnsub();
624
+ return {
625
+ onDeactivation: () => {
626
+ stopped = true;
627
+ timer.cancel();
628
+ if (srcUnsub) srcUnsub();
629
+ if (optsUnsub) optsUnsub();
630
+ }
625
631
  };
626
632
  },
627
633
  {
@@ -831,7 +837,7 @@ function withBreaker(breaker, options) {
831
837
  }
832
838
  });
833
839
  syncState();
834
- return unsub;
840
+ return { onDeactivation: unsub };
835
841
  },
836
842
  {
837
843
  ...operatorOpts(),
@@ -984,8 +990,10 @@ function budgetGate(source, constraints, opts) {
984
990
  })
985
991
  );
986
992
  }
987
- return () => {
988
- for (const u of unsubs) u();
993
+ return {
994
+ onDeactivation: () => {
995
+ for (const u of unsubs) u();
996
+ }
989
997
  };
990
998
  },
991
999
  {
@@ -1114,9 +1122,11 @@ function fallback(source, fb, options) {
1114
1122
  } else a.down([m]);
1115
1123
  }
1116
1124
  });
1117
- return () => {
1118
- sourceUnsub?.();
1119
- fallbackUnsub?.();
1125
+ return {
1126
+ onDeactivation: () => {
1127
+ sourceUnsub?.();
1128
+ fallbackUnsub?.();
1129
+ }
1120
1130
  };
1121
1131
  },
1122
1132
  {
@@ -1566,11 +1576,13 @@ function rateLimiter(source, opts) {
1566
1576
  } else a.down([m]);
1567
1577
  }
1568
1578
  });
1569
- return () => {
1570
- terminated = true;
1571
- timer.cancel();
1572
- unsub();
1573
- optMirror.unsub();
1579
+ return {
1580
+ onDeactivation: () => {
1581
+ terminated = true;
1582
+ timer.cancel();
1583
+ unsub();
1584
+ optMirror.unsub();
1585
+ }
1574
1586
  };
1575
1587
  },
1576
1588
  {