@graphrefly/graphrefly 0.18.0 → 0.20.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 (74) hide show
  1. package/dist/{chunk-J7S54G7I.js → chunk-2L5J6RPM.js} +7 -2
  2. package/dist/chunk-2L5J6RPM.js.map +1 -0
  3. package/dist/{chunk-LB3RYLSC.js → chunk-3N2Y6PCR.js} +197 -42
  4. package/dist/chunk-3N2Y6PCR.js.map +1 -0
  5. package/dist/{chunk-KJGUP35I.js → chunk-5PSVTDNZ.js} +22 -9
  6. package/dist/chunk-5PSVTDNZ.js.map +1 -0
  7. package/dist/{chunk-76YPZQTW.js → chunk-BJAOEU4D.js} +34 -29
  8. package/dist/chunk-BJAOEU4D.js.map +1 -0
  9. package/dist/{chunk-UVWEKTYC.js → chunk-IAPLC4NR.js} +3 -3
  10. package/dist/{chunk-F6ORUNO7.js → chunk-OOA2UTXF.js} +58 -2
  11. package/dist/chunk-OOA2UTXF.js.map +1 -0
  12. package/dist/{chunk-TNKODJ6E.js → chunk-PGEU5MEH.js} +7 -3
  13. package/dist/{chunk-TNKODJ6E.js.map → chunk-PGEU5MEH.js.map} +1 -1
  14. package/dist/chunk-R2LPZIY2.js +111 -0
  15. package/dist/chunk-R2LPZIY2.js.map +1 -0
  16. package/dist/{chunk-BV3TPSBK.js → chunk-XYL3GLB3.js} +742 -757
  17. package/dist/chunk-XYL3GLB3.js.map +1 -0
  18. package/dist/compat/nestjs/index.cjs +967 -811
  19. package/dist/compat/nestjs/index.cjs.map +1 -1
  20. package/dist/compat/nestjs/index.d.cts +4 -4
  21. package/dist/compat/nestjs/index.d.ts +4 -4
  22. package/dist/compat/nestjs/index.js +7 -7
  23. package/dist/core/index.cjs +653 -666
  24. package/dist/core/index.cjs.map +1 -1
  25. package/dist/core/index.d.cts +2 -2
  26. package/dist/core/index.d.ts +2 -2
  27. package/dist/core/index.js +7 -3
  28. package/dist/extra/index.cjs +728 -688
  29. package/dist/extra/index.cjs.map +1 -1
  30. package/dist/extra/index.d.cts +4 -4
  31. package/dist/extra/index.d.ts +4 -4
  32. package/dist/extra/index.js +9 -5
  33. package/dist/graph/index.cjs +836 -808
  34. package/dist/graph/index.cjs.map +1 -1
  35. package/dist/graph/index.d.cts +3 -3
  36. package/dist/graph/index.d.ts +3 -3
  37. package/dist/graph/index.js +8 -8
  38. package/dist/{graph-gISB9n3n.d.ts → graph-KsTe57nI.d.cts} +82 -8
  39. package/dist/{graph-BYFlyNpX.d.cts → graph-mILUUqW8.d.ts} +82 -8
  40. package/dist/{index-CgKPpiu8.d.ts → index-8a605sg9.d.ts} +2 -2
  41. package/dist/{index-DKaB2x0T.d.ts → index-B2SvPEbc.d.ts} +6 -65
  42. package/dist/{index-EmzYk-TG.d.cts → index-BHfg_Ez3.d.ts} +123 -77
  43. package/dist/{index-B80mMeuf.d.ts → index-Bc_diYYJ.d.cts} +123 -77
  44. package/dist/{index-B43mC7uY.d.cts → index-BjtlNirP.d.cts} +3 -3
  45. package/dist/{index-CEDaJaYE.d.ts → index-CgSiUouz.d.ts} +3 -3
  46. package/dist/{index-7WnwgjMu.d.ts → index-DuN3bhtm.d.ts} +82 -32
  47. package/dist/{index-D_tUMcpz.d.cts → index-SFzE_KTa.d.cts} +82 -32
  48. package/dist/{index-Ci_vPaVm.d.cts → index-UudxGnzc.d.cts} +6 -65
  49. package/dist/{index-BqOWSFhr.d.cts → index-VHA43cGP.d.cts} +2 -2
  50. package/dist/index.cjs +5936 -5522
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.cts +644 -379
  53. package/dist/index.d.ts +644 -379
  54. package/dist/index.js +4388 -4058
  55. package/dist/index.js.map +1 -1
  56. package/dist/{meta-npl5b97j.d.cts → meta-BnG7XAaE.d.cts} +394 -236
  57. package/dist/{meta-npl5b97j.d.ts → meta-BnG7XAaE.d.ts} +394 -236
  58. package/dist/{observable-DFBCBELR.d.cts → observable-C8Kx_O6k.d.cts} +1 -1
  59. package/dist/{observable-oAGygKvc.d.ts → observable-DcBwQY7t.d.ts} +1 -1
  60. package/dist/patterns/reactive-layout/index.cjs +865 -718
  61. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  62. package/dist/patterns/reactive-layout/index.d.cts +3 -3
  63. package/dist/patterns/reactive-layout/index.d.ts +3 -3
  64. package/dist/patterns/reactive-layout/index.js +4 -4
  65. package/package.json +2 -2
  66. package/dist/chunk-76YPZQTW.js.map +0 -1
  67. package/dist/chunk-BV3TPSBK.js.map +0 -1
  68. package/dist/chunk-F6ORUNO7.js.map +0 -1
  69. package/dist/chunk-FCLROC4Q.js +0 -231
  70. package/dist/chunk-FCLROC4Q.js.map +0 -1
  71. package/dist/chunk-J7S54G7I.js.map +0 -1
  72. package/dist/chunk-KJGUP35I.js.map +0 -1
  73. package/dist/chunk-LB3RYLSC.js.map +0 -1
  74. /package/dist/{chunk-UVWEKTYC.js.map → chunk-IAPLC4NR.js.map} +0 -0
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  DynamicNodeImpl,
3
+ NO_VALUE,
3
4
  NodeImpl,
