@mmstack/mesh 22.0.0 → 22.2.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.
package/README.md CHANGED
@@ -38,8 +38,8 @@ store also participates in transition scopes, so a reconnect shows up as `pendin
38
38
 
39
39
  Reconnection is automatic, with exponential backoff. On reconnect the client resumes from a
40
40
  delta when possible, and re-applies any writes made while offline on top of whatever the room
41
- moved to in the meantime. A relay restart is detected through a room epoch, so a stale sequence
42
- number never corrupts state.
41
+ moved to in the meantime. A relay restart is detected through a room instance nonce, so a stale
42
+ sequence number never corrupts state.
43
43
 
44
44
  ## Conflict resolution
45
45
 
@@ -63,9 +63,136 @@ meshSync(board, {
63
63
  });
64
64
  ```
65
65
 
66
- `preserve` turns a clash into a `Conflicted` value holding both sides, so nothing is silently
67
- lost and a resolution is just a later write. See `@mmstack/primitives` for the full set of
68
- merge policies; they are the same ones the store uses for forks and tabs.
66
+ `preserve` turns a clash into a `Conflicted` value holding every side that clashed, not just two,
67
+ so nothing is silently lost and a resolution is just a later write. See `@mmstack/primitives` for
68
+ the full set of merge policies; they are the same ones the store uses for forks and tabs.
69
+
70
+ The default is last-writer-wins on a leaf, which drops one side of a true clash on the same field.
71
+ For a field where losing a write matters, set `preserve` on its path and resolve the `Conflicted`
72
+ value in the UI.
73
+
74
+ ## Interop with other CRDTs
75
+
76
+ A merge policy is a plain function, so one path can hold another CRDT and merge through that
77
+ library. This is the path for rich text or a list where order matters, which this package does not
78
+ model itself. Store the encoded document state at the path, and let the policy merge two states:
79
+
80
+ ```ts
81
+ import * as Y from 'yjs';
82
+ import type { MergeFn } from '@mmstack/primitives';
83
+
84
+ // the value at `doc` is a base64-encoded Y.Doc state. Use your own base64 helpers,
85
+ // since a JSON transport cannot carry a raw Uint8Array.
86
+ const yjsDoc: MergeFn = (_ancestor, mine, theirs) => {
87
+ const merged = new Y.Doc();
88
+ Y.applyUpdate(merged, fromBase64(mine as string));
89
+ Y.applyUpdate(merged, fromBase64(theirs as string));
90
+ return toBase64(Y.encodeStateAsUpdate(merged));
91
+ };
92
+
93
+ meshSync(store, {
94
+ room, writer, transport,
95
+ policies: [{ path: 'doc', merge: yjsDoc }],
96
+ });
97
+ ```
98
+
99
+ A concurrent edit runs the merge, so two people typing at once combine into one document with no
100
+ lost characters. A sequential edit already contains the earlier one, so it is taken whole.
101
+
102
+ The state travels as one value, so the wire cost grows with the document. For a large document, keep
103
+ the `Y.Doc` as an opaque leaf your store does not diff, and sync it with the library's own
104
+ incremental updates over the same transport. This package syncs the rest of the app state, the
105
+ library syncs the document, and the two stay independent.
106
+
107
+ ## Offline and durable outbox
108
+
109
+ Writes made while disconnected are held locally and sent on reconnect. That queue lives in memory
110
+ by default, so a full reload loses any write the room never acknowledged. Pass `outbox` to persist
111
+ it:
112
+
113
+ ```ts
114
+ import * as idbKeyval from 'idb-keyval';
115
+
116
+ meshSync(board, {
117
+ room: 'board-42',
118
+ writer: currentUserId,
119
+ transport,
120
+ outbox: { key: 'board-42', store: idbKeyval },
121
+ });
122
+ ```
123
+
124
+ `store` is any `AsyncStore` (`get`, `set`, `del`), the same interface `persist` takes. On boot the
125
+ client restores the saved queue, adopts the origin it used before the reload, and resends the
126
+ unacknowledged writes when it reconnects. Those offline edits then rebase onto whatever the room
127
+ moved to while the tab was gone. The queue is saved on a 300ms debounce; set `debounceMs` to change
128
+ it, or `0` to write on every change.
129
+
130
+ One origin is driven by one tab at a time. `crossTab` sets what a second tab on the same key does:
131
+
132
+ - `'queue'` (default) takes a Web Lock on the key. The second tab waits, with `status()` reading
133
+ `'connecting'`, until the first tab closes, then takes over. Exactly one durable writer holds the
134
+ key at a time.
135
+ - `'off'` skips the lock. Use it when you coordinate ownership yourself, for example a distinct key
136
+ per tab, or leader election over `tabSync`.
137
+
138
+ When the Web Locks API is unavailable, `'queue'` logs a development warning and runs without the
139
+ lock.
140
+
141
+ The outbox persists your unacknowledged writes, not a full snapshot. For a meshed store, use it in
142
+ place of wrapping the store in `persist`. The two race on boot, and the outbox is the one that
143
+ rebases offline edits onto the room. `persist` stays the tool for a store that is only ever local. A
144
+ cold offline boot shows the store's initial value plus your restored writes until a welcome arrives.
145
+ If you also need to read the last room state while fully offline, assemble it as a base first, below.
146
+
147
+ ## Assemble a base before connecting
148
+
149
+ Pass `whenReady` to hold the connection until a local base is in place. `meshSync` awaits it before
150
+ it connects and before it restores the outbox, so a store filled from another source is ready when
151
+ the room welcome arrives and rebases your pending writes on top. The status reads `connecting` while
152
+ it waits, so a boundary shows the store as pending.
153
+
154
+ ```ts
155
+ meshSync(graph, {
156
+ room: 'graph-7',
157
+ writer: currentUserId,
158
+ transport,
159
+ outbox: { key: 'graph-7', store: idbKeyval },
160
+ whenReady: () => baseReady, // a promise that resolves once the base is filled
161
+ });
162
+ ```
163
+
164
+ This is the boot order for a worker-owned, meshed, persisted graph. The worker hydrates the base, the
165
+ outbox restores this device's offline writes, then the room welcome supersedes the base and rebases
166
+ those writes on top. Each source runs in turn instead of racing, so the result does not depend on
167
+ which one happened to finish first. A rejected `whenReady` is treated as ready, so a base that fails
168
+ to load never holds the connection open.
169
+
170
+ ## Multiple tabs
171
+
172
+ Run `tabSync` and `meshSync` on the same store to share it across a user's tabs while one connection
173
+ carries it to the room. The outbox lock elects the leader, so only one tab holds the relay
174
+ connection and the others share state over `tabSync`. A write in any tab reaches the room through the
175
+ leader, and a room write reaches every tab through `tabSync`. When the leader tab closes, another
176
+ tab acquires the lock and takes over, adopting the persisted origin.
177
+
178
+ ```ts
179
+ import { store, tabSync } from '@mmstack/primitives';
180
+ import { meshSync, webSocketTransport } from '@mmstack/mesh';
181
+
182
+ const board = store<Board>(initialBoard());
183
+ tabSync(board, { id: 'board-42' }); // share across this user's tabs
184
+ meshSync(board, {
185
+ room: 'board-42',
186
+ writer: currentUserId,
187
+ transport: webSocketTransport('wss://sync.example.com'),
188
+ outbox: { key: 'board-42', store: idbKeyval }, // crossTab:'queue' elects one leader
189
+ });
190
+ ```
191
+
192
+ Each layer is a separate reader on the store's op stream, so they compose without knowing about each
193
+ other. A follower tab's `meshSync` stays idle until it holds the lock, so it never opens a second
194
+ connection. `tabSync` also takes a `bus` if you want to route over a channel other than the default
195
+ `BroadcastChannel`.
69
196
 
70
197
  ## Presence
71
198
 
@@ -96,6 +223,89 @@ meshSync(store, {
96
223
  });
