@mmstack/mesh 21.1.0 → 21.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 +21 -20
- package/fesm2022/mmstack-mesh.mjs +0 -0
- package/fesm2022/mmstack-mesh.mjs.map +1 -1
- package/package.json +3 -3
- package/types/mmstack-mesh.d.ts +36 -6
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
|
|
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,9 @@ meshSync(board, {
|
|
|
63
63
|
});
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
`preserve` turns a clash into a `Conflicted` value holding
|
|
67
|
-
lost and a resolution is just a later write. See `@mmstack/primitives` for
|
|
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
69
|
|
|
70
70
|
The default is last-writer-wins on a leaf, which drops one side of a true clash on the same field.
|
|
71
71
|
For a field where losing a write matters, set `preserve` on its path and resolve the `Conflicted`
|
|
@@ -230,27 +230,28 @@ There are two ways to give it write access, for two levels of trust.
|
|
|
230
230
|
|
|
231
231
|
### Review a branch
|
|
232
232
|
|
|
233
|
-
|
|
234
|
-
until a person approves. `ops()` is the staged change as data, ready to render for
|
|
235
|
-
|
|
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
236
|
|
|
237
237
|
```ts
|
|
238
|
-
import { forkStore } from '@mmstack/primitives';
|
|
239
|
-
|
|
240
238
|
const board = store<Board>(initialBoard());
|
|
241
|
-
meshSync(board, { room: 'board-42', writer: userId, transport });
|
|
239
|
+
const mesh = meshSync(board, { room: 'board-42', writer: userId, transport });
|
|
242
240
|
|
|
243
|
-
const proposal =
|
|
244
|
-
runAgent(proposal.store);
|
|
241
|
+
const proposal = mesh.fork(); // the agent's isolated branch, off the room
|
|
242
|
+
runAgent(proposal.store); // it writes here
|
|
245
243
|
|
|
246
|
-
const changes = proposal.ops();
|
|
247
|
-
proposal.commit();
|
|
248
|
-
// proposal.
|
|
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
|
|
249
248
|
```
|
|
250
249
|
|
|
251
|
-
The
|
|
252
|
-
|
|
253
|
-
|
|
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.
|
|
254
255
|
|
|
255
256
|
### Write as a peer
|
|
256
257
|
|
|
@@ -299,7 +300,7 @@ meshSync(store, { room, writer, transport, schemaVersion: 2 });
|
|
|
299
300
|
```
|
|
300
301
|
|
|
301
302
|
A migration is an envelope: a privileged writer (run from your deploy) emits a root set carrying the
|
|
302
|
-
new `schemaVersion`. The relay bumps the room's schema and its
|
|
303
|
+
new `schemaVersion`. The relay bumps the room's schema and its instance nonce, so every watermark dies and
|
|
303
304
|
clients re-hydrate into the new shape. A client older than the room is rejected with reason
|
|
304
305
|
`schema`, and a client already connected when the migration lands stops applying and reports
|
|
305
306
|
`outdated`. Because the migration rides the log, a compacted snapshot and `relay.hydrate` are
|
|
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 AsyncStore,\n type MergePolicyEntry,\n type OpEnvelope,\n type OpSync,\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\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\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 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 = {\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 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 {\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 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 switch (status()) {\n case 'live':\n return { status: 'live', lastSyncedAt: at };\n case 'ejected': {\n const reason = lastReason();\n return reason && OUTDATED_REASONS.has(reason)\n ? { status: 'outdated', reason, lastSyncedAt: at }\n : { status: 'ejected', reason, lastSyncedAt: at };\n }\n default: // connecting / reconnecting / closed — not reachable\n return { status: 'offline', lastSyncedAt: at };\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 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 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 for (const env of [...unacked.values()].sort(\n (a, b) => a.version - b.version,\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 (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 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 }\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 (env.origin === sync.origin) {\n unacked.delete(env.version);\n highestAcked = Math.max(highestAcked, 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 unacked.set(env.version, env);\n persistOutbox();\n if (status() === 'live') sendEnv(env);\n });\n };\n\n const initCore = (restore?: PersistedOutbox): void => {\n if (closed) return;\n sync = opSync(source, {\n writer: opt.writer,\n policies: opt.policies,\n policyVersion,\n injector,\n origin: restore?.origin,\n });\n started = true;\n wireLocal();\n if (restore && (restore.envs.length > 0 || restore.version > 0)) {\n sync.restore(restore.envs, restore.version); // → subscribe repopulates `unacked` for resend\n }\n persistOutbox(true); // pin the (possibly freshly minted) origin so later boots reuse it\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 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} 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: 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": "21.
|
|
3
|
+
"version": "21.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": ">=21 <22",
|
|
23
|
-
"@mmstack/primitives": ">=21.
|
|
24
|
-
"@mmstack/mesh-protocol": ">=0.
|
|
23
|
+
"@mmstack/primitives": ">=21.8 <22",
|
|
24
|
+
"@mmstack/mesh-protocol": ">=0.3 <1"
|
|
25
25
|
},
|
|
26
26
|
"sideEffects": false,
|
|
27
27
|
"module": "fesm2022/mmstack-mesh.mjs",
|
package/types/mmstack-mesh.d.ts
CHANGED
|
@@ -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, AsyncStore } 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
|
|
@@ -37,6 +37,19 @@ type SyncHealth = {
|
|
|
37
37
|
readonly reason?: 'proto' | 'policy-version' | 'schema' | 'quota' | 'worker' | (string & {});
|
|
38
38
|
/** `Date.now()` of the last successful sync (welcome or applied env). */
|
|
39
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;
|
|
40
53
|
};
|
|
41
54
|
type MeshSyncOptions = {
|
|
42
55
|
readonly room: string;
|
|
@@ -49,6 +62,13 @@ type MeshSyncOptions = {
|
|
|
49
62
|
readonly policy?: OpPolicy;
|
|
50
63
|
/** kind/claims of this principal, so a shared policy evaluates identically on both sides. */
|
|
51
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
|
+
*/
|
|
52
72
|
readonly policyVersion?: number;
|
|
53
73
|
/** The data shape this client speaks. Older-than-room → `outdated`, and a
|
|
54
74
|
* newer-schema migration envelope arriving mid-session flips this client to `outdated` too. */
|
|
@@ -95,13 +115,21 @@ type MeshSyncOptions = {
|
|
|
95
115
|
readonly crossTab?: 'queue' | 'off';
|
|
96
116
|
};
|
|
97
117
|
};
|
|
98
|
-
type MeshSyncRef = {
|
|
118
|
+
type MeshSyncRef<T extends object = Record<string, unknown>> = {
|
|
99
119
|
readonly status: Signal<MeshStatus>;
|
|
100
120
|
/** Composed sync-health for a user-facing surface. */
|
|
101
121
|
readonly health: Signal<SyncHealth>;
|
|
102
122
|
readonly peers: Signal<readonly MeshPeer[]>;
|
|
103
123
|
/** Publish this client's ephemeral presence payload (cursor, section, activity…). */
|
|
104
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>>;
|
|
105
133
|
close(): void;
|
|
106
134
|
};
|
|
107
135
|
/**
|
|
@@ -111,7 +139,7 @@ type MeshSyncRef = {
|
|
|
111
139
|
* A synced store reads exactly like a local one — connection state surfaces only through
|
|
112
140
|
* `status` and the transition scope.
|
|
113
141
|
*/
|
|
114
|
-
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>;
|
|
115
143
|
|
|
116
144
|
/** A data channel as the P2P engine needs it; opens later, buffers nothing itself. */
|
|
117
145
|
type DataChannelLike = {
|
|
@@ -158,9 +186,11 @@ type WebRtcMeshRef = {
|
|
|
158
186
|
* Peer-to-peer mesh sync: the relay only signals
|
|
159
187
|
* and tracks membership; envelopes flow over WebRTC data channels and converge via the
|
|
160
188
|
* per-path register map. Catch-up is pairwise: on channel open both sides exchange
|
|
161
|
-
* watermarks; a side whose state is strictly covered hydrates from the other
|
|
162
|
-
*
|
|
163
|
-
*
|
|
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.
|
|
164
194
|
*/
|
|
165
195
|
declare function webRtcMesh<T extends object>(source: WritableSignal<T>, opt: WebRtcMeshOptions): WebRtcMeshRef;
|
|
166
196
|
|