4
5
  accessHintForGuard
5
- } from "./chunk-BV3TPSBK.js";
6
+ } from "./chunk-XYL3GLB3.js";
6
7
 
7
8
  // src/core/meta.ts
8
9
  function resolveDescribeFields(detail, fields) {
@@ -71,6 +72,10 @@ function describeNode(node, includeFields) {
71
72
  out.name = node.name;
72
73
  }
73
74
  if (all || includeFields.has("value")) {
75
+ const isSentinel = node instanceof NodeImpl && node._cached === NO_VALUE || node instanceof DynamicNodeImpl && node._cached === NO_VALUE;
76
+ if (isSentinel) {
77
+ out.sentinel = true;
78
+ }
74
79
  try {
75
80
  out.value = node.get();
76
81
  } catch {
@@ -101,4 +106,4 @@ export {
101
106
  resolveDescribeFields,
102
107
  describeNode
103
108
  };
104
- //# sourceMappingURL=chunk-J7S54G7I.js.map
109
+ //# sourceMappingURL=chunk-2L5J6RPM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/meta.ts"],"sourcesContent":["import type { Actor } from \"./actor.js\";\nimport { DynamicNodeImpl } from \"./dynamic-node.js\";\nimport { accessHintForGuard } from \"./guard.js\";\nimport { NO_VALUE, type Node, NodeImpl } from \"./node.js\";\n\n/** JSON-shaped slice of a node for Phase 1 `Graph.describe()` (GRAPHREFLY-SPEC §3.6, Appendix B). */\nexport type DescribeNodeOutput = {\n\ttype: \"state\" | \"derived\" | \"producer\" | \"operator\" | \"effect\";\n\tstatus?: Node[\"status\"];\n\tdeps: string[];\n\tmeta?: Record<string, unknown>;\n\tname?: string;\n\tvalue?: unknown;\n\t/** True when the node has never received or been initialized with a value (cache holds SENTINEL). */\n\tsentinel?: boolean;\n\t/** Node versioning info (GRAPHREFLY-SPEC §7). Present only when versioning is enabled. */\n\tv?: { id: string; version: number; cid?: string; prev?: string | null };\n\t/** Guard info (full detail). */\n\tguard?: string;\n\t/** Last mutation attribution (full detail). */\n\tlastMutation?: Readonly<{ actor: Actor; timestamp_ns: number }>;\n};\n\n/**\n * Detail level for `describe()` progressive disclosure (Phase 3.3b).\n * - `\"minimal\"` — type + deps only (default). LLM-friendly.\n * - `\"standard\"` — type, status, value, deps, meta, versioning (`v`).\n * - `\"full\"` — standard + guard, lastMutation.\n */\nexport type DescribeDetail = \"minimal\" | \"standard\" | \"full\";\n\n/**\n * Valid field names for `describe({ fields: [...] })` (Phase 3.3b).\n * Dotted paths like `\"meta.label\"` select specific meta keys.\n */\nexport type DescribeField =\n\t| \"type\"\n\t| \"status\"\n\t| \"value\"\n\t| \"deps\"\n\t| \"meta\"\n\t| \"v\"\n\t| \"guard\"\n\t| \"lastMutation\"\n\t| `meta.${string}`;\n\n/** Resolve which fields to include based on detail level or explicit field list. */\nexport function resolveDescribeFields(\n\tdetail?: DescribeDetail,\n\tfields?: readonly DescribeField[],\n): Set<string> | null {\n\t// Explicit fields override detail level\n\tif (fields != null && fields.length > 0) return new Set(fields);\n\tswitch (detail) {\n\t\tcase \"standard\":\n\t\t\treturn new Set([\"type\", \"status\", \"value\", \"deps\", \"meta\", \"v\"]);\n\t\tcase \"full\":\n\t\t\treturn null; // null = include everything\n\t\tdefault:\n\t\t\treturn new Set([\"type\", \"deps\"]);\n\t}\n}\n\nfunction inferDescribeType(n: NodeImpl): DescribeNodeOutput[\"type\"] {\n\tif (n._describeKind != null) return n._describeKind;\n\tif (!n._hasDeps) return n._fn != null ? \"producer\" : \"state\";\n\tif (n._fn == null) return \"derived\";\n\tif (n._manualEmitUsed) return \"operator\";\n\treturn \"derived\";\n}\n\n/**\n * Reads the current cached value of every companion meta field on a node,\n * suitable for merging into `describe()`-style JSON (GRAPHREFLY-SPEC §2.3, §3.6).\n *\n * @remarks\n * Values come from {@link Node.get}, which returns the **last settled** cache.\n * If a meta field is in `\"dirty\"` status (DIRTY received, DATA pending), the\n * snapshot contains the *previous* value — check `node.meta[key].status` when\n * freshness matters. Avoid calling mid-batch for the same reason.\n *\n * Meta nodes are **not** terminated when their parent receives COMPLETE or\n * ERROR — they remain writable so callers can record post-mortem metadata\n * (e.g. `meta.error`). They *are* torn down when the parent receives TEARDOWN.\n *\n * @param node - The node whose meta fields to snapshot.\n * @returns Plain object of `{ key: value }` pairs (empty if no meta defined).\n * Keys whose companion node's {@link Node.get} throws are omitted.\n *\n * @example\n * ```ts\n * import { core } from \"@graphrefly/graphrefly-ts\";\n *\n * const n = core.node({ initial: 0, meta: { tag: \"a\" } });\n * core.metaSnapshot(n); // { tag: \"a\" }\n * ```\n */\n/** @internal Used by {@link describeNode} — not part of the public API. */\nexport function metaSnapshot(node: Node): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, child] of Object.entries(node.meta)) {\n\t\ttry {\n\t\t\tout[key] = child.get();\n\t\t} catch {\n\t\t\t/* omit key — describe tooling still gets other fields */\n\t\t}\n\t}\n\treturn out;\n}\n\n/**\n * Builds a single-node slice of `Graph.describe()` JSON (structure + `meta` snapshot).\n * Parity with graphrefly-py `describe_node`.\n *\n * `type` is inferred from factory configuration, optional `describeKind` in node options,\n * and the last `manualEmitUsed` hint (operator vs derived). {@link effect} sets\n * `describeKind: \"effect\"`. Nodes not created by {@link node} fall back to `type: \"state\"` and empty `deps`.\n *\n * @param node - Any `Node` to introspect.\n * @returns `DescribeNodeOutput` suitable for merging into graph describe maps.\n *\n * @example\n * ```ts\n * import { describeNode, state } from \"@graphrefly/graphrefly-ts\";\n *\n * describeNode(state(0));\n * ```\n */\n/**\n * Builds a single-node slice for `Graph.describe()`.\n *\n * @param node - Node to introspect.\n * @param includeFields - Set of fields to include, or `null` for all. When omitted, all fields are included (legacy behavior).\n */\n/** @internal Used by {@link Graph.describe} — not part of the public API. */\nexport function describeNode(node: Node, includeFields?: Set<string> | null): DescribeNodeOutput {\n\tconst all = includeFields == null; // null or undefined → include everything\n\n\t// Specific meta keys requested via dotted paths (e.g. \"meta.label\")\n\tconst metaKeys: string[] | null =\n\t\t!all && includeFields != null\n\t\t\t? [...includeFields].filter((f) => f.startsWith(\"meta.\")).map((f) => f.slice(5))\n\t\t\t: null;\n\tconst wantsMeta = all || includeFields!.has(\"meta\") || (metaKeys != null && metaKeys.length > 0);\n\n\tlet type: DescribeNodeOutput[\"type\"] = \"state\";\n\tlet deps: string[] = [];\n\n\tif (node instanceof NodeImpl) {\n\t\ttype = inferDescribeType(node);\n\t\tdeps = node._deps.map((d) => d.name ?? \"\");\n\t} else if (node instanceof DynamicNodeImpl) {\n\t\ttype = node._describeKind ?? \"derived\";\n\t\tdeps = [];\n\t}\n\n\tconst out: DescribeNodeOutput = { type, deps };\n\n\t// status\n\tif (all || includeFields!.has(\"status\")) {\n\t\tout.status = node.status;\n\t}\n\n\t// Resolve guard once — used by both meta.access hint and standalone guard field\n\tconst guard =\n\t\t(node instanceof NodeImpl && node._guard) ||\n\t\t(node instanceof DynamicNodeImpl && node._guard) ||\n\t\tundefined;\n\n\t// meta\n\tif (wantsMeta) {\n\t\tconst rawMeta: Record<string, unknown> = { ...metaSnapshot(node) };\n\t\tif (guard != null && rawMeta.access === undefined) {\n\t\t\trawMeta.access = accessHintForGuard(guard);\n\t\t}\n\n\t\tif (metaKeys != null && metaKeys.length > 0 && !includeFields!.has(\"meta\")) {\n\t\t\t// Only specific meta keys\n\t\t\tconst filtered: Record<string, unknown> = {};\n\t\t\tfor (const k of metaKeys) {\n\t\t\t\tif (k in rawMeta) filtered[k] = rawMeta[k];\n\t\t\t}\n\t\t\tout.meta = filtered;\n\t\t} else {\n\t\t\tout.meta = rawMeta;\n\t\t}\n\t}\n\n\t// name (always include when present — it's identity, not detail)\n\tif (node.name != null) {\n\t\tout.name = node.name;\n\t}\n\n\t// value + sentinel indicator\n\tif (all || includeFields!.has(\"value\")) {\n\t\tconst isSentinel =\n\t\t\t(node instanceof NodeImpl && node._cached === NO_VALUE) ||\n\t\t\t(node instanceof DynamicNodeImpl && node._cached === NO_VALUE);\n\t\tif (isSentinel) {\n\t\t\tout.sentinel = true;\n\t\t}\n\t\ttry {\n\t\t\tout.value = node.get();\n\t\t} catch {\n\t\t\t/* omit value */\n\t\t}\n\t}\n\n\t// Versioning (GRAPHREFLY-SPEC §7)\n\tif ((all || includeFields!.has(\"v\")) && node.v != null) {\n\t\tconst vInfo: DescribeNodeOutput[\"v\"] = { id: node.v.id, version: node.v.version };\n\t\tif (\"cid\" in node.v) {\n\t\t\tvInfo!.cid = (node.v as { cid: string }).cid;\n\t\t\tvInfo!.prev = (node.v as { prev: string | null }).prev;\n\t\t}\n\t\tout.v = vInfo;\n\t}\n\n\t// Guard info (full detail)\n\tif (all || includeFields!.has(\"guard\")) {\n\t\tif (guard != null) {\n\t\t\tout.guard = accessHintForGuard(guard);\n\t\t}\n\t}\n\n\t// Last mutation attribution (full detail)\n\tif (all || includeFields!.has(\"lastMutation\")) {\n\t\tif (node.lastMutation != null) {\n\t\t\tout.lastMutation = node.lastMutation;\n\t\t}\n\t}\n\n\treturn out;\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeNode,
3
3
  resolveDescribeFields
4
- } from "./chunk-J7S54G7I.js";
4
+ } from "./chunk-2L5J6RPM.js";
5
5
  import {
6
6
  COMPLETE,
7
7
  DATA,
@@ -16,7 +16,130 @@ import {
16
16
  messageTier,
17
17
  monotonicNs,
18
18
  state
19
- } from "./chunk-BV3TPSBK.js";
19
+ } from "./chunk-XYL3GLB3.js";
20
+
21
+ // src/graph/sizeof.ts
22
+ var OVERHEAD = {
23
+ object: 56,
24
+ array: 64,
25
+ string: 40,
26
+ // header; content added separately
27
+ number: 8,
28
+ boolean: 4,
29
+ null: 0,
30
+ undefined: 0,
31
+ symbol: 40,
32
+ bigint: 16,
33
+ function: 120,
34
+ map: 72,
35
+ set: 72,
36
+ mapEntry: 40,
37
+ setEntry: 24
38
+ };
39
+ function sizeof(value) {
40
+ const seen = /* @__PURE__ */ new WeakSet();
41
+ return _sizeof(value, seen);
42
+ }
43
+ function _sizeof(value, seen) {
44
+ if (value == null) return 0;
45
+ const t = typeof value;
46
+ switch (t) {
47
+ case "number":
48
+ return OVERHEAD.number;
49
+ case "boolean":
50
+ return OVERHEAD.boolean;
51
+ case "string":
52
+ return OVERHEAD.string + value.length * 2;
53
+ // UTF-16
54
+ case "bigint":
55
+ return OVERHEAD.bigint;
56
+ case "symbol":
57
+ return OVERHEAD.symbol;
58
+ case "function":
59
+ if (seen.has(value)) return 0;
60
+ seen.add(value);
61
+ return OVERHEAD.function;
62
+ case "undefined":
63
+ return 0;
64
+ }
65
+ const obj = value;
66
+ if (seen.has(obj)) return 0;
67
+ seen.add(obj);
68
+ if (obj instanceof Map) {
69
+ let size2 = OVERHEAD.map;
70
+ for (const [k, v] of obj) {
71
+ size2 += OVERHEAD.mapEntry + _sizeof(k, seen) + _sizeof(v, seen);
72
+ }
73
+ return size2;
74
+ }
75
+ if (obj instanceof Set) {
76
+ let size2 = OVERHEAD.set;
77
+ for (const v of obj) {
78
+ size2 += OVERHEAD.setEntry + _sizeof(v, seen);
79
+ }
80
+ return size2;
81
+ }
82
+ if (Array.isArray(obj)) {
83
+ let size2 = OVERHEAD.array + obj.length * 8;
84
+ for (const item of obj) {
85
+ size2 += _sizeof(item, seen);
86
+ }
87
+ return size2;
88
+ }
89
+ if (obj instanceof ArrayBuffer) return obj.byteLength;
90
+ if (ArrayBuffer.isView(obj)) return obj.byteLength;
91
+ let size = OVERHEAD.object;
92
+ const keys = Object.keys(obj);
93
+ for (const key of keys) {
94
+ size += OVERHEAD.string + key.length * 2;
95
+ size += _sizeof(obj[key], seen);
96
+ }
97
+ return size;
98
+ }
99
+
100
+ // src/graph/profile.ts
101
+ function graphProfile(graph, opts) {
102
+ const topN = opts?.topN ?? 10;
103
+ const desc = graph.describe({ detail: "standard" });
104
+ const targets = [];
105
+ if (typeof graph._collectObserveTargets === "function") {
106
+ graph._collectObserveTargets("", targets);
107
+ }
108
+ const pathToNode = /* @__PURE__ */ new Map();
109
+ for (const [p, n] of targets) {
110
+ pathToNode.set(p, n);
111
+ }
112
+ const profiles = [];
113
+ for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
114
+ const nd = pathToNode.get(path);
115
+ const impl = nd instanceof NodeImpl ? nd : null;
116
+ const valueSizeBytes = impl ? sizeof(impl.get()) : 0;
117
+ const subscriberCount = impl ? impl._sinkCount : 0;
118
+ const depCount = nodeDesc.deps?.length ?? 0;
119
+ const isOrphanEffect = nodeDesc.type === "effect" && subscriberCount === 0;
120
+ profiles.push({
121
+ path,
122
+ type: nodeDesc.type,
123
+ status: nodeDesc.status ?? "unknown",
124
+ valueSizeBytes,
125
+ subscriberCount,
126
+ depCount,
127
+ isOrphanEffect
128
+ });
129
+ }
130
+ const totalValueSizeBytes = profiles.reduce((sum, p) => sum + p.valueSizeBytes, 0);
131
+ const hotspots = [...profiles].sort((a, b) => b.valueSizeBytes - a.valueSizeBytes).slice(0, topN);
132
+ const orphanEffects = profiles.filter((p) => p.isOrphanEffect);
133
+ return {
134
+ nodeCount: profiles.length,
135
+ edgeCount: desc.edges.length,
136
+ subgraphCount: desc.subgraphs.length,
137
+ nodes: profiles,
138
+ totalValueSizeBytes,
139
+ hotspots,
140
+ orphanEffects
141
+ };
142
+ }
20
143
 
