@graphrefly/graphrefly 0.25.0 → 0.27.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.
Files changed (231) hide show
  1. package/README.md +8 -0
  2. package/dist/ai-CaR_912Q.d.cts +1033 -0
  3. package/dist/ai-WlRltJV7.d.ts +1033 -0
  4. package/dist/audit-ClmqGOCx.d.cts +245 -0
  5. package/dist/audit-DRlSzBu9.d.ts +245 -0
  6. package/dist/{chunk-QOWVNWOC.js → chunk-3ZWCKRHX.js} +27 -25
  7. package/dist/{chunk-QOWVNWOC.js.map → chunk-3ZWCKRHX.js.map} +1 -1
  8. package/dist/chunk-APFNLIRG.js +62 -0
  9. package/dist/chunk-APFNLIRG.js.map +1 -0
  10. package/dist/chunk-AT5LKYNL.js +395 -0
  11. package/dist/chunk-AT5LKYNL.js.map +1 -0
  12. package/dist/{chunk-IAHGTNOZ.js → chunk-BQ6RQQFF.js} +351 -2095
  13. package/dist/chunk-BQ6RQQFF.js.map +1 -0
  14. package/dist/{chunk-L2GLW2U7.js → chunk-BVZYTZ5H.js} +9 -103
  15. package/dist/chunk-BVZYTZ5H.js.map +1 -0
  16. package/dist/{chunk-EVR6UFUV.js → chunk-DST5DKZS.js} +19 -15
  17. package/dist/{chunk-EVR6UFUV.js.map → chunk-DST5DKZS.js.map} +1 -1
  18. package/dist/{chunk-TKE3JGOH.js → chunk-GTE6PWRZ.js} +5 -692
  19. package/dist/chunk-GTE6PWRZ.js.map +1 -0
  20. package/dist/chunk-HXZEYDUR.js +94 -0
  21. package/dist/chunk-HXZEYDUR.js.map +1 -0
  22. package/dist/chunk-J22W6HV3.js +107 -0
  23. package/dist/chunk-J22W6HV3.js.map +1 -0
  24. package/dist/{chunk-PY4XCDLR.js → chunk-J2VBW3DZ.js} +6 -95
  25. package/dist/chunk-J2VBW3DZ.js.map +1 -0
  26. package/dist/{chunk-HWPIFSW2.js → chunk-JSCT3CR4.js} +6 -4
  27. package/dist/{chunk-HWPIFSW2.js.map → chunk-JSCT3CR4.js.map} +1 -1
  28. package/dist/chunk-JWBCY4NC.js +330 -0
  29. package/dist/chunk-JWBCY4NC.js.map +1 -0
  30. package/dist/chunk-K2AUJHVP.js +2251 -0
  31. package/dist/chunk-K2AUJHVP.js.map +1 -0
  32. package/dist/chunk-MJ2NKQQL.js +119 -0
  33. package/dist/chunk-MJ2NKQQL.js.map +1 -0
  34. package/dist/chunk-N6UR7YVY.js +198 -0
  35. package/dist/chunk-N6UR7YVY.js.map +1 -0
  36. package/dist/chunk-NC6S43JJ.js +456 -0
  37. package/dist/chunk-NC6S43JJ.js.map +1 -0
  38. package/dist/chunk-OFVJBJXR.js +98 -0
  39. package/dist/chunk-OFVJBJXR.js.map +1 -0
  40. package/dist/chunk-OHISZPOJ.js +97 -0
  41. package/dist/chunk-OHISZPOJ.js.map +1 -0
  42. package/dist/chunk-OU5CQKNW.js +102 -0
  43. package/dist/chunk-OU5CQKNW.js.map +1 -0
  44. package/dist/{chunk-XOFWRC73.js → chunk-PF7GRZMW.js} +316 -21
  45. package/dist/chunk-PF7GRZMW.js.map +1 -0
  46. package/dist/{chunk-5DJTTKX3.js → chunk-PHOUUNK7.js} +74 -111
  47. package/dist/chunk-PHOUUNK7.js.map +1 -0
  48. package/dist/chunk-RNHBMHKA.js +1665 -0
  49. package/dist/chunk-RNHBMHKA.js.map +1 -0
  50. package/dist/chunk-SX52TAR4.js +110 -0
  51. package/dist/chunk-SX52TAR4.js.map +1 -0
  52. package/dist/{chunk-H4RVA4VE.js → chunk-VYPWMZ6H.js} +2 -2
  53. package/dist/chunk-WBZOVTYK.js +171 -0
  54. package/dist/chunk-WBZOVTYK.js.map +1 -0
  55. package/dist/chunk-WKNUIZOY.js +354 -0
  56. package/dist/chunk-WKNUIZOY.js.map +1 -0
  57. package/dist/chunk-X3VMZYBT.js +713 -0
  58. package/dist/chunk-X3VMZYBT.js.map +1 -0
  59. package/dist/chunk-X5R3GL6H.js +525 -0
  60. package/dist/chunk-X5R3GL6H.js.map +1 -0
  61. package/dist/chunk-XGPU467M.js +136 -0
  62. package/dist/chunk-XGPU467M.js.map +1 -0
  63. package/dist/compat/index.cjs +7656 -0
  64. package/dist/compat/index.cjs.map +1 -0
  65. package/dist/compat/index.d.cts +18 -0
  66. package/dist/compat/index.d.ts +18 -0
  67. package/dist/compat/index.js +50 -0
  68. package/dist/compat/index.js.map +1 -0
  69. package/dist/compat/jotai/index.cjs +2048 -0
  70. package/dist/compat/jotai/index.cjs.map +1 -0
  71. package/dist/compat/jotai/index.d.cts +2 -0
  72. package/dist/compat/jotai/index.d.ts +2 -0
  73. package/dist/compat/jotai/index.js +9 -0
  74. package/dist/compat/jotai/index.js.map +1 -0
  75. package/dist/compat/nanostores/index.cjs +2175 -0
  76. package/dist/compat/nanostores/index.cjs.map +1 -0
  77. package/dist/compat/nanostores/index.d.cts +2 -0
  78. package/dist/compat/nanostores/index.d.ts +2 -0
  79. package/dist/compat/nanostores/index.js +23 -0
  80. package/dist/compat/nanostores/index.js.map +1 -0
  81. package/dist/compat/nestjs/index.cjs +350 -16
  82. package/dist/compat/nestjs/index.cjs.map +1 -1
  83. package/dist/compat/nestjs/index.d.cts +6 -6
  84. package/dist/compat/nestjs/index.d.ts +6 -6
  85. package/dist/compat/nestjs/index.js +11 -9
  86. package/dist/compat/react/index.cjs +141 -0
  87. package/dist/compat/react/index.cjs.map +1 -0
  88. package/dist/compat/react/index.d.cts +2 -0
  89. package/dist/compat/react/index.d.ts +2 -0
  90. package/dist/compat/react/index.js +12 -0
  91. package/dist/compat/react/index.js.map +1 -0
  92. package/dist/compat/solid/index.cjs +128 -0
  93. package/dist/compat/solid/index.cjs.map +1 -0
  94. package/dist/compat/solid/index.d.cts +2 -0
  95. package/dist/compat/solid/index.d.ts +2 -0
  96. package/dist/compat/solid/index.js +12 -0
  97. package/dist/compat/solid/index.js.map +1 -0
  98. package/dist/compat/svelte/index.cjs +131 -0
  99. package/dist/compat/svelte/index.cjs.map +1 -0
  100. package/dist/compat/svelte/index.d.cts +2 -0
  101. package/dist/compat/svelte/index.d.ts +2 -0
  102. package/dist/compat/svelte/index.js +12 -0
  103. package/dist/compat/svelte/index.js.map +1 -0
  104. package/dist/compat/vue/index.cjs +146 -0
  105. package/dist/compat/vue/index.cjs.map +1 -0
  106. package/dist/compat/vue/index.d.cts +3 -0
  107. package/dist/compat/vue/index.d.ts +3 -0
  108. package/dist/compat/vue/index.js +12 -0
  109. package/dist/compat/vue/index.js.map +1 -0
  110. package/dist/compat/zustand/index.cjs +4931 -0
  111. package/dist/compat/zustand/index.cjs.map +1 -0
  112. package/dist/compat/zustand/index.d.cts +5 -0
  113. package/dist/compat/zustand/index.d.ts +5 -0
  114. package/dist/compat/zustand/index.js +12 -0
  115. package/dist/compat/zustand/index.js.map +1 -0
  116. package/dist/composite-C7PcQvcs.d.cts +303 -0
  117. package/dist/composite-aUCvjZVR.d.ts +303 -0
  118. package/dist/core/index.cjs +53 -4
  119. package/dist/core/index.cjs.map +1 -1
  120. package/dist/core/index.d.cts +4 -3
  121. package/dist/core/index.d.ts +4 -3
  122. package/dist/core/index.js +26 -24
  123. package/dist/demo-shell-BDkOptd6.d.ts +102 -0
  124. package/dist/demo-shell-Crid1WdR.d.cts +102 -0
  125. package/dist/extra/index.cjs +222 -110
  126. package/dist/extra/index.cjs.map +1 -1
  127. package/dist/extra/index.d.cts +6 -4
  128. package/dist/extra/index.d.ts +6 -4
  129. package/dist/extra/index.js +72 -65
  130. package/dist/extra/sources.cjs +2486 -0
  131. package/dist/extra/sources.cjs.map +1 -0
  132. package/dist/extra/sources.d.cts +465 -0
  133. package/dist/extra/sources.d.ts +465 -0
  134. package/dist/extra/sources.js +57 -0
  135. package/dist/extra/sources.js.map +1 -0
  136. package/dist/graph/index.cjs +408 -14
  137. package/dist/graph/index.cjs.map +1 -1
  138. package/dist/graph/index.d.cts +5 -5
  139. package/dist/graph/index.d.ts +5 -5
  140. package/dist/graph/index.js +13 -5
  141. package/dist/{graph-D-3JIQme.d.cts → graph-CCwGKLCm.d.ts} +195 -4
  142. package/dist/{graph-B6NFqv3z.d.ts → graph-DNCrvZSn.d.cts} +195 -4
  143. package/dist/index-3lsddbbS.d.ts +86 -0
  144. package/dist/index-B1tloyhO.d.cts +34 -0
  145. package/dist/{index-CYkjxu3s.d.ts → index-B6D3QNSA.d.ts} +33 -4
  146. package/dist/index-B6EhDnjH.d.cts +37 -0
  147. package/dist/index-B9B7_HEY.d.ts +37 -0
  148. package/dist/{index-Ds23Wvou.d.ts → index-BHlKbUwO.d.cts} +131 -883
  149. package/dist/{index-DiobMNwE.d.ts → index-BPVt8kqc.d.ts} +3 -3
  150. package/dist/index-BaSM3aYt.d.ts +195 -0
  151. package/dist/index-BuEoe-Qu.d.ts +121 -0
  152. package/dist/{index-Ch0IpIO0.d.cts → index-BwfLUNw4.d.ts} +131 -883
  153. package/dist/index-ByQxazQJ.d.cts +86 -0
  154. package/dist/index-C0svESO4.d.ts +127 -0
  155. package/dist/{index-OXImXMq6.d.ts → index-C8oil6M6.d.ts} +18 -196
  156. package/dist/{index-DKE1EATr.d.cts → index-CI3DprxP.d.cts} +18 -196
  157. package/dist/{index-AMWewNDe.d.cts → index-CO8uBlUh.d.cts} +33 -4
  158. package/dist/index-CxFrXH4m.d.ts +45 -0
  159. package/dist/index-D8wS_PeY.d.cts +121 -0
  160. package/dist/index-DO_6JN9Z.d.cts +127 -0
  161. package/dist/index-DVGiGFGT.d.cts +195 -0
  162. package/dist/index-DYme44FM.d.cts +44 -0
  163. package/dist/{index-J7Kc0oIQ.d.cts → index-DlLp-2Xn.d.cts} +3 -3
  164. package/dist/index-Dzk2hrlR.d.ts +44 -0
  165. package/dist/index-VHqptjhu.d.cts +45 -0
  166. package/dist/index-VdHQMPy1.d.ts +36 -0
  167. package/dist/index-Xi3u0HCQ.d.cts +36 -0
  168. package/dist/index-wEn0eFe8.d.ts +34 -0
  169. package/dist/index.cjs +1780 -176
  170. package/dist/index.cjs.map +1 -1
  171. package/dist/index.d.cts +784 -2082
  172. package/dist/index.d.ts +784 -2082
  173. package/dist/index.js +955 -4349
  174. package/dist/index.js.map +1 -1
  175. package/dist/memory-C6Z2tGpC.d.cts +139 -0
  176. package/dist/memory-li6FL5RM.d.ts +139 -0
  177. package/dist/messaging-Gt4LPbyA.d.cts +269 -0
  178. package/dist/messaging-XDoYablx.d.ts +269 -0
  179. package/dist/{meta-DWbkoq1s.d.cts → meta-BxCA7rcr.d.cts} +1 -1
  180. package/dist/{meta-CnkLA_43.d.ts → meta-CbznRPYJ.d.ts} +1 -1
  181. package/dist/{node-B-f-Lu-k.d.cts → node-BmerH3kS.d.cts} +26 -1
  182. package/dist/{node-B-f-Lu-k.d.ts → node-BmerH3kS.d.ts} +26 -1
  183. package/dist/{observable-uP-wy_uK.d.ts → observable-BgGUwcqp.d.ts} +1 -1
  184. package/dist/{observable-DBnrwcar.d.cts → observable-DJt_AxzQ.d.cts} +1 -1
  185. package/dist/patterns/ai.cjs +7930 -0
  186. package/dist/patterns/ai.cjs.map +1 -0
  187. package/dist/patterns/ai.d.cts +10 -0
  188. package/dist/patterns/ai.d.ts +10 -0
  189. package/dist/patterns/ai.js +71 -0
  190. package/dist/patterns/ai.js.map +1 -0
  191. package/dist/patterns/audit.cjs +5805 -0
  192. package/dist/patterns/audit.cjs.map +1 -0
  193. package/dist/patterns/audit.d.cts +6 -0
  194. package/dist/patterns/audit.d.ts +6 -0
  195. package/dist/patterns/audit.js +29 -0
  196. package/dist/patterns/audit.js.map +1 -0
  197. package/dist/patterns/demo-shell.cjs +5604 -0
  198. package/dist/patterns/demo-shell.cjs.map +1 -0
  199. package/dist/patterns/demo-shell.d.cts +6 -0
  200. package/dist/patterns/demo-shell.d.ts +6 -0
  201. package/dist/patterns/demo-shell.js +15 -0
  202. package/dist/patterns/demo-shell.js.map +1 -0
  203. package/dist/patterns/memory.cjs +5283 -0
  204. package/dist/patterns/memory.cjs.map +1 -0
  205. package/dist/patterns/memory.d.cts +5 -0
  206. package/dist/patterns/memory.d.ts +5 -0
  207. package/dist/patterns/memory.js +20 -0
  208. package/dist/patterns/memory.js.map +1 -0
  209. package/dist/patterns/reactive-layout/index.cjs +355 -13
  210. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  211. package/dist/patterns/reactive-layout/index.d.cts +6 -5
  212. package/dist/patterns/reactive-layout/index.d.ts +6 -5
  213. package/dist/patterns/reactive-layout/index.js +15 -12
  214. package/dist/reactive-layout-MQP--J3F.d.cts +183 -0
  215. package/dist/reactive-layout-u5Ulnqag.d.ts +183 -0
  216. package/dist/{storage-BuTdpCI1.d.cts → storage-CMjUUuxn.d.ts} +10 -2
  217. package/dist/{storage-F2X1U1x0.d.ts → storage-DdWlZo6U.d.cts} +10 -2
  218. package/dist/sugar-CCOxXK1e.d.ts +201 -0
  219. package/dist/sugar-D02n5JjF.d.cts +201 -0
  220. package/package.json +63 -3
  221. package/dist/chunk-5DJTTKX3.js.map +0 -1
  222. package/dist/chunk-IAHGTNOZ.js.map +0 -1
  223. package/dist/chunk-L2GLW2U7.js.map +0 -1
  224. package/dist/chunk-MW4VAKAO.js +0 -47
  225. package/dist/chunk-MW4VAKAO.js.map +0 -1
  226. package/dist/chunk-PY4XCDLR.js.map +0 -1
  227. package/dist/chunk-TKE3JGOH.js.map +0 -1
  228. package/dist/chunk-XOFWRC73.js.map +0 -1
  229. package/dist/index-BJB7t9gg.d.cts +0 -392
  230. package/dist/index-C-TXEa7C.d.ts +0 -392
  231. /package/dist/{chunk-H4RVA4VE.js.map → chunk-VYPWMZ6H.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -101,15 +101,20 @@ __export(index_exports, {
101
101
  ResettableTimer: () => ResettableTimer,
102
102
  SIZEOF_OVERHEAD: () => OVERHEAD,
103
103
  SIZEOF_SYMBOL: () => SIZEOF_SYMBOL,
104
+ SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
105
+ SNAPSHOT_WIRE_VERSION: () => SNAPSHOT_WIRE_VERSION,
104
106
  START: () => START,
105
107
  START_MSG: () => START_MSG,
108
+ SurfaceError: () => SurfaceError,
106
109
  TEARDOWN: () => TEARDOWN,
107
110
  TEARDOWN_MSG: () => TEARDOWN_MSG,
108
111
  TEARDOWN_ONLY_BATCH: () => TEARDOWN_ONLY_BATCH,
109
112
  TimeoutError: () => TimeoutError,
110
113
  accessHintForGuard: () => accessHintForGuard,
114
+ accountability: () => audit_exports,
111
115
  advanceVersion: () => advanceVersion,
112
116
  ai: () => ai_exports,
117
+ asSurfaceError: () => asSurfaceError,
113
118
  audit: () => audit,
114
119
  autoTrackNode: () => autoTrackNode,
115
120
  batch: () => batch,
@@ -133,6 +138,7 @@ __export(index_exports, {
133
138
  cqrs: () => cqrs_exports,
134
139
  createDagCborCodec: () => createDagCborCodec,
135
140
  createDagCborZstdCodec: () => createDagCborZstdCodec,
141
+ createGraph: () => createGraph,
136
142
  createTransport: () => createTransport,
137
143
  createVersioning: () => createVersioning,
138
144
  createWatermarkController: () => createWatermarkController,
@@ -144,11 +150,13 @@ __export(index_exports, {
144
150
  defaultConfig: () => defaultConfig,
145
151
  defaultHash: () => defaultHash,
146
152
  delay: () => delay,
153
+ deleteSnapshot: () => deleteSnapshot,
147
154
  demoShell: () => demo_shell_exports,
148
155
  derived: () => derived,
149
156
  deserializeError: () => deserializeError,
150
157
  dictStorage: () => dictStorage,
151
158
  diffForWAL: () => diffForWAL,
159
+ diffSnapshots: () => diffSnapshots,
152
160
  distill: () => distill,
153
161
  distinctUntilChanged: () => distinctUntilChanged,
154
162
  domainTemplates: () => domain_templates_exports,
@@ -160,6 +168,7 @@ __export(index_exports, {
160
168
  encodeEnvelope: () => encodeEnvelope,
161
169
  escapeRegexChar: () => escapeRegexChar,
162
170
  exhaustMap: () => exhaustMap,
171
+ explainPath: () => explainPath,
163
172
  exponential: () => exponential,
164
173
  externalBundle: () => externalBundle,
165
174
  externalProducer: () => externalProducer,
@@ -215,6 +224,7 @@ __export(index_exports, {
215
224
  graph: () => graph_exports,
216
225
  graphProfile: () => graphProfile,
217
226
  graphspec: () => graphspec_exports,
227
+ guarded: () => guarded_execution_exports,
218
228
  harness: () => harness_exports,
219
229
  indexedDbStorage: () => indexedDbStorage,
220
230
  interval: () => interval,
@@ -224,7 +234,9 @@ __export(index_exports, {
224
234
  keepalive: () => keepalive,
225
235
  last: () => last,
226
236
  layout: () => reactive_layout_exports,
237
+ lens: () => lens_exports,
227
238
  linear: () => linear,
239
+ listSnapshots: () => listSnapshots,
228
240
  lru: () => lru,
229
241
  map: () => map2,
230
242
  matchesAnyPattern: () => matchesAnyPattern,
@@ -274,11 +286,15 @@ __export(index_exports, {
274
286
  replay: () => replay,
275
287
  replayWAL: () => replayWAL,
276
288
  rescue: () => rescue,
289
+ resilientPipeline: () => resilient_pipeline_exports,
277
290
  resolveBackoffPreset: () => resolveBackoffPreset,
278
291
  resolveDescribeFields: () => resolveDescribeFields,
292
+ restoreSnapshot: () => restoreSnapshot,
279
293
  retry: () => retry,
280
294
  retrySource: () => retrySource,
295
+ runReduction: () => runReduction,
281
296
  sample: () => sample,
297
+ saveSnapshot: () => saveSnapshot,
282
298
  scan: () => scan,
283
299
  serializeError: () => serializeError,
284
300
  share: () => share,
@@ -290,6 +306,7 @@ __export(index_exports, {
290
306
  solid: () => solid_exports,
291
307
  sqliteStorage: () => sqliteStorage,
292
308
  state: () => state,
309
+ surface: () => surface_exports,
293
310
  svelte: () => svelte_exports,
294
311
  switchMap: () => switchMap,
295
312
  take: () => take,
@@ -327,6 +344,7 @@ __export(index_exports, {
327
344
  version: () => version,
328
345
  vue: () => vue_exports,
329
346
  wallClockNs: () => wallClockNs,
347
+ watchTopologyTree: () => watchTopologyTree,
330
348
  window: () => window,
331
349
  windowCount: () => windowCount,
332
350
  windowTime: () => windowTime,
@@ -1338,6 +1356,12 @@ var NodeImpl = class _NodeImpl {
1338
1356
  _autoError;
1339
1357
  _pausable;
1340
1358
  _guard;
1359
+ /**
1360
+ * @internal Additional guards stacked at runtime via {@link NodeImpl._pushGuard}
1361
+ * (e.g. by `policyEnforcer({ mode: "enforce" })`, roadmap §9.2). Effective
1362
+ * write/signal/observe checks AND the original `_guard` with every entry here.
1363
+ */
1364
+ _extraGuards;
1341
1365
  _hashFn;
1342
1366
  _versioning;
1343
1367
  /**
@@ -1511,18 +1535,61 @@ var NodeImpl = class _NodeImpl {
1511
1535
  if (this._inspectorHooks?.size === 0) this._inspectorHooks = void 0;
1512
1536
  };
1513
1537
  }
1538
+ /**
1539
+ * @internal Push an additional guard onto this node. Effective enforcement
1540
+ * is the AND of `_guard` and every guard pushed via this hook — any one
1541
+ * rejecting throws {@link GuardDenied}. Returns a disposer that removes
1542
+ * the pushed guard. Multiple guards may be stacked simultaneously.
1543
+ *
1544
+ * Used by `policyEnforcer({ mode: "enforce" })` (roadmap §9.2) to overlay
1545
+ * runtime constraint enforcement onto an existing graph without rebuilding
1546
+ * its nodes. Pre-1.0 internal API; not part of the public surface.
1547
+ *
1548
+ * **Identity semantics:** guards are tracked in a `Set`, so pushing the
1549
+ * same `NodeGuard` reference twice is a single registration. Wrap each
1550
+ * push in a unique closure if independent stacking is needed.
1551
+ *
1552
+ * **Iteration order:** insertion-ordered (`Set` semantics). Determinism
1553
+ * follows from single-threaded JS execution; nested re-entry from inside
1554
+ * a guard body (push/pop while iterating) is undefined-but-survivable.
1555
+ */
1556
+ _pushGuard(guard) {
1557
+ if (this._extraGuards == null) this._extraGuards = /* @__PURE__ */ new Set();
1558
+ this._extraGuards.add(guard);
1559
+ return () => {
1560
+ this._extraGuards?.delete(guard);
1561
+ if (this._extraGuards?.size === 0) this._extraGuards = void 0;
1562
+ };
1563
+ }
1514
1564
  allowsObserve(actor) {
1515
- if (this._guard == null) return true;
1516
- return this._guard(normalizeActor(actor), "observe");
1565
+ if (this._guard == null && this._extraGuards == null) return true;
1566
+ const a = normalizeActor(actor);
1567
+ if (this._guard != null && !this._guard(a, "observe")) return false;
1568
+ if (this._extraGuards != null) {
1569
+ for (const eg of this._extraGuards) {
1570
+ if (!eg(a, "observe")) return false;
1571
+ }
1572
+ }
1573
+ return true;
1517
1574
  }
1518
1575
  // --- Guard helper ---
1519
1576
  _checkGuard(options) {
1520
- if (options?.internal || this._guard == null) return;
1577
+ if (options?.internal) return;
1578
+ const hasGuard = this._guard != null || this._extraGuards != null;
1579
+ const hasActor = options?.actor != null;
1580
+ if (!hasGuard && !hasActor) return;
1521
1581
  const actor = normalizeActor(options?.actor);
1522
1582
  const action2 = options?.delivery === "signal" ? "signal" : "write";
1523
- if (!this._guard(actor, action2)) {
1583
+ if (this._guard != null && !this._guard(actor, action2)) {
1524
1584
  throw new GuardDenied({ actor, action: action2, nodeName: this.name });
1525
1585
  }
1586
+ if (this._extraGuards != null) {
1587
+ for (const eg of this._extraGuards) {
1588
+ if (!eg(actor, action2)) {
1589
+ throw new GuardDenied({ actor, action: action2, nodeName: this.name });
1590
+ }
1591
+ }
1592
+ }
1526
1593
  this._lastMutation = { actor, timestamp_ns: wallClockNs() };
1527
1594
  }
1528
1595
  // --- Public transport ---
@@ -3046,10 +3113,6 @@ function SagaHandler(cqrsName, sagaName, eventNames) {
3046
3113
  };
3047
3114
  }
3048
3115
 
3049
- // src/extra/sources.ts
3050
- var import_node_fs = require("fs");
3051
- var import_node_path = require("path");
3052
-
3053
3116
  // src/extra/cron.ts
3054
3117
  function parseField(field, min, max) {
3055
3118
  const result = /* @__PURE__ */ new Set();
@@ -3287,99 +3350,6 @@ function fromEvent(target, type, opts) {
3287
3350
  return () => target.removeEventListener(type, handler, options);
3288
3351
  }, sourceOpts(rest));
3289
3352
  }
3290
- function fromFSWatch(paths, opts) {
3291
- const list = Array.isArray(paths) ? paths : [paths];
3292
- if (list.length === 0) {
3293
- throw new RangeError("fromFSWatch expects at least one path");
3294
- }
3295
- const { recursive = true, debounce: debounce2 = 100, include, exclude, ...rest } = opts ?? {};
3296
- const includePatterns = include?.map(globToRegExp) ?? [];
3297
- const excludePatterns = (exclude ?? ["**/node_modules/**", "**/.git/**", "**/dist/**"]).map(
3298
- globToRegExp
3299
- );
3300
- return producer((a) => {
3301
- const pending = /* @__PURE__ */ new Map();
3302
- const watchers = [];
3303
- let stopped = false;
3304
- let terminalEmitted = false;
3305
- let generation = 0;
3306
- const closeWatchers = () => {
3307
- for (const watcher of watchers.splice(0)) watcher.close();
3308
- };
3309
- const emitError = (err) => {
3310
- if (terminalEmitted) return;
3311
- terminalEmitted = true;
3312
- stopped = true;
3313
- if (timer !== void 0) clearTimeout(timer);
3314
- timer = void 0;
3315
- pending.clear();
3316
- closeWatchers();
3317
- a.down([[ERROR, err]]);
3318
- };
3319
- let timer;
3320
- const flush = (token) => {
3321
- timer = void 0;
3322
- if (stopped || terminalEmitted) return;
3323
- if (pending.size === 0) return;
3324
- const batchMessages = [];
3325
- for (const evt of pending.values()) batchMessages.push([DATA, evt]);
3326
- pending.clear();
3327
- if (stopped || terminalEmitted || token !== generation) return;
3328
- a.down(batchMessages);
3329
- };
3330
- try {
3331
- for (const basePath of list) {
3332
- const watcher = (0, import_node_fs.watch)(
3333
- basePath,
3334
- { recursive },
3335
- (eventType, fileName) => {
3336
- if (stopped || terminalEmitted) return;
3337
- if (fileName == null) return;
3338
- const rel = String(fileName).replaceAll("\\", "/");
3339
- const abs = (0, import_node_path.resolve)(basePath, String(fileName));
3340
- const normalized = abs.replaceAll("\\", "/");
3341
- const root = (0, import_node_path.resolve)(basePath).replaceAll("\\", "/");
3342
- const relForMatch = rel.startsWith("./") ? rel.slice(2) : rel;
3343
- const included = includePatterns.length === 0 || matchesAnyPattern(normalized, includePatterns) || matchesAnyPattern(relForMatch, includePatterns);
3344
- if (!included) return;
3345
- const excluded = matchesAnyPattern(normalized, excludePatterns) || matchesAnyPattern(relForMatch, excludePatterns);
3346
- if (excluded) return;
3347
- let kind = "change";
3348
- if (eventType === "rename") {
3349
- try {
3350
- kind = (0, import_node_fs.existsSync)(normalized) ? "create" : "delete";
3351
- } catch {
3352
- kind = "rename";
3353
- }
3354
- }
3355
- pending.set(normalized, {
3356
- type: kind,
3357
- path: normalized,
3358
- root,
3359
- relative_path: relForMatch,
3360
- timestamp_ns: wallClockNs()
3361
- });
3362
- if (timer !== void 0) clearTimeout(timer);
3363
- const token = generation;
3364
- timer = setTimeout(() => flush(token), debounce2);
3365
- }
3366
- );
3367
- watcher.on("error", (err) => emitError(err));
3368
- watchers.push(watcher);
3369
- }
3370
- } catch (err) {
3371
- emitError(err);
3372
- }
3373
- return () => {
3374
- stopped = true;
3375
- generation += 1;
3376
- if (timer !== void 0) clearTimeout(timer);
3377
- timer = void 0;
3378
- closeWatchers();
3379
- pending.clear();
3380
- };
3381
- }, sourceOpts(rest));
3382
- }
3383
3353
  function fromIter(iterable, opts) {
3384
3354
  return producer((a) => {
3385
3355
  let cancelled = false;
@@ -4594,6 +4564,200 @@ var RingBuffer = class {
4594
4564
  }
4595
4565
  };
4596
4566
 
4567
+ // src/graph/explain.ts
4568
+ function explainPath(described, from, to, opts = {}) {
4569
+ const fromExists = from in described.nodes;
4570
+ const toExists = to in described.nodes;
4571
+ if (!fromExists) return makeFailure(from, to, "no-such-from");
4572
+ if (!toExists) return makeFailure(from, to, "no-such-to");
4573
+ const maxDepth = opts.maxDepth;
4574
+ if (maxDepth != null && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
4575
+ throw new Error(`explainPath: maxDepth must be an integer >= 0`);
4576
+ }
4577
+ if (from === to) {
4578
+ if (opts.findCycle === true) {
4579
+ const cycle = findShortestCycle(described, from, opts);
4580
+ if (cycle != null) return cycle;
4581
+ }
4582
+ const step = buildStep(from, described.nodes[from], 0, opts);
4583
+ return makeSuccess(from, to, [step]);
4584
+ }
4585
+ if (maxDepth === 0) return makeFailure(from, to, "no-path");
4586
+ const result = bfsShortestPath(described, from, to, maxDepth);
4587
+ if (!result.found) {
4588
+ return makeFailure(from, to, result.truncated ? "max-depth-exceeded" : "no-path");
4589
+ }
4590
+ return makeSuccess(from, to, materializeSteps(described, result.pathOrder, opts));
4591
+ }
4592
+ function bfsShortestPath(described, from, to, maxDepth) {
4593
+ const pred = /* @__PURE__ */ new Map();
4594
+ const queue = [{ path: to, depth: 0 }];
4595
+ const visited = /* @__PURE__ */ new Set([to]);
4596
+ let head = 0;
4597
+ let truncated = false;
4598
+ while (head < queue.length) {
4599
+ const cur = queue[head++];
4600
+ if (cur.path === from) break;
4601
+ if (maxDepth != null && cur.depth >= maxDepth) {
4602
+ const node3 = described.nodes[cur.path];
4603
+ if (node3?.deps && node3.deps.length > 0) truncated = true;
4604
+ continue;
4605
+ }
4606
+ const node2 = described.nodes[cur.path];
4607
+ if (node2 == null) continue;
4608
+ const deps = node2.deps ?? [];
4609
+ const slots = /* @__PURE__ */ new Map();
4610
+ for (let i = 0; i < deps.length; i++) {
4611
+ const dep = deps[i];
4612
+ if (!dep) continue;
4613
+ let arr = slots.get(dep);
4614
+ if (arr == null) {
4615
+ arr = [];
4616
+ slots.set(dep, arr);
4617
+ }
4618
+ arr.push(i);
4619
+ }
4620
+ for (const [dep, indices] of slots) {
4621
+ if (visited.has(dep)) continue;
4622
+ visited.add(dep);
4623
+ pred.set(dep, { from: cur.path, depIndices: indices });
4624
+ queue.push({ path: dep, depth: cur.depth + 1 });
4625
+ }
4626
+ }
4627
+ if (!pred.has(from)) {
4628
+ return { found: false, pathOrder: [], truncated };
4629
+ }
4630
+ const pathOrder = [{ path: from }];
4631
+ let cursor = from;
4632
+ while (cursor !== to) {
4633
+ const p = pred.get(cursor);
4634
+ if (p == null) return { found: false, pathOrder: [], truncated: false };
4635
+ pathOrder[pathOrder.length - 1].depIndices = p.depIndices;
4636
+ pathOrder.push({ path: p.from });
4637
+ cursor = p.from;
4638
+ }
4639
+ return { found: true, pathOrder, truncated: false };
4640
+ }
4641
+ function findShortestCycle(described, start, opts) {
4642
+ const startNode = described.nodes[start];
4643
+ if (startNode == null) return null;
4644
+ const startDeps = startNode.deps ?? [];
4645
+ const selfSlots = [];
4646
+ for (let i = 0; i < startDeps.length; i++) if (startDeps[i] === start) selfSlots.push(i);
4647
+ if (selfSlots.length > 0) {
4648
+ const step0 = buildStep(start, startNode, 0, opts);
4649
+ step0.dep_index = selfSlots[0];
4650
+ const step1 = buildStep(start, startNode, 1, opts);
4651
+ return makeSuccess(start, start, [step0, step1]);
4652
+ }
4653
+ let best = null;
4654
+ for (let i = 0; i < startDeps.length; i++) {
4655
+ const dep = startDeps[i];
4656
+ if (!dep || dep === start) continue;
4657
+ const sub = bfsShortestPath(described, dep, start, opts.maxDepth);
4658
+ if (!sub.found) continue;
4659
+ if (best == null || sub.pathOrder.length < best.pathOrder.length) {
4660
+ best = sub;
4661
+ best = {
4662
+ found: true,
4663
+ pathOrder: [{ path: start, depIndices: [i] }, ...sub.pathOrder],
4664
+ truncated: false
4665
+ };
4666
+ }
4667
+ }
4668
+ if (best == null) return null;
4669
+ return makeSuccess(start, start, materializeSteps(described, best.pathOrder, opts));
4670
+ }
4671
+ function materializeSteps(described, pathOrder, opts) {
4672
+ return pathOrder.map((entry, i) => {
4673
+ const node2 = described.nodes[entry.path];
4674
+ const step = buildStep(entry.path, node2, i, opts);
4675
+ if (entry.depIndices != null && entry.depIndices.length > 0) {
4676
+ step.dep_index = entry.depIndices[0];
4677
+ if (entry.depIndices.length > 1) step.dep_indices = [...entry.depIndices];
4678
+ }
4679
+ return step;
4680
+ });
4681
+ }
4682
+ function buildStep(path, node2, hop, opts) {
4683
+ const step = {
4684
+ path,
4685
+ type: node2.type,
4686
+ hop
4687
+ };
4688
+ if (node2.status !== void 0) step.status = node2.status;
4689
+ if ("value" in node2) step.value = node2.value;
4690
+ if (node2.v != null) step.v = node2.v;
4691
+ const annotation = opts.annotations?.get(path) ?? node2.reason;
4692
+ if (annotation != null) step.reason = annotation;
4693
+ const lastMutation = opts.lastMutations?.get(path) ?? node2.lastMutation;
4694
+ if (lastMutation != null) step.lastMutation = lastMutation;
4695
+ return step;
4696
+ }
4697
+ function makeSuccess(from, to, steps) {
4698
+ return finalize(from, to, true, "ok", steps);
4699
+ }
4700
+ function makeFailure(from, to, reason) {
4701
+ return finalize(from, to, false, reason, []);
4702
+ }
4703
+ function finalize(from, to, found, reason, steps) {
4704
+ const text = renderChain(from, to, found, reason, steps);
4705
+ return {
4706
+ from,
4707
+ to,
4708
+ found,
4709
+ reason,
4710
+ steps,
4711
+ text,
4712
+ toJSON() {
4713
+ return { from, to, found, reason, steps };
4714
+ }
4715
+ };
4716
+ }
4717
+ function renderChain(from, to, found, reason, steps) {
4718
+ if (!found) {
4719
+ switch (reason) {
4720
+ case "no-such-from":
4721
+ return `explainPath: no node named "${from}"`;
4722
+ case "no-such-to":
4723
+ return `explainPath: no node named "${to}"`;
4724
+ case "max-depth-exceeded":
4725
+ return `explainPath: no path from "${from}" to "${to}" within maxDepth`;
4726
+ default:
4727
+ return `explainPath: no path from "${from}" to "${to}"`;
4728
+ }
4729
+ }
4730
+ const lines = [`Causal path: ${from} \u2192 ${to} (${steps.length} step(s))`];
4731
+ for (const step of steps) {
4732
+ const arrow = step.hop === 0 ? "\xB7" : "\u2193";
4733
+ const head = ` ${arrow} ${step.path} (${step.type}${step.status ? `/${step.status}` : ""})`;
4734
+ lines.push(head);
4735
+ if ("value" in step) {
4736
+ lines.push(` value: ${formatValue(step.value)}`);
4737
+ }
4738
+ if (step.reason != null) {
4739
+ lines.push(` reason: ${step.reason}`);
4740
+ }
4741
+ if (step.lastMutation != null) {
4742
+ const a = step.lastMutation.actor;
4743
+ lines.push(` actor: ${a.type}${a.id ? `:${a.id}` : ""}`);
4744
+ }
4745
+ }
4746
+ return lines.join("\n");
4747
+ }
4748
+ function formatValue(v) {
4749
+ if (v === void 0) return "<sentinel>";
4750
+ if (v === null) return "null";
4751
+ if (typeof v === "string") return JSON.stringify(v);
4752
+ if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
4753
+ try {
4754
+ const s = JSON.stringify(v);
4755
+ return s.length > 80 ? `${s.slice(0, 77)}...` : s;
4756
+ } catch {
4757
+ return String(v);
4758
+ }
4759
+ }
4760
+
4597
4761
  // src/extra/utils/sizeof.ts
4598
4762
  var OVERHEAD = {
4599
4763
  object: 56,
@@ -5227,6 +5391,20 @@ var Graph = class _Graph {
5227
5391
  _parent = void 0;
5228
5392
  _storageDisposers = /* @__PURE__ */ new Set();
5229
5393
  _disposers = /* @__PURE__ */ new Set();
5394
+ /**
5395
+ * @internal Lazy `TopologyEvent` producer. Created on first `.topology`
5396
+ * access. Zero cost until something subscribes — producer fn only runs when
5397
+ * the first sink attaches, registering one handler into
5398
+ * {@link Graph._topologyEmitters}.
5399
+ */
5400
+ _topology;
5401
+ /**
5402
+ * @internal Active emit handlers for the topology producer. Each entry is
5403
+ * the closure registered by the producer fn on activation; cleared on
5404
+ * deactivation. `_emitTopology` broadcasts through every entry (there is at
5405
+ * most one per activation cycle of the producer).
5406
+ */
5407
+ _topologyEmitters = /* @__PURE__ */ new Set();
5230
5408
  /**
5231
5409
  * @param name - Non-empty graph id (must not contain `::` and must not
5232
5410
  * equal the reserved meta segment `__meta__`).
@@ -5266,6 +5444,55 @@ var Graph = class _Graph {
5266
5444
  return out;
5267
5445
  }
5268
5446
  // ——————————————————————————————————————————————————————————————
5447
+ // Topology companion (structural-change event stream)
5448
+ // ——————————————————————————————————————————————————————————————
5449
+ /**
5450
+ * Reactive stream of structural changes to this graph's own registry
5451
+ * (add / mount / remove). Value mutations live on `observe()`; this
5452
+ * companion only fires when the topology shape changes.
5453
+ *
5454
+ * Lazy: the underlying node is created on first access and activates when
5455
+ * something subscribes. No emission replay — late subscribers do not
5456
+ * receive historical events and should snapshot via {@link Graph.describe}
5457
+ * before listening for incremental changes. Events that fire while the
5458
+ * producer has zero subscribers are dropped (no retention).
5459
+ *
5460
+ * Own-graph only: a parent's `topology` does NOT emit for structural
5461
+ * changes inside a mounted child. Transitive consumers subscribe to each
5462
+ * child's topology separately (recurse through `topology`'s own "added"
5463
+ * events with `nodeKind: "mount"` to discover new children).
5464
+ *
5465
+ * See {@link TopologyEvent} for payload shape.
5466
+ *
5467
+ * @category observability
5468
+ */
5469
+ get topology() {
5470
+ if (this._topology == null) {
5471
+ this._topology = producer(
5472
+ (actions) => {
5473
+ const handler = (event) => {
5474
+ actions.emit(event);
5475
+ };
5476
+ this._topologyEmitters.add(handler);
5477
+ return () => {
5478
+ this._topologyEmitters.delete(handler);
5479
+ };
5480
+ },
5481
+ { name: `${this.name}_topology` }
5482
+ );
5483
+ }
5484
+ return this._topology;
5485
+ }
5486
+ /**
5487
+ * @internal Fire a {@link TopologyEvent} to every active subscriber of
5488
+ * `this.topology`. No-op when the topology node has never been accessed or
5489
+ * currently has no sinks — zero cost for graphs nobody observes.
5490
+ */
5491
+ _emitTopology(event) {
5492
+ if (this._topology == null || this._topologyEmitters.size === 0) return;
5493
+ for (const h of this._topologyEmitters) h(event);
5494
+ }
5495
+ // ——————————————————————————————————————————————————————————————
5269
5496
  // Node registry
5270
5497
  // ——————————————————————————————————————————————————————————————
5271
5498
  /**
@@ -5295,6 +5522,7 @@ var Graph = class _Graph {
5295
5522
  }
5296
5523
  this._nodes.set(name, node2);
5297
5524
  this._nodeToName.set(node2, name);
5525
+ this._emitTopology({ kind: "added", name, nodeKind: "node" });
5298
5526
  return node2;
5299
5527
  }
5300
5528
  /**
@@ -5335,22 +5563,23 @@ var Graph = class _Graph {
5335
5563
  assertRegisterableName(name, this.name, "remove");
5336
5564
  const child = this._mounts.get(name);
5337
5565
  if (child) {
5338
- const audit2 = { kind: "mount", nodes: [], mounts: [] };
5566
+ const audit3 = { kind: "mount", nodes: [], mounts: [] };
5339
5567
  const targets = [];
5340
5568
  child._collectObserveTargets("", targets);
5341
5569
  for (const [p, n] of targets) {
5342
5570
  if (!p.includes(`${PATH_SEP}${GRAPH_META_SEGMENT}${PATH_SEP}`)) {
5343
- audit2.nodes.push(p);
5571
+ audit3.nodes.push(p);
5344
5572
  }
5345
5573
  void n;
5346
5574
  }
5347
- audit2.nodes.sort();
5348
- audit2.mounts.push(name);
5349
- audit2.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
5575
+ audit3.nodes.sort();
5576
+ audit3.mounts.push(name);
5577
+ audit3.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
5350
5578
  this._mounts.delete(name);
5351
5579
  child._parent = void 0;
5352
5580
  teardownMountedGraph(child);
5353
- return audit2;
5581
+ this._emitTopology({ kind: "removed", name, nodeKind: "mount", audit: audit3 });
5582
+ return audit3;
5354
5583
  }
5355
5584
  const node2 = this._nodes.get(name);
5356
5585
  if (!node2) {
@@ -5359,7 +5588,9 @@ var Graph = class _Graph {
5359
5588
  this._nodes.delete(name);
5360
5589
  this._nodeToName.delete(node2);
5361
5590
  node2.down([[TEARDOWN]], { internal: true });
5362
- return { kind: "node", nodes: [name], mounts: [] };
5591
+ const audit2 = { kind: "node", nodes: [name], mounts: [] };
5592
+ this._emitTopology({ kind: "removed", name, nodeKind: "node", audit: audit2 });
5593
+ return audit2;
5363
5594
  }
5364
5595
  /**
5365
5596
  * Bulk remove — invokes {@link Graph.remove} for every local name matching
@@ -5588,6 +5819,7 @@ var Graph = class _Graph {
5588
5819
  }
5589
5820
  this._mounts.set(name, child);
5590
5821
  child._parent = this;
5822
+ this._emitTopology({ kind: "added", name, nodeKind: "mount" });
5591
5823
  return child;
5592
5824
  }
5593
5825
  /**
@@ -5878,6 +6110,33 @@ var Graph = class _Graph {
5878
6110
  }
5879
6111
  return reachable(this.describe(), from, direction, opts);
5880
6112
  }
6113
+ /**
6114
+ * Causal walkback: shortest dep-chain from `from` to `to`, enriched with
6115
+ * each node's value, status, last-mutation actor, and reasoning annotation
6116
+ * from {@link Graph.trace}. Wraps {@link explainPath} (roadmap §9.2).
6117
+ *
6118
+ * @param from - Upstream node (the cause).
6119
+ * @param to - Downstream node (the effect).
6120
+ * @param opts - Optional `maxDepth` and `findCycle`. When `findCycle:true`
6121
+ * and `from === to`, returns the shortest cycle through other nodes
6122
+ * (useful for diagnosing feedback loops, COMPOSITION-GUIDE §7).
6123
+ * Annotations and lastMutations are collected automatically from the
6124
+ * live graph.
6125
+ */
6126
+ explain(from, to, opts) {
6127
+ const described = this.describe({ detail: "full" });
6128
+ const annotations = new Map(this._annotations);
6129
+ const lastMutations = /* @__PURE__ */ new Map();
6130
+ for (const [path, n] of Object.entries(described.nodes)) {
6131
+ if (n.lastMutation != null) lastMutations.set(path, n.lastMutation);
6132
+ }
6133
+ return explainPath(described, from, to, {
6134
+ ...opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {},
6135
+ ...opts?.findCycle === true ? { findCycle: true } : {},
6136
+ annotations,
6137
+ lastMutations
6138
+ });
6139
+ }
5881
6140
  /**
5882
6141
  * @internal Collect all qualified paths in this graph tree matching a
5883
6142
  * glob pattern. Used by scoped autoCheckpoint subscription.
@@ -6556,7 +6815,7 @@ var Graph = class _Graph {
6556
6815
  return;
6557
6816
  }
6558
6817
  const nextSeq = s.seq + 1;
6559
- const timestamp_ns = monotonicNs();
6818
+ const timestamp_ns = wallClockNs();
6560
6819
  const isFirst = s.lastSnapshot == null;
6561
6820
  const shouldCompact = isFirst || nextSeq % s.compactEvery === 0;
6562
6821
  const record = shouldCompact ? {
@@ -7330,18 +7589,67 @@ __export(graph_exports, {
7330
7589
  JsonCodec: () => JsonCodec,
7331
7590
  SIZEOF_OVERHEAD: () => OVERHEAD,
7332
7591
  SIZEOF_SYMBOL: () => SIZEOF_SYMBOL,
7592
+ SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
7333
7593
  createDagCborCodec: () => createDagCborCodec,
7334
7594
  createDagCborZstdCodec: () => createDagCborZstdCodec,
7335
7595
  decodeEnvelope: () => decodeEnvelope,
7336
7596
  diffForWAL: () => diffForWAL,
7337
7597
  encodeEnvelope: () => encodeEnvelope,
7598
+ explainPath: () => explainPath,
7338
7599
  graphProfile: () => graphProfile,
7339
7600
  reachable: () => reachable,
7340
7601
  registerBuiltinCodecs: () => registerBuiltinCodecs,
7341
7602
  replayWAL: () => replayWAL,
7342
- sizeof: () => sizeof
7603
+ sizeof: () => sizeof,
7604
+ watchTopologyTree: () => watchTopologyTree
7343
7605
  });
7344
7606
 
7607
+ // src/graph/topology-tree.ts
7608
+ function watchTopologyTree(graph, cb) {
7609
+ const subs = /* @__PURE__ */ new Map();
7610
+ const wire = (g, prefix) => {
7611
+ if (subs.has(g)) return;
7612
+ const placeholder = { off: () => {
7613
+ }, prefix };
7614
+ subs.set(g, placeholder);
7615
+ const off = g.topology.subscribe((msgs) => {
7616
+ for (const m of msgs) {
7617
+ if (m[0] !== DATA) continue;
7618
+ const event = m[1];
7619
+ cb(event, g, prefix);
7620
+ if (event.kind === "added" && event.nodeKind === "mount") {
7621
+ const child = g._mounts.get(event.name);
7622
+ if (child instanceof Graph) {
7623
+ const childPrefix = `${prefix}${event.name}::`;
7624
+ wire(child, childPrefix);
7625
+ }
7626
+ } else if (event.kind === "removed" && event.nodeKind === "mount") {
7627
+ const removedPrefix = `${prefix}${event.name}::`;
7628
+ for (const [trackedGraph, trackedEntry] of Array.from(subs.entries())) {
7629
+ if (trackedGraph === graph) continue;
7630
+ if (trackedEntry.prefix.startsWith(removedPrefix)) {
7631
+ trackedEntry.off();
7632
+ subs.delete(trackedGraph);
7633
+ }
7634
+ }
7635
+ }
7636
+ }
7637
+ });
7638
+ placeholder.off = off;
7639
+ for (const [mountName, child] of g._mounts) {
7640
+ if (child instanceof Graph) {
7641
+ const childPrefix = `${prefix}${mountName}::`;
7642
+ wire(child, childPrefix);
7643
+ }
7644
+ }
7645
+ };
7646
+ wire(graph, "");
7647
+ return () => {
7648
+ for (const entry of subs.values()) entry.off();
7649
+ subs.clear();
7650
+ };
7651
+ }
7652
+
7345
7653
  // src/patterns/_internal.ts
7346
7654
  function emitToMeta(metaNode, value) {
7347
7655
  if (metaNode == null) return;
@@ -14210,71 +14518,212 @@ function reactiveList(initial, options = {}) {
14210
14518
  };
14211
14519
  }
14212
14520
 
14213
- // src/extra/storage.ts
14214
- var import_node_crypto = require("crypto");
14215
- var import_node_fs2 = require("fs");
14216
- var import_node_path2 = require("path");
14217
- var import_node_sqlite = require("sqlite");
14218
- function sortJsonValue2(value) {
14219
- if (value === null || typeof value !== "object") return value;
14220
- if (Array.isArray(value)) return value.map(sortJsonValue2);
14221
- const obj = value;
14222
- const keys = Object.keys(obj).sort();
14223
- const out = {};
14224
- for (const k of keys) out[k] = sortJsonValue2(obj[k]);
14225
- return out;
14226
- }
14227
- function stableJsonString(data) {
14228
- return JSON.stringify(sortJsonValue2(data), void 0, 0);
14229
- }
14230
- function memoryStorage() {
14231
- const data = /* @__PURE__ */ new Map();
14232
- return {
14233
- save(key, record) {
14234
- data.set(key, JSON.parse(JSON.stringify(record)));
14235
- },
14236
- load(key) {
14237
- const v = data.get(key);
14238
- return v === void 0 ? null : JSON.parse(JSON.stringify(v));
14239
- },
14240
- clear(key) {
14241
- data.delete(key);
14242
- }
14243
- };
14244
- }
14245
- function dictStorage(storage) {
14246
- return {
14247
- save(key, record) {
14248
- storage[key] = JSON.parse(JSON.stringify(record));
14249
- },
14250
- load(key) {
14251
- const raw = storage[key];
14252
- return raw === void 0 ? null : JSON.parse(JSON.stringify(raw));
14253
- },
14254
- clear(key) {
14255
- delete storage[key];
14256
- }
14257
- };
14521
+ // src/extra/sources-fs.ts
14522
+ var import_node_fs = require("node:fs");
14523
+ var import_node_path = require("node:path");
14524
+ function sourceOpts4(opts) {
14525
+ return { describeKind: "producer", ...opts };
14258
14526
  }
14259
- function fileStorage(dir) {
14260
- const pathFor = (key) => {
14261
- const safe = key.replace(
14262
- /[^a-zA-Z0-9_-]/g,
14263
- (c) => `%${c.charCodeAt(0).toString(16).padStart(2, "0")}`
14264
- );
14265
- return (0, import_node_path2.join)(dir, `${safe}.json`);
14266
- };
14267
- return {
14268
- save(key, record) {
14269
- (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
14270
- const filePath = pathFor(key);
14271
- const payload = `${stableJsonString(record)}
14272
- `;
14273
- const base = (0, import_node_path2.basename)(filePath);
14274
- const d = (0, import_node_path2.dirname)(filePath);
14275
- const tmp = (0, import_node_path2.join)(d, `.${base}.${(0, import_node_crypto.randomBytes)(8).toString("hex")}.tmp`);
14276
- try {
14277
- (0, import_node_fs2.writeFileSync)(tmp, payload, "utf8");
14527
+ function fromFSWatch(paths, opts) {
14528
+ const list = Array.isArray(paths) ? paths : [paths];
14529
+ if (list.length === 0) {
14530
+ throw new RangeError("fromFSWatch expects at least one path");
14531
+ }
14532
+ const { recursive = true, debounce: debounce2 = 100, include, exclude, ...rest } = opts ?? {};
14533
+ const includePatterns = include?.map(globToRegExp) ?? [];
14534
+ const excludePatterns = (exclude ?? ["**/node_modules/**", "**/.git/**", "**/dist/**"]).map(
14535
+ globToRegExp
14536
+ );
14537
+ return producer((a) => {
14538
+ const pending = /* @__PURE__ */ new Map();
14539
+ const watchers = [];
14540
+ let stopped = false;
14541
+ let terminalEmitted = false;
14542
+ let generation = 0;
14543
+ const closeWatchers = () => {
14544
+ for (const watcher of watchers.splice(0)) watcher.close();
14545
+ };
14546
+ const emitError = (err) => {
14547
+ if (terminalEmitted) return;
14548
+ terminalEmitted = true;
14549
+ stopped = true;
14550
+ if (timer !== void 0) clearTimeout(timer);
14551
+ timer = void 0;
14552
+ pending.clear();
14553
+ closeWatchers();
14554
+ a.down([[ERROR, err]]);
14555
+ };
14556
+ let timer;
14557
+ const flush = (token) => {
14558
+ timer = void 0;
14559
+ if (stopped || terminalEmitted) return;
14560
+ if (pending.size === 0) return;
14561
+ const batchMessages = [];
14562
+ for (const evt of pending.values()) batchMessages.push([DATA, evt]);
14563
+ pending.clear();
14564
+ if (stopped || terminalEmitted || token !== generation) return;
14565
+ a.down(batchMessages);
14566
+ };
14567
+ try {
14568
+ for (const basePath of list) {
14569
+ const watcher = (0, import_node_fs.watch)(
14570
+ basePath,
14571
+ { recursive },
14572
+ (eventType, fileName) => {
14573
+ if (stopped || terminalEmitted) return;
14574
+ if (fileName == null) return;
14575
+ const rel = String(fileName).replaceAll("\\", "/");
14576
+ const abs = (0, import_node_path.resolve)(basePath, String(fileName));
14577
+ const normalized = abs.replaceAll("\\", "/");
14578
+ const root = (0, import_node_path.resolve)(basePath).replaceAll("\\", "/");
14579
+ const relForMatch = rel.startsWith("./") ? rel.slice(2) : rel;
14580
+ const included = includePatterns.length === 0 || matchesAnyPattern(normalized, includePatterns) || matchesAnyPattern(relForMatch, includePatterns);
14581
+ if (!included) return;
14582
+ const excluded = matchesAnyPattern(normalized, excludePatterns) || matchesAnyPattern(relForMatch, excludePatterns);
14583
+ if (excluded) return;
14584
+ let kind = "change";
14585
+ if (eventType === "rename") {
14586
+ try {
14587
+ kind = (0, import_node_fs.existsSync)(normalized) ? "create" : "delete";
14588
+ } catch {
14589
+ kind = "rename";
14590
+ }
14591
+ }
14592
+ pending.set(normalized, {
14593
+ type: kind,
14594
+ path: normalized,
14595
+ root,
14596
+ relative_path: relForMatch,
14597
+ timestamp_ns: wallClockNs()
14598
+ });
14599
+ if (timer !== void 0) clearTimeout(timer);
14600
+ const token = generation;
14601
+ timer = setTimeout(() => flush(token), debounce2);
14602
+ }
14603
+ );
14604
+ watcher.on("error", (err) => emitError(err));
14605
+ watchers.push(watcher);
14606
+ }
14607
+ } catch (err) {
14608
+ emitError(err);
14609
+ }
14610
+ return () => {
14611
+ stopped = true;
14612
+ generation += 1;
14613
+ if (timer !== void 0) clearTimeout(timer);
14614
+ timer = void 0;
14615
+ closeWatchers();
14616
+ pending.clear();
14617
+ };
14618
+ }, sourceOpts4(rest));
14619
+ }
14620
+
14621
+ // src/extra/storage.ts
14622
+ var import_node_crypto = require("node:crypto");
14623
+ var import_node_fs2 = require("node:fs");
14624
+ var import_node_path2 = require("node:path");
14625
+ var import_node_sqlite = require("node:sqlite");
14626
+ function sortJsonValue2(value) {
14627
+ if (value === null || typeof value !== "object") return value;
14628
+ if (Array.isArray(value)) return value.map(sortJsonValue2);
14629
+ const obj = value;
14630
+ const keys = Object.keys(obj).sort();
14631
+ const out = {};
14632
+ for (const k of keys) out[k] = sortJsonValue2(obj[k]);
14633
+ return out;
14634
+ }
14635
+ function stableJsonString(data) {
14636
+ return JSON.stringify(sortJsonValue2(data), void 0, 0);
14637
+ }
14638
+ function memoryStorage() {
14639
+ const data = /* @__PURE__ */ new Map();
14640
+ return {
14641
+ save(key, record) {
14642
+ data.set(key, JSON.parse(JSON.stringify(record)));
14643
+ },
14644
+ load(key) {
14645
+ const v = data.get(key);
14646
+ return v === void 0 ? null : JSON.parse(JSON.stringify(v));
14647
+ },
14648
+ clear(key) {
14649
+ data.delete(key);
14650
+ },
14651
+ list() {
14652
+ return [...data.keys()].sort();
14653
+ }
14654
+ };
14655
+ }
14656
+ function dictStorage(storage) {
14657
+ return {
14658
+ save(key, record) {
14659
+ storage[key] = JSON.parse(JSON.stringify(record));
14660
+ },
14661
+ load(key) {
14662
+ const raw = storage[key];
14663
+ return raw === void 0 ? null : JSON.parse(JSON.stringify(raw));
14664
+ },
14665
+ clear(key) {
14666
+ delete storage[key];
14667
+ },
14668
+ list() {
14669
+ return Object.keys(storage).sort();
14670
+ }
14671
+ };
14672
+ }
14673
+ function fileStorage(dir) {
14674
+ const encoder = new TextEncoder();
14675
+ const decoder = new TextDecoder("utf-8", { fatal: true });
14676
+ const pathFor = (key) => {
14677
+ let out = "";
14678
+ for (const ch of key) {
14679
+ if (ch.length === 1 && /[a-zA-Z0-9_-]/.test(ch)) {
14680
+ out += ch;
14681
+ continue;
14682
+ }
14683
+ for (const byte of encoder.encode(ch)) {
14684
+ out += `%${byte.toString(16).padStart(2, "0")}`;
14685
+ }
14686
+ }
14687
+ return (0, import_node_path2.join)(dir, `${out}.json`);
14688
+ };
14689
+ const keyFromFilename = (filename) => {
14690
+ if (!filename.endsWith(".json")) return null;
14691
+ const stem = filename.slice(0, -".json".length);
14692
+ const bytes = [];
14693
+ const encodeAscii = (s) => {
14694
+ for (let i2 = 0; i2 < s.length; i2++) bytes.push(s.charCodeAt(i2));
14695
+ };
14696
+ let i = 0;
14697
+ while (i < stem.length) {
14698
+ const ch = stem[i];
14699
+ if (ch === "%" && i + 2 < stem.length) {
14700
+ const hex = stem.slice(i + 1, i + 3);
14701
+ if (/^[0-9a-f]{2}$/i.test(hex)) {
14702
+ bytes.push(Number.parseInt(hex, 16));
14703
+ i += 3;
14704
+ continue;
14705
+ }
14706
+ }
14707
+ encodeAscii(ch);
14708
+ i += 1;
14709
+ }
14710
+ try {
14711
+ return decoder.decode(new Uint8Array(bytes));
14712
+ } catch {
14713
+ return null;
14714
+ }
14715
+ };
14716
+ return {
14717
+ save(key, record) {
14718
+ (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
14719
+ const filePath = pathFor(key);
14720
+ const payload = `${stableJsonString(record)}
14721
+ `;
14722
+ const base = (0, import_node_path2.basename)(filePath);
14723
+ const d = (0, import_node_path2.dirname)(filePath);
14724
+ const tmp = (0, import_node_path2.join)(d, `.${base}.${(0, import_node_crypto.randomBytes)(8).toString("hex")}.tmp`);
14725
+ try {
14726
+ (0, import_node_fs2.writeFileSync)(tmp, payload, "utf8");
14278
14727
  (0, import_node_fs2.renameSync)(tmp, filePath);
14279
14728
  } catch (e) {
14280
14729
  try {
@@ -14299,6 +14748,21 @@ function fileStorage(dir) {
14299
14748
  } catch (e) {
14300
14749
  if (e.code !== "ENOENT") throw e;
14301
14750
  }
14751
+ },
14752
+ list() {
14753
+ try {
14754
+ const entries = (0, import_node_fs2.readdirSync)(dir);
14755
+ const keys = [];
14756
+ for (const entry of entries) {
14757
+ if (entry.startsWith(".")) continue;
14758
+ const k = keyFromFilename(entry);
14759
+ if (k !== null) keys.push(k);
14760
+ }
14761
+ return keys.sort();
14762
+ } catch (e) {
14763
+ if (e.code === "ENOENT") return [];
14764
+ throw e;
14765
+ }
14302
14766
  }
14303
14767
  };
14304
14768
  }
@@ -14321,6 +14785,10 @@ function sqliteStorage(path) {
14321
14785
  clear(key) {
14322
14786
  db.prepare(`DELETE FROM graphrefly_checkpoint WHERE k = ?`).run(key);
14323
14787
  },
14788
+ list() {
14789
+ const rows = db.prepare(`SELECT k FROM graphrefly_checkpoint ORDER BY k`).all();
14790
+ return rows.map((r) => r.k);
14791
+ },
14324
14792
  close() {
14325
14793
  try {
14326
14794
  db.close();
@@ -14970,17 +15438,32 @@ function workerSelf(target, opts) {
14970
15438
  // src/patterns/index.ts
14971
15439
  var patterns_exports = {};
14972
15440
  __export(patterns_exports, {
15441
+ SNAPSHOT_WIRE_VERSION: () => SNAPSHOT_WIRE_VERSION,
15442
+ SurfaceError: () => SurfaceError,
15443
+ accountability: () => audit_exports,
14973
15444
  ai: () => ai_exports,
15445
+ asSurfaceError: () => asSurfaceError,
14974
15446
  cqrs: () => cqrs_exports,
15447
+ createGraph: () => createGraph,
15448
+ deleteSnapshot: () => deleteSnapshot,
14975
15449
  demoShell: () => demo_shell_exports,
15450
+ diffSnapshots: () => diffSnapshots,
14976
15451
  domainTemplates: () => domain_templates_exports,
14977
15452
  graphspec: () => graphspec_exports,
15453
+ guarded: () => guarded_execution_exports,
14978
15454
  harness: () => harness_exports,
14979
15455
  layout: () => reactive_layout_exports,
15456
+ lens: () => lens_exports,
15457
+ listSnapshots: () => listSnapshots,
14980
15458
  memory: () => memory_exports,
14981
15459
  messaging: () => messaging_exports,
14982
15460
  orchestration: () => orchestration_exports,
14983
- reduction: () => reduction_exports
15461
+ reduction: () => reduction_exports,
15462
+ resilientPipeline: () => resilient_pipeline_exports,
15463
+ restoreSnapshot: () => restoreSnapshot,
15464
+ runReduction: () => runReduction,
15465
+ saveSnapshot: () => saveSnapshot,
15466
+ surface: () => surface_exports
14984
15467
  });
14985
15468
 
14986
15469
  // src/patterns/ai.ts
@@ -18018,6 +18501,420 @@ async function suggestStrategy(graph, problem, adapter, opts) {
18018
18501
  };
18019
18502
  }
18020
18503
 
18504
+ // src/patterns/audit.ts
18505
+ var audit_exports = {};
18506
+ __export(audit_exports, {
18507
+ AuditTrailGraph: () => AuditTrailGraph,
18508
+ PolicyEnforcerGraph: () => PolicyEnforcerGraph,
18509
+ auditTrail: () => auditTrail,
18510
+ complianceSnapshot: () => complianceSnapshot,
18511
+ policyEnforcer: () => policyEnforcer,
18512
+ reactiveExplainPath: () => reactiveExplainPath
18513
+ });
18514
+ function auditMeta(kind, extra) {
18515
+ return domainMeta("audit", kind, extra);
18516
+ }
18517
+ var DEFAULT_INCLUDE_TYPES = /* @__PURE__ */ new Set([
18518
+ "data",
18519
+ "error",
18520
+ "complete",
18521
+ "teardown"
18522
+ ]);
18523
+ var AuditTrailGraph = class extends Graph {
18524
+ entries;
18525
+ count;
18526
+ _log;
18527
+ _target;
18528
+ constructor(target, opts) {
18529
+ super(opts.name ?? `${target.name}_audit`, opts.graph);
18530
+ this._target = target;
18531
+ this._log = reactiveLog([], {
18532
+ name: "entries",
18533
+ ...opts.maxSize != null ? { maxSize: opts.maxSize } : {}
18534
+ });
18535
+ this.entries = this._log.entries;
18536
+ this.add("entries", this.entries);
18537
+ this.count = derived(
18538
+ [this.entries],
18539
+ ([snapshot]) => snapshot.length,
18540
+ { name: "count", describeKind: "derived", meta: auditMeta("count") }
18541
+ );
18542
+ this.add("count", this.count);
18543
+ this.addDisposer(keepalive(this.count));
18544
+ const includeTypes = opts.includeTypes != null ? new Set(opts.includeTypes) : DEFAULT_INCLUDE_TYPES;
18545
+ const filter2 = opts.filter;
18546
+ let seq = 0;
18547
+ const handle = target.observe({ timeline: true, structured: true });
18548
+ const offEvent = handle.onEvent((event) => {
18549
+ if (event.type === "derived") return;
18550
+ const type = event.type;
18551
+ if (!includeTypes.has(type)) return;
18552
+ const path = event.path ?? "";
18553
+ const entry = {
18554
+ seq: seq++,
18555
+ timestamp_ns: event.timestamp_ns ?? monotonicNs(),
18556
+ wall_clock_ns: wallClockNs(),
18557
+ path,
18558
+ type
18559
+ };
18560
+ const node2 = path ? safeNode(target, path) : void 0;
18561
+ const lastMutation = node2?.lastMutation;
18562
+ if (lastMutation != null) entry.actor = lastMutation.actor;
18563
+ if (type === "data") entry.value = event.data;
18564
+ if (type === "error") entry.error = event.data;
18565
+ const reason = path ? safeAnnotation(target, path) : void 0;
18566
+ if (reason != null) entry.reason = reason;
18567
+ if (filter2 != null && !filter2(entry)) return;
18568
+ this._log.append(entry);
18569
+ });
18570
+ this.addDisposer(() => {
18571
+ offEvent();
18572
+ handle.dispose();
18573
+ });
18574
+ this.addDisposer(() => this._log.disposeAllViews());
18575
+ }
18576
+ /** All entries currently in the ring (snapshot). */
18577
+ all() {
18578
+ return this.entries.cache ?? [];
18579
+ }
18580
+ /** Entries matching `path`. Order preserved. */
18581
+ byNode(path) {
18582
+ return this.all().filter((e) => e.path === path);
18583
+ }
18584
+ /** Entries whose `actor.id` matches. Use `byActorType` for type filtering. */
18585
+ byActor(actorId) {
18586
+ return this.all().filter((e) => e.actor?.id === actorId);
18587
+ }
18588
+ /** Entries whose `actor.type` matches (e.g. `"llm"`, `"human"`). */
18589
+ byActorType(type) {
18590
+ return this.all().filter((e) => e.actor?.type === type);
18591
+ }
18592
+ /**
18593
+ * Entries with `timestamp_ns` in `[start_ns, end_ns)` (end exclusive).
18594
+ * Omit `end_ns` to query open-ended.
18595
+ */
18596
+ byTimeRange(start_ns, end_ns) {
18597
+ return this.all().filter((e) => {
18598
+ if (e.timestamp_ns < start_ns) return false;
18599
+ if (end_ns != null && e.timestamp_ns >= end_ns) return false;
18600
+ return true;
18601
+ });
18602
+ }
18603
+ /** Reference to the audited graph (escape hatch for tooling). */
18604
+ get target() {
18605
+ return this._target;
18606
+ }
18607
+ };
18608
+ function auditTrail(target, opts = {}) {
18609
+ return new AuditTrailGraph(target, opts);
18610
+ }
18611
+ var PolicyEnforcerGraph = class extends Graph {
18612
+ policies;
18613
+ violations;
18614
+ violationCount;
18615
+ _target;
18616
+ _mode;
18617
+ _currentGuard;
18618
+ constructor(target, policies, opts) {
18619
+ super(opts.name ?? `${target.name}_policy`, opts.graph);
18620
+ this._target = target;
18621
+ this._mode = opts.mode ?? "audit";
18622
+ const policiesNode = isNode3(policies) ? policies : state(policies, { name: "policies" });
18623
+ this.policies = policiesNode;
18624
+ this.add("policies", this.policies);
18625
+ this.violations = new TopicGraph("violations", {
18626
+ retainedLimit: opts.violationsLimit ?? 1e3
18627
+ });
18628
+ this.mount("violations", this.violations);
18629
+ this.violationCount = derived(
18630
+ [this.violations.events],
18631
+ ([snapshot]) => snapshot.length,
18632
+ {
18633
+ name: "violationCount",
18634
+ describeKind: "derived",
18635
+ meta: auditMeta("policy_violation_count")
18636
+ }
18637
+ );
18638
+ this.add("violationCount", this.violationCount);
18639
+ this.addDisposer(keepalive(this.violationCount));
18640
+ const initialRules = policiesNode.cache ?? [];
18641
+ let latestRules = initialRules;
18642
+ this._currentGuard = policyFromRules(latestRules);
18643
+ const offPolicies = policiesNode.subscribe((msgs) => {
18644
+ for (const m of msgs) {
18645
+ if (m[0] === DATA) {
18646
+ latestRules = m[1] ?? [];
18647
+ this._currentGuard = policyFromRules(latestRules);
18648
+ }
18649
+ }
18650
+ });
18651
+ this.addDisposer(offPolicies);
18652
+ const paths = opts.paths != null ? [...opts.paths] : collectPaths(target);
18653
+ if (this._mode === "enforce") {
18654
+ const restorers = /* @__PURE__ */ new Map();
18655
+ const wrapAndPush = (path) => {
18656
+ if (restorers.has(path)) return;
18657
+ const node2 = safeNode(target, path);
18658
+ if (!(node2 instanceof NodeImpl)) return;
18659
+ const pathGuard = (actor, action2) => {
18660
+ const ok = this._currentGuard(actor, action2);
18661
+ if (!ok) {
18662
+ this._publishViolation(actor, action2, path, "blocked");
18663
+ }
18664
+ return ok;
18665
+ };
18666
+ restorers.set(path, node2._pushGuard(pathGuard));
18667
+ };
18668
+ for (const path of paths) wrapAndPush(path);
18669
+ if (opts.paths == null) {
18670
+ const offTopology = watchTopologyTree(target, (event, emitter, prefix) => {
18671
+ if (event.kind === "added") {
18672
+ if (event.nodeKind === "node") {
18673
+ wrapAndPush(`${prefix}${event.name}`);
18674
+ } else {
18675
+ const child = emitter._mounts.get(event.name);
18676
+ if (!(child instanceof Graph)) return;
18677
+ const mountPrefix = `${prefix}${event.name}::`;
18678
+ const localPaths = collectPaths(child);
18679
+ for (const localPath of localPaths) {
18680
+ wrapAndPush(
18681
+ localPath === "" ? `${prefix}${event.name}` : `${mountPrefix}${localPath}`
18682
+ );
18683
+ }
18684
+ }
18685
+ } else if (event.kind === "removed") {
18686
+ if (event.nodeKind === "node") {
18687
+ const qp = `${prefix}${event.name}`;
18688
+ const r = restorers.get(qp);
18689
+ if (r != null) {
18690
+ r();
18691
+ restorers.delete(qp);
18692
+ }
18693
+ } else {
18694
+ const mountQp = `${prefix}${event.name}`;
18695
+ const mountPrefix = `${mountQp}::`;
18696
+ for (const [p, r] of restorers) {
18697
+ if (p === mountQp || p.startsWith(mountPrefix)) {
18698
+ r();
18699
+ restorers.delete(p);
18700
+ }
18701
+ }
18702
+ }
18703
+ }
18704
+ });
18705
+ this.addDisposer(offTopology);
18706
+ } else {
18707
+ const offCleanup = target.topology.subscribe((msgs) => {
18708
+ for (const m of msgs) {
18709
+ if (m[0] !== DATA) continue;
18710
+ const event = m[1];
18711
+ if (event.kind !== "removed" || event.nodeKind !== "node") continue;
18712
+ const r = restorers.get(event.name);
18713
+ if (r != null) {
18714
+ r();
18715
+ restorers.delete(event.name);
18716
+ }
18717
+ }
18718
+ });
18719
+ this.addDisposer(offCleanup);
18720
+ }
18721
+ this.addDisposer(() => {
18722
+ for (const r of restorers.values()) r();
18723
+ restorers.clear();
18724
+ });
18725
+ } else {
18726
+ const handle = target.observe({ timeline: true, structured: true });
18727
+ const off = handle.onEvent((event) => {
18728
+ if (event.type !== "data" && event.type !== "error") return;
18729
+ const path = event.path ?? "";
18730
+ if (!path) return;
18731
+ if (opts.paths != null && !opts.paths.includes(path)) return;
18732
+ const node2 = safeNode(target, path);
18733
+ const lastMutation = node2?.lastMutation;
18734
+ if (lastMutation == null) return;
18735
+ const action2 = "write";
18736
+ if (this._currentGuard(lastMutation.actor, action2)) return;
18737
+ this._publishViolation(lastMutation.actor, action2, path, "observed");
18738
+ });
18739
+ this.addDisposer(() => {
18740
+ off();
18741
+ handle.dispose();
18742
+ });
18743
+ }
18744
+ }
18745
+ _publishViolation(actor, action2, path, result) {
18746
+ this.violations.publish({
18747
+ timestamp_ns: monotonicNs(),
18748
+ wall_clock_ns: wallClockNs(),
18749
+ path,
18750
+ actor,
18751
+ action: action2,
18752
+ mode: this._mode,
18753
+ result
18754
+ });
18755
+ }
18756
+ /** Snapshot of recorded violations. */
18757
+ all() {
18758
+ return this.violations.retained();
18759
+ }
18760
+ get mode() {
18761
+ return this._mode;
18762
+ }
18763
+ get target() {
18764
+ return this._target;
18765
+ }
18766
+ };
18767
+ function policyEnforcer(target, policies, opts = {}) {
18768
+ return new PolicyEnforcerGraph(target, policies, opts);
18769
+ }
18770
+ function reactiveExplainPath(target, from, to, opts) {
18771
+ let v = 0;
18772
+ const version2 = state(v, { name: "explain_version" });
18773
+ const handle = target.observe({ timeline: true, structured: true });
18774
+ const off = handle.onEvent((event) => {
18775
+ const t = event.type;
18776
+ if (t !== "data" && t !== "error" && t !== "complete" && t !== "teardown") return;
18777
+ v += 1;
18778
+ version2.emit(v);
18779
+ });
18780
+ const explainOpts = {
18781
+ ...opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {},
18782
+ ...opts?.findCycle === true ? { findCycle: true } : {}
18783
+ };
18784
+ const node2 = derived([version2], () => target.explain(from, to, explainOpts), {
18785
+ name: opts?.name ?? "explain",
18786
+ describeKind: "derived",
18787
+ equals: (a, b) => a.found === b.found && a.reason === b.reason && a.steps.length === b.steps.length && causalStepsEqual(a.steps, b.steps),
18788
+ meta: auditMeta("explain_path", { from, to })
18789
+ });
18790
+ const stopKeepalive = keepalive(node2);
18791
+ return {
18792
+ node: node2,
18793
+ dispose() {
18794
+ off();
18795
+ handle.dispose();
18796
+ stopKeepalive();
18797
+ }
18798
+ };
18799
+ }
18800
+ function causalStepsEqual(a, b) {
18801
+ for (let i = 0; i < a.length; i++) {
18802
+ const x = a[i];
18803
+ const y = b[i];
18804
+ if (x.path !== y.path) return false;
18805
+ if (x.type !== y.type) return false;
18806
+ if (x.status !== y.status) return false;
18807
+ if (x.hop !== y.hop) return false;
18808
+ if (x.dep_index !== y.dep_index) return false;
18809
+ if (x.reason !== y.reason) return false;
18810
+ if (x.value !== y.value) return false;
18811
+ if (x.lastMutation !== y.lastMutation) return false;
18812
+ const xv = x.v;
18813
+ const yv = y.v;
18814
+ if (xv !== yv) {
18815
+ if (xv == null || yv == null) return false;
18816
+ if (xv.id !== yv.id || xv.version !== yv.version) return false;
18817
+ }
18818
+ }
18819
+ return true;
18820
+ }
18821
+ function complianceSnapshot(target, opts = {}) {
18822
+ const result = {
18823
+ format_version: 1,
18824
+ timestamp_ns: monotonicNs(),
18825
+ wall_clock_ns: wallClockNs(),
18826
+ graph: target.snapshot()
18827
+ };
18828
+ if (opts.actor != null) result.actor = opts.actor;
18829
+ if (opts.audit != null) {
18830
+ const entries = [...opts.audit.all()];
18831
+ result.audit = { count: entries.length, entries };
18832
+ }
18833
+ if (opts.policies != null) {
18834
+ const rules = opts.policies.policies.cache ?? [];
18835
+ result.policies = {
18836
+ mode: opts.policies.mode,
18837
+ rules,
18838
+ violations: [...opts.policies.all()]
18839
+ };
18840
+ }
18841
+ const fingerprint = computeFingerprint(result);
18842
+ return { ...result, fingerprint };
18843
+ }
18844
+ function isNode3(x) {
18845
+ return typeof x === "object" && x !== null && "subscribe" in x;
18846
+ }
18847
+ function safeNode(target, path) {
18848
+ try {
18849
+ return target.node(path);
18850
+ } catch {
18851
+ return void 0;
18852
+ }
18853
+ }
18854
+ function safeAnnotation(target, path) {
18855
+ try {
18856
+ return target.annotation(path);
18857
+ } catch {
18858
+ return void 0;
18859
+ }
18860
+ }
18861
+ function collectPaths(target) {
18862
+ const described = target.describe({ detail: "minimal" });
18863
+ return Object.keys(described.nodes);
18864
+ }
18865
+ function computeFingerprint(value) {
18866
+ return defaultHash(JSON.stringify(canonicalize(value)));
18867
+ }
18868
+ function canonicalize(value) {
18869
+ const stack = /* @__PURE__ */ new Set();
18870
+ const walk = (v) => {
18871
+ if (v === void 0) return { __undefined: true };
18872
+ if (v === null) return null;
18873
+ const t = typeof v;
18874
+ if (t === "bigint") return { __bigint: v.toString() };
18875
+ if (t !== "object") return v;
18876
+ const obj = v;
18877
+ if (stack.has(obj)) return { __circular: true };
18878
+ stack.add(obj);
18879
+ try {
18880
+ if (Array.isArray(obj)) {
18881
+ return obj.map(walk);
18882
+ }
18883
+ if (obj instanceof Date) {
18884
+ return { __date: obj.toISOString() };
18885
+ }
18886
+ if (obj instanceof RegExp) {
18887
+ return { __regexp: { source: obj.source, flags: obj.flags } };
18888
+ }
18889
+ if (obj instanceof Map) {
18890
+ const entries = [...obj.entries()].map(([k, mv]) => [
18891
+ walk(k),
18892
+ walk(mv)
18893
+ ]);
18894
+ return { __map: entries };
18895
+ }
18896
+ if (obj instanceof Set) {
18897
+ const items = [...obj].map(walk);
18898
+ return { __set: items };
18899
+ }
18900
+ if (ArrayBuffer.isView(obj)) {
18901
+ const ta = obj;
18902
+ const arr = new Array(ta.length);
18903
+ for (let i = 0; i < ta.length; i++) arr[i] = ta[i] ?? 0;
18904
+ return { __typed_array: { ctor: obj.constructor.name, data: arr } };
18905
+ }
18906
+ const out = {};
18907
+ for (const k of Object.keys(obj).sort()) {
18908
+ out[k] = walk(obj[k]);
18909
+ }
18910
+ return out;
18911
+ } finally {
18912
+ stack.delete(obj);
18913
+ }
18914
+ };
18915
+ return walk(value);
18916
+ }
18917
+
18021
18918
  // src/patterns/demo-shell.ts
18022
18919
  var demo_shell_exports = {};
18023
18920
  __export(demo_shell_exports, {
@@ -20998,6 +21895,65 @@ ${validation.errors.join("\n")}`);
20998
21895
  return parsed;
20999
21896
  }
21000
21897
 
21898
+ // src/patterns/guarded-execution.ts
21899
+ var guarded_execution_exports = {};
21900
+ __export(guarded_execution_exports, {
21901
+ GuardedExecutionGraph: () => GuardedExecutionGraph,
21902
+ guardedExecution: () => guardedExecution
21903
+ });
21904
+ var GuardedExecutionGraph = class extends Graph {
21905
+ enforcer;
21906
+ violations;
21907
+ _target;
21908
+ _defaultActor;
21909
+ constructor(target, opts) {
21910
+ super(opts.name ?? `${target.name}_guarded`, opts.graph);
21911
+ this._target = target;
21912
+ this._defaultActor = opts.actor;
21913
+ const enforcerOpts = {
21914
+ mode: opts.mode ?? "enforce",
21915
+ name: "enforcer"
21916
+ };
21917
+ if (opts.violationsLimit != null) enforcerOpts.violationsLimit = opts.violationsLimit;
21918
+ this.enforcer = policyEnforcer(target, opts.policies, enforcerOpts);
21919
+ this.violations = this.enforcer.violations;
21920
+ this.mount("enforcer", this.enforcer);
21921
+ }
21922
+ /**
21923
+ * Describe the **target** graph scoped to the configured actor. Returns
21924
+ * only nodes the actor is permitted to see (via the target's node guards
21925
+ * filtering `describe()` via `actor`).
21926
+ *
21927
+ * Pass `{actor}` in opts to override the configured actor for this call.
21928
+ * Pass any standard {@link GraphDescribeOptions} fields (`detail`,
21929
+ * `fields`, `filter`) — they apply to the target's describe.
21930
+ *
21931
+ * **Mode interaction:**
21932
+ * - In `mode: "enforce"` (default), the enforcer stacks a policy-derived
21933
+ * guard on every target node. `scopedDescribe({actor})` then filters by
21934
+ * the AND of per-node guards AND the stacked policy guard.
21935
+ * - In `mode: "audit"`, NO guards are stacked — `scopedDescribe` filters
21936
+ * purely by the target's pre-existing per-node guards. If a target has
21937
+ * no node-level guards, the policy rules you pass have no effect on
21938
+ * visibility (they only populate the `violations` topic on writes).
21939
+ */
21940
+ scopedDescribe(opts) {
21941
+ const actor = opts?.actor ?? this._defaultActor;
21942
+ const describeOpts = {
21943
+ ...opts,
21944
+ ...actor != null ? { actor } : {}
21945
+ };
21946
+ return this._target.describe(describeOpts);
21947
+ }
21948
+ /** The wrapped graph (escape hatch for tooling). */
21949
+ get target() {
21950
+ return this._target;
21951
+ }
21952
+ };
21953
+ function guardedExecution(target, opts) {
21954
+ return new GuardedExecutionGraph(target, opts);
21955
+ }
21956
+
21001
21957
  // src/patterns/harness/index.ts
21002
21958
  var harness_exports = {};
21003
21959
  __export(harness_exports, {
@@ -21679,6 +22635,257 @@ function truncate(s, max) {
21679
22635
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
21680
22636
  }
21681
22637
 
22638
+ // src/patterns/lens.ts
22639
+ var lens_exports = {};
22640
+ __export(lens_exports, {
22641
+ LensGraph: () => LensGraph,
22642
+ graphLens: () => graphLens,
22643
+ watchTopologyTree: () => watchTopologyTree
22644
+ });
22645
+ function lensMeta(kind) {
22646
+ return domainMeta("lens", kind);
22647
+ }
22648
+ function computeTopologyStats(described) {
22649
+ const paths = Object.keys(described.nodes);
22650
+ const depsByPath = /* @__PURE__ */ new Map();
22651
+ const dependents = /* @__PURE__ */ new Map();
22652
+ for (const path of paths) {
22653
+ const deps = described.nodes[path]?.deps ?? [];
22654
+ depsByPath.set(path, deps);
22655
+ for (const dep of deps) {
22656
+ if (!dependents.has(dep)) dependents.set(dep, /* @__PURE__ */ new Set());
22657
+ dependents.get(dep).add(path);
22658
+ }
22659
+ }
22660
+ const sources = [];
22661
+ const sinks = [];
22662
+ for (const path of paths) {
22663
+ if ((depsByPath.get(path) ?? []).length === 0) sources.push(path);
22664
+ if (!dependents.has(path)) sinks.push(path);
22665
+ }
22666
+ sources.sort();
22667
+ sinks.sort();
22668
+ const edgeCount = described.edges.length;
22669
+ const WHITE = 0;
22670
+ const GRAY = 1;
22671
+ const BLACK = 2;
22672
+ const color = /* @__PURE__ */ new Map();
22673
+ for (const p of paths) color.set(p, WHITE);
22674
+ let hasCycles = false;
22675
+ const longestFrom = /* @__PURE__ */ new Map();
22676
+ const dfs = (path) => {
22677
+ const c = color.get(path) ?? WHITE;
22678
+ if (c === GRAY) {
22679
+ hasCycles = true;
22680
+ return 0;
22681
+ }
22682
+ if (c === BLACK) return longestFrom.get(path) ?? 0;
22683
+ color.set(path, GRAY);
22684
+ let best = 0;
22685
+ const kids = dependents.get(path);
22686
+ if (kids != null) {
22687
+ for (const k of kids) {
22688
+ const childLongest = dfs(k);
22689
+ if (childLongest + 1 > best) best = childLongest + 1;
22690
+ }
22691
+ }
22692
+ color.set(path, BLACK);
22693
+ longestFrom.set(path, best);
22694
+ return best;
22695
+ };
22696
+ let depth = 0;
22697
+ for (const src of sources) {
22698
+ const d = dfs(src);
22699
+ if (d > depth) depth = d;
22700
+ }
22701
+ for (const p of paths) {
22702
+ if (color.get(p) === WHITE) dfs(p);
22703
+ }
22704
+ return {
22705
+ nodeCount: paths.length,
22706
+ edgeCount,
22707
+ subgraphCount: described.subgraphs.length,
22708
+ sources,
22709
+ sinks,
22710
+ depth,
22711
+ hasCycles
22712
+ };
22713
+ }
22714
+ function computeHealthReport(described) {
22715
+ const problems = [];
22716
+ for (const [path, desc] of Object.entries(described.nodes)) {
22717
+ if (desc.status !== "errored") continue;
22718
+ const entry = { path, status: "errored" };
22719
+ const upstream = reachable(described, path, "upstream", {});
22720
+ for (const p of upstream) {
22721
+ if (p === path) continue;
22722
+ if (described.nodes[p]?.status === "errored") {
22723
+ entry.upstreamCause = p;
22724
+ break;
22725
+ }
22726
+ }
22727
+ problems.push(entry);
22728
+ }
22729
+ problems.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
22730
+ return { ok: problems.length === 0, problems };
22731
+ }
22732
+ function topologyStatsEqual(a, b) {
22733
+ return a.nodeCount === b.nodeCount && a.edgeCount === b.edgeCount && a.subgraphCount === b.subgraphCount && a.depth === b.depth && a.hasCycles === b.hasCycles && stringArrayEqual(a.sources, b.sources) && stringArrayEqual(a.sinks, b.sinks);
22734
+ }
22735
+ function healthReportEqual(a, b) {
22736
+ if (a.ok !== b.ok) return false;
22737
+ if (a.problems.length !== b.problems.length) return false;
22738
+ for (let i = 0; i < a.problems.length; i++) {
22739
+ const x = a.problems[i];
22740
+ const y = b.problems[i];
22741
+ if (x.path !== y.path) return false;
22742
+ if (x.status !== y.status) return false;
22743
+ if (x.upstreamCause !== y.upstreamCause) return false;
22744
+ }
22745
+ return true;
22746
+ }
22747
+ function stringArrayEqual(a, b) {
22748
+ if (a.length !== b.length) return false;
22749
+ for (let i = 0; i < a.length; i++) {
22750
+ if (a[i] !== b[i]) return false;
22751
+ }
22752
+ return true;
22753
+ }
22754
+ var LensGraph = class extends Graph {
22755
+ /**
22756
+ * Aggregate structural stats — `nodeCount`, `edgeCount`, `sources`,
22757
+ * `sinks`, `depth`, `hasCycles`, `subgraphCount`. Recomputes on every
22758
+ * structural change via {@link watchTopologyTree} (transitive).
22759
+ *
22760
+ * Named `stats` (not `topology`) because `Graph.topology` already names
22761
+ * the raw `TopologyEvent` stream on every graph including `LensGraph`;
22762
+ * giving the lens its own `topology` accessor with an incompatible
22763
+ * `Node<TopologyStats>` type would break Liskov substitutability.
22764
+ */
22765
+ stats;
22766
+ health;
22767
+ /**
22768
+ * Per-path flow tracker — a live {@link ReactiveMapBundle} keyed by
22769
+ * qualified path. Use `.get(path)` / `.has(path)` / `.size` for O(1)
22770
+ * sync queries; subscribe to `.entries` for a reactive snapshot of the
22771
+ * whole map. Lazy — the snapshot is materialized only while `.entries`
22772
+ * has subscribers.
22773
+ *
22774
+ * Shape intentionally differs from `stats` / `health` (which are plain
22775
+ * `Node<Report>`) because `flow` is a keyed collection, not a single
22776
+ * aggregate value. The map shape exposes cheaper queries than any
22777
+ * snapshot-based design.
22778
+ */
22779
+ flow;
22780
+ _target;
22781
+ constructor(target, opts = {}) {
22782
+ super(opts.name ?? `${target.name}_lens`, opts.graph);
22783
+ this._target = target;
22784
+ let statsVersion = 0;
22785
+ let healthVersion = 0;
22786
+ const statsTick = state(0, { name: "stats_tick" });
22787
+ const healthTick = state(0, { name: "health_tick" });
22788
+ this.add("stats_tick", statsTick);
22789
+ this.add("health_tick", healthTick);
22790
+ const mapOpts = { name: "flow" };
22791
+ if (opts.maxFlowPaths != null) mapOpts.maxSize = opts.maxFlowPaths;
22792
+ this.flow = reactiveMap(mapOpts);
22793
+ this.add("flow", this.flow.entries);
22794
+ const pathFilter = opts.pathFilter;
22795
+ const offTopology = watchTopologyTree(target, (event, _emitter, prefix) => {
22796
+ statsVersion += 1;
22797
+ statsTick.emit(statsVersion);
22798
+ healthVersion += 1;
22799
+ healthTick.emit(healthVersion);
22800
+ if (event.kind === "removed") {
22801
+ if (event.nodeKind === "node") {
22802
+ const qp = `${prefix}${event.name}`;
22803
+ this.flow.delete(qp);
22804
+ } else {
22805
+ const mountPrefix = `${prefix}${event.name}::`;
22806
+ const keysToDelete = [];
22807
+ for (const relativePath of event.audit.nodes) {
22808
+ const qualified = relativePath === "" ? `${prefix}${event.name}` : `${mountPrefix}${relativePath}`;
22809
+ keysToDelete.push(qualified);
22810
+ }
22811
+ if (keysToDelete.length > 0) this.flow.deleteMany(keysToDelete);
22812
+ }
22813
+ }
22814
+ });
22815
+ this.addDisposer(offTopology);
22816
+ const observeHandle = target.observe({ timeline: true, structured: true });
22817
+ const offObserve = observeHandle.onEvent((event) => {
22818
+ const t = event.type;
22819
+ if (t === "error" || t === "complete" || t === "data" || t === "teardown") {
22820
+ healthVersion += 1;
22821
+ healthTick.emit(healthVersion);
22822
+ }
22823
+ if (t === "data") {
22824
+ const path = event.path ?? "";
22825
+ if (!path) return;
22826
+ if (pathFilter != null && !pathFilter(path)) return;
22827
+ const existing = this.flow.get(path);
22828
+ const count = existing != null ? existing.count + 1 : 1;
22829
+ this.flow.set(path, { path, count, lastUpdate_ns: monotonicNs() });
22830
+ }
22831
+ });
22832
+ this.addDisposer(() => {
22833
+ offObserve();
22834
+ observeHandle.dispose();
22835
+ });
22836
+ this.stats = derived(
22837
+ [statsTick],
22838
+ () => computeTopologyStats(target.describe({ detail: "minimal" })),
22839
+ {
22840
+ name: "stats",
22841
+ describeKind: "derived",
22842
+ equals: topologyStatsEqual,
22843
+ meta: lensMeta("stats")
22844
+ }
22845
+ );
22846
+ this.add("stats", this.stats);
22847
+ this.addDisposer(keepalive(this.stats));
22848
+ this.health = derived(
22849
+ [healthTick],
22850
+ () => computeHealthReport(target.describe({ detail: "standard" })),
22851
+ {
22852
+ name: "health",
22853
+ describeKind: "derived",
22854
+ equals: healthReportEqual,
22855
+ meta: lensMeta("health")
22856
+ }
22857
+ );
22858
+ this.add("health", this.health);
22859
+ this.addDisposer(keepalive(this.health));
22860
+ }
22861
+ /**
22862
+ * Live causal chain from `from` to `to`. Recomputes whenever the target
22863
+ * mutates. Disposed automatically when the lens is destroyed.
22864
+ *
22865
+ * **Lifetime note:** every call to `why()` registers a lens-owned disposer
22866
+ * that runs on `lens.destroy()`. The returned `dispose` function releases
22867
+ * the internal subscription but does NOT remove the lens-owned disposer —
22868
+ * so heavy calling (e.g. per render frame) accumulates no-op disposers
22869
+ * until lens teardown. Cache the returned handle for long-lived queries.
22870
+ *
22871
+ * @param from - Qualified path of the upstream endpoint.
22872
+ * @param to - Qualified path of the downstream endpoint.
22873
+ * @param opts - See {@link reactiveExplainPath}.
22874
+ */
22875
+ why(from, to, opts) {
22876
+ const handle = reactiveExplainPath(this._target, from, to, opts);
22877
+ this.addDisposer(handle.dispose);
22878
+ return handle;
22879
+ }
22880
+ /** Reference to the lensed graph. */
22881
+ get target() {
22882
+ return this._target;
22883
+ }
22884
+ };
22885
+ function graphLens(target, opts) {
22886
+ return new LensGraph(target, opts);
22887
+ }
22888
+
21682
22889
  // src/patterns/reactive-layout/index.ts
21683
22890
  var reactive_layout_exports = {};
21684
22891
  __export(reactive_layout_exports, {
@@ -21899,7 +23106,7 @@ var CanvasMeasureAdapter = class {
21899
23106
  this.currentFont = font;
21900
23107
  }
21901
23108
  let width = ctx.measureText(text).width;
21902
- if (this.emojiCorrection !== 1 && /\p{Emoji_Presentation}/u.test(text)) {
23109
+ if (this.emojiCorrection !== 1 && new RegExp("\\p{Emoji_Presentation}", "u").test(text)) {
21903
23110
  width *= this.emojiCorrection;
21904
23111
  }
21905
23112
  return { width };
@@ -22349,6 +23556,385 @@ function reactiveFlowLayout(opts) {
22349
23556
  };
22350
23557
  }
22351
23558
 
23559
+ // src/patterns/resilient-pipeline.ts
23560
+ var resilient_pipeline_exports = {};
23561
+ __export(resilient_pipeline_exports, {
23562
+ NS_PER_MS: () => NS_PER_MS,
23563
+ NS_PER_SEC: () => NS_PER_SEC,
23564
+ resilientPipeline: () => resilientPipeline
23565
+ });
23566
+ function resilientPipeline(source, opts = {}) {
23567
+ let current = source;
23568
+ if (opts.rateLimit != null) {
23569
+ current = rateLimiter(current, opts.rateLimit);
23570
+ }
23571
+ if (opts.budget != null && opts.budget.length > 0) {
23572
+ current = budgetGate(current, opts.budget);
23573
+ }
23574
+ let breakerState;
23575
+ if (opts.breaker != null) {
23576
+ const breaker = circuitBreaker(opts.breaker);
23577
+ const onOpen = opts.breakerOnOpen ?? "skip";
23578
+ const wrapped = withBreaker(breaker, { onOpen })(current);
23579
+ current = wrapped.node;
23580
+ breakerState = wrapped.breakerState;
23581
+ }
23582
+ if (opts.timeoutMs != null) {
23583
+ if (opts.timeoutMs <= 0) throw new RangeError("timeoutMs must be > 0");
23584
+ if (opts.timeoutMs > 9e6) {
23585
+ throw new RangeError(
23586
+ "timeoutMs must be <= 9_000_000 (\u22482.5h) to stay within safe ns arithmetic"
23587
+ );
23588
+ }
23589
+ current = timeout(current, opts.timeoutMs * NS_PER_MS);
23590
+ }
23591
+ if (opts.retry != null) {
23592
+ current = retry(current, opts.retry);
23593
+ }
23594
+ if (opts.fallback !== void 0) {
23595
+ current = fallback(current, opts.fallback);
23596
+ }
23597
+ const withStatusBundle = withStatus(current, { initialStatus: opts.initialStatus ?? "pending" });
23598
+ return {
23599
+ node: withStatusBundle.node,
23600
+ status: withStatusBundle.status,
23601
+ error: withStatusBundle.error,
23602
+ breakerState
23603
+ };
23604
+ }
23605
+
23606
+ // src/patterns/surface/index.ts
23607
+ var surface_exports = {};
23608
+ __export(surface_exports, {
23609
+ SNAPSHOT_WIRE_VERSION: () => SNAPSHOT_WIRE_VERSION,
23610
+ SurfaceError: () => SurfaceError,
23611
+ asSurfaceError: () => asSurfaceError,
23612
+ createGraph: () => createGraph,
23613
+ deleteSnapshot: () => deleteSnapshot,
23614
+ diffSnapshots: () => diffSnapshots,
23615
+ listSnapshots: () => listSnapshots,
23616
+ restoreSnapshot: () => restoreSnapshot,
23617
+ runReduction: () => runReduction,
23618
+ saveSnapshot: () => saveSnapshot
23619
+ });
23620
+
23621
+ // src/patterns/surface/errors.ts
23622
+ var SurfaceError = class extends Error {
23623
+ code;
23624
+ details;
23625
+ constructor(code, message, details) {
23626
+ super(message);
23627
+ this.name = "SurfaceError";
23628
+ this.code = code;
23629
+ if (details !== void 0) this.details = details;
23630
+ }
23631
+ /**
23632
+ * JSON-safe payload for wire serialization. Defensively validates
23633
+ * `details` — if it can't be round-tripped through `JSON.stringify`
23634
+ * (cyclic refs, `BigInt`, `Error` instance not pre-toJSON'd), the
23635
+ * payload falls back to `{code, message}` only rather than crashing
23636
+ * the MCP/CLI wrapper when it serializes this error onto the wire.
23637
+ */
23638
+ toJSON() {
23639
+ const out = { code: this.code, message: this.message };
23640
+ if (this.details !== void 0) {
23641
+ const safe = safeDetails(this.details);
23642
+ if (safe !== void 0) out.details = safe;
23643
+ }
23644
+ return out;
23645
+ }
23646
+ };
23647
+ function safeDetails(details) {
23648
+ try {
23649
+ return JSON.parse(JSON.stringify(details));
23650
+ } catch {
23651
+ return void 0;
23652
+ }
23653
+ }
23654
+ function asSurfaceError(err, fallbackCode = "internal-error") {
23655
+ if (err instanceof SurfaceError) return err;
23656
+ const message = err instanceof Error ? err.message : String(err);
23657
+ return new SurfaceError(fallbackCode, message);
23658
+ }
23659
+
23660
+ // src/patterns/surface/create.ts
23661
+ function createGraph(spec, opts) {
23662
+ const structural = validateSpec(spec);
23663
+ if (!structural.valid) {
23664
+ throw new SurfaceError(
23665
+ "invalid-spec",
23666
+ `GraphSpec validation failed:
23667
+ ${structural.errors.join("\n")}`,
23668
+ { errors: structural.errors }
23669
+ );
23670
+ }
23671
+ const catalog = opts?.catalog ?? {};
23672
+ const catalogValidation = validateSpecAgainstCatalog(spec, catalog);
23673
+ if (!catalogValidation.valid) {
23674
+ throw new SurfaceError(
23675
+ "catalog-error",
23676
+ `Catalog validation failed:
23677
+ ${catalogValidation.errors.join("\n")}`,
23678
+ { errors: catalogValidation.errors }
23679
+ );
23680
+ }
23681
+ try {
23682
+ return compileSpec(spec, opts);
23683
+ } catch (err) {
23684
+ const message = err instanceof Error ? err.message : String(err);
23685
+ throw new SurfaceError("catalog-error", message);
23686
+ }
23687
+ }
23688
+
23689
+ // src/patterns/surface/reduce.ts
23690
+ var DEFAULT_TIMEOUT_MS2 = 3e4;
23691
+ async function runReduction(spec, input, opts) {
23692
+ const inputPath = opts?.inputPath ?? "input";
23693
+ const outputPath = opts?.outputPath ?? "output";
23694
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
23695
+ const graph = createGraph(spec, { catalog: opts?.catalog });
23696
+ let outputNode;
23697
+ try {
23698
+ outputNode = graph.resolve(outputPath);
23699
+ } catch {
23700
+ graph.destroy();
23701
+ throw new SurfaceError(
23702
+ "node-not-found",
23703
+ `reduce: output path "${outputPath}" is not registered`,
23704
+ { path: outputPath }
23705
+ );
23706
+ }
23707
+ try {
23708
+ graph.resolve(inputPath);
23709
+ } catch {
23710
+ graph.destroy();
23711
+ throw new SurfaceError(
23712
+ "node-not-found",
23713
+ `reduce: input path "${inputPath}" is not registered`,
23714
+ { path: inputPath }
23715
+ );
23716
+ }
23717
+ try {
23718
+ return await new Promise((resolve, reject) => {
23719
+ let primed = false;
23720
+ let settled = false;
23721
+ let timer;
23722
+ let unsub;
23723
+ let shouldUnsub = false;
23724
+ const finish = (action2) => {
23725
+ if (settled) return;
23726
+ settled = true;
23727
+ if (timer !== void 0) clearTimeout(timer);
23728
+ if (unsub !== void 0) {
23729
+ unsub();
23730
+ unsub = void 0;
23731
+ } else {
23732
+ shouldUnsub = true;
23733
+ }
23734
+ action2();
23735
+ };
23736
+ unsub = outputNode.subscribe((msgs) => {
23737
+ for (const m of msgs) {
23738
+ if (settled) return;
23739
+ if (!primed) continue;
23740
+ if (m[0] === DATA) {
23741
+ finish(() => resolve(m[1]));
23742
+ return;
23743
+ }
23744
+ if (m[0] === RESOLVED) {
23745
+ const cached2 = outputNode.cache;
23746
+ finish(() => resolve(cached2));
23747
+ return;
23748
+ }
23749
+ if (m[0] === ERROR) {
23750
+ const payload = m[1];
23751
+ const message = payload instanceof Error ? payload.message : String(payload);
23752
+ const cause = payload instanceof Error ? payload : void 0;
23753
+ finish(
23754
+ () => reject(
23755
+ new SurfaceError(
23756
+ "internal-error",
23757
+ `reduce: output emitted ERROR: ${message}`,
23758
+ cause != null ? { cause } : void 0
23759
+ )
23760
+ )
23761
+ );
23762
+ return;
23763
+ }
23764
+ if (m[0] === COMPLETE) {
23765
+ finish(
23766
+ () => reject(
23767
+ new SurfaceError(
23768
+ "internal-error",
23769
+ `reduce: output COMPLETEd without a post-push DATA`
23770
+ )
23771
+ )
23772
+ );
23773
+ return;
23774
+ }
23775
+ }
23776
+ });
23777
+ if (shouldUnsub) {
23778
+ unsub?.();
23779
+ unsub = void 0;
23780
+ }
23781
+ primed = true;
23782
+ try {
23783
+ graph.set(inputPath, input);
23784
+ } catch (err) {
23785
+ const message = err instanceof Error ? err.message : String(err);
23786
+ const cause = err instanceof Error ? err : void 0;
23787
+ finish(
23788
+ () => reject(
23789
+ new SurfaceError(
23790
+ "internal-error",
23791
+ `reduce: failed to set input on "${inputPath}": ${message}`,
23792
+ cause != null ? { path: inputPath, cause } : { path: inputPath }
23793
+ )
23794
+ )
23795
+ );
23796
+ return;
23797
+ }
23798
+ if (!settled && Number.isFinite(timeoutMs) && timeoutMs > 0) {
23799
+ timer = setTimeout(() => {
23800
+ finish(
23801
+ () => reject(
23802
+ new SurfaceError(
23803
+ "reduce-timeout",
23804
+ `reduce: no output emitted within ${timeoutMs}ms`,
23805
+ { timeoutMs, outputPath }
23806
+ )
23807
+ )
23808
+ );
23809
+ }, timeoutMs);
23810
+ timer.unref?.();
23811
+ }
23812
+ });
23813
+ } finally {
23814
+ graph.destroy();
23815
+ }
23816
+ }
23817
+
23818
+ // src/patterns/surface/snapshot.ts
23819
+ var SNAPSHOT_WIRE_VERSION = SNAPSHOT_VERSION;
23820
+ function unwrapCheckpoint(raw, snapshotId) {
23821
+ if (raw == null || typeof raw !== "object") {
23822
+ throw new SurfaceError("snapshot-not-found", `snapshot "${snapshotId}" not found in tier`, {
23823
+ snapshotId
23824
+ });
23825
+ }
23826
+ const record = raw;
23827
+ if ("mode" in record) {
23828
+ if (record.mode === "full" && "snapshot" in record) {
23829
+ return record.snapshot;
23830
+ }
23831
+ if (record.mode === "diff") {
23832
+ throw new SurfaceError(
23833
+ "restore-failed",
23834
+ `snapshot "${snapshotId}" is a diff record; restore the baseline and replay WAL instead`,
23835
+ { snapshotId, mode: "diff" }
23836
+ );
23837
+ }
23838
+ throw new SurfaceError(
23839
+ "restore-failed",
23840
+ `snapshot "${snapshotId}" has unknown mode "${String(record.mode)}"`,
23841
+ { snapshotId, mode: String(record.mode) }
23842
+ );
23843
+ }
23844
+ if ("nodes" in record && "edges" in record && "subgraphs" in record && "name" in record) {
23845
+ return record;
23846
+ }
23847
+ throw new SurfaceError(
23848
+ "restore-failed",
23849
+ `snapshot "${snapshotId}" payload is not a GraphCheckpointRecord or GraphPersistSnapshot`,
23850
+ { snapshotId }
23851
+ );
23852
+ }
23853
+ async function saveSnapshot(graph, snapshotId, tier) {
23854
+ let snapshot;
23855
+ try {
23856
+ snapshot = graph.snapshot();
23857
+ } catch (err) {
23858
+ const message = err instanceof Error ? err.message : String(err);
23859
+ throw new SurfaceError(
23860
+ "snapshot-failed",
23861
+ `snapshot "${snapshotId}" serialization failed: ${message}`,
23862
+ { snapshotId }
23863
+ );
23864
+ }
23865
+ const record = {
23866
+ mode: "full",
23867
+ seq: 0,
23868
+ timestamp_ns: wallClockNs(),
23869
+ format_version: SNAPSHOT_WIRE_VERSION,
23870
+ snapshot
23871
+ };
23872
+ try {
23873
+ await tier.save(snapshotId, record);
23874
+ } catch (err) {
23875
+ const message = err instanceof Error ? err.message : String(err);
23876
+ throw new SurfaceError("snapshot-failed", `snapshot "${snapshotId}" save failed: ${message}`, {
23877
+ snapshotId
23878
+ });
23879
+ }
23880
+ return { snapshotId, timestamp_ns: record.timestamp_ns };
23881
+ }
23882
+ async function restoreSnapshot(snapshotId, tier, opts) {
23883
+ const raw = await tier.load(snapshotId);
23884
+ const snapshot = unwrapCheckpoint(raw, snapshotId);
23885
+ try {
23886
+ return Graph.fromSnapshot(
23887
+ snapshot,
23888
+ opts?.factories ? { factories: opts.factories } : void 0
23889
+ );
23890
+ } catch (err) {
23891
+ const message = err instanceof Error ? err.message : String(err);
23892
+ throw new SurfaceError(
23893
+ "restore-failed",
23894
+ `snapshot "${snapshotId}" restore failed: ${message}`,
23895
+ {
23896
+ snapshotId
23897
+ }
23898
+ );
23899
+ }
23900
+ }
23901
+ async function diffSnapshots(snapshotIdA, snapshotIdB, tier) {
23902
+ const [rawA, rawB] = await Promise.all([tier.load(snapshotIdA), tier.load(snapshotIdB)]);
23903
+ const snapshotA = unwrapCheckpoint(rawA, snapshotIdA);
23904
+ const snapshotB = unwrapCheckpoint(rawB, snapshotIdB);
23905
+ return Graph.diff(snapshotA, snapshotB);
23906
+ }
23907
+ async function listSnapshots(tier) {
23908
+ if (typeof tier.list !== "function") {
23909
+ throw new SurfaceError(
23910
+ "tier-no-list",
23911
+ "StorageTier does not implement list(); wrap the tier with an enumerator or use a different backend"
23912
+ );
23913
+ }
23914
+ return tier.list();
23915
+ }
23916
+ async function deleteSnapshot(snapshotId, tier) {
23917
+ if (typeof tier.clear !== "function") {
23918
+ throw new SurfaceError(
23919
+ "snapshot-failed",
23920
+ `StorageTier is append-only (no clear()); cannot delete "${snapshotId}"`,
23921
+ { snapshotId }
23922
+ );
23923
+ }
23924
+ try {
23925
+ await tier.clear(snapshotId);
23926
+ } catch (err) {
23927
+ const message = err instanceof Error ? err.message : String(err);
23928
+ throw new SurfaceError(
23929
+ "snapshot-failed",
23930
+ `snapshot "${snapshotId}" delete failed: ${message}`,
23931
+ {
23932
+ snapshotId
23933
+ }
23934
+ );
23935
+ }
23936
+ }
23937
+
22352
23938
  // src/index.ts
22353
23939
  var version = "0.0.0";
22354
23940
  // Annotate the CommonJS export names for ESM import in node:
@@ -22389,15 +23975,20 @@ var version = "0.0.0";
22389
23975
  ResettableTimer,
22390
23976
  SIZEOF_OVERHEAD,
22391
23977
  SIZEOF_SYMBOL,
23978
+ SNAPSHOT_VERSION,
23979
+ SNAPSHOT_WIRE_VERSION,
22392
23980
  START,
22393
23981
  START_MSG,
23982
+ SurfaceError,
22394
23983
  TEARDOWN,
22395
23984
  TEARDOWN_MSG,
22396
23985
  TEARDOWN_ONLY_BATCH,
22397
23986
  TimeoutError,
22398
23987
  accessHintForGuard,
23988
+ accountability,
22399
23989
  advanceVersion,
22400
23990
  ai,
23991
+ asSurfaceError,
22401
23992
  audit,
22402
23993
  autoTrackNode,
22403
23994
  batch,
@@ -22421,6 +24012,7 @@ var version = "0.0.0";
22421
24012
  cqrs,
22422
24013
  createDagCborCodec,
22423
24014
  createDagCborZstdCodec,
24015
+ createGraph,
22424
24016
  createTransport,
22425
24017
  createVersioning,
22426
24018
  createWatermarkController,
@@ -22432,11 +24024,13 @@ var version = "0.0.0";
22432
24024
  defaultConfig,
22433
24025
  defaultHash,
22434
24026
  delay,
24027
+ deleteSnapshot,
22435
24028
  demoShell,
22436
24029
  derived,
22437
24030
  deserializeError,
22438
24031
  dictStorage,
22439
24032
  diffForWAL,
24033
+ diffSnapshots,
22440
24034
  distill,
22441
24035
  distinctUntilChanged,
22442
24036
  domainTemplates,
@@ -22448,6 +24042,7 @@ var version = "0.0.0";
22448
24042
  encodeEnvelope,
22449
24043
  escapeRegexChar,
22450
24044
  exhaustMap,
24045
+ explainPath,
22451
24046
  exponential,
22452
24047
  externalBundle,
22453
24048
  externalProducer,
@@ -22503,6 +24098,7 @@ var version = "0.0.0";
22503
24098
  graph,
22504
24099
  graphProfile,
22505
24100
  graphspec,
24101
+ guarded,
22506
24102
  harness,
22507
24103
  indexedDbStorage,
22508
24104
  interval,
@@ -22512,7 +24108,9 @@ var version = "0.0.0";
22512
24108
  keepalive,
22513
24109
  last,
22514
24110
  layout,
24111
+ lens,
22515
24112
  linear,
24113
+ listSnapshots,
22516
24114
  lru,
22517
24115
  map,
22518
24116
  matchesAnyPattern,
@@ -22562,11 +24160,15 @@ var version = "0.0.0";
22562
24160
  replay,
22563
24161
  replayWAL,
22564
24162
  rescue,
24163
+ resilientPipeline,
22565
24164
  resolveBackoffPreset,
22566
24165
  resolveDescribeFields,
24166
+ restoreSnapshot,
22567
24167
  retry,
22568
24168
  retrySource,
24169
+ runReduction,
22569
24170
  sample,
24171
+ saveSnapshot,
22570
24172
  scan,
22571
24173
  serializeError,
22572
24174
  share,
@@ -22578,6 +24180,7 @@ var version = "0.0.0";
22578
24180
  solid,
22579
24181
  sqliteStorage,
22580
24182
  state,
24183
+ surface,
22581
24184
  svelte,
22582
24185
  switchMap,
22583
24186
  take,
@@ -22615,6 +24218,7 @@ var version = "0.0.0";
22615
24218
  version,
22616
24219
  vue,
22617
24220
  wallClockNs,
24221
+ watchTopologyTree,
22618
24222
  window,
22619
24223
  windowCount,
22620
24224
  windowTime,