97
224
  ```
98
225
 
226
+ ## Agents
227
+
228
+ An agent acts under the same protocol as a person: the same envelopes, attribution, ACLs, and undo.
229
+ There are two ways to give it write access, for two levels of trust.
230
+
231
+ ### Review a branch
232
+
233
+ `mesh.fork()` gives the agent a fork of the synced store. Its writes stay on the fork, so nothing
234
+ reaches the room until a person approves. `ops()` is the staged change as data, ready to render for
235
+ review. `commit()` emits it to the room; `discard()` drops it.
236
+
237
+ ```ts
238
+ const board = store<Board>(initialBoard());
239
+ const mesh = meshSync(board, { room: 'board-42', writer: userId, transport });
240
+
241
+ const proposal = mesh.fork(); // the agent's isolated branch, off the room
242
+ runAgent(proposal.store); // it writes here
243
+
244
+ const changes = proposal.ops(); // StoreOp[] for the reviewer to see
245
+ proposal.commit(); // approve: emits as concurrent writes to the room
246
+ // proposal.rebase(); // re-observe the room, then commit on top
247
+ // proposal.discard(); // reject: drops the staged writes
248
+ ```
249
+
250
+ The commit cites what the fork observed when it forked, so an edit that lands on the room while a
251
+ person reviews stays a concurrent value the merge policy decides, never silently overwritten by the
252
+ approval. Call `rebase()` to re-observe the room first when the proposal should apply on top of the
253
+ latest. The reviewer reads and writes normal store values, and the agent never touches the room
254
+ directly. This is the fit when a write should be seen before it lands.
255
+
256
+ ### Write as a peer
257
+
258
+ An agent can also join the room directly, scoped by the relay ACL. Give it a narrower `ctx` and a
259
+ `policy`, and the relay ejects any write outside its scope (see [Trust](#trust)).
260
+
261
+ ```ts
262
+ meshSync(board, {
263
+ room: 'board-42',
264
+ writer: agentId,
265
+ transport,
266
+ ctx: { kind: 'agent', claims: { scope: 'pricing' } },
267
+ policy: pricingScopeOnly,
268
+ });
269
+ ```
270
+
271
+ A live agent inherits the same conflict rules as everyone else, so a fast agent can win a
272
+ last-writer-wins race on a shared field. Reach for the branch when a write should be reviewed, or
273
+ when the field carries real weight.
274
+
275
+ ## Health
276
+
277
+ `meshSync` returns a `health` signal alongside `status`. It composes the connection state and any
278
+ reject reason into one value you can render:
279
+
280
+ ```ts
281
+ const mesh = meshSync(store, { room, writer, transport });
282
+ // mesh.health() -> { status, reason?, lastSyncedAt? }
283
+ ```
284
+
285
+ `status` is one of `live`, `offline`, `outdated`, `ejected`, or `degraded`. The useful distinction
286
+ is `outdated` versus `ejected`. A versioned reject (the client's `proto`, `policyVersion`, or
287
+ `schemaVersion` is behind the room) reports `outdated` with the reason, so you can show an update or
288
+ reload prompt instead of a dead connection. A policy tripwire reports `ejected`. `degraded` is the
289
+ slot for local problems such as a full storage quota or a dead worker; those are your own signals to
290
+ fold in, since `meshSync` only owns the connection side.
291
+
292
+ ## Schema versions
293
+
294
+ The data shape a room holds is a third version axis next to `proto` and `policyVersion`. Additive
295
+ changes need no version at all: new fields fold in, and a client ignores fields it does not render.
296
+ For a breaking change, pass `schemaVersion` and migrate through the log.
297
+
298
+ ```ts
299
+ meshSync(store, { room, writer, transport, schemaVersion: 2 });
300
+ ```
301
+
302
+ A migration is an envelope: a privileged writer (run from your deploy) emits a root set carrying the
303
+ new `schemaVersion`. The relay bumps the room's schema and its instance nonce, so every watermark dies and
304
+ clients re-hydrate into the new shape. A client older than the room is rejected with reason
305
+ `schema`, and a client already connected when the migration lands stops applying and reports
306
+ `outdated`. Because the migration rides the log, a compacted snapshot and `relay.hydrate` are
307
+ post-migration by construction, and journal replay stays correct forever.
308
+
99
309
  ## Transports
100
310
 
101
311
  - `webSocketTransport(url)` for a relay over WebSocket.
Binary file
@@ -1 +1 @@
1
- {"version":3,"file":"mmstack-mesh.mjs","sources":["../../../../../packages/mesh/client/src/lib/mesh-sync.ts","../../../../../packages/mesh/client/src/lib/transport.ts","../../../../../packages/mesh/client/src/lib/webrtc-mesh.ts","../../../../../packages/mesh/client/src/mmstack-mesh.ts"],"sourcesContent":["import {\n computed,\n DestroyRef,\n inject,\n Injector,\n isDevMode,\n runInInjectionContext,\n signal,\n type Signal,\n type WritableSignal,\n} from '@angular/core';\nimport {\n checkEnvelope,\n MESH_PROTO_VERSION,\n type OpPolicy,\n type PresenceState,\n type PrincipalCtx,\n type SeqEnvelope,\n type ServerMsg,\n} from '@mmstack/mesh-protocol';\nimport {\n opSync,\n registerResource,\n type MergePolicyEntry,\n type OpEnvelope,\n type ResourceLike,\n} from '@mmstack/primitives';\nimport type { MeshTransport, MeshTransportFactory } from './transport';\n\nexport type MeshStatus =\n | 'connecting'\n | 'live'\n | 'reconnecting'\n | 'ejected'\n | 'closed';\n\nexport type MeshPeer = PresenceState;\n\nexport type MeshSyncOptions = {\n readonly room: string;\n /** Opaque principal pseudonym — provided, never minted (op-protocol RFC §3). */\n readonly writer: string;\n readonly transport: MeshTransportFactory;\n /** Per-path merge policies for rebase/convergence (`lww` default). */\n readonly policies?: readonly MergePolicyEntry[];\n /** Emit-side validation, symmetric with the relay's (tripwire honesty). */\n readonly policy?: OpPolicy;\n /** kind/claims of this principal, so a shared policy evaluates identically on both sides. */\n readonly ctx?: Omit<PrincipalCtx, 'writer'>;\n readonly policyVersion?: number;\n /** Exponential backoff cap for reconnects (default 15s; base 500ms + jitter). */\n readonly reconnect?: { readonly maxDelayMs?: number };\n readonly injector?: Injector;\n /** Register with the nearest transition scope so (re)connection surfaces as `pending`. */\n readonly register?: 'track' | 'suspend';\n readonly onEject?: (reason: string) => void;\n};\n\nexport type MeshSyncRef = {\n readonly status: Signal<MeshStatus>;\n readonly peers: Signal<readonly MeshPeer[]>;\n /** Publish this client's ephemeral presence payload (cursor, section, activity…). */\n setPresence(data: unknown): void;\n close(): void;\n};\n\nconst RECONNECT_BASE_MS = 500;\n\n/**\n * Replicates a signal store across clients through a relay room: local writes emit stamped\n * envelopes, remote envelopes fold in convergently, reconnects resume via delta or snapshot\n * with unacknowledged local writes rebased on top, and presence rides an ephemeral channel.\n * A synced store reads exactly like a local one — connection state surfaces only through\n * `status` and the transition scope (op-protocol RFC §10).\n */\nexport function meshSync<T extends object>(\n source: WritableSignal<T>,\n opt: MeshSyncOptions,\n): MeshSyncRef {\n const injector = opt.injector ?? inject(Injector);\n const status = signal<MeshStatus>('connecting');\n const peerMap = signal<ReadonlyMap<string, MeshPeer>>(new Map());\n const peers = computed(() => [...peerMap().values()]);\n const policyVersion = opt.policyVersion ?? 0;\n\n const sync = opSync(source, {\n writer: opt.writer,\n policies: opt.policies,\n policyVersion,\n injector,\n });\n\n const unacked = new Map<number, OpEnvelope>();\n let highestAcked = 0;\n let lastSeq = 0;\n let epoch: string | undefined;\n let presenceData: unknown;\n let hasPresence = false;\n let transport: MeshTransport | null = null;\n let attempts = 0;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n let unsubs: (() => void)[] = [];\n let closed = false;\n\n const terminal = (state: 'ejected' | 'closed', reason?: string): void => {\n closed = true;\n if (reconnectTimer !== undefined) clearTimeout(reconnectTimer);\n for (const unsub of unsubs.splice(0)) unsub();\n unsubLocal();\n unacked.clear();\n transport?.close();\n transport = null;\n peerMap.set(new Map());\n status.set(state);\n if (reason !== undefined) opt.onEject?.(reason);\n };\n\n const sendEnv = (env: OpEnvelope): void => {\n transport?.send({ t: 'env', room: opt.room, env });\n };\n\n const flushUnacked = (): void => {\n for (const env of [...unacked.values()].sort((a, b) => a.version - b.version)) {\n sendEnv(env);\n }\n };\n\n const handle = (msg: ServerMsg): void => {\n if (msg.room !== opt.room) return;\n switch (msg.t) {\n case 'welcome': {\n if (epoch !== undefined && msg.epoch !== epoch) lastSeq = 0;\n epoch = msg.epoch;\n peerMap.set(new Map(msg.peers.map((p) => [p.origin, p])));\n if (msg.mode === 'delta') {\n for (const env of msg.envs) applyRemote(env);\n } else if (msg.mode === 'snapshot') {\n sync.hydrate(msg.root as T, { [sync.origin]: highestAcked });\n lastSeq = msg.seq;\n } else if (msg.seq === 0 && lastSeq === 0) {\n sync.seed();\n }\n lastSeq = Math.max(lastSeq, msg.seq);\n attempts = 0;\n status.set('live');\n flushUnacked();\n if (hasPresence) transport?.send({ t: 'presence', room: opt.room, data: presenceData });\n return;\n }\n case 'env':\n applyRemote(msg.env);\n return;\n case 'presence': {\n const next = new Map(peerMap());\n if (msg.gone) next.delete(msg.peer.origin);\n else next.set(msg.peer.origin, msg.peer);\n peerMap.set(next);\n return;\n }\n case 'eject':\n if (msg.writer === opt.writer) terminal('ejected', msg.reason);\n return;\n case 'reject':\n terminal('ejected', msg.reason);\n return;\n }\n };\n\n const applyRemote = (env: SeqEnvelope): void => {\n lastSeq = Math.max(lastSeq, env.seq);\n if (env.origin === sync.origin) {\n unacked.delete(env.version);\n highestAcked = Math.max(highestAcked, env.version);\n return;\n }\n sync.receive(env);\n };\n\n const connect = (): void => {\n if (closed) return;\n for (const unsub of unsubs.splice(0)) unsub();\n const t = opt.transport();\n transport = t;\n unsubs = [\n t.onMessage(handle),\n t.onClose(() => {\n if (closed || transport !== t) return;\n transport = null;\n status.set('reconnecting');\n const delay = Math.min(\n opt.reconnect?.maxDelayMs ?? 15_000,\n RECONNECT_BASE_MS * 2 ** attempts++,\n );\n reconnectTimer = setTimeout(() => {\n reconnectTimer = undefined;\n connect();\n }, delay + Math.random() * 100);\n }),\n ];\n t.send({\n t: 'hello',\n room: opt.room,\n origin: sync.origin,\n proto: MESH_PROTO_VERSION,\n policyVersion,\n seq: lastSeq > 0 ? lastSeq : undefined,\n });\n };\n\n const unsubLocal = sync.subscribe((env) => {\n const violation = checkEnvelope(opt.policy, env, { ...opt.ctx, writer: opt.writer });\n if (violation) {\n if (isDevMode()) {\n console.warn('[@mmstack/mesh] local write violates the room policy — not sent', violation);\n }\n return;\n }\n unacked.set(env.version, env);\n if (status() === 'live') sendEnv(env);\n });\n\n if (opt.register) {\n const connection: ResourceLike = {\n status: computed(() => {\n const s = status();\n return s === 'connecting'\n ? 'loading'\n : s === 'reconnecting'\n ? 'reloading'\n : s === 'ejected'\n ? 'error'\n : 'resolved';\n }),\n isLoading: computed(\n () => status() === 'connecting' || status() === 'reconnecting',\n ),\n hasValue: () => true,\n };\n runInInjectionContext(injector, () =>\n registerResource(connection, { suspends: opt.register === 'suspend' }),\n );\n }\n\n injector.get(DestroyRef).onDestroy(() => {\n if (!closed) terminal('closed');\n unsubLocal();\n sync.destroy();\n });\n\n connect();\n\n return {\n status: status.asReadonly(),\n peers,\n setPresence: (data) => {\n presenceData = data;\n hasPresence = true;\n if (status() === 'live') {\n transport?.send({ t: 'presence', room: opt.room, data });\n }\n },\n close: () => {\n if (!closed) terminal('closed');\n unsubLocal();\n sync.destroy();\n },\n };\n}\n","import { isDevMode } from '@angular/core';\nimport type {\n ClientMsg,\n PrincipalCtx,\n Relay,\n ServerMsg,\n} from '@mmstack/mesh-protocol';\n\n/**\n * One live connection to a relay. `meshSync` calls the factory per (re)connect; a transport\n * represents a single connection and never reconnects itself.\n */\nexport type MeshTransport = {\n send(msg: ClientMsg): void;\n onMessage(cb: (msg: ServerMsg) => void): () => void;\n onClose(cb: () => void): () => void;\n close(): void;\n};\n\nexport type MeshTransportFactory = () => MeshTransport;\n\n/**\n * JSON-over-WebSocket transport. Messages sent before the socket opens are buffered and\n * flushed on open; malformed inbound frames are dropped.\n */\nexport function webSocketTransport(\n url: string,\n protocols?: string | string[],\n): MeshTransportFactory {\n return () => {\n const ws = new WebSocket(url, protocols);\n const messageCbs = new Set<(msg: ServerMsg) => void>();\n const closeCbs = new Set<() => void>();\n const pending: string[] = [];\n\n ws.onopen = () => {\n for (const frame of pending.splice(0)) ws.send(frame);\n };\n ws.onmessage = (event) => {\n let msg: ServerMsg;\n try {\n msg = JSON.parse(String(event.data)) as ServerMsg;\n } catch {\n if (isDevMode()) {\n console.warn('[@mmstack/mesh] dropped unparseable frame (expected JSON text)');\n }\n return;\n }\n for (const cb of [...messageCbs]) cb(msg);\n };\n ws.onclose = () => {\n for (const cb of [...closeCbs]) cb();\n };\n\n return {\n send: (msg) => {\n const frame = JSON.stringify(msg);\n if (ws.readyState === WebSocket.OPEN) ws.send(frame);\n else if (ws.readyState === WebSocket.CONNECTING) pending.push(frame);\n },\n onMessage: (cb) => {\n messageCbs.add(cb);\n return () => messageCbs.delete(cb);\n },\n onClose: (cb) => {\n closeCbs.add(cb);\n return () => closeCbs.delete(cb);\n },\n close: () => ws.close(),\n };\n };\n}\n\n/**\n * Connects directly to an in-process `createRelay` — no network. The backbone of full-loop\n * tests, and handy for single-process demos or an SSR-side room.\n */\nexport function directTransport(\n relay: Relay,\n ctx: PrincipalCtx,\n): MeshTransportFactory {\n return () => {\n const messageCbs = new Set<(msg: ServerMsg) => void>();\n const closeCbs = new Set<() => void>();\n let open = true;\n\n const connection = relay.connect(\n {\n send: (msg) => {\n if (!open) return;\n for (const cb of [...messageCbs]) cb(msg);\n },\n close: () => {\n if (!open) return;\n open = false;\n connection.disconnect();\n for (const cb of [...closeCbs]) cb();\n },\n },\n ctx,\n );\n\n return {\n send: (msg) => {\n if (open) connection.receive(msg);\n },\n onMessage: (cb) => {\n messageCbs.add(cb);\n return () => messageCbs.delete(cb);\n },\n onClose: (cb) => {\n closeCbs.add(cb);\n return () => closeCbs.delete(cb);\n },\n close: () => {\n if (!open) return;\n open = false;\n connection.disconnect();\n for (const cb of [...closeCbs]) cb();\n },\n };\n };\n}\n","import {\n computed,\n DestroyRef,\n inject,\n Injector,\n signal,\n type Signal,\n type WritableSignal,\n} from '@angular/core';\nimport { MESH_PROTO_VERSION, type ServerMsg } from '@mmstack/mesh-protocol';\nimport {\n opSync,\n type MergePolicyEntry,\n type OpEnvelope,\n} from '@mmstack/primitives';\nimport type { MeshTransport, MeshTransportFactory } from './transport';\n\n/** A data channel as the P2P engine needs it; opens later, buffers nothing itself. */\nexport type DataChannelLike = {\n send(frame: string): void;\n onMessage(cb: (frame: string) => void): () => void;\n onOpen(cb: () => void): () => void;\n onClose(cb: () => void): () => void;\n close(): void;\n};\n\n/**\n * Creates one peer link. `polite` assigns the perfect-negotiation role (derived\n * deterministically from origin ordering); `sendSignal` routes offer/answer/ICE payloads\n * through the relay; `signal` delivers the remote side's payloads.\n */\nexport type PeerConnector = (opt: {\n readonly remote: string;\n readonly polite: boolean;\n readonly sendSignal: (data: unknown) => void;\n}) => {\n readonly channel: DataChannelLike;\n signal(data: unknown): void;\n close(): void;\n};\n\n/** WebRTC `PeerConnector` implementing the perfect-negotiation pattern. Browser-only. */\nexport function rtcPeerConnector(config?: RTCConfiguration): PeerConnector {\n return ({ polite, sendSignal }) => {\n const pc = new RTCPeerConnection(config);\n const messageCbs = new Set<(frame: string) => void>();\n const openCbs = new Set<() => void>();\n const closeCbs = new Set<() => void>();\n const pending: string[] = [];\n let dc: RTCDataChannel | null = null;\n let makingOffer = false;\n let ignoreOffer = false;\n\n const attach = (channel: RTCDataChannel): void => {\n dc = channel;\n channel.onmessage = (e) => {\n for (const cb of [...messageCbs]) cb(String(e.data));\n };\n channel.onopen = () => {\n for (const frame of pending.splice(0)) channel.send(frame);\n for (const cb of [...openCbs]) cb();\n };\n channel.onclose = () => {\n for (const cb of [...closeCbs]) cb();\n };\n };\n\n if (!polite) attach(pc.createDataChannel('mmstack-mesh'));\n else pc.ondatachannel = (e) => attach(e.channel);\n\n pc.onicecandidate = (e) => {\n if (e.candidate) sendSignal({ ice: e.candidate.toJSON() });\n };\n pc.onnegotiationneeded = async () => {\n try {\n makingOffer = true;\n await pc.setLocalDescription();\n sendSignal({ description: pc.localDescription });\n } finally {\n makingOffer = false;\n }\n };\n\n return {\n channel: {\n send: (frame) => {\n if (dc && dc.readyState === 'open') dc.send(frame);\n else pending.push(frame);\n },\n onMessage: (cb) => (messageCbs.add(cb), () => messageCbs.delete(cb)),\n onOpen: (cb) => (openCbs.add(cb), () => openCbs.delete(cb)),\n onClose: (cb) => (closeCbs.add(cb), () => closeCbs.delete(cb)),\n close: () => dc?.close(),\n },\n signal: async (data) => {\n const { description, ice } = (data ?? {}) as {\n description?: RTCSessionDescriptionInit;\n ice?: RTCIceCandidateInit;\n };\n if (description) {\n const collision =\n description.type === 'offer' &&\n (makingOffer || pc.signalingState !== 'stable');\n ignoreOffer = !polite && collision;\n if (ignoreOffer) return;\n await pc.setRemoteDescription(description);\n if (description.type === 'offer') {\n await pc.setLocalDescription();\n sendSignal({ description: pc.localDescription });\n }\n } else if (ice) {\n try {\n await pc.addIceCandidate(ice);\n } catch (err) {\n if (!ignoreOffer) throw err;\n }\n }\n },\n close: () => {\n dc?.close();\n pc.close();\n },\n };\n };\n}\n\ntype P2PMsg =\n | { t: 'hello'; wm: Record<string, number> }\n | { t: 'state'; root: unknown; wm: Record<string, number> }\n | { t: 'uptodate' }\n | { t: 'env'; env: OpEnvelope };\n\nexport type WebRtcMeshOptions = {\n readonly room: string;\n readonly writer: string;\n /** Signaling path to the relay (ws or direct) — data flows peer-to-peer. */\n readonly signaling: MeshTransportFactory;\n /** Peer link factory; defaults to {@link rtcPeerConnector}. Injectable for tests. */\n readonly connector?: PeerConnector;\n readonly policies?: readonly MergePolicyEntry[];\n readonly policyVersion?: number;\n readonly injector?: Injector;\n};\n\nexport type WebRtcMeshRef = {\n readonly status: Signal<'connecting' | 'live'>;\n /** Origins with an OPEN data channel. */\n readonly peers: Signal<readonly string[]>;\n close(): void;\n};\n\ntype Peer = {\n link: ReturnType<PeerConnector>;\n open: boolean;\n unsubs: (() => void)[];\n};\n\n/**\n * Peer-to-peer mesh sync (unsequenced topology, op-protocol RFC §4): the relay only signals\n * and tracks membership; envelopes flow over WebRTC data channels and converge via the\n * per-path register map. Catch-up is pairwise: on channel open both sides exchange\n * watermarks; a side whose state is strictly covered hydrates from the other. Two peers that\n * diverged while BOTH held state keep their convergent go-forward guarantees but do not\n * retroactively merge history (same contract as the tab rung).\n */\nexport function webRtcMesh<T extends object>(\n source: WritableSignal<T>,\n opt: WebRtcMeshOptions,\n): WebRtcMeshRef {\n const injector = opt.injector ?? inject(Injector);\n const connector = opt.connector ?? rtcPeerConnector();\n const status = signal<'connecting' | 'live'>('connecting');\n const openPeers = signal<ReadonlySet<string>>(new Set());\n const peers = new Map<string, Peer>();\n let signalingUnsubs: (() => void)[] = [];\n let transport: MeshTransport | null = null;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n let closed = false;\n\n const sync = opSync(source, {\n writer: opt.writer,\n policies: opt.policies,\n policyVersion: opt.policyVersion,\n injector,\n });\n\n const covered = (\n mine: Record<string, number>,\n theirs: Record<string, number>,\n ): boolean => Object.entries(mine).every(([o, v]) => (theirs[o] ?? 0) >= v);\n\n const sendTo = (peer: Peer, msg: P2PMsg): void => {\n peer.link.channel.send(JSON.stringify(msg));\n };\n\n const broadcast = (msg: P2PMsg): void => {\n for (const peer of peers.values()) {\n if (peer.open) sendTo(peer, msg);\n }\n };\n\n const dropPeer = (origin: string): void => {\n const peer = peers.get(origin);\n if (!peer) return;\n peers.delete(origin);\n for (const unsub of peer.unsubs) unsub();\n peer.link.close();\n openPeers.update((set) => {\n const next = new Set(set);\n next.delete(origin);\n return next;\n });\n };\n\n const ensurePeer = (origin: string): Peer => {\n let peer = peers.get(origin);\n if (peer) return peer;\n const link = connector({\n remote: origin,\n polite: sync.origin > origin,\n sendSignal: (data) =>\n transport?.send({ t: 'signal', room: opt.room, to: origin, data }),\n });\n peer = { link, open: false, unsubs: [] };\n peers.set(origin, peer);\n peer.unsubs.push(\n link.channel.onOpen(() => {\n peer!.open = true;\n openPeers.update((set) => new Set(set).add(origin));\n sendTo(peer!, { t: 'hello', wm: sync.watermark() });\n }),\n link.channel.onMessage((frame) => {\n let msg: P2PMsg;\n try {\n msg = JSON.parse(frame) as P2PMsg;\n } catch {\n return;\n }\n handlePeerMsg(peer!, msg);\n }),\n link.channel.onClose(() => dropPeer(origin)),\n );\n return peer;\n };\n\n const handlePeerMsg = (peer: Peer, msg: P2PMsg): void => {\n switch (msg.t) {\n case 'hello': {\n const snap = sync.snapshot();\n if (covered(snap.wm, msg.wm)) sendTo(peer, { t: 'uptodate' });\n else sendTo(peer, { t: 'state', root: snap.root, wm: snap.wm });\n return;\n }\n case 'state': {\n if (covered(sync.watermark(), msg.wm)) {\n sync.hydrate(msg.root as T, msg.wm);\n }\n return;\n }\n case 'uptodate':\n return;\n case 'env':\n sync.receive(msg.env);\n return;\n }\n };\n\n const handleSignaling = (msg: ServerMsg): void => {\n if (msg.room !== opt.room) return;\n switch (msg.t) {\n case 'welcome':\n for (const origin of msg.members) ensurePeer(origin);\n status.set('live');\n return;\n case 'member':\n if (msg.gone) dropPeer(msg.origin);\n else ensurePeer(msg.origin);\n return;\n case 'signal':\n ensurePeer(msg.from).link.signal(msg.data);\n return;\n case 'reject':\n close();\n return;\n }\n };\n\n const connectSignaling = (): void => {\n if (closed) return;\n for (const unsub of signalingUnsubs.splice(0)) unsub();\n const t = opt.signaling();\n transport = t;\n signalingUnsubs = [\n t.onMessage(handleSignaling),\n t.onClose(() => {\n if (closed || transport !== t) return;\n transport = null;\n reconnectTimer = setTimeout(connectSignaling, 1000);\n }),\n ];\n t.send({\n t: 'hello',\n room: opt.room,\n origin: sync.origin,\n proto: MESH_PROTO_VERSION,\n policyVersion: opt.policyVersion ?? 0,\n });\n };\n\n const unsubLocal = sync.subscribe((env) => broadcast({ t: 'env', env }));\n\n const close = (): void => {\n if (closed) return;\n closed = true;\n if (reconnectTimer !== undefined) clearTimeout(reconnectTimer);\n unsubLocal();\n for (const origin of [...peers.keys()]) dropPeer(origin);\n for (const unsub of signalingUnsubs.splice(0)) unsub();\n transport?.close();\n transport = null;\n sync.destroy();\n status.set('connecting');\n };\n\n injector.get(DestroyRef).onDestroy(close);\n connectSignaling();\n\n return {\n status: status.asReadonly(),\n peers: computed(() => [...openPeers()]),\n close,\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mmstack-mesh.mjs","sources":["../../../../../packages/mesh/client/src/lib/mesh-sync.ts","../../../../../packages/mesh/client/src/lib/transport.ts","../../../../../packages/mesh/client/src/lib/webrtc-mesh.ts","../../../../../packages/mesh/client/src/mmstack-mesh.ts"],"sourcesContent":["import {\n computed,\n DestroyRef,\n inject,\n Injector,\n isDevMode,\n runInInjectionContext,\n signal,\n type Signal,\n type WritableSignal,\n} from '@angular/core';\nimport {\n checkEnvelope,\n MESH_PROTO_VERSION,\n type OpPolicy,\n type PresenceState,\n type PrincipalCtx,\n type SeqEnvelope,\n type ServerMsg,\n} from '@mmstack/mesh-protocol';\nimport {\n createConvergingApply,\n forkStore,\n OP_PROTO_VERSION,\n opSync,\n registerResource,\n type AsyncStore,\n type DotFrontier,\n type ForkStoreOptions,\n type MergePolicyEntry,\n type OpEnvelope,\n type OpSync,\n type ResourceLike,\n type SyncedFork,\n type SyncOp,\n type WritableSignalStore,\n} from '@mmstack/primitives';\nimport type { MeshTransport, MeshTransportFactory } from './transport';\n\nexport type MeshStatus =\n | 'connecting'\n | 'live'\n | 'reconnecting'\n | 'ejected'\n | 'closed';\n\nexport type MeshPeer = PresenceState;\n\n/**\n * A composed, user-facing health status for a synced store the surface that\n * turns a versioned reject from a dead socket into a speakable 'outdated' banner.\n */\nexport type SyncHealthStatus =\n | 'live'\n | 'offline'\n | 'outdated'\n | 'ejected'\n | 'degraded';\n\nexport type SyncHealth = {\n readonly status: SyncHealthStatus;\n /** Why, when the status is `outdated`/`ejected`/`degraded`. */\n readonly reason?:\n | 'proto'\n | 'policy-version'\n | 'schema'\n | 'quota'\n | 'worker'\n | (string & {});\n /** `Date.now()` of the last successful sync (welcome or applied env). */\n readonly lastSyncedAt?: number;\n /**\n * Offline writes persisted by an older build that could not be restored on boot. Envelopes\n * from a previous wire-protocol version lack the citation metadata current merging needs,\n * so they are dropped (loudly) instead of being upgraded with fabricated citations.\n */\n readonly droppedOfflineWrites?: number;\n /**\n * Received envelopes rejected as malformed by the deterministic well-formedness check (a bad id\n * or path segment, a non-integer version, an unknown op kind, a negative epoch, forged cites, a\n * root delete). Direct peer-to-peer rooms are trust-full for authority, so this is a peer's line\n * against a malformed neighbor; through a relay these are also rejected at the relay.\n */\n readonly droppedInvalidEnvelopes?: number;\n};\n\n// a versioned reject means \"your build is behind\" → prompt an update, not a dead socket\nconst OUTDATED_REASONS: ReadonlySet<string> = new Set([\n 'proto',\n 'policy-version',\n 'schema',\n]);\n\nexport type MeshSyncOptions = {\n readonly room: string;\n /** Opaque principal pseudonym — provided, never minted. */\n readonly writer: string;\n readonly transport: MeshTransportFactory;\n /** Per-path merge policies for rebase/convergence (`lww` default). */\n readonly policies?: readonly MergePolicyEntry[];\n /** Emit-side validation, symmetric with the relay's (tripwire honesty). */\n readonly policy?: OpPolicy;\n /** kind/claims of this principal, so a shared policy evaluates identically on both sides. */\n readonly ctx?: Omit<PrincipalCtx, 'writer'>;\n /**\n * The room's shared configuration pin, checked at hello against the relay's. Bump it\n * together whenever anything peers must agree on changes: the write policy, and just as\n * importantly the merge/fold configuration (`policies`). Peers folding the same register\n * state with different rules read different values, so one room means one fold config; a\n * client pinned to an older version is rejected `policy-version` and surfaces `outdated`.\n */\n readonly policyVersion?: number;\n /** The data shape this client speaks. Older-than-room → `outdated`, and a\n * newer-schema migration envelope arriving mid-session flips this client to `outdated` too. */\n readonly schemaVersion?: number;\n /** Exponential backoff cap for reconnects (default 15s; base 500ms + jitter). */\n readonly reconnect?: { readonly maxDelayMs?: number };\n readonly injector?: Injector;\n /** Register with the nearest transition scope so (re)connection surfaces as `pending`. */\n readonly register?: 'track' | 'suspend';\n readonly onEject?: (reason: string) => void;\n /**\n * Hold the connection until the local base is assembled. `meshSync` awaits this before it connects\n * (and before it restores an `outbox`), so a store hydrated from another source first (a worker\n * graph, a disk snapshot) is in place when the room welcome arrives and rebases pending on top.\n * The status stays `connecting` while it is pending. A rejection is treated as ready, so a base\n * that fails to load never wedges the connection.\n */\n readonly whenReady?: () => PromiseLike<void> | void;\n /**\n * Persist the unacknowledged local outbox (and this client's stable origin) so offline writes\n * survive a REBOOT, not just a live reconnect: on boot they are restored and rebased onto the room\n * on the next welcome, instead of being lost with in-memory state. The payload is written to\n * `store` under `key`, debounced.\n *\n * By default (`crossTab: 'queue'`) a Web Lock on `key` makes this a single-writer-per-key resource:\n * a second tab sharing the key WAITS (stays `connecting`, surfaced via `status`/`health`) until the\n * first releases, rather than restoring the same origin and colliding on version mints. Set\n * `crossTab: 'off'` to skip the lock and coordinate ownership yourself (e.g. a per-tab key, or\n * leader election over `tabSync`). A debounced write means a hard crash within the debounce window\n * can drop the very last mint — a small, best-effort gap; tune it with `debounceMs`.\n */\n readonly outbox?: {\n readonly key: string;\n readonly store: AsyncStore;\n /** Coalesce outbox writes by this many ms (default 300; `0` = write on every change). */\n readonly debounceMs?: number;\n /**\n * Cross-tab contention for the shared `key`. `'queue'` (default) holds a Web Lock so only one tab\n * owns the durable outbox at a time; others wait. `'off'` skips the lock (you coordinate).\n * (A future `'ephemeral'` — non-leaders run live with a throwaway origin — is planned.)\n */\n readonly crossTab?: 'queue' | 'off';\n };\n};\n\n/** The shape persisted under `outbox.key`: the stable origin, the emit high-water, and the tail. */\ntype PersistedOutbox = {\n readonly origin: string;\n readonly version: number;\n readonly envs: readonly OpEnvelope[];\n};\n\nexport type MeshSyncRef<T extends object = Record<string, unknown>> = {\n readonly status: Signal<MeshStatus>;\n /** Composed sync-health for a user-facing surface. */\n readonly health: Signal<SyncHealth>;\n readonly peers: Signal<readonly MeshPeer[]>;\n /** Publish this client's ephemeral presence payload (cursor, section, activity…). */\n setPresence(data: unknown): void;\n /**\n * Fork the synced store for isolated, reviewable edits (an agent branch, a staged change). The\n * fork observes the room as it is now; committing emits its diff citing only those observed\n * writes, so an edit that lands on the room while the fork is open stays a concurrent value the\n * merge policy decides rather than being overwritten by the commit. Call `rebase()` to re-observe\n * the room and commit on top of the latest instead. `discard()` drops the staged edits.\n */\n fork(opt?: ForkStoreOptions<T & Record<string, any>>): SyncedFork<T & Record<string, any>>;\n close(): void;\n};\n\nconst RECONNECT_BASE_MS = 500;\nconst OUTBOX_DEBOUNCE_MS = 300;\n\n/**\n * Replicates a signal store across clients through a relay room: local writes emit stamped\n * envelopes, remote envelopes fold in convergently, reconnects resume via delta or snapshot\n * with unacknowledged local writes rebased on top, and presence rides an ephemeral channel.\n * A synced store reads exactly like a local one — connection state surfaces only through\n * `status` and the transition scope.\n */\nexport function meshSync<T extends object>(\n source: WritableSignal<T>,\n opt: MeshSyncOptions,\n): MeshSyncRef<T> {\n const injector = opt.injector ?? inject(Injector);\n const status = signal<MeshStatus>('connecting');\n const lastReason = signal<string | undefined>(undefined);\n const lastSyncedAt = signal<number | undefined>(undefined);\n const droppedOffline = signal(0);\n const droppedInvalid = signal(0);\n const peerMap = signal<ReadonlyMap<string, MeshPeer>>(new Map());\n const peers = computed(() => [...peerMap().values()]);\n const policyVersion = opt.policyVersion ?? 0;\n\n const health = computed<SyncHealth>(() => {\n const at = lastSyncedAt();\n const dropped = droppedOffline();\n const invalid = droppedInvalid();\n const extra = {\n ...(dropped > 0 ? { droppedOfflineWrites: dropped } : undefined),\n ...(invalid > 0 ? { droppedInvalidEnvelopes: invalid } : undefined),\n };\n switch (status()) {\n case 'live':\n return { status: 'live', lastSyncedAt: at, ...extra };\n case 'ejected': {\n const reason = lastReason();\n return reason && OUTDATED_REASONS.has(reason)\n ? { status: 'outdated', reason, lastSyncedAt: at, ...extra }\n : { status: 'ejected', reason, lastSyncedAt: at, ...extra };\n }\n default: // connecting / reconnecting / closed — not reachable\n return { status: 'offline', lastSyncedAt: at, ...extra };\n }\n });\n\n // Created lazily: with a persisted outbox we must adopt the stored origin BEFORE minting anything\n let sync!: OpSync<T>;\n let started = false;\n let unsubLocal: () => void = () => undefined;\n\n // A fresh origin is minted every boot; a restored outbox tail resends under ITS recorded origin.\n // So this client is responsible for more than one origin, and unacked writes are keyed by\n // (origin, version) with an acked high-water per origin. `ownOrigins` is every origin we resend\n // for (the fresh mint plus any restored-tail origins), so an echo of any of them clears an ack.\n const ownOrigins = new Set<string>();\n const unacked = new Map<string, OpEnvelope>();\n const acked = new Map<string, number>();\n const unackedKey = (env: { origin: string; version: number }): string =>\n `${env.origin}\u0000${env.version}`;\n let lastSeq = 0;\n let instance: string | undefined;\n let presenceData: unknown;\n let hasPresence = false;\n let transport: MeshTransport | null = null;\n let attempts = 0;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n let unsubs: (() => void)[] = [];\n let closed = false;\n let persistTimer: ReturnType<typeof setTimeout> | undefined;\n // single-writer Web Lock (crossTab:'queue'): `release` frees it for the next tab once acquired;\n // `cancel` aborts a still-queued request if we tear down before the lock is ever granted.\n let releaseLock: (() => void) | undefined;\n let cancelLock: (() => void) | undefined;\n\n const doPersist = (): void => {\n if (!opt.outbox || !started) return;\n const payload: PersistedOutbox = {\n origin: sync.origin,\n version: sync.watermark()[sync.origin] ?? 0,\n envs: [...unacked.values()],\n };\n void Promise.resolve(opt.outbox.store.set(opt.outbox.key, payload));\n };\n // coalesce outbox writes; `immediate` forces a synchronous-path write (first boot, teardown)\n const persistOutbox = (immediate = false): void => {\n if (!opt.outbox) return;\n if (immediate) {\n if (persistTimer !== undefined) clearTimeout(persistTimer);\n persistTimer = undefined;\n doPersist();\n return;\n }\n if (persistTimer !== undefined) return;\n persistTimer = setTimeout(() => {\n persistTimer = undefined;\n doPersist();\n }, opt.outbox.debounceMs ?? OUTBOX_DEBOUNCE_MS);\n };\n\n const terminal = (state: 'ejected' | 'closed', reason?: string): void => {\n closed = true;\n cancelLock?.(); // drop a still-queued lock request so we never steal it after teardown\n releaseLock?.(); // free a held lock for the next waiting tab\n cancelLock = releaseLock = undefined;\n if (reconnectTimer !== undefined) clearTimeout(reconnectTimer);\n for (const unsub of unsubs.splice(0)) unsub();\n unsubLocal();\n persistOutbox(true); // save the still-unacked tail for the next boot before dropping it\n unacked.clear();\n transport?.close();\n transport = null;\n peerMap.set(new Map());\n lastReason.set(reason);\n status.set(state);\n if (reason !== undefined) opt.onEject?.(reason);\n };\n\n const sendEnv = (env: OpEnvelope): void => {\n transport?.send({ t: 'env', room: opt.room, env });\n };\n\n const flushUnacked = (): void => {\n // per origin, versions must go out in order; across origins the order is irrelevant\n for (const env of [...unacked.values()].sort((a, b) =>\n a.origin === b.origin ? a.version - b.version : a.origin < b.origin ? -1 : 1,\n )) {\n sendEnv(env);\n }\n };\n\n const handle = (msg: ServerMsg): void => {\n if (msg.room !== opt.room) return;\n switch (msg.t) {\n case 'welcome': {\n if (instance !== undefined && msg.instance !== instance) lastSeq = 0;\n instance = msg.instance;\n peerMap.set(new Map(msg.peers.map((p) => [p.origin, p])));\n if (msg.mode === 'delta') {\n for (const env of msg.envs) applyRemote(env);\n } else if (msg.mode === 'snapshot') {\n // The room ships register state, not a value: fold it locally (this client's own\n // policies) to derive the root, then hydrate root + registers together. Each own\n // origin's watermark stays at its acked high-water so unacked local writes rebase on top.\n const conv = createConvergingApply({ policies: opt.policies });\n conv.load(msg.registers);\n const ownWm: Record<string, number> = {};\n for (const o of ownOrigins) ownWm[o] = acked.get(o) ?? 0;\n // rebase from the durable outbox, not opSync's bounded recent-local ring, so a long\n // offline burst larger than that ring is never dropped from the rebase on a snapshot join\n sync.hydrate(\n {\n root: conv.materialize() as T,\n registers: msg.registers,\n wm: { ...msg.wm, ...ownWm },\n },\n [...unacked.values()],\n );\n lastSeq = msg.seq;\n } else if (msg.seq === 0 && lastSeq === 0) {\n sync.seed();\n }\n lastSeq = Math.max(lastSeq, msg.seq);\n attempts = 0;\n lastSyncedAt.set(Date.now());\n status.set('live');\n flushUnacked();\n if (hasPresence)\n transport?.send({\n t: 'presence',\n room: opt.room,\n data: presenceData,\n });\n return;\n }\n case 'env':\n applyRemote(msg.env);\n return;\n case 'presence': {\n const next = new Map(peerMap());\n if (msg.gone) next.delete(msg.peer.origin);\n else next.set(msg.peer.origin, msg.peer);\n peerMap.set(next);\n return;\n }\n case 'eject':\n if (msg.writer === opt.writer) terminal('ejected', msg.reason);\n return;\n case 'reject':\n terminal('ejected', msg.reason);\n return;\n case 'frontier':\n // the relay compacted past this stamp: reclaim our own settled register state to match,\n // so a long-lived connection stays bounded by live data rather than history\n if (started) sync.prune(msg.frontier);\n return;\n }\n };\n\n const applyRemote = (env: SeqEnvelope): void => {\n lastSeq = Math.max(lastSeq, env.seq);\n lastSyncedAt.set(Date.now());\n // a migration to a shape newer than ours: stop applying + surface 'outdated' (never downgrade-interpret newer data)\n if (\n opt.schemaVersion !== undefined &&\n env.schemaVersion !== undefined &&\n env.schemaVersion > opt.schemaVersion\n ) {\n terminal('ejected', 'schema');\n return;\n }\n if (ownOrigins.has(env.origin)) {\n unacked.delete(unackedKey(env));\n acked.set(env.origin, Math.max(acked.get(env.origin) ?? 0, env.version));\n persistOutbox();\n return;\n }\n sync.receive(env);\n };\n\n const connect = (): void => {\n if (closed) return;\n for (const unsub of unsubs.splice(0)) unsub();\n const t = opt.transport();\n transport = t;\n unsubs = [\n t.onMessage(handle),\n t.onClose(() => {\n if (closed || transport !== t) return;\n transport = null;\n status.set('reconnecting');\n const delay = Math.min(\n opt.reconnect?.maxDelayMs ?? 15_000,\n RECONNECT_BASE_MS * 2 ** attempts++,\n );\n reconnectTimer = setTimeout(\n () => {\n reconnectTimer = undefined;\n connect();\n },\n delay + Math.random() * 100,\n );\n }),\n ];\n t.send({\n t: 'hello',\n room: opt.room,\n origin: sync.origin,\n proto: MESH_PROTO_VERSION,\n policyVersion,\n seq: lastSeq > 0 ? lastSeq : undefined,\n schemaVersion: opt.schemaVersion,\n });\n };\n\n const wireLocal = (): void => {\n unsubLocal = sync.subscribe((env) => {\n const violation = checkEnvelope(opt.policy, env, {\n ...opt.ctx,\n writer: opt.writer,\n });\n if (violation) {\n if (isDevMode()) {\n console.warn(\n '[@mmstack/mesh] local write violates the room policy — not sent',\n violation,\n );\n }\n return;\n }\n ownOrigins.add(env.origin); // the fresh mint, or a restored-tail origin resending verbatim\n unacked.set(unackedKey(env), env);\n persistOutbox();\n if (status() === 'live') sendEnv(env);\n });\n };\n\n // A persisted envelope from a previous wire-protocol version lacks cites/epoch; there is no\n // sound way to mint citations after the fact, so such writes are dropped loudly, never adapted.\n const restorable = (env: OpEnvelope): boolean =>\n env.proto === OP_PROTO_VERSION &&\n env.ops.every(\n (op) =>\n Array.isArray((op as SyncOp).cites) &&\n typeof (op as SyncOp).epoch === 'number',\n );\n\n const initCore = (restore?: PersistedOutbox): void => {\n if (closed) return;\n // Mint a FRESH origin every boot: a dot is (origin, hlc), so a never-before-used origin cannot\n // collide with anything a prior boot (or a byte clone of this outbox) emitted. The persisted\n // origin is NOT reused for minting; it survives only as metadata on the restored tail, which\n // resends verbatim under it. Per-boot origin growth is bounded (one per boot) and dead origins'\n // register watermarks compact away below the prune frontier.\n sync = opSync(source, {\n writer: opt.writer,\n policies: opt.policies,\n policyVersion,\n injector,\n onReject: (_env, reason) => {\n droppedInvalid.update((n) => n + 1);\n if (isDevMode()) {\n console.warn(`[@mmstack/mesh] dropped malformed envelope from a peer (${reason})`);\n }\n },\n });\n ownOrigins.add(sync.origin);\n started = true;\n wireLocal();\n if (restore && (restore.envs.length > 0 || restore.version > 0)) {\n const kept = restore.envs.filter(restorable);\n const dropped = restore.envs.length - kept.length;\n if (dropped > 0) {\n droppedOffline.set(dropped);\n if (isDevMode()) {\n console.warn(\n `[@mmstack/mesh] dropped ${dropped} persisted offline write(s) from an older protocol version: ops without cites/epoch cannot be merged soundly, and citations are never fabricated`,\n );\n }\n }\n // the tail resends verbatim under its recorded origin; new writes mint on the fresh origin,\n // so a version acked + dropped before the reboot can never be re-minted (the origin differs)\n sync.restore(kept, restore.version); // → subscribe repopulates `unacked` for resend\n }\n persistOutbox(true); // pin the freshly minted origin immediately, so a crash before any write is safe\n connect();\n };\n\n if (opt.register) {\n const connection: ResourceLike = {\n status: computed(() => {\n const s = status();\n return s === 'connecting'\n ? 'loading'\n : s === 'reconnecting'\n ? 'reloading'\n : s === 'ejected'\n ? 'error'\n : 'resolved';\n }),\n isLoading: computed(\n () => status() === 'connecting' || status() === 'reconnecting',\n ),\n hasValue: () => true,\n };\n runInInjectionContext(injector, () =>\n registerResource(connection, { suspends: opt.register === 'suspend' }),\n );\n }\n\n injector.get(DestroyRef).onDestroy(() => {\n if (!closed) terminal('closed');\n unsubLocal();\n if (started) sync.destroy();\n });\n\n // Load the persisted outbox, then boot with the adopted origin. A fresh/unreadable slot boots clean.\n const bootFromDisk = async (): Promise<void> => {\n if (closed || !opt.outbox) return;\n let saved: PersistedOutbox | undefined;\n try {\n const raw = await opt.outbox.store.get(opt.outbox.key);\n saved =\n raw && typeof raw === 'object' && 'origin' in raw\n ? (raw as PersistedOutbox)\n : undefined;\n } catch {\n saved = undefined; // an unreadable slot must not wedge the boot\n }\n initCore(saved);\n };\n\n const beginConnect = (): void => {\n if (closed) return;\n if (!opt.outbox) {\n initCore();\n } else if ((opt.outbox.crossTab ?? 'queue') === 'off') {\n void bootFromDisk(); // no single-writer lock — the app coordinates ownership\n } else {\n // crossTab:'queue' — hold an exclusive Web Lock on the key for this tab's lifetime, so a\n // second tab sharing the key WAITS (stays 'connecting') instead of restoring the same origin\n // and colliding on version mints. The lock auto-releases if the tab crashes.\n const locks = globalThis.navigator?.locks;\n if (!locks) {\n if (isDevMode()) {\n console.warn(\n '[@mmstack/mesh] outbox crossTab:\"queue\" needs the Web Locks API (navigator.locks), unavailable here — running WITHOUT a single-writer lock. Two tabs sharing this key can diverge; coordinate ownership yourself, or set crossTab:\"off\" to silence this.',\n );\n }\n void bootFromDisk();\n } else {\n const abort = new AbortController();\n cancelLock = () => abort.abort();\n void locks\n .request(\n `@mmstack/mesh:outbox:${opt.outbox.key}`,\n { mode: 'exclusive', signal: abort.signal },\n () => {\n cancelLock = undefined; // granted — no longer abortable, only releasable\n if (closed) return Promise.resolve();\n // hold the lock until teardown resolves this promise\n return new Promise<void>((release) => {\n releaseLock = release;\n void bootFromDisk();\n });\n },\n )\n .catch((e: unknown) => {\n // an aborted request is our own teardown; any other failure → degrade to no-lock\n if (!closed && (e as { name?: string })?.name !== 'AbortError') {\n void bootFromDisk();\n }\n });\n }\n }\n };\n\n // Assemble the local base first (worker hydration, disk snapshot), THEN connect, so the room\n // welcome rebases pending onto a populated store instead of racing it. A rejection is treated as\n // ready so a failed base load never wedges the connection.\n if (opt.whenReady) {\n void Promise.resolve()\n .then(() => opt.whenReady?.())\n .then(\n () => beginConnect(),\n () => beginConnect(),\n );\n } else {\n beginConnect();\n }\n\n return {\n status: status.asReadonly(),\n health,\n peers,\n setPresence: (data) => {\n presenceData = data;\n hasPresence = true;\n if (status() === 'live') {\n transport?.send({ t: 'presence', room: opt.room, data });\n }\n },\n fork: (forkOpt) => {\n const f = forkStore(\n source as unknown as WritableSignalStore<T & Record<string, any>>,\n forkOpt,\n );\n // a fork created before the sync connected observed nothing, so an empty frontier is correct\n // (its commit cites nothing and lands as concurrent); once connected, capture the live one\n let frontier: DotFrontier = started ? sync.captureFrontier() : { seq: 0 };\n const recapture = (): void => {\n frontier = started ? sync.captureFrontier() : { seq: 0 };\n };\n return {\n store: f.store,\n ops: f.ops,\n commit: () =>\n started ? sync.commitScope(frontier, () => f.commit()) : f.commit(),\n discard: () => {\n f.discard();\n recapture();\n },\n rebase: recapture,\n };\n },\n close: () => {\n if (!closed) terminal('closed');\n unsubLocal();\n if (started) sync.destroy();\n },\n };\n}\n","import { isDevMode } from '@angular/core';\nimport type {\n ClientMsg,\n PrincipalCtx,\n Relay,\n ServerMsg,\n} from '@mmstack/mesh-protocol';\n\n/**\n * One live connection to a relay. `meshSync` calls the factory per (re)connect; a transport\n * represents a single connection and never reconnects itself.\n */\nexport type MeshTransport = {\n send(msg: ClientMsg): void;\n onMessage(cb: (msg: ServerMsg) => void): () => void;\n onClose(cb: () => void): () => void;\n close(): void;\n};\n\nexport type MeshTransportFactory = () => MeshTransport;\n\n/**\n * JSON-over-WebSocket transport. Messages sent before the socket opens are buffered and\n * flushed on open; malformed inbound frames are dropped.\n */\nexport function webSocketTransport(\n url: string,\n protocols?: string | string[],\n): MeshTransportFactory {\n return () => {\n const ws = new WebSocket(url, protocols);\n const messageCbs = new Set<(msg: ServerMsg) => void>();\n const closeCbs = new Set<() => void>();\n const pending: string[] = [];\n\n ws.onopen = () => {\n for (const frame of pending.splice(0)) ws.send(frame);\n };\n ws.onmessage = (event) => {\n let msg: ServerMsg;\n try {\n msg = JSON.parse(String(event.data)) as ServerMsg;\n } catch {\n if (isDevMode()) {\n console.warn('[@mmstack/mesh] dropped unparseable frame (expected JSON text)');\n }\n return;\n }\n for (const cb of [...messageCbs]) cb(msg);\n };\n ws.onclose = () => {\n for (const cb of [...closeCbs]) cb();\n };\n\n return {\n send: (msg) => {\n const frame = JSON.stringify(msg);\n if (ws.readyState === WebSocket.OPEN) ws.send(frame);\n else if (ws.readyState === WebSocket.CONNECTING) pending.push(frame);\n },\n onMessage: (cb) => {\n messageCbs.add(cb);\n return () => messageCbs.delete(cb);\n },\n onClose: (cb) => {\n closeCbs.add(cb);\n return () => closeCbs.delete(cb);\n },\n close: () => ws.close(),\n };\n };\n}\n\n/**\n * Connects directly to an in-process `createRelay` — no network. The backbone of full-loop\n * tests, and handy for single-process demos or an SSR-side room.\n */\nexport function directTransport(\n relay: Relay,\n ctx: PrincipalCtx,\n): MeshTransportFactory {\n return () => {\n const messageCbs = new Set<(msg: ServerMsg) => void>();\n const closeCbs = new Set<() => void>();\n let open = true;\n\n const connection = relay.connect(\n {\n send: (msg) => {\n if (!open) return;\n for (const cb of [...messageCbs]) cb(msg);\n },\n close: () => {\n if (!open) return;\n open = false;\n connection.disconnect();\n for (const cb of [...closeCbs]) cb();\n },\n },\n ctx,\n );\n\n return {\n send: (msg) => {\n if (open) connection.receive(msg);\n },\n onMessage: (cb) => {\n messageCbs.add(cb);\n return () => messageCbs.delete(cb);\n },\n onClose: (cb) => {\n closeCbs.add(cb);\n return () => closeCbs.delete(cb);\n },\n close: () => {\n if (!open) return;\n open = false;\n connection.disconnect();\n for (const cb of [...closeCbs]) cb();\n },\n };\n };\n}\n","import {\n computed,\n DestroyRef,\n inject,\n Injector,\n signal,\n type Signal,\n type WritableSignal,\n} from '@angular/core';\nimport { MESH_PROTO_VERSION, type ServerMsg } from '@mmstack/mesh-protocol';\nimport {\n opSync,\n type MergePolicyEntry,\n type OpEnvelope,\n type OpSyncCheckpoint,\n} from '@mmstack/primitives';\nimport type { MeshTransport, MeshTransportFactory } from './transport';\n\n/** A data channel as the P2P engine needs it; opens later, buffers nothing itself. */\nexport type DataChannelLike = {\n send(frame: string): void;\n onMessage(cb: (frame: string) => void): () => void;\n onOpen(cb: () => void): () => void;\n onClose(cb: () => void): () => void;\n close(): void;\n};\n\n/**\n * Creates one peer link. `polite` assigns the perfect-negotiation role (derived\n * deterministically from origin ordering); `sendSignal` routes offer/answer/ICE payloads\n * through the relay; `signal` delivers the remote side's payloads.\n */\nexport type PeerConnector = (opt: {\n readonly remote: string;\n readonly polite: boolean;\n readonly sendSignal: (data: unknown) => void;\n}) => {\n readonly channel: DataChannelLike;\n signal(data: unknown): void;\n close(): void;\n};\n\n/** WebRTC `PeerConnector` implementing the perfect-negotiation pattern. Browser-only. */\nexport function rtcPeerConnector(config?: RTCConfiguration): PeerConnector {\n return ({ polite, sendSignal }) => {\n const pc = new RTCPeerConnection(config);\n const messageCbs = new Set<(frame: string) => void>();\n const openCbs = new Set<() => void>();\n const closeCbs = new Set<() => void>();\n const pending: string[] = [];\n let dc: RTCDataChannel | null = null;\n let makingOffer = false;\n let ignoreOffer = false;\n\n const attach = (channel: RTCDataChannel): void => {\n dc = channel;\n channel.onmessage = (e) => {\n for (const cb of [...messageCbs]) cb(String(e.data));\n };\n channel.onopen = () => {\n for (const frame of pending.splice(0)) channel.send(frame);\n for (const cb of [...openCbs]) cb();\n };\n channel.onclose = () => {\n for (const cb of [...closeCbs]) cb();\n };\n };\n\n if (!polite) attach(pc.createDataChannel('mmstack-mesh'));\n else pc.ondatachannel = (e) => attach(e.channel);\n\n pc.onicecandidate = (e) => {\n if (e.candidate) sendSignal({ ice: e.candidate.toJSON() });\n };\n pc.onnegotiationneeded = async () => {\n try {\n makingOffer = true;\n await pc.setLocalDescription();\n sendSignal({ description: pc.localDescription });\n } finally {\n makingOffer = false;\n }\n };\n\n return {\n channel: {\n send: (frame) => {\n if (dc && dc.readyState === 'open') dc.send(frame);\n else pending.push(frame);\n },\n onMessage: (cb) => (messageCbs.add(cb), () => messageCbs.delete(cb)),\n onOpen: (cb) => (openCbs.add(cb), () => openCbs.delete(cb)),\n onClose: (cb) => (closeCbs.add(cb), () => closeCbs.delete(cb)),\n close: () => dc?.close(),\n },\n signal: async (data) => {\n const { description, ice } = (data ?? {}) as {\n description?: RTCSessionDescriptionInit;\n ice?: RTCIceCandidateInit;\n };\n if (description) {\n const collision =\n description.type === 'offer' &&\n (makingOffer || pc.signalingState !== 'stable');\n ignoreOffer = !polite && collision;\n if (ignoreOffer) return;\n await pc.setRemoteDescription(description);\n if (description.type === 'offer') {\n await pc.setLocalDescription();\n sendSignal({ description: pc.localDescription });\n }\n } else if (ice) {\n try {\n await pc.addIceCandidate(ice);\n } catch (err) {\n if (!ignoreOffer) throw err;\n }\n }\n },\n close: () => {\n dc?.close();\n pc.close();\n },\n };\n };\n}\n\ntype P2PMsg =\n | { t: 'hello'; wm: Record<string, number> }\n // the full checkpoint (root + register state + watermark), NOT a bare value: the covered\n // side must inherit supersession state or already-superseded stragglers would resurrect\n | { t: 'state'; state: OpSyncCheckpoint<object> }\n | { t: 'uptodate' }\n | { t: 'env'; env: OpEnvelope };\n\nexport type WebRtcMeshOptions = {\n readonly room: string;\n readonly writer: string;\n /** Signaling path to the relay (ws or direct) — data flows peer-to-peer. */\n readonly signaling: MeshTransportFactory;\n /** Peer link factory; defaults to {@link rtcPeerConnector}. Injectable for tests. */\n readonly connector?: PeerConnector;\n readonly policies?: readonly MergePolicyEntry[];\n readonly policyVersion?: number;\n readonly injector?: Injector;\n};\n\nexport type WebRtcMeshRef = {\n readonly status: Signal<'connecting' | 'live'>;\n /** Origins with an OPEN data channel. */\n readonly peers: Signal<readonly string[]>;\n close(): void;\n};\n\ntype Peer = {\n link: ReturnType<PeerConnector>;\n open: boolean;\n unsubs: (() => void)[];\n};\n\n/**\n * Peer-to-peer mesh sync: the relay only signals\n * and tracks membership; envelopes flow over WebRTC data channels and converge via the\n * per-path register map. Catch-up is pairwise: on channel open both sides exchange\n * watermarks; a side whose state is strictly covered hydrates from the other's FULL\n * checkpoint (root + per-path register state + watermark), so supersession and precedence\n * carry over intact: a late joiner is indistinguishable from a peer that saw every\n * envelope. Two peers that each hold envelopes the other lacks keep their convergent\n * go-forward guarantees but do not exchange the missed envelopes retroactively.\n */\nexport function webRtcMesh<T extends object>(\n source: WritableSignal<T>,\n opt: WebRtcMeshOptions,\n): WebRtcMeshRef {\n const injector = opt.injector ?? inject(Injector);\n const connector = opt.connector ?? rtcPeerConnector();\n const status = signal<'connecting' | 'live'>('connecting');\n const openPeers = signal<ReadonlySet<string>>(new Set());\n const peers = new Map<string, Peer>();\n let signalingUnsubs: (() => void)[] = [];\n let transport: MeshTransport | null = null;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n let closed = false;\n\n const sync = opSync(source, {\n writer: opt.writer,\n policies: opt.policies,\n policyVersion: opt.policyVersion,\n injector,\n });\n\n const covered = (\n mine: Record<string, number>,\n theirs: Record<string, number>,\n ): boolean => Object.entries(mine).every(([o, v]) => (theirs[o] ?? 0) >= v);\n\n const sendTo = (peer: Peer, msg: P2PMsg): void => {\n peer.link.channel.send(JSON.stringify(msg));\n };\n\n const broadcast = (msg: P2PMsg): void => {\n for (const peer of peers.values()) {\n if (peer.open) sendTo(peer, msg);\n }\n };\n\n const dropPeer = (origin: string): void => {\n const peer = peers.get(origin);\n if (!peer) return;\n peers.delete(origin);\n for (const unsub of peer.unsubs) unsub();\n peer.link.close();\n openPeers.update((set) => {\n const next = new Set(set);\n next.delete(origin);\n return next;\n });\n };\n\n const ensurePeer = (origin: string): Peer => {\n let peer = peers.get(origin);\n if (peer) return peer;\n const link = connector({\n remote: origin,\n polite: sync.origin > origin,\n sendSignal: (data) =>\n transport?.send({ t: 'signal', room: opt.room, to: origin, data }),\n });\n peer = { link, open: false, unsubs: [] };\n peers.set(origin, peer);\n peer.unsubs.push(\n link.channel.onOpen(() => {\n peer!.open = true;\n openPeers.update((set) => new Set(set).add(origin));\n sendTo(peer!, { t: 'hello', wm: sync.watermark() });\n }),\n link.channel.onMessage((frame) => {\n let msg: P2PMsg;\n try {\n msg = JSON.parse(frame) as P2PMsg;\n } catch {\n return;\n }\n handlePeerMsg(peer!, msg);\n }),\n link.channel.onClose(() => dropPeer(origin)),\n );\n return peer;\n };\n\n const handlePeerMsg = (peer: Peer, msg: P2PMsg): void => {\n switch (msg.t) {\n case 'hello': {\n const snap = sync.snapshot();\n if (covered(snap.wm, msg.wm)) sendTo(peer, { t: 'uptodate' });\n else sendTo(peer, { t: 'state', state: snap });\n return;\n }\n case 'state': {\n if (covered(sync.watermark(), msg.state.wm)) {\n sync.hydrate(msg.state as OpSyncCheckpoint<T>);\n }\n return;\n }\n case 'uptodate':\n return;\n case 'env':\n sync.receive(msg.env);\n return;\n }\n };\n\n const handleSignaling = (msg: ServerMsg): void => {\n if (msg.room !== opt.room) return;\n switch (msg.t) {\n case 'welcome':\n for (const origin of msg.members) ensurePeer(origin);\n status.set('live');\n return;\n case 'member':\n if (msg.gone) dropPeer(msg.origin);\n else ensurePeer(msg.origin);\n return;\n case 'signal':\n ensurePeer(msg.from).link.signal(msg.data);\n return;\n case 'reject':\n close();\n return;\n }\n };\n\n const connectSignaling = (): void => {\n if (closed) return;\n for (const unsub of signalingUnsubs.splice(0)) unsub();\n const t = opt.signaling();\n transport = t;\n signalingUnsubs = [\n t.onMessage(handleSignaling),\n t.onClose(() => {\n if (closed || transport !== t) return;\n transport = null;\n reconnectTimer = setTimeout(connectSignaling, 1000);\n }),\n ];\n t.send({\n t: 'hello',\n room: opt.room,\n origin: sync.origin,\n proto: MESH_PROTO_VERSION,\n policyVersion: opt.policyVersion ?? 0,\n });\n };\n\n const unsubLocal = sync.subscribe((env) => broadcast({ t: 'env', env }));\n\n const close = (): void => {\n if (closed) return;\n closed = true;\n if (reconnectTimer !== undefined) clearTimeout(reconnectTimer);\n unsubLocal();\n for (const origin of [...peers.keys()]) dropPeer(origin);\n for (const unsub of signalingUnsubs.splice(0)) unsub();\n transport?.close();\n transport = null;\n sync.destroy();\n status.set('connecting');\n };\n\n injector.get(DestroyRef).onDestroy(close);\n connectSignaling();\n\n return {\n status: status.asReadonly(),\n peers: computed(() => [...openPeers()]),\n close,\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/mesh",
3
- "version": "22.0.0",
3
+ "version": "22.2.0",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "signals",
@@ -20,8 +20,8 @@
20
20
  "homepage": "https://github.com/mihajm/mmstack/blob/master/packages/mesh/client",
21
21
  "peerDependencies": {
22
22
  "@angular/core": ">=22 <23",
23
- "@mmstack/primitives": ">=22.6 <23",
24
- "@mmstack/mesh-protocol": ">=0.1.0 <1"
23
+ "@mmstack/primitives": ">=22.8 <23",
24
+ "@mmstack/mesh-protocol": ">=0.3 <1"
25
25
  },
26
26
  "sideEffects": false,
27
27
  "module": "fesm2022/mmstack-mesh.mjs",
@@ -1,6 +1,6 @@
1
1
  import { Injector, Signal, WritableSignal } from '@angular/core';
2
2
  import { ClientMsg, ServerMsg, Relay, PrincipalCtx, PresenceState, OpPolicy } from '@mmstack/mesh-protocol';
3
- import { MergePolicyEntry } from '@mmstack/primitives';
3
+ import { MergePolicyEntry, AsyncStore, ForkStoreOptions, SyncedFork } from '@mmstack/primitives';
4
4
 
5
5
  /**
6
6
  * One live connection to a relay. `meshSync` calls the factory per (re)connect; a transport
@@ -26,9 +26,34 @@ declare function directTransport(relay: Relay, ctx: PrincipalCtx): MeshTransport
26
26
 
27
27
  type MeshStatus = 'connecting' | 'live' | 'reconnecting' | 'ejected' | 'closed';
28
28
  type MeshPeer = PresenceState;
29
+ /**
30
+ * A composed, user-facing health status for a synced store the surface that
31
+ * turns a versioned reject from a dead socket into a speakable 'outdated' banner.
32
+ */
33
+ type SyncHealthStatus = 'live' | 'offline' | 'outdated' | 'ejected' | 'degraded';
34
+ type SyncHealth = {
35
+ readonly status: SyncHealthStatus;
36
+ /** Why, when the status is `outdated`/`ejected`/`degraded`. */
37
+ readonly reason?: 'proto' | 'policy-version' | 'schema' | 'quota' | 'worker' | (string & {});
38
+ /** `Date.now()` of the last successful sync (welcome or applied env). */
39
+ readonly lastSyncedAt?: number;
40
+ /**
41
+ * Offline writes persisted by an older build that could not be restored on boot. Envelopes
42
+ * from a previous wire-protocol version lack the citation metadata current merging needs,
43
+ * so they are dropped (loudly) instead of being upgraded with fabricated citations.
44
+ */
45
+ readonly droppedOfflineWrites?: number;
46
+ /**
47
+ * Received envelopes rejected as malformed by the deterministic well-formedness check (a bad id
48
+ * or path segment, a non-integer version, an unknown op kind, a negative epoch, forged cites, a
49
+ * root delete). Direct peer-to-peer rooms are trust-full for authority, so this is a peer's line
50
+ * against a malformed neighbor; through a relay these are also rejected at the relay.
51
+ */
52
+ readonly droppedInvalidEnvelopes?: number;
53
+ };
29
54
  type MeshSyncOptions = {
30
55
  readonly room: string;
31
- /** Opaque principal pseudonym — provided, never minted (op-protocol RFC §3). */
56
+ /** Opaque principal pseudonym — provided, never minted. */
32
57
  readonly writer: string;
33
58
  readonly transport: MeshTransportFactory;
34
59
  /** Per-path merge policies for rebase/convergence (`lww` default). */
@@ -37,7 +62,17 @@ type MeshSyncOptions = {
37
62
  readonly policy?: OpPolicy;
38
63
  /** kind/claims of this principal, so a shared policy evaluates identically on both sides. */
39
64
  readonly ctx?: Omit<PrincipalCtx, 'writer'>;
65
+ /**
66
+ * The room's shared configuration pin, checked at hello against the relay's. Bump it
67
+ * together whenever anything peers must agree on changes: the write policy, and just as
68
+ * importantly the merge/fold configuration (`policies`). Peers folding the same register
69
+ * state with different rules read different values, so one room means one fold config; a
70
+ * client pinned to an older version is rejected `policy-version` and surfaces `outdated`.
71
+ */
40
72
  readonly policyVersion?: number;
73
+ /** The data shape this client speaks. Older-than-room → `outdated`, and a
74
+ * newer-schema migration envelope arriving mid-session flips this client to `outdated` too. */
75
+ readonly schemaVersion?: number;
41
76
  /** Exponential backoff cap for reconnects (default 15s; base 500ms + jitter). */
42
77
  readonly reconnect?: {
43
78
  readonly maxDelayMs?: number;
@@ -46,12 +81,55 @@ type MeshSyncOptions = {
46
81
  /** Register with the nearest transition scope so (re)connection surfaces as `pending`. */
47
82
  readonly register?: 'track' | 'suspend';
48
83
  readonly onEject?: (reason: string) => void;
84
+ /**
85
+ * Hold the connection until the local base is assembled. `meshSync` awaits this before it connects
86
+ * (and before it restores an `outbox`), so a store hydrated from another source first (a worker
87
+ * graph, a disk snapshot) is in place when the room welcome arrives and rebases pending on top.
88
+ * The status stays `connecting` while it is pending. A rejection is treated as ready, so a base
89
+ * that fails to load never wedges the connection.
90
+ */
91
+ readonly whenReady?: () => PromiseLike<void> | void;
92
+ /**
93
+ * Persist the unacknowledged local outbox (and this client's stable origin) so offline writes
94
+ * survive a REBOOT, not just a live reconnect: on boot they are restored and rebased onto the room
95
+ * on the next welcome, instead of being lost with in-memory state. The payload is written to
96
+ * `store` under `key`, debounced.
97
+ *
98
+ * By default (`crossTab: 'queue'`) a Web Lock on `key` makes this a single-writer-per-key resource:
99
+ * a second tab sharing the key WAITS (stays `connecting`, surfaced via `status`/`health`) until the
100
+ * first releases, rather than restoring the same origin and colliding on version mints. Set
101
+ * `crossTab: 'off'` to skip the lock and coordinate ownership yourself (e.g. a per-tab key, or
102
+ * leader election over `tabSync`). A debounced write means a hard crash within the debounce window
103
+ * can drop the very last mint — a small, best-effort gap; tune it with `debounceMs`.
104
+ */
105
+ readonly outbox?: {
106
+ readonly key: string;
107
+ readonly store: AsyncStore;
108
+ /** Coalesce outbox writes by this many ms (default 300; `0` = write on every change). */
109
+ readonly debounceMs?: number;
110
+ /**
111
+ * Cross-tab contention for the shared `key`. `'queue'` (default) holds a Web Lock so only one tab
112
+ * owns the durable outbox at a time; others wait. `'off'` skips the lock (you coordinate).
113
+ * (A future `'ephemeral'` — non-leaders run live with a throwaway origin — is planned.)
114
+ */
115
+ readonly crossTab?: 'queue' | 'off';
116
+ };
49
117
  };
50
- type MeshSyncRef = {
118
+ type MeshSyncRef<T extends object = Record<string, unknown>> = {
51
119
  readonly status: Signal<MeshStatus>;
120
+ /** Composed sync-health for a user-facing surface. */
121
+ readonly health: Signal<SyncHealth>;
52
122
  readonly peers: Signal<readonly MeshPeer[]>;
53
123
  /** Publish this client's ephemeral presence payload (cursor, section, activity…). */
54
124
  setPresence(data: unknown): void;
125
+ /**
126
+ * Fork the synced store for isolated, reviewable edits (an agent branch, a staged change). The
127
+ * fork observes the room as it is now; committing emits its diff citing only those observed
128
+ * writes, so an edit that lands on the room while the fork is open stays a concurrent value the
129
+ * merge policy decides rather than being overwritten by the commit. Call `rebase()` to re-observe
130
+ * the room and commit on top of the latest instead. `discard()` drops the staged edits.
131
+ */
132
+ fork(opt?: ForkStoreOptions<T & Record<string, any>>): SyncedFork<T & Record<string, any>>;
55
133
  close(): void;
56
134
  };
57
135
  /**
@@ -59,9 +137,9 @@ type MeshSyncRef = {
59
137
  * envelopes, remote envelopes fold in convergently, reconnects resume via delta or snapshot
60
138
  * with unacknowledged local writes rebased on top, and presence rides an ephemeral channel.
61
139
  * A synced store reads exactly like a local one — connection state surfaces only through
62
- * `status` and the transition scope (op-protocol RFC §10).
140
+ * `status` and the transition scope.
63
141
  */
64
- declare function meshSync<T extends object>(source: WritableSignal<T>, opt: MeshSyncOptions): MeshSyncRef;
142
+ declare function meshSync<T extends object>(source: WritableSignal<T>, opt: MeshSyncOptions): MeshSyncRef<T>;
65
143
 
66
144
  /** A data channel as the P2P engine needs it; opens later, buffers nothing itself. */
67
145
  type DataChannelLike = {
@@ -105,14 +183,16 @@ type WebRtcMeshRef = {
105
183
  close(): void;
106
184
  };
107
185
  /**
108
- * Peer-to-peer mesh sync (unsequenced topology, op-protocol RFC §4): the relay only signals
186
+ * Peer-to-peer mesh sync: the relay only signals
109
187
  * and tracks membership; envelopes flow over WebRTC data channels and converge via the
110
188
  * per-path register map. Catch-up is pairwise: on channel open both sides exchange
111
- * watermarks; a side whose state is strictly covered hydrates from the other. Two peers that
112
- * diverged while BOTH held state keep their convergent go-forward guarantees but do not
113
- * retroactively merge history (same contract as the tab rung).
189
+ * watermarks; a side whose state is strictly covered hydrates from the other's FULL
190
+ * checkpoint (root + per-path register state + watermark), so supersession and precedence
191
+ * carry over intact: a late joiner is indistinguishable from a peer that saw every
192
+ * envelope. Two peers that each hold envelopes the other lacks keep their convergent
193
+ * go-forward guarantees but do not exchange the missed envelopes retroactively.
114
194
  */
115
195
  declare function webRtcMesh<T extends object>(source: WritableSignal<T>, opt: WebRtcMeshOptions): WebRtcMeshRef;
116
196
 
117
197
  export { directTransport, meshSync, rtcPeerConnector, webRtcMesh, webSocketTransport };
118
- export type { DataChannelLike, MeshPeer, MeshStatus, MeshSyncOptions, MeshSyncRef, MeshTransport, MeshTransportFactory, PeerConnector, WebRtcMeshOptions, WebRtcMeshRef };
198
+ export type { DataChannelLike, MeshPeer, MeshStatus, MeshSyncOptions, MeshSyncRef, MeshTransport, MeshTransportFactory, PeerConnector, SyncHealth, SyncHealthStatus, WebRtcMeshOptions, WebRtcMeshRef };