@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
@@ -368,10 +368,14 @@ function partitionForBatch(messages) {
368
368
  }
369
369
  return { immediate, deferred, terminal };
370
370
  }
371
- function emitWithBatch(emit, messages, phase = 2) {
371
+ function emitWithBatch(emit, messages, phase = 2, options) {
372
372
  if (messages.length === 0) {
373
373
  return;
374
374
  }
375
+ if (options?.strategy === "sequential") {
376
+ _emitSequential(emit, messages, phase);
377
+ return;
378
+ }
375
379
  const queue = phase === 3 ? pendingPhase3 : pendingPhase2;
376
380
  if (messages.length === 1) {
377
381
  const t = messages[0][0];
@@ -412,6 +416,29 @@ function emitWithBatch(emit, messages, phase = 2) {
412
416
  }
413
417
  }
414
418
  }
419
+ function _emitSequential(emit, messages, phase = 2) {
420
+ const dataQueue = phase === 3 ? pendingPhase3 : pendingPhase2;
421
+ for (const msg of messages) {
422
+ const tier = messageTier(msg[0]);
423
+ if (tier === 2) {
424
+ if (isBatching()) {
425
+ const m = msg;
426
+ dataQueue.push(() => emit([m]));
427
+ } else {
428
+ emit([msg]);
429
+ }
430
+ } else if (tier >= 3) {
431
+ if (isBatching()) {
432
+ const m = msg;
433
+ pendingPhase3.push(() => emit([m]));
434
+ } else {
435
+ emit([msg]);
436
+ }
437
+ } else {
438
+ emit([msg]);
439
+ }
440
+ }
441
+ }
415
442
 
416
443
  // src/core/clock.ts
417
444
  function monotonicNs() {
@@ -520,6 +547,7 @@ function advanceVersion(info, newValue, hashFn) {
520
547
  }
521
548
 
522
549
  // src/core/node.ts
550
+ var NO_VALUE = /* @__PURE__ */ Symbol.for("graphrefly/NO_VALUE");
523
551
  function createIntBitSet() {
524
552
  let bits = 0;
525
553
  return {
@@ -649,10 +677,10 @@ var NodeImpl = class {
649
677
  this._hasDeps = deps.length > 0;
650
678
  this._autoComplete = opts.completeWhenDepsComplete ?? true;
651
679
  this._isSingleDep = deps.length === 1 && fn != null;
652
- this._cached = opts.initial;
680
+ this._cached = "initial" in opts ? opts.initial : NO_VALUE;
653
681
  this._status = this._hasDeps ? "disconnected" : "settled";
654
682
  this._hashFn = opts.versioningHash ?? defaultHash;
655
- this._versioning = opts.versioning != null ? createVersioning(opts.versioning, this._cached, {
683
+ this._versioning = opts.versioning != null ? createVersioning(opts.versioning, this._cached === NO_VALUE ? void 0 : this._cached, {
656
684
  id: opts.versioningId,
657
685
  hash: this._hashFn
658
686
  }) : void 0;
@@ -736,10 +764,14 @@ var NodeImpl = class {
736
764
  _applyVersioning(level, opts) {
737
765
  if (this._versioning != null) return;
738
766
  this._hashFn = opts?.hash ?? this._hashFn;
739
- this._versioning = createVersioning(level, this._cached, {
740
- id: opts?.id,
741
- hash: this._hashFn
742
- });
767
+ this._versioning = createVersioning(
768
+ level,
769
+ this._cached === NO_VALUE ? void 0 : this._cached,
770
+ {
771
+ id: opts?.id,
772
+ hash: this._hashFn
773
+ }
774
+ );
743
775
  }
744
776
  hasGuard() {
745
777
  return this._guard != null;
@@ -749,7 +781,7 @@ var NodeImpl = class {
749
781
  return this._guard(normalizeActor(actor), "observe");
750
782
  }
751
783
  get() {
752
- return this._cached;
784
+ return this._cached === NO_VALUE ? void 0 : this._cached;
753
785
  }
754
786
  down(messages, options) {
755
787
  if (messages.length === 0) return;
@@ -806,6 +838,7 @@ var NodeImpl = class {
806
838
  }
807
839
  if (this._terminal && this._opts.resubscribable) {
808
840
  this._terminal = false;
841
+ this._cached = NO_VALUE;
809
842
  this._status = this._hasDeps ? "disconnected" : "settled";
810
843
  this._opts.onResubscribe?.();
811
844
  }
@@ -905,7 +938,7 @@ var NodeImpl = class {
905
938
  const cleanupFn = this._cleanup;
906
939
  this._cleanup = void 0;
907
940
  cleanupFn?.();
908
- this._cached = void 0;
941
+ this._cached = NO_VALUE;
909
942
  this._lastDepValues = void 0;
910
943
  }
911
944
  this._status = statusAfterMessage(this._status, m);
@@ -914,7 +947,7 @@ var NodeImpl = class {
914
947
  }
915
948
  if (t === TEARDOWN) {
916
949
  if (this._opts.resetOnTeardown) {
917
- this._cached = void 0;
950
+ this._cached = NO_VALUE;
918
951
  }
919
952
  const teardownCleanup = this._cleanup;
920
953
  this._cleanup = void 0;
@@ -945,7 +978,15 @@ var NodeImpl = class {
945
978
  }
946
979
  _emitAutoValue(value) {
947
980
  const wasDirty = this._status === "dirty";
948
- const unchanged = this._equals(this._cached, value);
981
+ let unchanged;
982
+ try {
983
+ unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
984
+ } catch (eqErr) {
985
+ const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
986
+ const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
987
+ this._downInternal([[ERROR, wrapped]]);
988
+ return;
989
+ }
949
990
  if (unchanged) {
950
991
  this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
951
992
  return;
@@ -992,7 +1033,9 @@ var NodeImpl = class {
992
1033
  if (out === void 0) return;
993
1034
  this._emitAutoValue(out);
994
1035
  } catch (err) {
995
- this._downInternal([[ERROR, err]]);
1036
+ const errMsg = err instanceof Error ? err.message : String(err);
1037
+ const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
1038
+ this._downInternal([[ERROR, wrapped]]);
996
1039
  }
997
1040
  }
998
1041
  _onDepDirty(index) {
@@ -1027,7 +1070,11 @@ var NodeImpl = class {
1027
1070
  try {
1028
1071
  if (this._onMessage(msg, index, this._actions)) continue;
1029
1072
  } catch (err) {
1030
- this._downInternal([[ERROR, err]]);
1073
+ const errMsg = err instanceof Error ? err.message : String(err);
1074
+ const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
1075
+ cause: err
1076
+ });
1077
+ this._downInternal([[ERROR, wrapped]]);
1031
1078
  return;
1032
1079
  }
1033
1080
  }
@@ -1168,7 +1215,7 @@ var DynamicNodeImpl = class {
1168
1215
  _actions;
1169
1216
  _boundEmitToSinks;
1170
1217
  // Mutable state
1171
- _cached;
1218
+ _cached = NO_VALUE;
1172
1219
  _status = "disconnected";
1173
1220
  _terminal = false;
1174
1221
  _connected = false;
@@ -1262,7 +1309,7 @@ var DynamicNodeImpl = class {
1262
1309
  return this._guard(normalizeActor(actor), "observe");
1263
1310
  }
1264
1311
  get() {
1265
- return this._cached;
1312
+ return this._cached === NO_VALUE ? void 0 : this._cached;
1266
1313
  }
1267
1314
  down(messages, options) {
1268
1315
  if (messages.length === 0) return;
@@ -1320,6 +1367,7 @@ var DynamicNodeImpl = class {
1320
1367
  }
1321
1368
  if (this._terminal && this._resubscribable) {
1322
1369
  this._terminal = false;
1370
+ this._cached = NO_VALUE;
1323
1371
  this._status = "disconnected";
1324
1372
  this._onResubscribe?.();
1325
1373
  }
@@ -1397,10 +1445,13 @@ var DynamicNodeImpl = class {
1397
1445
  const t = m[0];
1398
1446
  if (t === DATA) this._cached = m[1];
1399
1447
  if (t === INVALIDATE) {
1400
- this._cached = void 0;
1448
+ this._cached = NO_VALUE;
1449
+ this._status = "dirty";
1401
1450
  }
1402
- if (t === DATA || t === RESOLVED) {
1451
+ if (t === DATA) {
1403
1452
  this._status = "settled";
1453
+ } else if (t === RESOLVED) {
1454
+ this._status = "resolved";
1404
1455
  } else if (t === DIRTY) {
1405
1456
  this._status = "dirty";
1406
1457
  } else if (t === COMPLETE) {
@@ -1411,7 +1462,7 @@ var DynamicNodeImpl = class {
1411
1462
  this._terminal = true;
1412
1463
  }
1413
1464
  if (t === TEARDOWN) {
1414
- if (this._resetOnTeardown) this._cached = void 0;
1465
+ if (this._resetOnTeardown) this._cached = NO_VALUE;
1415
1466
  try {
1416
1467
  this._propagateToMeta(t);
1417
1468
  } finally {
@@ -1434,7 +1485,15 @@ var DynamicNodeImpl = class {
1434
1485
  }
1435
1486
  _emitAutoValue(value) {
1436
1487
  const wasDirty = this._status === "dirty";
1437
- const unchanged = this._equals(this._cached, value);
1488
+ let unchanged;
1489
+ try {
1490
+ unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
1491
+ } catch (eqErr) {
1492
+ const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
1493
+ const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
1494
+ this._downInternal([[ERROR, wrapped]]);
1495
+ return;
1496
+ }
1438
1497
  if (unchanged) {
1439
1498
  this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
1440
1499
  return;
@@ -1600,6 +1659,19 @@ var DynamicNodeImpl = class {
1600
1659
  };
1601
1660
 
1602
1661
  // src/core/meta.ts
1662
+ function resolveDescribeFields(detail, fields) {
1663
+ if (fields != null && fields.length > 0) return new Set(fields);
1664
+ switch (detail) {
1665
+ case "standard":
1666
+ return /* @__PURE__ */ new Set(["type", "status", "value", "deps", "meta", "v"]);
1667
+ case "full":
1668
+ return null;
1669
+ // null = include everything
1670
+ case "minimal":
1671
+ default:
1672
+ return /* @__PURE__ */ new Set(["type", "deps"]);
1673
+ }
1674
+ }
1603
1675
  function inferDescribeType(n) {
1604
1676
  if (n._describeKind != null) return n._describeKind;
1605
1677
  if (!n._hasDeps) return n._fn != null ? "producer" : "state";
@@ -1617,12 +1689,10 @@ function metaSnapshot(node2) {
1617
1689
  }
1618
1690
  return out;
1619
1691
  }
1620
- function describeNode(node2) {
1621
- const meta = { ...metaSnapshot(node2) };
1622
- const guard = node2 instanceof NodeImpl && node2._guard || node2 instanceof DynamicNodeImpl && node2._guard || void 0;
1623
- if (guard != null && meta.access === void 0) {
1624
- meta.access = accessHintForGuard(guard);
1625
- }
1692
+ function describeNode(node2, includeFields) {
1693
+ const all = includeFields == null;
1694
+ const metaKeys = !all && includeFields != null ? [...includeFields].filter((f) => f.startsWith("meta.")).map((f) => f.slice(5)) : null;
1695
+ const wantsMeta = all || includeFields.has("meta") || metaKeys != null && metaKeys.length > 0;
1626
1696
  let type = "state";
1627
1697
  let deps = [];
1628
1698
  if (node2 instanceof NodeImpl) {
@@ -1632,20 +1702,36 @@ function describeNode(node2) {
1632
1702
  type = node2._describeKind ?? "derived";
1633
1703
  deps = [];
1634
1704
  }
1635
- const out = {
1636
- type,
1637
- status: node2.status,
1638
- deps,
1639
- meta
1640
- };
1705
+ const out = { type, deps };
1706
+ if (all || includeFields.has("status")) {
1707
+ out.status = node2.status;
1708
+ }
1709
+ const guard = node2 instanceof NodeImpl && node2._guard || node2 instanceof DynamicNodeImpl && node2._guard || void 0;
1710
+ if (wantsMeta) {
1711
+ const rawMeta = { ...metaSnapshot(node2) };
1712
+ if (guard != null && rawMeta.access === void 0) {
1713
+ rawMeta.access = accessHintForGuard(guard);
1714
+ }
1715
+ if (metaKeys != null && metaKeys.length > 0 && !includeFields.has("meta")) {
1716
+ const filtered = {};
1717
+ for (const k of metaKeys) {
1718
+ if (k in rawMeta) filtered[k] = rawMeta[k];
1719
+ }
1720
+ out.meta = filtered;
1721
+ } else {
1722
+ out.meta = rawMeta;
1723
+ }
1724
+ }
1641
1725
  if (node2.name != null) {
1642
1726
  out.name = node2.name;
1643
1727
  }
1644
- try {
1645
- out.value = node2.get();
1646
- } catch {
1728
+ if (all || includeFields.has("value")) {
1729
+ try {
1730
+ out.value = node2.get();
1731
+ } catch {
1732
+ }
1647
1733
  }
1648
- if (node2.v != null) {
1734
+ if ((all || includeFields.has("v")) && node2.v != null) {
1649
1735
  const vInfo = { id: node2.v.id, version: node2.v.version };
1650
1736
  if ("cid" in node2.v) {
1651
1737
  vInfo.cid = node2.v.cid;
@@ -1653,6 +1739,16 @@ function describeNode(node2) {
1653
1739
  }
1654
1740
  out.v = vInfo;
1655
1741
  }
1742
+ if (all || includeFields.has("guard")) {
1743
+ if (guard != null) {
1744
+ out.guard = accessHintForGuard(guard);
1745
+ }
1746
+ }
1747
+ if (all || includeFields.has("lastMutation")) {
1748
+ if (node2.lastMutation != null) {
1749
+ out.lastMutation = node2.lastMutation;
1750
+ }
1751
+ }
1656
1752
  return out;
1657
1753
  }
1658
1754
 
@@ -1842,6 +1938,23 @@ function resolveSpyTheme(theme) {
1842
1938
  reset: theme.reset ?? ""
1843
1939
  };
1844
1940
  }
1941
+ function resolveObserveDetail(opts) {
1942
+ if (opts == null) return {};
1943
+ const detail = opts.detail;
1944
+ if (detail === "full") {
1945
+ return {
1946
+ ...opts,
1947
+ structured: opts.structured ?? true,
1948
+ timeline: opts.timeline ?? true,
1949
+ causal: opts.causal ?? true,
1950
+ derived: opts.derived ?? true
1951
+ };
1952
+ }
1953
+ if (detail === "minimal") {
1954
+ return { ...opts, structured: opts.structured ?? true };
1955
+ }
1956
+ return opts;
1957
+ }
1845
1958
  function assertLocalName(name, graphName, label) {
1846
1959
  if (name === "") {
1847
1960
  throw new Error(`Graph "${graphName}": ${label} name must be non-empty`);
@@ -2039,6 +2152,22 @@ var Graph = class _Graph {
2039
2152
  if (this._defaultVersioningLevel != null) {
2040
2153
  node2._applyVersioning(this._defaultVersioningLevel);
2041
2154
  }
2155
+ if (node2._deps.length > 0) {
2156
+ for (const dep of node2._deps) {
2157
+ for (const [depName, depNode] of this._nodes) {
2158
+ if (depNode === dep) {
2159
+ this._edges.add(edgeKey(depName, name));
2160
+ break;
2161
+ }
2162
+ }
2163
+ }
2164
+ }
2165
+ for (const [otherName, otherNode] of this._nodes) {
2166
+ if (otherName === name) continue;
2167
+ if (otherNode instanceof NodeImpl && otherNode._deps.includes(node2)) {
2168
+ this._edges.add(edgeKey(name, otherName));
2169
+ }
2170
+ }
2042
2171
  }
2043
2172
  }
2044
2173
  /**
@@ -2425,6 +2554,9 @@ var Graph = class _Graph {
2425
2554
  describe(options) {
2426
2555
  const actor = options?.actor;
2427
2556
  const filter = options?.filter;
2557
+ const includeFields = resolveDescribeFields(options?.detail, options?.fields);
2558
+ const isSpec = options?.format === "spec";
2559
+ const effectiveFields = isSpec ? resolveDescribeFields("minimal") : includeFields;
2428
2560
  const targets = [];
2429
2561
  this._collectObserveTargets("", targets);
2430
2562
  const nodeToPath = /* @__PURE__ */ new Map();
@@ -2434,7 +2566,7 @@ var Graph = class _Graph {
2434
2566
  const nodes = {};
2435
2567
  for (const [p, n] of targets) {
2436
2568
  if (actor != null && !n.allowsObserve(actor)) continue;
2437
- const raw = describeNode(n);
2569
+ const raw = describeNode(n, effectiveFields);
2438
2570
  const deps = n instanceof NodeImpl ? n._deps.map((d) => nodeToPath.get(d) ?? d.name ?? "") : [];
2439
2571
  const { name: _name, ...rest } = raw;
2440
2572
  const entry = { ...rest, deps };
@@ -2455,7 +2587,7 @@ var Graph = class _Graph {
2455
2587
  continue;
2456
2588
  }
2457
2589
  if (normalizedKey === "metaHas") {
2458
- if (!Object.hasOwn(entry.meta, String(fv))) {
2590
+ if (!Object.hasOwn(entry.meta ?? {}, String(fv))) {
2459
2591
  match = false;
2460
2592
  break;
2461
2593
  }
@@ -2488,11 +2620,24 @@ var Graph = class _Graph {
2488
2620
  const prefix = `${sg}${PATH_SEP}`;
2489
2621
  return [...nodeKeys].some((k) => k === sg || k.startsWith(prefix));
2490
2622
  }) : allSubgraphs;
2623
+ const graph = this;
2624
+ const baseOpts = options;
2491
2625
  return {
2492
2626
  name: this.name,
2493
2627
  nodes,
2494
2628
  edges,
2495
- subgraphs
2629
+ subgraphs,
2630
+ expand(detailOrFields) {
2631
+ const merged = { ...baseOpts, format: void 0 };
2632
+ if (Array.isArray(detailOrFields)) {
2633
+ merged.fields = detailOrFields;
2634
+ merged.detail = void 0;
2635
+ } else {
2636
+ merged.detail = detailOrFields;
2637
+ merged.fields = void 0;
2638
+ }
2639
+ return graph.describe(merged);
2640
+ }
2496
2641
  };
2497
2642
  }
2498
2643
  _collectSubgraphs(prefix) {
@@ -2545,14 +2690,15 @@ var Graph = class _Graph {
2545
2690
  observe(pathOrOpts, options) {
2546
2691
  if (typeof pathOrOpts === "string") {
2547
2692
  const path = pathOrOpts;
2548
- const actor2 = options?.actor;
2693
+ const resolved = resolveObserveDetail(options);
2694
+ const actor2 = resolved.actor;
2549
2695
  const target = this.resolve(path);
2550
2696
  if (actor2 != null && !target.allowsObserve(actor2)) {
2551
2697
  throw new GuardDenied({ actor: actor2, action: "observe", nodeName: path });
2552
2698
  }
2553
- const wantsStructured2 = options?.structured === true || options?.timeline === true || options?.causal === true || options?.derived === true;
2699
+ const wantsStructured2 = resolved.structured === true || resolved.timeline === true || resolved.causal === true || resolved.derived === true || resolved.detail === "minimal" || resolved.detail === "full";
2554
2700
  if (wantsStructured2 && _Graph.inspectorEnabled) {
2555
- return this._createObserveResult(path, target, options);
2701
+ return this._createObserveResult(path, target, resolved);
2556
2702
  }
2557
2703
  return {
2558
2704
  subscribe(sink) {
@@ -2568,11 +2714,11 @@ var Graph = class _Graph {
2568
2714
  }
2569
2715
  };
2570
2716
  }
2571
- const opts = pathOrOpts;
2572
- const actor = opts?.actor;
2573
- const wantsStructured = opts?.structured === true || opts?.timeline === true || opts?.causal === true || opts?.derived === true;
2717
+ const opts = resolveObserveDetail(pathOrOpts);
2718
+ const actor = opts.actor;
2719
+ const wantsStructured = opts.structured === true || opts.timeline === true || opts.causal === true || opts.derived === true || opts.detail === "minimal" || opts.detail === "full";
2574
2720
  if (wantsStructured && _Graph.inspectorEnabled) {
2575
- return this._createObserveResultForAll(opts ?? {});
2721
+ return this._createObserveResultForAll(opts);
2576
2722
  }
2577
2723
  return {
2578
2724
  subscribe: (sink) => {
@@ -2604,6 +2750,7 @@ var Graph = class _Graph {
2604
2750
  const timeline = options.timeline === true;
2605
2751
  const causal = options.causal === true;
2606
2752
  const derived2 = options.derived === true;
2753
+ const minimal = options.detail === "minimal";
2607
2754
  const result = {
2608
2755
  values: {},
2609
2756
  dirtyCount: 0,
@@ -2649,6 +2796,11 @@ var Graph = class _Graph {
2649
2796
  if (t === DATA) {
2650
2797
  result.values[path] = m[1];
2651
2798
  result.events.push({ type: "data", path, data: m[1], ...base, ...withCausal });
2799
+ } else if (minimal) {
2800
+ if (t === DIRTY) result.dirtyCount++;
2801
+ else if (t === RESOLVED) result.resolvedCount++;
2802
+ else if (t === COMPLETE && !result.errored) result.completedCleanly = true;
2803
+ else if (t === ERROR) result.errored = true;
2652
2804
  } else if (t === DIRTY) {
2653
2805
  result.dirtyCount++;
2654
2806
  result.events.push({ type: "dirty", path, ...base });
@@ -2664,6 +2816,8 @@ var Graph = class _Graph {
2664
2816
  }
2665
2817
  }
2666
2818
  });
2819
+ const graph = this;
2820
+ const basePath = path;
2667
2821
  return {
2668
2822
  get values() {
2669
2823
  return result.values;
@@ -2686,11 +2840,28 @@ var Graph = class _Graph {
2686
2840
  dispose() {
2687
2841
  unsub();
2688
2842
  detachInspectorHook?.();
2843
+ },
2844
+ expand(extra) {
2845
+ unsub();
2846
+ detachInspectorHook?.();
2847
+ const merged = { ...options };
2848
+ if (typeof extra === "string") {
2849
+ merged.detail = extra;
2850
+ } else {
2851
+ Object.assign(merged, extra);
2852
+ }
2853
+ const resolvedTarget = graph.resolve(basePath);
2854
+ return graph._createObserveResult(
2855
+ basePath,
2856
+ resolvedTarget,
2857
+ resolveObserveDetail(merged)
2858
+ );
2689
2859
  }
2690
2860
  };
2691
2861
  }
2692
2862
  _createObserveResultForAll(options) {
2693
2863
  const timeline = options.timeline === true;
2864
+ const minimal = options.detail === "minimal";
2694
2865
  const result = {
2695
2866
  values: {},
2696
2867
  dirtyCount: 0,
@@ -2712,6 +2883,11 @@ var Graph = class _Graph {
2712
2883
  if (t === DATA) {
2713
2884
  result.values[path] = m[1];
2714
2885
  result.events.push({ type: "data", path, data: m[1], ...base });
2886
+ } else if (minimal) {
2887
+ if (t === DIRTY) result.dirtyCount++;
2888
+ else if (t === RESOLVED) result.resolvedCount++;
2889
+ else if (t === COMPLETE && !result.errored) result.completedCleanly = true;
2890
+ else if (t === ERROR) result.errored = true;
2715
2891
  } else if (t === DIRTY) {
2716
2892
  result.dirtyCount++;
2717
2893
  result.events.push({ type: "dirty", path, ...base });
@@ -2728,6 +2904,7 @@ var Graph = class _Graph {
2728
2904
  }
2729
2905
  })
2730
2906
  );
2907
+ const graph = this;
2731
2908
  return {
2732
2909
  get values() {
2733
2910
  return result.values;
@@ -2749,6 +2926,16 @@ var Graph = class _Graph {
2749
2926
  },
2750
2927
  dispose() {
2751
2928
  for (const u of unsubs) u();
2929
+ },
2930
+ expand(extra) {
2931
+ for (const u of unsubs) u();
2932
+ const merged = { ...options };
2933
+ if (typeof extra === "string") {
2934
+ merged.detail = extra;
2935
+ } else {
2936
+ Object.assign(merged, extra);
2937
+ }
2938
+ return graph._createObserveResultForAll(resolveObserveDetail(merged));
2752
2939
  }
2753
2940
  };
2754
2941
  }
@@ -2824,6 +3011,9 @@ var Graph = class _Graph {
2824
3011
  },
2825
3012
  dispose() {
2826
3013
  stop2();
3014
+ },
3015
+ expand() {
3016
+ throw new Error("expand() requires inspector mode (Graph.inspectorEnabled = true)");
2827
3017
  }
2828
3018
  };
2829
3019
  const pushEvent = (path, message) => {
@@ -2924,16 +3114,16 @@ var Graph = class _Graph {
2924
3114
  * @returns Rendered graph text.
2925
3115
  */
2926
3116
  dumpGraph(options = {}) {
2927
- const described = this.describe({
3117
+ const { expand: _, ...described } = this.describe({
2928
3118
  actor: options.actor,
2929
- filter: options.filter
3119
+ filter: options.filter,
3120
+ detail: "standard"
2930
3121
  });
2931
3122
  const includeEdges = options.includeEdges ?? true;
2932
3123
  const includeSubgraphs = options.includeSubgraphs ?? true;
2933
3124
  if (options.format === "json") {
2934
3125
  const payload = {
2935
- name: described.name,
2936
- nodes: described.nodes,
3126
+ ...described,
2937
3127
  edges: includeEdges ? described.edges : [],
2938
3128
  subgraphs: includeSubgraphs ? described.subgraphs : []
2939
3129
  };
@@ -3003,10 +3193,11 @@ var Graph = class _Graph {
3003
3193
  * @returns Persistable snapshot with sorted keys.
3004
3194
  */
3005
3195
  snapshot() {
3006
- const d = this.describe();
3196
+ const { expand: _, ...d } = this.describe({ detail: "full" });
3007
3197
  const sortedNodes = {};
3008
3198
  for (const key of Object.keys(d.nodes).sort()) {
3009
- sortedNodes[key] = d.nodes[key];
3199
+ const { lastMutation: _lm, guard: _g, ...node2 } = d.nodes[key];
3200
+ sortedNodes[key] = node2;
3010
3201
  }
3011
3202
  const sortedSubgraphs = [...d.subgraphs].sort();
3012
3203
  return { ...d, version: 1, nodes: sortedNodes, subgraphs: sortedSubgraphs };
@@ -3122,20 +3313,28 @@ var Graph = class _Graph {
3122
3313
  return g;
3123
3314
  }
3124
3315
  /**
3125
- * Plain snapshot with **recursively sorted object keys** for deterministic serialization (§3.8).
3316
+ * Plain snapshot object with **recursively sorted object keys** for deterministic serialization (§3.8).
3126
3317
  *
3127
3318
  * @remarks
3128
- * ECMAScript `JSON.stringify(graph)` invokes this method; it must return a plain object, not an
3129
- * already-stringified JSON string (otherwise the graph would be double-encoded).
3130
3319
  * For a single UTF-8 string with a trailing newline (convenient for git), use {@link Graph.toJSONString}.
3131
3320
  *
3132
3321
  * @returns Same object as {@link Graph.snapshot}.
3133
3322
  */
3134
- toJSON() {
3323
+ toObject() {
3135
3324
  return this.snapshot();
3136
3325
  }
3137
3326
  /**
3138
- * Deterministic JSON **text**: `JSON.stringify` of {@link Graph.toJSON} plus a trailing newline (§3.8).
3327
+ * ECMAScript `JSON.stringify` hook — delegates to {@link Graph.toObject}.
3328
+ *
3329
+ * @remarks
3330
+ * Must return a plain object (not a string) so `JSON.stringify(graph)` works correctly
3331
+ * without double-encoding.
3332
+ */
3333
+ toJSON() {
3334
+ return this.toObject();
3335
+ }
3336
+ /**
3337
+ * Deterministic JSON **text**: `JSON.stringify` of {@link Graph.toObject} plus a trailing newline (§3.8).
3139
3338
  *
3140
3339
  * @returns Stable string suitable for diffs.
3141
3340
  */
@@ -3160,16 +3359,22 @@ var Graph = class _Graph {
3160
3359
  if (!pending) return;
3161
3360
  pending = false;
3162
3361
  try {
3163
- const described = this.describe();
3362
+ const { expand: _expand, ...raw } = this.describe({ detail: "full" });
3363
+ const cleanNodes = {};
3364
+ for (const [p, n] of Object.entries(raw.nodes)) {
3365
+ const { lastMutation: _lm, guard: _g, ...node2 } = n;
3366
+ cleanNodes[p] = node2;
3367
+ }
3368
+ const described = { ...raw, nodes: cleanNodes };
3164
3369
  const snapshot = { ...described, version: SNAPSHOT_VERSION };
3165
3370
  seq += 1;
3166
3371
  const shouldCompact = lastDescribe == null || seq % compactEvery === 0;
3167
3372
  if (shouldCompact) {
3168
- adapter.save({ mode: "full", snapshot, seq });
3373
+ adapter.save(this.name, { mode: "full", snapshot, seq });
3169
3374
  } else {
3170
3375
  const previous = lastDescribe;
3171
3376
  if (previous == null) return;
3172
- adapter.save({
3377
+ adapter.save(this.name, {
3173
3378
  mode: "diff",
3174
3379
  diff: _Graph.diff(previous, described),
3175
3380
  snapshot,
@@ -3190,8 +3395,10 @@ var Graph = class _Graph {
3190
3395
  const triggeredByTier = messages.some((m) => messageTier(m[0]) >= 2);
3191
3396
  if (!triggeredByTier) return;
3192
3397
  if (options.filter) {
3193
- const described = this.describe().nodes[path];
3194
- if (described == null || !options.filter(path, described)) return;
3398
+ const nd = this.resolve(path);
3399
+ if (nd == null) return;
3400
+ const described = describeNode(nd, resolveDescribeFields("standard"));
3401
+ if (!options.filter(path, described)) return;
3195
3402
  }
3196
3403
  schedule();
3197
3404
  });