@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
@@ -26,16 +26,19 @@ __export(graph_exports, {
26
26
  JsonCodec: () => JsonCodec,
27
27
  SIZEOF_OVERHEAD: () => OVERHEAD,
28
28
  SIZEOF_SYMBOL: () => SIZEOF_SYMBOL,
29
+ SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
29
30
  createDagCborCodec: () => createDagCborCodec,
30
31
  createDagCborZstdCodec: () => createDagCborZstdCodec,
31
32
  decodeEnvelope: () => decodeEnvelope,
32
33
  diffForWAL: () => diffForWAL,
33
34
  encodeEnvelope: () => encodeEnvelope,
35
+ explainPath: () => explainPath,
34
36
  graphProfile: () => graphProfile,
35
37
  reachable: () => reachable,
36
38
  registerBuiltinCodecs: () => registerBuiltinCodecs,
37
39
  replayWAL: () => replayWAL,
38
- sizeof: () => sizeof
40
+ sizeof: () => sizeof,
41
+ watchTopologyTree: () => watchTopologyTree
39
42
  });
40
43
  module.exports = __toCommonJS(graph_exports);
41
44
 
@@ -317,6 +320,200 @@ function replayWAL(entries) {
317
320
  return result;
318
321
  }
319
322
 
323
+ // src/graph/explain.ts
324
+ function explainPath(described, from, to, opts = {}) {
325
+ const fromExists = from in described.nodes;
326
+ const toExists = to in described.nodes;
327
+ if (!fromExists) return makeFailure(from, to, "no-such-from");
328
+ if (!toExists) return makeFailure(from, to, "no-such-to");
329
+ const maxDepth = opts.maxDepth;
330
+ if (maxDepth != null && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
331
+ throw new Error(`explainPath: maxDepth must be an integer >= 0`);
332
+ }
333
+ if (from === to) {
334
+ if (opts.findCycle === true) {
335
+ const cycle = findShortestCycle(described, from, opts);
336
+ if (cycle != null) return cycle;
337
+ }
338
+ const step = buildStep(from, described.nodes[from], 0, opts);
339
+ return makeSuccess(from, to, [step]);
340
+ }
341
+ if (maxDepth === 0) return makeFailure(from, to, "no-path");
342
+ const result = bfsShortestPath(described, from, to, maxDepth);
343
+ if (!result.found) {
344
+ return makeFailure(from, to, result.truncated ? "max-depth-exceeded" : "no-path");
345
+ }
346
+ return makeSuccess(from, to, materializeSteps(described, result.pathOrder, opts));
347
+ }
348
+ function bfsShortestPath(described, from, to, maxDepth) {
349
+ const pred = /* @__PURE__ */ new Map();
350
+ const queue = [{ path: to, depth: 0 }];
351
+ const visited = /* @__PURE__ */ new Set([to]);
352
+ let head = 0;
353
+ let truncated = false;
354
+ while (head < queue.length) {
355
+ const cur = queue[head++];
356
+ if (cur.path === from) break;
357
+ if (maxDepth != null && cur.depth >= maxDepth) {
358
+ const node3 = described.nodes[cur.path];
359
+ if (node3?.deps && node3.deps.length > 0) truncated = true;
360
+ continue;
361
+ }
362
+ const node2 = described.nodes[cur.path];
363
+ if (node2 == null) continue;
364
+ const deps = node2.deps ?? [];
365
+ const slots = /* @__PURE__ */ new Map();
366
+ for (let i = 0; i < deps.length; i++) {
367
+ const dep = deps[i];
368
+ if (!dep) continue;
369
+ let arr = slots.get(dep);
370
+ if (arr == null) {
371
+ arr = [];
372
+ slots.set(dep, arr);
373
+ }
374
+ arr.push(i);
375
+ }
376
+ for (const [dep, indices] of slots) {
377
+ if (visited.has(dep)) continue;
378
+ visited.add(dep);
379
+ pred.set(dep, { from: cur.path, depIndices: indices });
380
+ queue.push({ path: dep, depth: cur.depth + 1 });
381
+ }
382
+ }
383
+ if (!pred.has(from)) {
384
+ return { found: false, pathOrder: [], truncated };
385
+ }
386
+ const pathOrder = [{ path: from }];
387
+ let cursor = from;
388
+ while (cursor !== to) {
389
+ const p = pred.get(cursor);
390
+ if (p == null) return { found: false, pathOrder: [], truncated: false };
391
+ pathOrder[pathOrder.length - 1].depIndices = p.depIndices;
392
+ pathOrder.push({ path: p.from });
393
+ cursor = p.from;
394
+ }
395
+ return { found: true, pathOrder, truncated: false };
396
+ }
397
+ function findShortestCycle(described, start, opts) {
398
+ const startNode = described.nodes[start];
399
+ if (startNode == null) return null;
400
+ const startDeps = startNode.deps ?? [];
401
+ const selfSlots = [];
402
+ for (let i = 0; i < startDeps.length; i++) if (startDeps[i] === start) selfSlots.push(i);
403
+ if (selfSlots.length > 0) {
404
+ const step0 = buildStep(start, startNode, 0, opts);
405
+ step0.dep_index = selfSlots[0];
406
+ const step1 = buildStep(start, startNode, 1, opts);
407
+ return makeSuccess(start, start, [step0, step1]);
408
+ }
409
+ let best = null;
410
+ for (let i = 0; i < startDeps.length; i++) {
411
+ const dep = startDeps[i];
412
+ if (!dep || dep === start) continue;
413
+ const sub = bfsShortestPath(described, dep, start, opts.maxDepth);
414
+ if (!sub.found) continue;
415
+ if (best == null || sub.pathOrder.length < best.pathOrder.length) {
416
+ best = sub;
417
+ best = {
418
+ found: true,
419
+ pathOrder: [{ path: start, depIndices: [i] }, ...sub.pathOrder],
420
+ truncated: false
421
+ };
422
+ }
423
+ }
424
+ if (best == null) return null;
425
+ return makeSuccess(start, start, materializeSteps(described, best.pathOrder, opts));
426
+ }
427
+ function materializeSteps(described, pathOrder, opts) {
428
+ return pathOrder.map((entry, i) => {
429
+ const node2 = described.nodes[entry.path];
430
+ const step = buildStep(entry.path, node2, i, opts);
431
+ if (entry.depIndices != null && entry.depIndices.length > 0) {
432
+ step.dep_index = entry.depIndices[0];
433
+ if (entry.depIndices.length > 1) step.dep_indices = [...entry.depIndices];
434
+ }
435
+ return step;
436
+ });
437
+ }
438
+ function buildStep(path, node2, hop, opts) {
439
+ const step = {
440
+ path,
441
+ type: node2.type,
442
+ hop
443
+ };
444
+ if (node2.status !== void 0) step.status = node2.status;
445
+ if ("value" in node2) step.value = node2.value;
446
+ if (node2.v != null) step.v = node2.v;
447
+ const annotation = opts.annotations?.get(path) ?? node2.reason;
448
+ if (annotation != null) step.reason = annotation;
449
+ const lastMutation = opts.lastMutations?.get(path) ?? node2.lastMutation;
450
+ if (lastMutation != null) step.lastMutation = lastMutation;
451
+ return step;
452
+ }
453
+ function makeSuccess(from, to, steps) {
454
+ return finalize(from, to, true, "ok", steps);
455
+ }
456
+ function makeFailure(from, to, reason) {
457
+ return finalize(from, to, false, reason, []);
458
+ }
459
+ function finalize(from, to, found, reason, steps) {
460
+ const text = renderChain(from, to, found, reason, steps);
461
+ return {
462
+ from,
463
+ to,
464
+ found,
465
+ reason,
466
+ steps,
467
+ text,
468
+ toJSON() {
469
+ return { from, to, found, reason, steps };
470
+ }
471
+ };
472
+ }
473
+ function renderChain(from, to, found, reason, steps) {
474
+ if (!found) {
475
+ switch (reason) {
476
+ case "no-such-from":
477
+ return `explainPath: no node named "${from}"`;
478
+ case "no-such-to":
479
+ return `explainPath: no node named "${to}"`;
480
+ case "max-depth-exceeded":
481
+ return `explainPath: no path from "${from}" to "${to}" within maxDepth`;
482
+ default:
483
+ return `explainPath: no path from "${from}" to "${to}"`;
484
+ }
485
+ }
486
+ const lines = [`Causal path: ${from} \u2192 ${to} (${steps.length} step(s))`];
487
+ for (const step of steps) {
488
+ const arrow = step.hop === 0 ? "\xB7" : "\u2193";
489
+ const head = ` ${arrow} ${step.path} (${step.type}${step.status ? `/${step.status}` : ""})`;
490
+ lines.push(head);
491
+ if ("value" in step) {
492
+ lines.push(` value: ${formatValue(step.value)}`);
493
+ }
494
+ if (step.reason != null) {
495
+ lines.push(` reason: ${step.reason}`);
496
+ }
497
+ if (step.lastMutation != null) {
498
+ const a = step.lastMutation.actor;
499
+ lines.push(` actor: ${a.type}${a.id ? `:${a.id}` : ""}`);
500
+ }
501
+ }
502
+ return lines.join("\n");
503
+ }
504
+ function formatValue(v) {
505
+ if (v === void 0) return "<sentinel>";
506
+ if (v === null) return "null";
507
+ if (typeof v === "string") return JSON.stringify(v);
508
+ if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
509
+ try {
510
+ const s = JSON.stringify(v);
511
+ return s.length > 80 ? `${s.slice(0, 77)}...` : s;
512
+ } catch {
513
+ return String(v);
514
+ }
515
+ }
516
+
320
517
  // src/core/batch.ts
