@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 @@
1
+ {"version":3,"sources":["../src/patterns/audit.ts"],"sourcesContent":["/**\n * Audit, policy enforcement, and compliance export (roadmap §9.2).\n *\n * Three composed factories that wrap any {@link Graph} with the harness\n * accountability layer:\n *\n * - {@link auditTrail} — reactive mutation log with by-node/by-actor/by-time\n * queries.\n * - {@link policyEnforcer} — reactive ABAC enforcement; in `\"audit\"` mode\n * records would-be denials, in `\"enforce\"` mode pushes guards onto target\n * nodes so subsequent writes throw {@link GuardDenied}.\n * - {@link complianceSnapshot} — point-in-time export of graph state +\n * audit trail + policies for regulatory archival.\n *\n * @module\n */\nimport type { Actor } from \"../core/actor.js\";\nimport { monotonicNs, wallClockNs } from \"../core/clock.js\";\nimport type { GuardAction, NodeGuard, PolicyRuleData } from \"../core/guard.js\";\nimport { policyFromRules } from \"../core/guard.js\";\nimport { DATA } from \"../core/messages.js\";\nimport type { Node } from \"../core/node.js\";\nimport { NodeImpl } from \"../core/node.js\";\nimport { derived, state } from \"../core/sugar.js\";\nimport { defaultHash } from \"../core/versioning.js\";\nimport { reactiveLog } from \"../extra/reactive-log.js\";\nimport {\n\ttype CausalChain,\n\ttype CausalStep,\n\tGraph,\n\ttype GraphOptions,\n\ttype GraphPersistSnapshot,\n\ttype TopologyEvent,\n\twatchTopologyTree,\n} from \"../graph/index.js\";\nimport { domainMeta, keepalive } from \"./_internal.js\";\nimport { TopicGraph } from \"./messaging.js\";\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\n/** A single recorded mutation/event in an {@link AuditTrailGraph}. */\nexport interface AuditEntry {\n\tseq: number;\n\ttimestamp_ns: number;\n\twall_clock_ns: number;\n\tpath: string;\n\ttype:\n\t\t| \"data\"\n\t\t| \"dirty\"\n\t\t| \"resolved\"\n\t\t| \"invalidate\"\n\t\t| \"pause\"\n\t\t| \"resume\"\n\t\t| \"complete\"\n\t\t| \"error\"\n\t\t| \"teardown\";\n\tactor?: Actor;\n\tvalue?: unknown;\n\terror?: unknown;\n\treason?: string;\n}\n\nfunction auditMeta(kind: string, extra?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"audit\", kind, extra);\n}\n\n// ---------------------------------------------------------------------------\n// auditTrail\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_INCLUDE_TYPES: ReadonlySet<AuditEntry[\"type\"]> = new Set([\n\t\"data\",\n\t\"error\",\n\t\"complete\",\n\t\"teardown\",\n]);\n\n/** Options for {@link auditTrail}. */\nexport interface AuditTrailOptions {\n\tname?: string;\n\tgraph?: GraphOptions;\n\t/** Ring-buffer cap for the underlying `reactiveLog`. Default: unbounded. */\n\tmaxSize?: number;\n\t/**\n\t * Which event types to record. Default: `[\"data\", \"error\", \"complete\",\n\t * \"teardown\"]` — the user-meaningful set. Opt in to mid-wave protocol\n\t * events (`\"dirty\"`, `\"resolved\"`, `\"invalidate\"`, `\"pause\"`, `\"resume\"`)\n\t * by listing them explicitly. Note: those tier-1/tier-2 events do not\n\t * carry an `actor` (no `lastMutation` populated) — record them only for\n\t * protocol-level diagnostics.\n\t */\n\tincludeTypes?: readonly AuditEntry[\"type\"][];\n\t/** Per-event filter; return false to skip. */\n\tfilter?: (entry: AuditEntry) => boolean;\n}\n\n/**\n * Mounted audit log — `entries` exposes the reactive `AuditEntry[]`; query\n * helpers are sync convenience wrappers over the cached snapshot.\n */\nexport class AuditTrailGraph extends Graph {\n\treadonly entries: Node<readonly AuditEntry[]>;\n\treadonly count: Node<number>;\n\tprivate readonly _log;\n\tprivate readonly _target: Graph;\n\n\tconstructor(target: Graph, opts: AuditTrailOptions) {\n\t\tsuper(opts.name ?? `${target.name}_audit`, opts.graph);\n\t\tthis._target = target;\n\t\tthis._log = reactiveLog<AuditEntry>([], {\n\t\t\tname: \"entries\",\n\t\t\t...(opts.maxSize != null ? { maxSize: opts.maxSize } : {}),\n\t\t});\n\t\tthis.entries = this._log.entries;\n\t\tthis.add(\"entries\", this.entries);\n\n\t\tthis.count = derived<number>(\n\t\t\t[this.entries],\n\t\t\t([snapshot]) => (snapshot as readonly AuditEntry[]).length,\n\t\t\t{ name: \"count\", describeKind: \"derived\", meta: auditMeta(\"count\") },\n\t\t);\n\t\tthis.add(\"count\", this.count);\n\t\tthis.addDisposer(keepalive(this.count));\n\n\t\tconst includeTypes =\n\t\t\topts.includeTypes != null ? new Set(opts.includeTypes) : DEFAULT_INCLUDE_TYPES;\n\t\tconst filter = opts.filter;\n\n\t\t// Monotonic per-trail. Wraps around at Number.MAX_SAFE_INTEGER (~9e15);\n\t\t// at 100k events/sec that's ~3000 years — not a practical concern.\n\t\tlet seq = 0;\n\t\tconst handle = target.observe({ timeline: true, structured: true });\n\t\tconst offEvent = handle.onEvent((event) => {\n\t\t\t// `event.type` includes \"derived\" (causal-trace recompute marker) which\n\t\t\t// isn't a recordable mutation — skip it. Cast through narrowed type\n\t\t\t// after the discriminator check.\n\t\t\tif (event.type === \"derived\") return;\n\t\t\tconst type = event.type as AuditEntry[\"type\"];\n\t\t\tif (!includeTypes.has(type)) return;\n\t\t\tconst path = event.path ?? \"\";\n\t\t\tconst entry: AuditEntry = {\n\t\t\t\tseq: seq++,\n\t\t\t\ttimestamp_ns: event.timestamp_ns ?? monotonicNs(),\n\t\t\t\twall_clock_ns: wallClockNs(),\n\t\t\t\tpath,\n\t\t\t\ttype,\n\t\t\t};\n\t\t\t// Attribution + value enrichment.\n\t\t\tconst node = path ? safeNode(target, path) : undefined;\n\t\t\tconst lastMutation = node?.lastMutation;\n\t\t\tif (lastMutation != null) entry.actor = lastMutation.actor;\n\t\t\tif (type === \"data\") entry.value = (event as { data: unknown }).data;\n\t\t\tif (type === \"error\") entry.error = (event as { data: unknown }).data;\n\t\t\tconst reason = path ? safeAnnotation(target, path) : undefined;\n\t\t\tif (reason != null) entry.reason = reason;\n\t\t\tif (filter != null && !filter(entry)) return;\n\t\t\tthis._log.append(entry);\n\t\t});\n\n\t\tthis.addDisposer(() => {\n\t\t\toffEvent();\n\t\t\thandle.dispose();\n\t\t});\n\t\tthis.addDisposer(() => this._log.disposeAllViews());\n\t}\n\n\t/** All entries currently in the ring (snapshot). */\n\tall(): readonly AuditEntry[] {\n\t\treturn (this.entries.cache as readonly AuditEntry[] | undefined) ?? [];\n\t}\n\n\t/** Entries matching `path`. Order preserved. */\n\tbyNode(path: string): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => e.path === path);\n\t}\n\n\t/** Entries whose `actor.id` matches. Use `byActorType` for type filtering. */\n\tbyActor(actorId: string): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => e.actor?.id === actorId);\n\t}\n\n\t/** Entries whose `actor.type` matches (e.g. `\"llm\"`, `\"human\"`). */\n\tbyActorType(type: string): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => e.actor?.type === type);\n\t}\n\n\t/**\n\t * Entries with `timestamp_ns` in `[start_ns, end_ns)` (end exclusive).\n\t * Omit `end_ns` to query open-ended.\n\t */\n\tbyTimeRange(start_ns: number, end_ns?: number): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => {\n\t\t\tif (e.timestamp_ns < start_ns) return false;\n\t\t\tif (end_ns != null && e.timestamp_ns >= end_ns) return false;\n\t\t\treturn true;\n\t\t});\n\t}\n\n\t/** Reference to the audited graph (escape hatch for tooling). */\n\tget target(): Graph {\n\t\treturn this._target;\n\t}\n}\n\n/**\n * Wraps any {@link Graph} with a reactive audit trail recording every event\n * matching `includeTypes` (default: data + error + complete + teardown).\n *\n * Each entry carries `seq`, `timestamp_ns` (monotonic), `wall_clock_ns`,\n * `path`, `type`, and — when available — `actor`, `value`, `error`, and the\n * `graph.trace()` reasoning annotation for the path.\n *\n * The returned graph mounts an `entries` node + `count` derived. Query\n * helpers (`byNode`, `byActor`, `byTimeRange`) operate on the cached\n * snapshot synchronously.\n */\nexport function auditTrail(target: Graph, opts: AuditTrailOptions = {}): AuditTrailGraph {\n\treturn new AuditTrailGraph(target, opts);\n}\n\n// ---------------------------------------------------------------------------\n// policyEnforcer\n// ---------------------------------------------------------------------------\n\n/** A single policy denial recorded by {@link PolicyEnforcerGraph}. */\nexport interface PolicyViolation {\n\ttimestamp_ns: number;\n\twall_clock_ns: number;\n\tpath: string;\n\tactor: Actor;\n\taction: GuardAction;\n\tmode: \"audit\" | \"enforce\";\n\t/** `\"observed\"` (audit mode after-the-fact) or `\"blocked\"` (enforce mode pre-write). */\n\tresult: \"observed\" | \"blocked\";\n}\n\n/** Options for {@link policyEnforcer}. */\nexport interface PolicyEnforcerOptions {\n\tname?: string;\n\tgraph?: GraphOptions;\n\t/**\n\t * `\"audit\"` (default) — observe events and record would-be denials;\n\t * does not block writes. Audit mode requires `lastMutation` attribution\n\t * on the audited node — anonymous/internal writes (no `actor` passed,\n\t * unguarded node) are skipped silently because the policy cannot be\n\t * evaluated without an actor.\n\t *\n\t * `\"enforce\"` — push guards onto target nodes so disallowed writes\n\t * throw {@link GuardDenied}. Reverted on dispose.\n\t */\n\tmode?: \"audit\" | \"enforce\";\n\t/**\n\t * Restrict enforcement to specific node paths (qualified). When omitted,\n\t * applies to every node visible in `target.describe()` at construction\n\t * time (subgraphs are walked transitively) AND subscribes to the full\n\t * topology tree via {@link watchTopologyTree}, so nodes added to\n\t * `target` OR any transitively-mounted subgraph after construction are\n\t * guarded automatically (enforce mode only).\n\t *\n\t * **Cost:** unrestricted mode runs `describe({detail:\"minimal\"})` once\n\t * at construction (O(N) over the graph tree) plus one topology\n\t * subscription per graph instance in the mount tree. Restricted mode\n\t * skips both and disables dynamic coverage — callers providing\n\t * `paths` must re-create on subgraph changes.\n\t */\n\tpaths?: readonly string[];\n\t/** Ring-buffer cap for the violations topic. Default: 1000. */\n\tviolationsLimit?: number;\n}\n\n/**\n * Reactive ABAC enforcement layer. Policies are reactive — pass a\n * `Node<readonly PolicyRuleData[]>` to allow LLMs (or any reactive source)\n * to update them at runtime; the enforcer rebinds its internal\n * {@link NodeGuard} on every push.\n */\nexport class PolicyEnforcerGraph extends Graph {\n\treadonly policies: Node<readonly PolicyRuleData[]>;\n\treadonly violations: TopicGraph<PolicyViolation>;\n\treadonly violationCount: Node<number>;\n\tprivate readonly _target: Graph;\n\tprivate readonly _mode: \"audit\" | \"enforce\";\n\tprivate _currentGuard: NodeGuard;\n\n\tconstructor(\n\t\ttarget: Graph,\n\t\tpolicies: readonly PolicyRuleData[] | Node<readonly PolicyRuleData[]>,\n\t\topts: PolicyEnforcerOptions,\n\t) {\n\t\tsuper(opts.name ?? `${target.name}_policy`, opts.graph);\n\t\tthis._target = target;\n\t\tthis._mode = opts.mode ?? \"audit\";\n\n\t\tconst policiesNode = isNode(policies)\n\t\t\t? policies\n\t\t\t: state<readonly PolicyRuleData[]>(policies, { name: \"policies\" });\n\t\tthis.policies = policiesNode;\n\t\tthis.add(\"policies\", this.policies);\n\n\t\tthis.violations = new TopicGraph<PolicyViolation>(\"violations\", {\n\t\t\tretainedLimit: opts.violationsLimit ?? 1000,\n\t\t});\n\t\tthis.mount(\"violations\", this.violations);\n\n\t\tthis.violationCount = derived<number>(\n\t\t\t[this.violations.events],\n\t\t\t([snapshot]) => (snapshot as readonly PolicyViolation[]).length,\n\t\t\t{\n\t\t\t\tname: \"violationCount\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: auditMeta(\"policy_violation_count\"),\n\t\t\t},\n\t\t);\n\t\tthis.add(\"violationCount\", this.violationCount);\n\t\tthis.addDisposer(keepalive(this.violationCount));\n\n\t\t// Factory-time seed (COMPOSITION-GUIDE §28): cache the latest rules\n\t\t// inside a closure, refresh on each subscribe-pushed update, and read\n\t\t// closure inside the guard so policy updates take effect immediately.\n\t\tconst initialRules = (policiesNode.cache as readonly PolicyRuleData[] | undefined) ?? [];\n\t\tlet latestRules: readonly PolicyRuleData[] = initialRules;\n\t\tthis._currentGuard = policyFromRules(latestRules);\n\t\tconst offPolicies = policiesNode.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tlatestRules = (m[1] as readonly PolicyRuleData[] | undefined) ?? [];\n\t\t\t\t\tthis._currentGuard = policyFromRules(latestRules);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.addDisposer(offPolicies);\n\n\t\t// Determine which target paths to watch.\n\t\tconst paths = opts.paths != null ? [...opts.paths] : collectPaths(target);\n\n\t\tif (this._mode === \"enforce\") {\n\t\t\t// Track which paths are currently guarded so dynamic adds don't\n\t\t\t// double-wrap and removed nodes release guard handles.\n\t\t\tconst restorers = new Map<string, () => void>();\n\t\t\tconst wrapAndPush = (path: string): void => {\n\t\t\t\tif (restorers.has(path)) return;\n\t\t\t\tconst node = safeNode(target, path);\n\t\t\t\tif (!(node instanceof NodeImpl)) return;\n\t\t\t\tconst pathGuard: NodeGuard = (actor, action) => {\n\t\t\t\t\tconst ok = this._currentGuard(actor, action);\n\t\t\t\t\tif (!ok) {\n\t\t\t\t\t\tthis._publishViolation(actor, action, path, \"blocked\");\n\t\t\t\t\t}\n\t\t\t\t\treturn ok;\n\t\t\t\t};\n\t\t\t\trestorers.set(path, node._pushGuard(pathGuard));\n\t\t\t};\n\t\t\t// Initial sweep: guard every path present at construction.\n\t\t\tfor (const path of paths) wrapAndPush(path);\n\n\t\t\t// Dynamic coverage: when `paths` was NOT explicitly provided, follow\n\t\t\t// the full topology tree (target + every transitively-mounted\n\t\t\t// subgraph, including subgraphs mounted after construction) so late\n\t\t\t// adds at any depth get guarded. `prefix` carries the qualified\n\t\t\t// path-prefix from `target` to the emitter graph.\n\t\t\tif (opts.paths == null) {\n\t\t\t\tconst offTopology = watchTopologyTree(target, (event, emitter, prefix) => {\n\t\t\t\t\tif (event.kind === \"added\") {\n\t\t\t\t\t\tif (event.nodeKind === \"node\") {\n\t\t\t\t\t\t\twrapAndPush(`${prefix}${event.name}`);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Mount added. Walk just the newly-mounted subgraph's local\n\t\t\t\t\t\t\t// paths (scoped describe — O(M) in the mounted subtree)\n\t\t\t\t\t\t\t// rather than re-describing the entire target tree. The\n\t\t\t\t\t\t\t// emitter is the PARENT of the new mount; resolve the child\n\t\t\t\t\t\t\t// via its `_mounts` map.\n\t\t\t\t\t\t\tconst child = emitter._mounts.get(event.name);\n\t\t\t\t\t\t\tif (!(child instanceof Graph)) return;\n\t\t\t\t\t\t\tconst mountPrefix = `${prefix}${event.name}::`;\n\t\t\t\t\t\t\tconst localPaths = collectPaths(child);\n\t\t\t\t\t\t\tfor (const localPath of localPaths) {\n\t\t\t\t\t\t\t\t// `localPath` is relative to `child`; qualify with the\n\t\t\t\t\t\t\t\t// mount prefix so guard keys stay target-rooted.\n\t\t\t\t\t\t\t\twrapAndPush(\n\t\t\t\t\t\t\t\t\tlocalPath === \"\" ? `${prefix}${event.name}` : `${mountPrefix}${localPath}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (event.kind === \"removed\") {\n\t\t\t\t\t\t// TEARDOWN already unhooks the guard; release bookkeeping so\n\t\t\t\t\t\t// re-adds under the same qualified path re-wrap cleanly.\n\t\t\t\t\t\tif (event.nodeKind === \"node\") {\n\t\t\t\t\t\t\tconst qp = `${prefix}${event.name}`;\n\t\t\t\t\t\t\tconst r = restorers.get(qp);\n\t\t\t\t\t\t\tif (r != null) {\n\t\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\t\trestorers.delete(qp);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst mountQp = `${prefix}${event.name}`;\n\t\t\t\t\t\t\tconst mountPrefix = `${mountQp}::`;\n\t\t\t\t\t\t\tfor (const [p, r] of restorers) {\n\t\t\t\t\t\t\t\tif (p === mountQp || p.startsWith(mountPrefix)) {\n\t\t\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\t\t\trestorers.delete(p);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.addDisposer(offTopology);\n\t\t\t} else {\n\t\t\t\t// Restricted mode: subscribe to target.topology (own-graph only —\n\t\t\t\t// explicit `paths` means caller owns the path set) so node removals\n\t\t\t\t// release their restorers instead of leaking until enforcer dispose.\n\t\t\t\tconst offCleanup = target.topology.subscribe((msgs) => {\n\t\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\t\tconst event = m[1] as TopologyEvent;\n\t\t\t\t\t\tif (event.kind !== \"removed\" || event.nodeKind !== \"node\") continue;\n\t\t\t\t\t\tconst r = restorers.get(event.name);\n\t\t\t\t\t\tif (r != null) {\n\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\trestorers.delete(event.name);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.addDisposer(offCleanup);\n\t\t\t}\n\t\t\tthis.addDisposer(() => {\n\t\t\t\tfor (const r of restorers.values()) r();\n\t\t\t\trestorers.clear();\n\t\t\t});\n\t\t} else {\n\t\t\t// Audit mode: observe writes, evaluate against current guard, record\n\t\t\t// violations without blocking. Use the structured observe stream so\n\t\t\t// `path` is supplied without per-node subscription bookkeeping.\n\t\t\tconst handle = target.observe({ timeline: true, structured: true });\n\t\t\tconst off = handle.onEvent((event) => {\n\t\t\t\tif (event.type !== \"data\" && event.type !== \"error\") return;\n\t\t\t\tconst path = event.path ?? \"\";\n\t\t\t\tif (!path) return;\n\t\t\t\tif (opts.paths != null && !opts.paths.includes(path)) return;\n\t\t\t\tconst node = safeNode(target, path);\n\t\t\t\tconst lastMutation = node?.lastMutation;\n\t\t\t\tif (lastMutation == null) return; // cannot attribute → no policy decision\n\t\t\t\tconst action: GuardAction = \"write\";\n\t\t\t\tif (this._currentGuard(lastMutation.actor, action)) return;\n\t\t\t\tthis._publishViolation(lastMutation.actor, action, path, \"observed\");\n\t\t\t});\n\t\t\tthis.addDisposer(() => {\n\t\t\t\toff();\n\t\t\t\thandle.dispose();\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _publishViolation(\n\t\tactor: Actor,\n\t\taction: GuardAction,\n\t\tpath: string,\n\t\tresult: \"observed\" | \"blocked\",\n\t): void {\n\t\tthis.violations.publish({\n\t\t\ttimestamp_ns: monotonicNs(),\n\t\t\twall_clock_ns: wallClockNs(),\n\t\t\tpath,\n\t\t\tactor,\n\t\t\taction,\n\t\t\tmode: this._mode,\n\t\t\tresult,\n\t\t});\n\t}\n\n\t/** Snapshot of recorded violations. */\n\tall(): readonly PolicyViolation[] {\n\t\treturn this.violations.retained();\n\t}\n\n\tget mode(): \"audit\" | \"enforce\" {\n\t\treturn this._mode;\n\t}\n\n\tget target(): Graph {\n\t\treturn this._target;\n\t}\n}\n\n/**\n * Wraps a {@link Graph} with reactive policy enforcement. Pass either a\n * static rule list or a {@link Node} of rules (LLM-updatable). Records\n * `PolicyViolation` entries to `violations` topic; in `\"enforce\"` mode also\n * pushes guards onto target nodes so disallowed writes throw.\n */\nexport function policyEnforcer(\n\ttarget: Graph,\n\tpolicies: readonly PolicyRuleData[] | Node<readonly PolicyRuleData[]>,\n\topts: PolicyEnforcerOptions = {},\n): PolicyEnforcerGraph {\n\treturn new PolicyEnforcerGraph(target, policies, opts);\n}\n\n// ---------------------------------------------------------------------------\n// reactiveExplainPath\n// ---------------------------------------------------------------------------\n\n/**\n * Reactive {@link CausalChain} that recomputes whenever the audited graph\n * changes. Returns a `Node<CausalChain>` suitable for subscription, mounting,\n * or composition (e.g. inside `graphLens.why(node)`).\n *\n * **How it stays live:** an internal `version` state is bumped by an observer\n * attached to `target.observe()`; the derived chain depends on `version`, so\n * each mutation triggers a recompute. To avoid stalling on no-op events, only\n * `data`, `error`, `complete`, and `teardown` bump the version (matching the\n * audit defaults).\n */\nexport function reactiveExplainPath(\n\ttarget: Graph,\n\tfrom: string,\n\tto: string,\n\topts?: { maxDepth?: number; name?: string; findCycle?: boolean },\n): { node: Node<CausalChain>; dispose: () => void } {\n\t// Closure-held counter (COMPOSITION-GUIDE §28 / spec §3.6 sanctioned\n\t// pattern). We do NOT read `version.cache` from inside the subscribe\n\t// callback — that would be a P3 violation under batched waves where\n\t// multiple events deliver to the same listener with stale cache.\n\tlet v = 0;\n\tconst version = state(v, { name: \"explain_version\" });\n\tconst handle = target.observe({ timeline: true, structured: true });\n\tconst off = handle.onEvent((event) => {\n\t\tconst t = event.type;\n\t\tif (t !== \"data\" && t !== \"error\" && t !== \"complete\" && t !== \"teardown\") return;\n\t\tv += 1;\n\t\tversion.emit(v);\n\t});\n\n\tconst explainOpts = {\n\t\t...(opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {}),\n\t\t...(opts?.findCycle === true ? { findCycle: true as const } : {}),\n\t};\n\tconst node = derived<CausalChain>([version], () => target.explain(from, to, explainOpts), {\n\t\tname: opts?.name ?? \"explain\",\n\t\tdescribeKind: \"derived\",\n\t\tequals: (a, b) =>\n\t\t\ta.found === b.found &&\n\t\t\ta.reason === b.reason &&\n\t\t\ta.steps.length === b.steps.length &&\n\t\t\tcausalStepsEqual(a.steps, b.steps),\n\t\tmeta: auditMeta(\"explain_path\", { from, to }),\n\t});\n\tconst stopKeepalive = keepalive(node);\n\n\treturn {\n\t\tnode,\n\t\tdispose() {\n\t\t\toff();\n\t\t\thandle.dispose();\n\t\t\tstopKeepalive();\n\t\t},\n\t};\n}\n\nfunction causalStepsEqual(a: readonly CausalStep[], b: readonly CausalStep[]): boolean {\n\tfor (let i = 0; i < a.length; i++) {\n\t\tconst x = a[i]!;\n\t\tconst y = b[i]!;\n\t\tif (x.path !== y.path) return false;\n\t\tif (x.type !== y.type) return false;\n\t\tif (x.status !== y.status) return false;\n\t\tif (x.hop !== y.hop) return false;\n\t\tif (x.dep_index !== y.dep_index) return false;\n\t\tif (x.reason !== y.reason) return false;\n\t\t// Value identity — derived snapshots reuse same refs unless changed.\n\t\tif (x.value !== y.value) return false;\n\t\t// `lastMutation` is `Readonly<{actor, timestamp_ns}>`; identity compare\n\t\t// is sufficient because `_lastMutation` is replaced on every mutation\n\t\t// (new object per write) — same identity ⇒ no actor/timestamp change.\n\t\tif (x.lastMutation !== y.lastMutation) return false;\n\t\tconst xv = x.v;\n\t\tconst yv = y.v;\n\t\tif (xv !== yv) {\n\t\t\tif (xv == null || yv == null) return false;\n\t\t\tif (xv.id !== yv.id || xv.version !== yv.version) return false;\n\t\t}\n\t}\n\treturn true;\n}\n\n// ---------------------------------------------------------------------------\n// complianceSnapshot\n// ---------------------------------------------------------------------------\n\n/** Options for {@link complianceSnapshot}. */\nexport interface ComplianceSnapshotOptions {\n\taudit?: AuditTrailGraph;\n\tpolicies?: PolicyEnforcerGraph;\n\t/** Actor recorded as the snapshot taker. */\n\tactor?: Actor;\n}\n\n/** Output of {@link complianceSnapshot}. JSON-serializable. */\nexport interface ComplianceSnapshotResult {\n\tformat_version: 1;\n\ttimestamp_ns: number;\n\twall_clock_ns: number;\n\tactor?: Actor;\n\tgraph: GraphPersistSnapshot;\n\taudit?: { count: number; entries: AuditEntry[] };\n\tpolicies?: {\n\t\tmode: \"audit\" | \"enforce\";\n\t\trules: readonly PolicyRuleData[];\n\t\tviolations: readonly PolicyViolation[];\n\t};\n\t/**\n\t * Truncated SHA-256 hex (16 chars / ~64 bits) over a canonical encoding\n\t * of every field above (excluding `fingerprint` itself). Deterministic\n\t * across runs given identical inputs. Suitable for casual tamper-evidence\n\t * and content-addressed dedup; for full cryptographic strength, hash the\n\t * canonical JSON externally with Web Crypto / Node `crypto`.\n\t */\n\tfingerprint: string;\n}\n\n/**\n * One-shot point-in-time export of a {@link Graph}'s state plus optional\n * audit + policy bundles. Returns a JSON-serializable object with a\n * deterministic truncated-SHA-256 {@link ComplianceSnapshotResult.fingerprint}\n * over the canonical payload for tamper-evidence in regulatory archival.\n *\n * **Cryptographic strength:** the fingerprint is truncated to 64 bits for\n * compact archival. Collision-resistant for casual integrity checks but NOT\n * sufficient for adversarial tamper-evidence — pair with a full SHA-256\n * (or stronger) over the canonical JSON when regulatory requirements demand\n * collision resistance.\n */\nexport function complianceSnapshot(\n\ttarget: Graph,\n\topts: ComplianceSnapshotOptions = {},\n): ComplianceSnapshotResult {\n\tconst result: Omit<ComplianceSnapshotResult, \"fingerprint\"> = {\n\t\tformat_version: 1,\n\t\ttimestamp_ns: monotonicNs(),\n\t\twall_clock_ns: wallClockNs(),\n\t\tgraph: target.snapshot() as GraphPersistSnapshot,\n\t};\n\tif (opts.actor != null) result.actor = opts.actor;\n\tif (opts.audit != null) {\n\t\tconst entries = [...opts.audit.all()];\n\t\tresult.audit = { count: entries.length, entries };\n\t}\n\tif (opts.policies != null) {\n\t\tconst rules = (opts.policies.policies.cache as readonly PolicyRuleData[] | undefined) ?? [];\n\t\tresult.policies = {\n\t\t\tmode: opts.policies.mode,\n\t\t\trules,\n\t\t\tviolations: [...opts.policies.all()],\n\t\t};\n\t}\n\tconst fingerprint = computeFingerprint(result);\n\treturn { ...result, fingerprint };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\nfunction isNode<T>(x: unknown): x is Node<T> {\n\treturn typeof x === \"object\" && x !== null && \"subscribe\" in (x as object);\n}\n\nfunction safeNode(target: Graph, path: string): Node | undefined {\n\ttry {\n\t\treturn target.node(path);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction safeAnnotation(target: Graph, path: string): string | undefined {\n\ttry {\n\t\treturn target.annotation(path);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Walks every locally-registered node path in `target`, descending through\n * mounted subgraphs. Returns qualified paths.\n */\nfunction collectPaths(target: Graph): string[] {\n\tconst described = target.describe({ detail: \"minimal\" });\n\treturn Object.keys(described.nodes);\n}\n\n/**\n * Stable canonical JSON → truncated SHA-256 hex fingerprint (16 hex chars,\n * ~64-bit). Uses the same vendored sync SHA-256 as `core/versioning.ts`\n * `defaultHash`, so cross-module fingerprints stay consistent.\n *\n * Canonicalization handles cycles (recursion-stack tracker), `undefined`,\n * `bigint`, `Map`, `Set`, `Date`, `RegExp`, and typed arrays via typed\n * markers — see {@link canonicalize}.\n *\n * **Note:** truncated to 16 hex chars (~64-bit) for compact archival. For\n * full 256-bit cryptographic strength, hash {@link complianceSnapshot} JSON\n * externally with Web Crypto / Node `crypto`.\n */\nfunction computeFingerprint(value: unknown): string {\n\t// Pre-stringify our canonical form so `defaultHash`'s\n\t// `canonicalizeForHash` (which rejects unsafe integers) only ever sees a\n\t// JSON string. Compliance payloads carry `timestamp_ns` values that\n\t// exceed `Number.MAX_SAFE_INTEGER` — JSON.stringify handles them fine,\n\t// the hash function only cares about deterministic input bytes.\n\treturn defaultHash(JSON.stringify(canonicalize(value)));\n}\n\n/**\n * Cycle-safe canonical encoding. Uses a recursion-stack `Set` (push on\n * descent, pop on return) so legitimate DAG re-references are encoded as\n * themselves; only true cycles produce a `__circular: true` marker. Typed\n * markers preserve `undefined` / `bigint` / `Map` / `Set` / `Date` / `RegExp`\n * / typed-array information that bare `JSON.stringify` would silently drop\n * or collide with strings.\n */\nfunction canonicalize(value: unknown): unknown {\n\tconst stack = new Set<object>();\n\tconst walk = (v: unknown): unknown => {\n\t\tif (v === undefined) return { __undefined: true };\n\t\tif (v === null) return null;\n\t\tconst t = typeof v;\n\t\tif (t === \"bigint\") return { __bigint: (v as bigint).toString() };\n\t\tif (t !== \"object\") return v;\n\t\tconst obj = v as object;\n\t\tif (stack.has(obj)) return { __circular: true };\n\t\tstack.add(obj);\n\t\ttry {\n\t\t\tif (Array.isArray(obj)) {\n\t\t\t\treturn (obj as unknown[]).map(walk);\n\t\t\t}\n\t\t\tif (obj instanceof Date) {\n\t\t\t\treturn { __date: obj.toISOString() };\n\t\t\t}\n\t\t\tif (obj instanceof RegExp) {\n\t\t\t\treturn { __regexp: { source: obj.source, flags: obj.flags } };\n\t\t\t}\n\t\t\tif (obj instanceof Map) {\n\t\t\t\tconst entries = [...(obj as Map<unknown, unknown>).entries()].map(([k, mv]) => [\n\t\t\t\t\twalk(k),\n\t\t\t\t\twalk(mv),\n\t\t\t\t]);\n\t\t\t\treturn { __map: entries };\n\t\t\t}\n\t\t\tif (obj instanceof Set) {\n\t\t\t\tconst items = [...(obj as Set<unknown>)].map(walk);\n\t\t\t\treturn { __set: items };\n\t\t\t}\n\t\t\tif (ArrayBuffer.isView(obj)) {\n\t\t\t\tconst ta = obj as unknown as { length: number; [i: number]: number };\n\t\t\t\tconst arr: number[] = new Array(ta.length);\n\t\t\t\tfor (let i = 0; i < ta.length; i++) arr[i] = ta[i] ?? 0;\n\t\t\t\treturn { __typed_array: { ctor: obj.constructor.name, data: arr } };\n\t\t\t}\n\t\t\tconst out: Record<string, unknown> = {};\n\t\t\tfor (const k of Object.keys(obj as Record<string, unknown>).sort()) {\n\t\t\t\tout[k] = walk((obj as Record<string, unknown>)[k]);\n\t\t\t}\n\t\t\treturn out;\n\t\t} finally {\n\t\t\tstack.delete(obj);\n\t\t}\n\t};\n\treturn walk(value);\n}\n\n// `explainPath` / `CausalChain` / `CausalStep` are exported from `graph/`\n// at module root; do not re-export here to keep the namespace boundary clean\n// and avoid duplicate-identifier issues in bundled .d.ts.\n"],"mappings":"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","names":[]}
@@ -0,0 +1,98 @@
1
+ import {
2
+ COMPLETE,
3
+ DATA,
4
+ ERROR,
5
+ PAUSE,
6
+ RESUME
7
+ } from "./chunk-SX52TAR4.js";
8
+
9
+ // src/extra/observable.ts
10
+ import { Observable } from "rxjs";
11
+ function toObservable(node, options) {
12
+ if (options?.raw) {
13
+ return new Observable((subscriber) => {
14
+ const unsub = node.subscribe((msgs) => {
15
+ if (subscriber.closed) return;
16
+ subscriber.next(msgs);
17
+ for (const m of msgs) {
18
+ if (m[0] === ERROR) {
19
+ subscriber.error(m[1]);
20
+ return;
21
+ }
22
+ if (m[0] === COMPLETE) {
23
+ subscriber.complete();
24
+ return;
25
+ }
26
+ }
27
+ });
28
+ return unsub;
29
+ });
30
+ }
31
+ return new Observable((subscriber) => {
32
+ const unsub = node.subscribe((msgs) => {
33
+ for (const m of msgs) {
34
+ if (subscriber.closed) return;
35
+ if (m[0] === DATA) {
36
+ subscriber.next(m[1]);
37
+ } else if (m[0] === ERROR) {
38
+ subscriber.error(m[1]);
39
+ return;
40
+ } else if (m[0] === COMPLETE) {
41
+ subscriber.complete();
42
+ return;
43
+ }
44
+ }
45
+ });
46
+ return unsub;
47
+ });
48
+ }
49
+
50
+ // src/extra/backpressure.ts
51
+ var nextLockId = 0;
52
+ function createWatermarkController(sendUp, opts) {
53
+ if (opts.highWaterMark < 1) throw new RangeError("highWaterMark must be >= 1");
54
+ if (opts.lowWaterMark < 0) throw new RangeError("lowWaterMark must be >= 0");
55
+ if (opts.lowWaterMark >= opts.highWaterMark)
56
+ throw new RangeError("lowWaterMark must be < highWaterMark");
57
+ const lockId = /* @__PURE__ */ Symbol(`bp-${++nextLockId}`);
58
+ let pending = 0;
59
+ let paused = false;
60
+ return {
61
+ onEnqueue() {
62
+ pending += 1;
63
+ if (!paused && pending >= opts.highWaterMark) {
64
+ paused = true;
65
+ sendUp([[PAUSE, lockId]]);
66
+ return true;
67
+ }
68
+ return false;
69
+ },
70
+ onDequeue() {
71
+ if (pending > 0) pending -= 1;
72
+ if (paused && pending <= opts.lowWaterMark) {
73
+ paused = false;
74
+ sendUp([[RESUME, lockId]]);
75
+ return true;
76
+ }
77
+ return false;
78
+ },
79
+ get pending() {
80
+ return pending;
81
+ },
82
+ get paused() {
83
+ return paused;
84
+ },
85
+ dispose() {
86
+ if (paused) {
87
+ paused = false;
88
+ sendUp([[RESUME, lockId]]);
89
+ }
90
+ }
91
+ };
92
+ }
93
+
94
+ export {
95
+ toObservable,
96
+ createWatermarkController
97
+ };
98
+ //# sourceMappingURL=chunk-OFVJBJXR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/extra/observable.ts","../src/extra/backpressure.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// RxJS bridge — reactive interop between GraphReFly nodes and RxJS Observables.\n// ---------------------------------------------------------------------------\n// Usage:\n// import { toObservable } from '@graphrefly/graphrefly-ts/extra';\n// const values$ = toObservable(myNode); // Observable<T>\n// const msgs$ = toObservable(myNode, { raw: true }); // Observable<Messages>\n// ---------------------------------------------------------------------------\n\nimport { Observable } from \"rxjs\";\nimport { COMPLETE, DATA, ERROR, type Messages } from \"../core/messages.js\";\nimport type { Node } from \"../core/node.js\";\n\n/** Options for {@link toObservable}. */\nexport type ToObservableOptions = {\n\t/**\n\t * When `true`, emit raw `Messages` batches instead of extracted `DATA` values.\n\t * Terminal batches are still emitted as the final `next()` before the\n\t * Observable signal (error/complete).\n\t */\n\traw?: boolean;\n};\n\n/**\n * Bridge a `Node<T>` to an RxJS `Observable`.\n *\n * Default mode emits the node's value on each `DATA` message. Maps `ERROR` to\n * `subscriber.error()` and `COMPLETE` to `subscriber.complete()`.\n * Protocol-internal signals (DIRTY, RESOLVED, PAUSE, etc.) are skipped.\n *\n * With `{ raw: true }`, emits full `[[Type, Data?], ...]` message batches.\n * The Observable terminates on ERROR or COMPLETE (the terminal batch is still\n * emitted as the final `next()` before the Observable signal).\n *\n * For graph-level observation, use `toObservable(graph.resolve(path))` or\n * subscribe to `graph.observe()` directly.\n *\n * Unsubscribing the Observable unsubscribes the node.\n */\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions?: ToObservableOptions & { raw?: false },\n): Observable<T>;\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions: ToObservableOptions & { raw: true },\n): Observable<Messages>;\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions?: ToObservableOptions,\n): Observable<T | Messages> {\n\tif (options?.raw) {\n\t\treturn new Observable<Messages>((subscriber) => {\n\t\t\tconst unsub = node.subscribe((msgs) => {\n\t\t\t\tif (subscriber.closed) return;\n\t\t\t\tsubscriber.next(msgs);\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\t\tsubscriber.error(m[1]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\t\tsubscriber.complete();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn unsub;\n\t\t});\n\t}\n\n\treturn new Observable<T>((subscriber) => {\n\t\tconst unsub = node.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (subscriber.closed) return;\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tsubscriber.next(m[1] as T);\n\t\t\t\t} else if (m[0] === ERROR) {\n\t\t\t\t\tsubscriber.error(m[1]);\n\t\t\t\t\treturn;\n\t\t\t\t} else if (m[0] === COMPLETE) {\n\t\t\t\t\tsubscriber.complete();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn unsub;\n\t});\n}\n","/**\n * Watermark-based backpressure controller — reactive PAUSE/RESUME flow control.\n *\n * Purely synchronous, event-driven. No timers, no polling, no Promises.\n * Each controller instance uses a unique lockId so multiple controllers\n * on the same upstream node do not collide.\n *\n * @module\n */\n\nimport { type Messages, PAUSE, RESUME } from \"../core/messages.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type WatermarkOptions = {\n\t/** Pending count at which PAUSE is sent upstream. */\n\thighWaterMark: number;\n\t/** Pending count at which RESUME is sent upstream (after being paused). */\n\tlowWaterMark: number;\n};\n\nexport type WatermarkController = {\n\t/** Call when a DATA message is buffered/enqueued. Returns `true` if PAUSE was just sent. */\n\tonEnqueue(): boolean;\n\t/** Call when a buffered item is consumed. Returns `true` if RESUME was just sent. */\n\tonDequeue(): boolean;\n\t/** Current un-consumed item count. */\n\treadonly pending: number;\n\t/** Whether upstream is currently paused by this controller. */\n\treadonly paused: boolean;\n\t/** Dispose: if paused, sends RESUME to unblock upstream. */\n\tdispose(): void;\n};\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nlet nextLockId = 0;\n\n/**\n * Creates a watermark-based backpressure controller.\n *\n * @param sendUp - Callback that delivers messages upstream (typically `handle.up`).\n * @param opts - High/low watermark thresholds (item counts).\n * @returns A {@link WatermarkController}.\n *\n * @example\n * ```ts\n * const handle = graph.observe(\"fast-source\");\n * const wm = createWatermarkController(\n * (msgs) => handle.up(msgs),\n * { highWaterMark: 64, lowWaterMark: 16 },\n * );\n *\n * // In sink callback:\n * handle.subscribe((msgs) => {\n * for (const msg of msgs) {\n * if (msg[0] === DATA) {\n * buffer.push(msg[1]);\n * wm.onEnqueue();\n * }\n * }\n * });\n *\n * // When consumer drains:\n * const item = buffer.shift();\n * wm.onDequeue();\n * ```\n *\n * @category extra\n */\nexport function createWatermarkController(\n\tsendUp: (messages: Messages) => void,\n\topts: WatermarkOptions,\n): WatermarkController {\n\tif (opts.highWaterMark < 1) throw new RangeError(\"highWaterMark must be >= 1\");\n\tif (opts.lowWaterMark < 0) throw new RangeError(\"lowWaterMark must be >= 0\");\n\tif (opts.lowWaterMark >= opts.highWaterMark)\n\t\tthrow new RangeError(\"lowWaterMark must be < highWaterMark\");\n\tconst lockId = Symbol(`bp-${++nextLockId}`);\n\tlet pending = 0;\n\tlet paused = false;\n\n\treturn {\n\t\tonEnqueue(): boolean {\n\t\t\tpending += 1;\n\t\t\tif (!paused && pending >= opts.highWaterMark) {\n\t\t\t\tpaused = true;\n\t\t\t\tsendUp([[PAUSE, lockId]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\tonDequeue(): boolean {\n\t\t\tif (pending > 0) pending -= 1;\n\t\t\tif (paused && pending <= opts.lowWaterMark) {\n\t\t\t\tpaused = false;\n\t\t\t\tsendUp([[RESUME, lockId]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\tget pending() {\n\t\t\treturn pending;\n\t\t},\n\t\tget paused() {\n\t\t\treturn paused;\n\t\t},\n\t\tdispose() {\n\t\t\tif (paused) {\n\t\t\t\tpaused = false;\n\t\t\t\tsendUp([[RESUME, lockId]]);\n\t\t\t}\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;AASA,SAAS,kBAAkB;AAsCpB,SAAS,aACf,MACA,SAC2B;AAC3B,MAAI,SAAS,KAAK;AACjB,WAAO,IAAI,WAAqB,CAAC,eAAe;AAC/C,YAAM,QAAQ,KAAK,UAAU,CAAC,SAAS;AACtC,YAAI,WAAW,OAAQ;AACvB,mBAAW,KAAK,IAAI;AACpB,mBAAW,KAAK,MAAM;AACrB,cAAI,EAAE,CAAC,MAAM,OAAO;AACnB,uBAAW,MAAM,EAAE,CAAC,CAAC;AACrB;AAAA,UACD;AACA,cAAI,EAAE,CAAC,MAAM,UAAU;AACtB,uBAAW,SAAS;AACpB;AAAA,UACD;AAAA,QACD;AAAA,MACD,CAAC;AACD,aAAO;AAAA,IACR,CAAC;AAAA,EACF;AAEA,SAAO,IAAI,WAAc,CAAC,eAAe;AACxC,UAAM,QAAQ,KAAK,UAAU,CAAC,SAAS;AACtC,iBAAW,KAAK,MAAM;AACrB,YAAI,WAAW,OAAQ;AACvB,YAAI,EAAE,CAAC,MAAM,MAAM;AAClB,qBAAW,KAAK,EAAE,CAAC,CAAM;AAAA,QAC1B,WAAW,EAAE,CAAC,MAAM,OAAO;AAC1B,qBAAW,MAAM,EAAE,CAAC,CAAC;AACrB;AAAA,QACD,WAAW,EAAE,CAAC,MAAM,UAAU;AAC7B,qBAAW,SAAS;AACpB;AAAA,QACD;AAAA,MACD;AAAA,IACD,CAAC;AACD,WAAO;AAAA,EACR,CAAC;AACF;;;AChDA,IAAI,aAAa;AAkCV,SAAS,0BACf,QACA,MACsB;AACtB,MAAI,KAAK,gBAAgB,EAAG,OAAM,IAAI,WAAW,4BAA4B;AAC7E,MAAI,KAAK,eAAe,EAAG,OAAM,IAAI,WAAW,2BAA2B;AAC3E,MAAI,KAAK,gBAAgB,KAAK;AAC7B,UAAM,IAAI,WAAW,sCAAsC;AAC5D,QAAM,SAAS,uBAAO,MAAM,EAAE,UAAU,EAAE;AAC1C,MAAI,UAAU;AACd,MAAI,SAAS;AAEb,SAAO;AAAA,IACN,YAAqB;AACpB,iBAAW;AACX,UAAI,CAAC,UAAU,WAAW,KAAK,eAAe;AAC7C,iBAAS;AACT,eAAO,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC;AACxB,eAAO;AAAA,MACR;AACA,aAAO;AAAA,IACR;AAAA,IACA,YAAqB;AACpB,UAAI,UAAU,EAAG,YAAW;AAC5B,UAAI,UAAU,WAAW,KAAK,cAAc;AAC3C,iBAAS;AACT,eAAO,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC;AACzB,eAAO;AAAA,MACR;AACA,aAAO;AAAA,IACR;AAAA,IACA,IAAI,UAAU;AACb,aAAO;AAAA,IACR;AAAA,IACA,IAAI,SAAS;AACZ,aAAO;AAAA,IACR;AAAA,IACA,UAAU;AACT,UAAI,QAAQ;AACX,iBAAS;AACT,eAAO,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC;AAAA,MAC1B;AAAA,IACD;AAAA,EACD;AACD;","names":[]}
@@ -0,0 +1,97 @@
1
+ import {
2
+ DATA,
3
+ DIRTY,
4
+ RESOLVED,
5
+ __export
6
+ } from "./chunk-SX52TAR4.js";
7
+
8
+ // src/compat/svelte/index.ts
9
+ var svelte_exports = {};
10
+ __export(svelte_exports, {
11
+ useStore: () => useStore,
12
+ useSubscribe: () => useSubscribe,
13
+ useSubscribeRecord: () => useSubscribeRecord
14
+ });
15
+ function useSubscribe(node) {
16
+ return {
17
+ subscribe(run) {
18
+ const unsub = node.subscribe(() => {
19
+ run(node.cache);
20
+ });
21
+ run(node.cache);
22
+ return unsub;
23
+ }
24
+ };
25
+ }
26
+ function useStore(node) {
27
+ return {
28
+ subscribe(run) {
29
+ const unsub = node.subscribe(() => {
30
+ run(node.cache);
31
+ });
32
+ run(node.cache);
33
+ return unsub;
34
+ },
35
+ set(value) {
36
+ node.down([[DIRTY], [DATA, value]]);
37
+ },
38
+ update(updater) {
39
+ const next = updater(node.cache);
40
+ node.down([[DIRTY], [DATA, next]]);
41
+ }
42
+ };
43
+ }
44
+ function useSubscribeRecord(keysNode, factory) {
45
+ return {
46
+ subscribe(run) {
47
+ let entrySubs = [];
48
+ const cleanupEntries = () => {
49
+ for (const unsub of entrySubs) unsub();
50
+ entrySubs = [];
51
+ };
52
+ const buildSnapshot = () => {
53
+ const snap = {};
54
+ for (const key of keysNode.cache ?? []) {
55
+ const nodes = factory(key);
56
+ const values = {};
57
+ for (const field of Object.keys(nodes)) {
58
+ values[field] = nodes[field].cache;
59
+ }
60
+ snap[key] = values;
61
+ }
62
+ return snap;
63
+ };
64
+ const sync = (nextKeys) => {
65
+ cleanupEntries();
66
+ for (const key of nextKeys) {
67
+ const nodes = factory(key);
68
+ for (const field of Object.keys(nodes)) {
69
+ const unsub = nodes[field].subscribe(() => {
70
+ run(buildSnapshot());
71
+ });
72
+ entrySubs.push(unsub);
73
+ }
74
+ }
75
+ run(buildSnapshot());
76
+ };
77
+ const keysUnsub = keysNode.subscribe((msgs) => {
78
+ if (msgs.some((m) => m[0] === DATA || m[0] === RESOLVED)) {
79
+ sync(keysNode.cache ?? []);
80
+ }
81
+ });
82
+ sync(keysNode.cache ?? []);
83
+ return () => {
84
+ keysUnsub();
85
+ cleanupEntries();
86
+ };
87
+ }
88
+ };
89
+ }
90
+
91
+ export {
92
+ useSubscribe,
93
+ useStore,
94
+ useSubscribeRecord,
95
+ svelte_exports
96
+ };
97
+ //# sourceMappingURL=chunk-OHISZPOJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/compat/svelte/index.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Svelte bindings — useSubscribe / useStore\n// ---------------------------------------------------------------------------\n// Bridges GraphReFly nodes into Svelte's store contract. Works with any\n// Node<T>, including companion nodes (node.meta.status).\n//\n// Usage:\n// import { useSubscribe, useStore } from '@graphrefly/graphrefly-ts/compat/svelte';\n// // Optional peer install (only for this adapter): pnpm add svelte\n// const status = useSubscribe(wsStatusNode); // Svelte readable store\n// const count = useStore(countNode); // Svelte writable store\n// // In template: $status, $count\n// // $count = 42\n// ---------------------------------------------------------------------------\n\nimport { DATA, DIRTY, type Messages, RESOLVED } from \"../../core/messages.js\";\nimport type { Node } from \"../../core/node.js\";\n\n/** Svelte store contract — implements the minimal `subscribe` method. */\nexport interface SvelteReadable<T> {\n\tsubscribe(run: (value: T) => void): () => void;\n}\n\n/** Svelte writable store contract. */\nexport interface SvelteWritable<T> extends SvelteReadable<T> {\n\tset(value: T): void;\n\tupdate(updater: (value: T) => T): void;\n}\n\n/**\n * Subscribe to a `Node<T>` as a Svelte readable store (implements Svelte store contract).\n * Subscription lifecycle is tied to Svelte store unsubscription (not node terminal messages).\n */\nexport function useSubscribe<T>(node: Node<T>): SvelteReadable<T | undefined | null> {\n\treturn {\n\t\tsubscribe(run: (value: T | undefined | null) => void): () => void {\n\t\t\tconst unsub = node.subscribe(() => {\n\t\t\t\trun(node.cache);\n\t\t\t});\n\t\t\trun(node.cache);\n\t\t\treturn unsub;\n\t\t},\n\t};\n}\n\n/**\n * Bind a writable `Node<T>` as a Svelte writable store.\n * Reads and writes adapt seamlessly.\n * Setter/update always forward `[[DIRTY], [DATA, value]]`, including `value === undefined`.\n * Subscription lifecycle is tied to Svelte store unsubscription (not node terminal messages).\n */\nexport function useStore<T>(node: Node<T>): SvelteWritable<T | undefined | null> {\n\treturn {\n\t\tsubscribe(run: (value: T | undefined | null) => void): () => void {\n\t\t\tconst unsub = node.subscribe(() => {\n\t\t\t\trun(node.cache);\n\t\t\t});\n\t\t\trun(node.cache);\n\t\t\treturn unsub;\n\t\t},\n\t\tset(value: T | undefined | null) {\n\t\t\tnode.down([[DIRTY], [DATA, value]]);\n\t\t},\n\t\tupdate(updater: (value: T | undefined | null) => T | undefined | null) {\n\t\t\tconst next = updater(node.cache);\n\t\t\tnode.down([[DIRTY], [DATA, next]]);\n\t\t},\n\t};\n}\n\n/** Maps a key to an object of nodes. Used by `useSubscribeRecord`. */\nexport type NodeFactory<K, R extends Record<string, any>> = (key: K) => {\n\t[P in keyof R]: Node<R[P]>;\n};\n\n/**\n * Subscribe to a dynamic keyed record of nodes as a Svelte readable store.\n * Re-subscribes all per-key fields whenever `keysNode` changes.\n * Key re-sync is gated to settled batches (`messageTier >= 3`) to avoid DIRTY-phase churn.\n */\nexport function useSubscribeRecord<K extends string, R extends Record<string, any>>(\n\tkeysNode: Node<K[]>,\n\tfactory: NodeFactory<K, R>,\n): SvelteReadable<Record<K, R>> {\n\treturn {\n\t\tsubscribe(run: (value: Record<K, R>) => void): () => void {\n\t\t\tlet entrySubs: Array<() => void> = [];\n\n\t\t\tconst cleanupEntries = () => {\n\t\t\t\tfor (const unsub of entrySubs) unsub();\n\t\t\t\tentrySubs = [];\n\t\t\t};\n\n\t\t\tconst buildSnapshot = (): Record<K, R> => {\n\t\t\t\tconst snap = {} as Record<K, R>;\n\t\t\t\tfor (const key of keysNode.cache ?? []) {\n\t\t\t\t\tconst nodes = factory(key);\n\t\t\t\t\tconst values = {} as R;\n\t\t\t\t\tfor (const field of Object.keys(nodes) as (keyof R)[]) {\n\t\t\t\t\t\tvalues[field] = nodes[field].cache as R[keyof R];\n\t\t\t\t\t}\n\t\t\t\t\tsnap[key] = values;\n\t\t\t\t}\n\t\t\t\treturn snap;\n\t\t\t};\n\n\t\t\tconst sync = (nextKeys: K[]) => {\n\t\t\t\tcleanupEntries();\n\t\t\t\tfor (const key of nextKeys) {\n\t\t\t\t\tconst nodes = factory(key);\n\t\t\t\t\tfor (const field of Object.keys(nodes) as (keyof R)[]) {\n\t\t\t\t\t\tconst unsub = nodes[field].subscribe(() => {\n\t\t\t\t\t\t\trun(buildSnapshot());\n\t\t\t\t\t\t});\n\t\t\t\t\t\tentrySubs.push(unsub);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trun(buildSnapshot());\n\t\t\t};\n\n\t\t\tconst keysUnsub = keysNode.subscribe((msgs: Messages) => {\n\t\t\t\tif (msgs.some((m) => m[0] === DATA || m[0] === RESOLVED)) {\n\t\t\t\t\tsync(keysNode.cache ?? []);\n\t\t\t\t}\n\t\t\t});\n\t\t\tsync(keysNode.cache ?? []);\n\n\t\t\treturn () => {\n\t\t\t\tkeysUnsub();\n\t\t\t\tcleanupEntries();\n\t\t\t};\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCO,SAAS,aAAgB,MAAqD;AACpF,SAAO;AAAA,IACN,UAAU,KAAwD;AACjE,YAAM,QAAQ,KAAK,UAAU,MAAM;AAClC,YAAI,KAAK,KAAK;AAAA,MACf,CAAC;AACD,UAAI,KAAK,KAAK;AACd,aAAO;AAAA,IACR;AAAA,EACD;AACD;AAQO,SAAS,SAAY,MAAqD;AAChF,SAAO;AAAA,IACN,UAAU,KAAwD;AACjE,YAAM,QAAQ,KAAK,UAAU,MAAM;AAClC,YAAI,KAAK,KAAK;AAAA,MACf,CAAC;AACD,UAAI,KAAK,KAAK;AACd,aAAO;AAAA,IACR;AAAA,IACA,IAAI,OAA6B;AAChC,WAAK,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC;AAAA,IACnC;AAAA,IACA,OAAO,SAAgE;AACtE,YAAM,OAAO,QAAQ,KAAK,KAAK;AAC/B,WAAK,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;AAAA,IAClC;AAAA,EACD;AACD;AAYO,SAAS,mBACf,UACA,SAC+B;AAC/B,SAAO;AAAA,IACN,UAAU,KAAgD;AACzD,UAAI,YAA+B,CAAC;AAEpC,YAAM,iBAAiB,MAAM;AAC5B,mBAAW,SAAS,UAAW,OAAM;AACrC,oBAAY,CAAC;AAAA,MACd;AAEA,YAAM,gBAAgB,MAAoB;AACzC,cAAM,OAAO,CAAC;AACd,mBAAW,OAAO,SAAS,SAAS,CAAC,GAAG;AACvC,gBAAM,QAAQ,QAAQ,GAAG;AACzB,gBAAM,SAAS,CAAC;AAChB,qBAAW,SAAS,OAAO,KAAK,KAAK,GAAkB;AACtD,mBAAO,KAAK,IAAI,MAAM,KAAK,EAAE;AAAA,UAC9B;AACA,eAAK,GAAG,IAAI;AAAA,QACb;AACA,eAAO;AAAA,MACR;AAEA,YAAM,OAAO,CAAC,aAAkB;AAC/B,uBAAe;AACf,mBAAW,OAAO,UAAU;AAC3B,gBAAM,QAAQ,QAAQ,GAAG;AACzB,qBAAW,SAAS,OAAO,KAAK,KAAK,GAAkB;AACtD,kBAAM,QAAQ,MAAM,KAAK,EAAE,UAAU,MAAM;AAC1C,kBAAI,cAAc,CAAC;AAAA,YACpB,CAAC;AACD,sBAAU,KAAK,KAAK;AAAA,UACrB;AAAA,QACD;AACA,YAAI,cAAc,CAAC;AAAA,MACpB;AAEA,YAAM,YAAY,SAAS,UAAU,CAAC,SAAmB;AACxD,YAAI,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC,MAAM,QAAQ,EAAE,CAAC,MAAM,QAAQ,GAAG;AACzD,eAAK,SAAS,SAAS,CAAC,CAAC;AAAA,QAC1B;AAAA,MACD,CAAC;AACD,WAAK,SAAS,SAAS,CAAC,CAAC;AAEzB,aAAO,MAAM;AACZ,kBAAU;AACV,uBAAe;AAAA,MAChB;AAAA,IACD;AAAA,EACD;AACD;","names":[]}
@@ -0,0 +1,102 @@
1
+ import {
2
+ GRAPH_META_SEGMENT,
3
+ Graph,
4
+ OVERHEAD,
5
+ SIZEOF_SYMBOL,
6
+ SNAPSHOT_VERSION,
7
+ diffForWAL,
8
+ explainPath,
9
+ graphProfile,
10
+ reachable,
11
+ sizeof
12
+ } from "./chunk-PF7GRZMW.js";
13
+ import {
14
+ ENVELOPE_VERSION,
15
+ JsonCodec,
16
+ createDagCborCodec,
17
+ createDagCborZstdCodec,
18
+ decodeEnvelope,
19
+ encodeEnvelope,
20
+ registerBuiltinCodecs,
21
+ replayWAL
22
+ } from "./chunk-PHOUUNK7.js";
23
+ import {
24
+ DATA,
25
+ __export
26
+ } from "./chunk-SX52TAR4.js";
27
+
28
+ // src/graph/index.ts
29
+ var graph_exports = {};
30
+ __export(graph_exports, {
31
+ ENVELOPE_VERSION: () => ENVELOPE_VERSION,
32
+ GRAPH_META_SEGMENT: () => GRAPH_META_SEGMENT,
33
+ Graph: () => Graph,
34
+ JsonCodec: () => JsonCodec,
35
+ SIZEOF_OVERHEAD: () => OVERHEAD,
36
+ SIZEOF_SYMBOL: () => SIZEOF_SYMBOL,
37
+ SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
38
+ createDagCborCodec: () => createDagCborCodec,
39
+ createDagCborZstdCodec: () => createDagCborZstdCodec,
40
+ decodeEnvelope: () => decodeEnvelope,
41
+ diffForWAL: () => diffForWAL,
42
+ encodeEnvelope: () => encodeEnvelope,
43
+ explainPath: () => explainPath,
44
+ graphProfile: () => graphProfile,
45
+ reachable: () => reachable,
46
+ registerBuiltinCodecs: () => registerBuiltinCodecs,
47
+ replayWAL: () => replayWAL,
48
+ sizeof: () => sizeof,
49
+ watchTopologyTree: () => watchTopologyTree
50
+ });
51
+
52
+ // src/graph/topology-tree.ts
53
+ function watchTopologyTree(graph, cb) {
54
+ const subs = /* @__PURE__ */ new Map();
55
+ const wire = (g, prefix) => {
56
+ if (subs.has(g)) return;
57
+ const placeholder = { off: () => {
58
+ }, prefix };
59
+ subs.set(g, placeholder);
60
+ const off = g.topology.subscribe((msgs) => {
61
+ for (const m of msgs) {
62
+ if (m[0] !== DATA) continue;
63
+ const event = m[1];
64
+ cb(event, g, prefix);
65
+ if (event.kind === "added" && event.nodeKind === "mount") {
66
+ const child = g._mounts.get(event.name);
67
+ if (child instanceof Graph) {
68
+ const childPrefix = `${prefix}${event.name}::`;
69
+ wire(child, childPrefix);
70
+ }
71
+ } else if (event.kind === "removed" && event.nodeKind === "mount") {
72
+ const removedPrefix = `${prefix}${event.name}::`;
73
+ for (const [trackedGraph, trackedEntry] of Array.from(subs.entries())) {
74
+ if (trackedGraph === graph) continue;
75
+ if (trackedEntry.prefix.startsWith(removedPrefix)) {
76
+ trackedEntry.off();
77
+ subs.delete(trackedGraph);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ });
83
+ placeholder.off = off;
84
+ for (const [mountName, child] of g._mounts) {
85
+ if (child instanceof Graph) {
86
+ const childPrefix = `${prefix}${mountName}::`;
87
+ wire(child, childPrefix);
88
+ }
89
+ }
90
+ };
91
+ wire(graph, "");
92
+ return () => {
93
+ for (const entry of subs.values()) entry.off();
94
+ subs.clear();
95
+ };
96
+ }
97
+
98
+ export {
99
+ watchTopologyTree,
100
+ graph_exports
101
+ };
102
+ //# sourceMappingURL=chunk-OU5CQKNW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/graph/index.ts","../src/graph/topology-tree.ts"],"sourcesContent":["/**\n * Graph container: registry, wiring, introspection (Phase 1).\n */\n\nexport { OVERHEAD as SIZEOF_OVERHEAD, SIZEOF_SYMBOL, sizeof } from \"../extra/utils/sizeof.js\";\nexport {\n\tcreateDagCborCodec,\n\tcreateDagCborZstdCodec,\n\tdecodeEnvelope,\n\tENVELOPE_VERSION,\n\ttype EvictedSubgraphInfo,\n\ttype EvictionPolicy,\n\tencodeEnvelope,\n\ttype GraphCodec,\n\tJsonCodec,\n\ttype LazyGraphCodec,\n\tregisterBuiltinCodecs,\n\treplayWAL,\n\ttype WALEntry,\n} from \"./codec.js\";\nexport {\n\ttype CausalChain,\n\ttype CausalStep,\n\ttype ExplainPathOptions,\n\texplainPath,\n} from \"./explain.js\";\nexport {\n\ttype DescribeFilter,\n\tdiffForWAL,\n\tGRAPH_META_SEGMENT,\n\tGraph,\n\ttype GraphActorOptions,\n\ttype GraphAttachStorageOptions,\n\ttype GraphCheckpointRecord,\n\ttype GraphDescribeOptions,\n\ttype GraphDescribeOutput,\n\ttype GraphDiagramDirection,\n\ttype GraphDiagramOptions,\n\ttype GraphDiffChange,\n\ttype GraphDiffResult,\n\ttype GraphFactoryContext,\n\ttype GraphNodeFactory,\n\ttype GraphObserveAll,\n\ttype GraphObserveOne,\n\ttype GraphOptions,\n\ttype GraphPersistSnapshot,\n\ttype GraphVersionChange,\n\ttype GraphWALDiff,\n\ttype ObserveDetail,\n\ttype ObserveEvent,\n\ttype ObserveOptions,\n\ttype ObserveResult,\n\ttype ObserveTheme,\n\ttype ObserveThemeName,\n\ttype ReachableDirection,\n\ttype ReachableOptions,\n\treachable,\n\tSNAPSHOT_VERSION,\n\ttype TopologyEvent,\n\ttype TraceEntry,\n} from \"./graph.js\";\nexport {\n\ttype GraphProfileOptions,\n\ttype GraphProfileResult,\n\tgraphProfile,\n\ttype NodeProfile,\n} from \"./profile.js\";\nexport { watchTopologyTree } from \"./topology-tree.js\";\n","/**\n * Transitive structural-change subscription helper.\n *\n * Subscribes to a graph's {@link Graph.topology} event stream AND recurses\n * into every mounted subgraph, auto-wiring new mounts when they appear\n * (via parent `added: mount` events) and tearing down subscriptions for\n * unmounted subgraphs (via `removed: mount` events + the audit record).\n *\n * Lives in `graph/` rather than `patterns/` because it depends only on\n * the `Graph` primitive and the `TopologyEvent` type — no domain-layer\n * factories. Consumers that need full-tree dynamic coverage (e.g.\n * `policyEnforcer`, `graphLens`) import from here to avoid circular\n * references between audit/lens modules.\n *\n * @module\n */\nimport { DATA } from \"../core/messages.js\";\nimport type { TopologyEvent } from \"./graph.js\";\nimport { Graph } from \"./graph.js\";\n\n/**\n * Subscribe to structural changes across `graph` and every transitively\n * mounted subgraph. `cb` fires on every {@link TopologyEvent} from any\n * graph in the tree. Newly-mounted subgraphs are auto-wired when their\n * parent emits `{kind: \"added\", nodeKind: \"mount\"}`; newly-unmounted\n * subgraphs' subscriptions are disposed via the parent's\n * `{kind: \"removed\", nodeKind: \"mount\"}` event plus the returned\n * `GraphRemoveAudit`.\n *\n * The callback receives a third argument `prefix`: the `::`-delimited\n * path from the root watched graph to the emitter, ending with `\"::\"`\n * (empty string when the event comes from the root itself). Compute\n * a qualified path for an added/removed entry as `prefix + event.name`.\n *\n * @param graph - Root graph to watch.\n * @param cb - Receives `(event, emitterGraph, prefix)`.\n * @returns Dispose function — tears down every active subscription.\n *\n * @category observability\n */\nexport function watchTopologyTree(\n\tgraph: Graph,\n\tcb: (event: TopologyEvent, emitter: Graph, prefix: string) => void,\n): () => void {\n\t// Tracks every wired graph with its qualified prefix (path from root).\n\t// Prefix is used for both qualified-path emission to `cb` AND prefix-match\n\t// disposal when a mount is removed — more robust than relying on\n\t// `_parent == null` which only nulls on the direct child, not on\n\t// grandchildren within an unmounted subtree.\n\ttype Entry = { off: () => void; prefix: string };\n\tconst subs = new Map<Graph, Entry>();\n\n\tconst wire = (g: Graph, prefix: string): void => {\n\t\tif (subs.has(g)) return;\n\t\t// Placeholder entry set BEFORE subscribe so any synchronous reentry\n\t\t// (e.g. a mount-added handler firing during subscribe's initial push)\n\t\t// sees this graph as already wired and skips rewiring.\n\t\tconst placeholder: Entry = { off: () => {}, prefix };\n\t\tsubs.set(g, placeholder);\n\t\tconst off = g.topology.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\tconst event = m[1] as TopologyEvent;\n\t\t\t\tcb(event, g, prefix);\n\t\t\t\tif (event.kind === \"added\" && event.nodeKind === \"mount\") {\n\t\t\t\t\tconst child = g._mounts.get(event.name);\n\t\t\t\t\tif (child instanceof Graph) {\n\t\t\t\t\t\tconst childPrefix = `${prefix}${event.name}::`;\n\t\t\t\t\t\twire(child, childPrefix);\n\t\t\t\t\t}\n\t\t\t\t} else if (event.kind === \"removed\" && event.nodeKind === \"mount\") {\n\t\t\t\t\t// Dispose every tracked sub whose prefix is under the removed\n\t\t\t\t\t// mount. Matches on qualified-prefix rather than `_parent`\n\t\t\t\t\t// so deep descendants are released even when their parent\n\t\t\t\t\t// pointers haven't been nulled.\n\t\t\t\t\tconst removedPrefix = `${prefix}${event.name}::`;\n\t\t\t\t\tfor (const [trackedGraph, trackedEntry] of Array.from(subs.entries())) {\n\t\t\t\t\t\tif (trackedGraph === graph) continue;\n\t\t\t\t\t\tif (trackedEntry.prefix.startsWith(removedPrefix)) {\n\t\t\t\t\t\t\ttrackedEntry.off();\n\t\t\t\t\t\t\tsubs.delete(trackedGraph);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tplaceholder.off = off;\n\t\t// Recursively wire any children already mounted when this call runs.\n\t\tfor (const [mountName, child] of g._mounts) {\n\t\t\tif (child instanceof Graph) {\n\t\t\t\tconst childPrefix = `${prefix}${mountName}::`;\n\t\t\t\twire(child, childPrefix);\n\t\t\t}\n\t\t}\n\t};\n\n\twire(graph, \"\");\n\n\treturn () => {\n\t\tfor (const entry of subs.values()) entry.off();\n\t\tsubs.clear();\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwCO,SAAS,kBACf,OACA,IACa;AAOb,QAAM,OAAO,oBAAI,IAAkB;AAEnC,QAAM,OAAO,CAAC,GAAU,WAAyB;AAChD,QAAI,KAAK,IAAI,CAAC,EAAG;AAIjB,UAAM,cAAqB,EAAE,KAAK,MAAM;AAAA,IAAC,GAAG,OAAO;AACnD,SAAK,IAAI,GAAG,WAAW;AACvB,UAAM,MAAM,EAAE,SAAS,UAAU,CAAC,SAAS;AAC1C,iBAAW,KAAK,MAAM;AACrB,YAAI,EAAE,CAAC,MAAM,KAAM;AACnB,cAAM,QAAQ,EAAE,CAAC;AACjB,WAAG,OAAO,GAAG,MAAM;AACnB,YAAI,MAAM,SAAS,WAAW,MAAM,aAAa,SAAS;AACzD,gBAAM,QAAQ,EAAE,QAAQ,IAAI,MAAM,IAAI;AACtC,cAAI,iBAAiB,OAAO;AAC3B,kBAAM,cAAc,GAAG,MAAM,GAAG,MAAM,IAAI;AAC1C,iBAAK,OAAO,WAAW;AAAA,UACxB;AAAA,QACD,WAAW,MAAM,SAAS,aAAa,MAAM,aAAa,SAAS;AAKlE,gBAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,IAAI;AAC5C,qBAAW,CAAC,cAAc,YAAY,KAAK,MAAM,KAAK,KAAK,QAAQ,CAAC,GAAG;AACtE,gBAAI,iBAAiB,MAAO;AAC5B,gBAAI,aAAa,OAAO,WAAW,aAAa,GAAG;AAClD,2BAAa,IAAI;AACjB,mBAAK,OAAO,YAAY;AAAA,YACzB;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD,CAAC;AACD,gBAAY,MAAM;AAElB,eAAW,CAAC,WAAW,KAAK,KAAK,EAAE,SAAS;AAC3C,UAAI,iBAAiB,OAAO;AAC3B,cAAM,cAAc,GAAG,MAAM,GAAG,SAAS;AACzC,aAAK,OAAO,WAAW;AAAA,MACxB;AAAA,IACD;AAAA,EACD;AAEA,OAAK,OAAO,EAAE;AAEd,SAAO,MAAM;AACZ,eAAW,SAAS,KAAK,OAAO,EAAG,OAAM,IAAI;AAC7C,SAAK,MAAM;AAAA,EACZ;AACD;","names":[]}