@graphrefly/graphrefly 0.6.0 → 0.8.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/README.md +30 -14
  2. package/dist/{chunk-HP7OKEOE.js → chunk-A2AJJOSJ.js} +3 -3
  3. package/dist/chunk-A2AJJOSJ.js.map +1 -0
  4. package/dist/{chunk-CP6MNKAA.js → chunk-E7OH6ZAZ.js} +10 -4
  5. package/dist/{chunk-CP6MNKAA.js.map → chunk-E7OH6ZAZ.js.map} +1 -1
  6. package/dist/chunk-LR2CLSEF.js +106 -0
  7. package/dist/chunk-LR2CLSEF.js.map +1 -0
  8. package/dist/{chunk-5X3LAO3B.js → chunk-QTZSBQGJ.js} +79 -20
  9. package/dist/chunk-QTZSBQGJ.js.map +1 -0
  10. package/dist/{chunk-V3UACY6A.js → chunk-TZLX4KIT.js} +790 -203
  11. package/dist/chunk-TZLX4KIT.js.map +1 -0
  12. package/dist/{chunk-QW7H3ICI.js → chunk-UCW3VWMN.js} +4 -4
  13. package/dist/{chunk-6W5SGIGB.js → chunk-WYI7YW54.js} +142 -30
  14. package/dist/chunk-WYI7YW54.js.map +1 -0
  15. package/dist/chunk-WZ2Z2CRV.js +32 -0
  16. package/dist/chunk-WZ2Z2CRV.js.map +1 -0
  17. package/dist/{chunk-Z4Y4FMQN.js → chunk-XCZPGOVP.js} +7 -7
  18. package/dist/{chunk-KWXPDASV.js → chunk-YWTP2XRJ.js} +2 -2
  19. package/dist/compat/nestjs/index.cjs +268 -61
  20. package/dist/compat/nestjs/index.cjs.map +1 -1
  21. package/dist/compat/nestjs/index.d.cts +4 -4
  22. package/dist/compat/nestjs/index.d.ts +4 -4
  23. package/dist/compat/nestjs/index.js +8 -7
  24. package/dist/core/index.cjs +163 -35
  25. package/dist/core/index.cjs.map +1 -1
  26. package/dist/core/index.d.cts +2 -2
  27. package/dist/core/index.d.ts +2 -2
  28. package/dist/core/index.js +10 -4
  29. package/dist/extra/index.cjs +892 -221
  30. package/dist/extra/index.cjs.map +1 -1
  31. package/dist/extra/index.d.cts +4 -4
  32. package/dist/extra/index.d.ts +4 -4
  33. package/dist/extra/index.js +22 -3
  34. package/dist/graph/index.cjs +268 -61
  35. package/dist/graph/index.cjs.map +1 -1
  36. package/dist/graph/index.d.cts +3 -3
  37. package/dist/graph/index.d.ts +3 -3
  38. package/dist/graph/index.js +4 -4
  39. package/dist/{graph-CL_ZDAj9.d.cts → graph-DqTICAY2.d.cts} +69 -12
  40. package/dist/{graph-D18qmsNm.d.ts → graph-X9uwnD_z.d.ts} +69 -12
  41. package/dist/{index-C3BMRmmp.d.cts → index-3U0WxdD-.d.cts} +3 -3
  42. package/dist/{index-Bk_idZm1.d.cts → index-BP1t_38S.d.cts} +406 -61
  43. package/dist/{index-BtK55IE2.d.ts → index-BPCeYDS4.d.ts} +4 -2
  44. package/dist/{index-Bvy_6CaN.d.ts → index-BVG5pjin.d.ts} +50 -5
  45. package/dist/{index-C5mqLhMX.d.cts → index-BYEgosAX.d.cts} +50 -5
  46. package/dist/{index-D_geH2Bm.d.cts → index-BYa2YMat.d.cts} +3 -3
  47. package/dist/{index-CP_QvbWu.d.ts → index-DLO8wnYU.d.ts} +3 -3
  48. package/dist/{index-B7eOdgEx.d.ts → index-DMv1Etbi.d.ts} +3 -3
  49. package/dist/{index-BvhgZRHK.d.cts → index-DbwgQ4Cw.d.cts} +4 -2
  50. package/dist/{index-B2jmzVxL.d.ts → index-a5gHmH5b.d.ts} +406 -61
  51. package/dist/index.cjs +2966 -1790
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.cts +112 -14
  54. package/dist/index.d.ts +112 -14
  55. package/dist/index.js +385 -20
  56. package/dist/index.js.map +1 -1
  57. package/dist/{meta-BsF6Sag9.d.cts → meta-BJEU8fYz.d.cts} +31 -4
  58. package/dist/{meta-BsF6Sag9.d.ts → meta-BJEU8fYz.d.ts} +31 -4
  59. package/dist/patterns/reactive-layout/index.cjs +268 -61
  60. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  61. package/dist/patterns/reactive-layout/index.d.cts +3 -3
  62. package/dist/patterns/reactive-layout/index.d.ts +3 -3
  63. package/dist/patterns/reactive-layout/index.js +4 -4
  64. package/dist/{reactive-log-BfvfNWQh.d.cts → reactive-log-BfX6bOSZ.d.cts} +2 -2
  65. package/dist/{reactive-log-ohLmTXoZ.d.ts → reactive-log-RhgIog2Z.d.ts} +2 -2
  66. package/package.json +29 -18
  67. package/dist/chunk-5X3LAO3B.js.map +0 -1
  68. package/dist/chunk-6W5SGIGB.js.map +0 -1
  69. package/dist/chunk-HP7OKEOE.js.map +0 -1
  70. package/dist/chunk-O3PI7W45.js +0 -68
  71. package/dist/chunk-O3PI7W45.js.map +0 -1
  72. package/dist/chunk-V3UACY6A.js.map +0 -1
  73. /package/dist/{chunk-QW7H3ICI.js.map → chunk-UCW3VWMN.js.map} +0 -0
  74. /package/dist/{chunk-Z4Y4FMQN.js.map → chunk-XCZPGOVP.js.map} +0 -0
  75. /package/dist/{chunk-KWXPDASV.js.map → chunk-YWTP2XRJ.js.map} +0 -0