321
518
  var MAX_DRAIN_ITERATIONS = 1e3;
322
519
  var batchDepth = 0;
@@ -1077,6 +1274,12 @@ var NodeImpl = class _NodeImpl {
1077
1274
  _autoError;
1078
1275
  _pausable;
1079
1276
  _guard;
1277
+ /**
1278
+ * @internal Additional guards stacked at runtime via {@link NodeImpl._pushGuard}
1279
+ * (e.g. by `policyEnforcer({ mode: "enforce" })`, roadmap §9.2). Effective
1280
+ * write/signal/observe checks AND the original `_guard` with every entry here.
1281
+ */
1282
+ _extraGuards;
1080
1283
  _hashFn;
1081
1284
  _versioning;
1082
1285
  /**
@@ -1250,18 +1453,61 @@ var NodeImpl = class _NodeImpl {
1250
1453
  if (this._inspectorHooks?.size === 0) this._inspectorHooks = void 0;
1251
1454
  };
1252
1455
  }
1456
+ /**
1457
+ * @internal Push an additional guard onto this node. Effective enforcement
1458
+ * is the AND of `_guard` and every guard pushed via this hook — any one
1459
+ * rejecting throws {@link GuardDenied}. Returns a disposer that removes
1460
+ * the pushed guard. Multiple guards may be stacked simultaneously.
1461
+ *
1462
+ * Used by `policyEnforcer({ mode: "enforce" })` (roadmap §9.2) to overlay
1463
+ * runtime constraint enforcement onto an existing graph without rebuilding
1464
+ * its nodes. Pre-1.0 internal API; not part of the public surface.
1465
+ *
1466
+ * **Identity semantics:** guards are tracked in a `Set`, so pushing the
1467
+ * same `NodeGuard` reference twice is a single registration. Wrap each
1468
+ * push in a unique closure if independent stacking is needed.
1469
+ *
1470
+ * **Iteration order:** insertion-ordered (`Set` semantics). Determinism
1471
+ * follows from single-threaded JS execution; nested re-entry from inside
1472
+ * a guard body (push/pop while iterating) is undefined-but-survivable.
1473
+ */
1474
+ _pushGuard(guard) {
1475
+ if (this._extraGuards == null) this._extraGuards = /* @__PURE__ */ new Set();
1476
+ this._extraGuards.add(guard);
1477
+ return () => {
1478
+ this._extraGuards?.delete(guard);
1479
+ if (this._extraGuards?.size === 0) this._extraGuards = void 0;
1480
+ };
1481
+ }
1253
1482
  allowsObserve(actor) {
1254
- if (this._guard == null) return true;
1255
- return this._guard(normalizeActor(actor), "observe");
1483
+ if (this._guard == null && this._extraGuards == null) return true;
1484
+ const a = normalizeActor(actor);
1485
+ if (this._guard != null && !this._guard(a, "observe")) return false;
1486
+ if (this._extraGuards != null) {
1487
+ for (const eg of this._extraGuards) {
1488
+ if (!eg(a, "observe")) return false;
1489
+ }
1490
+ }
1491
+ return true;
1256
1492
  }
1257
1493
  // --- Guard helper ---
1258
1494
  _checkGuard(options) {
1259
- if (options?.internal || this._guard == null) return;
1495
+ if (options?.internal) return;
1496
+ const hasGuard = this._guard != null || this._extraGuards != null;
1497
+ const hasActor = options?.actor != null;
1498
+ if (!hasGuard && !hasActor) return;
1260
1499
  const actor = normalizeActor(options?.actor);
1261
1500
  const action = options?.delivery === "signal" ? "signal" : "write";
1262
- if (!this._guard(actor, action)) {
1501
+ if (this._guard != null && !this._guard(actor, action)) {
1263
1502
  throw new GuardDenied({ actor, action, nodeName: this.name });
1264
1503
  }
1504
+ if (this._extraGuards != null) {
1505
+ for (const eg of this._extraGuards) {
1506
+ if (!eg(actor, action)) {
1507
+ throw new GuardDenied({ actor, action, nodeName: this.name });
1508
+ }
1509
+ }
1510
+ }
1265
1511
  this._lastMutation = { actor, timestamp_ns: wallClockNs() };
1266
1512
  }
1267
1513
  // --- Public transport ---
@@ -2275,6 +2521,10 @@ function describeNode(node2, includeFields) {
2275
2521
  function state(initial, opts) {
2276
2522
  return node([], { ...opts, initial });
2277
2523
  }
2524
+ function producer(fn, opts) {
2525
+ const wrapped = (_data, actions, ctx) => fn(actions, ctx) ?? void 0;
2526
+ return node(wrapped, { describeKind: "producer", ...opts });
2527
+ }
2278
2528
 
2279
2529
  // src/extra/timer.ts
2280
2530
  var ResettableTimer = class {
@@ -2871,6 +3121,20 @@ var Graph = class _Graph {
2871
3121
  _parent = void 0;
2872
3122
  _storageDisposers = /* @__PURE__ */ new Set();
2873
3123
  _disposers = /* @__PURE__ */ new Set();
3124
+ /**
3125
+ * @internal Lazy `TopologyEvent` producer. Created on first `.topology`
3126
+ * access. Zero cost until something subscribes — producer fn only runs when
3127
+ * the first sink attaches, registering one handler into
3128
+ * {@link Graph._topologyEmitters}.
3129
+ */
3130
+ _topology;
3131
+ /**
3132
+ * @internal Active emit handlers for the topology producer. Each entry is
3133
+ * the closure registered by the producer fn on activation; cleared on
3134
+ * deactivation. `_emitTopology` broadcasts through every entry (there is at
3135
+ * most one per activation cycle of the producer).
3136
+ */
3137
+ _topologyEmitters = /* @__PURE__ */ new Set();
2874
3138
  /**
2875
3139
  * @param name - Non-empty graph id (must not contain `::` and must not
2876
3140
  * equal the reserved meta segment `__meta__`).
@@ -2910,6 +3174,55 @@ var Graph = class _Graph {
2910
3174
  return out;
2911
3175
  }
2912
3176
  // ——————————————————————————————————————————————————————————————
3177
+ // Topology companion (structural-change event stream)
3178
+ // ——————————————————————————————————————————————————————————————
3179
+ /**
3180
+ * Reactive stream of structural changes to this graph's own registry
3181
+ * (add / mount / remove). Value mutations live on `observe()`; this
3182
+ * companion only fires when the topology shape changes.
3183
+ *
3184
+ * Lazy: the underlying node is created on first access and activates when
3185
+ * something subscribes. No emission replay — late subscribers do not
3186
+ * receive historical events and should snapshot via {@link Graph.describe}
3187
+ * before listening for incremental changes. Events that fire while the
3188
+ * producer has zero subscribers are dropped (no retention).
3189
+ *
3190
+ * Own-graph only: a parent's `topology` does NOT emit for structural
3191
+ * changes inside a mounted child. Transitive consumers subscribe to each
3192
+ * child's topology separately (recurse through `topology`'s own "added"
3193
+ * events with `nodeKind: "mount"` to discover new children).
3194
+ *
3195
+ * See {@link TopologyEvent} for payload shape.
3196
+ *
3197
+ * @category observability
3198
+ */
3199
+ get topology() {
3200
+ if (this._topology == null) {
3201
+ this._topology = producer(
3202
+ (actions) => {
3203
+ const handler = (event) => {
3204
+ actions.emit(event);
3205
+ };
3206
+ this._topologyEmitters.add(handler);
3207
+ return () => {
3208
+ this._topologyEmitters.delete(handler);
3209
+ };
3210
+ },
3211
+ { name: `${this.name}_topology` }
3212
+ );
3213
+ }
3214
+ return this._topology;
3215
+ }
3216
+ /**
3217
+ * @internal Fire a {@link TopologyEvent} to every active subscriber of
3218
+ * `this.topology`. No-op when the topology node has never been accessed or
3219
+ * currently has no sinks — zero cost for graphs nobody observes.
3220
+ */
3221
+ _emitTopology(event) {
3222
+ if (this._topology == null || this._topologyEmitters.size === 0) return;
3223
+ for (const h of this._topologyEmitters) h(event);
3224
+ }
3225
+ // ——————————————————————————————————————————————————————————————
2913
3226
  // Node registry
2914
3227
  // ——————————————————————————————————————————————————————————————
2915
3228
  /**
@@ -2939,6 +3252,7 @@ var Graph = class _Graph {
2939
3252
  }
2940
3253
  this._nodes.set(name, node2);
2941
3254
  this._nodeToName.set(node2, name);
3255
+ this._emitTopology({ kind: "added", name, nodeKind: "node" });
2942
3256
  return node2;
2943
3257
  }
2944
3258
  /**
@@ -2979,22 +3293,23 @@ var Graph = class _Graph {
2979
3293
  assertRegisterableName(name, this.name, "remove");
2980
3294
  const child = this._mounts.get(name);
2981
3295
  if (child) {
2982
- const audit = { kind: "mount", nodes: [], mounts: [] };
3296
+ const audit2 = { kind: "mount", nodes: [], mounts: [] };
2983
3297
  const targets = [];
2984
3298
  child._collectObserveTargets("", targets);
2985
3299
  for (const [p, n] of targets) {
2986
3300
  if (!p.includes(`${PATH_SEP}${GRAPH_META_SEGMENT}${PATH_SEP}`)) {
2987
- audit.nodes.push(p);
3301
+ audit2.nodes.push(p);
2988
3302
  }
2989
3303
  void n;
2990
3304
  }
2991
- audit.nodes.sort();
2992
- audit.mounts.push(name);
2993
- audit.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
3305
+ audit2.nodes.sort();
3306
+ audit2.mounts.push(name);
3307
+ audit2.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
2994
3308
  this._mounts.delete(name);
2995
3309
  child._parent = void 0;
2996
3310
  teardownMountedGraph(child);
2997
- return audit;
3311
+ this._emitTopology({ kind: "removed", name, nodeKind: "mount", audit: audit2 });
3312
+ return audit2;
2998
3313
  }
2999
3314
  const node2 = this._nodes.get(name);
3000
3315
  if (!node2) {
@@ -3003,7 +3318,9 @@ var Graph = class _Graph {
3003
3318
  this._nodes.delete(name);
3004
3319
  this._nodeToName.delete(node2);
3005
3320
  node2.down([[TEARDOWN]], { internal: true });
3006
- return { kind: "node", nodes: [name], mounts: [] };
3321
+ const audit = { kind: "node", nodes: [name], mounts: [] };
3322
+ this._emitTopology({ kind: "removed", name, nodeKind: "node", audit });
3323
+ return audit;
3007
3324
  }
3008
3325
  /**
3009
3326
  * Bulk remove — invokes {@link Graph.remove} for every local name matching
@@ -3232,6 +3549,7 @@ var Graph = class _Graph {
3232
3549
  }
3233
3550
  this._mounts.set(name, child);
3234
3551
  child._parent = this;
3552
+ this._emitTopology({ kind: "added", name, nodeKind: "mount" });
3235
3553
  return child;
3236
3554
  }
3237
3555
  /**
@@ -3522,6 +3840,33 @@ var Graph = class _Graph {
3522
3840
  }
3523
3841
  return reachable(this.describe(), from, direction, opts);
3524
3842
  }
3843
+ /**
3844
+ * Causal walkback: shortest dep-chain from `from` to `to`, enriched with
3845
+ * each node's value, status, last-mutation actor, and reasoning annotation
3846
+ * from {@link Graph.trace}. Wraps {@link explainPath} (roadmap §9.2).
3847
+ *
3848
+ * @param from - Upstream node (the cause).
3849
+ * @param to - Downstream node (the effect).
3850
+ * @param opts - Optional `maxDepth` and `findCycle`. When `findCycle:true`
3851
+ * and `from === to`, returns the shortest cycle through other nodes
3852
+ * (useful for diagnosing feedback loops, COMPOSITION-GUIDE §7).
3853
+ * Annotations and lastMutations are collected automatically from the
3854
+ * live graph.
3855
+ */
3856
+ explain(from, to, opts) {
3857
+ const described = this.describe({ detail: "full" });
3858
+ const annotations = new Map(this._annotations);
3859
+ const lastMutations = /* @__PURE__ */ new Map();
3860
+ for (const [path, n] of Object.entries(described.nodes)) {
3861
+ if (n.lastMutation != null) lastMutations.set(path, n.lastMutation);
3862
+ }
3863
+ return explainPath(described, from, to, {
3864
+ ...opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {},
3865
+ ...opts?.findCycle === true ? { findCycle: true } : {},
3866
+ annotations,
3867
+ lastMutations
3868
+ });
3869
+ }
3525
3870
  /**
3526
3871
  * @internal Collect all qualified paths in this graph tree matching a
3527
3872
  * glob pattern. Used by scoped autoCheckpoint subscription.
@@ -4200,7 +4545,7 @@ var Graph = class _Graph {
4200
4545
  return;
4201
4546
  }
4202
4547
  const nextSeq = s.seq + 1;
4203
- const timestamp_ns = monotonicNs();
4548
+ const timestamp_ns = wallClockNs();
4204
4549
  const isFirst = s.lastSnapshot == null;
4205
4550
  const shouldCompact = isFirst || nextSeq % s.compactEvery === 0;
4206
4551
  const record = shouldCompact ? {
@@ -4614,6 +4959,52 @@ function reachable(described, from, direction, options = {}) {
4614
4959
  if (options.withDetail) return { paths, depths, truncated };
4615
4960
  return paths;
4616
4961
  }
4962
+
4963
+ // src/graph/topology-tree.ts
4964
+ function watchTopologyTree(graph, cb) {
4965
+ const subs = /* @__PURE__ */ new Map();
4966
+ const wire = (g, prefix) => {
4967
+ if (subs.has(g)) return;
4968
+ const placeholder = { off: () => {
4969
+ }, prefix };
4970
+ subs.set(g, placeholder);
4971
+ const off = g.topology.subscribe((msgs) => {
4972
+ for (const m of msgs) {
4973
+ if (m[0] !== DATA) continue;
4974
+ const event = m[1];
4975
+ cb(event, g, prefix);
4976
+ if (event.kind === "added" && event.nodeKind === "mount") {
4977
+ const child = g._mounts.get(event.name);
4978
+ if (child instanceof Graph) {
4979
+ const childPrefix = `${prefix}${event.name}::`;
4980
+ wire(child, childPrefix);
4981
+ }
4982
+ } else if (event.kind === "removed" && event.nodeKind === "mount") {
4983
+ const removedPrefix = `${prefix}${event.name}::`;
4984
+ for (const [trackedGraph, trackedEntry] of Array.from(subs.entries())) {
4985
+ if (trackedGraph === graph) continue;
4986
+ if (trackedEntry.prefix.startsWith(removedPrefix)) {
4987
+ trackedEntry.off();
4988
+ subs.delete(trackedGraph);
4989
+ }
4990
+ }
4991
+ }
4992
+ }
4993
+ });
4994
+ placeholder.off = off;
4995
+ for (const [mountName, child] of g._mounts) {
4996
+ if (child instanceof Graph) {
4997
+ const childPrefix = `${prefix}${mountName}::`;
4998
+ wire(child, childPrefix);
4999
+ }
5000
+ }
5001
+ };
5002
+ wire(graph, "");
5003
+ return () => {
5004
+ for (const entry of subs.values()) entry.off();
5005
+ subs.clear();
5006
+ };
5007
+ }
4617
5008
  // Annotate the CommonJS export names for ESM import in node:
4618
5009
  0 && (module.exports = {
4619
5010
  ENVELOPE_VERSION,
@@ -4622,15 +5013,18 @@ function reachable(described, from, direction, options = {}) {
4622
5013
  JsonCodec,
4623
5014
  SIZEOF_OVERHEAD,
4624
5015
  SIZEOF_SYMBOL,
5016
+ SNAPSHOT_VERSION,
4625
5017
  createDagCborCodec,
4626
5018
  createDagCborZstdCodec,
4627
5019
  decodeEnvelope,
4628
5020
  diffForWAL,
4629
5021
  encodeEnvelope,
5022
+ explainPath,
4630
5023
  graphProfile,
4631
5024
  reachable,
4632
5025
  registerBuiltinCodecs,
4633
5026
  replayWAL,
4634
- sizeof
5027
+ sizeof,
5028
+ watchTopologyTree
4635
5029
  });
4636
5030
  //# sourceMappingURL=index.cjs.map