@graphrefly/graphrefly 0.5.0 → 0.7.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-5X3LAO3B.js → chunk-3EVXOI5C.js} +50 -5
  3. package/dist/chunk-3EVXOI5C.js.map +1 -0
  4. package/dist/chunk-47YJEZUJ.js +106 -0
  5. package/dist/chunk-47YJEZUJ.js.map +1 -0
  6. package/dist/{chunk-6W5SGIGB.js → chunk-BLCXEMAD.js} +129 -25
  7. package/dist/chunk-BLCXEMAD.js.map +1 -0
  8. package/dist/{chunk-QW7H3ICI.js → chunk-FGLZ5QID.js} +4 -4
  9. package/dist/{chunk-CP6MNKAA.js → chunk-ISGMZ2T3.js} +10 -4
  10. package/dist/{chunk-CP6MNKAA.js.map → chunk-ISGMZ2T3.js.map} +1 -1
  11. package/dist/{chunk-HP7OKEOE.js → chunk-L4J2K2RT.js} +3 -3
  12. package/dist/chunk-L4J2K2RT.js.map +1 -0
  13. package/dist/{chunk-ZERWUCGK.js → chunk-ONLYF6GA.js} +866 -206
  14. package/dist/chunk-ONLYF6GA.js.map +1 -0
  15. package/dist/{chunk-Z4Y4FMQN.js → chunk-OSR3G3DP.js} +7 -7
  16. package/dist/{chunk-KWXPDASV.js → chunk-PEBORXRA.js} +2 -2
  17. package/dist/chunk-WZ2Z2CRV.js +32 -0
  18. package/dist/chunk-WZ2Z2CRV.js.map +1 -0
  19. package/dist/compat/nestjs/index.cjs +226 -41
  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 +134 -20
  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 +939 -209
  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 +26 -3
  34. package/dist/graph/index.cjs +226 -41
  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-B3BoJjcb.d.cts} +58 -7
  40. package/dist/{graph-D18qmsNm.d.ts → graph-CmiUuhaN.d.ts} +58 -7
  41. package/dist/{index-B7eOdgEx.d.ts → index-Bf2X1YSI.d.ts} +3 -3
  42. package/dist/{index-BvhgZRHK.d.cts → index-Bl7hJcc3.d.cts} +4 -2
  43. package/dist/{index-Bvy_6CaN.d.ts → index-BrgtEG-C.d.ts} +47 -4
  44. package/dist/{index-D_geH2Bm.d.cts → index-BsuKSs4L.d.cts} +3 -3
  45. package/dist/{index-BtK55IE2.d.ts → index-CsUq2rrK.d.ts} +4 -2
  46. package/dist/{index-C3BMRmmp.d.cts → index-D0cx-Yht.d.cts} +3 -3
  47. package/dist/{index-Cz8og7-L.d.cts → index-D1hgSTzr.d.cts} +488 -60
  48. package/dist/{index-C5mqLhMX.d.cts → index-D8NIq6om.d.cts} +47 -4
  49. package/dist/{index-CP_QvbWu.d.ts → index-DFFNKYig.d.ts} +3 -3
  50. package/dist/{index-C3LRU4jB.d.ts → index-Pm68AYPh.d.ts} +488 -60
  51. package/dist/index.cjs +3005 -1776
  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 +396 -27
  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 +226 -41
  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-CAXzJ7hw.d.cts} +2 -2
  65. package/dist/{reactive-log-ohLmTXoZ.d.ts → reactive-log-DwNhOe0g.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-ZERWUCGK.js.map +0 -1
  73. /package/dist/{chunk-QW7H3ICI.js.map → chunk-FGLZ5QID.js.map} +0 -0
  74. /package/dist/{chunk-Z4Y4FMQN.js.map → chunk-OSR3G3DP.js.map} +0 -0
  75. /package/dist/{chunk-KWXPDASV.js.map → chunk-PEBORXRA.js.map} +0 -0
@@ -544,9 +544,9 @@ declare function node<T = unknown>(depsOrFn?: readonly Node[] | NodeFn<T> | Node
544
544
  /** JSON-shaped slice of a node for Phase 1 `Graph.describe()` (GRAPHREFLY-SPEC §3.6, Appendix B). */
545
545
  type DescribeNodeOutput = {
546
546
  type: "state" | "derived" | "producer" | "operator" | "effect";
547
- status: Node["status"];
547
+ status?: Node["status"];
548
548
  deps: string[];
549
- meta: Record<string, unknown>;
549
+ meta?: Record<string, unknown>;
550
550
  name?: string;
551
551
  value?: unknown;
552
552
  /** Node versioning info (GRAPHREFLY-SPEC §7). Present only when versioning is enabled. */
@@ -556,7 +556,28 @@ type DescribeNodeOutput = {
556
556
  cid?: string;
557
557
  prev?: string | null;
558
558
  };
559
+ /** Guard info (full detail). */
560
+ guard?: string;
561
+ /** Last mutation attribution (full detail). */
562
+ lastMutation?: Readonly<{
563
+ actor: Actor;
564
+ timestamp_ns: number;
565
+ }>;
559
566
  };
567
+ /**
568
+ * Detail level for `describe()` progressive disclosure (Phase 3.3b).
569
+ * - `"minimal"` — type + deps only (default). LLM-friendly.
570
+ * - `"standard"` — type, status, value, deps, meta, versioning (`v`).
571
+ * - `"full"` — standard + guard, lastMutation.
572
+ */
573
+ type DescribeDetail = "minimal" | "standard" | "full";
574
+ /**
575
+ * Valid field names for `describe({ fields: [...] })` (Phase 3.3b).
576
+ * Dotted paths like `"meta.label"` select specific meta keys.
577
+ */
578
+ type DescribeField = "type" | "status" | "value" | "deps" | "meta" | "v" | "guard" | "lastMutation" | `meta.${string}`;
579
+ /** Resolve which fields to include based on detail level or explicit field list. */
580
+ declare function resolveDescribeFields(detail?: DescribeDetail, fields?: readonly DescribeField[]): Set<string> | null;
560
581
  /**
561
582
  * Reads the current cached value of every companion meta field on a node,
562
583
  * suitable for merging into `describe()`-style JSON (GRAPHREFLY-SPEC §2.3, §3.6).
@@ -602,6 +623,12 @@ declare function metaSnapshot(node: Node): Record<string, unknown>;
602
623
  * describeNode(state(0));
603
624
  * ```
604
625
  */
605
- declare function describeNode(node: Node): DescribeNodeOutput;
626
+ /**
627
+ * Builds a single-node slice for `Graph.describe()`.
628
+ *
629
+ * @param node - Node to introspect.
630
+ * @param includeFields - Set of fields to include, or `null` for all. When omitted, all fields are included (legacy behavior).
631
+ */
632
+ declare function describeNode(node: Node, includeFields?: Set<string> | null): DescribeNodeOutput;
606
633
 
607
- export { propagatesToMeta as $, type Actor as A, describeNode as B, COMPLETE as C, DATA as D, ERROR as E, isKnownMessageType as F, type GuardAction as G, type HashFn as H, INVALIDATE as I, isPhase2Message as J, isTerminalMessage as K, isV1 as L, type Message as M, type Node as N, type OnMessageHandler as O, PAUSE as P, knownMessageTypes as Q, RESOLVED as R, type SubscribeHints as S, TEARDOWN as T, messageTier as U, type V0 as V, metaSnapshot as W, node as X, normalizeActor as Y, policy as Z, policyFromRules as _, type NodeOptions as a, type NodeInspectorHook as a0, type NodeActions as b, type NodeFn as c, DEFAULT_ACTOR as d, DIRTY as e, type DescribeNodeOutput as f, GuardDenied as g, type GuardDeniedDetails as h, type Messages as i, type NodeDescribeKind as j, type NodeGuard as k, type NodeSink as l, type NodeStatus as m, type NodeTransportOptions as n, type NodeVersionInfo as o, type PolicyAllow as p, type PolicyDeny as q, type PolicyRuleData as r, RESUME as s, type V1 as t, type VersioningLevel as u, type VersioningOptions as v, accessHintForGuard as w, advanceVersion as x, createVersioning as y, defaultHash as z };
634
+ export { policy as $, type Actor as A, createVersioning as B, COMPLETE as C, DATA as D, ERROR as E, defaultHash as F, type GuardAction as G, type HashFn as H, INVALIDATE as I, describeNode as J, isKnownMessageType as K, isPhase2Message as L, type Message as M, type Node as N, type OnMessageHandler as O, PAUSE as P, isTerminalMessage as Q, RESOLVED as R, type SubscribeHints as S, TEARDOWN as T, isV1 as U, type V0 as V, knownMessageTypes as W, messageTier as X, metaSnapshot as Y, node as Z, normalizeActor as _, type NodeOptions as a, policyFromRules as a0, propagatesToMeta as a1, resolveDescribeFields as a2, type NodeInspectorHook as a3, type NodeActions as b, type NodeFn as c, DEFAULT_ACTOR as d, DIRTY as e, type DescribeDetail as f, type DescribeField as g, type DescribeNodeOutput as h, GuardDenied as i, type GuardDeniedDetails as j, type Messages as k, type NodeDescribeKind as l, type NodeGuard as m, type NodeSink as n, type NodeStatus as o, type NodeTransportOptions as p, type NodeVersionInfo as q, type PolicyAllow as r, type PolicyDeny as s, type PolicyRuleData as t, RESUME as u, type V1 as v, type VersioningLevel as w, type VersioningOptions as x, accessHintForGuard as y, advanceVersion as z };
@@ -544,9 +544,9 @@ declare function node<T = unknown>(depsOrFn?: readonly Node[] | NodeFn<T> | Node
544
544
  /** JSON-shaped slice of a node for Phase 1 `Graph.describe()` (GRAPHREFLY-SPEC §3.6, Appendix B). */
545
545
  type DescribeNodeOutput = {
546
546
  type: "state" | "derived" | "producer" | "operator" | "effect";
547
- status: Node["status"];
547
+ status?: Node["status"];
548
548
  deps: string[];
549
- meta: Record<string, unknown>;
549
+ meta?: Record<string, unknown>;
550
550
  name?: string;
551
551
  value?: unknown;
552
552
  /** Node versioning info (GRAPHREFLY-SPEC §7). Present only when versioning is enabled. */
@@ -556,7 +556,28 @@ type DescribeNodeOutput = {
556
556
  cid?: string;
557
557
  prev?: string | null;
558
558
  };
559
+ /** Guard info (full detail). */
560
+ guard?: string;
561
+ /** Last mutation attribution (full detail). */
562
+ lastMutation?: Readonly<{
563
+ actor: Actor;
564
+ timestamp_ns: number;
565
+ }>;
559
566
  };
567
+ /**
568
+ * Detail level for `describe()` progressive disclosure (Phase 3.3b).
569
+ * - `"minimal"` — type + deps only (default). LLM-friendly.
570
+ * - `"standard"` — type, status, value, deps, meta, versioning (`v`).
571
+ * - `"full"` — standard + guard, lastMutation.
572
+ */
573
+ type DescribeDetail = "minimal" | "standard" | "full";
574
+ /**
575
+ * Valid field names for `describe({ fields: [...] })` (Phase 3.3b).
576
+ * Dotted paths like `"meta.label"` select specific meta keys.
577
+ */
578
+ type DescribeField = "type" | "status" | "value" | "deps" | "meta" | "v" | "guard" | "lastMutation" | `meta.${string}`;
579
+ /** Resolve which fields to include based on detail level or explicit field list. */
580
+ declare function resolveDescribeFields(detail?: DescribeDetail, fields?: readonly DescribeField[]): Set<string> | null;
560
581
  /**
561
582
  * Reads the current cached value of every companion meta field on a node,
562
583
  * suitable for merging into `describe()`-style JSON (GRAPHREFLY-SPEC §2.3, §3.6).
@@ -602,6 +623,12 @@ declare function metaSnapshot(node: Node): Record<string, unknown>;
602
623
  * describeNode(state(0));
603
624
  * ```
604
625
  */
605
- declare function describeNode(node: Node): DescribeNodeOutput;
626
+ /**
627
+ * Builds a single-node slice for `Graph.describe()`.
628
+ *
629
+ * @param node - Node to introspect.
630
+ * @param includeFields - Set of fields to include, or `null` for all. When omitted, all fields are included (legacy behavior).
631
+ */
632
+ declare function describeNode(node: Node, includeFields?: Set<string> | null): DescribeNodeOutput;
606
633
 
607
- export { propagatesToMeta as $, type Actor as A, describeNode as B, COMPLETE as C, DATA as D, ERROR as E, isKnownMessageType as F, type GuardAction as G, type HashFn as H, INVALIDATE as I, isPhase2Message as J, isTerminalMessage as K, isV1 as L, type Message as M, type Node as N, type OnMessageHandler as O, PAUSE as P, knownMessageTypes as Q, RESOLVED as R, type SubscribeHints as S, TEARDOWN as T, messageTier as U, type V0 as V, metaSnapshot as W, node as X, normalizeActor as Y, policy as Z, policyFromRules as _, type NodeOptions as a, type NodeInspectorHook as a0, type NodeActions as b, type NodeFn as c, DEFAULT_ACTOR as d, DIRTY as e, type DescribeNodeOutput as f, GuardDenied as g, type GuardDeniedDetails as h, type Messages as i, type NodeDescribeKind as j, type NodeGuard as k, type NodeSink as l, type NodeStatus as m, type NodeTransportOptions as n, type NodeVersionInfo as o, type PolicyAllow as p, type PolicyDeny as q, type PolicyRuleData as r, RESUME as s, type V1 as t, type VersioningLevel as u, type VersioningOptions as v, accessHintForGuard as w, advanceVersion as x, createVersioning as y, defaultHash as z };
634
+ export { policy as $, type Actor as A, createVersioning as B, COMPLETE as C, DATA as D, ERROR as E, defaultHash as F, type GuardAction as G, type HashFn as H, INVALIDATE as I, describeNode as J, isKnownMessageType as K, isPhase2Message as L, type Message as M, type Node as N, type OnMessageHandler as O, PAUSE as P, isTerminalMessage as Q, RESOLVED as R, type SubscribeHints as S, TEARDOWN as T, isV1 as U, type V0 as V, knownMessageTypes as W, messageTier as X, metaSnapshot as Y, node as Z, normalizeActor as _, type NodeOptions as a, policyFromRules as a0, propagatesToMeta as a1, resolveDescribeFields as a2, type NodeInspectorHook as a3, type NodeActions as b, type NodeFn as c, DEFAULT_ACTOR as d, DIRTY as e, type DescribeDetail as f, type DescribeField as g, type DescribeNodeOutput as h, GuardDenied as i, type GuardDeniedDetails as j, type Messages as k, type NodeDescribeKind as l, type NodeGuard as m, type NodeSink as n, type NodeStatus as o, type NodeTransportOptions as p, type NodeVersionInfo as q, type PolicyAllow as r, type PolicyDeny as s, type PolicyRuleData as t, RESUME as u, type V1 as v, type VersioningLevel as w, type VersioningOptions as x, accessHintForGuard as y, advanceVersion as z };
@@ -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() {
@@ -620,6 +647,7 @@ var NodeImpl = class {
620
647
  _producerStarted = false;
621
648
  _connecting = false;
622
649
  _manualEmitUsed = false;
650
+ _hasEmittedData = false;
623
651
  _sinkCount = 0;
624
652
  _singleDepSinkCount = 0;
625
653
  // --- Object/collection state ---
@@ -906,6 +934,7 @@ var NodeImpl = class {
906
934
  this._cleanup = void 0;
907
935
  cleanupFn?.();
908
936
  this._cached = void 0;
937
+ this._hasEmittedData = false;
909
938
  this._lastDepValues = void 0;
910
939
  }
911
940
  this._status = statusAfterMessage(this._status, m);
@@ -915,6 +944,7 @@ var NodeImpl = class {
915
944
  if (t === TEARDOWN) {
916
945
  if (this._opts.resetOnTeardown) {
917
946
  this._cached = void 0;
947
+ this._hasEmittedData = false;
918
948
  }
919
949
  const teardownCleanup = this._cleanup;
920
950
  this._cleanup = void 0;
@@ -945,7 +975,16 @@ var NodeImpl = class {
945
975
  }
946
976
  _emitAutoValue(value) {
947
977
  const wasDirty = this._status === "dirty";
948
- const unchanged = this._equals(this._cached, value);
978
+ let unchanged;
979
+ try {
980
+ unchanged = this._hasEmittedData && this._equals(this._cached, value);
981
+ } catch (eqErr) {
982
+ const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
983
+ const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
984
+ this._downInternal([[ERROR, wrapped]]);
985
+ return;
986
+ }
987
+ this._hasEmittedData = true;
949
988
  if (unchanged) {
950
989
  this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
951
990
  return;
@@ -992,7 +1031,9 @@ var NodeImpl = class {
992
1031
  if (out === void 0) return;
993
1032
  this._emitAutoValue(out);
994
1033
  } catch (err) {
995
- this._downInternal([[ERROR, err]]);
1034
+ const errMsg = err instanceof Error ? err.message : String(err);
1035
+ const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
1036
+ this._downInternal([[ERROR, wrapped]]);
996
1037
  }
997
1038
  }
998
1039
  _onDepDirty(index) {
@@ -1027,7 +1068,11 @@ var NodeImpl = class {
1027
1068
  try {
1028
1069
  if (this._onMessage(msg, index, this._actions)) continue;
1029
1070
  } catch (err) {
1030
- this._downInternal([[ERROR, err]]);
1071
+ const errMsg = err instanceof Error ? err.message : String(err);
1072
+ const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
1073
+ cause: err
1074
+ });
1075
+ this._downInternal([[ERROR, wrapped]]);
1031
1076
  return;
1032
1077
  }
1033
1078
  }
@@ -1600,6 +1645,19 @@ var DynamicNodeImpl = class {
1600
1645
  };
1601
1646
 
1602
1647
  // src/core/meta.ts
1648
+ function resolveDescribeFields(detail, fields) {
1649
+ if (fields != null && fields.length > 0) return new Set(fields);
1650
+ switch (detail) {
1651
+ case "standard":
1652
+ return /* @__PURE__ */ new Set(["type", "status", "value", "deps", "meta", "v"]);
1653
+ case "full":
1654
+ return null;
1655
+ // null = include everything
1656
+ case "minimal":
1657
+ default:
1658
+ return /* @__PURE__ */ new Set(["type", "deps"]);
1659
+ }
1660
+ }
1603
1661
  function inferDescribeType(n) {
1604
1662
  if (n._describeKind != null) return n._describeKind;
1605
1663
  if (!n._hasDeps) return n._fn != null ? "producer" : "state";
@@ -1617,12 +1675,10 @@ function metaSnapshot(node2) {
1617
1675
  }
1618
1676
  return out;
1619
1677
  }
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
- }
1678
+ function describeNode(node2, includeFields) {
1679
+ const all = includeFields == null;
1680
+ const metaKeys = !all && includeFields != null ? [...includeFields].filter((f) => f.startsWith("meta.")).map((f) => f.slice(5)) : null;
1681
+ const wantsMeta = all || includeFields.has("meta") || metaKeys != null && metaKeys.length > 0;
1626
1682
  let type = "state";
1627
1683
  let deps = [];
1628
1684
  if (node2 instanceof NodeImpl) {
@@ -1632,20 +1688,36 @@ function describeNode(node2) {
1632
1688
  type = node2._describeKind ?? "derived";
1633
1689
  deps = [];
1634
1690
  }
1635
- const out = {
1636
- type,
1637
- status: node2.status,
1638
- deps,
1639
- meta
1640
- };
1691
+ const out = { type, deps };
1692
+ if (all || includeFields.has("status")) {
1693
+ out.status = node2.status;
1694
+ }
1695
+ const guard = node2 instanceof NodeImpl && node2._guard || node2 instanceof DynamicNodeImpl && node2._guard || void 0;
1696
+ if (wantsMeta) {
1697
+ const rawMeta = { ...metaSnapshot(node2) };
1698
+ if (guard != null && rawMeta.access === void 0) {
1699
+ rawMeta.access = accessHintForGuard(guard);
1700
+ }
1701
+ if (metaKeys != null && metaKeys.length > 0 && !includeFields.has("meta")) {
1702
+ const filtered = {};
1703
+ for (const k of metaKeys) {
1704
+ if (k in rawMeta) filtered[k] = rawMeta[k];
1705
+ }
1706
+ out.meta = filtered;
1707
+ } else {
1708
+ out.meta = rawMeta;
1709
+ }
1710
+ }
1641
1711
  if (node2.name != null) {
1642
1712
  out.name = node2.name;
1643
1713
  }
1644
- try {
1645
- out.value = node2.get();
1646
- } catch {
1714
+ if (all || includeFields.has("value")) {
1715
+ try {
1716
+ out.value = node2.get();
1717
+ } catch {
1718
+ }
1647
1719
  }
1648
- if (node2.v != null) {
1720
+ if ((all || includeFields.has("v")) && node2.v != null) {
1649
1721
  const vInfo = { id: node2.v.id, version: node2.v.version };
1650
1722
  if ("cid" in node2.v) {
1651
1723
  vInfo.cid = node2.v.cid;
@@ -1653,6 +1725,16 @@ function describeNode(node2) {
1653
1725
  }
1654
1726
  out.v = vInfo;
1655
1727
  }
1728
+ if (all || includeFields.has("guard")) {
1729
+ if (guard != null) {
1730
+ out.guard = accessHintForGuard(guard);
1731
+ }
1732
+ }
1733
+ if (all || includeFields.has("lastMutation")) {
1734
+ if (node2.lastMutation != null) {
1735
+ out.lastMutation = node2.lastMutation;
1736
+ }
1737
+ }
1656
1738
  return out;
1657
1739
  }
1658
1740
 
@@ -1842,6 +1924,23 @@ function resolveSpyTheme(theme) {
1842
1924
  reset: theme.reset ?? ""
1843
1925
  };
1844
1926
  }
1927
+ function resolveObserveDetail(opts) {
1928
+ if (opts == null) return {};
1929
+ const detail = opts.detail;
1930
+ if (detail === "full") {
1931
+ return {
1932
+ ...opts,
1933
+ structured: opts.structured ?? true,
1934
+ timeline: opts.timeline ?? true,
1935
+ causal: opts.causal ?? true,
1936
+ derived: opts.derived ?? true
1937
+ };
1938
+ }
1939
+ if (detail === "minimal") {
1940
+ return { ...opts, structured: opts.structured ?? true };
1941
+ }
1942
+ return opts;
1943
+ }
1845
1944
  function assertLocalName(name, graphName, label) {
1846
1945
  if (name === "") {
1847
1946
  throw new Error(`Graph "${graphName}": ${label} name must be non-empty`);
@@ -2039,6 +2138,22 @@ var Graph = class _Graph {
2039
2138
  if (this._defaultVersioningLevel != null) {
2040
2139
  node2._applyVersioning(this._defaultVersioningLevel);
2041
2140
  }
2141
+ if (node2._deps.length > 0) {
2142
+ for (const dep of node2._deps) {
2143
+ for (const [depName, depNode] of this._nodes) {
2144
+ if (depNode === dep) {
2145
+ this._edges.add(edgeKey(depName, name));
2146
+ break;
2147
+ }
2148
+ }
2149
+ }
2150
+ }
2151
+ for (const [otherName, otherNode] of this._nodes) {
2152
+ if (otherName === name) continue;
2153
+ if (otherNode instanceof NodeImpl && otherNode._deps.includes(node2)) {
2154
+ this._edges.add(edgeKey(name, otherName));
2155
+ }
2156
+ }
2042
2157
  }
2043
2158
  }
2044
2159
  /**
@@ -2425,6 +2540,9 @@ var Graph = class _Graph {
2425
2540
  describe(options) {
2426
2541
  const actor = options?.actor;
2427
2542
  const filter = options?.filter;
2543
+ const includeFields = resolveDescribeFields(options?.detail, options?.fields);
2544
+ const isSpec = options?.format === "spec";
2545
+ const effectiveFields = isSpec ? resolveDescribeFields("minimal") : includeFields;
2428
2546
  const targets = [];
2429
2547
  this._collectObserveTargets("", targets);
2430
2548
  const nodeToPath = /* @__PURE__ */ new Map();
@@ -2434,7 +2552,7 @@ var Graph = class _Graph {
2434
2552
  const nodes = {};
2435
2553
  for (const [p, n] of targets) {
2436
2554
  if (actor != null && !n.allowsObserve(actor)) continue;
2437
- const raw = describeNode(n);
2555
+ const raw = describeNode(n, effectiveFields);
2438
2556
  const deps = n instanceof NodeImpl ? n._deps.map((d) => nodeToPath.get(d) ?? d.name ?? "") : [];
2439
2557
  const { name: _name, ...rest } = raw;
2440
2558
  const entry = { ...rest, deps };
@@ -2455,7 +2573,7 @@ var Graph = class _Graph {
2455
2573
  continue;
2456
2574
  }
2457
2575
  if (normalizedKey === "metaHas") {
2458
- if (!Object.hasOwn(entry.meta, String(fv))) {
2576
+ if (!Object.hasOwn(entry.meta ?? {}, String(fv))) {
2459
2577
  match = false;
2460
2578
  break;
2461
2579
  }
@@ -2488,11 +2606,24 @@ var Graph = class _Graph {
2488
2606
  const prefix = `${sg}${PATH_SEP}`;
2489
2607
  return [...nodeKeys].some((k) => k === sg || k.startsWith(prefix));
2490
2608
  }) : allSubgraphs;
2609
+ const graph = this;
2610
+ const baseOpts = options;
2491
2611
  return {
2492
2612
  name: this.name,
2493
2613
  nodes,
2494
2614
  edges,
2495
- subgraphs
2615
+ subgraphs,
2616
+ expand(detailOrFields) {
2617
+ const merged = { ...baseOpts, format: void 0 };
2618
+ if (Array.isArray(detailOrFields)) {
2619
+ merged.fields = detailOrFields;
2620
+ merged.detail = void 0;
2621
+ } else {
2622
+ merged.detail = detailOrFields;
2623
+ merged.fields = void 0;
2624
+ }
2625
+ return graph.describe(merged);
2626
+ }
2496
2627
  };
2497
2628
  }
2498
2629
  _collectSubgraphs(prefix) {
@@ -2545,14 +2676,15 @@ var Graph = class _Graph {
2545
2676
  observe(pathOrOpts, options) {
2546
2677
  if (typeof pathOrOpts === "string") {
2547
2678
  const path = pathOrOpts;
2548
- const actor2 = options?.actor;
2679
+ const resolved = resolveObserveDetail(options);
2680
+ const actor2 = resolved.actor;
2549
2681
  const target = this.resolve(path);
2550
2682
  if (actor2 != null && !target.allowsObserve(actor2)) {
2551
2683
  throw new GuardDenied({ actor: actor2, action: "observe", nodeName: path });
2552
2684
  }
2553
- const wantsStructured2 = options?.structured === true || options?.timeline === true || options?.causal === true || options?.derived === true;
2685
+ const wantsStructured2 = resolved.structured === true || resolved.timeline === true || resolved.causal === true || resolved.derived === true || resolved.detail === "minimal" || resolved.detail === "full";
2554
2686
  if (wantsStructured2 && _Graph.inspectorEnabled) {
2555
- return this._createObserveResult(path, target, options);
2687
+ return this._createObserveResult(path, target, resolved);
2556
2688
  }
2557
2689
  return {
2558
2690
  subscribe(sink) {
@@ -2568,11 +2700,11 @@ var Graph = class _Graph {
2568
2700
  }
2569
2701
  };
2570
2702
  }
2571
- const opts = pathOrOpts;
2572
- const actor = opts?.actor;
2573
- const wantsStructured = opts?.structured === true || opts?.timeline === true || opts?.causal === true || opts?.derived === true;
2703
+ const opts = resolveObserveDetail(pathOrOpts);
2704
+ const actor = opts.actor;
2705
+ const wantsStructured = opts.structured === true || opts.timeline === true || opts.causal === true || opts.derived === true || opts.detail === "minimal" || opts.detail === "full";
2574
2706
  if (wantsStructured && _Graph.inspectorEnabled) {
2575
- return this._createObserveResultForAll(opts ?? {});
2707
+ return this._createObserveResultForAll(opts);
2576
2708
  }
2577
2709
  return {
2578
2710
  subscribe: (sink) => {
@@ -2604,6 +2736,7 @@ var Graph = class _Graph {
2604
2736
  const timeline = options.timeline === true;
2605
2737
  const causal = options.causal === true;
2606
2738
  const derived2 = options.derived === true;
2739
+ const minimal = options.detail === "minimal";
2607
2740
  const result = {
2608
2741
  values: {},
2609
2742
  dirtyCount: 0,
@@ -2649,6 +2782,11 @@ var Graph = class _Graph {
2649
2782
  if (t === DATA) {
2650
2783
  result.values[path] = m[1];
2651
2784
  result.events.push({ type: "data", path, data: m[1], ...base, ...withCausal });
2785
+ } else if (minimal) {
2786
+ if (t === DIRTY) result.dirtyCount++;
2787
+ else if (t === RESOLVED) result.resolvedCount++;
2788
+ else if (t === COMPLETE && !result.errored) result.completedCleanly = true;
2789
+ else if (t === ERROR) result.errored = true;
2652
2790
  } else if (t === DIRTY) {
2653
2791
  result.dirtyCount++;
2654
2792
  result.events.push({ type: "dirty", path, ...base });
@@ -2664,6 +2802,8 @@ var Graph = class _Graph {
2664
2802
  }
2665
2803
  }
2666
2804
  });
2805
+ const graph = this;
2806
+ const basePath = path;
2667
2807
  return {
2668
2808
  get values() {
2669
2809
  return result.values;
@@ -2686,11 +2826,28 @@ var Graph = class _Graph {
2686
2826
  dispose() {
2687
2827
  unsub();
2688
2828
  detachInspectorHook?.();
2829
+ },
2830
+ expand(extra) {
2831
+ unsub();
2832
+ detachInspectorHook?.();
2833
+ const merged = { ...options };
2834
+ if (typeof extra === "string") {
2835
+ merged.detail = extra;
2836
+ } else {
2837
+ Object.assign(merged, extra);
2838
+ }
2839
+ const resolvedTarget = graph.resolve(basePath);
2840
+ return graph._createObserveResult(
2841
+ basePath,
2842
+ resolvedTarget,
2843
+ resolveObserveDetail(merged)
2844
+ );
2689
2845
  }
2690
2846
  };
2691
2847
  }
2692
2848
  _createObserveResultForAll(options) {
2693
2849
  const timeline = options.timeline === true;
2850
+ const minimal = options.detail === "minimal";
2694
2851
  const result = {
2695
2852
  values: {},
2696
2853
  dirtyCount: 0,
@@ -2712,6 +2869,11 @@ var Graph = class _Graph {
2712
2869
  if (t === DATA) {
2713
2870
  result.values[path] = m[1];
2714
2871
  result.events.push({ type: "data", path, data: m[1], ...base });
2872
+ } else if (minimal) {
2873
+ if (t === DIRTY) result.dirtyCount++;
2874
+ else if (t === RESOLVED) result.resolvedCount++;
2875
+ else if (t === COMPLETE && !result.errored) result.completedCleanly = true;
2876
+ else if (t === ERROR) result.errored = true;
2715
2877
  } else if (t === DIRTY) {
2716
2878
  result.dirtyCount++;
2717
2879
  result.events.push({ type: "dirty", path, ...base });
@@ -2728,6 +2890,7 @@ var Graph = class _Graph {
2728
2890
  }
2729
2891
  })
2730
2892
  );
2893
+ const graph = this;
2731
2894
  return {
2732
2895
  get values() {
2733
2896
  return result.values;
@@ -2749,6 +2912,16 @@ var Graph = class _Graph {
2749
2912
  },
2750
2913
  dispose() {
2751
2914
  for (const u of unsubs) u();
2915
+ },
2916
+ expand(extra) {
2917
+ for (const u of unsubs) u();
2918
+ const merged = { ...options };
2919
+ if (typeof extra === "string") {
2920
+ merged.detail = extra;
2921
+ } else {
2922
+ Object.assign(merged, extra);
2923
+ }
2924
+ return graph._createObserveResultForAll(resolveObserveDetail(merged));
2752
2925
  }
2753
2926
  };
2754
2927
  }
@@ -2824,6 +2997,9 @@ var Graph = class _Graph {
2824
2997
  },
2825
2998
  dispose() {
2826
2999
  stop2();
3000
+ },
3001
+ expand() {
3002
+ throw new Error("expand() requires inspector mode (Graph.inspectorEnabled = true)");
2827
3003
  }
2828
3004
  };
2829
3005
  const pushEvent = (path, message) => {
@@ -2924,16 +3100,16 @@ var Graph = class _Graph {
2924
3100
  * @returns Rendered graph text.
2925
3101
  */
2926
3102
  dumpGraph(options = {}) {
2927
- const described = this.describe({
3103
+ const { expand: _, ...described } = this.describe({
2928
3104
  actor: options.actor,
2929
- filter: options.filter
3105
+ filter: options.filter,
3106
+ detail: "standard"
2930
3107
  });
2931
3108
  const includeEdges = options.includeEdges ?? true;
2932
3109
  const includeSubgraphs = options.includeSubgraphs ?? true;
2933
3110
  if (options.format === "json") {
2934
3111
  const payload = {
2935
- name: described.name,
2936
- nodes: described.nodes,
3112
+ ...described,
2937
3113
  edges: includeEdges ? described.edges : [],
2938
3114
  subgraphs: includeSubgraphs ? described.subgraphs : []
2939
3115
  };
@@ -3003,10 +3179,11 @@ var Graph = class _Graph {
3003
3179
  * @returns Persistable snapshot with sorted keys.
3004
3180
  */
3005
3181
  snapshot() {
3006
- const d = this.describe();
3182
+ const { expand: _, ...d } = this.describe({ detail: "full" });
3007
3183
  const sortedNodes = {};
3008
3184
  for (const key of Object.keys(d.nodes).sort()) {
3009
- sortedNodes[key] = d.nodes[key];
3185
+ const { lastMutation: _lm, guard: _g, ...node2 } = d.nodes[key];
3186
+ sortedNodes[key] = node2;
3010
3187
  }
3011
3188
  const sortedSubgraphs = [...d.subgraphs].sort();
3012
3189
  return { ...d, version: 1, nodes: sortedNodes, subgraphs: sortedSubgraphs };
@@ -3160,16 +3337,22 @@ var Graph = class _Graph {
3160
3337
  if (!pending) return;
3161
3338
  pending = false;
3162
3339
  try {
3163
- const described = this.describe();
3340
+ const { expand: _expand, ...raw } = this.describe({ detail: "full" });
3341
+ const cleanNodes = {};
3342
+ for (const [p, n] of Object.entries(raw.nodes)) {
3343
+ const { lastMutation: _lm, guard: _g, ...node2 } = n;
3344
+ cleanNodes[p] = node2;
3345
+ }
3346
+ const described = { ...raw, nodes: cleanNodes };
3164
3347
  const snapshot = { ...described, version: SNAPSHOT_VERSION };
3165
3348
  seq += 1;
3166
3349
  const shouldCompact = lastDescribe == null || seq % compactEvery === 0;
3167
3350
  if (shouldCompact) {
3168
- adapter.save({ mode: "full", snapshot, seq });
3351
+ adapter.save(this.name, { mode: "full", snapshot, seq });
3169
3352
  } else {
3170
3353
  const previous = lastDescribe;
3171
3354
  if (previous == null) return;
3172
- adapter.save({
3355
+ adapter.save(this.name, {
3173
3356
  mode: "diff",
3174
3357
  diff: _Graph.diff(previous, described),
3175
3358
  snapshot,
@@ -3190,8 +3373,10 @@ var Graph = class _Graph {
3190
3373
  const triggeredByTier = messages.some((m) => messageTier(m[0]) >= 2);
3191
3374
  if (!triggeredByTier) return;
3192
3375
  if (options.filter) {
3193
- const described = this.describe().nodes[path];
3194
- if (described == null || !options.filter(path, described)) return;
3376
+ const nd = this.resolve(path);
3377
+ if (nd == null) return;
3378
+ const described = describeNode(nd, resolveDescribeFields("standard"));
3379
+ if (!options.filter(path, described)) return;
3195
3380
  }
3196
3381
  schedule();
3197
3382
  });