21
144
  // src/graph/graph.ts
22
145
  var PATH_SEP = "::";
@@ -930,6 +1053,16 @@ var Graph = class _Graph {
930
1053
  }
931
1054
  return out;
932
1055
  }
1056
+ /**
1057
+ * Snapshot-based resource profile: per-node stats, orphan effect detection,
1058
+ * memory hotspots. Zero runtime overhead — walks nodes on demand.
1059
+ *
1060
+ * @param opts - Optional `topN` for hotspot limit (default 10).
1061
+ * @returns Aggregate profile with per-node details, hotspots, and orphan effects.
1062
+ */
1063
+ resourceProfile(opts) {
1064
+ return graphProfile(this, opts);
1065
+ }
933
1066
  _qualifyEdgeEndpoint(part, prefix) {
934
1067
  if (part.includes(PATH_SEP)) return part;
935
1068
  return prefix === "" ? part : `${prefix}${PATH_SEP}${part}`;
@@ -1031,8 +1164,8 @@ var Graph = class _Graph {
1031
1164
  dirtyCount: 0,
1032
1165
  resolvedCount: 0,
1033
1166
  events: [],
1034
- completedCleanly: false,
1035
- errored: false
1167
+ anyCompletedCleanly: false,
1168
+ anyErrored: false
1036
1169
  };
1037
1170
  let lastTriggerDepIndex;
1038
1171
  let lastRunDepValues;
@@ -1076,8 +1209,8 @@ var Graph = class _Graph {
1076
1209
  } else if (minimal) {
1077
1210
  if (t === DIRTY) result.dirtyCount++;
1078
1211
  else if (t === RESOLVED) result.resolvedCount++;
1079
- else if (t === COMPLETE && !result.errored) result.completedCleanly = true;
1080
- else if (t === ERROR) result.errored = true;
1212
+ else if (t === COMPLETE && !result.anyErrored) result.anyCompletedCleanly = true;
1213
+ else if (t === ERROR) result.anyErrored = true;
1081
1214
  } else if (t === DIRTY) {
1082
1215
  result.dirtyCount++;
1083
1216
  result.events.push({ type: "dirty", path, ...base });
@@ -1085,10 +1218,10 @@ var Graph = class _Graph {
1085
1218
  result.resolvedCount++;
1086
1219
  result.events.push({ type: "resolved", path, ...base, ...withCausal });
1087
1220
  } else if (t === COMPLETE) {
1088
- if (!result.errored) result.completedCleanly = true;
1221
+ if (!result.anyErrored) result.anyCompletedCleanly = true;
1089
1222
  result.events.push({ type: "complete", path, ...base });
1090
1223
  } else if (t === ERROR) {
1091
- result.errored = true;
1224
+ result.anyErrored = true;
1092
1225
  result.events.push({ type: "error", path, data: m[1], ...base });
1093
1226
  }
1094
1227
  }
@@ -1108,11 +1241,14 @@ var Graph = class _Graph {
1108
1241
  get events() {
1109
1242
  return result.events;
1110
1243
  },
1111
- get completedCleanly() {
1112
- return result.completedCleanly;
1244
+ get anyCompletedCleanly() {
1245
+ return result.anyCompletedCleanly;
1113
1246
  },
1114
- get errored() {
1115
- return result.errored;
1247
+ get anyErrored() {
1248
+ return result.anyErrored;
1249
+ },
1250
+ get completedWithoutErrors() {
1251
+ return result.anyCompletedCleanly && !result.anyErrored;
1116
1252
  },
1117
1253
  dispose() {
1118
1254
  unsub();
@@ -1148,9 +1284,10 @@ var Graph = class _Graph {
1148
1284
  dirtyCount: 0,
1149
1285
  resolvedCount: 0,
1150
1286
  events: [],
1151
- completedCleanly: false,
1152
- errored: false
1287
+ anyCompletedCleanly: false,
1288
+ anyErrored: false
1153
1289
  };
1290
+ const nodeErrored = /* @__PURE__ */ new Set();
1154
1291
  const actor = options.actor;
1155
1292
  const targets = [];
1156
1293
  this._collectObserveTargets("", targets);
@@ -1169,8 +1306,11 @@ var Graph = class _Graph {
1169
1306
  } else if (minimal) {
1170
1307
  if (t === DIRTY) result.dirtyCount++;
1171
1308
  else if (t === RESOLVED) result.resolvedCount++;
1172
- else if (t === COMPLETE && !result.errored) result.completedCleanly = true;
1173
- else if (t === ERROR) result.errored = true;
1309
+ else if (t === COMPLETE && !nodeErrored.has(path)) result.anyCompletedCleanly = true;
1310
+ else if (t === ERROR) {
1311
+ result.anyErrored = true;
1312
+ nodeErrored.add(path);
1313
+ }
1174
1314
  } else if (t === DIRTY) {
1175
1315
  result.dirtyCount++;
1176
1316
  result.events.push({ type: "dirty", path, ...base });
@@ -1178,10 +1318,11 @@ var Graph = class _Graph {
1178
1318
  result.resolvedCount++;
1179
1319
  result.events.push({ type: "resolved", path, ...base });
1180
1320
  } else if (t === COMPLETE) {
1181
- if (!result.errored) result.completedCleanly = true;
1321
+ if (!nodeErrored.has(path)) result.anyCompletedCleanly = true;
1182
1322
  result.events.push({ type: "complete", path, ...base });
1183
1323
  } else if (t === ERROR) {
1184
- result.errored = true;
1324
+ result.anyErrored = true;
1325
+ nodeErrored.add(path);
1185
1326
  result.events.push({ type: "error", path, data: m[1], ...base });
1186
1327
  }
1187
1328
  }
@@ -1201,11 +1342,14 @@ var Graph = class _Graph {
1201
1342
  get events() {
1202
1343
  return result.events;
1203
1344
  },
1204
- get completedCleanly() {
1205
- return result.completedCleanly;
1345
+ get anyCompletedCleanly() {
1346
+ return result.anyCompletedCleanly;
1347
+ },
1348
+ get anyErrored() {
1349
+ return result.anyErrored;
1206
1350
  },
1207
- get errored() {
1208
- return result.errored;
1351
+ get completedWithoutErrors() {
1352
+ return result.anyCompletedCleanly && !result.anyErrored;
1209
1353
  },
1210
1354
  dispose() {
1211
1355
  for (const u of unsubs) u();
@@ -1237,8 +1381,8 @@ var Graph = class _Graph {
1237
1381
  dirtyCount: 0,
1238
1382
  resolvedCount: 0,
1239
1383
  events: [],
1240
- completedCleanly: false,
1241
- errored: false
1384
+ anyCompletedCleanly: false,
1385
+ anyErrored: false
1242
1386
  };
1243
1387
  const target = this.resolve(path);
1244
1388
  let batchSeq = 0;
@@ -1257,10 +1401,10 @@ var Graph = class _Graph {
1257
1401
  acc.resolvedCount++;
1258
1402
  acc.events.push({ type: "resolved", path, ...base });
1259
1403
  } else if (t === COMPLETE) {
1260
- if (!acc.errored) acc.completedCleanly = true;
1404
+ if (!acc.anyErrored) acc.anyCompletedCleanly = true;
1261
1405
  acc.events.push({ type: "complete", path, ...base });
1262
1406
  } else if (t === ERROR) {
1263
- acc.errored = true;
1407
+ acc.anyErrored = true;
1264
1408
  acc.events.push({ type: "error", path, data: m[1], ...base });
1265
1409
  }
1266
1410
  }
@@ -1278,11 +1422,14 @@ var Graph = class _Graph {
1278
1422
  get events() {
1279
1423
  return acc.events;
1280
1424
  },
1281
- get completedCleanly() {
1282
- return acc.completedCleanly;
1425
+ get anyCompletedCleanly() {
1426
+ return acc.anyCompletedCleanly;
1283
1427
  },
1284
- get errored() {
1285
- return acc.errored;
1428
+ get anyErrored() {
1429
+ return acc.anyErrored;
1430
+ },
1431
+ get completedWithoutErrors() {
1432
+ return acc.anyCompletedCleanly && !acc.anyErrored;
1286
1433
  },
1287
1434
  dispose() {
1288
1435
  unsub();
@@ -1303,9 +1450,10 @@ var Graph = class _Graph {
1303
1450
  dirtyCount: 0,
1304
1451
  resolvedCount: 0,
1305
1452
  events: [],
1306
- completedCleanly: false,
1307
- errored: false
1453
+ anyCompletedCleanly: false,
1454
+ anyErrored: false
1308
1455
  };
1456
+ const nodeErrored = /* @__PURE__ */ new Set();
1309
1457
  const targets = [];
1310
1458
  this._collectObserveTargets("", targets);
1311
1459
  targets.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
@@ -1327,10 +1475,11 @@ var Graph = class _Graph {
1327
1475
  acc.resolvedCount++;
1328
1476
  acc.events.push({ type: "resolved", path, ...base });
1329
1477
  } else if (t === COMPLETE) {
1330
- if (!acc.errored) acc.completedCleanly = true;
1478
+ if (!nodeErrored.has(path)) acc.anyCompletedCleanly = true;
1331
1479
  acc.events.push({ type: "complete", path, ...base });
1332
1480
  } else if (t === ERROR) {
1333
- acc.errored = true;
1481
+ acc.anyErrored = true;
1482
+ nodeErrored.add(path);
1334
1483
  acc.events.push({ type: "error", path, data: m[1], ...base });
1335
1484
  }
1336
1485
  }
@@ -1349,11 +1498,14 @@ var Graph = class _Graph {
1349
1498
  get events() {
1350
1499
  return acc.events;
1351
1500
  },
1352
- get completedCleanly() {
1353
- return acc.completedCleanly;
1501
+ get anyCompletedCleanly() {
1502
+ return acc.anyCompletedCleanly;
1503
+ },
1504
+ get anyErrored() {
1505
+ return acc.anyErrored;
1354
1506
  },
1355
- get errored() {
1356
- return acc.errored;
1507
+ get completedWithoutErrors() {
1508
+ return acc.anyCompletedCleanly && !acc.anyErrored;
1357
1509
  },
1358
1510
  dispose() {
1359
1511
  for (const u of unsubs) u();
@@ -1679,8 +1831,9 @@ var Graph = class _Graph {
1679
1831
  /**
1680
1832
  * Debounced persistence wired to graph-wide observe stream (spec §3.8 auto-checkpoint).
1681
1833
  *
1682
- * Checkpoint trigger uses {@link messageTier}: only batches containing tier >= 2 messages
1683
- * schedule a save (`DATA`/`RESOLVED`/terminal/destruction), never pure tier-0/1 control waves.
1834
+ * Checkpoint trigger uses {@link messageTier}: only batches containing tier >= 3 messages
1835
+ * schedule a save (`DATA`/`RESOLVED`/terminal/destruction), never pure tier-0/1/2 control
1836
+ * waves (`START`/`DIRTY`/`INVALIDATE`/`PAUSE`/`RESUME`).
1684
1837
  */
1685
1838
  autoCheckpoint(adapter, options = {}) {
1686
1839
  const debounceMs = Math.max(0, options.debounceMs ?? 500);
@@ -1727,7 +1880,7 @@ var Graph = class _Graph {
1727
1880
  timer = setTimeout(flush, debounceMs);
1728
1881
  };
1729
1882
  const off = this.observe().subscribe((path, messages) => {
1730
- const triggeredByTier = messages.some((m) => messageTier(m[0]) >= 2);
1883
+ const triggeredByTier = messages.some((m) => messageTier(m[0]) >= 3);
1731
1884
  if (!triggeredByTier) return;
1732
1885
  if (options.filter) {
1733
1886
  const nd = this.resolve(path);
@@ -1955,8 +2108,10 @@ function reachable(described, from, direction, options = {}) {
1955
2108
  }
1956
2109
 
1957
2110
  export {
2111
+ sizeof,
2112
+ graphProfile,
1958
2113
  GRAPH_META_SEGMENT,
1959
2114
  Graph,
1960
2115
  reachable
1961
2116
  };
1962
- //# sourceMappingURL=chunk-LB3RYLSC.js.map
2117
+ //# sourceMappingURL=chunk-3N2Y6PCR.js.map