@earendil-works/chord 1.2.0 → 1.4.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 (122) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +69 -14
  3. package/dist/cell.d.ts +30 -9
  4. package/dist/cell.d.ts.map +1 -1
  5. package/dist/cell.js +52 -12
  6. package/dist/cell.js.map +1 -1
  7. package/dist/cluster.d.ts +4 -3
  8. package/dist/cluster.d.ts.map +1 -1
  9. package/dist/cluster.js +58 -42
  10. package/dist/cluster.js.map +1 -1
  11. package/dist/connection.d.ts +171 -58
  12. package/dist/connection.d.ts.map +1 -1
  13. package/dist/connection.js +676 -217
  14. package/dist/connection.js.map +1 -1
  15. package/dist/context/index.d.ts +8 -0
  16. package/dist/context/index.d.ts.map +1 -1
  17. package/dist/context/index.js +15 -0
  18. package/dist/context/index.js.map +1 -1
  19. package/dist/contract.d.ts +120 -45
  20. package/dist/contract.d.ts.map +1 -1
  21. package/dist/contract.js +104 -15
  22. package/dist/contract.js.map +1 -1
  23. package/dist/declare.d.ts +11 -48
  24. package/dist/declare.d.ts.map +1 -1
  25. package/dist/declare.js +1 -21
  26. package/dist/declare.js.map +1 -1
  27. package/dist/delta/index.d.ts +2 -1
  28. package/dist/delta/index.d.ts.map +1 -1
  29. package/dist/delta/index.js +4 -0
  30. package/dist/delta/index.js.map +1 -1
  31. package/dist/errors.d.ts +4 -3
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/errors.js +15 -0
  34. package/dist/errors.js.map +1 -1
  35. package/dist/host.d.ts +214 -120
  36. package/dist/host.d.ts.map +1 -1
  37. package/dist/host.js +446 -241
  38. package/dist/host.js.map +1 -1
  39. package/dist/index.d.ts +8 -8
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +4 -4
  42. package/dist/index.js.map +1 -1
  43. package/dist/link.d.ts +47 -12
  44. package/dist/link.d.ts.map +1 -1
  45. package/dist/link.js +200 -14
  46. package/dist/link.js.map +1 -1
  47. package/dist/loader.d.ts.map +1 -1
  48. package/dist/loader.js +7 -2
  49. package/dist/loader.js.map +1 -1
  50. package/dist/manager.d.ts.map +1 -1
  51. package/dist/manager.js +35 -9
  52. package/dist/manager.js.map +1 -1
  53. package/dist/object.d.ts +438 -0
  54. package/dist/object.d.ts.map +1 -0
  55. package/dist/{record.js → object.js} +614 -335
  56. package/dist/object.js.map +1 -0
  57. package/dist/output.d.ts +91 -0
  58. package/dist/output.d.ts.map +1 -0
  59. package/dist/output.js +360 -0
  60. package/dist/output.js.map +1 -0
  61. package/dist/registry.d.ts +3 -3
  62. package/dist/registry.d.ts.map +1 -1
  63. package/dist/registry.js +1 -1
  64. package/dist/registry.js.map +1 -1
  65. package/dist/reload.d.ts +35 -27
  66. package/dist/reload.d.ts.map +1 -1
  67. package/dist/reload.js +256 -192
  68. package/dist/reload.js.map +1 -1
  69. package/dist/remote.d.ts +130 -35
  70. package/dist/remote.d.ts.map +1 -1
  71. package/dist/remote.js +493 -189
  72. package/dist/remote.js.map +1 -1
  73. package/dist/sqlite/cloudflare.d.ts +52 -0
  74. package/dist/sqlite/cloudflare.d.ts.map +1 -0
  75. package/dist/sqlite/cloudflare.js +134 -0
  76. package/dist/sqlite/cloudflare.js.map +1 -0
  77. package/dist/sqlite/database.d.ts +37 -0
  78. package/dist/sqlite/database.d.ts.map +1 -0
  79. package/dist/sqlite/database.js +2 -0
  80. package/dist/sqlite/database.js.map +1 -0
  81. package/dist/sqlite/index.d.ts +4 -0
  82. package/dist/sqlite/index.d.ts.map +1 -0
  83. package/dist/sqlite/index.js +3 -0
  84. package/dist/sqlite/index.js.map +1 -0
  85. package/dist/sqlite/migrations.d.ts +10 -0
  86. package/dist/sqlite/migrations.d.ts.map +1 -0
  87. package/dist/sqlite/migrations.js +33 -0
  88. package/dist/sqlite/migrations.js.map +1 -0
  89. package/dist/sqlite/node.d.ts +38 -0
  90. package/dist/sqlite/node.d.ts.map +1 -0
  91. package/dist/sqlite/node.js +174 -0
  92. package/dist/sqlite/node.js.map +1 -0
  93. package/dist/sqlite/schema.d.ts +3 -0
  94. package/dist/sqlite/schema.d.ts.map +1 -0
  95. package/dist/sqlite/schema.js +10 -0
  96. package/dist/sqlite/schema.js.map +1 -0
  97. package/dist/telemetry-sqlite/index.d.ts +24 -0
  98. package/dist/telemetry-sqlite/index.d.ts.map +1 -0
  99. package/dist/telemetry-sqlite/index.js +273 -0
  100. package/dist/telemetry-sqlite/index.js.map +1 -0
  101. package/dist/telemetry-sqlite/store.d.ts +9 -0
  102. package/dist/telemetry-sqlite/store.d.ts.map +1 -0
  103. package/dist/telemetry-sqlite/store.js +110 -0
  104. package/dist/telemetry-sqlite/store.js.map +1 -0
  105. package/dist/types.d.ts +39 -28
  106. package/dist/types.d.ts.map +1 -1
  107. package/dist/types.js.map +1 -1
  108. package/dist/websocket.d.ts +4 -5
  109. package/dist/websocket.d.ts.map +1 -1
  110. package/dist/websocket.js +3 -2
  111. package/dist/websocket.js.map +1 -1
  112. package/dist/wire.d.ts +210 -129
  113. package/dist/wire.d.ts.map +1 -1
  114. package/dist/wire.js +108 -55
  115. package/dist/wire.js.map +1 -1
  116. package/docs/guide.md +267 -203
  117. package/docs/spec.md +141 -114
  118. package/docs/tutorial.md +257 -235
  119. package/package.json +27 -1
  120. package/dist/record.d.ts +0 -320
  121. package/dist/record.d.ts.map +0 -1
  122. package/dist/record.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.4.0] - 2026-10-21
