@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
@@ -0,0 +1,5 @@
1
+ import '../node-BmerH3kS.cjs';
2
+ import '../graph-DNCrvZSn.cjs';
3
+ export { C as CollectionEntry, b as CollectionGraph, c as CollectionOptions, d as CollectionPolicy, H as HnswAdapter, e as KnowledgeEdge, K as KnowledgeGraphGraph, L as LightCollectionBundle, f as LightCollectionEntry, g as LightCollectionOptions, R as RankedCollectionEntry, h as VectorBackend, V as VectorIndexBundle, i as VectorIndexOptions, j as VectorRecord, a as VectorSearchResult, k as collection, l as decay, n as knowledgeGraph, o as lightCollection, v as vectorIndex } from '../memory-C6Z2tGpC.cjs';
4
+ import '../meta-BxCA7rcr.cjs';
5
+ import '../storage-DdWlZo6U.cjs';
@@ -0,0 +1,5 @@
1
+ import '../node-BmerH3kS.js';
2
+ import '../graph-CCwGKLCm.js';
3
+ export { C as CollectionEntry, b as CollectionGraph, c as CollectionOptions, d as CollectionPolicy, H as HnswAdapter, e as KnowledgeEdge, K as KnowledgeGraphGraph, L as LightCollectionBundle, f as LightCollectionEntry, g as LightCollectionOptions, R as RankedCollectionEntry, h as VectorBackend, V as VectorIndexBundle, i as VectorIndexOptions, j as VectorRecord, a as VectorSearchResult, k as collection, l as decay, n as knowledgeGraph, o as lightCollection, v as vectorIndex } from '../memory-li6FL5RM.js';
4
+ import '../meta-CbznRPYJ.js';
5
+ import '../storage-CMjUUuxn.js';
@@ -0,0 +1,20 @@
1
+ import {
2
+ collection,
3
+ decay,
4
+ knowledgeGraph,
5
+ lightCollection,
6
+ vectorIndex
7
+ } from "../chunk-AT5LKYNL.js";
8
+ import "../chunk-PF7GRZMW.js";
9
+ import "../chunk-VYPWMZ6H.js";
10
+ import "../chunk-7TAQJHQV.js";
11
+ import "../chunk-PHOUUNK7.js";
12
+ import "../chunk-SX52TAR4.js";
13
+ export {
14
+ collection,
15
+ decay,
16
+ knowledgeGraph,
17
+ lightCollection,
18
+ vectorIndex
19
+ };
20
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -238,7 +238,7 @@ var CanvasMeasureAdapter = class {
238
238
  this.currentFont = font;
239
239
  }
240
240
  let width = ctx.measureText(text).width;
