@graphrefly/graphrefly 0.15.0 → 0.17.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 (75) hide show
  1. package/dist/{chunk-4APC3AFN.js → chunk-2PORF4RP.js} +22 -49
  2. package/dist/chunk-2PORF4RP.js.map +1 -0
  3. package/dist/{chunk-QVYZD65U.js → chunk-646OG3PO.js} +10 -38
  4. package/dist/chunk-646OG3PO.js.map +1 -0
  5. package/dist/{chunk-H243FWYP.js → chunk-BV3TPSBK.js} +1 -2
  6. package/dist/chunk-BV3TPSBK.js.map +1 -0
  7. package/dist/chunk-EBNKJULL.js +231 -0
  8. package/dist/chunk-EBNKJULL.js.map +1 -0
  9. package/dist/{chunk-XQ4UMAU7.js → chunk-F2ULI3Q3.js} +2 -2
  10. package/dist/{chunk-CRACCCJY.js → chunk-IHJHBADD.js} +29 -5
  11. package/dist/chunk-IHJHBADD.js.map +1 -0
  12. package/dist/{chunk-YW6LFCFS.js → chunk-R6OHUUYB.js} +3 -3
  13. package/dist/{chunk-2ZICUAUJ.js → chunk-XJ6EMQ22.js} +10 -12
  14. package/dist/chunk-XJ6EMQ22.js.map +1 -0
  15. package/dist/{chunk-GKRKDYNT.js → chunk-YXROQFXZ.js} +3 -3
  16. package/dist/compat/nestjs/index.cjs +38 -42
  17. package/dist/compat/nestjs/index.cjs.map +1 -1
  18. package/dist/compat/nestjs/index.d.cts +4 -4
  19. package/dist/compat/nestjs/index.d.ts +4 -4
  20. package/dist/compat/nestjs/index.js +7 -7
  21. package/dist/core/index.cjs +0 -1
  22. package/dist/core/index.cjs.map +1 -1
  23. package/dist/core/index.d.cts +2 -2
  24. package/dist/core/index.d.ts +2 -2
  25. package/dist/core/index.js +3 -3
  26. package/dist/extra/index.cjs +27 -79
  27. package/dist/extra/index.cjs.map +1 -1
  28. package/dist/extra/index.d.cts +4 -4
  29. package/dist/extra/index.d.ts +4 -4
  30. package/dist/extra/index.js +3 -3
  31. package/dist/graph/index.cjs +151 -5
  32. package/dist/graph/index.cjs.map +1 -1
  33. package/dist/graph/index.d.cts +3 -3
  34. package/dist/graph/index.d.ts +3 -3
  35. package/dist/graph/index.js +10 -6
  36. package/dist/{graph-BXIK5Dq5.d.ts → graph-Dc-P9BVm.d.ts} +15 -3
  37. package/dist/{graph-BhADtuFU.d.cts → graph-fCsaaVIa.d.cts} +15 -3
  38. package/dist/{index-BkToATim.d.ts → index-4OIX-q0C.d.cts} +88 -3
  39. package/dist/{index-DSp5R3Xq.d.ts → index-BBVBYPxr.d.cts} +4 -4
  40. package/dist/{index-BNB0KjKe.d.ts → index-BmoUvOGN.d.ts} +1 -1
  41. package/dist/{index-DBhLjWSV.d.cts → index-ClaKZFPl.d.cts} +100 -31
  42. package/dist/{index-Wa8jXne6.d.cts → index-D7y9Q8W4.d.ts} +88 -3
  43. package/dist/{index-Dqemj9q0.d.cts → index-DWq0P9T6.d.ts} +4 -4
  44. package/dist/{index-fYObbpUw.d.ts → index-DhXznWyH.d.ts} +2 -2
  45. package/dist/{index-DANO9Gg7.d.cts → index-DlGMf_Qe.d.cts} +2 -2
  46. package/dist/{index-CKyYg4IP.d.ts → index-N704txAA.d.ts} +100 -31
  47. package/dist/{index-DKIyo4Bq.d.cts → index-YlOH1Gw6.d.cts} +1 -1
  48. package/dist/index.cjs +302 -231
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +71 -41
  51. package/dist/index.d.ts +71 -41
  52. package/dist/index.js +131 -151
  53. package/dist/index.js.map +1 -1
  54. package/dist/{meta-CrZUQAJ6.d.cts → meta-BV4pj9ML.d.cts} +6 -0
  55. package/dist/{meta-CrZUQAJ6.d.ts → meta-BV4pj9ML.d.ts} +6 -0
  56. package/dist/observable-Cz-AWhwR.d.cts +42 -0
  57. package/dist/observable-DCqlwGyl.d.ts +42 -0
  58. package/dist/patterns/reactive-layout/index.cjs +26 -3
  59. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  60. package/dist/patterns/reactive-layout/index.d.cts +3 -3
  61. package/dist/patterns/reactive-layout/index.d.ts +3 -3
  62. package/dist/patterns/reactive-layout/index.js +4 -4
  63. package/package.json +1 -1
  64. package/dist/chunk-2ZICUAUJ.js.map +0 -1
  65. package/dist/chunk-4APC3AFN.js.map +0 -1
  66. package/dist/chunk-CRACCCJY.js.map +0 -1
  67. package/dist/chunk-H243FWYP.js.map +0 -1
  68. package/dist/chunk-QVYZD65U.js.map +0 -1
  69. package/dist/chunk-ZHTHUX5D.js +0 -107
  70. package/dist/chunk-ZHTHUX5D.js.map +0 -1
  71. package/dist/reactive-log-ChbpUrY2.d.cts +0 -137
  72. package/dist/reactive-log-DV--7BWd.d.ts +0 -137
  73. /package/dist/{chunk-XQ4UMAU7.js.map → chunk-F2ULI3Q3.js.map} +0 -0
  74. /package/dist/{chunk-YW6LFCFS.js.map → chunk-R6OHUUYB.js.map} +0 -0
  75. /package/dist/{chunk-GKRKDYNT.js.map → chunk-YXROQFXZ.js.map} +0 -0
