@mocanvas/sync 4.0.2 → 4.1.1

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/ARCHITECTURE.md CHANGED
@@ -18,9 +18,15 @@ document model, tools, and UI live in TypeScript.
18
18
 
19
19
  ## Non-goals (v1)
20
20
 
21
- - Wire compatibility with tldraw's sync protocol.
22
- - Slot-compatible UI components.
21
+ - Wire compatibility with tldraw's sync protocol. `@mocanvas/sync` is a
22
+ transport of its own; it does not speak tldraw's wire format.
23
23
  - Pixel-identical rendering of every built-in shape.
24
+ - Shipping message catalogues. The translation seam exists
25
+ (`TLUiOverrides.translations`); the strings are the host's.
26
+
27
+ Slot-compatible UI components were a non-goal here and are no longer: the
28
+ `TLComponents` and `TLUiOverrides` surfaces are implemented. See
29
+ [MIGRATION.md §7](MIGRATION.md).
24
30
 
25
31
  ## Clean-room rule
26
32
 
package/MIGRATION.md CHANGED
@@ -10,7 +10,8 @@ The short version: your **documents, records, ids and `.tldr` files carry over
10
10
  untouched**, most of your **`Editor` calls carry over untouched**, your
11
11
  **`ShapeUtil` / `StateNode` / `BindingUtil` subclasses keep their shape**, and
12
12
  the real work is (a) imports, (b) teaching custom shapes about the GPU
13
- renderer, and (c) rebuilding UI, which has no slot compatibility yet.
13
+ renderer, and (c) UI, where the slots are compatible but the message
14
+ catalogues are yours to supply (§7).
14
15
 
15
16
  ---
16
17
 
@@ -237,6 +238,21 @@ the `export * from "@mocanvas/mocanvas"` at the top of the package.
237
238
  Everything `@mocanvas/editor` exports is re-exported by `@mocanvas/mocanvas`, so in app
238
239
  code you can import from `@mocanvas/mocanvas` alone.
239
240
 
241
+ ### Step 1b — the stylesheet
242
+
243
+ | Old import | New import |
244
+ | ------------------------ | --------------------------------- |
245
+ | `"tldraw/tldraw.css"` | `"@mocanvas/mocanvas/mocanvas.css"` |
246
+
247
+ Same arrangement, same one line, wherever you had it. The default UI is
248
+ unstyled without it.
249
+
250
+ `@mocanvas/compat` does not re-export the stylesheet — a package can only
251
+ export files it contains — so this import names `@mocanvas/mocanvas` even
252
+ during the zero-rename step. Under pnpm's strict `node_modules` that means
253
+ adding `@mocanvas/mocanvas` to your own dependencies alongside
254
+ `@mocanvas/compat`.
255
+
240
256
  ### Step 2 — drop the prefixes
241
257
 
242
258
  Once the app builds and runs against `@mocanvas/compat`, rename `TLFoo` →
@@ -499,26 +515,35 @@ returns both and `putContentOntoCurrentPage` accepts both.
499
515
 
500
516
  ## 7. UI
501
517
 
502
- **There is no slot compatibility yet**, and it is an explicit v1 non-goal. Do
503
- not expect your existing overrides of tldraw's UI components to compile.
518
+ **Slot compatibility exists.** This section used to say it did not and that it
519
+ was a v1 non-goal; both stopped being true before 4.0. Your existing overrides
520
+ of tldraw's UI components are the shape mocanvas expects.
504
521
 
505
522
  What you have:
506
523
 
507
- - `hideUi` on `<Mocanvas />` turns off the default toolbar and zoom bar
508
- entirely, leaving you the canvas and your `children`.
509
- - `components` (passed through to `<Canvas>`) lets you replace the canvas-level
510
- render slots.
524
+ - `components` the `TLComponents` map, at both the canvas level and the
525
+ chrome level. Pass a component to replace a slot, `null` to remove it.
526
+ - `overrides` `TLUiOverrides`, with `tools`, `actions` and `translations`.
527
+ The tool and action lists are what the toolbar, the menus and the keyboard
528
+ bindings are all built from, so rewriting an entry changes all three
529
+ together rather than only what is drawn.
530
+ - The default chrome as named exports — `TldrawUi`, `DefaultUi`,
531
+ `DefaultToolbar`, `DefaultMainMenu` and the rest — so you can render one
532
+ piece of it inside chrome of your own.
533
+ - `useTools()` and `useActions()` return those lists after overrides, which is
534
+ what a toolbar of your own should render from.
535
+ - `hideUi` on `<Mocanvas />` still turns the chrome off entirely, leaving you
536
+ the canvas and your `children`.
511
537
  - `useEditor()` inside any descendant of `<Canvas>` / `<EditorProvider>`
512
538
  returns the `Editor` (`useMaybeEditor()` returns `Editor | null`).
513
539
  - `track(Component)` and `useValue` from `@mocanvas/state/react` — re-exported
514
- from `@mocanvas/editor` and `@mocanvas/mocanvas` — make a component re-render when the
515
- signals it reads change.
540
+ from `@mocanvas/editor` and `@mocanvas/mocanvas` — make a component re-render
541
+ when the signals it reads change.
516
542
 
517
- The practical migration is: `hideUi`, then rebuild your chrome as ordinary
518
- React inside `<Mocanvas>`, reading and driving the editor through `useEditor`
519
- and `track`. Because the default UI is still being reworked, treat its internals
520
- as unstable and build against `useEditor` rather than against specific UI
521
- components.
543
+ One real difference remains: mocanvas ships **no message catalogues**, so
544
+ `overrides.translations` is where UI strings come from. The language menu lists
545
+ only the locales you supply a non-empty dictionary for, and renders nothing at
546
+ all if you supply none see the note in `UI.md`.
522
547
 
523
548
  ---
524
549
 
@@ -626,19 +651,20 @@ before.
626
651
 
627
652
  ## 10. Known gaps
628
653
 
629
- Taken from the "phase 3" and "later" rows of [COMPAT.md](COMPAT.md), plus what
630
- the code confirms today.
654
+ Every row below was re-checked against the built package rather than carried
655
+ forward from the roadmap. Rows that used to sit here and no longer belong —
656
+ `editor.resizeShape` and `stretchShapes`, `editor.getSvgString` and `toImage`,
657
+ `editor.textMeasure`/`user`/`menus`, presence records, and slot-compatible UI —
658
+ are all present, and the entries claiming otherwise were stale rather than
659
+ aspirational.
631
660
 
632
661
  | Gap | Status |
633
662
  | --- | ------ |
634
- | `editor.resizeShape`, `editor.stretchShapes` | phase 3. Interactive resize lives in the select tool; `ShapeUtil.onResize` and `BaseBoxShapeUtil` work, but there is no imperative resize entry point on `Editor`. |
635
- | `editor.getSvgString`, `editor.toImage` | Not `Editor` methods. Export is a set of free functions in `@mocanvas/mocanvas`: `getSvgString(editor, ids?, opts?)`, `exportToBlob(editor, opts)`, `downloadBlob(blob, filename)`, `copyBlobToClipboard(blob)`. |
636
663
  | `ShapeUtil.toSvg`, `ShapeUtil.toBackgroundSvg` | Not `ShapeUtil` members. Custom shapes contribute to SVG export through `registerShapeSvgRenderer(type, renderer)`; without one they fall back to `geometryFallbackSvg`. |
637
- | `pointer` and `instance_presence` records | later collaboration. No presence records, and no `mergeRemoteChanges` transport yet. |
638
- | Sync protocol | Explicit v1 non-goal. Wire compatibility with tldraw's sync protocol is not planned for v1. |
639
- | Slot-compatible UI | Explicit v1 non-goal. See §7. |
664
+ | Sync protocol | Wire compatibility with tldraw's own sync protocol is not planned. `@mocanvas/sync` is a working transport of its own — presence records, `store.mergeRemoteChanges`, and a relay but it does not speak tldraw's wire format. |
640
665
  | `image` shape on the GPU | The texture path exists in the engine (`StyleWords.texture` + `uploadTexture`) but the `image` shape still draws an `<img>` in the DOM overlay. |
641
- | `editor.textMeasure`, `editor.user`, `editor.menus` | Not implemented. Text measurement is DOM-backed inside the text layer; `editor.inputs` and `editor.sideEffects` do exist. |
666
+ | `FrameShapeUtil` default size | mocanvas creates a frame at **160×90**; tldraw creates one at 320×180. Same aspect, half the size. It only bites code that creates a frame *programmatically* without passing `w`/`h` drawing one with the tool sizes it from the drag either way. Pass explicit dimensions if the size matters to you. Aligning the default is a behaviour change and is not being made in a patch release. |
667
+ | An unregistered shape type in `store.put()` | Accepted, deliberately, and confirmed rather than fixed. A `.tldr` written by a build that knows a shape type this one does not must survive a load/save round trip instead of being dropped on the next save, so an unknown `type` passes its props through untouched. Its *other* fields are still validated — `x` must be a number whatever the type is — and a type you did register is validated in full. See `COMPAT.md`. |
642
668
  | Text rendering | DOM overlay. Glyph-atlas text in WASM is phase 3. |
643
669
  | GPU frame clipping | The `CLIP` flag and `isClipShape` hook are wired end to end, but no built-in shape enables it yet (phase 3). |
644
670
  | WebGPU backend | Phase 3. WebGL2 is the only backend today, behind `RenderBackend`. |
