@defensestation/ysync-go 0.1.0-dev.3.3d87b03

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # @defensestation/ysync-go
2
+
3
+ Browser [Yjs](https://yjs.dev) providers for the
4
+ [ysync-go](https://github.com/defensestation/ysync-go) collaboration backend.
5
+ Two interchangeable providers - pick per environment, mix freely: a
6
+ WebSocket client and a Connect RPC client collaborate live on the same
7
+ document.
8
+
9
+ ```bash
10
+ npm install @defensestation/ysync-go yjs
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import * as Y from "yjs";
17
+ import { WebSocketYjsProvider } from "@defensestation/ysync-go";
18
+
19
+ const ydoc = new Y.Doc();
20
+ const provider = new WebSocketYjsProvider({
21
+ ydoc,
22
+ docId: "doc-1",
23
+ baseUrl: "https://collab.example.com",
24
+ user: { name: "Ada", color: "#4338ca" },
25
+ });
26
+
27
+ // Bind ydoc to your editor (BlockNote, Tiptap, y-prosemirror, y-codemirror…)
28
+ // and provider.awareness to its cursor/presence plugin.
29
+
30
+ provider.onStatus((status) => console.log(status)); // connecting | connected | disconnected
31
+ provider.destroy(); // on unmount: announces departure, closes cleanly
32
+ ```
33
+
34
+ Or over Connect RPC (unary + server-streaming; works everywhere `fetch`
35
+ streams responses):
36
+
37
+ ```ts
38
+ import { ConnectYjsProvider } from "@defensestation/ysync-go";
39
+
40
+ const provider = new ConnectYjsProvider({
41
+ ydoc,
42
+ docId: "doc-1",
43
+ baseUrl: "https://collab.example.com",
44
+ user: { name: "Ada" },
45
+ });
46
+ ```
47
+
48
+ ## Choosing a provider
49
+
50
+ | | `WebSocketYjsProvider` | `ConnectYjsProvider` |
51
+ | --- | --- | --- |
52
+ | Wire | JSON frames over `/ysync/ws` | `ysync.v1.YjsSyncService` (Connect) |
53
+ | Edits upstream | one message per update | micro-batched unary pushes with idempotency keys |
54
+ | Best when | plain WebSockets fit your infra | you already run Connect/gRPC middleware (auth interceptors, gateways) |
55
+
56
+ Both reconnect automatically with exponential backoff (500 ms → 5 s),
57
+ re-join with the document's state vector, and resync - offline edits made
58
+ while disconnected are delivered on reconnect (Yjs deduplicates replays).
59
+
60
+ ## Common API (both providers)
61
+
62
+ | Member | Description |
63
+ | --- | --- |
64
+ | `awareness` | The `y-protocols` `Awareness` instance - hand it to your editor's cursor plugin. The local state's `user` field is prefilled from `options.user`. |
65
+ | `clientId` | Stable per-provider ID (UUID unless you pass `clientId`). |
66
+ | `status` / `onStatus(fn)` | `"connecting" \| "connected" \| "disconnected"`; returns an unsubscribe function. |
67
+ | `identity` / `onIdentity(fn)` | Server-assigned presence identity (sanitized `name`, palette `color` distinct among participants) - use it for cursor rendering instead of trusting local input. |
68
+ | `destroy()` | Detaches from the doc, announces departure so peers drop the cursor immediately, and closes the connection. |
69
+
70
+ ### Options
71
+
72
+ Shared: `ydoc`, `docId`, `user: { name, color? }`, `baseUrl?` (defaults to
73
+ `window.location.origin`), `clientId?`, `reconnectInitialDelayMs?`,
74
+ `reconnectMaxDelayMs?`, `awarenessThrottleMs?` (default 150 ms,
75
+ leading+trailing throttle for cursor moves).
76
+
77
+ `ConnectYjsProvider` only: `updateFlushDelayMs?` (default 100 ms) -
78
+ micro-batch window merging keystrokes into one push while a request is in
79
+ flight; retries resend the identical payload under the same idempotency
80
+ key. `rpcClient?` lets you supply your own Connect client (e.g. with auth
81
+ interceptors); the default client forwards `user.name`/`user.color` as
82
+ `x-user-*` headers, which the server treats as unauthenticated hints.
83
+
84
+ ## Server
85
+
86
+ This package is the client half. The server is a Go library -
87
+ [github.com/defensestation/ysync-go](https://github.com/defensestation/ysync-go) - with
88
+ Postgres persistence, Redis multi-node fanout, and auth hooks. Clone the
89
+ repo and `go run ./examples/minimal -addr :8080` for a complete local
90
+ server.
91
+
92
+ The full wire protocol (for writing clients in other languages) is
93
+ documented in
94
+ [docs/protocol.md](https://github.com/defensestation/ysync-go/blob/main/docs/protocol.md).
95
+
96
+ Generated protobuf descriptors are exported under `@defensestation/ysync-go/ysync/v1`
97
+ if you need raw RPC access.
98
+
99
+ ## License
100
+
101
+ MIT
@@ -0,0 +1,107 @@
1
+ import { Awareness } from "y-protocols/awareness";
2
+ import * as Y from "yjs";
3
+ import type { YjsProviderIdentity, YjsProviderIdentityListener, YjsProviderStatus, YjsProviderStatusListener } from "./provider-status.js";
4
+ declare function createDefaultClient(baseUrl: string, user?: {
5
+ name: string;
6
+ color?: string;
7
+ }): import("@connectrpc/connect").Client<import("@bufbuild/protobuf/codegenv2").GenService<{
8
+ sync: {
9
+ methodKind: "bidi_streaming";
10
+ input: typeof import("../gen/ysync/v1/sync_pb.js").SyncRequestSchema;
11
+ output: typeof import("../gen/ysync/v1/sync_pb.js").SyncResponseSchema;
12
+ };
13
+ joinDocument: {
14
+ methodKind: "unary";
15
+ input: typeof import("../gen/ysync/v1/sync_pb.js").JoinDocumentRequestSchema;
16
+ output: typeof import("../gen/ysync/v1/sync_pb.js").JoinDocumentResponseSchema;
17
+ };
18
+ pushYjsUpdate: {
19
+ methodKind: "unary";
20
+ input: typeof import("../gen/ysync/v1/sync_pb.js").PushYjsUpdateRequestSchema;
21
+ output: typeof import("../gen/ysync/v1/sync_pb.js").PushYjsUpdateResponseSchema;
22
+ };
23
+ pushAwarenessUpdate: {
24
+ methodKind: "unary";
25
+ input: typeof import("../gen/ysync/v1/sync_pb.js").PushAwarenessUpdateRequestSchema;
26
+ output: typeof import("../gen/ysync/v1/sync_pb.js").PushAwarenessUpdateResponseSchema;
27
+ };
28
+ streamDocumentFrames: {
29
+ methodKind: "server_streaming";
30
+ input: typeof import("../gen/ysync/v1/sync_pb.js").StreamDocumentFramesRequestSchema;
31
+ output: typeof import("../gen/ysync/v1/sync_pb.js").StreamDocumentFramesResponseSchema;
32
+ };
33
+ leaveDocument: {
34
+ methodKind: "unary";
35
+ input: typeof import("../gen/ysync/v1/sync_pb.js").LeaveDocumentRequestSchema;
36
+ output: typeof import("../gen/ysync/v1/sync_pb.js").LeaveDocumentResponseSchema;
37
+ };
38
+ listDocumentUpdates: {
39
+ methodKind: "unary";
40
+ input: typeof import("../gen/ysync/v1/sync_pb.js").ListDocumentUpdatesRequestSchema;
41
+ output: typeof import("../gen/ysync/v1/sync_pb.js").ListDocumentUpdatesResponseSchema;
42
+ };
43
+ compactDocument: {
44
+ methodKind: "unary";
45
+ input: typeof import("../gen/ysync/v1/sync_pb.js").CompactDocumentRequestSchema;
46
+ output: typeof import("../gen/ysync/v1/sync_pb.js").CompactDocumentResponseSchema;
47
+ };
48
+ }>>;
49
+ export type YjsSyncRpcClient = ReturnType<typeof createDefaultClient>;
50
+ export interface ConnectYjsProviderOptions {
51
+ ydoc: Y.Doc;
52
+ docId: string;
53
+ baseUrl?: string;
54
+ rpcClient?: YjsSyncRpcClient;
55
+ clientId?: string;
56
+ user: {
57
+ name: string;
58
+ color?: string;
59
+ };
60
+ reconnectInitialDelayMs?: number;
61
+ reconnectMaxDelayMs?: number;
62
+ awarenessThrottleMs?: number;
63
+ updateFlushDelayMs?: number;
64
+ }
65
+ export declare class ConnectYjsProvider {
66
+ readonly awareness: Awareness;
67
+ readonly clientId: string;
68
+ private readonly client;
69
+ private readonly options;
70
+ private abortController?;
71
+ private reconnectTimer?;
72
+ private awarenessSendTimer?;
73
+ private awarenessDirty;
74
+ private updateFlushTimer?;
75
+ private updatesDirty;
76
+ private destroyed;
77
+ private joined;
78
+ private reconnectDelayMs;
79
+ private pendingUpdates;
80
+ private inFlightBatch?;
81
+ private pushInFlight;
82
+ private updateCounter;
83
+ private currentStatus;
84
+ private readonly statusListeners;
85
+ private currentIdentity?;
86
+ private readonly identityListeners;
87
+ private readonly yjsUpdateHandler;
88
+ private readonly awarenessUpdateHandler;
89
+ constructor(options: ConnectYjsProviderOptions);
90
+ get status(): YjsProviderStatus;
91
+ get identity(): YjsProviderIdentity | undefined;
92
+ onIdentity(listener: YjsProviderIdentityListener): () => void;
93
+ private applyIdentity;
94
+ onStatus(listener: YjsProviderStatusListener): () => void;
95
+ private setStatus;
96
+ destroy(): void;
97
+ private connect;
98
+ private runSession;
99
+ private scheduleReconnect;
100
+ private scheduleUpdateFlush;
101
+ private flushUpdates;
102
+ private scheduleAwarenessSend;
103
+ private sendLocalAwarenessNow;
104
+ private applyFrame;
105
+ }
106
+ export {};
107
+ //# sourceMappingURL=connect-yjs-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect-yjs-provider.d.ts","sourceRoot":"","sources":["../../src/client/connect-yjs-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAA+C,MAAM,uBAAuB,CAAC;AAC/F,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAGzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAY3I,iBAAS,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAapF;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAI7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAQD,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0K;IAClM,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,cAAc,CAAC,CAAgC;IACvD,OAAO,CAAC,kBAAkB,CAAC,CAAgC;IAC3D,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,gBAAgB,CAAC,CAAgC;IACzD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,cAAc,CAAoB;IAI1C,OAAO,CAAC,aAAa,CAAC,CAAsC;IAC5D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwC;IACxE,OAAO,CAAC,eAAe,CAAC,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0C;IAE5E,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAI/B;IAKF,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAIrC;gBAEU,OAAO,EAAE,yBAAyB;IAsB9C,IAAI,MAAM,IAAI,iBAAiB,CAE9B;IAGD,IAAI,QAAQ,IAAI,mBAAmB,GAAG,SAAS,CAE9C;IAGD,UAAU,CAAC,QAAQ,EAAE,2BAA2B,GAAG,MAAM,IAAI;IAK7D,OAAO,CAAC,aAAa;IAYrB,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,IAAI;IAKzD,OAAO,CAAC,SAAS;IAMjB,OAAO;IA2BP,OAAO,CAAC,OAAO;YAQD,UAAU;IA8CxB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,mBAAmB;YAoBb,YAAY;IAuC1B,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,UAAU;CAenB"}
@@ -0,0 +1,333 @@
1
+ import { Code, ConnectError, createClient } from "@connectrpc/connect";
2
+ import { createConnectTransport } from "@connectrpc/connect-web";
3
+ import { Awareness, applyAwarenessUpdate, encodeAwarenessUpdate } from "y-protocols/awareness";
4
+ import * as Y from "yjs";
5
+ import { YjsSyncService } from "../gen/ysync/v1/sync_pb.js";
6
+ import { makeClientId } from "./id.js";
7
+ const REMOTE_ORIGIN = "connectrpc-remote";
8
+ const DEFAULT_RECONNECT_INITIAL_DELAY_MS = 500;
9
+ const DEFAULT_RECONNECT_MAX_DELAY_MS = 5000;
10
+ const DEFAULT_AWARENESS_THROTTLE_MS = 150;
11
+ const DEFAULT_UPDATE_FLUSH_DELAY_MS = 100;
12
+ function defaultBaseUrl() {
13
+ return typeof window !== "undefined" && window.location?.origin ? window.location.origin : "http://localhost:8080";
14
+ }
15
+ function createDefaultClient(baseUrl, user) {
16
+ const interceptors = [];
17
+ if (user) {
18
+ // Actor identity for audit history; the server reads these headers on
19
+ // every RPC. Real deployments should derive identity from auth instead.
20
+ const asciiOnly = (value) => /^[\x20-\x7E]*$/.test(value);
21
+ interceptors.push((next) => (req) => {
22
+ if (asciiOnly(user.name))
23
+ req.header.set("x-user-name", user.name);
24
+ if (user.color && asciiOnly(user.color))
25
+ req.header.set("x-user-color", user.color);
26
+ return next(req);
27
+ });
28
+ }
29
+ return createClient(YjsSyncService, createConnectTransport({ baseUrl, useBinaryFormat: true, interceptors }));
30
+ }
31
+ // Browser-compatible Connect RPC provider. Browsers cannot open bidirectional
32
+ // Connect streams (fetch has no streaming request bodies), so this provider
33
+ // uses the unary/server-stream workflow from the proto contract:
34
+ // JoinDocument -> StreamDocumentFrames + PushYjsUpdate/PushAwarenessUpdate,
35
+ // then LeaveDocument on teardown. The bidirectional Sync RPC remains available
36
+ // for clients on transports that support it (e.g. connect-go over HTTP/2).
37
+ export class ConnectYjsProvider {
38
+ awareness;
39
+ clientId;
40
+ client;
41
+ options;
42
+ abortController;
43
+ reconnectTimer;
44
+ awarenessSendTimer;
45
+ awarenessDirty = false;
46
+ updateFlushTimer;
47
+ updatesDirty = false;
48
+ destroyed = false;
49
+ joined = false;
50
+ reconnectDelayMs;
51
+ pendingUpdates = [];
52
+ // The current batch is frozen with its idempotency key so a retry after a
53
+ // failed push resends the exact same payload; updates queued in the
54
+ // meantime merge into the NEXT batch.
55
+ inFlightBatch;
56
+ pushInFlight = false;
57
+ updateCounter = 0;
58
+ currentStatus = "connecting";
59
+ statusListeners = new Set();
60
+ currentIdentity;
61
+ identityListeners = new Set();
62
+ yjsUpdateHandler = (update, origin) => {
63
+ if (origin === REMOTE_ORIGIN)
64
+ return;
65
+ this.pendingUpdates.push(update);
66
+ this.scheduleUpdateFlush();
67
+ };
68
+ // Only push changes to the LOCAL awareness state. Remote states arrive from
69
+ // the server already fanned out; re-broadcasting them would multiply every
70
+ // cursor move by the number of connected clients.
71
+ awarenessUpdateHandler = ({ added, updated, removed }) => {
72
+ const local = this.awareness.clientID;
73
+ if (!added.includes(local) && !updated.includes(local) && !removed.includes(local))
74
+ return;
75
+ this.scheduleAwarenessSend();
76
+ };
77
+ constructor(options) {
78
+ this.options = {
79
+ ...options,
80
+ reconnectInitialDelayMs: options.reconnectInitialDelayMs ?? DEFAULT_RECONNECT_INITIAL_DELAY_MS,
81
+ reconnectMaxDelayMs: options.reconnectMaxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS,
82
+ awarenessThrottleMs: options.awarenessThrottleMs ?? DEFAULT_AWARENESS_THROTTLE_MS,
83
+ updateFlushDelayMs: options.updateFlushDelayMs ?? DEFAULT_UPDATE_FLUSH_DELAY_MS,
84
+ };
85
+ this.clientId = options.clientId ?? makeClientId();
86
+ this.client = options.rpcClient ?? createDefaultClient(options.baseUrl ?? defaultBaseUrl(), options.user);
87
+ this.reconnectDelayMs = this.options.reconnectInitialDelayMs;
88
+ this.awareness = new Awareness(options.ydoc);
89
+ this.awareness.setLocalStateField("user", { name: options.user.name, color: options.user.color });
90
+ this.options.ydoc.on("update", this.yjsUpdateHandler);
91
+ // No extra heartbeat timer: y-protocols renews the local awareness state
92
+ // (bumping its clock) roughly every 15s while idle, which fires this
93
+ // handler. A fixed-interval resend of the same clock would be ignored by
94
+ // receivers anyway.
95
+ this.awareness.on("update", this.awarenessUpdateHandler);
96
+ this.connect();
97
+ }
98
+ get status() {
99
+ return this.currentStatus;
100
+ }
101
+ // Server-assigned presence identity; undefined until the join completes.
102
+ get identity() {
103
+ return this.currentIdentity;
104
+ }
105
+ // Returns an unsubscribe function.
106
+ onIdentity(listener) {
107
+ this.identityListeners.add(listener);
108
+ return () => this.identityListeners.delete(listener);
109
+ }
110
+ applyIdentity(name, color) {
111
+ if (!name && !color)
112
+ return;
113
+ const identity = {
114
+ name: name || this.options.user.name,
115
+ color: color ?? this.options.user.color,
116
+ };
117
+ this.currentIdentity = identity;
118
+ this.awareness.setLocalStateField("user", { name: identity.name, color: identity.color });
119
+ for (const listener of this.identityListeners)
120
+ listener(identity);
121
+ }
122
+ // Returns an unsubscribe function.
123
+ onStatus(listener) {
124
+ this.statusListeners.add(listener);
125
+ return () => this.statusListeners.delete(listener);
126
+ }
127
+ setStatus(status) {
128
+ if (this.currentStatus === status)
129
+ return;
130
+ this.currentStatus = status;
131
+ for (const listener of this.statusListeners)
132
+ listener(status);
133
+ }
134
+ destroy() {
135
+ if (this.destroyed)
136
+ return;
137
+ this.options.ydoc.off("update", this.yjsUpdateHandler);
138
+ this.awareness.off("update", this.awarenessUpdateHandler);
139
+ // Announce departure so peers drop the cursor immediately instead of
140
+ // after the 30s awareness timeout.
141
+ this.awareness.setLocalState(null);
142
+ if (this.joined) {
143
+ void this.client
144
+ .pushAwarenessUpdate({
145
+ docId: this.options.docId,
146
+ clientId: this.clientId,
147
+ awarenessUpdate: encodeAwarenessUpdate(this.awareness, [this.awareness.clientID]),
148
+ })
149
+ .catch(() => undefined);
150
+ }
151
+ this.destroyed = true;
152
+ this.setStatus("disconnected");
153
+ if (this.reconnectTimer)
154
+ clearTimeout(this.reconnectTimer);
155
+ if (this.awarenessSendTimer)
156
+ clearTimeout(this.awarenessSendTimer);
157
+ if (this.updateFlushTimer)
158
+ clearTimeout(this.updateFlushTimer);
159
+ void this.client
160
+ .leaveDocument({ docId: this.options.docId, clientId: this.clientId })
161
+ .catch(() => undefined);
162
+ this.abortController?.abort();
163
+ }
164
+ connect() {
165
+ if (this.destroyed)
166
+ return;
167
+ this.abortController?.abort();
168
+ this.abortController = new AbortController();
169
+ this.setStatus("connecting");
170
+ void this.runSession(this.abortController.signal);
171
+ }
172
+ async runSession(signal) {
173
+ try {
174
+ const join = await this.client.joinDocument({
175
+ docId: this.options.docId,
176
+ clientId: this.clientId,
177
+ stateVector: Y.encodeStateVector(this.options.ydoc),
178
+ }, { signal });
179
+ for (const update of join.syncUpdates) {
180
+ if (update.length > 0)
181
+ Y.applyUpdate(this.options.ydoc, update, REMOTE_ORIGIN);
182
+ }
183
+ this.joined = true;
184
+ this.applyIdentity(join.assignedDisplayName, join.assignedColor);
185
+ this.setStatus("connected");
186
+ this.reconnectDelayMs = this.options.reconnectInitialDelayMs;
187
+ this.sendLocalAwarenessNow();
188
+ void this.flushUpdates();
189
+ const frames = this.client.streamDocumentFrames({
190
+ docId: this.options.docId,
191
+ clientId: this.clientId,
192
+ afterSequence: join.sequence,
193
+ }, { signal });
194
+ for await (const response of frames) {
195
+ if (response.frame)
196
+ this.applyFrame(response.frame);
197
+ }
198
+ this.joined = false;
199
+ if (!this.destroyed && !signal.aborted) {
200
+ this.setStatus("disconnected");
201
+ this.scheduleReconnect();
202
+ }
203
+ }
204
+ catch (error) {
205
+ this.joined = false;
206
+ if (!this.destroyed && !isAbortError(error)) {
207
+ this.setStatus("disconnected");
208
+ console.error("Yjs sync stream failed", error);
209
+ this.scheduleReconnect();
210
+ }
211
+ }
212
+ }
213
+ scheduleReconnect() {
214
+ if (this.destroyed || this.reconnectTimer)
215
+ return;
216
+ const delay = this.reconnectDelayMs;
217
+ this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, this.options.reconnectMaxDelayMs);
218
+ this.reconnectTimer = setTimeout(() => {
219
+ this.reconnectTimer = undefined;
220
+ this.connect();
221
+ }, delay);
222
+ }
223
+ // Leading + trailing micro-batch, same shape as the awareness throttle.
224
+ scheduleUpdateFlush() {
225
+ if (this.destroyed)
226
+ return;
227
+ if (this.options.updateFlushDelayMs <= 0) {
228
+ void this.flushUpdates();
229
+ return;
230
+ }
231
+ if (this.updateFlushTimer) {
232
+ this.updatesDirty = true;
233
+ return;
234
+ }
235
+ void this.flushUpdates();
236
+ this.updateFlushTimer = setTimeout(() => {
237
+ this.updateFlushTimer = undefined;
238
+ if (this.updatesDirty) {
239
+ this.updatesDirty = false;
240
+ this.scheduleUpdateFlush();
241
+ }
242
+ }, this.options.updateFlushDelayMs);
243
+ }
244
+ async flushUpdates() {
245
+ if (this.pushInFlight || !this.joined)
246
+ return;
247
+ this.pushInFlight = true;
248
+ try {
249
+ while ((this.inFlightBatch || this.pendingUpdates.length > 0) && this.joined && !this.destroyed) {
250
+ if (!this.inFlightBatch) {
251
+ // Merge everything queued so far into one push. Under network
252
+ // latency this naturally batches keystrokes: while one push is in
253
+ // flight, new updates accumulate and go out as a single request.
254
+ const batch = this.pendingUpdates.splice(0);
255
+ this.inFlightBatch = {
256
+ key: `${this.clientId}-${this.updateCounter++}`,
257
+ update: batch.length === 1 ? batch[0] : Y.mergeUpdates(batch),
258
+ };
259
+ }
260
+ try {
261
+ await this.client.pushYjsUpdate({
262
+ docId: this.options.docId,
263
+ clientId: this.clientId,
264
+ yjsUpdate: this.inFlightBatch.update,
265
+ idempotencyKey: this.inFlightBatch.key,
266
+ });
267
+ }
268
+ catch (error) {
269
+ if (!this.destroyed && !isAbortError(error)) {
270
+ console.error("Yjs update push failed", error);
271
+ this.scheduleReconnect();
272
+ }
273
+ return;
274
+ }
275
+ this.inFlightBatch = undefined;
276
+ }
277
+ }
278
+ finally {
279
+ this.pushInFlight = false;
280
+ }
281
+ }
282
+ // Leading + trailing throttle: the first change sends immediately, changes
283
+ // arriving inside the window (e.g. cursor movement while typing or
284
+ // selecting) collapse into one trailing send.
285
+ scheduleAwarenessSend() {
286
+ if (this.destroyed)
287
+ return;
288
+ if (this.awarenessSendTimer) {
289
+ this.awarenessDirty = true;
290
+ return;
291
+ }
292
+ this.sendLocalAwarenessNow();
293
+ this.awarenessSendTimer = setTimeout(() => {
294
+ this.awarenessSendTimer = undefined;
295
+ if (this.awarenessDirty) {
296
+ this.awarenessDirty = false;
297
+ this.scheduleAwarenessSend();
298
+ }
299
+ }, this.options.awarenessThrottleMs);
300
+ }
301
+ sendLocalAwarenessNow() {
302
+ if (this.destroyed || !this.joined)
303
+ return;
304
+ void this.client
305
+ .pushAwarenessUpdate({
306
+ docId: this.options.docId,
307
+ clientId: this.clientId,
308
+ awarenessUpdate: encodeAwarenessUpdate(this.awareness, [this.awareness.clientID]),
309
+ })
310
+ .catch(() => undefined);
311
+ }
312
+ applyFrame(frame) {
313
+ switch (frame.payload.case) {
314
+ case "syncUpdate":
315
+ case "yjsUpdate":
316
+ Y.applyUpdate(this.options.ydoc, frame.payload.value, REMOTE_ORIGIN);
317
+ this.reconnectDelayMs = this.options.reconnectInitialDelayMs;
318
+ break;
319
+ case "awarenessUpdate":
320
+ applyAwarenessUpdate(this.awareness, frame.payload.value, REMOTE_ORIGIN);
321
+ break;
322
+ case "error":
323
+ console.error("Yjs sync server error", frame.payload.value);
324
+ break;
325
+ }
326
+ }
327
+ }
328
+ function isAbortError(error) {
329
+ if (error instanceof DOMException && error.name === "AbortError")
330
+ return true;
331
+ return error instanceof ConnectError && error.code === Code.Canceled;
332
+ }
333
+ //# sourceMappingURL=connect-yjs-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect-yjs-provider.js","sourceRoot":"","sources":["../../src/client/connect-yjs-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function makeClientId(): string;
2
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/client/id.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,WAqB3B"}
@@ -0,0 +1,21 @@
1
+ export function makeClientId() {
2
+ const cryptoApi = globalThis.crypto;
3
+ if (typeof cryptoApi?.randomUUID === "function") {
4
+ return cryptoApi.randomUUID();
5
+ }
6
+ if (typeof cryptoApi?.getRandomValues === "function") {
7
+ const bytes = cryptoApi.getRandomValues(new Uint8Array(16));
8
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
9
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
10
+ const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0"));
11
+ return [
12
+ hex.slice(0, 4).join(""),
13
+ hex.slice(4, 6).join(""),
14
+ hex.slice(6, 8).join(""),
15
+ hex.slice(8, 10).join(""),
16
+ hex.slice(10, 16).join(""),
17
+ ].join("-");
18
+ }
19
+ return `client-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
20
+ }
21
+ //# sourceMappingURL=id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/client/id.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY;IAC1B,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC;IACpC,IAAI,OAAO,SAAS,EAAE,UAAU,KAAK,UAAU,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;IAED,IAAI,OAAO,SAAS,EAAE,eAAe,KAAK,UAAU,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,SAAS,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5D,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;QACpC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;QACpC,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACzE,OAAO;YACL,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;SAC3B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACd,CAAC;IAED,OAAO,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AACxF,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type YjsProviderStatus = "connecting" | "connected" | "disconnected";
2
+ export type YjsProviderStatusListener = (status: YjsProviderStatus) => void;
3
+ export type YjsProviderIdentity = {
4
+ name: string;
5
+ color?: string;
6
+ };
7
+ export type YjsProviderIdentityListener = (identity: YjsProviderIdentity) => void;
8
+ //# sourceMappingURL=provider-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-status.d.ts","sourceRoot":"","sources":["../../src/client/provider-status.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,CAAC;AAE5E,MAAM,MAAM,yBAAyB,GAAG,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAI5E,MAAM,MAAM,mBAAmB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,2BAA2B,GAAG,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-status.js","sourceRoot":"","sources":["../../src/client/provider-status.ts"],"names":[],"mappings":""}