@@ -0,0 +1,231 @@
1
+ import {
2
+ GRAPH_META_SEGMENT,
3
+ Graph,
4
+ reachable
5
+ } from "./chunk-IHJHBADD.js";
6
+ import {
7
+ NodeImpl,
8
+ __export
9
+ } from "./chunk-BV3TPSBK.js";
10
+
11
+ // src/graph/index.ts
12
+ var graph_exports = {};
13
+ __export(graph_exports, {
14
+ GRAPH_META_SEGMENT: () => GRAPH_META_SEGMENT,
15
+ Graph: () => Graph,
16
+ JsonCodec: () => JsonCodec,
17
+ createDagCborCodec: () => createDagCborCodec,
18
+ createDagCborZstdCodec: () => createDagCborZstdCodec,
19
+ graphProfile: () => graphProfile,
20
+ negotiateCodec: () => negotiateCodec,
21
+ reachable: () => reachable,
22
+ replayWAL: () => replayWAL,
23
+ sizeof: () => sizeof
24
+ });
25
+
26
+ // src/graph/codec.ts
27
+ var JsonCodec = {
28
+ contentType: "application/json",
29
+ name: "json",
30
+ encode(snapshot) {
31
+ const json = JSON.stringify(snapshot);
32
+ return new TextEncoder().encode(json);
33
+ },
34
+ decode(buffer) {
35
+ const json = new TextDecoder().decode(buffer);
36
+ return JSON.parse(json);
37
+ }
38
+ };
39
+ function createDagCborCodec(dagCbor) {
40
+ return {
41
+ contentType: "application/dag-cbor",
42
+ name: "dag-cbor",
43
+ encode: (snapshot) => dagCbor.encode(snapshot),
44
+ decode: (buffer) => dagCbor.decode(buffer)
45
+ };
46
+ }
47
+ function createDagCborZstdCodec(dagCbor, zstd) {
48
+ return {
49
+ contentType: "application/dag-cbor+zstd",
50
+ name: "dag-cbor-zstd",
51
+ encode: (snapshot) => zstd.compressSync(dagCbor.encode(snapshot)),
52
+ decode: (buffer) => dagCbor.decode(zstd.decompressSync(buffer))
53
+ };
54
+ }
55
+ function negotiateCodec(localPreference, remoteContentTypes) {
56
+ const remoteSet = new Set(remoteContentTypes);
57
+ for (const codec of localPreference) {
58
+ if (remoteSet.has(codec.contentType)) return codec;
59
+ }
60
+ return null;
61
+ }
62
+ function replayWAL(entries) {
63
+ if (entries.length === 0) {
64
+ throw new Error("WAL is empty \u2014 need at least one full snapshot");
65
+ }
66
+ const first = entries[0];
67
+ if (first.type !== "full") {
68
+ throw new Error("WAL must start with a full snapshot");
69
+ }
70
+ const result = JSON.parse(JSON.stringify(first.snapshot));
71
+ for (let i = 1; i < entries.length; i++) {
72
+ const entry = entries[i];
73
+ if (entry.type === "full") {
74
+ Object.assign(result, JSON.parse(JSON.stringify(entry.snapshot)));
75
+ continue;
76
+ }
77
+ const delta = entry.delta;
78
+ for (const [name, patch] of Object.entries(delta.nodes)) {
79
+ if (result.nodes[name]) {
80
+ result.nodes[name].value = patch.value;
81
+ if (patch.meta) {
82
+ result.nodes[name].meta = patch.meta;
83
+ }
84
+ }
85
+ }
86
+ for (const name of delta.removed) {
87
+ delete result.nodes[name];
88
+ }
89
+ const edges = [...result.edges];
90
+ for (const edge of delta.edgesRemoved) {
91
+ const idx = edges.findIndex((e) => e.from === edge.from && e.to === edge.to);
92
+ if (idx !== -1) edges.splice(idx, 1);
93
+ }
94
+ for (const edge of delta.edgesAdded) {
95
+ edges.push(edge);
96
+ }
97
+ result.edges = edges;
98
+ }
99
+ return result;
100
+ }
101
+
102
+ // src/graph/sizeof.ts
103
+ var OVERHEAD = {
104
+ object: 56,
105
+ array: 64,
106
+ string: 40,
107
+ // header; content added separately
108
+ number: 8,
109
+ boolean: 4,
110
+ null: 0,
111
+ undefined: 0,
112
+ symbol: 40,
113
+ bigint: 16,
114
+ function: 120,
115
+ map: 72,
116
+ set: 72,
117
+ mapEntry: 40,
118
+ setEntry: 24
119
+ };
120
+ function sizeof(value) {
121
+ const seen = /* @__PURE__ */ new WeakSet();
122
+ return _sizeof(value, seen);
123
+ }
124
+ function _sizeof(value, seen) {
125
+ if (value == null) return 0;
126
+ const t = typeof value;
127
+ switch (t) {
128
+ case "number":
129
+ return OVERHEAD.number;
130
+ case "boolean":
131
+ return OVERHEAD.boolean;
132
+ case "string":
133
+ return OVERHEAD.string + value.length * 2;
134
+ // UTF-16
135
+ case "bigint":
136
+ return OVERHEAD.bigint;
137
+ case "symbol":
138
+ return OVERHEAD.symbol;
139
+ case "function":
140
+ if (seen.has(value)) return 0;
141
+ seen.add(value);
142
+ return OVERHEAD.function;
143
+ case "undefined":
144
+ return 0;
145
+ }
146
+ const obj = value;
147
+ if (seen.has(obj)) return 0;
148
+ seen.add(obj);
149
+ if (obj instanceof Map) {
150
+ let size2 = OVERHEAD.map;
151
+ for (const [k, v] of obj) {
152
+ size2 += OVERHEAD.mapEntry + _sizeof(k, seen) + _sizeof(v, seen);
153
+ }
154
+ return size2;
155
+ }
156
+ if (obj instanceof Set) {
157
+ let size2 = OVERHEAD.set;
158
+ for (const v of obj) {
159
+ size2 += OVERHEAD.setEntry + _sizeof(v, seen);
160
+ }
161
+ return size2;
162
+ }
163
+ if (Array.isArray(obj)) {
164
+ let size2 = OVERHEAD.array + obj.length * 8;
165
+ for (const item of obj) {
166
+ size2 += _sizeof(item, seen);
167
+ }
168
+ return size2;
169
+ }
170
+ if (obj instanceof ArrayBuffer) return obj.byteLength;
171
+ if (ArrayBuffer.isView(obj)) return obj.byteLength;
172
+ let size = OVERHEAD.object;
173
+ const keys = Object.keys(obj);
174
+ for (const key of keys) {
175
+ size += OVERHEAD.string + key.length * 2;
176
+ size += _sizeof(obj[key], seen);
177
+ }
178
+ return size;
179
+ }
180
+
181
+ // src/graph/profile.ts
182
+ function graphProfile(graph, opts) {
183
+ const topN = opts?.topN ?? 10;
184
+ const desc = graph.describe({ detail: "standard" });
185
+ const targets = [];
186
+ if (typeof graph._collectObserveTargets === "function") {
187
+ graph._collectObserveTargets("", targets);
188
+ }
189
+ const pathToNode = /* @__PURE__ */ new Map();
190
+ for (const [p, n] of targets) {
191
+ pathToNode.set(p, n);
192
+ }
193
+ const profiles = [];
194
+ for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
195
+ const nd = pathToNode.get(path);
196
+ const impl = nd instanceof NodeImpl ? nd : null;
197
+ const valueSizeBytes = impl ? sizeof(impl.get()) : 0;
198
+ const subscriberCount = impl ? impl._sinkCount : 0;
199
+ const depCount = nodeDesc.deps?.length ?? 0;
200
+ profiles.push({
201
+ path,
202
+ type: nodeDesc.type,
203
+ status: nodeDesc.status ?? "unknown",
204
+ valueSizeBytes,
205
+ subscriberCount,
206
+ depCount
207
+ });
208
+ }
209
+ const totalValueSizeBytes = profiles.reduce((sum, p) => sum + p.valueSizeBytes, 0);
210
+ const hotspots = [...profiles].sort((a, b) => b.valueSizeBytes - a.valueSizeBytes).slice(0, topN);
211
+ return {
212
+ nodeCount: profiles.length,
213
+ edgeCount: desc.edges.length,
214
+ subgraphCount: desc.subgraphs.length,
215
+ nodes: profiles,
216
+ totalValueSizeBytes,
217
+ hotspots
218
+ };
219
+ }
220
+
221
+ export {
222
+ JsonCodec,
223
+ createDagCborCodec,
224
+ createDagCborZstdCodec,
225
+ negotiateCodec,
226
+ replayWAL,
227
+ sizeof,
228
+ graphProfile,
229
+ graph_exports
230
+ };
231
+ //# sourceMappingURL=chunk-EBNKJULL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/graph/index.ts","../src/graph/codec.ts","../src/graph/sizeof.ts","../src/graph/profile.ts"],"sourcesContent":["/**\n * Graph container: registry, wiring, introspection (Phase 1).\n */\n\nexport {\n\tcreateDagCborCodec,\n\tcreateDagCborZstdCodec,\n\ttype DeltaCheckpoint,\n\ttype EvictedSubgraphInfo,\n\ttype EvictionPolicy,\n\ttype GraphCodec,\n\tJsonCodec,\n\ttype LazyGraphCodec,\n\tnegotiateCodec,\n\treplayWAL,\n\ttype WALEntry,\n} from \"./codec.js\";\nexport {\n\ttype AutoCheckpointAdapter,\n\ttype DescribeFilter,\n\tGRAPH_META_SEGMENT,\n\tGraph,\n\ttype GraphActorOptions,\n\ttype GraphAutoCheckpointHandle,\n\ttype GraphAutoCheckpointOptions,\n\ttype GraphCheckpointRecord,\n\ttype GraphDescribeOptions,\n\ttype GraphDescribeOutput,\n\ttype GraphDiagramDirection,\n\ttype GraphDiagramOptions,\n\ttype GraphDiffChange,\n\ttype GraphDiffResult,\n\ttype GraphDumpOptions,\n\ttype GraphFactoryContext,\n\ttype GraphNodeFactory,\n\ttype GraphObserveAll,\n\ttype GraphObserveOne,\n\ttype GraphOptions,\n\ttype GraphPersistSnapshot,\n\ttype GraphSpyHandle,\n\ttype GraphSpyOptions,\n\ttype GraphSpyTheme,\n\ttype GraphSpyThemeName,\n\ttype ObserveDetail,\n\ttype ObserveEvent,\n\ttype ObserveOptions,\n\ttype ObserveResult,\n\ttype ReachableDirection,\n\ttype ReachableOptions,\n\treachable,\n\ttype TraceEntry,\n} from \"./graph.js\";\nexport {\n\ttype GraphProfileOptions,\n\ttype GraphProfileResult,\n\tgraphProfile,\n\ttype NodeProfile,\n} from \"./profile.js\";\nexport { sizeof } from \"./sizeof.js\";\n","/**\n * GraphCodec — pluggable serialization for graph snapshots (Phase 8.6).\n *\n * Design reference: `archive/docs/SESSION-serialization-memory-footprint.md`\n *\n * The codec interface decouples snapshot format from graph internals.\n * Default is JSON (current behavior). DAG-CBOR and compressed variants\n * ship as optional codecs. FlatBuffers/Arrow for advanced tiers.\n *\n * Tiered representation:\n * HOT — JS objects (live propagation, no codec involved)\n * WARM — DAG-CBOR in-memory buffer (lazy hydration, delta checkpoints)\n * COLD — Arrow/Parquet (bulk storage, ML pipelines, archival)\n * PEEK — FlatBuffers (zero-copy read from dormant graph)\n */\n\nimport type { GraphPersistSnapshot } from \"./graph.js\";\n\n// ---------------------------------------------------------------------------\n// Core codec interface\n// ---------------------------------------------------------------------------\n\n/**\n * Encode/decode graph snapshots to/from binary.\n *\n * Implementations must be deterministic: `encode(x)` always produces the\n * same bytes for the same input. This is critical for CID computation (V1)\n * and snapshot hash-comparison.\n */\nexport interface GraphCodec {\n\t/** MIME-like content type identifier (e.g. \"application/dag-cbor+zstd\"). */\n\treadonly contentType: string;\n\n\t/** Human-readable name for diagnostics. */\n\treadonly name: string;\n\n\t/** Encode a snapshot to binary. */\n\tencode(snapshot: GraphPersistSnapshot): Uint8Array;\n\n\t/**\n\t * Decode binary back to a snapshot.\n\t *\n\t * For lazy codecs, this may return a proxy that decodes nodes on access\n\t * (see {@link LazyGraphCodec}).\n\t */\n\tdecode(buffer: Uint8Array): GraphPersistSnapshot;\n}\n\n/**\n * Extended codec that supports lazy (on-demand) node decoding.\n *\n * `decodeLazy` returns a snapshot where `nodes` is a Proxy — individual\n * nodes are decoded only when accessed. This enables near-zero cold-start\n * for large graphs (decode envelope + topology, skip node values until read).\n */\nexport interface LazyGraphCodec extends GraphCodec {\n\t/** Decode envelope and topology; defer node value decoding to access time. */\n\tdecodeLazy(buffer: Uint8Array): GraphPersistSnapshot;\n}\n\n// ---------------------------------------------------------------------------\n// Delta checkpoint types (requires V0 — Phase 6.0)\n// ---------------------------------------------------------------------------\n\n/**\n * A delta checkpoint: only the nodes that changed since last checkpoint.\n *\n * Append-only: each delta is identified by `seq` (monotonic). A full\n * snapshot is taken every `compactEvery` deltas for WAL compaction.\n */\nexport interface DeltaCheckpoint {\n\t/** Monotonic sequence number. */\n\tseq: number;\n\t/** Graph name. */\n\tname: string;\n\t/** Base snapshot seq this delta applies to (0 = initial full snapshot). */\n\tbaseSec: number;\n\t/** Only nodes with version > lastCheckpoint. Keyed by node name. */\n\tnodes: Record<\n\t\tstring,\n\t\t{\n\t\t\t/** V0 version at time of checkpoint. */\n\t\t\tversion: number;\n\t\t\t/** Serialized node value (codec-dependent). */\n\t\t\tvalue: unknown;\n\t\t\t/** Meta snapshot (only if materialized). */\n\t\t\tmeta?: Record<string, unknown>;\n\t\t}\n\t>;\n\t/** Nodes removed since last checkpoint. */\n\tremoved: string[];\n\t/** Edges added since last checkpoint. */\n\tedgesAdded: ReadonlyArray<{ from: string; to: string }>;\n\t/** Edges removed since last checkpoint. */\n\tedgesRemoved: ReadonlyArray<{ from: string; to: string }>;\n\t/** Timestamp (wall-clock ns) of this checkpoint. */\n\ttimestampNs: bigint;\n}\n\n/**\n * WAL entry: either a full snapshot or a delta.\n */\nexport type WALEntry =\n\t| { type: \"full\"; snapshot: GraphPersistSnapshot; seq: number }\n\t| { type: \"delta\"; delta: DeltaCheckpoint };\n\n// ---------------------------------------------------------------------------\n// Eviction policy (dormant subgraph management)\n// ---------------------------------------------------------------------------\n\n/**\n * Policy for evicting dormant subgraphs to reduce memory.\n *\n * When a subgraph hasn't propagated for `idleTimeoutMs`, it is serialized\n * using the graph's codec and JS objects are released. Re-hydrated on next\n * access (read, propagation, describe).\n */\nexport interface EvictionPolicy {\n\t/** Milliseconds of inactivity before eviction. */\n\tidleTimeoutMs: number;\n\t/** Codec to use for serializing evicted subgraphs (default: graph's codec). */\n\tcodec?: GraphCodec;\n}\n\n/** Metadata about an evicted subgraph, exposed via describe(). */\nexport interface EvictedSubgraphInfo {\n\t/** True if currently evicted (serialized, JS objects released). */\n\tevicted: true;\n\t/** Wall-clock ns of last propagation before eviction. */\n\tlastActiveNs: bigint;\n\t/** Size of serialized buffer in bytes. */\n\tserializedBytes: number;\n\t/** Codec used for serialization. */\n\tcodecName: string;\n}\n\n// ---------------------------------------------------------------------------\n// JSON codec (default — wraps current behavior)\n// ---------------------------------------------------------------------------\n\n/**\n * Default JSON codec. Wraps `JSON.stringify`/`JSON.parse` with deterministic\n * key ordering (matching current `snapshot()` behavior).\n */\nexport const JsonCodec: GraphCodec = {\n\tcontentType: \"application/json\",\n\tname: \"json\",\n\n\tencode(snapshot: GraphPersistSnapshot): Uint8Array {\n\t\t// Deterministic: snapshot() already sorts keys.\n\t\tconst json = JSON.stringify(snapshot);\n\t\treturn new TextEncoder().encode(json);\n\t},\n\n\tdecode(buffer: Uint8Array): GraphPersistSnapshot {\n\t\tconst json = new TextDecoder().decode(buffer);\n\t\treturn JSON.parse(json) as GraphPersistSnapshot;\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// DAG-CBOR codec (stub — requires @ipld/dag-cbor)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a DAG-CBOR codec.\n *\n * Requires `@ipld/dag-cbor` as a peer dependency. ~40-50% smaller than JSON,\n * deterministic encoding (required for V1 CID), CID links as native type.\n *\n * @example\n * ```ts\n * import * as dagCbor from \"@ipld/dag-cbor\";\n * const codec = createDagCborCodec(dagCbor);\n * const bytes = codec.encode(graph.snapshot());\n * ```\n */\nexport function createDagCborCodec(dagCbor: {\n\tencode: (value: unknown) => Uint8Array;\n\tdecode: (bytes: Uint8Array) => unknown;\n}): GraphCodec {\n\treturn {\n\t\tcontentType: \"application/dag-cbor\",\n\t\tname: \"dag-cbor\",\n\t\tencode: (snapshot) => dagCbor.encode(snapshot),\n\t\tdecode: (buffer) => dagCbor.decode(buffer) as GraphPersistSnapshot,\n\t};\n}\n\n/**\n * Create a DAG-CBOR + zstd codec. ~80-90% smaller than JSON.\n *\n * Requires `@ipld/dag-cbor` and a zstd implementation (e.g. `fzstd` for\n * browser, `node:zlib` for Node.js).\n *\n * @example\n * ```ts\n * import * as dagCbor from \"@ipld/dag-cbor\";\n * import { compressSync, decompressSync } from \"fzstd\";\n * const codec = createDagCborZstdCodec(dagCbor, { compressSync, decompressSync });\n * ```\n */\nexport function createDagCborZstdCodec(\n\tdagCbor: {\n\t\tencode: (value: unknown) => Uint8Array;\n\t\tdecode: (bytes: Uint8Array) => unknown;\n\t},\n\tzstd: {\n\t\tcompressSync: (data: Uint8Array) => Uint8Array;\n\t\tdecompressSync: (data: Uint8Array) => Uint8Array;\n\t},\n): GraphCodec {\n\treturn {\n\t\tcontentType: \"application/dag-cbor+zstd\",\n\t\tname: \"dag-cbor-zstd\",\n\t\tencode: (snapshot) => zstd.compressSync(dagCbor.encode(snapshot)),\n\t\tdecode: (buffer) => dagCbor.decode(zstd.decompressSync(buffer)) as GraphPersistSnapshot,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Codec negotiation (for peerGraph)\n// ---------------------------------------------------------------------------\n\n/**\n * Negotiate a common codec between two peers.\n *\n * Each peer advertises its supported codecs (ordered by preference).\n * Returns the first codec supported by both, or null if none.\n */\nexport function negotiateCodec(\n\tlocalPreference: readonly GraphCodec[],\n\tremoteContentTypes: readonly string[],\n): GraphCodec | null {\n\tconst remoteSet = new Set(remoteContentTypes);\n\tfor (const codec of localPreference) {\n\t\tif (remoteSet.has(codec.contentType)) return codec;\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// WAL helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Reconstruct a snapshot from a WAL (full snapshot + sequence of deltas).\n *\n * Applies deltas in order on top of the base snapshot. Validates that\n * delta `baseSec` chains correctly.\n */\nexport function replayWAL(entries: readonly WALEntry[]): GraphPersistSnapshot {\n\tif (entries.length === 0) {\n\t\tthrow new Error(\"WAL is empty — need at least one full snapshot\");\n\t}\n\n\tconst first = entries[0]!;\n\tif (first.type !== \"full\") {\n\t\tthrow new Error(\"WAL must start with a full snapshot\");\n\t}\n\n\t// Deep clone the base snapshot so we can mutate it.\n\tconst result: GraphPersistSnapshot = JSON.parse(JSON.stringify(first.snapshot));\n\n\tfor (let i = 1; i < entries.length; i++) {\n\t\tconst entry = entries[i]!;\n\t\tif (entry.type === \"full\") {\n\t\t\t// A compaction point — replace the entire result.\n\t\t\tObject.assign(result, JSON.parse(JSON.stringify(entry.snapshot)));\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst delta = entry.delta;\n\n\t\t// Apply node changes.\n\t\tfor (const [name, patch] of Object.entries(delta.nodes)) {\n\t\t\tif (result.nodes[name]) {\n\t\t\t\tresult.nodes[name]!.value = patch.value;\n\t\t\t\tif (patch.meta) {\n\t\t\t\t\tresult.nodes[name]!.meta = patch.meta;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Apply removals.\n\t\tfor (const name of delta.removed) {\n\t\t\tdelete result.nodes[name];\n\t\t}\n\n\t\t// Apply edge changes.\n\t\tconst edges = [...result.edges];\n\t\tfor (const edge of delta.edgesRemoved) {\n\t\t\tconst idx = edges.findIndex((e) => e.from === edge.from && e.to === edge.to);\n\t\t\tif (idx !== -1) edges.splice(idx, 1);\n\t\t}\n\t\tfor (const edge of delta.edgesAdded) {\n\t\t\tedges.push(edge);\n\t\t}\n\t\t(result as unknown as { edges: typeof edges }).edges = edges;\n\t}\n\n\treturn result;\n}\n","/**\n * Approximate in-memory size estimation for arbitrary JS values.\n *\n * Uses a recursive walk with cycle detection. Not exact — provides a\n * reasonable approximation for profiling and hotspot detection.\n *\n * @module\n * @internal\n */\n\n/** Approximate per-type overhead in bytes (V8 heuristics). */\nconst OVERHEAD = {\n\tobject: 56,\n\tarray: 64,\n\tstring: 40, // header; content added separately\n\tnumber: 8,\n\tboolean: 4,\n\tnull: 0,\n\tundefined: 0,\n\tsymbol: 40,\n\tbigint: 16,\n\tfunction: 120,\n\tmap: 72,\n\tset: 72,\n\tmapEntry: 40,\n\tsetEntry: 24,\n} as const;\n\n/**\n * Estimate the approximate retained memory (in bytes) of a JS value.\n *\n * Handles primitives, plain objects, arrays, Maps, Sets, and nested\n * combinations. Uses a `WeakSet` for cycle detection — cyclic refs\n * are counted once.\n *\n * @param value - The value to measure.\n * @returns Approximate size in bytes.\n */\nexport function sizeof(value: unknown): number {\n\tconst seen = new WeakSet();\n\treturn _sizeof(value, seen);\n}\n\nfunction _sizeof(value: unknown, seen: WeakSet<WeakKey>): number {\n\tif (value == null) return 0;\n\n\tconst t = typeof value;\n\n\tswitch (t) {\n\t\tcase \"number\":\n\t\t\treturn OVERHEAD.number;\n\t\tcase \"boolean\":\n\t\t\treturn OVERHEAD.boolean;\n\t\tcase \"string\":\n\t\t\treturn OVERHEAD.string + (value as string).length * 2; // UTF-16\n\t\tcase \"bigint\":\n\t\t\treturn OVERHEAD.bigint;\n\t\tcase \"symbol\":\n\t\t\treturn OVERHEAD.symbol;\n\t\tcase \"function\":\n\t\t\tif (seen.has(value as object)) return 0;\n\t\t\tseen.add(value as object);\n\t\t\treturn OVERHEAD.function;\n\t\tcase \"undefined\":\n\t\t\treturn 0;\n\t}\n\n\t// Object types — cycle detection\n\tconst obj = value as object;\n\tif (seen.has(obj)) return 0;\n\tseen.add(obj);\n\n\tif (obj instanceof Map) {\n\t\tlet size = OVERHEAD.map;\n\t\tfor (const [k, v] of obj) {\n\t\t\tsize += OVERHEAD.mapEntry + _sizeof(k, seen) + _sizeof(v, seen);\n\t\t}\n\t\treturn size;\n\t}\n\n\tif (obj instanceof Set) {\n\t\tlet size = OVERHEAD.set;\n\t\tfor (const v of obj) {\n\t\t\tsize += OVERHEAD.setEntry + _sizeof(v, seen);\n\t\t}\n\t\treturn size;\n\t}\n\n\tif (Array.isArray(obj)) {\n\t\tlet size = OVERHEAD.array + obj.length * 8; // pointer slots\n\t\tfor (const item of obj) {\n\t\t\tsize += _sizeof(item, seen);\n\t\t}\n\t\treturn size;\n\t}\n\n\t// ArrayBuffer / TypedArray\n\tif (obj instanceof ArrayBuffer) return obj.byteLength;\n\tif (ArrayBuffer.isView(obj)) return (obj as { byteLength: number }).byteLength;\n\n\t// Plain object\n\tlet size = OVERHEAD.object;\n\tconst keys = Object.keys(obj);\n\tfor (const key of keys) {\n\t\tsize += OVERHEAD.string + key.length * 2; // key\n\t\tsize += _sizeof((obj as Record<string, unknown>)[key], seen);\n\t}\n\treturn size;\n}\n","/**\n * Graph profiling and inspection utilities.\n *\n * Provides per-node memory estimation, connectivity stats, and hotspot\n * detection. Non-invasive — reads from `describe()` and node internals\n * without modifying state.\n *\n * @module\n */\n\nimport { NodeImpl } from \"../core/node.js\";\nimport type { Graph, GraphDescribeOutput } from \"./graph.js\";\nimport { sizeof } from \"./sizeof.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Per-node profile entry. */\nexport interface NodeProfile {\n\t/** Qualified path within the graph. */\n\tpath: string;\n\t/** Node type (state, derived, producer, effect). */\n\ttype: string;\n\t/** Node status (disconnected, dirty, settled, errored, completed). */\n\tstatus: string;\n\t/** Approximate retained bytes for the node's cached value. */\n\tvalueSizeBytes: number;\n\t/** Number of downstream subscribers (sinks). */\n\tsubscriberCount: number;\n\t/** Number of upstream dependencies. */\n\tdepCount: number;\n}\n\n/** Aggregate graph profile. */\nexport interface GraphProfileResult {\n\t/** Total node count. */\n\tnodeCount: number;\n\t/** Total edge count. */\n\tedgeCount: number;\n\t/** Subgraph count. */\n\tsubgraphCount: number;\n\t/** All node profiles. */\n\tnodes: NodeProfile[];\n\t/** Total approximate value memory across all nodes. */\n\ttotalValueSizeBytes: number;\n\t/** Nodes sorted by valueSizeBytes descending (top N). */\n\thotspots: NodeProfile[];\n}\n\n/** Options for {@link graphProfile}. */\nexport interface GraphProfileOptions {\n\t/** Limit hotspot list (default 10). */\n\ttopN?: number;\n\t/** Include subgraph nodes recursively (default true). */\n\trecursive?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Profile a graph's memory and connectivity characteristics.\n *\n * Uses `describe({ detail: \"standard\" })` for node metadata and direct\n * `NodeImpl` access for subscriber counts and cached values.\n *\n * @param graph - The graph to profile.\n * @param opts - Optional configuration.\n * @returns Aggregate profile with per-node details and hotspots.\n */\nexport function graphProfile(graph: Graph, opts?: GraphProfileOptions): GraphProfileResult {\n\tconst topN = opts?.topN ?? 10;\n\n\tconst desc: GraphDescribeOutput = graph.describe({ detail: \"standard\" });\n\n\t// Build path→Node lookup via _collectObserveTargets (same as describe uses).\n\t// Runtime guard: if the internal method is missing (refactored), degrade\n\t// gracefully — profiles will show 0 for valueSizeBytes and subscriberCount.\n\tconst targets: [string, import(\"../core/node.js\").Node][] = [];\n\tif (typeof (graph as any)._collectObserveTargets === \"function\") {\n\t\t(graph as any)._collectObserveTargets(\"\", targets);\n\t}\n\tconst pathToNode = new Map<string, import(\"../core/node.js\").Node>();\n\tfor (const [p, n] of targets) {\n\t\tpathToNode.set(p, n);\n\t}\n\n\tconst profiles: NodeProfile[] = [];\n\n\tfor (const [path, nodeDesc] of Object.entries(desc.nodes)) {\n\t\tconst nd = pathToNode.get(path);\n\t\tconst impl = nd instanceof NodeImpl ? nd : null;\n\n\t\tconst valueSizeBytes = impl ? sizeof(impl.get()) : 0;\n\t\tconst subscriberCount = impl ? impl._sinkCount : 0;\n\t\tconst depCount = nodeDesc.deps?.length ?? 0;\n\n\t\tprofiles.push({\n\t\t\tpath,\n\t\t\ttype: nodeDesc.type,\n\t\t\tstatus: nodeDesc.status ?? \"unknown\",\n\t\t\tvalueSizeBytes,\n\t\t\tsubscriberCount,\n\t\t\tdepCount,\n\t\t});\n\t}\n\n\tconst totalValueSizeBytes = profiles.reduce((sum, p) => sum + p.valueSizeBytes, 0);\n\n\tconst hotspots = [...profiles].sort((a, b) => b.valueSizeBytes - a.valueSizeBytes).slice(0, topN);\n\n\treturn {\n\t\tnodeCount: profiles.length,\n\t\tedgeCount: desc.edges.length,\n\t\tsubgraphCount: desc.subgraphs.length,\n\t\tnodes: profiles,\n\t\ttotalValueSizeBytes,\n\t\thotspots,\n\t};\n}\n"],"mappings":"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","names":["size"]}
@@ -2,7 +2,7 @@ import {
2
2
  DynamicNodeImpl,
3
3
  NodeImpl,
4
4
  accessHintForGuard
5
- } from "./chunk-H243FWYP.js";
5
+ } from "./chunk-BV3TPSBK.js";
6
6
 