package/README.md CHANGED
@@ -48,6 +48,47 @@ const { status } = useSync(editor, { roomId, transport: () => createBroadcastCha
48
48
  selection outlines in screen space. It positions itself absolutely, so it goes
49
49
  anywhere inside the canvas container.
50
50
 
51
+ That is the whole integration: nothing above pins a page, and nothing names a
52
+ person. Two things make it work, and both are worth knowing because both used
53
+ to have to be worked around by hand.
54
+
55
+ ### The page both replicas start on
56
+
57
+ A blank document's first page has a **fixed id** — `DEFAULT_PAGE_ID`, i.e.
58
+ `page:page` — so two `<Mocanvas />`s that each built their own store start on
59
+ the *same* page. Shapes drawn in one tab land on the page the other is looking
60
+ at, and `getCollaboratorsOnCurrentPage()` has something to return. (Creating
61
+ the same record id on two peers is a field-by-field merge, and the two bodies
62
+ are identical, so there is nothing to settle.) Pages created *after* that get
63
+ random ids, so two people genuinely on different pages of one document stay
64
+ independent.
65
+
66
+ When a replica does end up alone on its page — someone moved to another page,
67
+ or two apps built their documents separately — the client says so once, in
68
+ development:
69
+
70
+ > mocanvas/sync: this replica is on page `page:…`, and none of the 1 peer(s) in
71
+ > room "…" is — they are on `page:…`. Their cursors and their shapes will not be
72
+ > visible here.
73
+
74
+ Everything is still `online` in that state, every message still arrives, and
75
+ nothing is drawn. It is the failure with the least evidence, which is why it
76
+ gets a sentence instead of silence.
77
+
78
+ ### A tab is not a person
79
+
80
+ Presence is per **editor instance**, not per user. `editor.user.getId()` is per
81
+ *browser* — the same in every tab, and the same on a phone and a laptop signed
82
+ in as one person — so a person with three tabs open would otherwise discard
83
+ their own other tabs as themselves. The identity that decides is
84
+ `editor.getInstancePresenceId()`: every editor mints one, this package
85
+ publishes the tab's presence record under it, and `editor.getCollaborators()`
86
+ drops that one record and no other.
87
+
88
+ So two tabs of one browser are two collaborators — which is the entire point of
89
+ `createBroadcastChannelTransport` — and an app does not have to invent a fake
90
+ per-tab identity to make its own demo work.
91
+
51
92
  ## Protocol
52
93
 
53
94
  Every message is one JSON object on the wire.
@@ -93,7 +134,13 @@ half-parsed. Nothing on the wire can crash this client.
93
134
  - Presence records are derived from the editor's camera,
94
135
  `inputs.currentPagePoint` and selection, throttled to at most 30 Hz, and
95
136
  re-sent on a heartbeat. A collaborator is dropped on `bye` or after 10s of
96
- silence.
137
+ silence. The record is keyed on the sending editor's instance id (see
138
+ "A tab is not a person" above), so one person's tabs do not overwrite each
139
+ other's presence.
140
+ - A `hello` makes every established peer **re-announce** its presence, even
141
+ when its own record has not changed — what changed is who is listening. A
142
+ peer that has been sitting still since before you joined is therefore visible
143
+ immediately rather than at its next heartbeat.
97
144
 
98
145
  ## Conflict policy: a CRDT over the record fields
99
146
 
package/UI.md CHANGED
@@ -4,6 +4,27 @@
4
4
  style panel, an optional statistics chip, and the chrome the editor draws on the
5
5
  canvas itself. All of it is optional and all of it is themeable from CSS.
6
6
 
7
+ ## Loading the stylesheet
8
+
9
+ The theme is published as `@mocanvas/mocanvas/mocanvas.css` and your app
10
+ imports it:
11
+
12
+ ```ts
13
+ import "@mocanvas/mocanvas/mocanvas.css"
14
+ ```
15
+
16
+ The package's JavaScript entry does not import it. That is deliberate: Node has
17
+ no loader for `.css`, so a CSS import inside `dist/index.js` makes
18
+ `import "@mocanvas/mocanvas"` throw `ERR_UNKNOWN_FILE_EXTENSION` anywhere there
19
+ is no bundler — vitest, SSR, a plain script. Up to and including 4.0.2 it did,
20
+ and the emitted file was content-hashed (`dist/ui-4C5V5GYT.css`) so there was no
21
+ stable name to import instead. Both are fixed; the cost is this one line in your
22
+ app.
23
+
24
+ `<Canvas />` from `@mocanvas/editor` carries fallback values for the canvas
25
+ chrome tokens, so an editor mounted without the stylesheet still renders
26
+ legibly — but the toolbar, panels, menus and dialogs need it.
27
+
7
28
  Source lives in `packages/mocanvas/src/ui/`:
8
29
 
9
30
  | File | What it holds |
@@ -122,8 +143,9 @@ so inline marks like the "mixed" badge keep the size they ask for.
122
143
  <Mocanvas hideUi />
123
144
  ```
124
145
 
125
- This drops the toolbar, zoom bar, style panel and stats chip. `ui.css` still
126
- loads, so the canvas chrome tokens above keep working. Default keyboard
146
+ This drops the toolbar, zoom bar, style panel and stats chip. The stylesheet is
147
+ imported by your app, not by the component, so the canvas chrome tokens above
148
+ keep working. Default keyboard
127
149
  shortcuts are wired by `<Mocanvas />` itself and are unaffected; call
128
150
  `useKeyboardShortcuts(editor)` yourself if you build on `<Canvas />` directly.
129
151
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Signal } from '@mocanvas/state';
2
2
  import { Store, UnknownRecord, RecordsDiff } from '@mocanvas/store';
3
- import { UserId, InstancePresence, InstancePresenceId, Editor } from '@mocanvas/editor';
3
+ import { UserId, InstancePresenceId, InstancePresence, Editor } from '@mocanvas/editor';
4
4
  import * as react from 'react';
5
5
 
6
6
  /** Presence is sent at most this often (30 Hz). */
@@ -47,6 +47,16 @@ interface PresenceEditor {
47
47
  scribbles: readonly unknown[];
48
48
  followingUserId: UserId | null;
49
49
  };
50
+ /**
51
+ * Optional: the id this instance's presence record is published under.
52
+ *
53
+ * A real `Editor` has one and it is the identity that matters here — a user
54
+ * id is per browser, so two tabs of one browser are one user and would
55
+ * discard each other's presence as their own. Structural stand-ins (tests,
56
+ * a headless participant) may omit it and fall back to
57
+ * {@link presenceIdForClient}.
58
+ */
59
+ getInstancePresenceId?(): InstancePresenceId;
50
60
  /** Optional: used to sample the cursor, which is not itself a signal. */
51
61
  on?(name: "event", fn: () => void): () => void;
52
62
  }
@@ -61,8 +71,17 @@ interface PresenceSyncOptions {
61
71
  interface PresenceSync {
62
72
  start(): void;
63
73
  stop(): void;
64
- /** Ask for a send; collapses with any other request inside the throttle window. */
65
- poke(): void;
74
+ /**
75
+ * Ask for a send; collapses with any other request inside the throttle
76
+ * window.
77
+ *
78
+ * `force` sends even when nothing about this client has changed, which is
79
+ * what a *re-announcement* is: somebody who was not listening when we last
80
+ * spoke has arrived, so the record has to go out again although it says the
81
+ * same thing. Without it a peer that has been sitting still since before the
82
+ * newcomer joined stays invisible until its next heartbeat.
83
+ */
84
+ poke(force?: boolean): void;
66
85
  /** The record last handed to `send`, for tests and debugging. */
67
86
  getLastSent(): InstancePresence | null;
68
87
  dispose(): void;
@@ -443,6 +462,11 @@ interface CollaboratorCursorsProps {
443
462
  /**
444
463
  * Other people's cursors and selections, drawn in screen space above the
445
464
  * canvas. Drop it inside `<Mocanvas>` or `<Canvas>`; it positions itself.
465
+ *
466
+ * The arrow below is the default. An app that supplies a
467
+ * {@link TLEditorComponents.CollaboratorCursor} component gets that instead —
468
+ * the slot was declared on two component maps and consulted by nothing, so an
469
+ * app that replaced the cursor silently kept seeing ours.
446
470
  */
447
471
  declare const CollaboratorCursors: ({ editor, showSelection, showNames, }: CollaboratorCursorsProps) => react.JSX.Element | null;
448
472
  interface UseSyncOptions {
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { react, atom } from '@mocanvas/state';
2
+ import { useEditorComponents, InstancePresenceRecordType, warnOnce } from '@mocanvas/editor';
2
3
  import { uniqueId, isRecordsDiffEmpty, createEmptyRecordsDiff } from '@mocanvas/store';
3
- import { InstancePresenceRecordType } from '@mocanvas/editor';
4
4
  import { track, useValue } from '@mocanvas/state/react';
5
5
  import { useRef, useState, useEffect } from 'react';
6
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
7
7
 
8
8
  // src/SyncClient.ts
9
9
  var CRDT_STATE_VERSION = 1;
@@ -504,7 +504,7 @@ function createPresenceSync(options) {
504
504
  const { editor, clientId, send } = options;
505
505
  const throttleMs = options.throttleMs ?? DEFAULT_PRESENCE_THROTTLE_MS;
506
506
  const heartbeatMs = options.heartbeatMs ?? DEFAULT_PRESENCE_HEARTBEAT_MS;
507
- const id = presenceIdForClient(clientId);
507
+ const id = editor.getInstancePresenceId?.() ?? presenceIdForClient(clientId);
508
508
  let timer = null;
509
509
  let heartbeat = null;
510
510
  let stopReactor = null;
@@ -538,11 +538,16 @@ function createPresenceSync(options) {
538
538
  last = next;
539
539
  send(next);
540
540
  };
541
- const poke = () => {
542
- if (!running || timer !== null) return;
541
+ let pendingForce = false;
542
+ const poke = (force = false) => {
543
+ if (!running) return;
544
+ if (force) pendingForce = true;
545
+ if (timer !== null) return;
543
546
  timer = setTimeout(() => {
544
547
  timer = null;
545
- flush(false);
548
+ const forced = pendingForce;
549
+ pendingForce = false;
550
+ flush(forced);
546
551
  }, throttleMs);
547
552
  };
548
553
  return {
@@ -564,6 +569,7 @@ function createPresenceSync(options) {
564
569
  },
565
570
  stop() {
566
571
  running = false;
572
+ pendingForce = false;
567
573
  if (timer !== null) {
568
574
  clearTimeout(timer);
569
575
  timer = null;
@@ -736,6 +742,8 @@ function createSyncClient(options) {
736
742
  });
737
743
  const unsubscribes = [];
738
744
  const reportedVersions = /* @__PURE__ */ new Set();
745
+ const peerPages = /* @__PURE__ */ new Map();
746
+ let warnedAboutPages = false;
739
747
  let presence = null;
740
748
  let seq = 0;
741
749
  let awaitingSnapshot = false;
@@ -830,7 +838,7 @@ function createSyncClient(options) {
830
838
  }
831
839
  awaitingSnapshot = false;
832
840
  sendSnapshot();
833
- presence?.poke();
841
+ presence?.poke(true);
834
842
  return;
835
843
  }
836
844
  case "snapshot": {
@@ -864,21 +872,38 @@ function createSyncClient(options) {
864
872
  case "presence": {
865
873
  if (message.clientId === clientId) return;
866
874
  room.receive(message.clientId, message.record);
875
+ notePeerPage(message.clientId, message.record);
867
876
  return;
868
877
  }
869
878
  case "bye": {
870
879
  if (message.clientId === clientId) return;
871
880
  room.remove(message.clientId);
881
+ peerPages.delete(message.clientId);
872
882
  return;
873
883
  }
874
884
  }
875
885
  };
886
+ const notePeerPage = (peerId, record) => {
887
+ const editor = options.presence?.editor;
888
+ if (!editor || warnedAboutPages) return;
889
+ const page = record.currentPageId;
890
+ if (typeof page !== "string") return;
891
+ peerPages.set(peerId, page);
892
+ const mine = editor.getCurrentPageId();
893
+ for (const peerPage of peerPages.values()) if (peerPage === mine) return;
894
+ warnedAboutPages = true;
895
+ const theirs = Array.from(new Set(peerPages.values())).join(", ");
896
+ warnOnce(
897
+ `sync.page:${roomId}:${clientId}`,
898
+ `mocanvas/sync: this replica is on page ${mine}, and none of the ${peerPages.size} peer(s) in room "${roomId}" is \u2014 they are on ${theirs}. Their cursors and their shapes will not be visible here. That is correct if you moved to another page on purpose; if you did not, the two replicas are on pages that were built separately. Every editor that starts from a blank document starts on the same default page, so check whether one side pinned, created or loaded a page of its own \u2014 and if so, call editor.setCurrentPage() with a page id both sides agree on.`
899
+ );
900
+ };
876
901
  const onOpen = () => {
877
902
  if (disposed || !connected) return;
878
903
  status.set("online");
879
904
  send({ type: "hello", clientId, version: PROTOCOL_VERSION });
880
905
  if (!awaitingSnapshot) sendSnapshot();
881
- presence?.poke();
906
+ presence?.poke(true);
882
907
  };
883
908
  const onClose = () => {
884
909
  if (disposed || !connected) return;
@@ -1138,12 +1163,29 @@ var CollaboratorCursors = track(function CollaboratorCursors2({
1138
1163
  showSelection = true,
1139
1164
  showNames = true
1140
1165
  }) {
1166
+ const components = useEditorComponents();
1141
1167
  const collaborators = editor.getCollaboratorsOnCurrentPage();
1142
1168
  if (collaborators.length === 0) return null;
1143
- return /* @__PURE__ */ jsx("svg", { className: "mocanvas-collaborators", style: layerStyle, children: collaborators.map((presence) => /* @__PURE__ */ jsxs("g", { children: [
1144
- showSelection ? /* @__PURE__ */ jsx(CollaboratorSelection, { editor, presence }) : null,
1145
- /* @__PURE__ */ jsx(CollaboratorCursor, { editor, presence, showName: showNames })
1146
- ] }, presence.id)) });
1169
+ const Slot = components.CollaboratorCursor;
1170
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1171
+ /* @__PURE__ */ jsx("svg", { className: "mocanvas-collaborators", style: layerStyle, children: collaborators.map((presence) => /* @__PURE__ */ jsxs("g", { children: [
1172
+ showSelection ? /* @__PURE__ */ jsx(CollaboratorSelection, { editor, presence }) : null,
1173
+ Slot === void 0 ? /* @__PURE__ */ jsx(CollaboratorCursor, { editor, presence, showName: showNames }) : null
1174
+ ] }, presence.id)) }),
1175
+ Slot ? /* @__PURE__ */ jsx("div", { className: "mocanvas-collaborator-cursors", style: layerStyle, children: collaborators.map(
1176
+ (presence) => presence.cursor === null ? null : /* @__PURE__ */ jsx(
1177
+ Slot,
1178
+ {
1179
+ type: "default",
1180
+ rotation: presence.cursor.rotation,
1181
+ color: presence.color,
1182
+ name: showNames ? presence.userName || "Anonymous" : null,
1183
+ point: editor.pageToViewport(presence.cursor)
1184
+ },
1185
+ presence.id
1186
+ )
1187
+ ) }) : null
1188
+ ] });
1147
1189
  });
1148
1190
  var CollaboratorCursor = track(function CollaboratorCursor2({
1149
1191
  editor,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/crdt.ts","../src/presence.ts","../src/protocol.ts","../src/SyncClient.ts","../src/transport.ts","../src/react.tsx"],"names":["createEmptyRecordsDiff","CollaboratorCursors","CollaboratorCursor","CollaboratorSelection"],"mappings":"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","file":"index.js","sourcesContent":["import { createEmptyRecordsDiff, type IdOf, type RecordsDiff, type UnknownRecord } from \"@mocanvas/store\"\n\n/**\n * A state-based CRDT over record fields.\n *\n * Every leaf field of every record is its own last-writer-wins register,\n * keyed by a dotted path (`x`, `props.w`, `meta.notes.title`). A write carries\n * a Lamport stamp; the greater stamp wins, and equal Lamport values are broken\n * by client id so that both sides of a race reach the same answer from the\n * same pair. Deletion is a stamped tombstone rather than a removal, so a late\n * message cannot resurrect a record that was deleted after it was written.\n *\n * Consequences worth knowing before reading the code:\n *\n * - Arrays are opaque. `props.points` is one register holding the whole array,\n * not one register per element. Two concurrent edits to one array pick a\n * winner instead of merging; splitting arrays into elements would need\n * element identity, which record data does not carry.\n * - A stamp covers a whole subtree. Writing `meta` (setting it, or deleting\n * the key) dominates `meta.a`: the claim loses to any stamp at or above its\n * own path, and dropping a subtree keeps descendants stamped later than the\n * write. Without that, `delete meta` racing `set meta.a` lands differently\n * depending on arrival order.\n * - A record also has one `base` register: the stamp at which some sender's\n * whole record body was current. Fields nobody holds a stamp for — a record\n * materialized without its creation message, or one that came back from\n * under a tombstone, which erases field stamps — are governed by that single\n * register, so every replica takes its body from the same message instead of\n * from whichever one happened to arrive first.\n * - Merging is idempotent (the same message twice is a no-op the second time)\n * and commutative (per path the result is the maximum stamp, which does not\n * depend on arrival order).\n * - The merge is a pure function of the stamps; values only ever ride along.\n */\n\n/** Version of the `CrdtState` blob on the wire. */\nexport const CRDT_STATE_VERSION = 1\n\n/** How many collected-and-gone records keep a tombstone before it is dropped. */\nexport const DEFAULT_TOMBSTONE_LIMIT = 5000\n/** How long a tombstone for a vanished record is kept. */\nexport const DEFAULT_TOMBSTONE_MAX_AGE_MS = 60 * 60 * 1000\n\n/* -------------------------------------------------------------------------- */\n/* stamps */\n/* -------------------------------------------------------------------------- */\n\n/** A point in the partial order of writes, made total by the client tiebreak. */\nexport interface Stamp {\n readonly lamport: number\n readonly client: string\n}\n\n/**\n * A total order on stamps: Lamport first, then client id. Two peers holding\n * the same pair always agree, which is what makes the merge deterministic.\n */\nexport function compareStamps(a: Stamp, b: Stamp): number {\n if (a.lamport !== b.lamport) return a.lamport < b.lamport ? -1 : 1\n if (a.client === b.client) return 0\n return a.client < b.client ? -1 : 1\n}\n\nexport function isStamp(value: unknown): value is Stamp {\n if (typeof value !== \"object\" || value === null) return false\n const stamp = value as { lamport?: unknown; client?: unknown }\n return typeof stamp.lamport === \"number\" && Number.isFinite(stamp.lamport) && typeof stamp.client === \"string\"\n}\n\nexport interface LamportClock {\n /** The current value. */\n get(): number\n /** Advance for a local write and return the new value. */\n tick(): number\n /** Fold in a stamp seen from a peer: `max(local, remote) + 1`. */\n observe(remote: number): void\n}\n\nexport function createLamportClock(start = 0): LamportClock {\n let value = start\n return {\n get: () => value,\n tick: () => (value += 1),\n observe(remote: number) {\n if (!Number.isFinite(remote)) return\n value = Math.max(value, remote) + 1\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* wire shapes */\n/* -------------------------------------------------------------------------- */\n\n/**\n * One record's changed fields. `record` is the whole record as the sender saw\n * it after the change; `fields` says which paths this message actually claims.\n * A receiver that already has the record takes only the winning paths out of\n * `record`; a receiver that does not have it materializes from `record` whole.\n */\nexport interface StampedPut<R extends UnknownRecord = UnknownRecord> {\n record: R\n fields: Record<string, Stamp>\n /**\n * The stamp at which `record` was the sender's whole view of that record.\n * Paths the receiver holds no stamp for are taken from `record` when this is\n * the greatest such stamp it has seen, and left alone otherwise.\n */\n base?: Stamp\n}\n\nexport interface StampedRemove {\n id: string\n stamp: Stamp\n}\n\n/** What a `diff` message carries: puts and removes, each field stamped. */\nexport interface StampedDiff<R extends UnknownRecord = UnknownRecord> {\n puts: StampedPut<R>[]\n removes: StampedRemove[]\n}\n\nexport function createEmptyStampedDiff<R extends UnknownRecord = UnknownRecord>(): StampedDiff<R> {\n return { puts: [], removes: [] }\n}\n\nexport function isStampedDiffEmpty<R extends UnknownRecord>(diff: StampedDiff<R>): boolean {\n return diff.puts.length === 0 && diff.removes.length === 0\n}\n\n/** Structural check used by `decodeMessage`; deliberately shallow but strict. */\nexport function isStampedDiffLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false\n const diff = value as { puts?: unknown; removes?: unknown }\n if (!Array.isArray(diff.puts) || !Array.isArray(diff.removes)) return false\n for (const put of diff.puts) {\n if (typeof put !== \"object\" || put === null) return false\n const { record, fields, base } = put as { record?: unknown; fields?: unknown; base?: unknown }\n if (typeof record !== \"object\" || record === null) return false\n if (typeof (record as { id?: unknown }).id !== \"string\") return false\n if (typeof (record as { typeName?: unknown }).typeName !== \"string\") return false\n if (!isStampMap(fields)) return false\n if (base !== undefined && !isStamp(base)) return false\n }\n for (const remove of diff.removes) {\n if (typeof remove !== \"object\" || remove === null) return false\n const { id, stamp } = remove as { id?: unknown; stamp?: unknown }\n if (typeof id !== \"string\" || !isStamp(stamp)) return false\n }\n return true\n}\n\n/** What one record's registers look like once serialized. */\nexport interface CrdtRecordState {\n fields: Record<string, Stamp>\n /** The stamp of the message this record's unstamped fields came from. */\n base?: Stamp\n deleted?: Stamp\n /** Local wall clock at the time the tombstone was taken, for collection. */\n deletedAt?: number\n}\n\n/** The whole replica's stamps, as carried by a `snapshot` message. */\nexport interface CrdtState {\n version: number\n clock: number\n records: Record<string, CrdtRecordState>\n}\n\nexport function createEmptyCrdtState(): CrdtState {\n return { version: CRDT_STATE_VERSION, clock: 0, records: {} }\n}\n\nexport function isCrdtStateLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false\n const state = value as { version?: unknown; clock?: unknown; records?: unknown }\n if (typeof state.version !== \"number\") return false\n if (typeof state.clock !== \"number\") return false\n if (typeof state.records !== \"object\" || state.records === null || Array.isArray(state.records)) return false\n for (const key of Object.keys(state.records as Record<string, unknown>)) {\n const record = (state.records as Record<string, unknown>)[key]\n if (typeof record !== \"object\" || record === null) return false\n const { fields, deleted, base } = record as { fields?: unknown; deleted?: unknown; base?: unknown }\n if (!isStampMap(fields)) return false\n if (deleted !== undefined && !isStamp(deleted)) return false\n if (base !== undefined && !isStamp(base)) return false\n }\n return true\n}\n\nfunction isStampMap(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false\n for (const key of Object.keys(value as Record<string, unknown>)) {\n if (!isStamp((value as Record<string, unknown>)[key])) return false\n }\n return true\n}\n\n/* -------------------------------------------------------------------------- */\n/* the CRDT */\n/* -------------------------------------------------------------------------- */\n\nexport interface CrdtOptions<R extends UnknownRecord = UnknownRecord> {\n /** Identifies this replica; also the tiebreak in `compareStamps`. */\n clientId: string\n /**\n * Read the replica's current value for a record. The CRDT owns the stamps,\n * never the values: the store stays the source of truth and a merge is\n * expressed as a diff against whatever `getRecord` reports.\n */\n getRecord?: ((id: string) => R | undefined) | undefined\n /** Tombstones kept for records that are gone. Default 5000. */\n tombstoneLimit?: number | undefined\n /** How long such a tombstone lives. Default one hour. */\n tombstoneMaxAgeMs?: number | undefined\n /** Injectable wall clock, for tests. */\n now?: (() => number) | undefined\n}\n\nexport interface Crdt<R extends UnknownRecord = UnknownRecord> {\n readonly clientId: string\n readonly clock: LamportClock\n /**\n * Stamp a diff the local user just made and return the message to send.\n * The whole diff shares one stamp: it was one operation.\n */\n stampLocal(diff: RecordsDiff<R>): StampedDiff<R>\n /**\n * Merge a peer's message. The returned diff holds only the fields that won,\n * as whole records the store can apply — never a blind overwrite.\n */\n mergeRemote(stamped: StampedDiff<R>): RecordsDiff<R>\n /** This replica's stamps, for a `snapshot` message. */\n getState(): CrdtState\n /**\n * Adopt a peer's stamps wholesale. This is what a replica that is joining\n * with no state of its own does, together with that peer's records: it takes\n * the peer's stamps instead of starting blank, so its own later writes sort\n * after everything already in the room. The returned diff removes whatever\n * the peer has tombstoned and we still hold. Only for a replica holding no\n * stamps at all: the caller adopts the peer's records wholesale alongside it,\n * so a replica with work of its own must merge the snapshot through\n * `stampedDiffFromSnapshot` instead of taking it.\n */\n applyState(state: CrdtState): RecordsDiff<R>\n /**\n * Writes this replica minted while merging, which the caller must broadcast.\n * A record kept alive by an edit that outranks a delete is re-claimed whole\n * under a fresh stamp: the tombstone leaves the fields it dominates with no\n * provenance at all, and only a stamped body can put every replica on the\n * same one. Empty when there is nothing to send.\n */\n takeOutgoing(): StampedDiff<R> | null\n /** Number of records the state currently tracks (tests and diagnostics). */\n size(): number\n /** Drop tombstones past the count or age bound. Called after every merge. */\n collectTombstones(): void\n}\n\ninterface RecordState {\n fields: Map<string, Stamp>\n /** The stamp of the message this record's unstamped fields came from. */\n base?: Stamp | undefined\n deleted?: Stamp | undefined\n deletedAt?: number | undefined\n}\n\n/** The greater of two stamps, either of which may be missing. */\nfunction maxStamp(a: Stamp | undefined, b: Stamp | undefined): Stamp | undefined {\n if (!a) return b\n if (!b) return a\n return compareStamps(a, b) >= 0 ? a : b\n}\n\n/** The greatest stamp a put claims, which is what its body is current as of. */\nfunction greatestStamp(fields: Record<string, Stamp>): Stamp | undefined {\n let best: Stamp | undefined\n for (const path of Object.keys(fields)) best = maxStamp(best, fields[path])\n return best\n}\n\n/** Every proper ancestor of a dotted path, outermost first. */\nfunction ancestorPaths(path: string): string[] {\n const out: string[] = []\n let index = path.indexOf(\".\")\n while (index !== -1) {\n out.push(path.slice(0, index))\n index = path.indexOf(\".\", index + 1)\n }\n return out\n}\n\n/**\n * Take `path` for `stamp`, unless something at or above it is stamped at least\n * as late — a write to `meta` covers `meta.a`, so the two cannot be compared\n * as if they were unrelated registers. Descendants the write covers are\n * dropped; descendants stamped later than it survive and are returned, because\n * their values have to be put back on top of whatever the write does.\n */\nfunction claimPath(state: RecordState, path: string, stamp: Stamp): string[] | null {\n const own = state.fields.get(path)\n if (own && compareStamps(stamp, own) <= 0) return null\n for (const ancestor of ancestorPaths(path)) {\n const above = state.fields.get(ancestor)\n if (above && compareStamps(stamp, above) <= 0) return null\n }\n const survivors: string[] = []\n const prefix = `${path}.`\n for (const [other, existing] of Array.from(state.fields)) {\n if (!other.startsWith(prefix)) continue\n if (compareStamps(existing, stamp) <= 0) state.fields.delete(other)\n else survivors.push(other)\n }\n state.fields.set(path, stamp)\n return survivors\n}\n\n/** True when this path, or one above it, carries a stamp of its own. */\nfunction isStamped(state: RecordState, path: string): boolean {\n if (state.fields.has(path)) return true\n for (const ancestor of ancestorPaths(path)) {\n if (state.fields.has(ancestor)) return true\n }\n return false\n}\n\n/**\n * The highest ancestor of `path` that the body has nothing at and no stamp\n * covers — the level a \"this is not here any more\" has to be applied at.\n */\nfunction highestAbsent(body: unknown, state: RecordState, path: string): string {\n for (const ancestor of ancestorPaths(path)) {\n if (readPath(body, ancestor) !== ABSENT) continue\n if (isStamped(state, ancestor) || hasStampedDescendant(state, ancestor)) continue\n return ancestor\n }\n return path\n}\n\n/** True when something strictly below this path carries a stamp. */\nfunction hasStampedDescendant(state: RecordState, path: string): boolean {\n const prefix = `${path}.`\n for (const other of state.fields.keys()) {\n if (other.startsWith(prefix)) return true\n }\n return false\n}\n\nexport function createCrdt<R extends UnknownRecord = UnknownRecord>(options: CrdtOptions<R>): Crdt<R> {\n const { clientId } = options\n const getRecord = options.getRecord ?? (() => undefined)\n const tombstoneLimit = options.tombstoneLimit ?? DEFAULT_TOMBSTONE_LIMIT\n const tombstoneMaxAgeMs = options.tombstoneMaxAgeMs ?? DEFAULT_TOMBSTONE_MAX_AGE_MS\n const now = options.now ?? (() => Date.now())\n const clock = createLamportClock()\n const states = new Map<string, RecordState>()\n let outgoing = createEmptyStampedDiff<R>()\n\n const stateFor = (id: string): RecordState => {\n let state = states.get(id)\n if (!state) {\n state = { fields: new Map() }\n states.set(id, state)\n }\n return state\n }\n\n /**\n * A merge in progress: `before` remembers what the store had when we first\n * looked at a record, `after` what it should end up with. Keeping both lets\n * one message both remove and re-create a record without contradicting\n * itself in the diff we hand back.\n */\n interface Pass {\n before: Map<string, R | undefined>\n after: Map<string, R | undefined>\n }\n\n const startPass = (): Pass => ({ before: new Map(), after: new Map() })\n\n const readBefore = (pass: Pass, id: string): R | undefined => {\n if (!pass.before.has(id)) pass.before.set(id, getRecord(id))\n return pass.before.get(id)\n }\n\n const readCurrent = (pass: Pass, id: string): R | undefined =>\n pass.after.has(id) ? pass.after.get(id) : readBefore(pass, id)\n\n const write = (pass: Pass, id: string, value: R | undefined): void => {\n readBefore(pass, id)\n pass.after.set(id, value)\n }\n\n const finishPass = (pass: Pass): RecordsDiff<R> => {\n const result = createEmptyRecordsDiff<R>()\n for (const [id, next] of pass.after) {\n const prev = pass.before.get(id)\n if (prev === next) continue\n const key = id as IdOf<R>\n if (prev === undefined) {\n if (next !== undefined) result.added[key] = next\n } else if (next === undefined) {\n result.removed[key] = prev\n } else {\n result.updated[key] = [prev, next]\n }\n }\n return result\n }\n\n /** Record a tombstone. Fields it dominates are dropped; survivors keep the record alive. */\n const applyRemove = (pass: Pass, id: string, stamp: Stamp): void => {\n const state = stateFor(id)\n if (state.deleted && compareStamps(stamp, state.deleted) <= 0) return\n state.deleted = stamp\n state.deletedAt = now()\n // The body older messages carry dies with the fields: only a message that\n // outranks the tombstone may supply one from here on.\n state.base = maxStamp(state.base, stamp)\n let alive = false\n for (const [path, existing] of Array.from(state.fields)) {\n if (compareStamps(existing, stamp) > 0) alive = true\n else state.fields.delete(path)\n }\n // A concurrent edit newer than the delete keeps the record: the delete only\n // wins when its stamp is greater than every field that would survive it.\n if (!alive) {\n if (readCurrent(pass, id) !== undefined) write(pass, id, undefined)\n return\n }\n // It survives — but the tombstone has just taken the provenance of every\n // field it outranks, and each replica is holding a different leftover for\n // those. Re-claim this body whole under a fresh stamp and send it, so the\n // record comes back as one agreed record rather than as a race.\n const record = readCurrent(pass, id)\n if (!record) return\n const revival: Stamp = { lamport: clock.tick(), client: clientId }\n const fields: Record<string, Stamp> = {}\n for (const path of leafPaths(record)) {\n fields[path] = revival\n claimPath(state, path, revival)\n }\n state.base = revival\n outgoing.puts.push({ record, fields, base: revival })\n }\n\n /**\n * Take every path neither the record nor the sender holds a stamp for from\n * the sender's body. Those are the fields the registers say nothing about,\n * and `base` is the one register that decides them.\n */\n const adoptBody = (record: R, body: R, state: RecordState): R => {\n const paths = new Set<string>([...leafPaths(body), ...leafPaths(record)])\n let next = record\n for (const path of Array.from(paths).sort()) {\n if (path === \"id\" || path === \"typeName\") continue\n if (isStamped(state, path) || hasStampedDescendant(state, path)) continue\n const value = readPath(body, path)\n // Take the whole subtree the body does not have, not just the leaf, so\n // dropping the last key of an object does not leave an empty one behind.\n next = writePath(next, value === ABSENT ? highestAbsent(body, state, path) : path, value)\n }\n return next\n }\n\n const applyPut = (pass: Pass, put: StampedPut<R>): void => {\n const id = put.record.id as string\n const state = stateFor(id)\n const claims: { path: string; survivors: string[] }[] = []\n for (const path of Object.keys(put.fields)) {\n const stamp = put.fields[path]\n if (!stamp) continue\n // A write older than the tombstone cannot resurrect the record.\n if (state.deleted && compareStamps(stamp, state.deleted) <= 0) continue\n // `<= 0` and not `< 0` inside: the same message twice changes nothing.\n const survivors = claimPath(state, path, stamp)\n if (survivors === null) continue\n claims.push({ path, survivors })\n }\n const base = put.base ?? greatestStamp(put.fields)\n const bodyWins = base !== undefined && (state.base === undefined || compareStamps(base, state.base) > 0)\n if (claims.length === 0 && !bodyWins) return\n\n const current = readCurrent(pass, id)\n if (current === undefined) {\n // Nothing here to merge into. A body alone must never bring a record\n // back: without a claim that outranks the tombstone this is a straggler.\n if (claims.length === 0) return\n write(pass, id, put.record)\n state.base = maxStamp(state.base, base)\n return\n }\n\n let next = current\n for (const { path, survivors } of claims) {\n if (path === \"id\") continue // identity is never merged\n const before = next\n next = writePath(next, path, readPath(put.record, path))\n // A subtree stamped later than this write outlives it, so put those\n // values back: `delete meta` racing `set meta.a` must land the same way\n // whichever arrives first.\n for (const survivor of survivors) next = writePath(next, survivor, readPath(before, survivor))\n }\n if (bodyWins) {\n next = adoptBody(next, put.record, state)\n state.base = base\n }\n if (next !== current) write(pass, id, next)\n }\n\n const observeStamps = (stamped: StampedDiff<R>): void => {\n let max = -1\n for (const put of stamped.puts) {\n for (const path of Object.keys(put.fields)) {\n const stamp = put.fields[path]\n if (stamp && stamp.lamport > max) max = stamp.lamport\n }\n if (put.base && put.base.lamport > max) max = put.base.lamport\n }\n for (const remove of stamped.removes) {\n if (remove.stamp.lamport > max) max = remove.stamp.lamport\n }\n if (max >= 0) clock.observe(max)\n }\n\n const collectTombstones = (): void => {\n const gone: { id: string; at: number }[] = []\n for (const [id, state] of states) {\n if (state.deleted && state.fields.size === 0) gone.push({ id, at: state.deletedAt ?? 0 })\n }\n if (gone.length === 0) return\n const cutoff = now() - tombstoneMaxAgeMs\n let overflow = gone.length - tombstoneLimit\n if (overflow <= 0 && gone.every((entry) => entry.at >= cutoff)) return\n gone.sort((a, b) => a.at - b.at)\n for (const entry of gone) {\n if (overflow > 0) {\n states.delete(entry.id)\n overflow--\n continue\n }\n if (entry.at < cutoff) states.delete(entry.id)\n }\n }\n\n return {\n clientId,\n clock,\n\n stampLocal(diff: RecordsDiff<R>): StampedDiff<R> {\n const stamp: Stamp = { lamport: clock.tick(), client: clientId }\n const result = createEmptyStampedDiff<R>()\n\n for (const id in diff.added) {\n const record = diff.added[id as IdOf<R>]\n if (!record) continue\n const fields: Record<string, Stamp> = {}\n const state = stateFor(id)\n for (const path of leafPaths(record)) {\n fields[path] = stamp\n claimPath(state, path, stamp)\n }\n // Our record is our whole view of it, as of now.\n state.base = stamp\n result.puts.push({ record, fields, base: stamp })\n }\n\n for (const id in diff.updated) {\n const pair = diff.updated[id as IdOf<R>]\n if (!pair) continue\n const [from, to] = pair\n const paths = changedPaths(from, to)\n if (paths.length === 0) continue\n const fields: Record<string, Stamp> = {}\n const state = stateFor(id)\n for (const path of paths) {\n fields[path] = stamp\n claimPath(state, path, stamp)\n }\n state.base = stamp\n result.puts.push({ record: to, fields, base: stamp })\n }\n\n for (const id in diff.removed) {\n const state = stateFor(id)\n state.deleted = stamp\n state.deletedAt = now()\n state.fields.clear()\n state.base = stamp\n result.removes.push({ id, stamp })\n }\n\n collectTombstones()\n return result\n },\n\n mergeRemote(stamped: StampedDiff<R>): RecordsDiff<R> {\n observeStamps(stamped)\n const pass = startPass()\n for (const remove of stamped.removes) applyRemove(pass, remove.id, remove.stamp)\n for (const put of stamped.puts) applyPut(pass, put)\n collectTombstones()\n return finishPass(pass)\n },\n\n getState(): CrdtState {\n const records: Record<string, CrdtRecordState> = {}\n for (const [id, state] of states) {\n const fields: Record<string, Stamp> = {}\n for (const [path, stamp] of state.fields) fields[path] = stamp\n records[id] = {\n fields,\n ...(state.base ? { base: state.base } : {}),\n ...(state.deleted ? { deleted: state.deleted, deletedAt: state.deletedAt ?? 0 } : {}),\n }\n }\n return { version: CRDT_STATE_VERSION, clock: clock.get(), records }\n },\n\n applyState(state: CrdtState): RecordsDiff<R> {\n const pass = startPass()\n if (state.version !== CRDT_STATE_VERSION) return finishPass(pass)\n clock.observe(state.clock)\n for (const id of Object.keys(state.records)) {\n const incoming = state.records[id]\n if (!incoming) continue\n if (incoming.deleted) applyRemove(pass, id, incoming.deleted)\n const local = stateFor(id)\n for (const path of Object.keys(incoming.fields)) {\n const stamp = incoming.fields[path]\n if (!stamp) continue\n clock.observe(stamp.lamport)\n if (local.deleted && compareStamps(stamp, local.deleted) <= 0) continue\n claimPath(local, path, stamp)\n }\n if (incoming.base) {\n clock.observe(incoming.base.lamport)\n local.base = maxStamp(local.base, incoming.base)\n }\n }\n collectTombstones()\n return finishPass(pass)\n },\n\n takeOutgoing(): StampedDiff<R> | null {\n if (isStampedDiffEmpty(outgoing)) return null\n const result = outgoing\n outgoing = createEmptyStampedDiff<R>()\n return result\n },\n\n size: () => states.size,\n collectTombstones,\n }\n}\n\n/**\n * Turn a peer's snapshot into a message the ordinary merge can eat: its\n * records supply the values, its state supplies the stamps. Paths the state\n * says nothing about are not claimed, so a snapshot can never overwrite a\n * field the receiver has a stamp for.\n */\nexport function stampedDiffFromSnapshot<R extends UnknownRecord>(\n records: readonly R[],\n state: CrdtState,\n): StampedDiff<R> {\n const result = createEmptyStampedDiff<R>()\n for (const record of records) {\n const entry = state.records[record.id as string]\n const fields = entry?.fields ?? {}\n const base = entry?.base ?? greatestStamp(fields)\n result.puts.push(base ? { record, fields, base } : { record, fields })\n }\n for (const id of Object.keys(state.records)) {\n const deleted = state.records[id]?.deleted\n if (deleted) result.removes.push({ id, stamp: deleted })\n }\n return result\n}\n\n/* -------------------------------------------------------------------------- */\n/* field paths */\n/* -------------------------------------------------------------------------- */\n\n/** A path that is not present at all, as distinct from one holding `undefined`. */\nconst ABSENT = Symbol(\"absent\")\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false\n const proto = Object.getPrototypeOf(value) as unknown\n return proto === Object.prototype || proto === null\n}\n\n/**\n * Every leaf path of a value. Plain objects are recursed into; arrays,\n * primitives, `null` and empty objects are leaves — an array is one register\n * holding the whole array.\n */\nexport function leafPaths(value: unknown, prefix = \"\"): string[] {\n const out: string[] = []\n collectLeaves(value, prefix, out)\n return out\n}\n\nfunction collectLeaves(value: unknown, prefix: string, out: string[]): void {\n if (isPlainObject(value)) {\n const keys = Object.keys(value)\n if (keys.length > 0) {\n for (const key of keys) collectLeaves(value[key], prefix ? `${prefix}.${key}` : key, out)\n return\n }\n }\n if (prefix !== \"\") out.push(prefix)\n}\n\n/** The paths whose values differ between two versions of a record. */\nexport function changedPaths(from: unknown, to: unknown): string[] {\n const out: string[] = []\n collectChanges(from, to, \"\", out)\n return out\n}\n\nfunction collectChanges(from: unknown, to: unknown, prefix: string, out: string[]): void {\n if (isPlainObject(from) && isPlainObject(to)) {\n for (const key of Object.keys(from)) {\n const path = prefix ? `${prefix}.${key}` : key\n // A key that disappeared: stamp the whole subtree as one removal.\n if (!(key in to)) out.push(path)\n else collectChanges(from[key], to[key], path, out)\n }\n for (const key of Object.keys(to)) {\n if (key in from) continue\n collectLeaves(to[key], prefix ? `${prefix}.${key}` : key, out)\n }\n return\n }\n if (!deepEqual(from, to) && prefix !== \"\") out.push(prefix)\n}\n\nfunction readPath(source: unknown, path: string): unknown {\n let current: unknown = source\n for (const segment of path.split(\".\")) {\n if (!isPlainObject(current) || !(segment in current)) return ABSENT\n current = current[segment]\n }\n return current\n}\n\n/**\n * A copy of `record` with `path` set to `value` (or the key deleted when the\n * path is absent in the source). Every level along the path is cloned, because\n * the store freezes records, their `props` and their `meta`.\n */\nfunction writePath<R extends UnknownRecord>(record: R, path: string, value: unknown): R {\n const current = readPath(record, path)\n if (value === ABSENT) {\n if (current === ABSENT) return record\n } else if (current !== ABSENT && deepEqual(current, value)) {\n return record\n }\n const segments = path.split(\".\")\n return setIn(record as unknown as Record<string, unknown>, segments, 0, value) as unknown as R\n}\n\nfunction setIn(\n target: Record<string, unknown>,\n segments: string[],\n index: number,\n value: unknown,\n): Record<string, unknown> {\n const key = segments[index]\n if (key === undefined) return target\n const next: Record<string, unknown> = { ...target }\n if (index === segments.length - 1) {\n if (value === ABSENT) delete next[key]\n else next[key] = value\n return next\n }\n const child = next[key]\n next[key] = setIn(isPlainObject(child) ? child : {}, segments, index + 1, value)\n return next\n}\n\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (Array.isArray(a)) {\n if (!Array.isArray(b) || a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n }\n if (isPlainObject(a) && isPlainObject(b)) {\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!(key in b)) return false\n if (!deepEqual(a[key], b[key])) return false\n }\n return true\n }\n return false\n}\n","import { react } from \"@mocanvas/state\"\nimport {\n InstancePresenceRecordType,\n type Editor,\n type InstancePresence,\n type InstancePresenceId,\n type UserId,\n} from \"@mocanvas/editor\"\nimport type { IdOf, Store, UnknownRecord } from \"@mocanvas/store\"\n\n/** Presence is sent at most this often (30 Hz). */\nexport const DEFAULT_PRESENCE_THROTTLE_MS = Math.ceil(1000 / 30)\n/** A presence record is re-sent this often even when nothing changed. */\nexport const DEFAULT_PRESENCE_HEARTBEAT_MS = 3000\n/** A collaborator we have not heard from in this long is dropped. */\nexport const DEFAULT_PRESENCE_TIMEOUT_MS = 10_000\n\n/**\n * The part of the editor presence reads. Declared structurally so that the\n * sync layer does not need a live `Editor` (and its WASM engine) to be tested.\n */\nexport interface PresenceEditor {\n readonly store: Store<any, any>\n readonly inputs: { readonly currentPagePoint: { x: number; y: number } }\n readonly user: { getId(): UserId; getName(): string; getColor(): string }\n getCurrentPageId(): string\n getCamera(): { x: number; y: number; z: number }\n getSelectedShapeIds(): readonly string[]\n getInstanceState(): {\n cursor: { type: string; rotation: number }\n brush: { x: number; y: number; w: number; h: number } | null\n scribbles: readonly unknown[]\n followingUserId: UserId | null\n }\n /** Optional: used to sample the cursor, which is not itself a signal. */\n on?(name: \"event\", fn: () => void): () => void\n}\n\n// A real Editor must satisfy the structural interface above; this fails to\n// compile if the two ever drift apart.\ntype Assert<T extends true> = T\nexport type EditorIsPresenceEditor = Assert<Editor extends PresenceEditor ? true : false>\n\nexport interface PresenceSyncOptions {\n editor: PresenceEditor\n /** Identifies this tab; one user may have several. */\n clientId: string\n send(record: InstancePresence): void\n throttleMs?: number\n heartbeatMs?: number\n}\n\nexport interface PresenceSync {\n start(): void\n stop(): void\n /** Ask for a send; collapses with any other request inside the throttle window. */\n poke(): void\n /** The record last handed to `send`, for tests and debugging. */\n getLastSent(): InstancePresence | null\n dispose(): void\n}\n\n/** The id a client's presence record always uses, so updates overwrite in place. */\nexport function presenceIdForClient(clientId: string): InstancePresenceId {\n return InstancePresenceRecordType.createId(clientId) as InstancePresenceId\n}\n\n/**\n * Watches the editor and pushes the local presence record out, at most once per\n * throttle window, plus a heartbeat so that idle collaborators do not time out.\n */\nexport function createPresenceSync(options: PresenceSyncOptions): PresenceSync {\n const { editor, clientId, send } = options\n const throttleMs = options.throttleMs ?? DEFAULT_PRESENCE_THROTTLE_MS\n const heartbeatMs = options.heartbeatMs ?? DEFAULT_PRESENCE_HEARTBEAT_MS\n const id = presenceIdForClient(clientId)\n\n let timer: ReturnType<typeof setTimeout> | null = null\n let heartbeat: ReturnType<typeof setInterval> | null = null\n let stopReactor: (() => void) | null = null\n let stopEvents: (() => void) | null = null\n let last: InstancePresence | null = null\n let running = false\n\n const build = (): InstancePresence => {\n const instance = editor.getInstanceState()\n const point = editor.inputs.currentPagePoint\n return InstancePresenceRecordType.create({\n id,\n userId: editor.user.getId(),\n userName: editor.user.getName(),\n color: editor.user.getColor(),\n currentPageId: editor.getCurrentPageId() as InstancePresence[\"currentPageId\"],\n cursor: { x: point.x, y: point.y, type: instance.cursor.type, rotation: instance.cursor.rotation },\n camera: { ...editor.getCamera() },\n selectedShapeIds: [...editor.getSelectedShapeIds()] as InstancePresence[\"selectedShapeIds\"],\n brush: instance.brush ? { ...instance.brush } : null,\n scribbles: [...instance.scribbles] as InstancePresence[\"scribbles\"],\n followingUserId: instance.followingUserId,\n lastActivityTimestamp: Date.now(),\n chatMessage: \"\",\n meta: {},\n })\n }\n\n const flush = (force: boolean) => {\n if (!running) return\n const next = build()\n if (!force && last && isSamePresence(last, next)) return\n last = next\n send(next)\n }\n\n const poke = () => {\n if (!running || timer !== null) return\n timer = setTimeout(() => {\n timer = null\n flush(false)\n }, throttleMs)\n }\n\n return {\n start() {\n if (running) return\n running = true\n // Camera, page and selection are signals, so a reactor covers them.\n stopReactor = react(\"sync.presence\", () => {\n editor.getCamera()\n editor.getCurrentPageId()\n editor.getSelectedShapeIds()\n editor.getInstanceState()\n editor.user.getName()\n editor.user.getColor()\n poke()\n })\n // The pointer position lives on `inputs`, which is not reactive.\n stopEvents = editor.on?.(\"event\", poke) ?? null\n heartbeat = setInterval(() => flush(true), heartbeatMs)\n flush(true)\n },\n stop() {\n running = false\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n if (heartbeat !== null) {\n clearInterval(heartbeat)\n heartbeat = null\n }\n stopReactor?.()\n stopReactor = null\n stopEvents?.()\n stopEvents = null\n },\n poke,\n getLastSent: () => last,\n dispose() {\n this.stop()\n last = null\n },\n }\n}\n\n/** Equal apart from `lastActivityTimestamp`, which always moves. */\nexport function isSamePresence(a: InstancePresence, b: InstancePresence): boolean {\n return (\n a.userId === b.userId &&\n a.userName === b.userName &&\n a.color === b.color &&\n a.currentPageId === b.currentPageId &&\n a.chatMessage === b.chatMessage &&\n a.followingUserId === b.followingUserId &&\n sameCursor(a.cursor, b.cursor) &&\n sameCamera(a.camera, b.camera) &&\n sameIds(a.selectedShapeIds, b.selectedShapeIds) &&\n sameBrush(a.brush, b.brush) &&\n a.scribbles.length === b.scribbles.length &&\n a.scribbles.every((s, i) => s === b.scribbles[i])\n )\n}\n\n/** Cameras compare equal when both are absent, or when all three fields match. */\nfunction sameCamera(a: InstancePresence[\"camera\"], b: InstancePresence[\"camera\"]): boolean {\n if (a === null || b === null) return a === b\n return a.x === b.x && a.y === b.y && a.z === b.z\n}\n\n/** Cursors compare equal when both are absent, or when all four fields match. */\nfunction sameCursor(a: InstancePresence[\"cursor\"], b: InstancePresence[\"cursor\"]): boolean {\n if (a === null || b === null) return a === b\n return a.x === b.x && a.y === b.y && a.type === b.type && a.rotation === b.rotation\n}\n\nfunction sameIds(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((id, i) => id === b[i])\n}\n\nfunction sameBrush(a: InstancePresence[\"brush\"], b: InstancePresence[\"brush\"]): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h\n}\n\n/**\n * Holds the presence records of everyone else: puts them in the store as they\n * arrive, drops them on `bye`, and sweeps clients that went quiet.\n */\nexport class PresenceRoom<R extends UnknownRecord> {\n private readonly seen = new Map<string, { id: string; at: number }>()\n private sweeper: ReturnType<typeof setInterval> | null = null\n\n constructor(\n private readonly store: Store<R, any>,\n private readonly timeoutMs: number = DEFAULT_PRESENCE_TIMEOUT_MS,\n ) {}\n\n /** Store an incoming presence record and remember when we saw its client. */\n receive(clientId: string, record: R): void {\n const previous = this.seen.get(clientId)\n if (previous && previous.id !== record.id) this.removeRecord(previous.id)\n this.seen.set(clientId, { id: record.id, at: Date.now() })\n this.store.mergeRemoteChanges(() => {\n this.store.put([record])\n })\n }\n\n /** Drop one client's presence (it said goodbye). */\n remove(clientId: string): void {\n const entry = this.seen.get(clientId)\n if (!entry) return\n this.seen.delete(clientId)\n this.removeRecord(entry.id)\n }\n\n /** Drop every client we have not heard from within the timeout. */\n sweep(now: number = Date.now()): void {\n const stale: string[] = []\n for (const [clientId, entry] of this.seen) {\n if (now - entry.at > this.timeoutMs) stale.push(clientId)\n }\n for (const clientId of stale) this.remove(clientId)\n }\n\n startSweeping(intervalMs: number = Math.max(1000, Math.floor(this.timeoutMs / 4))): void {\n if (this.sweeper !== null) return\n this.sweeper = setInterval(() => this.sweep(), intervalMs)\n }\n\n stopSweeping(): void {\n if (this.sweeper === null) return\n clearInterval(this.sweeper)\n this.sweeper = null\n }\n\n /** Remove every record this room put in the store. */\n clear(): void {\n const ids = Array.from(this.seen.values(), (entry) => entry.id)\n this.seen.clear()\n for (const id of ids) this.removeRecord(id)\n }\n\n private removeRecord(id: string): void {\n const recordId = id as IdOf<R>\n if (!this.store.unsafeGetWithoutCapture(recordId)) return\n this.store.mergeRemoteChanges(() => {\n this.store.remove([recordId])\n })\n }\n}\n","import type { UnknownRecord } from \"@mocanvas/store\"\nimport { isCrdtStateLike, isStampedDiffLike, type CrdtState, type StampedDiff } from \"./crdt\"\n\n/**\n * Bumped when the wire format changes incompatibly.\n *\n * 2: `diff` carries a `StampedDiff` (per-field Lamport stamps) instead of a\n * plain `RecordsDiff`, `snapshot` carries the CRDT state next to the\n * records, and every message carries `version`.\n * 3: a put carries `base`, the stamp its whole record body is current as of,\n * and a record kept alive by an edit that outranks a delete is re-claimed\n * whole under a fresh stamp. A version 2 peer merges the same messages to a\n * different answer, so the two must not share a room.\n */\nexport const PROTOCOL_VERSION = 3\n\n/**\n * Every message is one JSON object with a `type` discriminator and a\n * `version`, which `encodeMessage` stamps on and `decodeMessage` checks.\n *\n * ```\n * hello a client joined; existing peers answer with a snapshot\n * snapshot the document-scope records plus the sender's CRDT stamps\n * diff one squashed store diff, stamped field by field\n * presence one presence record (cursor, camera, selection)\n * bye the client is leaving; drop its presence\n * unsupported synthesised locally for a peer on another protocol version\n * ```\n */\nexport type SyncMessage<R extends UnknownRecord = UnknownRecord> =\n | HelloMessage\n | SnapshotMessage<R>\n | DiffMessage<R>\n | PresenceMessage<R>\n | ByeMessage\n | UnsupportedMessage\n\nexport interface HelloMessage {\n type: \"hello\"\n clientId: string\n version: number\n}\n\nexport interface SnapshotMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"snapshot\"\n records: R[]\n /** The stamps that go with those records, so a joiner does not start blank. */\n state: CrdtState\n}\n\nexport interface DiffMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"diff\"\n clientId: string\n /** Per-client counter; only used for logging and de-duplication. */\n seq: number\n diff: StampedDiff<R>\n}\n\nexport interface PresenceMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"presence\"\n clientId: string\n record: R\n}\n\nexport interface ByeMessage {\n type: \"bye\"\n clientId: string\n}\n\n/**\n * Never sent: `decodeMessage` returns this instead of guessing at a message\n * whose `version` is not ours, so a peer on another protocol is reported once\n * rather than half-parsed.\n */\nexport interface UnsupportedMessage {\n type: \"unsupported\"\n version: number\n clientId?: string\n}\n\nexport function encodeMessage<R extends UnknownRecord>(message: SyncMessage<R>): string {\n return JSON.stringify({ ...message, version: PROTOCOL_VERSION })\n}\n\n/**\n * Parse a message off the wire. Returns `null` for anything malformed and an\n * `unsupported` message for a peer on another protocol version: neither can\n * crash this client, and neither is ever mistaken for a message we understand.\n */\nexport function decodeMessage<R extends UnknownRecord = UnknownRecord>(data: unknown): SyncMessage<R> | null {\n let value: unknown = data\n if (typeof data === \"string\") {\n try {\n value = JSON.parse(data)\n } catch {\n return null\n }\n }\n if (typeof value !== \"object\" || value === null) return null\n const message = value as Record<string, unknown>\n\n const version = message[\"version\"]\n if (typeof version === \"number\" && version !== PROTOCOL_VERSION) {\n const clientId = message[\"clientId\"]\n return typeof clientId === \"string\"\n ? { type: \"unsupported\", version, clientId }\n : { type: \"unsupported\", version }\n }\n\n switch (message[\"type\"]) {\n case \"hello\":\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (typeof message[\"version\"] !== \"number\") return null\n return { type: \"hello\", clientId: message[\"clientId\"], version: message[\"version\"] }\n case \"snapshot\": {\n const records = message[\"records\"]\n if (!Array.isArray(records)) return null\n if (!records.every(isRecordLike)) return null\n const state = message[\"state\"]\n if (!isCrdtStateLike(state)) return null\n return { type: \"snapshot\", records: records as R[], state: state as CrdtState }\n }\n case \"diff\": {\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (typeof message[\"seq\"] !== \"number\") return null\n if (!isStampedDiffLike(message[\"diff\"])) return null\n return {\n type: \"diff\",\n clientId: message[\"clientId\"],\n seq: message[\"seq\"],\n diff: message[\"diff\"] as StampedDiff<R>,\n }\n }\n case \"presence\": {\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (!isRecordLike(message[\"record\"])) return null\n return { type: \"presence\", clientId: message[\"clientId\"], record: message[\"record\"] as R }\n }\n case \"bye\":\n if (typeof message[\"clientId\"] !== \"string\") return null\n return { type: \"bye\", clientId: message[\"clientId\"] }\n default:\n return null\n }\n}\n\nfunction isRecordLike(value: unknown): boolean {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { id?: unknown }).id === \"string\" &&\n typeof (value as { typeName?: unknown }).typeName === \"string\"\n )\n}\n","import { atom, type Signal } from \"@mocanvas/state\"\nimport {\n createEmptyRecordsDiff,\n isRecordsDiffEmpty,\n uniqueId,\n type IdOf,\n type RecordsDiff,\n type Store,\n type UnknownRecord,\n} from \"@mocanvas/store\"\nimport { createCrdt, deepEqual, isStampedDiffEmpty, stampedDiffFromSnapshot, type Crdt, type StampedDiff } from \"./crdt\"\nimport {\n createPresenceSync,\n DEFAULT_PRESENCE_TIMEOUT_MS,\n PresenceRoom,\n type PresenceEditor,\n type PresenceSync,\n} from \"./presence\"\nimport { PROTOCOL_VERSION, type SyncMessage } from \"./protocol\"\nimport type { Transport } from \"./transport\"\n\nexport type SyncStatus = \"offline\" | \"connecting\" | \"online\"\n\nexport interface SyncClientOptions<R extends UnknownRecord = UnknownRecord> {\n store: Store<R, any>\n /** Identifies the document. The transport is expected to be scoped to it too. */\n roomId: string\n transport: Transport<R>\n /** Turn on cursor/selection sharing for an editor. */\n presence?: { editor: PresenceEditor } | undefined\n /** Defaults to a fresh random id; one per tab, not per user. */\n clientId?: string | undefined\n /** Drop a collaborator after this long without a presence message. Default 10s. */\n presenceTimeoutMs?: number | undefined\n /** Upper bound on presence send rate. Default 34ms (~30 Hz). */\n presenceThrottleMs?: number | undefined\n presenceHeartbeatMs?: number | undefined\n /** How many tombstones for vanished records to keep. See `crdt.ts`. */\n tombstoneLimit?: number | undefined\n /** How long such a tombstone lives, in ms. */\n tombstoneMaxAgeMs?: number | undefined\n /** Called for protocol-level problems (a peer on another version, say). */\n onError?: ((error: Error) => void) | undefined\n}\n\nexport interface SyncClient {\n readonly clientId: string\n readonly roomId: string\n connect(): void\n disconnect(): void\n /** A signal, so React and reactors can follow the connection. */\n getStatus(): Signal<SyncStatus>\n dispose(): void\n}\n\n/**\n * Joins one room over `transport` and keeps `store` in step with its peers.\n *\n * Document changes made locally by the user are stamped by the CRDT (see\n * `crdt.ts`) and broadcast; incoming messages are merged field by field and\n * only the fields that won are applied, as remote changes, so they neither\n * echo back nor land in the undo stack. Concurrent edits to different fields\n * of one record all survive, and replicas converge whatever order messages\n * arrive in.\n */\nexport function createSyncClient<R extends UnknownRecord = UnknownRecord>(\n options: SyncClientOptions<R>,\n): SyncClient {\n const { store, roomId, transport } = options\n const clientId = options.clientId ?? uniqueId(12)\n const status = atom<SyncStatus>(`sync.status:${roomId}`, \"offline\")\n const room = new PresenceRoom<R>(store, options.presenceTimeoutMs ?? DEFAULT_PRESENCE_TIMEOUT_MS)\n\n const crdt: Crdt<R> = createCrdt<R>({\n clientId,\n getRecord: (id) => store.unsafeGetWithoutCapture(id as IdOf<R>) as R | undefined,\n ...(options.tombstoneLimit !== undefined ? { tombstoneLimit: options.tombstoneLimit } : {}),\n ...(options.tombstoneMaxAgeMs !== undefined ? { tombstoneMaxAgeMs: options.tombstoneMaxAgeMs } : {}),\n })\n\n const unsubscribes: (() => void)[] = []\n const reportedVersions = new Set<number>()\n let presence: PresenceSync | null = null\n let seq = 0\n /**\n * True while this replica is waiting to be handed the room, which only a\n * replica holding no stamps at all ever is: one that has just loaded the\n * page has nothing to lose by taking a peer's document whole, and nothing\n * worth pushing at anyone either. A replica with stamps — including one that\n * edited while it was disconnected — merges instead.\n */\n let awaitingSnapshot = false\n let connected = false\n let disposed = false\n\n const send = (message: SyncMessage<R>) => {\n try {\n transport.send(message)\n } catch (error) {\n options.onError?.(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n const broadcast = (stamped: StampedDiff<R>) => {\n if (isStampedDiffEmpty(stamped)) return\n send({ type: \"diff\", clientId, seq: seq++, diff: stamped })\n }\n\n /**\n * Writes the CRDT minted while merging — a record revived by an edit that\n * outranks a delete — go out like any other local diff.\n */\n const flushCrdt = () => {\n const outgoing = crdt.takeOutgoing()\n if (outgoing) broadcast(outgoing)\n }\n\n const sendSnapshot = () => {\n send({\n type: \"snapshot\",\n records: Object.values(store.serialize(\"document\")) as R[],\n state: crdt.getState(),\n })\n }\n\n const applyRemote = (diff: RecordsDiff<R>) => {\n if (isRecordsDiffEmpty(diff)) return\n const actual = store.extractingChanges(() => {\n store.mergeRemoteChanges(() => {\n store.applyDiff(diff)\n })\n })\n reportRewrites(diff, actual)\n }\n\n /**\n * What the store actually did, minus what we asked it to do. A validator or\n * a side effect that rewrites a merged record — or touches another one —\n * changes this replica only: the change is reported as `remote`, so the\n * listener below never sees it, and no peer would ever hear about it. Stamp\n * that difference as a local write and send it, so the rewrite is one more\n * write everyone can agree on rather than a private divergence.\n *\n * A side effect that is a function of the record and the document rewrites\n * the same way everywhere, so this settles in one round: the peers apply a\n * value they already hold and produce nothing further. A side effect that is\n * not a function of its inputs cannot converge, here or anywhere.\n */\n const reportRewrites = (asked: RecordsDiff<R>, actual: RecordsDiff<R>) => {\n const rewrite = createEmptyRecordsDiff<R>()\n const intended = (id: IdOf<R>): R | undefined => asked.added[id] ?? asked.updated[id]?.[1]\n const isDocument = (record: R) => store.getScope(record.typeName) === \"document\"\n\n for (const key in actual.added) {\n const id = key as IdOf<R>\n const after = actual.added[id]\n if (!after || !isDocument(after)) continue\n const want = intended(id)\n if (!want) rewrite.added[id] = after\n else if (!deepEqual(want, after)) rewrite.updated[id] = [want, after]\n }\n for (const key in actual.updated) {\n const id = key as IdOf<R>\n const pair = actual.updated[id]\n if (!pair || !isDocument(pair[1])) continue\n const want = intended(id)\n if (!want) rewrite.updated[id] = pair\n else if (!deepEqual(want, pair[1])) rewrite.updated[id] = [want, pair[1]]\n }\n for (const key in actual.removed) {\n const id = key as IdOf<R>\n const before = actual.removed[id]\n if (!before || !isDocument(before)) continue\n if (!asked.removed[id]) rewrite.removed[id] = before\n }\n if (isRecordsDiffEmpty(rewrite)) return\n broadcast(crdt.stampLocal(rewrite))\n }\n\n /**\n * Outgoing document changes: only what this user did, never what we applied\n * from a peer (those arrive with source \"remote\"). It is wired up when the\n * client is built and stays wired until `dispose`, so an edit made before\n * `connect()` or after `disconnect()` is stamped when it happens rather than\n * when the socket is up. That is what carries offline work through a\n * reconnect: unstamped, it would lose to every stamped write in the room.\n */\n const stopListening = store.listen(\n (entry) => {\n if (isRecordsDiffEmpty(entry.changes)) return\n const stamped = crdt.stampLocal(entry.changes)\n if (connected) broadcast(stamped)\n },\n { source: \"user\", scope: \"document\" },\n )\n\n const handle = (message: SyncMessage<R>) => {\n if (disposed) return\n switch (message.type) {\n case \"unsupported\": {\n if (reportedVersions.has(message.version)) return\n reportedVersions.add(message.version)\n options.onError?.(\n new Error(`A peer speaks protocol ${message.version}, we speak ${PROTOCOL_VERSION}`),\n )\n return\n }\n case \"hello\": {\n if (message.clientId === clientId) return\n if (message.version !== PROTOCOL_VERSION) {\n options.onError?.(\n new Error(`Peer ${message.clientId} speaks protocol ${message.version}, we speak ${PROTOCOL_VERSION}`),\n )\n return\n }\n // A newcomer needs the document; every established peer answers with\n // its records and its stamps. Answering also makes us established: we\n // hold state of our own now, so nobody's snapshot can start us blank.\n awaitingSnapshot = false\n sendSnapshot()\n // Re-announce our presence so the newcomer sees us immediately.\n presence?.poke()\n return\n }\n case \"snapshot\": {\n // Wholesale adoption is only safe with nothing of our own to lose:\n // one stamp means one edit that would be overwritten by the sender's\n // body, so `crdt.size()` and not just the flag decides.\n if (awaitingSnapshot && crdt.size() === 0) {\n // We have no stamps at all. Take the sender's records and its clock\n // wholesale, so our own later writes sort after everything already\n // in the room instead of racing it from zero.\n awaitingSnapshot = false\n const removals = crdt.applyState(message.state)\n const actual = store.extractingChanges(() => {\n store.mergeRemoteChanges(() => {\n store.put(message.records)\n store.applyDiff(removals)\n })\n })\n const asked = createEmptyRecordsDiff<R>()\n for (const record of message.records) asked.added[record.id as IdOf<R>] = record\n reportRewrites(asked, actual)\n flushCrdt()\n return\n }\n // Otherwise this is a reconciliation (a peer reconnected, or answered\n // someone else's hello), or we hold edits of our own: merge it field\n // by field. Nothing we hold a newer stamp for can be overwritten, so\n // it is always safe to apply.\n awaitingSnapshot = false\n applyRemote(crdt.mergeRemote(stampedDiffFromSnapshot(message.records, message.state)))\n flushCrdt()\n return\n }\n case \"diff\": {\n if (message.clientId === clientId) return\n if (isStampedDiffEmpty(message.diff)) return\n applyRemote(crdt.mergeRemote(message.diff))\n flushCrdt()\n return\n }\n case \"presence\": {\n if (message.clientId === clientId) return\n room.receive(message.clientId, message.record)\n return\n }\n case \"bye\": {\n if (message.clientId === clientId) return\n room.remove(message.clientId)\n return\n }\n }\n }\n\n const onOpen = () => {\n if (disposed || !connected) return\n status.set(\"online\")\n send({ type: \"hello\", clientId, version: PROTOCOL_VERSION })\n // On a reconnect we already hold stamps, possibly for work done while the\n // transport was down. Push them: the peers merge field by field, so\n // neither side of the split loses anything.\n if (!awaitingSnapshot) sendSnapshot()\n presence?.poke()\n }\n\n const onClose = () => {\n if (disposed || !connected) return\n // The transport reconnects on its own; from here it is just \"not online\".\n // Local edits keep flowing into the CRDT and go out on the next open.\n status.set(\"connecting\")\n }\n\n return {\n clientId,\n roomId,\n\n connect() {\n if (disposed || connected) return\n connected = true\n // Only a replica holding no stamps at all is a newcomer. One that edited\n // before it ever connected, or while it was disconnected, has work of\n // its own and merges the room's snapshot instead of taking it whole.\n awaitingSnapshot = crdt.size() === 0\n status.set(\"connecting\")\n\n unsubscribes.push(transport.onMessage(handle))\n if (transport.onOpen) unsubscribes.push(transport.onOpen(onOpen))\n if (transport.onClose) unsubscribes.push(transport.onClose(onClose))\n\n if (options.presence) {\n presence = createPresenceSync({\n editor: options.presence.editor,\n clientId,\n send: (record) => send({ type: \"presence\", clientId, record: record as unknown as R }),\n ...(options.presenceThrottleMs !== undefined ? { throttleMs: options.presenceThrottleMs } : {}),\n ...(options.presenceHeartbeatMs !== undefined ? { heartbeatMs: options.presenceHeartbeatMs } : {}),\n })\n presence.start()\n }\n room.startSweeping()\n\n if (!transport.onOpen) onOpen()\n },\n\n disconnect() {\n if (!connected) return\n connected = false\n send({ type: \"bye\", clientId })\n presence?.stop()\n presence = null\n room.stopSweeping()\n room.clear()\n for (const off of unsubscribes.splice(0)) off()\n status.set(\"offline\")\n },\n\n getStatus() {\n return status\n },\n\n dispose() {\n if (disposed) return\n this.disconnect()\n disposed = true\n stopListening()\n transport.close()\n },\n }\n}\n","import type { UnknownRecord } from \"@mocanvas/store\"\nimport { decodeMessage, encodeMessage, type SyncMessage } from \"./protocol\"\n\n/**\n * A duplex channel that carries `SyncMessage`s between peers in one room.\n *\n * The transport owns serialization and reconnection; the client above it only\n * sees decoded messages. `onOpen` fires every time the channel becomes usable\n * (including after a reconnect), `onClose` every time it stops being usable.\n * A transport with neither is treated as open from the moment it is connected.\n */\nexport interface Transport<R extends UnknownRecord = UnknownRecord> {\n send(message: SyncMessage<R>): void\n onMessage(callback: (message: SyncMessage<R>) => void): () => void\n onOpen?(callback: () => void): () => void\n onClose?(callback: () => void): () => void\n close(): void\n}\n\n/** @internal A tiny callback set with an unsubscribe function. */\nexport function createEmitter(): {\n add(cb: () => void): () => void\n emit(): void\n clear(): void\n} {\n const callbacks = new Set<() => void>()\n return {\n add(cb) {\n callbacks.add(cb)\n return () => {\n callbacks.delete(cb)\n }\n },\n emit() {\n for (const cb of Array.from(callbacks)) cb()\n },\n clear() {\n callbacks.clear()\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* BroadcastChannel */\n/* -------------------------------------------------------------------------- */\n\nexport interface BroadcastChannelTransportOptions {\n /** Channel name prefix, so two apps on one origin do not collide. */\n prefix?: string\n}\n\n/**\n * Same-origin tabs of one browser. There is no server, so every peer is\n * equally authoritative and the channel is open as soon as it is created.\n */\nexport function createBroadcastChannelTransport<R extends UnknownRecord = UnknownRecord>(\n roomId: string,\n options: BroadcastChannelTransportOptions = {},\n): Transport<R> {\n const name = `${options.prefix ?? \"mocanvas-sync\"}:${roomId}`\n const channel = new BroadcastChannel(name)\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n let closed = false\n\n channel.onmessage = (event: MessageEvent) => {\n const message = decodeMessage<R>(event.data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n }\n\n return {\n send(message) {\n if (closed) return\n channel.postMessage(encodeMessage(message))\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n // Already usable; hand control back to the caller before firing so that\n // `connect()` has finished wiring itself up.\n let cancelled = false\n queueMicrotask(() => {\n if (!cancelled && !closed) callback()\n })\n return () => {\n cancelled = true\n }\n },\n close() {\n closed = true\n listeners.clear()\n channel.onmessage = null\n channel.close()\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* WebSocket */\n/* -------------------------------------------------------------------------- */\n\nexport interface WebSocketTransportOptions {\n /** Reconnect with exponential backoff after an unexpected close. Default `true`. */\n reconnect?: boolean\n /** First backoff delay in ms. Default 500. */\n minDelayMs?: number\n /** Backoff ceiling in ms. Default 15000. */\n maxDelayMs?: number\n /** Injectable for tests and for Node (`ws`). Defaults to `globalThis.WebSocket`. */\n WebSocketImpl?: typeof WebSocket\n}\n\n/**\n * A WebSocket to a relay (see `scripts/relay.mjs`). Messages sent while the\n * socket is down are dropped rather than queued: the next `hello`/`snapshot`\n * exchange re-establishes the document anyway, and presence is re-sent on a\n * heartbeat.\n */\nexport function createWebSocketTransport<R extends UnknownRecord = UnknownRecord>(\n url: string,\n options: WebSocketTransportOptions = {},\n): Transport<R> {\n const reconnect = options.reconnect ?? true\n const minDelay = options.minDelayMs ?? 500\n const maxDelay = options.maxDelayMs ?? 15_000\n const Impl = options.WebSocketImpl ?? (globalThis as { WebSocket?: typeof WebSocket }).WebSocket\n if (!Impl) throw new Error(\"No WebSocket implementation available; pass options.WebSocketImpl\")\n\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n const open = createEmitter()\n const close = createEmitter()\n\n let socket: WebSocket | null = null\n let attempt = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n let disposed = false\n\n const connect = () => {\n if (disposed) return\n const ws = new Impl(url)\n socket = ws\n ws.onopen = () => {\n attempt = 0\n open.emit()\n }\n ws.onmessage = (event: MessageEvent) => {\n const message = decodeMessage<R>(event.data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n }\n ws.onerror = () => {\n // `onclose` always follows; the retry is scheduled there.\n }\n ws.onclose = () => {\n if (socket === ws) socket = null\n close.emit()\n if (!disposed && reconnect) schedule()\n }\n }\n\n const schedule = () => {\n if (timer !== null) return\n // Exponential backoff with full jitter, so a relay restart does not get a\n // thundering herd from every open tab.\n const ceiling = Math.min(maxDelay, minDelay * 2 ** attempt)\n attempt++\n const delay = Math.random() * ceiling\n timer = setTimeout(() => {\n timer = null\n connect()\n }, delay)\n }\n\n connect()\n\n return {\n send(message) {\n if (!socket || socket.readyState !== 1 /* OPEN */) return\n socket.send(encodeMessage(message))\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n const remove = open.add(callback)\n if (socket && socket.readyState === 1) queueMicrotask(callback)\n return remove\n },\n onClose(callback) {\n return close.add(callback)\n },\n close() {\n disposed = true\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n listeners.clear()\n open.clear()\n close.clear()\n const ws = socket\n socket = null\n if (ws) {\n ws.onopen = null\n ws.onmessage = null\n ws.onerror = null\n ws.onclose = null\n if (ws.readyState === 0 || ws.readyState === 1) ws.close()\n }\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* in-memory */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Two transports wired to each other, going through the same JSON encode and\n * decode as the real ones. Delivery is asynchronous (a microtask) so that a\n * peer never observes a message inside its own `send()` call stack.\n */\nexport function createMemoryTransportPair<R extends UnknownRecord = UnknownRecord>(): [Transport<R>, Transport<R>] {\n const hub = createMemoryHub<R>()\n return [hub.join(), hub.join()]\n}\n\nexport interface MemoryHub<R extends UnknownRecord = UnknownRecord> {\n /** Add another peer to the room. */\n join(): Transport<R>\n}\n\n/** A room every joined transport broadcasts into (itself excluded). */\nexport function createMemoryHub<R extends UnknownRecord = UnknownRecord>(): MemoryHub<R> {\n const peers = new Set<{ deliver(data: string): void }>()\n return {\n join(): Transport<R> {\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n let closed = false\n const peer = {\n deliver(data: string) {\n if (closed) return\n const message = decodeMessage<R>(data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n },\n }\n peers.add(peer)\n return {\n send(message) {\n if (closed) return\n const data = encodeMessage(message)\n for (const other of Array.from(peers)) {\n if (other === peer) continue\n queueMicrotask(() => other.deliver(data))\n }\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n let cancelled = false\n queueMicrotask(() => {\n if (!cancelled && !closed) callback()\n })\n return () => {\n cancelled = true\n }\n },\n close() {\n closed = true\n listeners.clear()\n peers.delete(peer)\n },\n }\n },\n }\n}\n","import { track, useValue } from \"@mocanvas/state/react\"\nimport type { Editor, EditorRecord, InstancePresence, UnknownShape } from \"@mocanvas/editor\"\nimport { useEffect, useRef, useState, type CSSProperties } from \"react\"\nimport { createSyncClient, type SyncClient, type SyncClientOptions, type SyncStatus } from \"./SyncClient\"\nimport type { Transport } from \"./transport\"\n\nconst layerStyle: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n overflow: \"visible\",\n}\n\nexport interface CollaboratorCursorsProps {\n editor: Editor\n /** Draw what each collaborator has selected. Default `true`. */\n showSelection?: boolean\n /** Draw a name chip next to each cursor. Default `true`. */\n showNames?: boolean\n}\n\n/**\n * Other people's cursors and selections, drawn in screen space above the\n * canvas. Drop it inside `<Mocanvas>` or `<Canvas>`; it positions itself.\n */\nexport const CollaboratorCursors = track(function CollaboratorCursors({\n editor,\n showSelection = true,\n showNames = true,\n}: CollaboratorCursorsProps) {\n const collaborators = editor.getCollaboratorsOnCurrentPage()\n if (collaborators.length === 0) return null\n return (\n <svg className=\"mocanvas-collaborators\" style={layerStyle}>\n {collaborators.map((presence) => (\n <g key={presence.id}>\n {showSelection ? <CollaboratorSelection editor={editor} presence={presence} /> : null}\n <CollaboratorCursor editor={editor} presence={presence} showName={showNames} />\n </g>\n ))}\n </svg>\n )\n})\n\nconst CollaboratorCursor = track(function CollaboratorCursor({\n editor,\n presence,\n showName,\n}: {\n editor: Editor\n presence: InstancePresence\n showName: boolean\n}) {\n // A collaborator with no pointer — an agent, or someone whose pointer has left\n // the canvas — draws no cursor at all.\n if (presence.cursor === null) return null\n // Viewport space: the overlay is positioned inside the canvas container, not\n // the window.\n const point = editor.pageToViewport(presence.cursor)\n const name = presence.userName || \"Anonymous\"\n // A 16x22 arrow drawn from the hotspot, then the name chip below it.\n return (\n <g transform={`translate(${point.x}, ${point.y}) rotate(${presence.cursor.rotation})`}>\n <path\n d=\"M0 0 L0 17.3 L4.3 13.4 L7 19.4 L9.9 18 L7.2 12.2 L12.8 12 Z\"\n fill={presence.color}\n stroke=\"#fff\"\n strokeWidth={1}\n strokeLinejoin=\"round\"\n />\n {showName ? (\n <g transform=\"translate(11, 18)\">\n <rect rx={4} ry={4} width={Math.max(24, name.length * 7 + 12)} height={18} fill={presence.color} />\n <text x={6} y={13} fontSize={11} fontFamily=\"system-ui, sans-serif\" fill=\"#fff\">\n {name}\n </text>\n </g>\n ) : null}\n </g>\n )\n})\n\nconst CollaboratorSelection = track(function CollaboratorSelection({\n editor,\n presence,\n}: {\n editor: Editor\n presence: InstancePresence\n}) {\n const outlines: string[] = []\n for (const id of presence.selectedShapeIds) {\n const shape: UnknownShape | undefined = editor.getShape(id)\n if (!shape) continue\n const bounds = editor.getShapeGeometryBounds(shape)\n if (!bounds) continue\n const m = editor.getShapePageTransform(shape)\n // Transform the local corners to page space, then to screen space, so the\n // outline follows rotation without a nested SVG transform.\n const corners: [number, number][] = [\n [bounds.x, bounds.y],\n [bounds.maxX, bounds.y],\n [bounds.maxX, bounds.maxY],\n [bounds.x, bounds.maxY],\n ]\n const points = corners.map(([x, y]) => {\n // Viewport space, like the cursor above: this overlay lives in the canvas\n // container, so window offsets must not be added.\n const p = editor.pageToViewport({ x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f })\n return `${p.x},${p.y}`\n })\n outlines.push(points.join(\" \"))\n }\n if (outlines.length === 0) return null\n return (\n <>\n {outlines.map((points, i) => (\n <polygon key={i} points={points} fill=\"none\" stroke={presence.color} strokeWidth={1.5} opacity={0.8} />\n ))}\n </>\n )\n})\n\nexport interface UseSyncOptions {\n roomId: string\n /**\n * The channel to the room. Pass a factory when the transport should be\n * recreated with the room (the usual case); it is read once per connection.\n */\n transport: Transport | (() => Transport)\n /** Share this editor's cursor and selection. Default `true`. */\n presence?: boolean\n /** Set `false` to stay offline (a read-only view, say). Default `true`. */\n enabled?: boolean\n clientId?: string\n presenceTimeoutMs?: number\n presenceThrottleMs?: number\n onError?: (error: Error) => void\n}\n\nexport interface UseSyncResult {\n status: SyncStatus\n client: SyncClient | null\n}\n\n/**\n * Connect an editor's store to a room for as long as the component is mounted.\n * Everything is torn down (including the transport) on unmount.\n */\nexport function useSync(editor: Editor | null, options: UseSyncOptions): UseSyncResult {\n const { roomId, enabled = true, presence = true } = options\n // Kept in a ref so that inline factories and callbacks do not reconnect.\n const latest = useRef(options)\n latest.current = options\n\n const [client, setClient] = useState<SyncClient | null>(null)\n\n useEffect(() => {\n if (!editor || !enabled) {\n setClient(null)\n return\n }\n const current = latest.current\n const transport = typeof current.transport === \"function\" ? current.transport() : current.transport\n const clientOptions: SyncClientOptions<EditorRecord> = {\n store: editor.store,\n roomId,\n transport: transport as Transport<EditorRecord>,\n ...(presence ? { presence: { editor } } : {}),\n ...(current.clientId !== undefined ? { clientId: current.clientId } : {}),\n ...(current.presenceTimeoutMs !== undefined ? { presenceTimeoutMs: current.presenceTimeoutMs } : {}),\n ...(current.presenceThrottleMs !== undefined ? { presenceThrottleMs: current.presenceThrottleMs } : {}),\n onError: (error: Error) => latest.current.onError?.(error),\n }\n const next = createSyncClient<EditorRecord>(clientOptions)\n setClient(next)\n next.connect()\n return () => {\n setClient(null)\n next.dispose()\n }\n }, [editor, roomId, enabled, presence])\n\n const status = useValue(\"sync.status\", () => client?.getStatus().get() ?? \"offline\", [client])\n return { status, client }\n}\n"]}
1
+ {"version":3,"sources":["../src/crdt.ts","../src/presence.ts","../src/protocol.ts","../src/SyncClient.ts","../src/transport.ts","../src/react.tsx"],"names":["createEmptyRecordsDiff","CollaboratorCursors","CollaboratorCursor","CollaboratorSelection"],"mappings":"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","file":"index.js","sourcesContent":["import { createEmptyRecordsDiff, type IdOf, type RecordsDiff, type UnknownRecord } from \"@mocanvas/store\"\n\n/**\n * A state-based CRDT over record fields.\n *\n * Every leaf field of every record is its own last-writer-wins register,\n * keyed by a dotted path (`x`, `props.w`, `meta.notes.title`). A write carries\n * a Lamport stamp; the greater stamp wins, and equal Lamport values are broken\n * by client id so that both sides of a race reach the same answer from the\n * same pair. Deletion is a stamped tombstone rather than a removal, so a late\n * message cannot resurrect a record that was deleted after it was written.\n *\n * Consequences worth knowing before reading the code:\n *\n * - Arrays are opaque. `props.points` is one register holding the whole array,\n * not one register per element. Two concurrent edits to one array pick a\n * winner instead of merging; splitting arrays into elements would need\n * element identity, which record data does not carry.\n * - A stamp covers a whole subtree. Writing `meta` (setting it, or deleting\n * the key) dominates `meta.a`: the claim loses to any stamp at or above its\n * own path, and dropping a subtree keeps descendants stamped later than the\n * write. Without that, `delete meta` racing `set meta.a` lands differently\n * depending on arrival order.\n * - A record also has one `base` register: the stamp at which some sender's\n * whole record body was current. Fields nobody holds a stamp for — a record\n * materialized without its creation message, or one that came back from\n * under a tombstone, which erases field stamps — are governed by that single\n * register, so every replica takes its body from the same message instead of\n * from whichever one happened to arrive first.\n * - Merging is idempotent (the same message twice is a no-op the second time)\n * and commutative (per path the result is the maximum stamp, which does not\n * depend on arrival order).\n * - The merge is a pure function of the stamps; values only ever ride along.\n */\n\n/** Version of the `CrdtState` blob on the wire. */\nexport const CRDT_STATE_VERSION = 1\n\n/** How many collected-and-gone records keep a tombstone before it is dropped. */\nexport const DEFAULT_TOMBSTONE_LIMIT = 5000\n/** How long a tombstone for a vanished record is kept. */\nexport const DEFAULT_TOMBSTONE_MAX_AGE_MS = 60 * 60 * 1000\n\n/* -------------------------------------------------------------------------- */\n/* stamps */\n/* -------------------------------------------------------------------------- */\n\n/** A point in the partial order of writes, made total by the client tiebreak. */\nexport interface Stamp {\n readonly lamport: number\n readonly client: string\n}\n\n/**\n * A total order on stamps: Lamport first, then client id. Two peers holding\n * the same pair always agree, which is what makes the merge deterministic.\n */\nexport function compareStamps(a: Stamp, b: Stamp): number {\n if (a.lamport !== b.lamport) return a.lamport < b.lamport ? -1 : 1\n if (a.client === b.client) return 0\n return a.client < b.client ? -1 : 1\n}\n\nexport function isStamp(value: unknown): value is Stamp {\n if (typeof value !== \"object\" || value === null) return false\n const stamp = value as { lamport?: unknown; client?: unknown }\n return typeof stamp.lamport === \"number\" && Number.isFinite(stamp.lamport) && typeof stamp.client === \"string\"\n}\n\nexport interface LamportClock {\n /** The current value. */\n get(): number\n /** Advance for a local write and return the new value. */\n tick(): number\n /** Fold in a stamp seen from a peer: `max(local, remote) + 1`. */\n observe(remote: number): void\n}\n\nexport function createLamportClock(start = 0): LamportClock {\n let value = start\n return {\n get: () => value,\n tick: () => (value += 1),\n observe(remote: number) {\n if (!Number.isFinite(remote)) return\n value = Math.max(value, remote) + 1\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* wire shapes */\n/* -------------------------------------------------------------------------- */\n\n/**\n * One record's changed fields. `record` is the whole record as the sender saw\n * it after the change; `fields` says which paths this message actually claims.\n * A receiver that already has the record takes only the winning paths out of\n * `record`; a receiver that does not have it materializes from `record` whole.\n */\nexport interface StampedPut<R extends UnknownRecord = UnknownRecord> {\n record: R\n fields: Record<string, Stamp>\n /**\n * The stamp at which `record` was the sender's whole view of that record.\n * Paths the receiver holds no stamp for are taken from `record` when this is\n * the greatest such stamp it has seen, and left alone otherwise.\n */\n base?: Stamp\n}\n\nexport interface StampedRemove {\n id: string\n stamp: Stamp\n}\n\n/** What a `diff` message carries: puts and removes, each field stamped. */\nexport interface StampedDiff<R extends UnknownRecord = UnknownRecord> {\n puts: StampedPut<R>[]\n removes: StampedRemove[]\n}\n\nexport function createEmptyStampedDiff<R extends UnknownRecord = UnknownRecord>(): StampedDiff<R> {\n return { puts: [], removes: [] }\n}\n\nexport function isStampedDiffEmpty<R extends UnknownRecord>(diff: StampedDiff<R>): boolean {\n return diff.puts.length === 0 && diff.removes.length === 0\n}\n\n/** Structural check used by `decodeMessage`; deliberately shallow but strict. */\nexport function isStampedDiffLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false\n const diff = value as { puts?: unknown; removes?: unknown }\n if (!Array.isArray(diff.puts) || !Array.isArray(diff.removes)) return false\n for (const put of diff.puts) {\n if (typeof put !== \"object\" || put === null) return false\n const { record, fields, base } = put as { record?: unknown; fields?: unknown; base?: unknown }\n if (typeof record !== \"object\" || record === null) return false\n if (typeof (record as { id?: unknown }).id !== \"string\") return false\n if (typeof (record as { typeName?: unknown }).typeName !== \"string\") return false\n if (!isStampMap(fields)) return false\n if (base !== undefined && !isStamp(base)) return false\n }\n for (const remove of diff.removes) {\n if (typeof remove !== \"object\" || remove === null) return false\n const { id, stamp } = remove as { id?: unknown; stamp?: unknown }\n if (typeof id !== \"string\" || !isStamp(stamp)) return false\n }\n return true\n}\n\n/** What one record's registers look like once serialized. */\nexport interface CrdtRecordState {\n fields: Record<string, Stamp>\n /** The stamp of the message this record's unstamped fields came from. */\n base?: Stamp\n deleted?: Stamp\n /** Local wall clock at the time the tombstone was taken, for collection. */\n deletedAt?: number\n}\n\n/** The whole replica's stamps, as carried by a `snapshot` message. */\nexport interface CrdtState {\n version: number\n clock: number\n records: Record<string, CrdtRecordState>\n}\n\nexport function createEmptyCrdtState(): CrdtState {\n return { version: CRDT_STATE_VERSION, clock: 0, records: {} }\n}\n\nexport function isCrdtStateLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false\n const state = value as { version?: unknown; clock?: unknown; records?: unknown }\n if (typeof state.version !== \"number\") return false\n if (typeof state.clock !== \"number\") return false\n if (typeof state.records !== \"object\" || state.records === null || Array.isArray(state.records)) return false\n for (const key of Object.keys(state.records as Record<string, unknown>)) {\n const record = (state.records as Record<string, unknown>)[key]\n if (typeof record !== \"object\" || record === null) return false\n const { fields, deleted, base } = record as { fields?: unknown; deleted?: unknown; base?: unknown }\n if (!isStampMap(fields)) return false\n if (deleted !== undefined && !isStamp(deleted)) return false\n if (base !== undefined && !isStamp(base)) return false\n }\n return true\n}\n\nfunction isStampMap(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false\n for (const key of Object.keys(value as Record<string, unknown>)) {\n if (!isStamp((value as Record<string, unknown>)[key])) return false\n }\n return true\n}\n\n/* -------------------------------------------------------------------------- */\n/* the CRDT */\n/* -------------------------------------------------------------------------- */\n\nexport interface CrdtOptions<R extends UnknownRecord = UnknownRecord> {\n /** Identifies this replica; also the tiebreak in `compareStamps`. */\n clientId: string\n /**\n * Read the replica's current value for a record. The CRDT owns the stamps,\n * never the values: the store stays the source of truth and a merge is\n * expressed as a diff against whatever `getRecord` reports.\n */\n getRecord?: ((id: string) => R | undefined) | undefined\n /** Tombstones kept for records that are gone. Default 5000. */\n tombstoneLimit?: number | undefined\n /** How long such a tombstone lives. Default one hour. */\n tombstoneMaxAgeMs?: number | undefined\n /** Injectable wall clock, for tests. */\n now?: (() => number) | undefined\n}\n\nexport interface Crdt<R extends UnknownRecord = UnknownRecord> {\n readonly clientId: string\n readonly clock: LamportClock\n /**\n * Stamp a diff the local user just made and return the message to send.\n * The whole diff shares one stamp: it was one operation.\n */\n stampLocal(diff: RecordsDiff<R>): StampedDiff<R>\n /**\n * Merge a peer's message. The returned diff holds only the fields that won,\n * as whole records the store can apply — never a blind overwrite.\n */\n mergeRemote(stamped: StampedDiff<R>): RecordsDiff<R>\n /** This replica's stamps, for a `snapshot` message. */\n getState(): CrdtState\n /**\n * Adopt a peer's stamps wholesale. This is what a replica that is joining\n * with no state of its own does, together with that peer's records: it takes\n * the peer's stamps instead of starting blank, so its own later writes sort\n * after everything already in the room. The returned diff removes whatever\n * the peer has tombstoned and we still hold. Only for a replica holding no\n * stamps at all: the caller adopts the peer's records wholesale alongside it,\n * so a replica with work of its own must merge the snapshot through\n * `stampedDiffFromSnapshot` instead of taking it.\n */\n applyState(state: CrdtState): RecordsDiff<R>\n /**\n * Writes this replica minted while merging, which the caller must broadcast.\n * A record kept alive by an edit that outranks a delete is re-claimed whole\n * under a fresh stamp: the tombstone leaves the fields it dominates with no\n * provenance at all, and only a stamped body can put every replica on the\n * same one. Empty when there is nothing to send.\n */\n takeOutgoing(): StampedDiff<R> | null\n /** Number of records the state currently tracks (tests and diagnostics). */\n size(): number\n /** Drop tombstones past the count or age bound. Called after every merge. */\n collectTombstones(): void\n}\n\ninterface RecordState {\n fields: Map<string, Stamp>\n /** The stamp of the message this record's unstamped fields came from. */\n base?: Stamp | undefined\n deleted?: Stamp | undefined\n deletedAt?: number | undefined\n}\n\n/** The greater of two stamps, either of which may be missing. */\nfunction maxStamp(a: Stamp | undefined, b: Stamp | undefined): Stamp | undefined {\n if (!a) return b\n if (!b) return a\n return compareStamps(a, b) >= 0 ? a : b\n}\n\n/** The greatest stamp a put claims, which is what its body is current as of. */\nfunction greatestStamp(fields: Record<string, Stamp>): Stamp | undefined {\n let best: Stamp | undefined\n for (const path of Object.keys(fields)) best = maxStamp(best, fields[path])\n return best\n}\n\n/** Every proper ancestor of a dotted path, outermost first. */\nfunction ancestorPaths(path: string): string[] {\n const out: string[] = []\n let index = path.indexOf(\".\")\n while (index !== -1) {\n out.push(path.slice(0, index))\n index = path.indexOf(\".\", index + 1)\n }\n return out\n}\n\n/**\n * Take `path` for `stamp`, unless something at or above it is stamped at least\n * as late — a write to `meta` covers `meta.a`, so the two cannot be compared\n * as if they were unrelated registers. Descendants the write covers are\n * dropped; descendants stamped later than it survive and are returned, because\n * their values have to be put back on top of whatever the write does.\n */\nfunction claimPath(state: RecordState, path: string, stamp: Stamp): string[] | null {\n const own = state.fields.get(path)\n if (own && compareStamps(stamp, own) <= 0) return null\n for (const ancestor of ancestorPaths(path)) {\n const above = state.fields.get(ancestor)\n if (above && compareStamps(stamp, above) <= 0) return null\n }\n const survivors: string[] = []\n const prefix = `${path}.`\n for (const [other, existing] of Array.from(state.fields)) {\n if (!other.startsWith(prefix)) continue\n if (compareStamps(existing, stamp) <= 0) state.fields.delete(other)\n else survivors.push(other)\n }\n state.fields.set(path, stamp)\n return survivors\n}\n\n/** True when this path, or one above it, carries a stamp of its own. */\nfunction isStamped(state: RecordState, path: string): boolean {\n if (state.fields.has(path)) return true\n for (const ancestor of ancestorPaths(path)) {\n if (state.fields.has(ancestor)) return true\n }\n return false\n}\n\n/**\n * The highest ancestor of `path` that the body has nothing at and no stamp\n * covers — the level a \"this is not here any more\" has to be applied at.\n */\nfunction highestAbsent(body: unknown, state: RecordState, path: string): string {\n for (const ancestor of ancestorPaths(path)) {\n if (readPath(body, ancestor) !== ABSENT) continue\n if (isStamped(state, ancestor) || hasStampedDescendant(state, ancestor)) continue\n return ancestor\n }\n return path\n}\n\n/** True when something strictly below this path carries a stamp. */\nfunction hasStampedDescendant(state: RecordState, path: string): boolean {\n const prefix = `${path}.`\n for (const other of state.fields.keys()) {\n if (other.startsWith(prefix)) return true\n }\n return false\n}\n\nexport function createCrdt<R extends UnknownRecord = UnknownRecord>(options: CrdtOptions<R>): Crdt<R> {\n const { clientId } = options\n const getRecord = options.getRecord ?? (() => undefined)\n const tombstoneLimit = options.tombstoneLimit ?? DEFAULT_TOMBSTONE_LIMIT\n const tombstoneMaxAgeMs = options.tombstoneMaxAgeMs ?? DEFAULT_TOMBSTONE_MAX_AGE_MS\n const now = options.now ?? (() => Date.now())\n const clock = createLamportClock()\n const states = new Map<string, RecordState>()\n let outgoing = createEmptyStampedDiff<R>()\n\n const stateFor = (id: string): RecordState => {\n let state = states.get(id)\n if (!state) {\n state = { fields: new Map() }\n states.set(id, state)\n }\n return state\n }\n\n /**\n * A merge in progress: `before` remembers what the store had when we first\n * looked at a record, `after` what it should end up with. Keeping both lets\n * one message both remove and re-create a record without contradicting\n * itself in the diff we hand back.\n */\n interface Pass {\n before: Map<string, R | undefined>\n after: Map<string, R | undefined>\n }\n\n const startPass = (): Pass => ({ before: new Map(), after: new Map() })\n\n const readBefore = (pass: Pass, id: string): R | undefined => {\n if (!pass.before.has(id)) pass.before.set(id, getRecord(id))\n return pass.before.get(id)\n }\n\n const readCurrent = (pass: Pass, id: string): R | undefined =>\n pass.after.has(id) ? pass.after.get(id) : readBefore(pass, id)\n\n const write = (pass: Pass, id: string, value: R | undefined): void => {\n readBefore(pass, id)\n pass.after.set(id, value)\n }\n\n const finishPass = (pass: Pass): RecordsDiff<R> => {\n const result = createEmptyRecordsDiff<R>()\n for (const [id, next] of pass.after) {\n const prev = pass.before.get(id)\n if (prev === next) continue\n const key = id as IdOf<R>\n if (prev === undefined) {\n if (next !== undefined) result.added[key] = next\n } else if (next === undefined) {\n result.removed[key] = prev\n } else {\n result.updated[key] = [prev, next]\n }\n }\n return result\n }\n\n /** Record a tombstone. Fields it dominates are dropped; survivors keep the record alive. */\n const applyRemove = (pass: Pass, id: string, stamp: Stamp): void => {\n const state = stateFor(id)\n if (state.deleted && compareStamps(stamp, state.deleted) <= 0) return\n state.deleted = stamp\n state.deletedAt = now()\n // The body older messages carry dies with the fields: only a message that\n // outranks the tombstone may supply one from here on.\n state.base = maxStamp(state.base, stamp)\n let alive = false\n for (const [path, existing] of Array.from(state.fields)) {\n if (compareStamps(existing, stamp) > 0) alive = true\n else state.fields.delete(path)\n }\n // A concurrent edit newer than the delete keeps the record: the delete only\n // wins when its stamp is greater than every field that would survive it.\n if (!alive) {\n if (readCurrent(pass, id) !== undefined) write(pass, id, undefined)\n return\n }\n // It survives — but the tombstone has just taken the provenance of every\n // field it outranks, and each replica is holding a different leftover for\n // those. Re-claim this body whole under a fresh stamp and send it, so the\n // record comes back as one agreed record rather than as a race.\n const record = readCurrent(pass, id)\n if (!record) return\n const revival: Stamp = { lamport: clock.tick(), client: clientId }\n const fields: Record<string, Stamp> = {}\n for (const path of leafPaths(record)) {\n fields[path] = revival\n claimPath(state, path, revival)\n }\n state.base = revival\n outgoing.puts.push({ record, fields, base: revival })\n }\n\n /**\n * Take every path neither the record nor the sender holds a stamp for from\n * the sender's body. Those are the fields the registers say nothing about,\n * and `base` is the one register that decides them.\n */\n const adoptBody = (record: R, body: R, state: RecordState): R => {\n const paths = new Set<string>([...leafPaths(body), ...leafPaths(record)])\n let next = record\n for (const path of Array.from(paths).sort()) {\n if (path === \"id\" || path === \"typeName\") continue\n if (isStamped(state, path) || hasStampedDescendant(state, path)) continue\n const value = readPath(body, path)\n // Take the whole subtree the body does not have, not just the leaf, so\n // dropping the last key of an object does not leave an empty one behind.\n next = writePath(next, value === ABSENT ? highestAbsent(body, state, path) : path, value)\n }\n return next\n }\n\n const applyPut = (pass: Pass, put: StampedPut<R>): void => {\n const id = put.record.id as string\n const state = stateFor(id)\n const claims: { path: string; survivors: string[] }[] = []\n for (const path of Object.keys(put.fields)) {\n const stamp = put.fields[path]\n if (!stamp) continue\n // A write older than the tombstone cannot resurrect the record.\n if (state.deleted && compareStamps(stamp, state.deleted) <= 0) continue\n // `<= 0` and not `< 0` inside: the same message twice changes nothing.\n const survivors = claimPath(state, path, stamp)\n if (survivors === null) continue\n claims.push({ path, survivors })\n }\n const base = put.base ?? greatestStamp(put.fields)\n const bodyWins = base !== undefined && (state.base === undefined || compareStamps(base, state.base) > 0)\n if (claims.length === 0 && !bodyWins) return\n\n const current = readCurrent(pass, id)\n if (current === undefined) {\n // Nothing here to merge into. A body alone must never bring a record\n // back: without a claim that outranks the tombstone this is a straggler.\n if (claims.length === 0) return\n write(pass, id, put.record)\n state.base = maxStamp(state.base, base)\n return\n }\n\n let next = current\n for (const { path, survivors } of claims) {\n if (path === \"id\") continue // identity is never merged\n const before = next\n next = writePath(next, path, readPath(put.record, path))\n // A subtree stamped later than this write outlives it, so put those\n // values back: `delete meta` racing `set meta.a` must land the same way\n // whichever arrives first.\n for (const survivor of survivors) next = writePath(next, survivor, readPath(before, survivor))\n }\n if (bodyWins) {\n next = adoptBody(next, put.record, state)\n state.base = base\n }\n if (next !== current) write(pass, id, next)\n }\n\n const observeStamps = (stamped: StampedDiff<R>): void => {\n let max = -1\n for (const put of stamped.puts) {\n for (const path of Object.keys(put.fields)) {\n const stamp = put.fields[path]\n if (stamp && stamp.lamport > max) max = stamp.lamport\n }\n if (put.base && put.base.lamport > max) max = put.base.lamport\n }\n for (const remove of stamped.removes) {\n if (remove.stamp.lamport > max) max = remove.stamp.lamport\n }\n if (max >= 0) clock.observe(max)\n }\n\n const collectTombstones = (): void => {\n const gone: { id: string; at: number }[] = []\n for (const [id, state] of states) {\n if (state.deleted && state.fields.size === 0) gone.push({ id, at: state.deletedAt ?? 0 })\n }\n if (gone.length === 0) return\n const cutoff = now() - tombstoneMaxAgeMs\n let overflow = gone.length - tombstoneLimit\n if (overflow <= 0 && gone.every((entry) => entry.at >= cutoff)) return\n gone.sort((a, b) => a.at - b.at)\n for (const entry of gone) {\n if (overflow > 0) {\n states.delete(entry.id)\n overflow--\n continue\n }\n if (entry.at < cutoff) states.delete(entry.id)\n }\n }\n\n return {\n clientId,\n clock,\n\n stampLocal(diff: RecordsDiff<R>): StampedDiff<R> {\n const stamp: Stamp = { lamport: clock.tick(), client: clientId }\n const result = createEmptyStampedDiff<R>()\n\n for (const id in diff.added) {\n const record = diff.added[id as IdOf<R>]\n if (!record) continue\n const fields: Record<string, Stamp> = {}\n const state = stateFor(id)\n for (const path of leafPaths(record)) {\n fields[path] = stamp\n claimPath(state, path, stamp)\n }\n // Our record is our whole view of it, as of now.\n state.base = stamp\n result.puts.push({ record, fields, base: stamp })\n }\n\n for (const id in diff.updated) {\n const pair = diff.updated[id as IdOf<R>]\n if (!pair) continue\n const [from, to] = pair\n const paths = changedPaths(from, to)\n if (paths.length === 0) continue\n const fields: Record<string, Stamp> = {}\n const state = stateFor(id)\n for (const path of paths) {\n fields[path] = stamp\n claimPath(state, path, stamp)\n }\n state.base = stamp\n result.puts.push({ record: to, fields, base: stamp })\n }\n\n for (const id in diff.removed) {\n const state = stateFor(id)\n state.deleted = stamp\n state.deletedAt = now()\n state.fields.clear()\n state.base = stamp\n result.removes.push({ id, stamp })\n }\n\n collectTombstones()\n return result\n },\n\n mergeRemote(stamped: StampedDiff<R>): RecordsDiff<R> {\n observeStamps(stamped)\n const pass = startPass()\n for (const remove of stamped.removes) applyRemove(pass, remove.id, remove.stamp)\n for (const put of stamped.puts) applyPut(pass, put)\n collectTombstones()\n return finishPass(pass)\n },\n\n getState(): CrdtState {\n const records: Record<string, CrdtRecordState> = {}\n for (const [id, state] of states) {\n const fields: Record<string, Stamp> = {}\n for (const [path, stamp] of state.fields) fields[path] = stamp\n records[id] = {\n fields,\n ...(state.base ? { base: state.base } : {}),\n ...(state.deleted ? { deleted: state.deleted, deletedAt: state.deletedAt ?? 0 } : {}),\n }\n }\n return { version: CRDT_STATE_VERSION, clock: clock.get(), records }\n },\n\n applyState(state: CrdtState): RecordsDiff<R> {\n const pass = startPass()\n if (state.version !== CRDT_STATE_VERSION) return finishPass(pass)\n clock.observe(state.clock)\n for (const id of Object.keys(state.records)) {\n const incoming = state.records[id]\n if (!incoming) continue\n if (incoming.deleted) applyRemove(pass, id, incoming.deleted)\n const local = stateFor(id)\n for (const path of Object.keys(incoming.fields)) {\n const stamp = incoming.fields[path]\n if (!stamp) continue\n clock.observe(stamp.lamport)\n if (local.deleted && compareStamps(stamp, local.deleted) <= 0) continue\n claimPath(local, path, stamp)\n }\n if (incoming.base) {\n clock.observe(incoming.base.lamport)\n local.base = maxStamp(local.base, incoming.base)\n }\n }\n collectTombstones()\n return finishPass(pass)\n },\n\n takeOutgoing(): StampedDiff<R> | null {\n if (isStampedDiffEmpty(outgoing)) return null\n const result = outgoing\n outgoing = createEmptyStampedDiff<R>()\n return result\n },\n\n size: () => states.size,\n collectTombstones,\n }\n}\n\n/**\n * Turn a peer's snapshot into a message the ordinary merge can eat: its\n * records supply the values, its state supplies the stamps. Paths the state\n * says nothing about are not claimed, so a snapshot can never overwrite a\n * field the receiver has a stamp for.\n */\nexport function stampedDiffFromSnapshot<R extends UnknownRecord>(\n records: readonly R[],\n state: CrdtState,\n): StampedDiff<R> {\n const result = createEmptyStampedDiff<R>()\n for (const record of records) {\n const entry = state.records[record.id as string]\n const fields = entry?.fields ?? {}\n const base = entry?.base ?? greatestStamp(fields)\n result.puts.push(base ? { record, fields, base } : { record, fields })\n }\n for (const id of Object.keys(state.records)) {\n const deleted = state.records[id]?.deleted\n if (deleted) result.removes.push({ id, stamp: deleted })\n }\n return result\n}\n\n/* -------------------------------------------------------------------------- */\n/* field paths */\n/* -------------------------------------------------------------------------- */\n\n/** A path that is not present at all, as distinct from one holding `undefined`. */\nconst ABSENT = Symbol(\"absent\")\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false\n const proto = Object.getPrototypeOf(value) as unknown\n return proto === Object.prototype || proto === null\n}\n\n/**\n * Every leaf path of a value. Plain objects are recursed into; arrays,\n * primitives, `null` and empty objects are leaves — an array is one register\n * holding the whole array.\n */\nexport function leafPaths(value: unknown, prefix = \"\"): string[] {\n const out: string[] = []\n collectLeaves(value, prefix, out)\n return out\n}\n\nfunction collectLeaves(value: unknown, prefix: string, out: string[]): void {\n if (isPlainObject(value)) {\n const keys = Object.keys(value)\n if (keys.length > 0) {\n for (const key of keys) collectLeaves(value[key], prefix ? `${prefix}.${key}` : key, out)\n return\n }\n }\n if (prefix !== \"\") out.push(prefix)\n}\n\n/** The paths whose values differ between two versions of a record. */\nexport function changedPaths(from: unknown, to: unknown): string[] {\n const out: string[] = []\n collectChanges(from, to, \"\", out)\n return out\n}\n\nfunction collectChanges(from: unknown, to: unknown, prefix: string, out: string[]): void {\n if (isPlainObject(from) && isPlainObject(to)) {\n for (const key of Object.keys(from)) {\n const path = prefix ? `${prefix}.${key}` : key\n // A key that disappeared: stamp the whole subtree as one removal.\n if (!(key in to)) out.push(path)\n else collectChanges(from[key], to[key], path, out)\n }\n for (const key of Object.keys(to)) {\n if (key in from) continue\n collectLeaves(to[key], prefix ? `${prefix}.${key}` : key, out)\n }\n return\n }\n if (!deepEqual(from, to) && prefix !== \"\") out.push(prefix)\n}\n\nfunction readPath(source: unknown, path: string): unknown {\n let current: unknown = source\n for (const segment of path.split(\".\")) {\n if (!isPlainObject(current) || !(segment in current)) return ABSENT\n current = current[segment]\n }\n return current\n}\n\n/**\n * A copy of `record` with `path` set to `value` (or the key deleted when the\n * path is absent in the source). Every level along the path is cloned, because\n * the store freezes records, their `props` and their `meta`.\n */\nfunction writePath<R extends UnknownRecord>(record: R, path: string, value: unknown): R {\n const current = readPath(record, path)\n if (value === ABSENT) {\n if (current === ABSENT) return record\n } else if (current !== ABSENT && deepEqual(current, value)) {\n return record\n }\n const segments = path.split(\".\")\n return setIn(record as unknown as Record<string, unknown>, segments, 0, value) as unknown as R\n}\n\nfunction setIn(\n target: Record<string, unknown>,\n segments: string[],\n index: number,\n value: unknown,\n): Record<string, unknown> {\n const key = segments[index]\n if (key === undefined) return target\n const next: Record<string, unknown> = { ...target }\n if (index === segments.length - 1) {\n if (value === ABSENT) delete next[key]\n else next[key] = value\n return next\n }\n const child = next[key]\n next[key] = setIn(isPlainObject(child) ? child : {}, segments, index + 1, value)\n return next\n}\n\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (Array.isArray(a)) {\n if (!Array.isArray(b) || a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n }\n if (isPlainObject(a) && isPlainObject(b)) {\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!(key in b)) return false\n if (!deepEqual(a[key], b[key])) return false\n }\n return true\n }\n return false\n}\n","import { react } from \"@mocanvas/state\"\nimport {\n InstancePresenceRecordType,\n type Editor,\n type InstancePresence,\n type InstancePresenceId,\n type UserId,\n} from \"@mocanvas/editor\"\nimport type { IdOf, Store, UnknownRecord } from \"@mocanvas/store\"\n\n/** Presence is sent at most this often (30 Hz). */\nexport const DEFAULT_PRESENCE_THROTTLE_MS = Math.ceil(1000 / 30)\n/** A presence record is re-sent this often even when nothing changed. */\nexport const DEFAULT_PRESENCE_HEARTBEAT_MS = 3000\n/** A collaborator we have not heard from in this long is dropped. */\nexport const DEFAULT_PRESENCE_TIMEOUT_MS = 10_000\n\n/**\n * The part of the editor presence reads. Declared structurally so that the\n * sync layer does not need a live `Editor` (and its WASM engine) to be tested.\n */\nexport interface PresenceEditor {\n readonly store: Store<any, any>\n readonly inputs: { readonly currentPagePoint: { x: number; y: number } }\n readonly user: { getId(): UserId; getName(): string; getColor(): string }\n getCurrentPageId(): string\n getCamera(): { x: number; y: number; z: number }\n getSelectedShapeIds(): readonly string[]\n getInstanceState(): {\n cursor: { type: string; rotation: number }\n brush: { x: number; y: number; w: number; h: number } | null\n scribbles: readonly unknown[]\n followingUserId: UserId | null\n }\n /**\n * Optional: the id this instance's presence record is published under.\n *\n * A real `Editor` has one and it is the identity that matters here — a user\n * id is per browser, so two tabs of one browser are one user and would\n * discard each other's presence as their own. Structural stand-ins (tests,\n * a headless participant) may omit it and fall back to\n * {@link presenceIdForClient}.\n */\n getInstancePresenceId?(): InstancePresenceId\n /** Optional: used to sample the cursor, which is not itself a signal. */\n on?(name: \"event\", fn: () => void): () => void\n}\n\n// A real Editor must satisfy the structural interface above; this fails to\n// compile if the two ever drift apart.\ntype Assert<T extends true> = T\nexport type EditorIsPresenceEditor = Assert<Editor extends PresenceEditor ? true : false>\n\nexport interface PresenceSyncOptions {\n editor: PresenceEditor\n /** Identifies this tab; one user may have several. */\n clientId: string\n send(record: InstancePresence): void\n throttleMs?: number\n heartbeatMs?: number\n}\n\nexport interface PresenceSync {\n start(): void\n stop(): void\n /**\n * Ask for a send; collapses with any other request inside the throttle\n * window.\n *\n * `force` sends even when nothing about this client has changed, which is\n * what a *re-announcement* is: somebody who was not listening when we last\n * spoke has arrived, so the record has to go out again although it says the\n * same thing. Without it a peer that has been sitting still since before the\n * newcomer joined stays invisible until its next heartbeat.\n */\n poke(force?: boolean): void\n /** The record last handed to `send`, for tests and debugging. */\n getLastSent(): InstancePresence | null\n dispose(): void\n}\n\n/** The id a client's presence record always uses, so updates overwrite in place. */\nexport function presenceIdForClient(clientId: string): InstancePresenceId {\n return InstancePresenceRecordType.createId(clientId) as InstancePresenceId\n}\n\n/**\n * Watches the editor and pushes the local presence record out, at most once per\n * throttle window, plus a heartbeat so that idle collaborators do not time out.\n */\nexport function createPresenceSync(options: PresenceSyncOptions): PresenceSync {\n const { editor, clientId, send } = options\n const throttleMs = options.throttleMs ?? DEFAULT_PRESENCE_THROTTLE_MS\n const heartbeatMs = options.heartbeatMs ?? DEFAULT_PRESENCE_HEARTBEAT_MS\n // The editor's own instance id when it has one, so that the id this tab\n // publishes under is the same id the editor filters its own record by.\n // `presenceIdForClient` is the fallback for a structural stand-in.\n const id = editor.getInstancePresenceId?.() ?? presenceIdForClient(clientId)\n\n let timer: ReturnType<typeof setTimeout> | null = null\n let heartbeat: ReturnType<typeof setInterval> | null = null\n let stopReactor: (() => void) | null = null\n let stopEvents: (() => void) | null = null\n let last: InstancePresence | null = null\n let running = false\n\n const build = (): InstancePresence => {\n const instance = editor.getInstanceState()\n const point = editor.inputs.currentPagePoint\n return InstancePresenceRecordType.create({\n id,\n userId: editor.user.getId(),\n userName: editor.user.getName(),\n color: editor.user.getColor(),\n currentPageId: editor.getCurrentPageId() as InstancePresence[\"currentPageId\"],\n cursor: { x: point.x, y: point.y, type: instance.cursor.type, rotation: instance.cursor.rotation },\n camera: { ...editor.getCamera() },\n selectedShapeIds: [...editor.getSelectedShapeIds()] as InstancePresence[\"selectedShapeIds\"],\n brush: instance.brush ? { ...instance.brush } : null,\n scribbles: [...instance.scribbles] as InstancePresence[\"scribbles\"],\n followingUserId: instance.followingUserId,\n lastActivityTimestamp: Date.now(),\n chatMessage: \"\",\n meta: {},\n })\n }\n\n const flush = (force: boolean) => {\n if (!running) return\n const next = build()\n if (!force && last && isSamePresence(last, next)) return\n last = next\n send(next)\n }\n\n /** Set while a forced send is waiting out the throttle window. */\n let pendingForce = false\n\n const poke = (force = false) => {\n if (!running) return\n if (force) pendingForce = true\n if (timer !== null) return\n timer = setTimeout(() => {\n timer = null\n const forced = pendingForce\n pendingForce = false\n flush(forced)\n }, throttleMs)\n }\n\n return {\n start() {\n if (running) return\n running = true\n // Camera, page and selection are signals, so a reactor covers them.\n stopReactor = react(\"sync.presence\", () => {\n editor.getCamera()\n editor.getCurrentPageId()\n editor.getSelectedShapeIds()\n editor.getInstanceState()\n editor.user.getName()\n editor.user.getColor()\n poke()\n })\n // The pointer position lives on `inputs`, which is not reactive.\n stopEvents = editor.on?.(\"event\", poke) ?? null\n heartbeat = setInterval(() => flush(true), heartbeatMs)\n flush(true)\n },\n stop() {\n running = false\n pendingForce = false\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n if (heartbeat !== null) {\n clearInterval(heartbeat)\n heartbeat = null\n }\n stopReactor?.()\n stopReactor = null\n stopEvents?.()\n stopEvents = null\n },\n poke,\n getLastSent: () => last,\n dispose() {\n this.stop()\n last = null\n },\n }\n}\n\n/** Equal apart from `lastActivityTimestamp`, which always moves. */\nexport function isSamePresence(a: InstancePresence, b: InstancePresence): boolean {\n return (\n a.userId === b.userId &&\n a.userName === b.userName &&\n a.color === b.color &&\n a.currentPageId === b.currentPageId &&\n a.chatMessage === b.chatMessage &&\n a.followingUserId === b.followingUserId &&\n sameCursor(a.cursor, b.cursor) &&\n sameCamera(a.camera, b.camera) &&\n sameIds(a.selectedShapeIds, b.selectedShapeIds) &&\n sameBrush(a.brush, b.brush) &&\n a.scribbles.length === b.scribbles.length &&\n a.scribbles.every((s, i) => s === b.scribbles[i])\n )\n}\n\n/** Cameras compare equal when both are absent, or when all three fields match. */\nfunction sameCamera(a: InstancePresence[\"camera\"], b: InstancePresence[\"camera\"]): boolean {\n if (a === null || b === null) return a === b\n return a.x === b.x && a.y === b.y && a.z === b.z\n}\n\n/** Cursors compare equal when both are absent, or when all four fields match. */\nfunction sameCursor(a: InstancePresence[\"cursor\"], b: InstancePresence[\"cursor\"]): boolean {\n if (a === null || b === null) return a === b\n return a.x === b.x && a.y === b.y && a.type === b.type && a.rotation === b.rotation\n}\n\nfunction sameIds(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((id, i) => id === b[i])\n}\n\nfunction sameBrush(a: InstancePresence[\"brush\"], b: InstancePresence[\"brush\"]): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h\n}\n\n/**\n * Holds the presence records of everyone else: puts them in the store as they\n * arrive, drops them on `bye`, and sweeps clients that went quiet.\n */\nexport class PresenceRoom<R extends UnknownRecord> {\n private readonly seen = new Map<string, { id: string; at: number }>()\n private sweeper: ReturnType<typeof setInterval> | null = null\n\n constructor(\n private readonly store: Store<R, any>,\n private readonly timeoutMs: number = DEFAULT_PRESENCE_TIMEOUT_MS,\n ) {}\n\n /** Store an incoming presence record and remember when we saw its client. */\n receive(clientId: string, record: R): void {\n const previous = this.seen.get(clientId)\n if (previous && previous.id !== record.id) this.removeRecord(previous.id)\n this.seen.set(clientId, { id: record.id, at: Date.now() })\n this.store.mergeRemoteChanges(() => {\n this.store.put([record])\n })\n }\n\n /** Drop one client's presence (it said goodbye). */\n remove(clientId: string): void {\n const entry = this.seen.get(clientId)\n if (!entry) return\n this.seen.delete(clientId)\n this.removeRecord(entry.id)\n }\n\n /** Drop every client we have not heard from within the timeout. */\n sweep(now: number = Date.now()): void {\n const stale: string[] = []\n for (const [clientId, entry] of this.seen) {\n if (now - entry.at > this.timeoutMs) stale.push(clientId)\n }\n for (const clientId of stale) this.remove(clientId)\n }\n\n startSweeping(intervalMs: number = Math.max(1000, Math.floor(this.timeoutMs / 4))): void {\n if (this.sweeper !== null) return\n this.sweeper = setInterval(() => this.sweep(), intervalMs)\n }\n\n stopSweeping(): void {\n if (this.sweeper === null) return\n clearInterval(this.sweeper)\n this.sweeper = null\n }\n\n /** Remove every record this room put in the store. */\n clear(): void {\n const ids = Array.from(this.seen.values(), (entry) => entry.id)\n this.seen.clear()\n for (const id of ids) this.removeRecord(id)\n }\n\n private removeRecord(id: string): void {\n const recordId = id as IdOf<R>\n if (!this.store.unsafeGetWithoutCapture(recordId)) return\n this.store.mergeRemoteChanges(() => {\n this.store.remove([recordId])\n })\n }\n}\n","import type { UnknownRecord } from \"@mocanvas/store\"\nimport { isCrdtStateLike, isStampedDiffLike, type CrdtState, type StampedDiff } from \"./crdt\"\n\n/**\n * Bumped when the wire format changes incompatibly.\n *\n * 2: `diff` carries a `StampedDiff` (per-field Lamport stamps) instead of a\n * plain `RecordsDiff`, `snapshot` carries the CRDT state next to the\n * records, and every message carries `version`.\n * 3: a put carries `base`, the stamp its whole record body is current as of,\n * and a record kept alive by an edit that outranks a delete is re-claimed\n * whole under a fresh stamp. A version 2 peer merges the same messages to a\n * different answer, so the two must not share a room.\n */\nexport const PROTOCOL_VERSION = 3\n\n/**\n * Every message is one JSON object with a `type` discriminator and a\n * `version`, which `encodeMessage` stamps on and `decodeMessage` checks.\n *\n * ```\n * hello a client joined; existing peers answer with a snapshot\n * snapshot the document-scope records plus the sender's CRDT stamps\n * diff one squashed store diff, stamped field by field\n * presence one presence record (cursor, camera, selection)\n * bye the client is leaving; drop its presence\n * unsupported synthesised locally for a peer on another protocol version\n * ```\n */\nexport type SyncMessage<R extends UnknownRecord = UnknownRecord> =\n | HelloMessage\n | SnapshotMessage<R>\n | DiffMessage<R>\n | PresenceMessage<R>\n | ByeMessage\n | UnsupportedMessage\n\nexport interface HelloMessage {\n type: \"hello\"\n clientId: string\n version: number\n}\n\nexport interface SnapshotMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"snapshot\"\n records: R[]\n /** The stamps that go with those records, so a joiner does not start blank. */\n state: CrdtState\n}\n\nexport interface DiffMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"diff\"\n clientId: string\n /** Per-client counter; only used for logging and de-duplication. */\n seq: number\n diff: StampedDiff<R>\n}\n\nexport interface PresenceMessage<R extends UnknownRecord = UnknownRecord> {\n type: \"presence\"\n clientId: string\n record: R\n}\n\nexport interface ByeMessage {\n type: \"bye\"\n clientId: string\n}\n\n/**\n * Never sent: `decodeMessage` returns this instead of guessing at a message\n * whose `version` is not ours, so a peer on another protocol is reported once\n * rather than half-parsed.\n */\nexport interface UnsupportedMessage {\n type: \"unsupported\"\n version: number\n clientId?: string\n}\n\nexport function encodeMessage<R extends UnknownRecord>(message: SyncMessage<R>): string {\n return JSON.stringify({ ...message, version: PROTOCOL_VERSION })\n}\n\n/**\n * Parse a message off the wire. Returns `null` for anything malformed and an\n * `unsupported` message for a peer on another protocol version: neither can\n * crash this client, and neither is ever mistaken for a message we understand.\n */\nexport function decodeMessage<R extends UnknownRecord = UnknownRecord>(data: unknown): SyncMessage<R> | null {\n let value: unknown = data\n if (typeof data === \"string\") {\n try {\n value = JSON.parse(data)\n } catch {\n return null\n }\n }\n if (typeof value !== \"object\" || value === null) return null\n const message = value as Record<string, unknown>\n\n const version = message[\"version\"]\n if (typeof version === \"number\" && version !== PROTOCOL_VERSION) {\n const clientId = message[\"clientId\"]\n return typeof clientId === \"string\"\n ? { type: \"unsupported\", version, clientId }\n : { type: \"unsupported\", version }\n }\n\n switch (message[\"type\"]) {\n case \"hello\":\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (typeof message[\"version\"] !== \"number\") return null\n return { type: \"hello\", clientId: message[\"clientId\"], version: message[\"version\"] }\n case \"snapshot\": {\n const records = message[\"records\"]\n if (!Array.isArray(records)) return null\n if (!records.every(isRecordLike)) return null\n const state = message[\"state\"]\n if (!isCrdtStateLike(state)) return null\n return { type: \"snapshot\", records: records as R[], state: state as CrdtState }\n }\n case \"diff\": {\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (typeof message[\"seq\"] !== \"number\") return null\n if (!isStampedDiffLike(message[\"diff\"])) return null\n return {\n type: \"diff\",\n clientId: message[\"clientId\"],\n seq: message[\"seq\"],\n diff: message[\"diff\"] as StampedDiff<R>,\n }\n }\n case \"presence\": {\n if (typeof message[\"clientId\"] !== \"string\") return null\n if (!isRecordLike(message[\"record\"])) return null\n return { type: \"presence\", clientId: message[\"clientId\"], record: message[\"record\"] as R }\n }\n case \"bye\":\n if (typeof message[\"clientId\"] !== \"string\") return null\n return { type: \"bye\", clientId: message[\"clientId\"] }\n default:\n return null\n }\n}\n\nfunction isRecordLike(value: unknown): boolean {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { id?: unknown }).id === \"string\" &&\n typeof (value as { typeName?: unknown }).typeName === \"string\"\n )\n}\n","import { atom, type Signal } from \"@mocanvas/state\"\nimport { warnOnce } from \"@mocanvas/editor\"\nimport {\n createEmptyRecordsDiff,\n isRecordsDiffEmpty,\n uniqueId,\n type IdOf,\n type RecordsDiff,\n type Store,\n type UnknownRecord,\n} from \"@mocanvas/store\"\nimport { createCrdt, deepEqual, isStampedDiffEmpty, stampedDiffFromSnapshot, type Crdt, type StampedDiff } from \"./crdt\"\nimport {\n createPresenceSync,\n DEFAULT_PRESENCE_TIMEOUT_MS,\n PresenceRoom,\n type PresenceEditor,\n type PresenceSync,\n} from \"./presence\"\nimport { PROTOCOL_VERSION, type SyncMessage } from \"./protocol\"\nimport type { Transport } from \"./transport\"\n\nexport type SyncStatus = \"offline\" | \"connecting\" | \"online\"\n\nexport interface SyncClientOptions<R extends UnknownRecord = UnknownRecord> {\n store: Store<R, any>\n /** Identifies the document. The transport is expected to be scoped to it too. */\n roomId: string\n transport: Transport<R>\n /** Turn on cursor/selection sharing for an editor. */\n presence?: { editor: PresenceEditor } | undefined\n /** Defaults to a fresh random id; one per tab, not per user. */\n clientId?: string | undefined\n /** Drop a collaborator after this long without a presence message. Default 10s. */\n presenceTimeoutMs?: number | undefined\n /** Upper bound on presence send rate. Default 34ms (~30 Hz). */\n presenceThrottleMs?: number | undefined\n presenceHeartbeatMs?: number | undefined\n /** How many tombstones for vanished records to keep. See `crdt.ts`. */\n tombstoneLimit?: number | undefined\n /** How long such a tombstone lives, in ms. */\n tombstoneMaxAgeMs?: number | undefined\n /** Called for protocol-level problems (a peer on another version, say). */\n onError?: ((error: Error) => void) | undefined\n}\n\nexport interface SyncClient {\n readonly clientId: string\n readonly roomId: string\n connect(): void\n disconnect(): void\n /** A signal, so React and reactors can follow the connection. */\n getStatus(): Signal<SyncStatus>\n dispose(): void\n}\n\n/**\n * Joins one room over `transport` and keeps `store` in step with its peers.\n *\n * Document changes made locally by the user are stamped by the CRDT (see\n * `crdt.ts`) and broadcast; incoming messages are merged field by field and\n * only the fields that won are applied, as remote changes, so they neither\n * echo back nor land in the undo stack. Concurrent edits to different fields\n * of one record all survive, and replicas converge whatever order messages\n * arrive in.\n */\nexport function createSyncClient<R extends UnknownRecord = UnknownRecord>(\n options: SyncClientOptions<R>,\n): SyncClient {\n const { store, roomId, transport } = options\n const clientId = options.clientId ?? uniqueId(12)\n const status = atom<SyncStatus>(`sync.status:${roomId}`, \"offline\")\n const room = new PresenceRoom<R>(store, options.presenceTimeoutMs ?? DEFAULT_PRESENCE_TIMEOUT_MS)\n\n const crdt: Crdt<R> = createCrdt<R>({\n clientId,\n getRecord: (id) => store.unsafeGetWithoutCapture(id as IdOf<R>) as R | undefined,\n ...(options.tombstoneLimit !== undefined ? { tombstoneLimit: options.tombstoneLimit } : {}),\n ...(options.tombstoneMaxAgeMs !== undefined ? { tombstoneMaxAgeMs: options.tombstoneMaxAgeMs } : {}),\n })\n\n const unsubscribes: (() => void)[] = []\n const reportedVersions = new Set<number>()\n /** Which page each peer says it is looking at, for the check below. */\n const peerPages = new Map<string, string>()\n let warnedAboutPages = false\n let presence: PresenceSync | null = null\n let seq = 0\n /**\n * True while this replica is waiting to be handed the room, which only a\n * replica holding no stamps at all ever is: one that has just loaded the\n * page has nothing to lose by taking a peer's document whole, and nothing\n * worth pushing at anyone either. A replica with stamps — including one that\n * edited while it was disconnected — merges instead.\n */\n let awaitingSnapshot = false\n let connected = false\n let disposed = false\n\n const send = (message: SyncMessage<R>) => {\n try {\n transport.send(message)\n } catch (error) {\n options.onError?.(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n const broadcast = (stamped: StampedDiff<R>) => {\n if (isStampedDiffEmpty(stamped)) return\n send({ type: \"diff\", clientId, seq: seq++, diff: stamped })\n }\n\n /**\n * Writes the CRDT minted while merging — a record revived by an edit that\n * outranks a delete — go out like any other local diff.\n */\n const flushCrdt = () => {\n const outgoing = crdt.takeOutgoing()\n if (outgoing) broadcast(outgoing)\n }\n\n const sendSnapshot = () => {\n send({\n type: \"snapshot\",\n records: Object.values(store.serialize(\"document\")) as R[],\n state: crdt.getState(),\n })\n }\n\n const applyRemote = (diff: RecordsDiff<R>) => {\n if (isRecordsDiffEmpty(diff)) return\n const actual = store.extractingChanges(() => {\n store.mergeRemoteChanges(() => {\n store.applyDiff(diff)\n })\n })\n reportRewrites(diff, actual)\n }\n\n /**\n * What the store actually did, minus what we asked it to do. A validator or\n * a side effect that rewrites a merged record — or touches another one —\n * changes this replica only: the change is reported as `remote`, so the\n * listener below never sees it, and no peer would ever hear about it. Stamp\n * that difference as a local write and send it, so the rewrite is one more\n * write everyone can agree on rather than a private divergence.\n *\n * A side effect that is a function of the record and the document rewrites\n * the same way everywhere, so this settles in one round: the peers apply a\n * value they already hold and produce nothing further. A side effect that is\n * not a function of its inputs cannot converge, here or anywhere.\n */\n const reportRewrites = (asked: RecordsDiff<R>, actual: RecordsDiff<R>) => {\n const rewrite = createEmptyRecordsDiff<R>()\n const intended = (id: IdOf<R>): R | undefined => asked.added[id] ?? asked.updated[id]?.[1]\n const isDocument = (record: R) => store.getScope(record.typeName) === \"document\"\n\n for (const key in actual.added) {\n const id = key as IdOf<R>\n const after = actual.added[id]\n if (!after || !isDocument(after)) continue\n const want = intended(id)\n if (!want) rewrite.added[id] = after\n else if (!deepEqual(want, after)) rewrite.updated[id] = [want, after]\n }\n for (const key in actual.updated) {\n const id = key as IdOf<R>\n const pair = actual.updated[id]\n if (!pair || !isDocument(pair[1])) continue\n const want = intended(id)\n if (!want) rewrite.updated[id] = pair\n else if (!deepEqual(want, pair[1])) rewrite.updated[id] = [want, pair[1]]\n }\n for (const key in actual.removed) {\n const id = key as IdOf<R>\n const before = actual.removed[id]\n if (!before || !isDocument(before)) continue\n if (!asked.removed[id]) rewrite.removed[id] = before\n }\n if (isRecordsDiffEmpty(rewrite)) return\n broadcast(crdt.stampLocal(rewrite))\n }\n\n /**\n * Outgoing document changes: only what this user did, never what we applied\n * from a peer (those arrive with source \"remote\"). It is wired up when the\n * client is built and stays wired until `dispose`, so an edit made before\n * `connect()` or after `disconnect()` is stamped when it happens rather than\n * when the socket is up. That is what carries offline work through a\n * reconnect: unstamped, it would lose to every stamped write in the room.\n */\n const stopListening = store.listen(\n (entry) => {\n if (isRecordsDiffEmpty(entry.changes)) return\n const stamped = crdt.stampLocal(entry.changes)\n if (connected) broadcast(stamped)\n },\n { source: \"user\", scope: \"document\" },\n )\n\n const handle = (message: SyncMessage<R>) => {\n if (disposed) return\n switch (message.type) {\n case \"unsupported\": {\n if (reportedVersions.has(message.version)) return\n reportedVersions.add(message.version)\n options.onError?.(\n new Error(`A peer speaks protocol ${message.version}, we speak ${PROTOCOL_VERSION}`),\n )\n return\n }\n case \"hello\": {\n if (message.clientId === clientId) return\n if (message.version !== PROTOCOL_VERSION) {\n options.onError?.(\n new Error(`Peer ${message.clientId} speaks protocol ${message.version}, we speak ${PROTOCOL_VERSION}`),\n )\n return\n }\n // A newcomer needs the document; every established peer answers with\n // its records and its stamps. Answering also makes us established: we\n // hold state of our own now, so nobody's snapshot can start us blank.\n awaitingSnapshot = false\n sendSnapshot()\n // Re-announce our presence so the newcomer sees us immediately.\n // Forced: our record has not changed — it is the audience that has —\n // and an unforced poke would compare equal and send nothing, leaving\n // us invisible to the newcomer until our next heartbeat.\n presence?.poke(true)\n return\n }\n case \"snapshot\": {\n // Wholesale adoption is only safe with nothing of our own to lose:\n // one stamp means one edit that would be overwritten by the sender's\n // body, so `crdt.size()` and not just the flag decides.\n if (awaitingSnapshot && crdt.size() === 0) {\n // We have no stamps at all. Take the sender's records and its clock\n // wholesale, so our own later writes sort after everything already\n // in the room instead of racing it from zero.\n awaitingSnapshot = false\n const removals = crdt.applyState(message.state)\n const actual = store.extractingChanges(() => {\n store.mergeRemoteChanges(() => {\n store.put(message.records)\n store.applyDiff(removals)\n })\n })\n const asked = createEmptyRecordsDiff<R>()\n for (const record of message.records) asked.added[record.id as IdOf<R>] = record\n reportRewrites(asked, actual)\n flushCrdt()\n return\n }\n // Otherwise this is a reconciliation (a peer reconnected, or answered\n // someone else's hello), or we hold edits of our own: merge it field\n // by field. Nothing we hold a newer stamp for can be overwritten, so\n // it is always safe to apply.\n awaitingSnapshot = false\n applyRemote(crdt.mergeRemote(stampedDiffFromSnapshot(message.records, message.state)))\n flushCrdt()\n return\n }\n case \"diff\": {\n if (message.clientId === clientId) return\n if (isStampedDiffEmpty(message.diff)) return\n applyRemote(crdt.mergeRemote(message.diff))\n flushCrdt()\n return\n }\n case \"presence\": {\n if (message.clientId === clientId) return\n room.receive(message.clientId, message.record)\n notePeerPage(message.clientId, message.record)\n return\n }\n case \"bye\": {\n if (message.clientId === clientId) return\n room.remove(message.clientId)\n peerPages.delete(message.clientId)\n return\n }\n }\n }\n\n /**\n * Say something when this replica is looking at a page none of its peers is.\n *\n * Everything still works — diffs and presence arrive, the status says\n * `online` — and nothing is drawn: `getCollaboratorsOnCurrentPage()` filters\n * every peer out and their shapes are on a page this replica is not showing.\n * It is the failure mode with the least evidence, so the library says it out\n * loud rather than letting a developer hunt for a transport bug that is not\n * there. Once per client, and only outside production.\n */\n const notePeerPage = (peerId: string, record: R) => {\n const editor = options.presence?.editor\n if (!editor || warnedAboutPages) return\n const page = (record as unknown as { currentPageId?: unknown }).currentPageId\n if (typeof page !== \"string\") return\n peerPages.set(peerId, page)\n const mine = editor.getCurrentPageId()\n for (const peerPage of peerPages.values()) if (peerPage === mine) return\n warnedAboutPages = true\n const theirs = Array.from(new Set(peerPages.values())).join(\", \")\n warnOnce(\n `sync.page:${roomId}:${clientId}`,\n `mocanvas/sync: this replica is on page ${mine}, and none of the ${peerPages.size} peer(s) in room \"${roomId}\" is — ` +\n `they are on ${theirs}. Their cursors and their shapes will not be visible here. That is correct if you moved to ` +\n `another page on purpose; if you did not, the two replicas are on pages that were built separately. Every editor ` +\n `that starts from a blank document starts on the same default page, so check whether one side pinned, created or ` +\n `loaded a page of its own — and if so, call editor.setCurrentPage() with a page id both sides agree on.`,\n )\n }\n\n const onOpen = () => {\n if (disposed || !connected) return\n status.set(\"online\")\n send({ type: \"hello\", clientId, version: PROTOCOL_VERSION })\n // On a reconnect we already hold stamps, possibly for work done while the\n // transport was down. Push them: the peers merge field by field, so\n // neither side of the split loses anything.\n if (!awaitingSnapshot) sendSnapshot()\n // Forced, for the same reason as the `hello` case: after a reconnect the\n // room has not heard from us in a while and our record may be unchanged.\n presence?.poke(true)\n }\n\n const onClose = () => {\n if (disposed || !connected) return\n // The transport reconnects on its own; from here it is just \"not online\".\n // Local edits keep flowing into the CRDT and go out on the next open.\n status.set(\"connecting\")\n }\n\n return {\n clientId,\n roomId,\n\n connect() {\n if (disposed || connected) return\n connected = true\n // Only a replica holding no stamps at all is a newcomer. One that edited\n // before it ever connected, or while it was disconnected, has work of\n // its own and merges the room's snapshot instead of taking it whole.\n awaitingSnapshot = crdt.size() === 0\n status.set(\"connecting\")\n\n unsubscribes.push(transport.onMessage(handle))\n if (transport.onOpen) unsubscribes.push(transport.onOpen(onOpen))\n if (transport.onClose) unsubscribes.push(transport.onClose(onClose))\n\n if (options.presence) {\n presence = createPresenceSync({\n editor: options.presence.editor,\n clientId,\n send: (record) => send({ type: \"presence\", clientId, record: record as unknown as R }),\n ...(options.presenceThrottleMs !== undefined ? { throttleMs: options.presenceThrottleMs } : {}),\n ...(options.presenceHeartbeatMs !== undefined ? { heartbeatMs: options.presenceHeartbeatMs } : {}),\n })\n presence.start()\n }\n room.startSweeping()\n\n if (!transport.onOpen) onOpen()\n },\n\n disconnect() {\n if (!connected) return\n connected = false\n send({ type: \"bye\", clientId })\n presence?.stop()\n presence = null\n room.stopSweeping()\n room.clear()\n for (const off of unsubscribes.splice(0)) off()\n status.set(\"offline\")\n },\n\n getStatus() {\n return status\n },\n\n dispose() {\n if (disposed) return\n this.disconnect()\n disposed = true\n stopListening()\n transport.close()\n },\n }\n}\n","import type { UnknownRecord } from \"@mocanvas/store\"\nimport { decodeMessage, encodeMessage, type SyncMessage } from \"./protocol\"\n\n/**\n * A duplex channel that carries `SyncMessage`s between peers in one room.\n *\n * The transport owns serialization and reconnection; the client above it only\n * sees decoded messages. `onOpen` fires every time the channel becomes usable\n * (including after a reconnect), `onClose` every time it stops being usable.\n * A transport with neither is treated as open from the moment it is connected.\n */\nexport interface Transport<R extends UnknownRecord = UnknownRecord> {\n send(message: SyncMessage<R>): void\n onMessage(callback: (message: SyncMessage<R>) => void): () => void\n onOpen?(callback: () => void): () => void\n onClose?(callback: () => void): () => void\n close(): void\n}\n\n/** @internal A tiny callback set with an unsubscribe function. */\nexport function createEmitter(): {\n add(cb: () => void): () => void\n emit(): void\n clear(): void\n} {\n const callbacks = new Set<() => void>()\n return {\n add(cb) {\n callbacks.add(cb)\n return () => {\n callbacks.delete(cb)\n }\n },\n emit() {\n for (const cb of Array.from(callbacks)) cb()\n },\n clear() {\n callbacks.clear()\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* BroadcastChannel */\n/* -------------------------------------------------------------------------- */\n\nexport interface BroadcastChannelTransportOptions {\n /** Channel name prefix, so two apps on one origin do not collide. */\n prefix?: string\n}\n\n/**\n * Same-origin tabs of one browser. There is no server, so every peer is\n * equally authoritative and the channel is open as soon as it is created.\n */\nexport function createBroadcastChannelTransport<R extends UnknownRecord = UnknownRecord>(\n roomId: string,\n options: BroadcastChannelTransportOptions = {},\n): Transport<R> {\n const name = `${options.prefix ?? \"mocanvas-sync\"}:${roomId}`\n const channel = new BroadcastChannel(name)\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n let closed = false\n\n channel.onmessage = (event: MessageEvent) => {\n const message = decodeMessage<R>(event.data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n }\n\n return {\n send(message) {\n if (closed) return\n channel.postMessage(encodeMessage(message))\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n // Already usable; hand control back to the caller before firing so that\n // `connect()` has finished wiring itself up.\n let cancelled = false\n queueMicrotask(() => {\n if (!cancelled && !closed) callback()\n })\n return () => {\n cancelled = true\n }\n },\n close() {\n closed = true\n listeners.clear()\n channel.onmessage = null\n channel.close()\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* WebSocket */\n/* -------------------------------------------------------------------------- */\n\nexport interface WebSocketTransportOptions {\n /** Reconnect with exponential backoff after an unexpected close. Default `true`. */\n reconnect?: boolean\n /** First backoff delay in ms. Default 500. */\n minDelayMs?: number\n /** Backoff ceiling in ms. Default 15000. */\n maxDelayMs?: number\n /** Injectable for tests and for Node (`ws`). Defaults to `globalThis.WebSocket`. */\n WebSocketImpl?: typeof WebSocket\n}\n\n/**\n * A WebSocket to a relay (see `scripts/relay.mjs`). Messages sent while the\n * socket is down are dropped rather than queued: the next `hello`/`snapshot`\n * exchange re-establishes the document anyway, and presence is re-sent on a\n * heartbeat.\n */\nexport function createWebSocketTransport<R extends UnknownRecord = UnknownRecord>(\n url: string,\n options: WebSocketTransportOptions = {},\n): Transport<R> {\n const reconnect = options.reconnect ?? true\n const minDelay = options.minDelayMs ?? 500\n const maxDelay = options.maxDelayMs ?? 15_000\n const Impl = options.WebSocketImpl ?? (globalThis as { WebSocket?: typeof WebSocket }).WebSocket\n if (!Impl) throw new Error(\"No WebSocket implementation available; pass options.WebSocketImpl\")\n\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n const open = createEmitter()\n const close = createEmitter()\n\n let socket: WebSocket | null = null\n let attempt = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n let disposed = false\n\n const connect = () => {\n if (disposed) return\n const ws = new Impl(url)\n socket = ws\n ws.onopen = () => {\n attempt = 0\n open.emit()\n }\n ws.onmessage = (event: MessageEvent) => {\n const message = decodeMessage<R>(event.data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n }\n ws.onerror = () => {\n // `onclose` always follows; the retry is scheduled there.\n }\n ws.onclose = () => {\n if (socket === ws) socket = null\n close.emit()\n if (!disposed && reconnect) schedule()\n }\n }\n\n const schedule = () => {\n if (timer !== null) return\n // Exponential backoff with full jitter, so a relay restart does not get a\n // thundering herd from every open tab.\n const ceiling = Math.min(maxDelay, minDelay * 2 ** attempt)\n attempt++\n const delay = Math.random() * ceiling\n timer = setTimeout(() => {\n timer = null\n connect()\n }, delay)\n }\n\n connect()\n\n return {\n send(message) {\n if (!socket || socket.readyState !== 1 /* OPEN */) return\n socket.send(encodeMessage(message))\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n const remove = open.add(callback)\n if (socket && socket.readyState === 1) queueMicrotask(callback)\n return remove\n },\n onClose(callback) {\n return close.add(callback)\n },\n close() {\n disposed = true\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n listeners.clear()\n open.clear()\n close.clear()\n const ws = socket\n socket = null\n if (ws) {\n ws.onopen = null\n ws.onmessage = null\n ws.onerror = null\n ws.onclose = null\n if (ws.readyState === 0 || ws.readyState === 1) ws.close()\n }\n },\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* in-memory */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Two transports wired to each other, going through the same JSON encode and\n * decode as the real ones. Delivery is asynchronous (a microtask) so that a\n * peer never observes a message inside its own `send()` call stack.\n */\nexport function createMemoryTransportPair<R extends UnknownRecord = UnknownRecord>(): [Transport<R>, Transport<R>] {\n const hub = createMemoryHub<R>()\n return [hub.join(), hub.join()]\n}\n\nexport interface MemoryHub<R extends UnknownRecord = UnknownRecord> {\n /** Add another peer to the room. */\n join(): Transport<R>\n}\n\n/** A room every joined transport broadcasts into (itself excluded). */\nexport function createMemoryHub<R extends UnknownRecord = UnknownRecord>(): MemoryHub<R> {\n const peers = new Set<{ deliver(data: string): void }>()\n return {\n join(): Transport<R> {\n const listeners = new Set<(message: SyncMessage<R>) => void>()\n let closed = false\n const peer = {\n deliver(data: string) {\n if (closed) return\n const message = decodeMessage<R>(data)\n if (!message) return\n for (const listener of Array.from(listeners)) listener(message)\n },\n }\n peers.add(peer)\n return {\n send(message) {\n if (closed) return\n const data = encodeMessage(message)\n for (const other of Array.from(peers)) {\n if (other === peer) continue\n queueMicrotask(() => other.deliver(data))\n }\n },\n onMessage(callback) {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n onOpen(callback) {\n let cancelled = false\n queueMicrotask(() => {\n if (!cancelled && !closed) callback()\n })\n return () => {\n cancelled = true\n }\n },\n close() {\n closed = true\n listeners.clear()\n peers.delete(peer)\n },\n }\n },\n }\n}\n","import { track, useValue } from \"@mocanvas/state/react\"\nimport { useEditorComponents, type Editor, type EditorRecord, type InstancePresence, type UnknownShape } from \"@mocanvas/editor\"\nimport { useEffect, useRef, useState, type CSSProperties } from \"react\"\nimport { createSyncClient, type SyncClient, type SyncClientOptions, type SyncStatus } from \"./SyncClient\"\nimport type { Transport } from \"./transport\"\n\nconst layerStyle: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n overflow: \"visible\",\n}\n\nexport interface CollaboratorCursorsProps {\n editor: Editor\n /** Draw what each collaborator has selected. Default `true`. */\n showSelection?: boolean\n /** Draw a name chip next to each cursor. Default `true`. */\n showNames?: boolean\n}\n\n/**\n * Other people's cursors and selections, drawn in screen space above the\n * canvas. Drop it inside `<Mocanvas>` or `<Canvas>`; it positions itself.\n *\n * The arrow below is the default. An app that supplies a\n * {@link TLEditorComponents.CollaboratorCursor} component gets that instead —\n * the slot was declared on two component maps and consulted by nothing, so an\n * app that replaced the cursor silently kept seeing ours.\n */\nexport const CollaboratorCursors = track(function CollaboratorCursors({\n editor,\n showSelection = true,\n showNames = true,\n}: CollaboratorCursorsProps) {\n const components = useEditorComponents()\n const collaborators = editor.getCollaboratorsOnCurrentPage()\n if (collaborators.length === 0) return null\n // `null` is an app switching the cursors off, which is different from not\n // supplying one; `undefined` means \"use ours\".\n const Slot = components.CollaboratorCursor\n return (\n <>\n <svg className=\"mocanvas-collaborators\" style={layerStyle}>\n {collaborators.map((presence) => (\n <g key={presence.id}>\n {showSelection ? <CollaboratorSelection editor={editor} presence={presence} /> : null}\n {Slot === undefined ? <CollaboratorCursor editor={editor} presence={presence} showName={showNames} /> : null}\n </g>\n ))}\n </svg>\n {/* A supplied slot is HTML, not SVG — `DefaultCursor` is a div — so it\n gets its own layer rather than being nested inside the <svg> above,\n where it would not render at all. */}\n {Slot ? (\n <div className=\"mocanvas-collaborator-cursors\" style={layerStyle}>\n {collaborators.map((presence) =>\n presence.cursor === null ? null : (\n <Slot\n key={presence.id}\n type=\"default\"\n rotation={presence.cursor.rotation}\n color={presence.color}\n name={showNames ? presence.userName || \"Anonymous\" : null}\n point={editor.pageToViewport(presence.cursor)}\n />\n ),\n )}\n </div>\n ) : null}\n </>\n )\n})\n\nconst CollaboratorCursor = track(function CollaboratorCursor({\n editor,\n presence,\n showName,\n}: {\n editor: Editor\n presence: InstancePresence\n showName: boolean\n}) {\n // A collaborator with no pointer — an agent, or someone whose pointer has left\n // the canvas — draws no cursor at all.\n if (presence.cursor === null) return null\n // Viewport space: the overlay is positioned inside the canvas container, not\n // the window.\n const point = editor.pageToViewport(presence.cursor)\n const name = presence.userName || \"Anonymous\"\n // A 16x22 arrow drawn from the hotspot, then the name chip below it.\n return (\n <g transform={`translate(${point.x}, ${point.y}) rotate(${presence.cursor.rotation})`}>\n <path\n d=\"M0 0 L0 17.3 L4.3 13.4 L7 19.4 L9.9 18 L7.2 12.2 L12.8 12 Z\"\n fill={presence.color}\n stroke=\"#fff\"\n strokeWidth={1}\n strokeLinejoin=\"round\"\n />\n {showName ? (\n <g transform=\"translate(11, 18)\">\n <rect rx={4} ry={4} width={Math.max(24, name.length * 7 + 12)} height={18} fill={presence.color} />\n <text x={6} y={13} fontSize={11} fontFamily=\"system-ui, sans-serif\" fill=\"#fff\">\n {name}\n </text>\n </g>\n ) : null}\n </g>\n )\n})\n\nconst CollaboratorSelection = track(function CollaboratorSelection({\n editor,\n presence,\n}: {\n editor: Editor\n presence: InstancePresence\n}) {\n const outlines: string[] = []\n for (const id of presence.selectedShapeIds) {\n const shape: UnknownShape | undefined = editor.getShape(id)\n if (!shape) continue\n const bounds = editor.getShapeGeometryBounds(shape)\n if (!bounds) continue\n const m = editor.getShapePageTransform(shape)\n // Transform the local corners to page space, then to screen space, so the\n // outline follows rotation without a nested SVG transform.\n const corners: [number, number][] = [\n [bounds.x, bounds.y],\n [bounds.maxX, bounds.y],\n [bounds.maxX, bounds.maxY],\n [bounds.x, bounds.maxY],\n ]\n const points = corners.map(([x, y]) => {\n // Viewport space, like the cursor above: this overlay lives in the canvas\n // container, so window offsets must not be added.\n const p = editor.pageToViewport({ x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f })\n return `${p.x},${p.y}`\n })\n outlines.push(points.join(\" \"))\n }\n if (outlines.length === 0) return null\n return (\n <>\n {outlines.map((points, i) => (\n <polygon key={i} points={points} fill=\"none\" stroke={presence.color} strokeWidth={1.5} opacity={0.8} />\n ))}\n </>\n )\n})\n\nexport interface UseSyncOptions {\n roomId: string\n /**\n * The channel to the room. Pass a factory when the transport should be\n * recreated with the room (the usual case); it is read once per connection.\n */\n transport: Transport | (() => Transport)\n /** Share this editor's cursor and selection. Default `true`. */\n presence?: boolean\n /** Set `false` to stay offline (a read-only view, say). Default `true`. */\n enabled?: boolean\n clientId?: string\n presenceTimeoutMs?: number\n presenceThrottleMs?: number\n onError?: (error: Error) => void\n}\n\nexport interface UseSyncResult {\n status: SyncStatus\n client: SyncClient | null\n}\n\n/**\n * Connect an editor's store to a room for as long as the component is mounted.\n * Everything is torn down (including the transport) on unmount.\n */\nexport function useSync(editor: Editor | null, options: UseSyncOptions): UseSyncResult {\n const { roomId, enabled = true, presence = true } = options\n // Kept in a ref so that inline factories and callbacks do not reconnect.\n const latest = useRef(options)\n latest.current = options\n\n const [client, setClient] = useState<SyncClient | null>(null)\n\n useEffect(() => {\n if (!editor || !enabled) {\n setClient(null)\n return\n }\n const current = latest.current\n const transport = typeof current.transport === \"function\" ? current.transport() : current.transport\n const clientOptions: SyncClientOptions<EditorRecord> = {\n store: editor.store,\n roomId,\n transport: transport as Transport<EditorRecord>,\n ...(presence ? { presence: { editor } } : {}),\n ...(current.clientId !== undefined ? { clientId: current.clientId } : {}),\n ...(current.presenceTimeoutMs !== undefined ? { presenceTimeoutMs: current.presenceTimeoutMs } : {}),\n ...(current.presenceThrottleMs !== undefined ? { presenceThrottleMs: current.presenceThrottleMs } : {}),\n onError: (error: Error) => latest.current.onError?.(error),\n }\n const next = createSyncClient<EditorRecord>(clientOptions)\n setClient(next)\n next.connect()\n return () => {\n setClient(null)\n next.dispose()\n }\n }, [editor, roomId, enabled, presence])\n\n const status = useValue(\"sync.status\", () => client?.getStatus().get() ?? \"offline\", [client])\n return { status, client }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mocanvas/sync",
3
- "version": "4.0.2",
3
+ "version": "4.1.1",
4
4
  "description": "Multiplayer for a mocanvas store: record diffs over a transport, plus presence and cursors.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Symbio Digital",
@@ -43,14 +43,16 @@
43
43
  "access": "public"
44
44
  },
45
45
  "dependencies": {
46
- "@mocanvas/editor": "4.0.2",
47
- "@mocanvas/state": "4.0.2",
48
- "@mocanvas/store": "4.0.2"
46
+ "@mocanvas/editor": "4.1.1",
47
+ "@mocanvas/state": "4.1.1",
48
+ "@mocanvas/store": "4.1.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/react": "^19.1.0",
52
+ "@types/react-dom": "^19.1.0",
52
53
  "@types/ws": "^8.5.0",
53
54
  "react": "^19.1.0",
55
+ "react-dom": "^19.1.0",
54
56
  "ws": "^8.18.0"
55
57
  },
56
58
  "peerDependencies": {