@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
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # GraphReFly
2
2
 
3
- **Reactive graph protocol for human + LLM co-operation.**
3
+ **Describe what matters. It watches, filters, and explains — persistently.**
4
4
 
5
- One primitive. Zero dependencies. Composable nodes with glitch-free diamond resolution, two-phase push propagation, durable streaming, and framework adapters for React, Vue, Svelte, Solid, and NestJS.
5
+ You're buried under emails, alerts, feeds, and messages. You can't process it all. GraphReFly lets you describe automations in plain language, review them visually, run them persistently, and trace every decision back to its source.
6
6
 
7
7
  [![npm](https://img.shields.io/npm/v/@graphrefly/graphrefly?color=blue)](https://www.npmjs.com/package/@graphrefly/graphrefly)
8
8
  [![license](https://img.shields.io/github/license/graphrefly/graphrefly-ts)](./LICENSE)
@@ -11,6 +11,18 @@ One primitive. Zero dependencies. Composable nodes with glitch-free diamond reso
11
11
 
12
12
  ---
13
13
 
14
+ <!-- TODO: Demo 0 GIF/video — NL → flow view → running → "why was this flagged?" -->
15
+
16
+ ## What can you do with it?
17
+
18
+ **Email triage** — "Watch my inbox. Urgent emails from my team go to a priority list. Newsletters get summarized weekly. Everything else, count by sender." It watches, classifies, and alerts — and when you ask "why was this flagged?", it walks you through the reasoning.
19
+
20
+ **Spending alerts** — Connect bank transactions to budget categories. Get a push notification when monthly dining exceeds your target. No polling, no manual checks — changes propagate the moment data arrives.
21
+
22
+ **Knowledge management** — Notes, bookmarks, highlights flow in. Contradictions surface automatically. Related ideas link themselves. Your second brain stays current without you maintaining it.
23
+
24
+ ---
25
+
14
26
  ## Quick start
15
27
 
16
28
  ```bash
@@ -30,20 +42,24 @@ count.set(3);
30
42
  // → doubled: 6
31
43
  ```
32
44
 
45
+ ## How it works
46
+
47
+ You describe what you need — an LLM composes a reactive graph (like SQL for data flows). The graph runs persistently, checkpoints its state, and traces every decision through a causal chain. Ask "why?" at any point and get a human-readable explanation from source to conclusion.
48
+
33
49
  ## Why GraphReFly?
34
50
 
35
- Most state libraries solve **one** problem well. GraphReFly solves the space between them:
36
-
37
- | | Zustand / Jotai | RxJS | XState | TC39 Signals | **GraphReFly** |
38
- |--|-----------------|------|--------|-------------|---------------|
39
- | Simple store API | yes | no | no | yes | **yes** |
40
- | Streaming operators | no | yes | no | no | **yes** |
41
- | Diamond resolution | no | n/a | n/a | partial | **glitch-free** |
42
- | Graph introspection | no | no | visual | no | **describe / observe / diagram** |
43
- | Durable checkpoints | no | no | persistence | no | **file / SQLite / IndexedDB** |
44
- | LLM orchestration | no | no | no | no | **agentLoop / chatStream / toolRegistry** |
45
- | Framework adapters | React | Angular | React / Vue | varies | **React / Vue / Svelte / Solid / NestJS** |
46
- | Dependencies | 0 | 0 | 0 | n/a | **0** |
51
+ | | Zustand / Jotai | RxJS | XState | LangGraph | TC39 Signals | **GraphReFly** |
52
+ |--|-----------------|------|--------|-----------|-------------|---------------|
53
+ | Simple store API | yes | no | no | no | yes | **yes** |
54
+ | Streaming operators | no | yes | no | no | no | **yes** |
55
+ | Diamond resolution | no | n/a | n/a | n/a | partial | **glitch-free** |
56
+ | Graph introspection | no | no | visual | checkpoints | no | **describe / observe / diagram** |
57
+ | Causal tracing | no | no | no | no | no | **explain every decision** |
58
+ | Durable checkpoints | no | no | persistence | yes | no | **file / SQLite / IndexedDB** |
59
+ | LLM orchestration | no | no | no | yes | no | **agentLoop / chatStream / toolRegistry** |
60
+ | NL graph composition | no | no | no | no | no | **graphFromSpec / llmCompose** |
61
+ | Framework adapters | React | Angular | React / Vue | n/a | varies | **React / Vue / Svelte / Solid / NestJS** |
62
+ | Dependencies | 0 | 0 | 0 | many | n/a | **0** |
47
63
 
48
64
  ## One primitive
49
65
 
@@ -2,10 +2,10 @@ import {
2
2
  GRAPH_META_SEGMENT,
3
3
  Graph,
4
4
  reachable
5
- } from "./chunk-6W5SGIGB.js";
5
+ } from "./chunk-WYI7YW54.js";
6
6
  import {
7
7
  __export
8
- } from "./chunk-5X3LAO3B.js";
8
+ } from "./chunk-QTZSBQGJ.js";
9
9
 
10
10
  // src/graph/index.ts
11
11
  var graph_exports = {};
@@ -104,4 +104,4 @@ export {
104
104
  replayWAL,
105
105
  graph_exports
106
106
  };
107
- //# sourceMappingURL=chunk-HP7OKEOE.js.map
107
+ //# sourceMappingURL=chunk-A2AJJOSJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/graph/index.ts","../src/graph/codec.ts"],"sourcesContent":["/**\n * Graph container: registry, wiring, introspection (Phase 1).\n */\n\nexport {\n\tcreateDagCborCodec,\n\tcreateDagCborZstdCodec,\n\ttype DeltaCheckpoint,\n\ttype EvictedSubgraphInfo,\n\ttype EvictionPolicy,\n\ttype GraphCodec,\n\tJsonCodec,\n\ttype LazyGraphCodec,\n\tnegotiateCodec,\n\treplayWAL,\n\ttype WALEntry,\n} from \"./codec.js\";\nexport {\n\ttype AutoCheckpointAdapter,\n\ttype DescribeFilter,\n\tGRAPH_META_SEGMENT,\n\tGraph,\n\ttype GraphActorOptions,\n\ttype GraphAutoCheckpointHandle,\n\ttype GraphAutoCheckpointOptions,\n\ttype GraphCheckpointRecord,\n\ttype GraphDescribeOptions,\n\ttype GraphDescribeOutput,\n\ttype GraphDiagramDirection,\n\ttype GraphDiagramOptions,\n\ttype GraphDiffChange,\n\ttype GraphDiffResult,\n\ttype GraphDumpOptions,\n\ttype GraphFactoryContext,\n\ttype GraphNodeFactory,\n\ttype GraphObserveAll,\n\ttype GraphObserveOne,\n\ttype GraphOptions,\n\ttype GraphPersistSnapshot,\n\ttype GraphSpyHandle,\n\ttype GraphSpyOptions,\n\ttype GraphSpyTheme,\n\ttype GraphSpyThemeName,\n\ttype ObserveDetail,\n\ttype ObserveEvent,\n\ttype ObserveOptions,\n\ttype ObserveResult,\n\ttype ReachableDirection,\n\ttype ReachableOptions,\n\treachable,\n\ttype TraceEntry,\n} from \"./graph.js\";\n","/**\n * GraphCodec — pluggable serialization for graph snapshots (Phase 8.6).\n *\n * Design reference: `archive/docs/SESSION-serialization-memory-footprint.md`\n *\n * The codec interface decouples snapshot format from graph internals.\n * Default is JSON (current behavior). DAG-CBOR and compressed variants\n * ship as optional codecs. FlatBuffers/Arrow for advanced tiers.\n *\n * Tiered representation:\n * HOT — JS objects (live propagation, no codec involved)\n * WARM — DAG-CBOR in-memory buffer (lazy hydration, delta checkpoints)\n * COLD — Arrow/Parquet (bulk storage, ML pipelines, archival)\n * PEEK — FlatBuffers (zero-copy read from dormant graph)\n */\n\nimport type { GraphPersistSnapshot } from \"./graph.js\";\n\n// ---------------------------------------------------------------------------\n// Core codec interface\n// ---------------------------------------------------------------------------\n\n/**\n * Encode/decode graph snapshots to/from binary.\n *\n * Implementations must be deterministic: `encode(x)` always produces the\n * same bytes for the same input. This is critical for CID computation (V1)\n * and snapshot hash-comparison.\n */\nexport interface GraphCodec {\n\t/** MIME-like content type identifier (e.g. \"application/dag-cbor+zstd\"). */\n\treadonly contentType: string;\n\n\t/** Human-readable name for diagnostics. */\n\treadonly name: string;\n\n\t/** Encode a snapshot to binary. */\n\tencode(snapshot: GraphPersistSnapshot): Uint8Array;\n\n\t/**\n\t * Decode binary back to a snapshot.\n\t *\n\t * For lazy codecs, this may return a proxy that decodes nodes on access\n\t * (see {@link LazyGraphCodec}).\n\t */\n\tdecode(buffer: Uint8Array): GraphPersistSnapshot;\n}\n\n/**\n * Extended codec that supports lazy (on-demand) node decoding.\n *\n * `decodeLazy` returns a snapshot where `nodes` is a Proxy — individual\n * nodes are decoded only when accessed. This enables near-zero cold-start\n * for large graphs (decode envelope + topology, skip node values until read).\n */\nexport interface LazyGraphCodec extends GraphCodec {\n\t/** Decode envelope and topology; defer node value decoding to access time. */\n\tdecodeLazy(buffer: Uint8Array): GraphPersistSnapshot;\n}\n\n// ---------------------------------------------------------------------------\n// Delta checkpoint types (requires V0 — Phase 6.0)\n// ---------------------------------------------------------------------------\n\n/**\n * A delta checkpoint: only the nodes that changed since last checkpoint.\n *\n * Append-only: each delta is identified by `seq` (monotonic). A full\n * snapshot is taken every `compactEvery` deltas for WAL compaction.\n */\nexport interface DeltaCheckpoint {\n\t/** Monotonic sequence number. */\n\tseq: number;\n\t/** Graph name. */\n\tname: string;\n\t/** Base snapshot seq this delta applies to (0 = initial full snapshot). */\n\tbaseSec: number;\n\t/** Only nodes with version > lastCheckpoint. Keyed by node name. */\n\tnodes: Record<\n\t\tstring,\n\t\t{\n\t\t\t/** V0 version at time of checkpoint. */\n\t\t\tversion: number;\n\t\t\t/** Serialized node value (codec-dependent). */\n\t\t\tvalue: unknown;\n\t\t\t/** Meta snapshot (only if materialized). */\n\t\t\tmeta?: Record<string, unknown>;\n\t\t}\n\t>;\n\t/** Nodes removed since last checkpoint. */\n\tremoved: string[];\n\t/** Edges added since last checkpoint. */\n\tedgesAdded: ReadonlyArray<{ from: string; to: string }>;\n\t/** Edges removed since last checkpoint. */\n\tedgesRemoved: ReadonlyArray<{ from: string; to: string }>;\n\t/** Timestamp (wall-clock ns) of this checkpoint. */\n\ttimestampNs: bigint;\n}\n\n/**\n * WAL entry: either a full snapshot or a delta.\n */\nexport type WALEntry =\n\t| { type: \"full\"; snapshot: GraphPersistSnapshot; seq: number }\n\t| { type: \"delta\"; delta: DeltaCheckpoint };\n\n// ---------------------------------------------------------------------------\n// Eviction policy (dormant subgraph management)\n// ---------------------------------------------------------------------------\n\n/**\n * Policy for evicting dormant subgraphs to reduce memory.\n *\n * When a subgraph hasn't propagated for `idleTimeoutMs`, it is serialized\n * using the graph's codec and JS objects are released. Re-hydrated on next\n * access (read, propagation, describe).\n */\nexport interface EvictionPolicy {\n\t/** Milliseconds of inactivity before eviction. */\n\tidleTimeoutMs: number;\n\t/** Codec to use for serializing evicted subgraphs (default: graph's codec). */\n\tcodec?: GraphCodec;\n}\n\n/** Metadata about an evicted subgraph, exposed via describe(). */\nexport interface EvictedSubgraphInfo {\n\t/** True if currently evicted (serialized, JS objects released). */\n\tevicted: true;\n\t/** Wall-clock ns of last propagation before eviction. */\n\tlastActiveNs: bigint;\n\t/** Size of serialized buffer in bytes. */\n\tserializedBytes: number;\n\t/** Codec used for serialization. */\n\tcodecName: string;\n}\n\n// ---------------------------------------------------------------------------\n// JSON codec (default — wraps current behavior)\n// ---------------------------------------------------------------------------\n\n/**\n * Default JSON codec. Wraps `JSON.stringify`/`JSON.parse` with deterministic\n * key ordering (matching current `snapshot()` behavior).\n */\nexport const JsonCodec: GraphCodec = {\n\tcontentType: \"application/json\",\n\tname: \"json\",\n\n\tencode(snapshot: GraphPersistSnapshot): Uint8Array {\n\t\t// Deterministic: snapshot() already sorts keys.\n\t\tconst json = JSON.stringify(snapshot);\n\t\treturn new TextEncoder().encode(json);\n\t},\n\n\tdecode(buffer: Uint8Array): GraphPersistSnapshot {\n\t\tconst json = new TextDecoder().decode(buffer);\n\t\treturn JSON.parse(json) as GraphPersistSnapshot;\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// DAG-CBOR codec (stub — requires @ipld/dag-cbor)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a DAG-CBOR codec.\n *\n * Requires `@ipld/dag-cbor` as a peer dependency. ~40-50% smaller than JSON,\n * deterministic encoding (required for V1 CID), CID links as native type.\n *\n * @example\n * ```ts\n * import * as dagCbor from \"@ipld/dag-cbor\";\n * const codec = createDagCborCodec(dagCbor);\n * const bytes = codec.encode(graph.snapshot());\n * ```\n */\nexport function createDagCborCodec(dagCbor: {\n\tencode: (value: unknown) => Uint8Array;\n\tdecode: (bytes: Uint8Array) => unknown;\n}): GraphCodec {\n\treturn {\n\t\tcontentType: \"application/dag-cbor\",\n\t\tname: \"dag-cbor\",\n\t\tencode: (snapshot) => dagCbor.encode(snapshot),\n\t\tdecode: (buffer) => dagCbor.decode(buffer) as GraphPersistSnapshot,\n\t};\n}\n\n/**\n * Create a DAG-CBOR + zstd codec. ~80-90% smaller than JSON.\n *\n * Requires `@ipld/dag-cbor` and a zstd implementation (e.g. `fzstd` for\n * browser, `node:zlib` for Node.js).\n *\n * @example\n * ```ts\n * import * as dagCbor from \"@ipld/dag-cbor\";\n * import { compressSync, decompressSync } from \"fzstd\";\n * const codec = createDagCborZstdCodec(dagCbor, { compressSync, decompressSync });\n * ```\n */\nexport function createDagCborZstdCodec(\n\tdagCbor: {\n\t\tencode: (value: unknown) => Uint8Array;\n\t\tdecode: (bytes: Uint8Array) => unknown;\n\t},\n\tzstd: {\n\t\tcompressSync: (data: Uint8Array) => Uint8Array;\n\t\tdecompressSync: (data: Uint8Array) => Uint8Array;\n\t},\n): GraphCodec {\n\treturn {\n\t\tcontentType: \"application/dag-cbor+zstd\",\n\t\tname: \"dag-cbor-zstd\",\n\t\tencode: (snapshot) => zstd.compressSync(dagCbor.encode(snapshot)),\n\t\tdecode: (buffer) => dagCbor.decode(zstd.decompressSync(buffer)) as GraphPersistSnapshot,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Codec negotiation (for peerGraph)\n// ---------------------------------------------------------------------------\n\n/**\n * Negotiate a common codec between two peers.\n *\n * Each peer advertises its supported codecs (ordered by preference).\n * Returns the first codec supported by both, or null if none.\n */\nexport function negotiateCodec(\n\tlocalPreference: readonly GraphCodec[],\n\tremoteContentTypes: readonly string[],\n): GraphCodec | null {\n\tconst remoteSet = new Set(remoteContentTypes);\n\tfor (const codec of localPreference) {\n\t\tif (remoteSet.has(codec.contentType)) return codec;\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// WAL helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Reconstruct a snapshot from a WAL (full snapshot + sequence of deltas).\n *\n * Applies deltas in order on top of the base snapshot. Validates that\n * delta `baseSec` chains correctly.\n */\nexport function replayWAL(entries: readonly WALEntry[]): GraphPersistSnapshot {\n\tif (entries.length === 0) {\n\t\tthrow new Error(\"WAL is empty — need at least one full snapshot\");\n\t}\n\n\tconst first = entries[0]!;\n\tif (first.type !== \"full\") {\n\t\tthrow new Error(\"WAL must start with a full snapshot\");\n\t}\n\n\t// Deep clone the base snapshot so we can mutate it.\n\tconst result: GraphPersistSnapshot = JSON.parse(JSON.stringify(first.snapshot));\n\n\tfor (let i = 1; i < entries.length; i++) {\n\t\tconst entry = entries[i]!;\n\t\tif (entry.type === \"full\") {\n\t\t\t// A compaction point — replace the entire result.\n\t\t\tObject.assign(result, JSON.parse(JSON.stringify(entry.snapshot)));\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst delta = entry.delta;\n\n\t\t// Apply node changes.\n\t\tfor (const [name, patch] of Object.entries(delta.nodes)) {\n\t\t\tif (result.nodes[name]) {\n\t\t\t\tresult.nodes[name]!.value = patch.value;\n\t\t\t\tif (patch.meta) {\n\t\t\t\t\tresult.nodes[name]!.meta = patch.meta;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Apply removals.\n\t\tfor (const name of delta.removed) {\n\t\t\tdelete result.nodes[name];\n\t\t}\n\n\t\t// Apply edge changes.\n\t\tconst edges = [...result.edges];\n\t\tfor (const edge of delta.edgesRemoved) {\n\t\t\tconst idx = edges.findIndex((e) => e.from === edge.from && e.to === edge.to);\n\t\t\tif (idx !== -1) edges.splice(idx, 1);\n\t\t}\n\t\tfor (const edge of delta.edgesAdded) {\n\t\t\tedges.push(edge);\n\t\t}\n\t\t(result as unknown as { edges: typeof edges }).edges = edges;\n\t}\n\n\treturn result;\n}\n"],"mappings":";;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACgJO,IAAM,YAAwB;AAAA,EACpC,aAAa;AAAA,EACb,MAAM;AAAA,EAEN,OAAO,UAA4C;AAElD,UAAM,OAAO,KAAK,UAAU,QAAQ;AACpC,WAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,EACrC;AAAA,EAEA,OAAO,QAA0C;AAChD,UAAM,OAAO,IAAI,YAAY,EAAE,OAAO,MAAM;AAC5C,WAAO,KAAK,MAAM,IAAI;AAAA,EACvB;AACD;AAmBO,SAAS,mBAAmB,SAGpB;AACd,SAAO;AAAA,IACN,aAAa;AAAA,IACb,MAAM;AAAA,IACN,QAAQ,CAAC,aAAa,QAAQ,OAAO,QAAQ;AAAA,IAC7C,QAAQ,CAAC,WAAW,QAAQ,OAAO,MAAM;AAAA,EAC1C;AACD;AAeO,SAAS,uBACf,SAIA,MAIa;AACb,SAAO;AAAA,IACN,aAAa;AAAA,IACb,MAAM;AAAA,IACN,QAAQ,CAAC,aAAa,KAAK,aAAa,QAAQ,OAAO,QAAQ,CAAC;AAAA,IAChE,QAAQ,CAAC,WAAW,QAAQ,OAAO,KAAK,eAAe,MAAM,CAAC;AAAA,EAC/D;AACD;AAYO,SAAS,eACf,iBACA,oBACoB;AACpB,QAAM,YAAY,IAAI,IAAI,kBAAkB;AAC5C,aAAW,SAAS,iBAAiB;AACpC,QAAI,UAAU,IAAI,MAAM,WAAW,EAAG,QAAO;AAAA,EAC9C;AACA,SAAO;AACR;AAYO,SAAS,UAAU,SAAoD;AAC7E,MAAI,QAAQ,WAAW,GAAG;AACzB,UAAM,IAAI,MAAM,qDAAgD;AAAA,EACjE;AAEA,QAAM,QAAQ,QAAQ,CAAC;AACvB,MAAI,MAAM,SAAS,QAAQ;AAC1B,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACtD;AAGA,QAAM,SAA+B,KAAK,MAAM,KAAK,UAAU,MAAM,QAAQ,CAAC;AAE9E,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACxC,UAAM,QAAQ,QAAQ,CAAC;AACvB,QAAI,MAAM,SAAS,QAAQ;AAE1B,aAAO,OAAO,QAAQ,KAAK,MAAM,KAAK,UAAU,MAAM,QAAQ,CAAC,CAAC;AAChE;AAAA,IACD;AAEA,UAAM,QAAQ,MAAM;AAGpB,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,KAAK,GAAG;AACxD,UAAI,OAAO,MAAM,IAAI,GAAG;AACvB,eAAO,MAAM,IAAI,EAAG,QAAQ,MAAM;AAClC,YAAI,MAAM,MAAM;AACf,iBAAO,MAAM,IAAI,EAAG,OAAO,MAAM;AAAA,QAClC;AAAA,MACD;AAAA,IACD;AAGA,eAAW,QAAQ,MAAM,SAAS;AACjC,aAAO,OAAO,MAAM,IAAI;AAAA,IACzB;AAGA,UAAM,QAAQ,CAAC,GAAG,OAAO,KAAK;AAC9B,eAAW,QAAQ,MAAM,cAAc;AACtC,YAAM,MAAM,MAAM,UAAU,CAAC,MAAM,EAAE,SAAS,KAAK,QAAQ,EAAE,OAAO,KAAK,EAAE;AAC3E,UAAI,QAAQ,GAAI,OAAM,OAAO,KAAK,CAAC;AAAA,IACpC;AACA,eAAW,QAAQ,MAAM,YAAY;AACpC,YAAM,KAAK,IAAI;AAAA,IAChB;AACA,IAAC,OAA8C,QAAQ;AAAA,EACxD;AAEA,SAAO;AACR;","names":[]}
@@ -1,7 +1,11 @@
1
+ import {
2
+ ResettableTimer
3
+ } from "./chunk-WZ2Z2CRV.js";
1
4
  import {
2
5
  describeNode,
3
- metaSnapshot
4
- } from "./chunk-O3PI7W45.js";
6
+ metaSnapshot,
7
+ resolveDescribeFields
8
+ } from "./chunk-LR2CLSEF.js";
5
9
  import {
6
10
  COMPLETE,
7
11
  DATA,
@@ -43,7 +47,7 @@ import {
43
47
  propagatesToMeta,
44
48
  state,
45
49
  wallClockNs
46
- } from "./chunk-5X3LAO3B.js";
50
+ } from "./chunk-QTZSBQGJ.js";
47
51
 
48
52
  // src/core/index.ts
49
53
  var core_exports = {};
@@ -59,6 +63,7 @@ __export(core_exports, {
59
63
  PAUSE: () => PAUSE,
60
64
  RESOLVED: () => RESOLVED,
61
65
  RESUME: () => RESUME,
66
+ ResettableTimer: () => ResettableTimer,
62
67
  TEARDOWN: () => TEARDOWN,
63
68
  accessHintForGuard: () => accessHintForGuard,
64
69
  advanceVersion: () => advanceVersion,
@@ -87,6 +92,7 @@ __export(core_exports, {
87
92
  policyFromRules: () => policyFromRules,
88
93
  producer: () => producer,
89
94
  propagatesToMeta: () => propagatesToMeta,
95
+ resolveDescribeFields: () => resolveDescribeFields,
90
96
  state: () => state,
91
97
  wallClockNs: () => wallClockNs
92
98
  });
@@ -94,4 +100,4 @@ __export(core_exports, {
94
100
  export {
95
101
  core_exports
96
102
  };
97
- //# sourceMappingURL=chunk-CP6MNKAA.js.map
103
+ //# sourceMappingURL=chunk-E7OH6ZAZ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/index.ts"],"sourcesContent":["/**\n * Core layer: message protocol, node primitive, lifecycle (Phase 0).\n */\nexport * from \"./actor.js\";\nexport * from \"./batch.js\";\nexport { monotonicNs, wallClockNs } from \"./clock.js\";\nexport * from \"./dynamic-node.js\";\nexport * from \"./guard.js\";\nexport * from \"./messages.js\";\nexport * from \"./meta.js\";\nexport {\n\ttype Node,\n\ttype NodeActions,\n\ttype NodeDescribeKind,\n\ttype NodeFn,\n\ttype NodeOptions,\n\ttype NodeSink,\n\ttype NodeStatus,\n\ttype NodeTransportOptions,\n\tnode,\n\ttype OnMessageHandler,\n\ttype SubscribeHints,\n} from \"./node.js\";\nexport * from \"./sugar.js\";\nexport {\n\tadvanceVersion,\n\tcreateVersioning,\n\tdefaultHash,\n\ttype HashFn,\n\tisV1,\n\ttype NodeVersionInfo,\n\ttype V0,\n\ttype V1,\n\ttype VersioningLevel,\n\ttype VersioningOptions,\n} from \"./versioning.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../src/core/index.ts"],"sourcesContent":["/**\n * Core layer: message protocol, node primitive, lifecycle (Phase 0).\n */\nexport * from \"./actor.js\";\nexport * from \"./batch.js\";\nexport { monotonicNs, wallClockNs } from \"./clock.js\";\nexport * from \"./dynamic-node.js\";\nexport * from \"./guard.js\";\nexport * from \"./messages.js\";\nexport * from \"./meta.js\";\nexport {\n\ttype Node,\n\ttype NodeActions,\n\ttype NodeDescribeKind,\n\ttype NodeFn,\n\ttype NodeOptions,\n\ttype NodeSink,\n\ttype NodeStatus,\n\ttype NodeTransportOptions,\n\tnode,\n\ttype OnMessageHandler,\n\ttype SubscribeHints,\n} from \"./node.js\";\nexport * from \"./sugar.js\";\nexport { ResettableTimer } from \"./timer.js\";\nexport {\n\tadvanceVersion,\n\tcreateVersioning,\n\tdefaultHash,\n\ttype HashFn,\n\tisV1,\n\ttype NodeVersionInfo,\n\ttype V0,\n\ttype V1,\n\ttype VersioningLevel,\n\ttype VersioningOptions,\n} from \"./versioning.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
@@ -0,0 +1,106 @@
1
+ import {
2
+ DynamicNodeImpl,
3
+ NodeImpl,
4
+ accessHintForGuard
5
+ } from "./chunk-QTZSBQGJ.js";
6
+
7
+ // src/core/meta.ts
8
+ function resolveDescribeFields(detail, fields) {
9
+ if (fields != null && fields.length > 0) return new Set(fields);
10
+ switch (detail) {
11
+ case "standard":
12
+ return /* @__PURE__ */ new Set(["type", "status", "value", "deps", "meta", "v"]);
13
+ case "full":
14
+ return null;
15
+ // null = include everything
16
+ case "minimal":
17
+ default:
18
+ return /* @__PURE__ */ new Set(["type", "deps"]);
19
+ }
20
+ }
21
+ function inferDescribeType(n) {
22
+ if (n._describeKind != null) return n._describeKind;
23
+ if (!n._hasDeps) return n._fn != null ? "producer" : "state";
24
+ if (n._fn == null) return "derived";
25
+ if (n._manualEmitUsed) return "operator";
26
+ return "derived";
27
+ }
28
+ function metaSnapshot(node) {
29
+ const out = {};
30
+ for (const [key, child] of Object.entries(node.meta)) {
31
+ try {
32
+ out[key] = child.get();
33
+ } catch {
34
+ }
35
+ }
36
+ return out;
37
+ }
38
+ function describeNode(node, includeFields) {
39
+ const all = includeFields == null;
40
+ const metaKeys = !all && includeFields != null ? [...includeFields].filter((f) => f.startsWith("meta.")).map((f) => f.slice(5)) : null;
41
+ const wantsMeta = all || includeFields.has("meta") || metaKeys != null && metaKeys.length > 0;
42
+ let type = "state";
43
+ let deps = [];
44
+ if (node instanceof NodeImpl) {
45
+ type = inferDescribeType(node);
46
+ deps = node._deps.map((d) => d.name ?? "");
47
+ } else if (node instanceof DynamicNodeImpl) {
48
+ type = node._describeKind ?? "derived";
49
+ deps = [];
50
+ }
51
+ const out = { type, deps };
52
+ if (all || includeFields.has("status")) {
53
+ out.status = node.status;
54
+ }
55
+ const guard = node instanceof NodeImpl && node._guard || node instanceof DynamicNodeImpl && node._guard || void 0;
56
+ if (wantsMeta) {
57
+ const rawMeta = { ...metaSnapshot(node) };
58
+ if (guard != null && rawMeta.access === void 0) {
59
+ rawMeta.access = accessHintForGuard(guard);
60
+ }
61
+ if (metaKeys != null && metaKeys.length > 0 && !includeFields.has("meta")) {
62
+ const filtered = {};
63
+ for (const k of metaKeys) {
64
+ if (k in rawMeta) filtered[k] = rawMeta[k];
65
+ }
66
+ out.meta = filtered;
67
+ } else {
68
+ out.meta = rawMeta;
69
+ }
70
+ }
71
+ if (node.name != null) {
72
+ out.name = node.name;
73
+ }
74
+ if (all || includeFields.has("value")) {
75
+ try {
76
+ out.value = node.get();
77
+ } catch {
78
+ }
79
+ }
80
+ if ((all || includeFields.has("v")) && node.v != null) {
81
+ const vInfo = { id: node.v.id, version: node.v.version };
82
+ if ("cid" in node.v) {
83
+ vInfo.cid = node.v.cid;
84
+ vInfo.prev = node.v.prev;
85
+ }
86
+ out.v = vInfo;
87
+ }
88
+ if (all || includeFields.has("guard")) {
89
+ if (guard != null) {
90
+ out.guard = accessHintForGuard(guard);
91
+ }
92
+ }
93
+ if (all || includeFields.has("lastMutation")) {
94
+ if (node.lastMutation != null) {
95
+ out.lastMutation = node.lastMutation;
96
+ }
97
+ }
98
+ return out;
99
+ }
100
+
101
+ export {
102
+ resolveDescribeFields,
103
+ metaSnapshot,
104
+ describeNode
105
+ };
106
+ //# sourceMappingURL=chunk-LR2CLSEF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/meta.ts"],"sourcesContent":["import type { Actor } from \"./actor.js\";\nimport { DynamicNodeImpl } from \"./dynamic-node.js\";\nimport { accessHintForGuard } from \"./guard.js\";\nimport { type Node, NodeImpl } from \"./node.js\";\n\n/** JSON-shaped slice of a node for Phase 1 `Graph.describe()` (GRAPHREFLY-SPEC §3.6, Appendix B). */\nexport type DescribeNodeOutput = {\n\ttype: \"state\" | \"derived\" | \"producer\" | \"operator\" | \"effect\";\n\tstatus?: Node[\"status\"];\n\tdeps: string[];\n\tmeta?: Record<string, unknown>;\n\tname?: string;\n\tvalue?: unknown;\n\t/** Node versioning info (GRAPHREFLY-SPEC §7). Present only when versioning is enabled. */\n\tv?: { id: string; version: number; cid?: string; prev?: string | null };\n\t/** Guard info (full detail). */\n\tguard?: string;\n\t/** Last mutation attribution (full detail). */\n\tlastMutation?: Readonly<{ actor: Actor; timestamp_ns: number }>;\n};\n\n/**\n * Detail level for `describe()` progressive disclosure (Phase 3.3b).\n * - `\"minimal\"` — type + deps only (default). LLM-friendly.\n * - `\"standard\"` — type, status, value, deps, meta, versioning (`v`).\n * - `\"full\"` — standard + guard, lastMutation.\n */\nexport type DescribeDetail = \"minimal\" | \"standard\" | \"full\";\n\n/**\n * Valid field names for `describe({ fields: [...] })` (Phase 3.3b).\n * Dotted paths like `\"meta.label\"` select specific meta keys.\n */\nexport type DescribeField =\n\t| \"type\"\n\t| \"status\"\n\t| \"value\"\n\t| \"deps\"\n\t| \"meta\"\n\t| \"v\"\n\t| \"guard\"\n\t| \"lastMutation\"\n\t| `meta.${string}`;\n\n/** Resolve which fields to include based on detail level or explicit field list. */\nexport function resolveDescribeFields(\n\tdetail?: DescribeDetail,\n\tfields?: readonly DescribeField[],\n): Set<string> | null {\n\t// Explicit fields override detail level\n\tif (fields != null && fields.length > 0) return new Set(fields);\n\tswitch (detail) {\n\t\tcase \"standard\":\n\t\t\treturn new Set([\"type\", \"status\", \"value\", \"deps\", \"meta\", \"v\"]);\n\t\tcase \"full\":\n\t\t\treturn null; // null = include everything\n\t\tcase \"minimal\":\n\t\tdefault:\n\t\t\treturn new Set([\"type\", \"deps\"]);\n\t}\n}\n\nfunction inferDescribeType(n: NodeImpl): DescribeNodeOutput[\"type\"] {\n\tif (n._describeKind != null) return n._describeKind;\n\tif (!n._hasDeps) return n._fn != null ? \"producer\" : \"state\";\n\tif (n._fn == null) return \"derived\";\n\tif (n._manualEmitUsed) return \"operator\";\n\treturn \"derived\";\n}\n\n/**\n * Reads the current cached value of every companion meta field on a node,\n * suitable for merging into `describe()`-style JSON (GRAPHREFLY-SPEC §2.3, §3.6).\n *\n * @remarks\n * Values come from {@link Node.get}, which returns the **last settled** cache.\n * If a meta field is in `\"dirty\"` status (DIRTY received, DATA pending), the\n * snapshot contains the *previous* value — check `node.meta[key].status` when\n * freshness matters. Avoid calling mid-batch for the same reason.\n *\n * Meta nodes are **not** terminated when their parent receives COMPLETE or\n * ERROR — they remain writable so callers can record post-mortem metadata\n * (e.g. `meta.error`). They *are* torn down when the parent receives TEARDOWN.\n *\n * @param node - The node whose meta fields to snapshot.\n * @returns Plain object of `{ key: value }` pairs (empty if no meta defined).\n * Keys whose companion node's {@link Node.get} throws are omitted.\n *\n * @example\n * ```ts\n * import { core } from \"@graphrefly/graphrefly-ts\";\n *\n * const n = core.node({ initial: 0, meta: { tag: \"a\" } });\n * core.metaSnapshot(n); // { tag: \"a\" }\n * ```\n */\nexport function metaSnapshot(node: Node): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, child] of Object.entries(node.meta)) {\n\t\ttry {\n\t\t\tout[key] = child.get();\n\t\t} catch {\n\t\t\t/* omit key — describe tooling still gets other fields */\n\t\t}\n\t}\n\treturn out;\n}\n\n/**\n * Builds a single-node slice of `Graph.describe()` JSON (structure + `meta` snapshot).\n * Parity with graphrefly-py `describe_node`.\n *\n * `type` is inferred from factory configuration, optional `describeKind` in node options,\n * and the last `manualEmitUsed` hint (operator vs derived). {@link effect} sets\n * `describeKind: \"effect\"`. Nodes not created by {@link node} fall back to `type: \"state\"` and empty `deps`.\n *\n * @param node - Any `Node` to introspect.\n * @returns `DescribeNodeOutput` suitable for merging into graph describe maps.\n *\n * @example\n * ```ts\n * import { describeNode, state } from \"@graphrefly/graphrefly-ts\";\n *\n * describeNode(state(0));\n * ```\n */\n/**\n * Builds a single-node slice for `Graph.describe()`.\n *\n * @param node - Node to introspect.\n * @param includeFields - Set of fields to include, or `null` for all. When omitted, all fields are included (legacy behavior).\n */\nexport function describeNode(node: Node, includeFields?: Set<string> | null): DescribeNodeOutput {\n\tconst all = includeFields == null; // null or undefined → include everything\n\n\t// Specific meta keys requested via dotted paths (e.g. \"meta.label\")\n\tconst metaKeys: string[] | null =\n\t\t!all && includeFields != null\n\t\t\t? [...includeFields].filter((f) => f.startsWith(\"meta.\")).map((f) => f.slice(5))\n\t\t\t: null;\n\tconst wantsMeta = all || includeFields!.has(\"meta\") || (metaKeys != null && metaKeys.length > 0);\n\n\tlet type: DescribeNodeOutput[\"type\"] = \"state\";\n\tlet deps: string[] = [];\n\n\tif (node instanceof NodeImpl) {\n\t\ttype = inferDescribeType(node);\n\t\tdeps = node._deps.map((d) => d.name ?? \"\");\n\t} else if (node instanceof DynamicNodeImpl) {\n\t\ttype = node._describeKind ?? \"derived\";\n\t\tdeps = [];\n\t}\n\n\tconst out: DescribeNodeOutput = { type, deps };\n\n\t// status\n\tif (all || includeFields!.has(\"status\")) {\n\t\tout.status = node.status;\n\t}\n\n\t// Resolve guard once — used by both meta.access hint and standalone guard field\n\tconst guard =\n\t\t(node instanceof NodeImpl && node._guard) ||\n\t\t(node instanceof DynamicNodeImpl && node._guard) ||\n\t\tundefined;\n\n\t// meta\n\tif (wantsMeta) {\n\t\tconst rawMeta: Record<string, unknown> = { ...metaSnapshot(node) };\n\t\tif (guard != null && rawMeta.access === undefined) {\n\t\t\trawMeta.access = accessHintForGuard(guard);\n\t\t}\n\n\t\tif (metaKeys != null && metaKeys.length > 0 && !includeFields!.has(\"meta\")) {\n\t\t\t// Only specific meta keys\n\t\t\tconst filtered: Record<string, unknown> = {};\n\t\t\tfor (const k of metaKeys) {\n\t\t\t\tif (k in rawMeta) filtered[k] = rawMeta[k];\n\t\t\t}\n\t\t\tout.meta = filtered;\n\t\t} else {\n\t\t\tout.meta = rawMeta;\n\t\t}\n\t}\n\n\t// name (always include when present — it's identity, not detail)\n\tif (node.name != null) {\n\t\tout.name = node.name;\n\t}\n\n\t// value\n\tif (all || includeFields!.has(\"value\")) {\n\t\ttry {\n\t\t\tout.value = node.get();\n\t\t} catch {\n\t\t\t/* omit value */\n\t\t}\n\t}\n\n\t// Versioning (GRAPHREFLY-SPEC §7)\n\tif ((all || includeFields!.has(\"v\")) && node.v != null) {\n\t\tconst vInfo: DescribeNodeOutput[\"v\"] = { id: node.v.id, version: node.v.version };\n\t\tif (\"cid\" in node.v) {\n\t\t\tvInfo!.cid = (node.v as { cid: string }).cid;\n\t\t\tvInfo!.prev = (node.v as { prev: string | null }).prev;\n\t\t}\n\t\tout.v = vInfo;\n\t}\n\n\t// Guard info (full detail)\n\tif (all || includeFields!.has(\"guard\")) {\n\t\tif (guard != null) {\n\t\t\tout.guard = accessHintForGuard(guard);\n\t\t}\n\t}\n\n\t// Last mutation attribution (full detail)\n\tif (all || includeFields!.has(\"lastMutation\")) {\n\t\tif (node.lastMutation != null) {\n\t\t\tout.lastMutation = node.lastMutation;\n\t\t}\n\t}\n\n\treturn out;\n}\n"],"mappings":"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","names":[]}
@@ -219,10 +219,14 @@ function partitionForBatch(messages) {
219
219
  }
220
220
  return { immediate, deferred, terminal };
221
221
  }
222
- function emitWithBatch(emit, messages, phase = 2) {
222
+ function emitWithBatch(emit, messages, phase = 2, options) {
223
223
  if (messages.length === 0) {
224
224
  return;
225
225
  }
226
+ if (options?.strategy === "sequential") {
227
+ _emitSequential(emit, messages, phase);
228
+ return;
229
+ }
226
230
  const queue = phase === 3 ? pendingPhase3 : pendingPhase2;
227
231
  if (messages.length === 1) {
228
232
  const t = messages[0][0];
@@ -263,6 +267,29 @@ function emitWithBatch(emit, messages, phase = 2) {
263
267
  }
264
268
  }
265
269
  }
270
+ function _emitSequential(emit, messages, phase = 2) {
271
+ const dataQueue = phase === 3 ? pendingPhase3 : pendingPhase2;
272
+ for (const msg of messages) {
273
+ const tier = messageTier(msg[0]);
274
+ if (tier === 2) {
275
+ if (isBatching()) {
276
+ const m = msg;
277
+ dataQueue.push(() => emit([m]));
278
+ } else {
279
+ emit([msg]);
280
+ }
281
+ } else if (tier >= 3) {
282
+ if (isBatching()) {
283
+ const m = msg;
284
+ pendingPhase3.push(() => emit([m]));
285
+ } else {
286
+ emit([msg]);
287
+ }
288
+ } else {
289
+ emit([msg]);
290
+ }
291
+ }
292
+ }
266
293
 
267
294
  // src/core/clock.ts
268
295
  function monotonicNs() {
@@ -426,6 +453,7 @@ function isV1(info) {
426
453
  }
427
454
 
428
455
  // src/core/node.ts
456
+ var NO_VALUE = /* @__PURE__ */ Symbol.for("graphrefly/NO_VALUE");
429
457
  function createIntBitSet() {
430
458
  let bits = 0;
431
459
  return {
@@ -555,10 +583,10 @@ var NodeImpl = class {
555
583
  this._hasDeps = deps.length > 0;
556
584
  this._autoComplete = opts.completeWhenDepsComplete ?? true;
557
585
  this._isSingleDep = deps.length === 1 && fn != null;
558
- this._cached = opts.initial;
586
+ this._cached = "initial" in opts ? opts.initial : NO_VALUE;
559
587
  this._status = this._hasDeps ? "disconnected" : "settled";
560
588
  this._hashFn = opts.versioningHash ?? defaultHash;
561
- this._versioning = opts.versioning != null ? createVersioning(opts.versioning, this._cached, {
589
+ this._versioning = opts.versioning != null ? createVersioning(opts.versioning, this._cached === NO_VALUE ? void 0 : this._cached, {
562
590
  id: opts.versioningId,
563
591
  hash: this._hashFn
564
592
  }) : void 0;
@@ -642,10 +670,14 @@ var NodeImpl = class {
642
670
  _applyVersioning(level, opts) {
643
671
  if (this._versioning != null) return;
644
672
  this._hashFn = opts?.hash ?? this._hashFn;
645
- this._versioning = createVersioning(level, this._cached, {
646
- id: opts?.id,
647
- hash: this._hashFn
648
- });
673
+ this._versioning = createVersioning(
674
+ level,
675
+ this._cached === NO_VALUE ? void 0 : this._cached,
676
+ {
677
+ id: opts?.id,
678
+ hash: this._hashFn
679
+ }
680
+ );
649
681
  }
650
682
  hasGuard() {
651
683
  return this._guard != null;
@@ -655,7 +687,7 @@ var NodeImpl = class {
655
687
  return this._guard(normalizeActor(actor), "observe");
656
688
  }
657
689
  get() {
658
- return this._cached;
690
+ return this._cached === NO_VALUE ? void 0 : this._cached;
659
691
  }
660
692
  down(messages, options) {
661
693
  if (messages.length === 0) return;
@@ -712,6 +744,7 @@ var NodeImpl = class {
712
744
  }
713
745
  if (this._terminal && this._opts.resubscribable) {
714
746
  this._terminal = false;
747
+ this._cached = NO_VALUE;
715
748
  this._status = this._hasDeps ? "disconnected" : "settled";
716
749
  this._opts.onResubscribe?.();
717
750
  }
@@ -811,7 +844,7 @@ var NodeImpl = class {
811
844
  const cleanupFn = this._cleanup;
812
845
  this._cleanup = void 0;
813
846
  cleanupFn?.();
814
- this._cached = void 0;
847
+ this._cached = NO_VALUE;
815
848
  this._lastDepValues = void 0;
816
849
  }
817
850
  this._status = statusAfterMessage(this._status, m);
@@ -820,7 +853,7 @@ var NodeImpl = class {
820
853
  }
821
854
  if (t === TEARDOWN) {
822
855
  if (this._opts.resetOnTeardown) {
823
- this._cached = void 0;
856
+ this._cached = NO_VALUE;
824
857
  }
825
858
  const teardownCleanup = this._cleanup;
826
859
  this._cleanup = void 0;
@@ -851,7 +884,15 @@ var NodeImpl = class {
851
884
  }
852
885
  _emitAutoValue(value) {
853
886
  const wasDirty = this._status === "dirty";
854
- const unchanged = this._equals(this._cached, value);
887
+ let unchanged;
888
+ try {
889
+ unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
890
+ } catch (eqErr) {
891
+ const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
892
+ const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
893
+ this._downInternal([[ERROR, wrapped]]);
894
+ return;
895
+ }
855
896
  if (unchanged) {
856
897
  this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
857
898
  return;
@@ -898,7 +939,9 @@ var NodeImpl = class {
898
939
  if (out === void 0) return;
899
940
  this._emitAutoValue(out);
900
941
  } catch (err) {
901
- this._downInternal([[ERROR, err]]);
942
+ const errMsg = err instanceof Error ? err.message : String(err);
943
+ const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
944
+ this._downInternal([[ERROR, wrapped]]);
902
945
  }
903
946
  }
904
947
  _onDepDirty(index) {
@@ -933,7 +976,11 @@ var NodeImpl = class {
933
976
  try {
934
977
  if (this._onMessage(msg, index, this._actions)) continue;
935
978
  } catch (err) {
936
- this._downInternal([[ERROR, err]]);
979
+ const errMsg = err instanceof Error ? err.message : String(err);
980
+ const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
981
+ cause: err
982
+ });
983
+ this._downInternal([[ERROR, wrapped]]);
937
984
  return;
938
985
  }
939
986
  }
@@ -1069,7 +1116,7 @@ var DynamicNodeImpl = class {
1069
1116
  _actions;
1070
1117
  _boundEmitToSinks;
1071
1118
  // Mutable state
1072
- _cached;
1119
+ _cached = NO_VALUE;
1073
1120
  _status = "disconnected";
1074
1121
  _terminal = false;
1075
1122
  _connected = false;
@@ -1163,7 +1210,7 @@ var DynamicNodeImpl = class {
1163
1210
  return this._guard(normalizeActor(actor), "observe");
1164
1211
  }
1165
1212
  get() {
1166
- return this._cached;
1213
+ return this._cached === NO_VALUE ? void 0 : this._cached;
1167
1214
  }
1168
1215
  down(messages, options) {
1169
1216
  if (messages.length === 0) return;
@@ -1221,6 +1268,7 @@ var DynamicNodeImpl = class {
1221
1268
  }
1222
1269
  if (this._terminal && this._resubscribable) {
1223
1270
  this._terminal = false;
1271
+ this._cached = NO_VALUE;
1224
1272
  this._status = "disconnected";
1225
1273
  this._onResubscribe?.();
1226
1274
  }
@@ -1298,10 +1346,13 @@ var DynamicNodeImpl = class {
1298
1346
  const t = m[0];
1299
1347
  if (t === DATA) this._cached = m[1];
1300
1348
  if (t === INVALIDATE) {
1301
- this._cached = void 0;
1349
+ this._cached = NO_VALUE;
1350
+ this._status = "dirty";
1302
1351
  }
1303
- if (t === DATA || t === RESOLVED) {
1352
+ if (t === DATA) {
1304
1353
  this._status = "settled";
1354
+ } else if (t === RESOLVED) {
1355
+ this._status = "resolved";
1305
1356
  } else if (t === DIRTY) {
1306
1357
  this._status = "dirty";
1307
1358
  } else if (t === COMPLETE) {
@@ -1312,7 +1363,7 @@ var DynamicNodeImpl = class {
1312
1363
  this._terminal = true;
1313
1364
  }
1314
1365
  if (t === TEARDOWN) {
1315
- if (this._resetOnTeardown) this._cached = void 0;
1366
+ if (this._resetOnTeardown) this._cached = NO_VALUE;
1316
1367
  try {
1317
1368
  this._propagateToMeta(t);
1318
1369
  } finally {
@@ -1335,7 +1386,15 @@ var DynamicNodeImpl = class {
1335
1386
  }
1336
1387
  _emitAutoValue(value) {
1337
1388
  const wasDirty = this._status === "dirty";
1338
- const unchanged = this._equals(this._cached, value);
1389
+ let unchanged;
1390
+ try {
1391
+ unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
1392
+ } catch (eqErr) {
1393
+ const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
1394
+ const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
1395
+ this._downInternal([[ERROR, wrapped]]);
1396
+ return;
1397
+ }
1339
1398
  if (unchanged) {
1340
1399
  this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
1341
1400
  return;
@@ -1568,4 +1627,4 @@ export {
1568
1627
  effect,
1569
1628
  pipe
1570
1629
  };
1571
- //# sourceMappingURL=chunk-5X3LAO3B.js.map
1630
+ //# sourceMappingURL=chunk-QTZSBQGJ.js.map