241
- if (this.emojiCorrection !== 1 && /\p{Emoji_Presentation}/u.test(text)) {
241
+ if (this.emojiCorrection !== 1 && new RegExp("\\p{Emoji_Presentation}", "u").test(text)) {
242
242
  width *= this.emojiCorrection;
243
243
  }
244
244
  return { width };
@@ -1165,6 +1165,12 @@ var NodeImpl = class _NodeImpl {
1165
1165
  _autoError;
1166
1166
  _pausable;
1167
1167
  _guard;
1168
+ /**
1169
+ * @internal Additional guards stacked at runtime via {@link NodeImpl._pushGuard}
1170
+ * (e.g. by `policyEnforcer({ mode: "enforce" })`, roadmap §9.2). Effective
1171
+ * write/signal/observe checks AND the original `_guard` with every entry here.
1172
+ */
1173
+ _extraGuards;
1168
1174
  _hashFn;
1169
1175
  _versioning;
1170
1176
  /**
@@ -1338,18 +1344,61 @@ var NodeImpl = class _NodeImpl {
1338
1344
  if (this._inspectorHooks?.size === 0) this._inspectorHooks = void 0;
1339
1345
  };
1340
1346
  }
1347
+ /**
1348
+ * @internal Push an additional guard onto this node. Effective enforcement
1349
+ * is the AND of `_guard` and every guard pushed via this hook — any one
1350
+ * rejecting throws {@link GuardDenied}. Returns a disposer that removes
1351
+ * the pushed guard. Multiple guards may be stacked simultaneously.
1352
+ *
1353
+ * Used by `policyEnforcer({ mode: "enforce" })` (roadmap §9.2) to overlay
1354
+ * runtime constraint enforcement onto an existing graph without rebuilding
1355
+ * its nodes. Pre-1.0 internal API; not part of the public surface.
1356
+ *
1357
+ * **Identity semantics:** guards are tracked in a `Set`, so pushing the
1358
+ * same `NodeGuard` reference twice is a single registration. Wrap each
1359
+ * push in a unique closure if independent stacking is needed.
1360
+ *
1361
+ * **Iteration order:** insertion-ordered (`Set` semantics). Determinism
1362
+ * follows from single-threaded JS execution; nested re-entry from inside
1363
+ * a guard body (push/pop while iterating) is undefined-but-survivable.
1364
+ */
1365
+ _pushGuard(guard) {
1366
+ if (this._extraGuards == null) this._extraGuards = /* @__PURE__ */ new Set();
1367
+ this._extraGuards.add(guard);
1368
+ return () => {
1369
+ this._extraGuards?.delete(guard);
1370
+ if (this._extraGuards?.size === 0) this._extraGuards = void 0;
1371
+ };
1372
+ }
1341
1373
  allowsObserve(actor) {
1342
- if (this._guard == null) return true;
1343
- return this._guard(normalizeActor(actor), "observe");
1374
+ if (this._guard == null && this._extraGuards == null) return true;
1375
+ const a = normalizeActor(actor);
1376
+ if (this._guard != null && !this._guard(a, "observe")) return false;
1377
+ if (this._extraGuards != null) {
1378
+ for (const eg of this._extraGuards) {
1379
+ if (!eg(a, "observe")) return false;
1380
+ }
1381
+ }
1382
+ return true;
1344
1383
  }
1345
1384
  // --- Guard helper ---
1346
1385
  _checkGuard(options) {
1347
- if (options?.internal || this._guard == null) return;
1386
+ if (options?.internal) return;
1387
+ const hasGuard = this._guard != null || this._extraGuards != null;
1388
+ const hasActor = options?.actor != null;
1389
+ if (!hasGuard && !hasActor) return;
1348
1390
  const actor = normalizeActor(options?.actor);
1349
1391
  const action = options?.delivery === "signal" ? "signal" : "write";
1350
- if (!this._guard(actor, action)) {
1392
+ if (this._guard != null && !this._guard(actor, action)) {
1351
1393
  throw new GuardDenied({ actor, action, nodeName: this.name });
1352
1394
  }
1395
+ if (this._extraGuards != null) {
1396
+ for (const eg of this._extraGuards) {
1397
+ if (!eg(actor, action)) {
1398
+ throw new GuardDenied({ actor, action, nodeName: this.name });
1399
+ }
1400
+ }
1401
+ }
1353
1402
  this._lastMutation = { actor, timestamp_ns: wallClockNs() };
1354
1403
  }
1355
1404
  // --- Public transport ---
@@ -2281,6 +2330,10 @@ function sentinelGuard(batchData, ctx, allowPartial) {
2281
2330
  function state(initial, opts) {
2282
2331
  return node([], { ...opts, initial });
2283
2332
  }
2333
+ function producer(fn, opts) {
2334
+ const wrapped = (_data, actions, ctx) => fn(actions, ctx) ?? void 0;
2335
+ return node(wrapped, { describeKind: "producer", ...opts });
2336
+ }
2284
2337
  function derived(deps, fn, opts) {
2285
2338
  const allowPartial = opts?.partial ?? false;
2286
2339
  const wrapped = (batchData, actions, ctx) => {
@@ -2483,6 +2536,200 @@ var RingBuffer = class {
2483
2536
  }
2484
2537
  };
2485
2538
 
2539
+ // src/graph/explain.ts
2540
+ function explainPath(described, from, to, opts = {}) {
2541
+ const fromExists = from in described.nodes;
2542
+ const toExists = to in described.nodes;
2543
+ if (!fromExists) return makeFailure(from, to, "no-such-from");
2544
+ if (!toExists) return makeFailure(from, to, "no-such-to");
2545
+ const maxDepth = opts.maxDepth;
2546
+ if (maxDepth != null && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
2547
+ throw new Error(`explainPath: maxDepth must be an integer >= 0`);
2548
+ }
2549
+ if (from === to) {
2550
+ if (opts.findCycle === true) {
2551
+ const cycle = findShortestCycle(described, from, opts);
2552
+ if (cycle != null) return cycle;
2553
+ }
2554
+ const step = buildStep(from, described.nodes[from], 0, opts);
2555
+ return makeSuccess(from, to, [step]);
2556
+ }
2557
+ if (maxDepth === 0) return makeFailure(from, to, "no-path");
2558
+ const result = bfsShortestPath(described, from, to, maxDepth);
2559
+ if (!result.found) {
2560
+ return makeFailure(from, to, result.truncated ? "max-depth-exceeded" : "no-path");
2561
+ }
2562
+ return makeSuccess(from, to, materializeSteps(described, result.pathOrder, opts));
2563
+ }
2564
+ function bfsShortestPath(described, from, to, maxDepth) {
2565
+ const pred = /* @__PURE__ */ new Map();
2566
+ const queue = [{ path: to, depth: 0 }];
2567
+ const visited = /* @__PURE__ */ new Set([to]);
2568
+ let head = 0;
2569
+ let truncated = false;
2570
+ while (head < queue.length) {
2571
+ const cur = queue[head++];
2572
+ if (cur.path === from) break;
2573
+ if (maxDepth != null && cur.depth >= maxDepth) {
2574
+ const node3 = described.nodes[cur.path];
2575
+ if (node3?.deps && node3.deps.length > 0) truncated = true;
2576
+ continue;
2577
+ }
2578
+ const node2 = described.nodes[cur.path];
2579
+ if (node2 == null) continue;
2580
+ const deps = node2.deps ?? [];
2581
+ const slots = /* @__PURE__ */ new Map();
2582
+ for (let i = 0; i < deps.length; i++) {
2583
+ const dep = deps[i];
2584
+ if (!dep) continue;
2585
+ let arr = slots.get(dep);
2586
+ if (arr == null) {
2587
+ arr = [];
2588
+ slots.set(dep, arr);
2589
+ }
2590
+ arr.push(i);
2591
+ }
2592
+ for (const [dep, indices] of slots) {
2593
+ if (visited.has(dep)) continue;
2594
+ visited.add(dep);
2595
+ pred.set(dep, { from: cur.path, depIndices: indices });
2596
+ queue.push({ path: dep, depth: cur.depth + 1 });
2597
+ }
2598
+ }
2599
+ if (!pred.has(from)) {
2600
+ return { found: false, pathOrder: [], truncated };
2601
+ }
2602
+ const pathOrder = [{ path: from }];
2603
+ let cursor = from;
2604
+ while (cursor !== to) {
2605
+ const p = pred.get(cursor);
2606
+ if (p == null) return { found: false, pathOrder: [], truncated: false };
2607
+ pathOrder[pathOrder.length - 1].depIndices = p.depIndices;
2608
+ pathOrder.push({ path: p.from });
2609
+ cursor = p.from;
2610
+ }
2611
+ return { found: true, pathOrder, truncated: false };
2612
+ }
2613
+ function findShortestCycle(described, start, opts) {
2614
+ const startNode = described.nodes[start];
2615
+ if (startNode == null) return null;
2616
+ const startDeps = startNode.deps ?? [];
2617
+ const selfSlots = [];
2618
+ for (let i = 0; i < startDeps.length; i++) if (startDeps[i] === start) selfSlots.push(i);
2619
+ if (selfSlots.length > 0) {
2620
+ const step0 = buildStep(start, startNode, 0, opts);
2621
+ step0.dep_index = selfSlots[0];
2622
+ const step1 = buildStep(start, startNode, 1, opts);
2623
+ return makeSuccess(start, start, [step0, step1]);
2624
+ }
2625
+ let best = null;
2626
+ for (let i = 0; i < startDeps.length; i++) {
2627
+ const dep = startDeps[i];
2628
+ if (!dep || dep === start) continue;
2629
+ const sub = bfsShortestPath(described, dep, start, opts.maxDepth);
2630
+ if (!sub.found) continue;
2631
+ if (best == null || sub.pathOrder.length < best.pathOrder.length) {
2632
+ best = sub;
2633
+ best = {
2634
+ found: true,
2635
+ pathOrder: [{ path: start, depIndices: [i] }, ...sub.pathOrder],
2636
+ truncated: false
2637
+ };
2638
+ }
2639
+ }
2640
+ if (best == null) return null;
2641
+ return makeSuccess(start, start, materializeSteps(described, best.pathOrder, opts));
2642
+ }
2643
+ function materializeSteps(described, pathOrder, opts) {
2644
+ return pathOrder.map((entry, i) => {
2645
+ const node2 = described.nodes[entry.path];
2646
+ const step = buildStep(entry.path, node2, i, opts);
2647
+ if (entry.depIndices != null && entry.depIndices.length > 0) {
2648
+ step.dep_index = entry.depIndices[0];
2649
+ if (entry.depIndices.length > 1) step.dep_indices = [...entry.depIndices];
2650
+ }
2651
+ return step;
2652
+ });
2653
+ }
2654
+ function buildStep(path, node2, hop, opts) {
2655
+ const step = {
2656
+ path,
2657
+ type: node2.type,
2658
+ hop
2659
+ };
2660
+ if (node2.status !== void 0) step.status = node2.status;
2661
+ if ("value" in node2) step.value = node2.value;
2662
+ if (node2.v != null) step.v = node2.v;
2663
+ const annotation = opts.annotations?.get(path) ?? node2.reason;
2664
+ if (annotation != null) step.reason = annotation;
2665
+ const lastMutation = opts.lastMutations?.get(path) ?? node2.lastMutation;
2666
+ if (lastMutation != null) step.lastMutation = lastMutation;
2667
+ return step;
2668
+ }
2669
+ function makeSuccess(from, to, steps) {
2670
+ return finalize(from, to, true, "ok", steps);
2671
+ }
2672
+ function makeFailure(from, to, reason) {
2673
+ return finalize(from, to, false, reason, []);
2674
+ }
2675
+ function finalize(from, to, found, reason, steps) {
2676
+ const text = renderChain(from, to, found, reason, steps);
2677
+ return {
2678
+ from,
2679
+ to,
2680
+ found,
2681
+ reason,
2682
+ steps,
2683
+ text,
2684
+ toJSON() {
2685
+ return { from, to, found, reason, steps };
2686
+ }
2687
+ };
2688
+ }
2689
+ function renderChain(from, to, found, reason, steps) {
2690
+ if (!found) {
2691
+ switch (reason) {
2692
+ case "no-such-from":
2693
+ return `explainPath: no node named "${from}"`;
2694
+ case "no-such-to":
2695
+ return `explainPath: no node named "${to}"`;
2696
+ case "max-depth-exceeded":
2697
+ return `explainPath: no path from "${from}" to "${to}" within maxDepth`;
2698
+ default:
2699
+ return `explainPath: no path from "${from}" to "${to}"`;
2700
+ }
2701
+ }
2702
+ const lines = [`Causal path: ${from} \u2192 ${to} (${steps.length} step(s))`];
2703
+ for (const step of steps) {
2704
+ const arrow = step.hop === 0 ? "\xB7" : "\u2193";
2705
+ const head = ` ${arrow} ${step.path} (${step.type}${step.status ? `/${step.status}` : ""})`;
2706
+ lines.push(head);
2707
+ if ("value" in step) {
2708
+ lines.push(` value: ${formatValue(step.value)}`);
2709
+ }
2710
+ if (step.reason != null) {
2711
+ lines.push(` reason: ${step.reason}`);
2712
+ }
2713
+ if (step.lastMutation != null) {
2714
+ const a = step.lastMutation.actor;
2715
+ lines.push(` actor: ${a.type}${a.id ? `:${a.id}` : ""}`);
2716
+ }
2717
+ }
2718
+ return lines.join("\n");
2719
+ }
2720
+ function formatValue(v) {
2721
+ if (v === void 0) return "<sentinel>";
2722
+ if (v === null) return "null";
2723
+ if (typeof v === "string") return JSON.stringify(v);
2724
+ if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
2725
+ try {
2726
+ const s = JSON.stringify(v);
2727
+ return s.length > 80 ? `${s.slice(0, 77)}...` : s;
2728
+ } catch {
2729
+ return String(v);
2730
+ }
2731
+ }
2732
+
2486
2733
  // src/extra/utils/sizeof.ts
2487
2734
  var OVERHEAD = {
2488
2735
  object: 56,
@@ -3116,6 +3363,20 @@ var Graph = class _Graph {
3116
3363
  _parent = void 0;
3117
3364
  _storageDisposers = /* @__PURE__ */ new Set();
3118
3365
  _disposers = /* @__PURE__ */ new Set();
3366
+ /**
3367
+ * @internal Lazy `TopologyEvent` producer. Created on first `.topology`
3368
+ * access. Zero cost until something subscribes — producer fn only runs when
3369
+ * the first sink attaches, registering one handler into
3370
+ * {@link Graph._topologyEmitters}.
3371
+ */
3372
+ _topology;
3373
+ /**
3374
+ * @internal Active emit handlers for the topology producer. Each entry is
3375
+ * the closure registered by the producer fn on activation; cleared on
3376
+ * deactivation. `_emitTopology` broadcasts through every entry (there is at
3377
+ * most one per activation cycle of the producer).
3378
+ */
3379
+ _topologyEmitters = /* @__PURE__ */ new Set();
3119
3380
  /**
3120
3381
  * @param name - Non-empty graph id (must not contain `::` and must not
3121
3382
  * equal the reserved meta segment `__meta__`).
@@ -3155,6 +3416,55 @@ var Graph = class _Graph {
3155
3416
  return out;
3156
3417
  }
3157
3418
  // ——————————————————————————————————————————————————————————————
3419
+ // Topology companion (structural-change event stream)
3420
+ // ——————————————————————————————————————————————————————————————
3421
+ /**
3422
+ * Reactive stream of structural changes to this graph's own registry
3423
+ * (add / mount / remove). Value mutations live on `observe()`; this
3424
+ * companion only fires when the topology shape changes.
3425
+ *
3426
+ * Lazy: the underlying node is created on first access and activates when
3427
+ * something subscribes. No emission replay — late subscribers do not
3428
+ * receive historical events and should snapshot via {@link Graph.describe}
3429
+ * before listening for incremental changes. Events that fire while the
3430
+ * producer has zero subscribers are dropped (no retention).
3431
+ *
3432
+ * Own-graph only: a parent's `topology` does NOT emit for structural
3433
+ * changes inside a mounted child. Transitive consumers subscribe to each
3434
+ * child's topology separately (recurse through `topology`'s own "added"
3435
+ * events with `nodeKind: "mount"` to discover new children).
3436
+ *
3437
+ * See {@link TopologyEvent} for payload shape.
3438
+ *
3439
+ * @category observability
3440
+ */
3441
+ get topology() {
3442
+ if (this._topology == null) {
3443
+ this._topology = producer(
3444
+ (actions) => {
3445
+ const handler = (event) => {
3446
+ actions.emit(event);
3447
+ };
3448
+ this._topologyEmitters.add(handler);
3449
+ return () => {
3450
+ this._topologyEmitters.delete(handler);
3451
+ };
3452
+ },
3453
+ { name: `${this.name}_topology` }
3454
+ );
3455
+ }
3456
+ return this._topology;
3457
+ }
3458
+ /**
3459
+ * @internal Fire a {@link TopologyEvent} to every active subscriber of
3460
+ * `this.topology`. No-op when the topology node has never been accessed or
3461
+ * currently has no sinks — zero cost for graphs nobody observes.
3462
+ */
3463
+ _emitTopology(event) {
3464
+ if (this._topology == null || this._topologyEmitters.size === 0) return;
3465
+ for (const h of this._topologyEmitters) h(event);
3466
+ }
3467
+ // ——————————————————————————————————————————————————————————————
3158
3468
  // Node registry
3159
3469
  // ——————————————————————————————————————————————————————————————
3160
3470
  /**
@@ -3184,6 +3494,7 @@ var Graph = class _Graph {
3184
3494
  }
3185
3495
  this._nodes.set(name, node2);
3186
3496
  this._nodeToName.set(node2, name);
3497
+ this._emitTopology({ kind: "added", name, nodeKind: "node" });
3187
3498
  return node2;
3188
3499
  }
3189
3500
  /**
@@ -3224,22 +3535,23 @@ var Graph = class _Graph {
3224
3535
  assertRegisterableName(name, this.name, "remove");
3225
3536
  const child = this._mounts.get(name);
3226
3537
  if (child) {
3227
- const audit = { kind: "mount", nodes: [], mounts: [] };
3538
+ const audit2 = { kind: "mount", nodes: [], mounts: [] };
3228
3539
  const targets = [];
3229
3540
  child._collectObserveTargets("", targets);
3230
3541
  for (const [p, n] of targets) {
3231
3542
  if (!p.includes(`${PATH_SEP}${GRAPH_META_SEGMENT}${PATH_SEP}`)) {
3232
- audit.nodes.push(p);
3543
+ audit2.nodes.push(p);
3233
3544
  }
3234
3545
  void n;
3235
3546
  }
3236
- audit.nodes.sort();
3237
- audit.mounts.push(name);
3238
- audit.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
3547
+ audit2.nodes.sort();
3548
+ audit2.mounts.push(name);
3549
+ audit2.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
3239
3550
  this._mounts.delete(name);
3240
3551
  child._parent = void 0;
3241
3552
  teardownMountedGraph(child);
3242
- return audit;
3553
+ this._emitTopology({ kind: "removed", name, nodeKind: "mount", audit: audit2 });
3554
+ return audit2;
3243
3555
  }
3244
3556
  const node2 = this._nodes.get(name);
3245
3557
  if (!node2) {
@@ -3248,7 +3560,9 @@ var Graph = class _Graph {
3248
3560
  this._nodes.delete(name);
3249
3561
  this._nodeToName.delete(node2);
3250
3562
  node2.down([[TEARDOWN]], { internal: true });
3251
- return { kind: "node", nodes: [name], mounts: [] };
3563
+ const audit = { kind: "node", nodes: [name], mounts: [] };
3564
+ this._emitTopology({ kind: "removed", name, nodeKind: "node", audit });
3565
+ return audit;
3252
3566
  }
3253
3567
  /**
3254
3568
  * Bulk remove — invokes {@link Graph.remove} for every local name matching
@@ -3477,6 +3791,7 @@ var Graph = class _Graph {
3477
3791
  }
3478
3792
  this._mounts.set(name, child);
3479
3793
  child._parent = this;
3794
+ this._emitTopology({ kind: "added", name, nodeKind: "mount" });
3480
3795
  return child;
3481
3796
  }
3482
3797
  /**
@@ -3767,6 +4082,33 @@ var Graph = class _Graph {
3767
4082
  }
3768
4083
  return reachable(this.describe(), from, direction, opts);
3769
4084
  }
4085
+ /**
4086
+ * Causal walkback: shortest dep-chain from `from` to `to`, enriched with
4087
+ * each node's value, status, last-mutation actor, and reasoning annotation
4088
+ * from {@link Graph.trace}. Wraps {@link explainPath} (roadmap §9.2).
4089
+ *
4090
+ * @param from - Upstream node (the cause).
4091
+ * @param to - Downstream node (the effect).
4092
+ * @param opts - Optional `maxDepth` and `findCycle`. When `findCycle:true`
4093
+ * and `from === to`, returns the shortest cycle through other nodes
4094
+ * (useful for diagnosing feedback loops, COMPOSITION-GUIDE §7).
4095
+ * Annotations and lastMutations are collected automatically from the
4096
+ * live graph.
4097
+ */
4098
+ explain(from, to, opts) {
4099
+ const described = this.describe({ detail: "full" });
4100
+ const annotations = new Map(this._annotations);
4101
+ const lastMutations = /* @__PURE__ */ new Map();
4102
+ for (const [path, n] of Object.entries(described.nodes)) {
4103
+ if (n.lastMutation != null) lastMutations.set(path, n.lastMutation);
4104
+ }
4105
+ return explainPath(described, from, to, {
4106
+ ...opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {},
4107
+ ...opts?.findCycle === true ? { findCycle: true } : {},
4108
+ annotations,
4109
+ lastMutations
4110
+ });
4111
+ }
3770
4112
  /**
3771
4113
  * @internal Collect all qualified paths in this graph tree matching a
3772
4114
  * glob pattern. Used by scoped autoCheckpoint subscription.
@@ -4445,7 +4787,7 @@ var Graph = class _Graph {
4445
4787
  return;
4446
4788
  }
4447
4789
  const nextSeq = s.seq + 1;
4448
- const timestamp_ns = monotonicNs();
4790
+ const timestamp_ns = wallClockNs();
4449
4791
  const isFirst = s.lastSnapshot == null;
4450
4792
  const shouldCompact = isFirst || nextSeq % s.compactEvery === 0;
4451
4793
  const record = shouldCompact ? {