@@ -159,10 +159,14 @@ function partitionForBatch(messages) {
159
159
  }
160
160
  return { immediate, deferred, terminal };
161
161
  }
162
- function emitWithBatch(emit, messages, phase = 2) {
162
+ function emitWithBatch(emit, messages, phase = 2, options) {
163
163
  if (messages.length === 0) {
164
164
  return;
165
165
  }
166
+ if (options?.strategy === "sequential") {
167
+ _emitSequential(emit, messages, phase);
168
+ return;
169
+ }
166
170
  const queue = phase === 3 ? pendingPhase3 : pendingPhase2;
167
171
  if (messages.length === 1) {
168
172
  const t = messages[0][0];
@@ -203,6 +207,29 @@ function emitWithBatch(emit, messages, phase = 2) {
203
207
  }
204
208
  }
205
209
  }
210
+ function _emitSequential(emit, messages, phase = 2) {
211
+ const dataQueue = phase === 3 ? pendingPhase3 : pendingPhase2;
212
+ for (const msg of messages) {
213
+ const tier = messageTier(msg[0]);
214
+ if (tier === 2) {
215
+ if (isBatching()) {
216
+ const m = msg;
217
+ dataQueue.push(() => emit([m]));
218
+ } else {
219
+ emit([msg]);
220
+ }
221
+ } else if (tier >= 3) {
222
+ if (isBatching()) {
223
+ const m = msg;
224
+ pendingPhase3.push(() => emit([m]));
225
+ } else {
226
+ emit([msg]);
227
+ }
228
+ } else {
229
+ emit([msg]);
230
+ }
231
+ }
232
+ }
206
233
 
207
234
  // src/core/clock.ts
208
235
  function monotonicNs() {
@@ -311,6 +338,7 @@ function advanceVersion(info, newValue, hashFn) {
311
338
  }
312
339
 
313
340
  // src/core/node.ts
341
+ var NO_VALUE = /* @__PURE__ */ Symbol.for("graphrefly/NO_VALUE");
314
342
  function createIntBitSet() {
315
343
  let bits = 0;
316
344
  return {
@@ -440,10 +468,10 @@ var NodeImpl = class {
440
468
  this._hasDeps = deps.length > 0;
441
469
  this._autoComplete = opts.completeWhenDepsComplete ?? true;
442
470
  this._isSingleDep = deps.length === 1 && fn != null;
443
- this._cached = opts.initial;
471
+ this._cached = "initial" in opts ? opts.initial : NO_VALUE;
444
472
  this._status = this._hasDeps ? "disconnected" : "settled";
445
473
  this._hashFn = opts.versioningHash ?? defaultHash;
446
- this._versioning = opts.versioning != null ? createVersioning(opts.versioning, this._cached, {
474
+ this._versioning = opts.versioning != null ? createVersioning(opts.versioning, this._cached === NO_VALUE ? void 0 : this._cached, {
447
475
  id: opts.versioningId,
448
476
  hash: this._hashFn
449
477
  }) : void 0;
@@ -527,10 +555,14 @@ var NodeImpl = class {
527
555
  _applyVersioning(level, opts) {
528
556
  if (this._versioning != null) return;
529
557
  this._hashFn = opts?.hash ?? this._hashFn;
530
- this._versioning = createVersioning(level, this._cached, {
531
- id: opts?.id,
532
- hash: this._hashFn
533
- });
558
+ this._versioning = createVersioning(
559
+ level,
560
+ this._cached === NO_VALUE ? void 0 : this._cached,
561
+ {
562
+ id: opts?.id,
563
+ hash: this._hashFn
564
+ }
565
+ );
534
566
  }
535
567
  hasGuard() {
536
568
  return this._guard != null;
@@ -540,7 +572,7 @@ var NodeImpl = class {
540
572
  return this._guard(normalizeActor(actor), "observe");
541
573
  }
542
574
  get() {
543
- return this._cached;
575
+ return this._cached === NO_VALUE ? void 0 : this._cached;
544
576
  }
545
577
  down(messages, options) {
546
578
  if (messages.length === 0) return;
@@ -597,6 +629,7 @@ var NodeImpl = class {
597
629
  }
598
630
  if (this._terminal && this._opts.resubscribable) {
599
631
  this._terminal = false;
632
+ this._cached = NO_VALUE;
600
633
  this._status = this._hasDeps ? "disconnected" : "settled";
601
634
  this._opts.onResubscribe?.();
602
635
  }
@@ -696,7 +729,7 @@ var NodeImpl = class {
696
729
  const cleanupFn = this._cleanup;
697
730
  this._cleanup = void 0;
698
731
  cleanupFn?.();
699
- this._cached = void 0;
732
+ this._cached = NO_VALUE;
700
733
  this._lastDepValues = void 0;
701
734
  }
702
735
  this._status = statusAfterMessage(this._status, m);
@@ -705,7 +738,7 @@ var NodeImpl = class {
705
738
  }
706
739
  if (t === TEARDOWN) {
707
740
  if (this._opts.resetOnTeardown) {
708
- this._cached = void 0;
741
+ this._cached = NO_VALUE;
709
742
  }
710
743
  const teardownCleanup = this._cleanup;
711
744
  this._cleanup = void 0;
@@ -736,7 +769,15 @@ var NodeImpl = class {
736
769
  }
737
770
  _emitAutoValue(value) {
738
771
  const wasDirty = this._status === "dirty";
739
- const unchanged = this._equals(this._cached, value);
772
+ let unchanged;
773
+ try {
774
+ unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
775
+ } catch (eqErr) {
776
+ const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
777
+ const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
778
+ this._downInternal([[ERROR, wrapped]]);
779
+ return;
780
+ }
740
781
  if (unchanged) {
741
782
  this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
742
783
  return;
@@ -783,7 +824,9 @@ var NodeImpl = class {
783
824
  if (out === void 0) return;
784
825
  this._emitAutoValue(out);
785
826
  } catch (err) {
786
- this._downInternal([[ERROR, err]]);
827
+ const errMsg = err instanceof Error ? err.message : String(err);
828
+ const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
829
+ this._downInternal([[ERROR, wrapped]]);
787
830
  }
788
831
  }
789
832
  _onDepDirty(index) {
@@ -818,7 +861,11 @@ var NodeImpl = class {
818
861
  try {
819
862
  if (this._onMessage(msg, index, this._actions)) continue;
820
863
  } catch (err) {
821
- this._downInternal([[ERROR, err]]);
864
+ const errMsg = err instanceof Error ? err.message : String(err);
865
+ const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
866
+ cause: err
867
+ });
868
+ this._downInternal([[ERROR, wrapped]]);
822
869
  return;
823
870
  }
824
871
  }
@@ -951,7 +998,7 @@ var DynamicNodeImpl = class {
951
998
  _actions;
952
999
  _boundEmitToSinks;
953
1000
  // Mutable state
954
- _cached;
1001
+ _cached = NO_VALUE;
955
1002
  _status = "disconnected";
956
1003
  _terminal = false;
957
1004
  _connected = false;
@@ -1045,7 +1092,7 @@ var DynamicNodeImpl = class {
1045
1092
  return this._guard(normalizeActor(actor), "observe");
1046
1093
  }
1047
1094
  get() {
1048
- return this._cached;
1095
+ return this._cached === NO_VALUE ? void 0 : this._cached;
1049
1096
  }
1050
1097
  down(messages, options) {
1051
1098
  if (messages.length === 0) return;
@@ -1103,6 +1150,7 @@ var DynamicNodeImpl = class {
1103
1150
  }
1104
1151
  if (this._terminal && this._resubscribable) {
1105
1152
  this._terminal = false;
1153
+ this._cached = NO_VALUE;
1106
1154
  this._status = "disconnected";
1107
1155
  this._onResubscribe?.();
1108
1156
  }
@@ -1180,10 +1228,13 @@ var DynamicNodeImpl = class {
1180
1228
  const t = m[0];
1181
1229
  if (t === DATA) this._cached = m[1];
1182
1230
  if (t === INVALIDATE) {
1183
- this._cached = void 0;
1231
+ this._cached = NO_VALUE;
1232
+ this._status = "dirty";
1184
1233
  }
1185
- if (t === DATA || t === RESOLVED) {
1234
+ if (t === DATA) {
1186
1235
  this._status = "settled";
1236
+ } else if (t === RESOLVED) {
1237
+ this._status = "resolved";
1187
1238
  } else if (t === DIRTY) {
1188
1239
  this._status = "dirty";
1189
1240
  } else if (t === COMPLETE) {
@@ -1194,7 +1245,7 @@ var DynamicNodeImpl = class {
1194
1245
  this._terminal = true;
1195
1246
  }
1196
1247
  if (t === TEARDOWN) {
1197
- if (this._resetOnTeardown) this._cached = void 0;
1248
+ if (this._resetOnTeardown) this._cached = NO_VALUE;
1198
1249
  try {
1199
1250
  this._propagateToMeta(t);
1200
1251
  } finally {
@@ -1217,7 +1268,15 @@ var DynamicNodeImpl = class {
1217
1268
  }
1218
1269
  _emitAutoValue(value) {
1219
1270
  const wasDirty = this._status === "dirty";
1220
- const unchanged = this._equals(this._cached, value);
1271
+ let unchanged;
1272
+ try {
1273
+ unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
1274
+ } catch (eqErr) {
1275
+ const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
1276
+ const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
1277
+ this._downInternal([[ERROR, wrapped]]);
1278
+ return;
1279
+ }
1221
1280
  if (unchanged) {
1222
1281
  this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
1223
1282
  return;
@@ -1383,6 +1442,19 @@ var DynamicNodeImpl = class {
1383
1442
  };
1384
1443
 
1385
1444
  // src/core/meta.ts
1445
+ function resolveDescribeFields(detail, fields) {
1446
+ if (fields != null && fields.length > 0) return new Set(fields);
1447
+ switch (detail) {
1448
+ case "standard":
1449
+ return /* @__PURE__ */ new Set(["type", "status", "value", "deps", "meta", "v"]);
1450
+ case "full":
1451
+ return null;
1452
+ // null = include everything
1453
+ case "minimal":
1454
+ default:
1455
+ return /* @__PURE__ */ new Set(["type", "deps"]);
1456
+ }
1457
+ }
1386
1458
  function inferDescribeType(n) {
1387
1459
  if (n._describeKind != null) return n._describeKind;
1388
1460
  if (!n._hasDeps) return n._fn != null ? "producer" : "state";
@@ -1400,12 +1472,10 @@ function metaSnapshot(node2) {
1400
1472
  }
1401
1473
  return out;
1402
1474
  }
1403
- function describeNode(node2) {
1404
- const meta = { ...metaSnapshot(node2) };
1405
- const guard = node2 instanceof NodeImpl && node2._guard || node2 instanceof DynamicNodeImpl && node2._guard || void 0;
1406
- if (guard != null && meta.access === void 0) {
1407
- meta.access = accessHintForGuard(guard);
1408
- }
1475
+ function describeNode(node2, includeFields) {
1476
+ const all = includeFields == null;
1477
+ const metaKeys = !all && includeFields != null ? [...includeFields].filter((f) => f.startsWith("meta.")).map((f) => f.slice(5)) : null;
1478
+ const wantsMeta = all || includeFields.has("meta") || metaKeys != null && metaKeys.length > 0;
1409
1479
  let type = "state";
1410
1480
  let deps = [];
1411
1481
  if (node2 instanceof NodeImpl) {
@@ -1415,20 +1485,36 @@ function describeNode(node2) {
1415
1485
  type = node2._describeKind ?? "derived";
1416
1486
  deps = [];
1417
1487
  }
1418
- const out = {
1419
- type,
1420
- status: node2.status,
1421
- deps,
1422
- meta
1423
- };
1488
+ const out = { type, deps };
1489
+ if (all || includeFields.has("status")) {
1490
+ out.status = node2.status;
1491
+ }
1492
+ const guard = node2 instanceof NodeImpl && node2._guard || node2 instanceof DynamicNodeImpl && node2._guard || void 0;
1493
+ if (wantsMeta) {
1494
+ const rawMeta = { ...metaSnapshot(node2) };
1495
+ if (guard != null && rawMeta.access === void 0) {
1496
+ rawMeta.access = accessHintForGuard(guard);
1497
+ }
1498
+ if (metaKeys != null && metaKeys.length > 0 && !includeFields.has("meta")) {
1499
+ const filtered = {};
1500
+ for (const k of metaKeys) {
1501
+ if (k in rawMeta) filtered[k] = rawMeta[k];
1502
+ }
1503
+ out.meta = filtered;
1504
+ } else {
1505
+ out.meta = rawMeta;
1506
+ }
1507
+ }
1424
1508
  if (node2.name != null) {
1425
1509
  out.name = node2.name;
1426
1510
  }
1427
- try {
1428
- out.value = node2.get();
1429
- } catch {
1511
+ if (all || includeFields.has("value")) {
1512
+ try {
1513
+ out.value = node2.get();
1514
+ } catch {
1515
+ }
1430
1516
  }
1431
- if (node2.v != null) {
1517
+ if ((all || includeFields.has("v")) && node2.v != null) {
1432
1518
  const vInfo = { id: node2.v.id, version: node2.v.version };
1433
1519
  if ("cid" in node2.v) {
1434
1520
  vInfo.cid = node2.v.cid;
@@ -1436,6 +1522,16 @@ function describeNode(node2) {
1436
1522
  }
1437
1523
  out.v = vInfo;
1438
1524
  }
1525
+ if (all || includeFields.has("guard")) {
1526
+ if (guard != null) {
1527
+ out.guard = accessHintForGuard(guard);
1528
+ }
1529
+ }
1530
+ if (all || includeFields.has("lastMutation")) {
1531
+ if (node2.lastMutation != null) {
1532
+ out.lastMutation = node2.lastMutation;
1533
+ }
1534
+ }
1439
1535
  return out;
1440
1536
  }
1441
1537
 
@@ -1630,6 +1726,23 @@ function resolveSpyTheme(theme) {
1630
1726
  reset: theme.reset ?? ""
1631
1727
  };
1632
1728
  }
1729
+ function resolveObserveDetail(opts) {
1730
+ if (opts == null) return {};
1731
+ const detail = opts.detail;
1732
+ if (detail === "full") {
1733
+ return {
1734
+ ...opts,
1735
+ structured: opts.structured ?? true,
1736
+ timeline: opts.timeline ?? true,
1737
+ causal: opts.causal ?? true,
1738
+ derived: opts.derived ?? true
1739
+ };
1740
+ }
1741
+ if (detail === "minimal") {
1742
+ return { ...opts, structured: opts.structured ?? true };
1743
+ }
1744
+ return opts;
1745
+ }
1633
1746
  function assertLocalName(name, graphName, label) {
1634
1747
  if (name === "") {
1635
1748
  throw new Error(`Graph "${graphName}": ${label} name must be non-empty`);
@@ -1827,6 +1940,22 @@ var Graph = class _Graph {
1827
1940
  if (this._defaultVersioningLevel != null) {
1828
1941
  node2._applyVersioning(this._defaultVersioningLevel);
1829
1942
  }
1943
+ if (node2._deps.length > 0) {
1944
+ for (const dep of node2._deps) {
1945
+ for (const [depName, depNode] of this._nodes) {
1946
+ if (depNode === dep) {
1947
+ this._edges.add(edgeKey(depName, name));
1948
+ break;
1949
+ }
1950
+ }
1951
+ }
1952
+ }
1953
+ for (const [otherName, otherNode] of this._nodes) {
1954
+ if (otherName === name) continue;
1955
+ if (otherNode instanceof NodeImpl && otherNode._deps.includes(node2)) {
1956
+ this._edges.add(edgeKey(name, otherName));
1957
+ }
1958
+ }
1830
1959
  }
1831
1960
  }
1832
1961
  /**
@@ -2213,6 +2342,9 @@ var Graph = class _Graph {
2213
2342
  describe(options) {
2214
2343
  const actor = options?.actor;
2215
2344
  const filter = options?.filter;
2345
+ const includeFields = resolveDescribeFields(options?.detail, options?.fields);
2346
+ const isSpec = options?.format === "spec";
2347
+ const effectiveFields = isSpec ? resolveDescribeFields("minimal") : includeFields;
2216
2348
  const targets = [];
2217
2349
  this._collectObserveTargets("", targets);
2218
2350
  const nodeToPath = /* @__PURE__ */ new Map();
@@ -2222,7 +2354,7 @@ var Graph = class _Graph {
2222
2354
  const nodes = {};
2223
2355
  for (const [p, n] of targets) {
2224
2356
  if (actor != null && !n.allowsObserve(actor)) continue;
2225
- const raw = describeNode(n);
2357
+ const raw = describeNode(n, effectiveFields);
2226
2358
  const deps = n instanceof NodeImpl ? n._deps.map((d) => nodeToPath.get(d) ?? d.name ?? "") : [];
2227
2359
  const { name: _name, ...rest } = raw;
2228
2360
  const entry = { ...rest, deps };
@@ -2243,7 +2375,7 @@ var Graph = class _Graph {
2243
2375
  continue;
2244
2376
  }
2245
2377
  if (normalizedKey === "metaHas") {
2246
- if (!Object.hasOwn(entry.meta, String(fv))) {
2378
+ if (!Object.hasOwn(entry.meta ?? {}, String(fv))) {
2247
2379
  match = false;
2248
2380
  break;
2249
2381
  }
@@ -2276,11 +2408,24 @@ var Graph = class _Graph {
2276
2408
  const prefix = `${sg}${PATH_SEP}`;
2277
2409
  return [...nodeKeys].some((k) => k === sg || k.startsWith(prefix));
2278
2410
  }) : allSubgraphs;
2411
+ const graph = this;
2412
+ const baseOpts = options;
2279
2413
  return {
2280
2414
  name: this.name,
2281
2415
  nodes,
2282
2416
  edges,
2283
- subgraphs
2417
+ subgraphs,
2418
+ expand(detailOrFields) {
2419
+ const merged = { ...baseOpts, format: void 0 };
2420
+ if (Array.isArray(detailOrFields)) {
2421
+ merged.fields = detailOrFields;
2422
+ merged.detail = void 0;
2423
+ } else {
2424
+ merged.detail = detailOrFields;
2425
+ merged.fields = void 0;
2426
+ }
2427
+ return graph.describe(merged);
2428
+ }
2284
2429
  };
2285
2430
  }
2286
2431
  _collectSubgraphs(prefix) {
@@ -2333,14 +2478,15 @@ var Graph = class _Graph {
2333
2478
  observe(pathOrOpts, options) {
2334
2479
  if (typeof pathOrOpts === "string") {
2335
2480
  const path = pathOrOpts;
2336
- const actor2 = options?.actor;
2481
+ const resolved = resolveObserveDetail(options);
2482
+ const actor2 = resolved.actor;
2337
2483
  const target = this.resolve(path);
2338
2484
  if (actor2 != null && !target.allowsObserve(actor2)) {
2339
2485
  throw new GuardDenied({ actor: actor2, action: "observe", nodeName: path });
2340
2486
  }
2341
- const wantsStructured2 = options?.structured === true || options?.timeline === true || options?.causal === true || options?.derived === true;
2487
+ const wantsStructured2 = resolved.structured === true || resolved.timeline === true || resolved.causal === true || resolved.derived === true || resolved.detail === "minimal" || resolved.detail === "full";
2342
2488
  if (wantsStructured2 && _Graph.inspectorEnabled) {
2343
- return this._createObserveResult(path, target, options);
2489
+ return this._createObserveResult(path, target, resolved);
2344
2490
  }
2345
2491
  return {
2346
2492
  subscribe(sink) {
@@ -2356,11 +2502,11 @@ var Graph = class _Graph {
2356
2502
  }
2357
2503
  };
2358
2504
  }
2359
- const opts = pathOrOpts;
2360
- const actor = opts?.actor;
2361
- const wantsStructured = opts?.structured === true || opts?.timeline === true || opts?.causal === true || opts?.derived === true;
2505
+ const opts = resolveObserveDetail(pathOrOpts);
2506
+ const actor = opts.actor;
2507
+ const wantsStructured = opts.structured === true || opts.timeline === true || opts.causal === true || opts.derived === true || opts.detail === "minimal" || opts.detail === "full";
2362
2508
  if (wantsStructured && _Graph.inspectorEnabled) {
2363
- return this._createObserveResultForAll(opts ?? {});
2509
+ return this._createObserveResultForAll(opts);
2364
2510
  }
2365
2511
  return {
2366
2512
  subscribe: (sink) => {
@@ -2392,6 +2538,7 @@ var Graph = class _Graph {
2392
2538
  const timeline = options.timeline === true;
2393
2539
  const causal = options.causal === true;
2394
2540
  const derived = options.derived === true;
2541
+ const minimal = options.detail === "minimal";
2395
2542
  const result = {
2396
2543
  values: {},
2397
2544
  dirtyCount: 0,
@@ -2437,6 +2584,11 @@ var Graph = class _Graph {
2437
2584
  if (t === DATA) {
2438
2585
  result.values[path] = m[1];
2439
2586
  result.events.push({ type: "data", path, data: m[1], ...base, ...withCausal });
2587
+ } else if (minimal) {
2588
+ if (t === DIRTY) result.dirtyCount++;
2589
+ else if (t === RESOLVED) result.resolvedCount++;
2590
+ else if (t === COMPLETE && !result.errored) result.completedCleanly = true;
2591
+ else if (t === ERROR) result.errored = true;
2440
2592
  } else if (t === DIRTY) {
2441
2593
  result.dirtyCount++;
2442
2594
  result.events.push({ type: "dirty", path, ...base });
@@ -2452,6 +2604,8 @@ var Graph = class _Graph {
2452
2604
  }
2453
2605
  }
2454
2606
  });
2607
+ const graph = this;
2608
+ const basePath = path;
2455
2609
  return {
2456
2610
  get values() {
2457
2611
  return result.values;
@@ -2474,11 +2628,28 @@ var Graph = class _Graph {
2474
2628
  dispose() {
2475
2629
  unsub();
2476
2630
  detachInspectorHook?.();
2631
+ },
2632
+ expand(extra) {
2633
+ unsub();
2634
+ detachInspectorHook?.();
2635
+ const merged = { ...options };
2636
+ if (typeof extra === "string") {
2637
+ merged.detail = extra;
2638
+ } else {
2639
+ Object.assign(merged, extra);
2640
+ }
2641
+ const resolvedTarget = graph.resolve(basePath);
2642
+ return graph._createObserveResult(
2643
+ basePath,
2644
+ resolvedTarget,
2645
+ resolveObserveDetail(merged)
2646
+ );
2477
2647
  }
2478
2648
  };
2479
2649
  }
2480
2650
  _createObserveResultForAll(options) {
2481
2651
  const timeline = options.timeline === true;
2652
+ const minimal = options.detail === "minimal";
2482
2653
  const result = {
2483
2654
  values: {},
2484
2655
  dirtyCount: 0,
@@ -2500,6 +2671,11 @@ var Graph = class _Graph {
2500
2671
  if (t === DATA) {
2501
2672
  result.values[path] = m[1];
2502
2673
  result.events.push({ type: "data", path, data: m[1], ...base });
2674
+ } else if (minimal) {
2675
+ if (t === DIRTY) result.dirtyCount++;
2676
+ else if (t === RESOLVED) result.resolvedCount++;
2677
+ else if (t === COMPLETE && !result.errored) result.completedCleanly = true;
2678
+ else if (t === ERROR) result.errored = true;
2503
2679
  } else if (t === DIRTY) {
2504
2680
  result.dirtyCount++;
2505
2681
  result.events.push({ type: "dirty", path, ...base });
@@ -2516,6 +2692,7 @@ var Graph = class _Graph {
2516
2692
  }
2517
2693
  })
2518
2694
  );
2695
+ const graph = this;
2519
2696
  return {
2520
2697
  get values() {
2521
2698
  return result.values;
@@ -2537,6 +2714,16 @@ var Graph = class _Graph {
2537
2714
  },
2538
2715
  dispose() {
2539
2716
  for (const u of unsubs) u();
2717
+ },
2718
+ expand(extra) {
2719
+ for (const u of unsubs) u();
2720
+ const merged = { ...options };
2721
+ if (typeof extra === "string") {
2722
+ merged.detail = extra;
2723
+ } else {
2724
+ Object.assign(merged, extra);
2725
+ }
2726
+ return graph._createObserveResultForAll(resolveObserveDetail(merged));
2540
2727
  }
2541
2728
  };
2542
2729
  }
@@ -2612,6 +2799,9 @@ var Graph = class _Graph {
2612
2799
  },
2613
2800
  dispose() {
2614
2801
  stop2();
2802
+ },
2803
+ expand() {
2804
+ throw new Error("expand() requires inspector mode (Graph.inspectorEnabled = true)");
2615
2805
  }
2616
2806
  };
2617
2807
  const pushEvent = (path, message) => {
@@ -2712,16 +2902,16 @@ var Graph = class _Graph {
2712
2902
  * @returns Rendered graph text.
2713
2903
  */
2714
2904
  dumpGraph(options = {}) {
2715
- const described = this.describe({
2905
+ const { expand: _, ...described } = this.describe({
2716
2906
  actor: options.actor,
2717
- filter: options.filter
2907
+ filter: options.filter,
2908
+ detail: "standard"
2718
2909
  });
2719
2910
  const includeEdges = options.includeEdges ?? true;
2720
2911
  const includeSubgraphs = options.includeSubgraphs ?? true;
2721
2912
  if (options.format === "json") {
2722
2913
  const payload = {
2723
- name: described.name,
2724
- nodes: described.nodes,
2914
+ ...described,
2725
2915
  edges: includeEdges ? described.edges : [],
2726
2916
  subgraphs: includeSubgraphs ? described.subgraphs : []
2727
2917
  };
@@ -2791,10 +2981,11 @@ var Graph = class _Graph {
2791
2981
  * @returns Persistable snapshot with sorted keys.
2792
2982
  */
2793
2983
  snapshot() {
2794
- const d = this.describe();
2984
+ const { expand: _, ...d } = this.describe({ detail: "full" });
2795
2985
  const sortedNodes = {};
2796
2986
  for (const key of Object.keys(d.nodes).sort()) {
2797
- sortedNodes[key] = d.nodes[key];
2987
+ const { lastMutation: _lm, guard: _g, ...node2 } = d.nodes[key];
2988
+ sortedNodes[key] = node2;
2798
2989
  }
2799
2990
  const sortedSubgraphs = [...d.subgraphs].sort();
2800
2991
  return { ...d, version: 1, nodes: sortedNodes, subgraphs: sortedSubgraphs };
@@ -2910,20 +3101,28 @@ var Graph = class _Graph {
2910
3101
  return g;
2911
3102
  }
2912
3103
  /**
2913
- * Plain snapshot with **recursively sorted object keys** for deterministic serialization (§3.8).
3104
+ * Plain snapshot object with **recursively sorted object keys** for deterministic serialization (§3.8).
2914
3105
  *
2915
3106
  * @remarks
2916
- * ECMAScript `JSON.stringify(graph)` invokes this method; it must return a plain object, not an
2917
- * already-stringified JSON string (otherwise the graph would be double-encoded).
2918
3107
  * For a single UTF-8 string with a trailing newline (convenient for git), use {@link Graph.toJSONString}.
2919
3108
  *
2920
3109
  * @returns Same object as {@link Graph.snapshot}.
2921
3110
  */
2922
- toJSON() {
3111
+ toObject() {
2923
3112
  return this.snapshot();
2924
3113
  }
2925
3114
  /**
2926
- * Deterministic JSON **text**: `JSON.stringify` of {@link Graph.toJSON} plus a trailing newline (§3.8).
3115
+ * ECMAScript `JSON.stringify` hook — delegates to {@link Graph.toObject}.
3116
+ *
3117
+ * @remarks
3118
+ * Must return a plain object (not a string) so `JSON.stringify(graph)` works correctly
3119
+ * without double-encoding.
3120
+ */
3121
+ toJSON() {
3122
+ return this.toObject();
3123
+ }
3124
+ /**
3125
+ * Deterministic JSON **text**: `JSON.stringify` of {@link Graph.toObject} plus a trailing newline (§3.8).
2927
3126
  *
2928
3127
  * @returns Stable string suitable for diffs.
2929
3128
  */
@@ -2948,16 +3147,22 @@ var Graph = class _Graph {
2948
3147
  if (!pending) return;
2949
3148
  pending = false;
2950
3149
  try {
2951
- const described = this.describe();
3150
+ const { expand: _expand, ...raw } = this.describe({ detail: "full" });
3151
+ const cleanNodes = {};
3152
+ for (const [p, n] of Object.entries(raw.nodes)) {
3153
+ const { lastMutation: _lm, guard: _g, ...node2 } = n;
3154
+ cleanNodes[p] = node2;
3155
+ }
3156
+ const described = { ...raw, nodes: cleanNodes };
2952
3157
  const snapshot = { ...described, version: SNAPSHOT_VERSION };
2953
3158
  seq += 1;
2954
3159
  const shouldCompact = lastDescribe == null || seq % compactEvery === 0;
2955
3160
  if (shouldCompact) {
2956
- adapter.save({ mode: "full", snapshot, seq });
3161
+ adapter.save(this.name, { mode: "full", snapshot, seq });
2957
3162
  } else {
2958
3163
  const previous = lastDescribe;
2959
3164
  if (previous == null) return;
2960
- adapter.save({
3165
+ adapter.save(this.name, {
2961
3166
  mode: "diff",
2962
3167
  diff: _Graph.diff(previous, described),
2963
3168
  snapshot,
@@ -2978,8 +3183,10 @@ var Graph = class _Graph {
2978
3183
  const triggeredByTier = messages.some((m) => messageTier(m[0]) >= 2);
2979
3184
  if (!triggeredByTier) return;
2980
3185
  if (options.filter) {
2981
- const described = this.describe().nodes[path];
2982
- if (described == null || !options.filter(path, described)) return;
3186
+ const nd = this.resolve(path);
3187
+ if (nd == null) return;
3188
+ const described = describeNode(nd, resolveDescribeFields("standard"));
3189
+ if (!options.filter(path, described)) return;
2983
3190
  }
2984
3191
  schedule();
2985
3192
  });