7
7
  // src/core/meta.ts
8
8
  function resolveDescribeFields(detail, fields) {
@@ -102,4 +102,4 @@ export {
102
102
  metaSnapshot,
103
103
  describeNode
104
104
  };
105
- //# sourceMappingURL=chunk-XQ4UMAU7.js.map
105
+ //# sourceMappingURL=chunk-F2ULI3Q3.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeNode,
3
3
  resolveDescribeFields
4
- } from "./chunk-XQ4UMAU7.js";
4
+ } from "./chunk-F2ULI3Q3.js";
5
5
  import {
6
6
  COMPLETE,
7
7
  DATA,
@@ -16,7 +16,7 @@ import {
16
16
  messageTier,
17
17
  monotonicNs,
18
18
  state
19
- } from "./chunk-H243FWYP.js";
19
+ } from "./chunk-BV3TPSBK.js";
20
20
 
21
21
  // src/graph/graph.ts
22
22
  var PATH_SEP = "::";
@@ -289,6 +289,7 @@ var Graph = class _Graph {
289
289
  /** @internal — exposed for {@link teardownMountedGraph}. */
290
290
  _mounts = /* @__PURE__ */ new Map();
291
291
  _autoCheckpointDisposers = /* @__PURE__ */ new Set();
292
+ _disposers = /* @__PURE__ */ new Set();
292
293
  _defaultVersioningLevel;
293
294
  static registerFactory(pattern, factory) {
294
295
  if (!pattern) {
@@ -1424,10 +1425,33 @@ var Graph = class _Graph {
1424
1425
  // Lifecycle & persistence (§3.7–§3.8)
1425
1426
  // ——————————————————————————————————————————————————————————————
1426
1427
  /**
1427
- * Sends `[[TEARDOWN]]` to all nodes, then clears registries on this graph and every
1428
- * mounted subgraph (§3.7). The instance is left empty and may be reused with {@link Graph.add}.
1428
+ * Register a cleanup function to be called on {@link Graph.destroy}.
1429
+ *
1430
+ * Factories use this to attach teardown logic for internal nodes, keepalive
1431
+ * subscriptions, or other resources that are not registered on the graph and
1432
+ * would otherwise leak on repeated create/destroy cycles.
1433
+ *
1434
+ * Returns a removal function — call it to unregister the disposer early.
1435
+ */
1436
+ addDisposer(fn) {
1437
+ this._disposers.add(fn);
1438
+ return () => {
1439
+ this._disposers.delete(fn);
1440
+ };
1441
+ }
1442
+ /**
1443
+ * Drains disposers (registered via {@link addDisposer}), then sends `[[TEARDOWN]]` to all
1444
+ * nodes and clears registries on this graph and every mounted subgraph (§3.7).
1445
+ * The instance is left empty and may be reused with {@link Graph.add}.
1429
1446
  */
1430
1447
  destroy() {
1448
+ for (const dispose of [...this._disposers]) {
1449
+ try {
1450
+ dispose();
1451
+ } catch {
1452
+ }
1453
+ }
1454
+ this._disposers.clear();
1431
1455
  this.signal([[TEARDOWN]], { internal: true });
1432
1456
  for (const dispose of [...this._autoCheckpointDisposers]) {
1433
1457
  try {
@@ -1902,4 +1926,4 @@ export {
1902
1926
  Graph,
1903
1927
  reachable
1904
1928
  };
1905
- //# sourceMappingURL=chunk-CRACCCJY.js.map
1929
+ //# sourceMappingURL=chunk-IHJHBADD.js.map