4
+
5
+ ### Breaking Changes
6
+
7
+ - Custom `Context` implementations must provide `telemetry: TelemetrySpan`; use the no-op span `NOOP_TELEMETRY_CONTEXT` when disabled. `withTelemetry()` accepts spans, not adapters.
8
+
9
+ ### Added
10
+
11
+ - `@earendil-works/chord/telemetry-sqlite`: portable SQLite telemetry recording with bounded settled-span queues, ten-second metric aggregates, periodic flushes, and retention cleanup.
12
+ - `Context.telemetry` is an explicit, non-optional telemetry parent (no-op by default). `withTelemetry(telemetry, context)` derives it without changing values or cancellation; `traceIn(context, options, fn)` starts a child span and passes its derived context to the callback.
13
+ - `@earendil-works/chord/sqlite`: portable SQLite interfaces, atomic migration runner, and schema-prefix validation, with Node (`/sqlite/node`) and Cloudflare Durable Object (`/sqlite/cloudflare`) drivers. Moved from Durable, which keeps its storage implementations and re-exports the moved public names.
14
+ - `@earendil-works/chord/output`: `Progress` takes an optional `ProgressClock` (`now`, `setTimer`) to pace commits on, by default `Date.now` and `setTimeout`.
15
+
16
+ ## [1.3.0] - 2026-10-15
17
+
18
+ ### Breaking Changes
19
+
20
+ - Terminology follows the concepts: a reference (what `get` returns) is `Ref<C>` (was `Handle<C>`), its status `RefStatus` (was `HandleStatus`); what a factory returns is `ImplementationOf<C>` (was `BehaviorOf<C>`); a resource's options are a `ResourceDeclaration` (was `ResourceSpec`); epochs are code versions: `Ctx.codeVersion`, `RootOptions.codeVersion`, `Host.codeVersion`/`previousCodeVersion`, and the `codeVersion` fields of frames and reload reports (all were `epoch`). Docs and messages say object (was record) and reference (was handle and hold); the `hold` and `release` frame types keep their names
21
+ - A remote object's variant is named `variant` (was `kind`): factories return `{ variant: "editor", … }`, references expose `ref.variant` and `shape.variant`, and the `shape` frame carries `variant`. `variant` is reserved as a member name; `kind` no longer is
22
+ - Links have a protocol version (`PROTOCOL_VERSION`, now 2), announced in `hello`. Peers of different versions do not talk: the side that notices closes the link with reason `protocol-version <n>`, and both sides report it through `LinkOptions.onProtocolMismatch`. Peers without a version (older Chord) are refused
23
+ - `serve(…, { lifetime })` replaces `eager`: `"referenced"` (the default) creates an object at the first `get` of its address and disposes it after the last reference is released (plus `linger`); `"host"` creates the type's one object when its facet loads and keeps it until the host closes or a reload removes its facet. `lifetime: "host"` is refused at `serve` for remote types, for inputs that do not accept `{}` and together with `linger`; every `get` of such a type reaches its one object, created for the host principal, and another input is `invalid/input`. `eager: { input }` and the extra objects host-principal `get`s with other inputs created are gone. Only `"host"` objects may register (reason `invalid/not_host_lifetime`, was `not_eager`). `LoadOptions.awaitHostObjects` replaces `awaitEager`
24
+ - Declarations are calls by name: `self.state(name, schema, initial, { version?, migrate? })` returns the cell, `self.resource(name, { version, deps?, exclusive?, open, close? })` the resource (`rev` is now `version`), `self.on(name, source, [select,] handler)` declares a listener; `self.derive(sources, fn)` and `self.task(fn)` take no name. `cell()`, `derivation()`, `resource()`, `listener()`, `self.resources`, `self.listen`, `self.tasks` and the declaration maps are gone, and so are the types `CellDecl`, `ResourceDecl`, `DerivationDecl`, `ListenerDecl`. A repeated name of one kind in a run is `invalid/duplicate`
25
+ - State versions belong to cells: `ServeOptions.state` is gone; a cell's `version` (default 1) and `migrate(old, from)` replace it. A raised version migrates only that cell; a lowered one refuses the reload
26
+ - A derivation is new at every reload (computed at the commit) instead of kept with its function replaced. A listener whose state source a reload replaced subscribes again and gets the current value again: listeners on state must reconcile. Listeners on state subscribe when they activate, so they get the value current then
27
+ - `method(args?, result?)`: without arguments a method is called `ref.name(ctx?)` and implemented `(ctx) => …`; without a result it is void (callers get `undefined`). Descriptions record both by leaving `args`/`result` out (fingerprints of such methods change), `call` frames omit `args` and `result` frames `value` for them. Coverage requires both sides to agree on arguments; a void consumer accepts a result
28
+ - Remote contracts declare where their objects live, and callers no longer name a host: `remote(name, { …, host: hostNamed("server") })` or `host: hostFamily("session:", "session")` (the host `session:` + the input's required string field `session`, checked by TypeScript). `host` is required on remote contracts; `GetOptions.at` is removed. A `get` normalizes the input, resolves the host, and acquires locally or over the link to it. `serve` refuses a type whose placement does not include the host (also at a reload), a host refuses a hold whose input its own contract places elsewhere (`invalid/placement`), and a relay refuses a hold whose target disagrees with its contract token. The placement is part of the description and fingerprint; coverage requires equal placements, and `accepts` adapters do not bridge placements. Remote references are addressed by type, normalized input and principal, without a host name. `PROTOCOL_VERSION` is 3 (remote tokens carry `host`; a hold's input is normalized). New exports: `hostNamed`, `hostFamily`, `Placement`, `PlacementFor`, `HostNamed`, `HostFamily`
29
+ - `FacetApi` has a `signal` (below): code that builds a `FacetApi` by hand, for example to wrap `serve`, must pass it on
30
+ - Principals are typed and supplied by the app: `createHost<P>`, `Host<P>`, `defineFacet<P>`, `FacetApi<P>`, `Serve<P>`, `Factory<C, P>`, `Self<P>`, `Ctx<P>`, `GetOptions<P>`, `RootOptions<P>`, `Route<P>`, `AcceptOptions<P>` and `PrincipalMatch<P>` take the app's principal type (default `unknown`), so factories, methods, listeners and tasks narrow on it without casts. Chord invents no principals any more: `createHost({ self })` (required) is the principal of the host's own code, replacing the built-in `hostPrincipal` (`{ host: true }`, removed); `connect(link, { principal })` (required) is what the parent's own requests act as, replacing `{ host: <parent name> }`; `Route.principal` is required, replacing the default `{ host: <host name> }`. `HostOptions.upstream` is `{ link, principal, ...LinkOptions }` (was a link, with `upstreamOptions`). The host's own principal is recognized by `principalKey`, not object identity. The `Principal` type is gone; `ConnectOptions` and `PrincipalPattern` are new
31
+ - `serve(type, factory, { lifetime: "host" })` compiles only for a local type whose input has no required fields, and without `linger`; `serve` still refuses the rest at runtime. `ServeOptions` is a union of `ReferencedServeOptions` and `HostServeOptions`, and `serve` takes `ServeOptionsFor<C>`
32
+ - An awaited reference has its state, local or remote. `ctx.get` and `host.get` are async: they resolve once the reference is live and every state member of its variant has its first snapshot, and reject with the activation error, `aborted` (the ctx aborted or its call settled; `host.get`'s `signal`) or `unavailable/ready_timeout` after `HostOptions.readyTimeout` (default 60000 ms), releasing only that reference. `host.hold` is the synchronous form (possibly `loading`) for UI runtimes; `ref.ready` stays. `self.get` stays synchronous: an object goes live only once its references are ready and hydrated, and the state it exposes or derives from (also forwarded from a remote reference held another way) has its values; activation and reload prepare wait at most `readyTimeout` (a reference cycle across hosts fails with a hint instead of hanging)
33
+ - State members are typed `T` (was `T | undefined`) and `StateSource<T>` is `Readable<T>`. Reading state before the first hydration throws `not_ready`, outside the current variant `not_in_variant`, after the reference ended its end error. A remote reference is a union discriminated by `variant`: another variant's members are reachable only after narrowing. `MembersOf` is gone. A listener added before a member is readable gets its snapshot then (no `undefined` snapshot first)
34
+ - A remote reference stays subscribed to its variant's state while held, with or without listeners (it no longer forgets its value when the last listener leaves). `GetOptions.state: false` makes a method-only reference: no state unless another reference to the object wants it, ready once live
35
+ - `Shape.codeVersion` and the `shape` frame's `codeVersion` are gone
36
+ - `PROTOCOL_VERSION` is 4: a `hold` implies the state of its variant (`state: false` opts out until `sub`; `unsub` stops it); the provider answers with units, a `shape` listing the members the consumer lacks (`state`) followed by their snapshots; `state` frames are addressed by hold and member (`hold`, `m`) with `as: "snap" | "reset" | "ops" | "error"` and `value` (absent: `undefined`); `sub`/`unsub` take a hold; `resync` asks for a member again; `again` marks a re-hold after a link loss; `origin` (replacing `client`) names the client connection a hold comes from
37
+
38
+ ### Added
39
+
40
+ - `@earendil-works/chord/output`: `OutputBuffer`, which keeps a head or tail window of a stream at a cost proportional to each chunk, `Progress`, which paces commits of a changing value by their size, plus `boundOutput`, `sanitizeOutput`, `characterEnd`, `utf8ByteLength`, and the `OutputLimits`, `BoundedOutput`, `OutputSlice`, and `OutputSkip` types. Moved from `@earendil-works/pi-durable`, which now uses them from here.
41
+ - Remote references to one object share one binding per host and key (type, normalized input identity, principal key, contract fingerprint): one hold, one stream per state member, calls over the same hold; every reference keeps its own status, observers and `release`. `HostOptions.linger` (`{ types: { [typeName]: ms }, cap }`, cap default 4) keeps a binding held and hydrated after its last reference is released, so a reference taken again meanwhile costs nothing on the link
42
+ - Holds from clients carry an opaque `origin`: the id the accepting host gave the client connection, forwarded by relays and honored only from the parent link, so a host can tell client connections apart; a disconnect releases that client's holds at once
43
+ - Flow control by units: the first unit of a new reference passes the window, so a `get` never waits behind other references' state; units for reloads and re-holds wait, built lazily from the current values; waiting ops are capped per link (`LinkOptions.pendingCap`, default four windows), beyond which the oldest streams become resets (a relay asks downstream with `resync`)
44
+ - `StateOptions`, `EventOf`, `ValueOf` and `Lifetime` types
45
+ - `self.state(name, schema, { init }, options?)`: a cell whose first value comes from its source. `init(ctx)` (may be async) runs once the object's resources are open and its references live, before the object goes live, so no method, listener, task or subscriber sees another value; a throw or an invalid value fails the activation. A reload keeps the cell without running `init`; a cell a reload adds runs it while the reload prepares (a failure refuses the reload). Until then the cell's `value` throws `not_ready`. `StateInit` type
46
+ - Links are flow controlled end to end: peers acknowledge what they processed (`ack` frames, announced with `acks` in `hello`), and while a window of frames is unacknowledged (`window` in `LinkOptions`, default 256 KiB), state frames wait at the sender, coalesced per subscription, instead of queuing in front of call results. A stalled peer costs a bounded slot per subscription; a slot that outgrows it becomes a reset. Calls, results and control frames are never held back, so under load a result can arrive before state changes made before it
47
+ - `maxCalls` in `AcceptOptions` limits the calls a peer may have in progress on a link; further ones are answered at once with `unavailable` (reason `too_many_calls`) and the link stays open
48
+ - `FacetApi.signal` aborts once that run of the facet is no longer installed: a committed reload replaced or removed it, the reload that loaded it aborted, or the host closed. While a reload prepares, the installed and the candidate run are both live
49
+ - `HostOptions.taskStopDeadline` (default 5000 ms) bounds how long a reload waits for aborted tasks before it starts their replacements
50
+ - Links deflate call arguments, results and state snapshots whose JSON exceeds `PACK_THRESHOLD` (64K characters): over IPC as a base64 field, over WebSockets as a binary message. Relaying hosts forward the deflated bytes unread; the host that uses the payload inflates it. `webSocketLink(socket, { compress, acceptPacked })` (`WebSocketLinkOptions`) controls sending and accepting packed frames (servers should refuse them from untrusted clients), and `inflateMessage` turns a packed WebSocket message back into a text frame
51
+
52
+ ### Changed
53
+
54
+ - `Link.send` returns the encoded frame's size as sent (deflated payloads count as sent) when the transport knows it (memory and WebSocket links); flow control counts these sizes, so links that return nothing (IPC, custom links) are not windowed
55
+ - A reload starts an object's new tasks only after its aborted ones finished (at most `taskStopDeadline`, then anyway, reported); before, the old and the new run of a task worked side by side
56
+ - When a reload forces the retirement of the previous code version, it first aborts the roots still running on it (their `ctx.signal`, reason `superseded`) and gives them up to 1 s to stop before it closes what only that code used; a call with such a ctx is `superseded`. Before, resources were closed under running roots, which were never told
57
+
58
+ ### Fixed
59
+
60
+ - `nodeLoader` registers every facet's directory before importing any, so a facet that imports a file of a facet listed after it gets the same module copy as that facet; before, the load order could produce duplicates.
61
+ - A link that is already closed when a host accepts or routes to it is no longer kept as a connection: before, a route that returned a dead link (a child process that exited before its link was wrapped) made every later call to that host name fail with `unavailable` until restart, and leaked the connection's ping timer.
62
+ - Fixed `processLock()` failing with `listen EINVAL` on macOS and the BSDs when the temporary directory is too deep for a socket path (sun_path holds 104 bytes); the lock socket then lives in a private per-user directory, `/tmp/chord2-<uid>`
63
+ - A child host whose hello arrived while a cluster reload ran was marked in step without taking part, so it stayed on the old code version and every call to it failed `superseded` until it reconnected. It is now synced or reconciled when the reload ends; a child already behind is fenced at once, so its roots' calls up are admitted meanwhile. A reconciliation that is running when a reload starts likewise syncs its child only after that reload, against the committed code version
64
+ - A child host retired its previous code version as soon as a cluster reload's `prepare` arrived, before its parent did, so the parent's old roots got `superseded` on it. The coordinator now retires before it sends `prepare`, a child waits for its parent's `retire` (at most `retireDeadline`), and a child coming in step (or about to be reconciled) is told which code versions the parent retired before
65
+ - Disposed objects stayed reachable from the host until an object with the same address was created again (every distinct guest, session or input leaked one object, its references and the code they bind)
66
+ - References kept every code version's services, and with them the factory code of every reload, reachable for as long as they lived (held references, objects of lifetime `"host"`); old code generations could never be collected
67
+ - Call, task and root contexts were linked to long-lived signals with `AbortSignal.any`; Node keeps such dependants reachable while they have abort listeners, so a listener on `ctx.signal` kept its closure (and its code generation) alive for the object's lifetime. Contexts now detach when they close
68
+ - IPC links and the host manager's control channel wrote to a peer process that had exited without a callback, so the `EPIPE` Node reports for a write in the gap between the exit and `disconnect` (an `ack` sent just after a worker shut down) became an unhandled `error` event that crashed the process. The link now ends on a failed write (reason `ipc send failed: EPIPE`), and control messages to a gone peer are dropped
69
+ - A relayed call ran under the origin's cancel signal only, so a forced retirement of the relaying host could not cancel it before closing the old code's resources; it now follows its root's signal
70
+ - A resource kept across a reload closed with the `close` of the factory run that first opened it, and kept that run's code reachable; it now adopts the newest declaration at commit
71
+ - `forkDriver` passed its own `CHORD2_LOCK` to forked children, so a child host that forked its own children made those without a lock of their own fail with `locked`
72
+ - A call through a remote reference accepted a ctx from another host, or an object not made by Chord, and carried its root over the link; it now rejects it with `invalid/foreign_ctx` like a local reference ([#10959](https://github.com/earendil-works/pi/issues/10959))
73
+
3
74
  ## [1.2.0] - 2026-10-12
4
75
 
5
76
  ### Breaking Changes
package/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Chord builds applications out of **objects** that live in several processes (a server, workers, browser tabs) and can be **hot reloaded** while they run.
4
4
 
5
- - An **object** is state plus code at an address. It is created on demand and lives while someone holds it.
6
- - Everyone gets a **handle**, never the object. Handles work the same locally and across processes, and survive reloads.
5
+ - An **object** is state plus code at an address. It is created on demand and lives while someone references it.
6
+ - Everyone gets a **reference**, never the object. References work the same locally and across processes, and survive reloads.
7
7
  - Every object knows its **principal**: an owner and a guest get different objects, with different methods.
8
8
  - **State** is observable JSON. Remote viewers get a snapshot followed by deltas.
9
9
  - **Hot reload** swaps code while it runs. Work in flight finishes on the code it started with, state is kept, and incompatible changes migrate at a safe point.
@@ -14,45 +14,50 @@ Contracts are runtime values described with [TypeBox](https://github.com/sinclai
14
14
 
15
15
  | Import | Contents | Runs in |
16
16
  |--------|----------|---------|
17
- | `@earendil-works/chord` | Contracts (`local`, `remote`, `method`, `opaque`), declarations (`cell`, `derivation`, `listener`, `resource`), hosts (`createHost`, `defineFacet`, `defineRegistry`), links (`memoryLinkPair`, `ipcLink`, `webSocketLink`, `acceptWebSockets`), errors (`ChordError`, `isChordError`, `forbidden`, `invalid`). Also the shared base: `Context`, `JsonValue`, `JsonRepresentation`, `Draft`, `copyJson`, `isJsonValue`, and `replicatedState` with its types. | Node and browsers |
17
+ | `@earendil-works/chord` | Contracts (`local`, `remote`, `method`, `opaque`), declarations (through the factory's `self`: `state`, `derive`, `resource`, `on`, `task`), hosts (`createHost`, `defineFacet`, `defineRegistry`), links (`memoryLinkPair`, `ipcLink`, `webSocketLink`, `acceptWebSockets`), errors (`ChordError`, `isChordError`, `forbidden`, `invalid`). Also the shared base: `Context`, `JsonValue`, `JsonRepresentation`, `Draft`, `copyJson`, `isJsonValue`, and `replicatedState` with its types. | Node and browsers |
18
18
  | `@earendil-works/chord/node` | Child host processes: `createHostManager`, `forkDriver`, `childControl`, `processLock`; the facet module loader `nodeLoader`. | Node |
19
- | `@earendil-works/chord/context` | Explicit cancellation and invocation values: `BACKGROUND_CONTEXT`, `withCancel`, `withAbortSignal`, `withoutAbortSignal`, `withContextValue`, `awaitWithContext`. | Node and browsers |
19
+ | `@earendil-works/chord/context` | Explicit cancellation, telemetry, and invocation values: `BACKGROUND_CONTEXT`, `withCancel`, `withAbortSignal`, `withoutAbortSignal`, `withTelemetry`, `traceIn`, `withContextValue`, `awaitWithContext`. | Node and browsers |
20
20
  | `@earendil-works/chord/delta` | The transactional JSON tracker, structural diff, immutable apply, and the wire encoder/decoder for operation batches. | Node and browsers |
21
+ | `@earendil-works/chord/output` | Bounded output streams and paced progress: `OutputBuffer` (head or tail window of a stream, `OutputLimits`, `OutputSkip`), `boundOutput`, `sanitizeOutput`, `utf8ByteLength`, and `Progress` (adaptive commits of a changing value). | Node and browsers |
22
+ | `@earendil-works/chord/telemetry-sqlite` | `createSqliteTelemetry(db, { retentionMs?, maxQueuedSpans? })`: buffered recording and backend-independent `TelemetryStore` queries. | Any SQLite adapter |
23
+ | `@earendil-works/chord/sqlite` | Portable `SqliteDatabase`, `SqliteExecutor`, and `SqliteValue`, atomic `applySqliteMigrations(database, migrations, { table })`, and `validateSqliteSchemaPrefix`. The metadata table must be a valid, non-reserved SQLite identifier; migration versions start at 1 and are contiguous. | Any SQLite adapter |
24
+ | `@earendil-works/chord/sqlite/node` | `NodeSqliteDatabase`, `openNodeSqliteDatabase`, and `NodeSqliteDatabaseOptions`. | Node |
25
+ | `@earendil-works/chord/sqlite/cloudflare` | `DurableObjectSqliteDatabase` and the structural `DurableObjectSqliteStorage` binding type. | Cloudflare Durable Objects |
21
26
 
22
27
  ## Quick start
23
28
 
24
29
  ```ts
25
30
  import { Type } from "typebox";
26
- import { cell, createHost, method, remote } from "@earendil-works/chord";
31
+ import { createHost, hostNamed, method, remote } from "@earendil-works/chord";
27
32
 
28
33
  // contract.ts: shared by provider and consumers
29
34
  export const Counter = remote("demo.counter", {
30
35
  version: 1,
31
36
  input: Type.Object({ name: Type.String() }),
37
+ host: hostNamed("server"), // where its objects live; callers never name a host
32
38
  variants: {
33
39
  default: {
34
40
  state: { count: Type.Number() },
35
- methods: { increment: method(Type.Object({ by: Type.Number() }), Type.Null()) },
41
+ methods: { increment: method(Type.Object({ by: Type.Number() })) },
36
42
  },
37
43
  },
38
44
  });
39
45
 
40
- const host = createHost({ name: "server", principalKey: (p) => p.kind });
46
+ const host = createHost({ name: "server", self: { kind: "server" } });
41
47
 
42
48
  host.serve(Counter, ({ name }, _principal, self) => {
43
- const state = self.state({ count: cell(Type.Number(), 0) });
49
+ const count = self.state("count", Type.Number(), 0);
44
50
  return {
45
- kind: "default",
46
- count: state.count,
51
+ variant: "default",
52
+ count,
47
53
  increment: async ({ by }, ctx) => {
48
- state.count.change(ctx, (n) => n + by);
49
- return null;
54
+ count.change(ctx, (n) => n + by);
50
55
  },
51
56
  };
52
57
  });
53
58
 
54
59
  await host.root(async (ctx) => {
55
- const clicks = ctx.get(Counter, { name: "clicks" }); // a handle; held until this root ends
60
+ const clicks = await ctx.get(Counter, { name: "clicks" }); // ready, with its state; released when this root ends
56
61
  clicks.count.subscribe((n) => console.log("count", n));
57
62
  await clicks.increment({ by: 2 }, ctx); // logs 0, then 2
58
63
  });
@@ -65,6 +70,56 @@ await host.root(async (ctx) => {
65
70
  - [Specification](docs/spec.md): compatibility, lifetime, and reload semantics, plus known limitations and implementation decisions.
66
71
  - [Delta guide](src/delta/README.md): ownership, lifecycle, operation, and replica contracts of `@earendil-works/chord/delta`.
67
72
 
73
+ ## Explicit Context telemetry
74
+
75
+ `Context` always carries a `telemetry: TelemetrySpan`, alongside `abortSignal`. `BACKGROUND_CONTEXT` and `TODO_CONTEXT` use the shared no-op span `NOOP_TELEMETRY_CONTEXT`. Adapters (`TelemetryContext`) belong to hosts and start root spans; adapters are never placed in a `Context`.
76
+
77
+ ```ts
78
+ import { BACKGROUND_CONTEXT, traceIn, withTelemetry } from '@earendil-works/chord/context';
79
+ import { InMemoryTelemetryContext } from '@earendil-works/pi-telemetry';
80
+
81
+ const telemetry = new InMemoryTelemetryContext();
82
+ await telemetry.startSpan({ name: 'app.request' }, async (span) => {
83
+ const context = withTelemetry(span, BACKGROUND_CONTEXT);
84
+ await traceIn(context, { name: 'app.load' }, async (child) => {
85
+ child.telemetry.addEvent('cache.miss');
86
+ return traceIn(child, { name: 'app.read' }, (nested) => readAccount(nested));
87
+ });
88
+ });
89
+ ```
90
+
91
+ `withTelemetry(span, context)` derives a new context; it never changes its parent, cancellation, or typed values. `traceIn(context, options, fn)` creates a callback-managed child span, and `fn` receives its derived context. Every context already supports `telemetry.addEvent()` and `telemetry.addLink()`. Values, cancellation, callback results, and rejection identities are preserved. Concurrent calls keep independent parents; there is no ambient current span. `value()`, `ContextKey`, and `withContextValue()` remain supported.
92
+
93
+ These helpers operate on the standalone `Context` API. They do not yet instrument Chord hosts, methods, or network frames.
94
+
95
+ ## SQLite telemetry
96
+
97
+ ```ts
98
+ import { createSqliteTelemetry } from '@earendil-works/chord/telemetry-sqlite';
99
+ import { openNodeSqliteDatabase } from '@earendil-works/chord/sqlite/node';
100
+ import { renderTrace } from '@earendil-works/pi-telemetry/render';
101
+
102
+ const db = await openNodeSqliteDatabase('telemetry.db');
103
+ const telemetry = await createSqliteTelemetry(db);
104
+ try {
105
+ await telemetry.startSpan({ name: 'app.request' }, async (span) => {
106
+ span.addEvent('request.accepted');
107
+ await span.startSpan({ name: 'app.read' }, () => readAccount());
108
+ });
109
+ await telemetry.flush();
110
+ const { traces } = await telemetry.findTraces({ name: 'app.request', limit: 10 });
111
+ for (const trace of traces) console.log(renderTrace(trace.spans));
112
+ } finally {
113
+ try { await telemetry.close(); } finally { await db.close(); }
114
+ }
115
+ ```
116
+
117
+ The adapter is portable over `SqliteDatabase` and owns no connection. Recording performs no SQL. A one-second timer flushes settled spans and completed ten-second metric intervals in one transaction, including retention cleanup. Metrics retain count, sum, minimum, and maximum per name/kind/unit/attribute series, not raw samples, buckets, or exemplars. `close()` stops recording and drains partial metric intervals without waiting for business callbacks or closing the database. Late span completions are ignored.
118
+
119
+ Retention defaults to seven days. `maxQueuedSpans` defaults to 10,000 and counts settled spans awaiting commit, including an in-flight batch. The same limit independently bounds pending metric interval/series pairs. Overflow increments `droppedSpans` or `droppedMeasurements`; the limits bound counts, not bytes or active spans. A write failure stops recording permanently: `flush()` and `close()` reject with the original error without retrying.
120
+
121
+ The adapter implements `TelemetryStore`: `findTraces()`, `getTrace()`, and `findMetrics()` read committed rows, including after adapter close while the database remains open. See [stored telemetry](../telemetry/README.md#stored-telemetry) for filters, pagination, and cross-process trace assembly. Schema tables are `telemetry_spans`, `telemetry_metrics`, and `telemetry_schema`.
122
+
68
123
  ## Tracking JSON deltas
69
124
 
70
125
  Import the standalone transactional tracker from `@earendil-works/chord/delta`:
@@ -112,7 +167,7 @@ status.change(context, (draft) => {
112
167
  ```
113
168
 
114
169
  A successful `change()` publishes exactly one atomic revision. If its callback
115
- throws, the original value and sequence remain unchanged. Draft handles become
170
+ throws, the original value and sequence remain unchanged. Draft references become
116
171
  unusable when the callback returns. The tracker emits string append and
117
172
  front-truncate operations, array splices and permutations, sets, and deletes; large
118
173
  edit sets may fold into a complete replacement.
package/dist/cell.d.ts CHANGED
@@ -6,7 +6,7 @@ export interface SourceOptions {
6
6
  readonly dev: boolean;
7
7
  readonly report: (error: unknown) => void;
8
8
  }
9
- /** Checks that `ctx` may write a record's cells now; throws otherwise. */
9
+ /** Checks that `ctx` may write an object's cells now; throws otherwise. */
10
10
  export type WriteGuard = (ctx: Ctx) => void;
11
11
  /** Fans out values and delta events; listener errors are reported, not thrown to the writer. */
12
12
  declare class Fanout<T> {
@@ -20,7 +20,21 @@ declare class Fanout<T> {
20
20
  */
21
21
  emit(event: StateEvent<T>): void;
22
22
  }
23
- /** A record-owned state source (cell or derivation). Writes recompute dependents atomically. */
23
+ /**
24
+ * Whether a state source has its value (entry 8): a remote reference's member before its first snapshot has none, and
25
+ * neither has anything derived from or forwarding it. Sources without this symbol always have their value.
26
+ */
27
+ export declare const HYDRATION: unique symbol;
28
+ export interface Hydration {
29
+ readonly hydrated: boolean;
30
+ /** Resolves once hydrated; rejects if the source ends first. */
31
+ whenHydrated(): Promise<void>;
32
+ }
33
+ /** The hydration of a readable, or undefined for a source that always has its value. */
34
+ export declare function hydrationOf(value: unknown): Hydration | undefined;
35
+ /** The hydrations of `sources` that are not hydrated yet. */
36
+ export declare const unhydrated: (sources: Iterable<unknown>) => Hydration[];
37
+ /** An object-owned state source (cell or derivation). Writes recompute dependents atomically. */
24
38
  export declare abstract class Source<T> implements Readable<T> {
25
39
  readonly key: string;
26
40
  readonly seq: number;
@@ -42,8 +56,15 @@ export declare class Cell<T> extends Source<T> {
42
56
  #private;
43
57
  /** Replaced at a reload commit that changes the schema (state-version bump). */
44
58
  schema: TSchema;
45
- /** `initial` is always validated against `schema`; ownership moves to the cell. */
46
- constructor(key: string, schema: TSchema, initial: T, guard: WriteGuard, options: SourceOptions);
59
+ /**
60
+ * `initial` is always validated against `schema`; ownership moves to the cell. Without `initial`, the cell has no
61
+ * value until `initialize` (a cell declared with `init`, filled before its object goes live).
62
+ */
63
+ constructor(key: string, schema: TSchema, initial: {
64
+ readonly value: T;
65
+ } | undefined, guard: WriteGuard, options: SourceOptions);
66
+ /** Sets the first value of a cell declared with `init` (validated; ownership moves to the cell). Publishes nothing. */
67
+ initialize(value: T): void;
47
68
  protected ownSchemas(): readonly TSchema[];
48
69
  get value(): T;
49
70
  /**
@@ -73,23 +94,23 @@ export type SourceValues<S extends Sources> = {
73
94
  [K in keyof S]: S[K] extends Readable<infer T> ? T : never;
74
95
  };
75
96
  /**
76
- * A synchronous derivation. Sources are record cells/derivations (recomputed atomically with the write) or other
77
- * readables such as handle state members (recomputed when they publish). Subscribers receive diffs.
97
+ * A synchronous derivation. Sources are object cells/derivations (recomputed atomically with the write) or other
98
+ * readables such as reference state members (recomputed when they publish). Subscribers receive diffs.
78
99
  */
79
100
  export declare class Derived<T> extends Source<T> {
80
101
  #private;
81
102
  readonly sources: readonly Readable<unknown>[];
82
103
  constructor(key: string, sources: Sources, fn: (...values: never[]) => unknown, options: SourceOptions);
83
104
  get value(): T;
105
+ /** Hydrated once every source is (a derivation over a remote member waits for its snapshot). */
106
+ get [HYDRATION](): Hydration | undefined;
84
107
  /**
85
108
  * Computes the first value (or takes it from `precomputed`, a reload's validated dry run) and starts following
86
109
  * the sources (at install, never inside a factory).
87
110
  */
88
111
  attach(precomputed?: ReadonlyMap<unknown, unknown>): void;
89
112
  detach(): void;
90
- compute(read: (source: Readable<unknown>) => unknown, fn?: (...values: never[]) => unknown): T;
91
- /** Replaces the function without recomputing (reload commit, before migrated cells are installed). */
92
- setFn(fn: (...values: never[]) => unknown): void;
113
+ compute(read: (source: Readable<unknown>) => unknown): T;
93
114
  /** Recomputes and republishes, including dependents. Errors are reported (the source write already happened). */
94
115
  refresh(): void;
95
116
  install(next: T): void;
@@ -1 +1 @@
1
- {"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,KAAK,KAAK,EAA+D,MAAM,kBAAkB,CAAC;AAC3G,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAED,2EAA2E;AAC3E,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;AAI5C,gGAAgG;AAChG,cAAM,MAAM,CAAC,CAAC;;IAKb,YAAY,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EAE3C;IAED,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,IAAI,CAI9D;IAED,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,IAAI,CAIvE;IAED;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAO/B;CASD;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,OAAO,MAAkC,CAAC;AAElE,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,gEAAgE;IAChE,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAGjE;AAED,6DAA6D;AAC7D,eAAO,MAAM,UAAU,YAAa,QAAQ,CAAC,OAAO,CAAC,KAAG,SAAS,EAC0C,CAAC;AAE5G,iGAAiG;AACjG,8BAAsB,MAAM,CAAC,CAAC,CAAE,YAAW,QAAQ,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,SAAc;IAC1B,QAAQ,CAAC,UAAU,wBAA+B;IAClD,sGAAsG;IACtG,OAAO,EAAE,SAAS,OAAO,EAAE,CAAM;IACjC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAC1C,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAErC,YAAY,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAI9C;IAED,QAAQ,KAAK,KAAK,IAAI,CAAC,CAAC;IAExB,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAElD;IAED,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAE3D;IAED,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAI7B;IAED,SAAS,CAAC,UAAU,IAAI,SAAS,OAAO,EAAE,CAEzC;CACD;AA6BD,8GAA8G;AAC9G,qBAAa,IAAI,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,CAAC,CAAC;;IACrC,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;IAMhB;;;OAGG;IACH,YACC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,OAAO,EACf,OAAO,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;KAAE,GAAG,SAAS,EAC1C,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,aAAa,EAQtB;IAED,uHAAuH;IACvH,UAAU,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAKzB;IAED,UAAmB,UAAU,IAAI,SAAS,OAAO,EAAE,CAElD;IAED,IAAI,KAAK,IAAI,CAAC,CAGb;IAED;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACnD,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IAsBtD;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,GAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,IAAI,CAO3E;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAGrC;CA6BD;AAkCD,MAAM,MAAM,OAAO,GAAG,SAAS,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;AACnD,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,OAAO,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK;CAAE,CAAC;AAQ7G;;;GAGG;AACH,qBAAa,OAAO,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,CAAC,CAAC;;IACxC,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;IAK/C,YAAY,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,OAAO,EAAE,aAAa,EAQrG;IAED,IAAI,KAAK,IAAI,CAAC,CAEb;IAED,gGAAgG;IAChG,IAAI,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,SAAS,CAOvC;IAED;;;OAGG;IACH,MAAM,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,IAAI,CAMxD;IAED,MAAM,IAAI,IAAI,CAIb;IAED,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,OAAO,GAAG,CAAC,CAIvD;IAED,iHAAiH;IACjH,OAAO,IAAI,IAAI,CAWd;IAED,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAQrB;CACD;AAED,eAAO,MAAM,QAAQ,UAAW,OAAO,KAAG,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAA4B,CAAC;AAE/G,8DAA8D;AAC9D,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,UAAiE,CAAC"}
package/dist/cell.js CHANGED
@@ -46,7 +46,20 @@ class Fanout {
46
46
  }
47
47
  }
48
48
  }
49
- /** A record-owned state source (cell or derivation). Writes recompute dependents atomically. */
49
+ /**
50
+ * Whether a state source has its value (entry 8): a remote reference's member before its first snapshot has none, and
51
+ * neither has anything derived from or forwarding it. Sources without this symbol always have their value.
52
+ */
53
+ export const HYDRATION = Symbol("chord.hydration");
54
+ /** The hydration of a readable, or undefined for a source that always has its value. */
55
+ export function hydrationOf(value) {
56
+ if (value === null || typeof value !== "object")
57
+ return undefined;
58
+ return value[HYDRATION];
59
+ }
60
+ /** The hydrations of `sources` that are not hydrated yet. */
61
+ export const unhydrated = (sources) => [...sources].map(hydrationOf).filter((hydration) => hydration?.hydrated === false);
62
+ /** An object-owned state source (cell or derivation). Writes recompute dependents atomically. */
50
63
  export class Source {
51
64
  key;
52
65
  seq = ++sequence;
@@ -110,22 +123,41 @@ export class Cell extends Source {
110
123
  schema;
111
124
  #tracker;
112
125
  #guard;
113
- /** `initial` is always validated against `schema`; ownership moves to the cell. */
126
+ /** False until `initialize` for a cell declared with `init`: reads and writes throw `not_ready`. */
127
+ #initialized;
128
+ /**
129
+ * `initial` is always validated against `schema`; ownership moves to the cell. Without `initial`, the cell has no
130
+ * value until `initialize` (a cell declared with `init`, filled before its object goes live).
131
+ */
114
132
  constructor(key, schema, initial, guard, options) {
115
133
  super(key, options);
116
- assertValid("state", `initial value of state ${key}`, schema, initial);
134
+ if (initial !== undefined)
135
+ assertValid("state", `initial value of state ${key}`, schema, initial.value);
117
136
  this.schema = schema;
118
- this.#tracker = track({ v: initial });
137
+ this.#tracker = track({ v: (initial?.value ?? null) });
119
138
  this.#guard = guard;
139
+ this.#initialized = initial !== undefined;
140
+ }
141
+ /** Sets the first value of a cell declared with `init` (validated; ownership moves to the cell). Publishes nothing. */
142
+ initialize(value) {
143
+ if (this.#initialized)
144
+ throw new Error(`state ${this.key} is already initialized`);
145
+ assertValid("state", `initial value of state ${this.key}`, this.schema, value);
146
+ this.#tracker = track({ v: value });
147
+ this.#initialized = true;
120
148
  }
121
149
  ownSchemas() {
122
150
  return [this.schema];
123
151
  }
124
152
  get value() {
153
+ if (!this.#initialized)
154
+ throw notReady(`state ${this.key} (its init has not run)`);
125
155
  return this.#tracker.value.v;
126
156
  }
127
157
  change(ctx, fn) {
128
158
  this.#guard(ctx);
159
+ if (!this.#initialized)
160
+ throw notReady(`state ${this.key} (its init has not run)`);
129
161
  const change = this.#tracker.beginChange();
130
162
  let prepared;
131
163
  try {
@@ -155,6 +187,8 @@ export class Cell extends Source {
155
187
  */
156
188
  replace(ctx, value, options = {}) {
157
189
  this.#guard(ctx);
190
+ if (!this.#initialized)
191
+ throw notReady(`state ${this.key} (its init has not run)`);
158
192
  const before = this.value;
159
193
  const diff = options.diff ?? sharesStructure(before, value);
160
194
  const ops = diff ? diffRevisions(before, value) : undefined;
@@ -234,8 +268,8 @@ const isReadable = (value) => value !== null &&
234
268
  typeof value.subscribe === "function" &&
235
269
  "value" in value;
236
270
  /**
237
- * A synchronous derivation. Sources are record cells/derivations (recomputed atomically with the write) or other
238
- * readables such as handle state members (recomputed when they publish). Subscribers receive diffs.
271
+ * A synchronous derivation. Sources are object cells/derivations (recomputed atomically with the write) or other
272
+ * readables such as reference state members (recomputed when they publish). Subscribers receive diffs.
239
273
  */
240
274
  export class Derived extends Source {
241
275
  sources;
@@ -255,6 +289,16 @@ export class Derived extends Source {
255
289
  get value() {
256
290
  return this.#value;
257
291
  }
292
+ /** Hydrated once every source is (a derivation over a remote member waits for its snapshot). */
293
+ get [HYDRATION]() {
294
+ const pending = unhydrated(this.sources);
295
+ if (pending.length === 0)
296
+ return undefined;
297
+ return {
298
+ hydrated: false,
299
+ whenHydrated: () => Promise.all(pending.map((hydration) => hydration.whenHydrated())).then(() => { }),
300
+ };
301
+ }
258
302
  /**
259
303
  * Computes the first value (or takes it from `precomputed`, a reload's validated dry run) and starts following
260
304
  * the sources (at install, never inside a factory).
@@ -275,16 +319,12 @@ export class Derived extends Source {
275
319
  for (const unsubscribe of this.#external.splice(0))
276
320
  unsubscribe();
277
321
  }
278
- compute(read, fn = this.#fn) {
279
- const result = fn(...this.sources.map(read));
322
+ compute(read) {
323
+ const result = this.#fn(...this.sources.map(read));
280
324
  if (result instanceof Promise)
281
325
  throw new TypeError(`derive ${this.key}: fn must be synchronous`);
282
326
  return result;
283
327
  }
284
- /** Replaces the function without recomputing (reload commit, before migrated cells are installed). */
285
- setFn(fn) {
286
- this.#fn = fn;
287
- }
288
328
  /** Recomputes and republishes, including dependents. Errors are reported (the source write already happened). */
289
329
  refresh() {
290
330
  try {
package/dist/cell.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cell.js","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TSchema } from \"typebox\";\nimport { type Draft, diffRevisions, type JsonValue, type Op, type Tracker, track } from \"./delta/index.ts\";\nimport { ChordError } from \"./errors.ts\";\nimport { enqueue, holdPublications } from \"./publish.ts\";\nimport { assertValid } from \"./schema.ts\";\nimport type { Ctx, Readable, StateEvent } from \"./types.ts\";\n\nexport interface SourceOptions {\n\treadonly dev: boolean;\n\treadonly report: (error: unknown) => void;\n}\n\n/** Checks that `ctx` may write a record's cells now; throws otherwise. */\nexport type WriteGuard = (ctx: Ctx) => void;\n\nlet sequence = 0;\n\n/** Fans out values and delta events; listener errors are reported, not thrown to the writer. */\nclass Fanout<T> {\n\treadonly #values = new Set<(value: T) => void>();\n\treadonly #events = new Set<(event: StateEvent<T>) => void>();\n\treadonly #report: (error: unknown) => void;\n\n\tconstructor(report: (error: unknown) => void) {\n\t\tthis.#report = report;\n\t}\n\n\tsubscribe(listener: (value: T) => void, current: T): () => void {\n\t\tthis.#values.add(listener);\n\t\tthis.#call(listener, current);\n\t\treturn () => this.#values.delete(listener);\n\t}\n\n\tevents(listener: (event: StateEvent<T>) => void, current: T): () => void {\n\t\tthis.#events.add(listener);\n\t\tthis.#call(listener, { type: \"snap\", value: current });\n\t\treturn () => this.#events.delete(listener);\n\t}\n\n\t/**\n\t * Queues delivery to the listeners subscribed now. A listener that subscribes later already got this revision in\n\t * its snapshot; one that unsubscribes before delivery is skipped.\n\t */\n\temit(event: StateEvent<T>): void {\n\t\tconst events = [...this.#events];\n\t\tconst values = [...this.#values];\n\t\tenqueue(() => {\n\t\t\tfor (const listener of events) if (this.#events.has(listener)) this.#call(listener, event);\n\t\t\tfor (const listener of values) if (this.#values.has(listener)) this.#call(listener, event.value);\n\t\t});\n\t}\n\n\t#call<V>(listener: (value: V) => void, value: V): void {\n\t\ttry {\n\t\t\tlistener(value);\n\t\t} catch (error) {\n\t\t\tthis.#report(error);\n\t\t}\n\t}\n}\n\n/** A record-owned state source (cell or derivation). Writes recompute dependents atomically. */\nexport abstract class Source<T> implements Readable<T> {\n\treadonly key: string;\n\treadonly seq = ++sequence;\n\treadonly dependents = new Set<Derived<unknown>>();\n\t/** Schemas of remote state members currently exposing this source (checked per write in dev mode). */\n\tschemas: readonly TSchema[] = [];\n\tprotected readonly options: SourceOptions;\n\tprotected readonly fanout: Fanout<T>;\n\n\tconstructor(key: string, options: SourceOptions) {\n\t\tthis.key = key;\n\t\tthis.options = options;\n\t\tthis.fanout = new Fanout(options.report);\n\t}\n\n\tabstract get value(): T;\n\n\tsubscribe(listener: (value: T) => void): () => void {\n\t\treturn this.fanout.subscribe(listener, this.value);\n\t}\n\n\tevents(listener: (event: StateEvent<T>) => void): () => void {\n\t\treturn this.fanout.events(listener, this.value);\n\t}\n\n\t/** Per-write validation, dev mode only. */\n\tvalidate(value: unknown): void {\n\t\tif (!this.options.dev) return;\n\t\tfor (const schema of this.ownSchemas()) assertValid(\"state\", `state ${this.key}`, schema, value);\n\t\tfor (const schema of this.schemas) assertValid(\"state\", `state ${this.key}`, schema, value);\n\t}\n\n\tprotected ownSchemas(): readonly TSchema[] {\n\t\treturn [];\n\t}\n}\n\n/** The tracker tracks containers, so a cell tracks `{ v: value }` and strips the box from published ops. */\ninterface Box<T> {\n\tv: T;\n}\n\nfunction unbox(ops: readonly Op[], value: unknown): Op[] {\n\tconst result: Op[] = [];\n\tfor (const op of ops) {\n\t\tif (op[0] === \"r\" || op[1].length === 0) return [[\"r\", value as JsonValue]];\n\t\tconst path = op[1].slice(1);\n\t\tswitch (op[0]) {\n\t\t\tcase \"p\":\n\t\t\t\tresult.push([\"p\", path, op[2], op[3], op[4]]);\n\t\t\t\tbreak;\n\t\t\tcase \"m\":\n\t\t\t\tresult.push([\"m\", path, op[2]]);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// s/d/a/t on the value itself replace it.\n\t\t\t\tif (path.length === 0) return [[\"r\", value as JsonValue]];\n\t\t\t\tif (op[0] === \"d\") result.push([\"d\", path as [string | number, ...(string | number)[]]]);\n\t\t\t\telse result.push([op[0], path as [string | number, ...(string | number)[]], op[2]] as Op);\n\t\t}\n\t}\n\treturn result;\n}\n\n/** A state cell holding any JSON value, validated against its schema at creation and (dev mode) on writes. */\nexport class Cell<T> extends Source<T> {\n\t/** Replaced at a reload commit that changes the schema (state-version bump). */\n\tschema: TSchema;\n\treadonly #tracker: Tracker<Box<T>>;\n\treadonly #guard: WriteGuard;\n\n\t/** `initial` is always validated against `schema`; ownership moves to the cell. */\n\tconstructor(key: string, schema: TSchema, initial: T, guard: WriteGuard, options: SourceOptions) {\n\t\tsuper(key, options);\n\t\tassertValid(\"state\", `initial value of state ${key}`, schema, initial);\n\t\tthis.schema = schema;\n\t\tthis.#tracker = track<Box<T>>({ v: initial });\n\t\tthis.#guard = guard;\n\t}\n\n\tprotected override ownSchemas(): readonly TSchema[] {\n\t\treturn [this.schema];\n\t}\n\n\tget value(): T {\n\t\treturn this.#tracker.value.v;\n\t}\n\n\t/**\n\t * Mutates a draft (only mutated paths are published), or returns a new value (`n => n + 1`). A returned value is\n\t * always validated against the schema. The draft is unusable after `fn` returns.\n\t */\n\tchange(ctx: Ctx, fn: (draft: Draft<T>) => T): void;\n\tchange(ctx: Ctx, fn: (draft: Draft<T>) => void): void;\n\tchange(ctx: Ctx, fn: (draft: Draft<T>) => unknown): void {\n\t\tthis.#guard(ctx);\n\t\tconst change = this.#tracker.beginChange();\n\t\tlet prepared: ReturnType<typeof change.prepare>;\n\t\ttry {\n\t\t\tconst draft = change.state as Draft<Box<T>>;\n\t\t\tconst result: unknown = fn(draft.v as Draft<T>);\n\t\t\tif (result instanceof Promise) throw new TypeError(\"cell.change callbacks must be synchronous\");\n\t\t\tif (result !== undefined) {\n\t\t\t\tif (result !== draft.v) assertValid(\"state\", `state ${this.key}`, this.schema, result);\n\t\t\t\tdraft.v = result as Draft<T>;\n\t\t\t}\n\t\t\tprepared = change.prepare();\n\t\t} catch (error) {\n\t\t\tchange.abort();\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#publish(prepared, \"ops\");\n\t}\n\n\t/**\n\t * Replaces the whole value. Ownership of `value` moves to the cell: never mutate it afterwards.\n\t *\n\t * Subscribers get a structural diff (`diffRevisions`) when `diff` is true, or by default when the new value shares\n\t * containers with the old one (a new immutable revision of the same state, e.g. mirrored from another store);\n\t * otherwise a single `r` op. The resulting value is the same either way; only the ops differ.\n\t */\n\treplace(ctx: Ctx, value: T, options: { readonly diff?: boolean } = {}): void {\n\t\tthis.#guard(ctx);\n\t\tconst before = this.value;\n\t\tconst diff = options.diff ?? sharesStructure(before, value);\n\t\tconst ops = diff ? diffRevisions(before as JsonValue, value as JsonValue) : undefined;\n\t\tthis.#publish(this.#tracker.prepareReplace({ v: value }), \"ops\", true, ops);\n\t}\n\n\t/**\n\t * Installs a migrated value and its schema (reload commit). The caller validated the value and the derivations\n\t * over it against every schema that will apply, so nothing is checked here. Subscribers receive a reset.\n\t */\n\treset(value: T, schema: TSchema): void {\n\t\tthis.schema = schema;\n\t\tthis.#publish(this.#tracker.prepareReplace({ v: value }), \"reset\", false);\n\t}\n\n\t/** Adopts this revision and every derived one, then queues all notifications (cell first, then derivations). */\n\t#publish(\n\t\tprepared: ReturnType<Tracker<Box<T>>[\"prepareReplace\"]>,\n\t\ttype: \"ops\" | \"reset\",\n\t\tvalidate = true,\n\t\tops?: readonly Op[],\n\t): void {\n\t\tif (prepared.ops.length === 0 && type === \"ops\") {\n\t\t\tthis.#tracker.adopt(prepared);\n\t\t\treturn;\n\t\t}\n\t\tconst value = prepared.value.v;\n\t\tlet candidates: Map<Derived<unknown>, unknown>;\n\t\ttry {\n\t\t\tif (validate) this.validate(value);\n\t\t\tcandidates = computeDependents(this as Source<unknown>, value, validate);\n\t\t} catch (error) {\n\t\t\tprepared.abort();\n\t\t\tthrow error;\n\t\t}\n\t\tholdPublications(() => {\n\t\t\tthis.#tracker.adopt(prepared);\n\t\t\tconst published = ops !== undefined && ops.length > 0 ? ops : unbox(prepared.ops, value);\n\t\t\tthis.fanout.emit(type === \"reset\" ? { type, value } : { type, ops: published, value });\n\t\t\tfor (const [derived, next] of candidates) derived.install(next);\n\t\t});\n\t}\n}\n\n/** True if `b` reuses at least one container of `a` at the same key: a new revision of the same state. */\nfunction sharesStructure(a: unknown, b: unknown): boolean {\n\tif (a === null || b === null || typeof a !== \"object\" || typeof b !== \"object\" || a === b) return false;\n\tif (Array.isArray(a) !== Array.isArray(b)) return false;\n\tfor (const [key, child] of Object.entries(b)) {\n\t\tif (child !== null && typeof child === \"object\" && (a as Record<string, unknown>)[key] === child) return true;\n\t}\n\treturn false;\n}\n\n/** Recomputes every transitive dependent of `changed` against candidate values and validates them. */\nfunction computeDependents(changed: Source<unknown>, value: unknown, validate = true): Map<Derived<unknown>, unknown> {\n\tconst pending = new Set<Derived<unknown>>();\n\tconst visit = (source: Source<unknown>): void => {\n\t\tfor (const derived of source.dependents) {\n\t\t\tif (pending.has(derived)) continue;\n\t\t\tpending.add(derived);\n\t\t\tvisit(derived);\n\t\t}\n\t};\n\tvisit(changed);\n\tconst values = new Map<Readable<unknown>, unknown>([[changed, value]]);\n\tconst result = new Map<Derived<unknown>, unknown>();\n\tfor (const derived of [...pending].sort((a, b) => a.seq - b.seq)) {\n\t\tconst next = derived.compute((source) => (values.has(source) ? values.get(source) : source.value));\n\t\tif (validate) derived.validate(next);\n\t\tvalues.set(derived, next);\n\t\tresult.set(derived, next);\n\t}\n\treturn result;\n}\n\nexport type Sources = readonly Readable<unknown>[];\nexport type SourceValues<S extends Sources> = { [K in keyof S]: S[K] extends Readable<infer T> ? T : never };\n\nconst isReadable = (value: unknown): value is Readable<unknown> =>\n\tvalue !== null &&\n\ttypeof value === \"object\" &&\n\ttypeof (value as Readable<unknown>).subscribe === \"function\" &&\n\t\"value\" in value;\n\n/**\n * A synchronous derivation. Sources are record cells/derivations (recomputed atomically with the write) or other\n * readables such as handle state members (recomputed when they publish). Subscribers receive diffs.\n */\nexport class Derived<T> extends Source<T> {\n\treadonly sources: readonly Readable<unknown>[];\n\t#fn: (...values: never[]) => unknown;\n\t#value: T;\n\t#external: (() => void)[] = [];\n\n\tconstructor(key: string, sources: Sources, fn: (...values: never[]) => unknown, options: SourceOptions) {\n\t\tsuper(key, options);\n\t\tthis.sources = sources.map((source) => {\n\t\t\tif (!isReadable(source)) throw new TypeError(`derive ${key}: sources must be readables`);\n\t\t\treturn source;\n\t\t});\n\t\tthis.#fn = fn;\n\t\tthis.#value = undefined as T;\n\t}\n\n\tget value(): T {\n\t\treturn this.#value;\n\t}\n\n\t/**\n\t * Computes the first value (or takes it from `precomputed`, a reload's validated dry run) and starts following\n\t * the sources (at install, never inside a factory).\n\t */\n\tattach(precomputed?: ReadonlyMap<unknown, unknown>): void {\n\t\tthis.#value = precomputed?.has(this) ? (precomputed.get(this) as T) : this.compute((source) => source.value);\n\t\tfor (const source of this.sources) {\n\t\t\tif (source instanceof Source) source.dependents.add(this as Derived<unknown>);\n\t\t\telse this.#external.push(source.subscribe(() => this.refresh()));\n\t\t}\n\t}\n\n\tdetach(): void {\n\t\tfor (const source of this.sources)\n\t\t\tif (source instanceof Source) source.dependents.delete(this as Derived<unknown>);\n\t\tfor (const unsubscribe of this.#external.splice(0)) unsubscribe();\n\t}\n\n\tcompute(read: (source: Readable<unknown>) => unknown, fn: (...values: never[]) => unknown = this.#fn): T {\n\t\tconst result: unknown = fn(...(this.sources.map(read) as never[]));\n\t\tif (result instanceof Promise) throw new TypeError(`derive ${this.key}: fn must be synchronous`);\n\t\treturn result as T;\n\t}\n\n\t/** Replaces the function without recomputing (reload commit, before migrated cells are installed). */\n\tsetFn(fn: (...values: never[]) => unknown): void {\n\t\tthis.#fn = fn;\n\t}\n\n\t/** Recomputes and republishes, including dependents. Errors are reported (the source write already happened). */\n\trefresh(): void {\n\t\ttry {\n\t\t\tconst next = this.compute((source) => source.value);\n\t\t\tconst dependents = computeDependents(this as Derived<unknown>, next);\n\t\t\tholdPublications(() => {\n\t\t\t\tthis.install(next);\n\t\t\t\tfor (const [derived, value] of dependents) derived.install(value);\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.options.report(error);\n\t\t}\n\t}\n\n\tinstall(next: T): void {\n\t\tconst before = this.#value;\n\t\tthis.#value = next;\n\t\tconst ops: Op[] =\n\t\t\tbefore === undefined || next === undefined\n\t\t\t\t? [[\"r\", (next ?? null) as JsonValue]]\n\t\t\t\t: diffRevisions(before as JsonValue, next as JsonValue);\n\t\tif (ops.length > 0) this.fanout.emit({ type: \"ops\", ops, value: next });\n\t}\n}\n\nexport const isSource = (value: unknown): value is Cell<unknown> | Derived<unknown> => value instanceof Source;\n\n/** Thrown by placeholders used before activation finished. */\nexport const notReady = (what: string): ChordError => new ChordError(\"not_ready\", `${what} is not ready`);\n"]}
1
+ {"version":3,"file":"cell.js","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TSchema } from \"typebox\";\nimport { type Draft, diffRevisions, type JsonValue, type Op, type Tracker, track } from \"./delta/index.ts\";\nimport { ChordError } from \"./errors.ts\";\nimport { enqueue, holdPublications } from \"./publish.ts\";\nimport { assertValid } from \"./schema.ts\";\nimport type { Ctx, Readable, StateEvent } from \"./types.ts\";\n\nexport interface SourceOptions {\n\treadonly dev: boolean;\n\treadonly report: (error: unknown) => void;\n}\n\n/** Checks that `ctx` may write an object's cells now; throws otherwise. */\nexport type WriteGuard = (ctx: Ctx) => void;\n\nlet sequence = 0;\n\n/** Fans out values and delta events; listener errors are reported, not thrown to the writer. */\nclass Fanout<T> {\n\treadonly #values = new Set<(value: T) => void>();\n\treadonly #events = new Set<(event: StateEvent<T>) => void>();\n\treadonly #report: (error: unknown) => void;\n\n\tconstructor(report: (error: unknown) => void) {\n\t\tthis.#report = report;\n\t}\n\n\tsubscribe(listener: (value: T) => void, current: T): () => void {\n\t\tthis.#values.add(listener);\n\t\tthis.#call(listener, current);\n\t\treturn () => this.#values.delete(listener);\n\t}\n\n\tevents(listener: (event: StateEvent<T>) => void, current: T): () => void {\n\t\tthis.#events.add(listener);\n\t\tthis.#call(listener, { type: \"snap\", value: current });\n\t\treturn () => this.#events.delete(listener);\n\t}\n\n\t/**\n\t * Queues delivery to the listeners subscribed now. A listener that subscribes later already got this revision in\n\t * its snapshot; one that unsubscribes before delivery is skipped.\n\t */\n\temit(event: StateEvent<T>): void {\n\t\tconst events = [...this.#events];\n\t\tconst values = [...this.#values];\n\t\tenqueue(() => {\n\t\t\tfor (const listener of events) if (this.#events.has(listener)) this.#call(listener, event);\n\t\t\tfor (const listener of values) if (this.#values.has(listener)) this.#call(listener, event.value);\n\t\t});\n\t}\n\n\t#call<V>(listener: (value: V) => void, value: V): void {\n\t\ttry {\n\t\t\tlistener(value);\n\t\t} catch (error) {\n\t\t\tthis.#report(error);\n\t\t}\n\t}\n}\n\n/**\n * Whether a state source has its value (entry 8): a remote reference's member before its first snapshot has none, and\n * neither has anything derived from or forwarding it. Sources without this symbol always have their value.\n */\nexport const HYDRATION: unique symbol = Symbol(\"chord.hydration\");\n\nexport interface Hydration {\n\treadonly hydrated: boolean;\n\t/** Resolves once hydrated; rejects if the source ends first. */\n\twhenHydrated(): Promise<void>;\n}\n\n/** The hydration of a readable, or undefined for a source that always has its value. */\nexport function hydrationOf(value: unknown): Hydration | undefined {\n\tif (value === null || typeof value !== \"object\") return undefined;\n\treturn (value as { readonly [HYDRATION]?: Hydration })[HYDRATION];\n}\n\n/** The hydrations of `sources` that are not hydrated yet. */\nexport const unhydrated = (sources: Iterable<unknown>): Hydration[] =>\n\t[...sources].map(hydrationOf).filter((hydration): hydration is Hydration => hydration?.hydrated === false);\n\n/** An object-owned state source (cell or derivation). Writes recompute dependents atomically. */\nexport abstract class Source<T> implements Readable<T> {\n\treadonly key: string;\n\treadonly seq = ++sequence;\n\treadonly dependents = new Set<Derived<unknown>>();\n\t/** Schemas of remote state members currently exposing this source (checked per write in dev mode). */\n\tschemas: readonly TSchema[] = [];\n\tprotected readonly options: SourceOptions;\n\tprotected readonly fanout: Fanout<T>;\n\n\tconstructor(key: string, options: SourceOptions) {\n\t\tthis.key = key;\n\t\tthis.options = options;\n\t\tthis.fanout = new Fanout(options.report);\n\t}\n\n\tabstract get value(): T;\n\n\tsubscribe(listener: (value: T) => void): () => void {\n\t\treturn this.fanout.subscribe(listener, this.value);\n\t}\n\n\tevents(listener: (event: StateEvent<T>) => void): () => void {\n\t\treturn this.fanout.events(listener, this.value);\n\t}\n\n\t/** Per-write validation, dev mode only. */\n\tvalidate(value: unknown): void {\n\t\tif (!this.options.dev) return;\n\t\tfor (const schema of this.ownSchemas()) assertValid(\"state\", `state ${this.key}`, schema, value);\n\t\tfor (const schema of this.schemas) assertValid(\"state\", `state ${this.key}`, schema, value);\n\t}\n\n\tprotected ownSchemas(): readonly TSchema[] {\n\t\treturn [];\n\t}\n}\n\n/** The tracker tracks containers, so a cell tracks `{ v: value }` and strips the box from published ops. */\ninterface Box<T> {\n\tv: T;\n}\n\nfunction unbox(ops: readonly Op[], value: unknown): Op[] {\n\tconst result: Op[] = [];\n\tfor (const op of ops) {\n\t\tif (op[0] === \"r\" || op[1].length === 0) return [[\"r\", value as JsonValue]];\n\t\tconst path = op[1].slice(1);\n\t\tswitch (op[0]) {\n\t\t\tcase \"p\":\n\t\t\t\tresult.push([\"p\", path, op[2], op[3], op[4]]);\n\t\t\t\tbreak;\n\t\t\tcase \"m\":\n\t\t\t\tresult.push([\"m\", path, op[2]]);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// s/d/a/t on the value itself replace it.\n\t\t\t\tif (path.length === 0) return [[\"r\", value as JsonValue]];\n\t\t\t\tif (op[0] === \"d\") result.push([\"d\", path as [string | number, ...(string | number)[]]]);\n\t\t\t\telse result.push([op[0], path as [string | number, ...(string | number)[]], op[2]] as Op);\n\t\t}\n\t}\n\treturn result;\n}\n\n/** A state cell holding any JSON value, validated against its schema at creation and (dev mode) on writes. */\nexport class Cell<T> extends Source<T> {\n\t/** Replaced at a reload commit that changes the schema (state-version bump). */\n\tschema: TSchema;\n\t#tracker: Tracker<Box<T>>;\n\treadonly #guard: WriteGuard;\n\t/** False until `initialize` for a cell declared with `init`: reads and writes throw `not_ready`. */\n\t#initialized: boolean;\n\n\t/**\n\t * `initial` is always validated against `schema`; ownership moves to the cell. Without `initial`, the cell has no\n\t * value until `initialize` (a cell declared with `init`, filled before its object goes live).\n\t */\n\tconstructor(\n\t\tkey: string,\n\t\tschema: TSchema,\n\t\tinitial: { readonly value: T } | undefined,\n\t\tguard: WriteGuard,\n\t\toptions: SourceOptions,\n\t) {\n\t\tsuper(key, options);\n\t\tif (initial !== undefined) assertValid(\"state\", `initial value of state ${key}`, schema, initial.value);\n\t\tthis.schema = schema;\n\t\tthis.#tracker = track<Box<T>>({ v: (initial?.value ?? null) as T });\n\t\tthis.#guard = guard;\n\t\tthis.#initialized = initial !== undefined;\n\t}\n\n\t/** Sets the first value of a cell declared with `init` (validated; ownership moves to the cell). Publishes nothing. */\n\tinitialize(value: T): void {\n\t\tif (this.#initialized) throw new Error(`state ${this.key} is already initialized`);\n\t\tassertValid(\"state\", `initial value of state ${this.key}`, this.schema, value);\n\t\tthis.#tracker = track<Box<T>>({ v: value });\n\t\tthis.#initialized = true;\n\t}\n\n\tprotected override ownSchemas(): readonly TSchema[] {\n\t\treturn [this.schema];\n\t}\n\n\tget value(): T {\n\t\tif (!this.#initialized) throw notReady(`state ${this.key} (its init has not run)`);\n\t\treturn this.#tracker.value.v;\n\t}\n\n\t/**\n\t * Mutates a draft (only mutated paths are published), or returns a new value (`n => n + 1`). A returned value is\n\t * always validated against the schema. The draft is unusable after `fn` returns.\n\t */\n\tchange(ctx: Ctx, fn: (draft: Draft<T>) => T): void;\n\tchange(ctx: Ctx, fn: (draft: Draft<T>) => void): void;\n\tchange(ctx: Ctx, fn: (draft: Draft<T>) => unknown): void {\n\t\tthis.#guard(ctx);\n\t\tif (!this.#initialized) throw notReady(`state ${this.key} (its init has not run)`);\n\t\tconst change = this.#tracker.beginChange();\n\t\tlet prepared: ReturnType<typeof change.prepare>;\n\t\ttry {\n\t\t\tconst draft = change.state as Draft<Box<T>>;\n\t\t\tconst result: unknown = fn(draft.v as Draft<T>);\n\t\t\tif (result instanceof Promise) throw new TypeError(\"cell.change callbacks must be synchronous\");\n\t\t\tif (result !== undefined) {\n\t\t\t\tif (result !== draft.v) assertValid(\"state\", `state ${this.key}`, this.schema, result);\n\t\t\t\tdraft.v = result as Draft<T>;\n\t\t\t}\n\t\t\tprepared = change.prepare();\n\t\t} catch (error) {\n\t\t\tchange.abort();\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#publish(prepared, \"ops\");\n\t}\n\n\t/**\n\t * Replaces the whole value. Ownership of `value` moves to the cell: never mutate it afterwards.\n\t *\n\t * Subscribers get a structural diff (`diffRevisions`) when `diff` is true, or by default when the new value shares\n\t * containers with the old one (a new immutable revision of the same state, e.g. mirrored from another store);\n\t * otherwise a single `r` op. The resulting value is the same either way; only the ops differ.\n\t */\n\treplace(ctx: Ctx, value: T, options: { readonly diff?: boolean } = {}): void {\n\t\tthis.#guard(ctx);\n\t\tif (!this.#initialized) throw notReady(`state ${this.key} (its init has not run)`);\n\t\tconst before = this.value;\n\t\tconst diff = options.diff ?? sharesStructure(before, value);\n\t\tconst ops = diff ? diffRevisions(before as JsonValue, value as JsonValue) : undefined;\n\t\tthis.#publish(this.#tracker.prepareReplace({ v: value }), \"ops\", true, ops);\n\t}\n\n\t/**\n\t * Installs a migrated value and its schema (reload commit). The caller validated the value and the derivations\n\t * over it against every schema that will apply, so nothing is checked here. Subscribers receive a reset.\n\t */\n\treset(value: T, schema: TSchema): void {\n\t\tthis.schema = schema;\n\t\tthis.#publish(this.#tracker.prepareReplace({ v: value }), \"reset\", false);\n\t}\n\n\t/** Adopts this revision and every derived one, then queues all notifications (cell first, then derivations). */\n\t#publish(\n\t\tprepared: ReturnType<Tracker<Box<T>>[\"prepareReplace\"]>,\n\t\ttype: \"ops\" | \"reset\",\n\t\tvalidate = true,\n\t\tops?: readonly Op[],\n\t): void {\n\t\tif (prepared.ops.length === 0 && type === \"ops\") {\n\t\t\tthis.#tracker.adopt(prepared);\n\t\t\treturn;\n\t\t}\n\t\tconst value = prepared.value.v;\n\t\tlet candidates: Map<Derived<unknown>, unknown>;\n\t\ttry {\n\t\t\tif (validate) this.validate(value);\n\t\t\tcandidates = computeDependents(this as Source<unknown>, value, validate);\n\t\t} catch (error) {\n\t\t\tprepared.abort();\n\t\t\tthrow error;\n\t\t}\n\t\tholdPublications(() => {\n\t\t\tthis.#tracker.adopt(prepared);\n\t\t\tconst published = ops !== undefined && ops.length > 0 ? ops : unbox(prepared.ops, value);\n\t\t\tthis.fanout.emit(type === \"reset\" ? { type, value } : { type, ops: published, value });\n\t\t\tfor (const [derived, next] of candidates) derived.install(next);\n\t\t});\n\t}\n}\n\n/** True if `b` reuses at least one container of `a` at the same key: a new revision of the same state. */\nfunction sharesStructure(a: unknown, b: unknown): boolean {\n\tif (a === null || b === null || typeof a !== \"object\" || typeof b !== \"object\" || a === b) return false;\n\tif (Array.isArray(a) !== Array.isArray(b)) return false;\n\tfor (const [key, child] of Object.entries(b)) {\n\t\tif (child !== null && typeof child === \"object\" && (a as Record<string, unknown>)[key] === child) return true;\n\t}\n\treturn false;\n}\n\n/** Recomputes every transitive dependent of `changed` against candidate values and validates them. */\nfunction computeDependents(changed: Source<unknown>, value: unknown, validate = true): Map<Derived<unknown>, unknown> {\n\tconst pending = new Set<Derived<unknown>>();\n\tconst visit = (source: Source<unknown>): void => {\n\t\tfor (const derived of source.dependents) {\n\t\t\tif (pending.has(derived)) continue;\n\t\t\tpending.add(derived);\n\t\t\tvisit(derived);\n\t\t}\n\t};\n\tvisit(changed);\n\tconst values = new Map<Readable<unknown>, unknown>([[changed, value]]);\n\tconst result = new Map<Derived<unknown>, unknown>();\n\tfor (const derived of [...pending].sort((a, b) => a.seq - b.seq)) {\n\t\tconst next = derived.compute((source) => (values.has(source) ? values.get(source) : source.value));\n\t\tif (validate) derived.validate(next);\n\t\tvalues.set(derived, next);\n\t\tresult.set(derived, next);\n\t}\n\treturn result;\n}\n\nexport type Sources = readonly Readable<unknown>[];\nexport type SourceValues<S extends Sources> = { [K in keyof S]: S[K] extends Readable<infer T> ? T : never };\n\nconst isReadable = (value: unknown): value is Readable<unknown> =>\n\tvalue !== null &&\n\ttypeof value === \"object\" &&\n\ttypeof (value as Readable<unknown>).subscribe === \"function\" &&\n\t\"value\" in value;\n\n/**\n * A synchronous derivation. Sources are object cells/derivations (recomputed atomically with the write) or other\n * readables such as reference state members (recomputed when they publish). Subscribers receive diffs.\n */\nexport class Derived<T> extends Source<T> {\n\treadonly sources: readonly Readable<unknown>[];\n\treadonly #fn: (...values: never[]) => unknown;\n\t#value: T;\n\t#external: (() => void)[] = [];\n\n\tconstructor(key: string, sources: Sources, fn: (...values: never[]) => unknown, options: SourceOptions) {\n\t\tsuper(key, options);\n\t\tthis.sources = sources.map((source) => {\n\t\t\tif (!isReadable(source)) throw new TypeError(`derive ${key}: sources must be readables`);\n\t\t\treturn source;\n\t\t});\n\t\tthis.#fn = fn;\n\t\tthis.#value = undefined as T;\n\t}\n\n\tget value(): T {\n\t\treturn this.#value;\n\t}\n\n\t/** Hydrated once every source is (a derivation over a remote member waits for its snapshot). */\n\tget [HYDRATION](): Hydration | undefined {\n\t\tconst pending = unhydrated(this.sources);\n\t\tif (pending.length === 0) return undefined;\n\t\treturn {\n\t\t\thydrated: false,\n\t\t\twhenHydrated: () => Promise.all(pending.map((hydration) => hydration.whenHydrated())).then(() => {}),\n\t\t};\n\t}\n\n\t/**\n\t * Computes the first value (or takes it from `precomputed`, a reload's validated dry run) and starts following\n\t * the sources (at install, never inside a factory).\n\t */\n\tattach(precomputed?: ReadonlyMap<unknown, unknown>): void {\n\t\tthis.#value = precomputed?.has(this) ? (precomputed.get(this) as T) : this.compute((source) => source.value);\n\t\tfor (const source of this.sources) {\n\t\t\tif (source instanceof Source) source.dependents.add(this as Derived<unknown>);\n\t\t\telse this.#external.push(source.subscribe(() => this.refresh()));\n\t\t}\n\t}\n\n\tdetach(): void {\n\t\tfor (const source of this.sources)\n\t\t\tif (source instanceof Source) source.dependents.delete(this as Derived<unknown>);\n\t\tfor (const unsubscribe of this.#external.splice(0)) unsubscribe();\n\t}\n\n\tcompute(read: (source: Readable<unknown>) => unknown): T {\n\t\tconst result: unknown = this.#fn(...(this.sources.map(read) as never[]));\n\t\tif (result instanceof Promise) throw new TypeError(`derive ${this.key}: fn must be synchronous`);\n\t\treturn result as T;\n\t}\n\n\t/** Recomputes and republishes, including dependents. Errors are reported (the source write already happened). */\n\trefresh(): void {\n\t\ttry {\n\t\t\tconst next = this.compute((source) => source.value);\n\t\t\tconst dependents = computeDependents(this as Derived<unknown>, next);\n\t\t\tholdPublications(() => {\n\t\t\t\tthis.install(next);\n\t\t\t\tfor (const [derived, value] of dependents) derived.install(value);\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.options.report(error);\n\t\t}\n\t}\n\n\tinstall(next: T): void {\n\t\tconst before = this.#value;\n\t\tthis.#value = next;\n\t\tconst ops: Op[] =\n\t\t\tbefore === undefined || next === undefined\n\t\t\t\t? [[\"r\", (next ?? null) as JsonValue]]\n\t\t\t\t: diffRevisions(before as JsonValue, next as JsonValue);\n\t\tif (ops.length > 0) this.fanout.emit({ type: \"ops\", ops, value: next });\n\t}\n}\n\nexport const isSource = (value: unknown): value is Cell<unknown> | Derived<unknown> => value instanceof Source;\n\n/** Thrown by placeholders used before activation finished. */\nexport const notReady = (what: string): ChordError => new ChordError(\"not_ready\", `${what} is not ready`);\n"]}
package/dist/cluster.d.ts CHANGED
@@ -4,9 +4,10 @@ import { type ReloadOptions, type ReloadReport } from "./reload.ts";
4
4
  import { type Frame } from "./wire.ts";
5
5
  export declare function runClusterReload(host: HostImpl, plugins: readonly PluginRef[], options: ReloadOptions): Promise<ReloadReport>;
6
6
  /**
7
- * Brings a child that connected behind this host's epoch up to it: a reload of the child's modules (the ones
8
- * cluster reloads gave its name) to this host's current epoch, prepared, voted and committed like a cluster reload.
9
- * Until then the child is fenced: nothing is routed to it. The child's old code is never relabeled as a newer epoch.
7
+ * Brings a child that connected behind this host's code version up to it: a reload of the child's modules (the ones
8
+ * cluster reloads gave its name) to this host's current code version, prepared, voted and committed like a cluster
9
+ * reload. Until then the child is fenced: nothing is routed to it. The child's old code is never relabeled as a newer
10
+ * code version.
10
11
  */
11
12
  export declare function reconcileChild(host: HostImpl, connection: Connection): Promise<void>;
12
13
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../src/cluster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAuC,KAAK,aAAa,EAAE,KAAK,YAAY,EAAe,MAAM,aAAa,CAAC;AACtH,OAAO,EAAE,KAAK,KAAK,EAAe,MAAM,WAAW,CAAC;AAkCpD,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,OAAO,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,CAAC,CAkEvB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B1F;AAUD;;;;GAIG;AACH,qBAAa,YAAY;;IAIxB,YAAY,IAAI,EAAE,QAAQ,EAEzB;IAED,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE;QAAE,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;KAAE,CAAC,GAAG,IAAI,CAyBjG;IAED,kGAAkG;IAClG,UAAU,IAAI,IAAI,CAEjB;CAwCD"}
1
+ {"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../src/cluster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAuC,KAAK,aAAa,EAAE,KAAK,YAAY,EAAe,MAAM,aAAa,CAAC;AACtH,OAAO,EAAE,KAAK,KAAK,EAAe,MAAM,WAAW,CAAC;AAkCpD,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,OAAO,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,CAAC,CAwEvB;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC1F;AAUD;;;;GAIG;AACH,qBAAa,YAAY;;IAIxB,YAAY,IAAI,EAAE,QAAQ,EAEzB;IAED,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE;QAAE,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;KAAE,CAAC,GAAG,IAAI,CAyBjG;IAED,kGAAkG;IAClG,UAAU,IAAI,IAAI,CAEjB;CAwCD"}