@pantheon-systems/css-client 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/auth.d.ts +54 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +72 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/broker.d.ts +41 -0
  6. package/dist/broker.d.ts.map +1 -0
  7. package/dist/broker.js +187 -0
  8. package/dist/broker.js.map +1 -0
  9. package/dist/client.d.ts +138 -0
  10. package/dist/client.d.ts.map +1 -0
  11. package/dist/client.js +130 -0
  12. package/dist/client.js.map +1 -0
  13. package/dist/content.d.ts +45 -0
  14. package/dist/content.d.ts.map +1 -0
  15. package/dist/content.js +65 -0
  16. package/dist/content.js.map +1 -0
  17. package/dist/endpoints/agent-edit.d.ts +79 -0
  18. package/dist/endpoints/agent-edit.d.ts.map +1 -0
  19. package/dist/endpoints/agent-edit.js +125 -0
  20. package/dist/endpoints/agent-edit.js.map +1 -0
  21. package/dist/endpoints/agent-registry.d.ts +59 -0
  22. package/dist/endpoints/agent-registry.d.ts.map +1 -0
  23. package/dist/endpoints/agent-registry.js +96 -0
  24. package/dist/endpoints/agent-registry.js.map +1 -0
  25. package/dist/endpoints/base.d.ts +49 -0
  26. package/dist/endpoints/base.d.ts.map +1 -0
  27. package/dist/endpoints/base.js +150 -0
  28. package/dist/endpoints/base.js.map +1 -0
  29. package/dist/endpoints/branches.d.ts +32 -0
  30. package/dist/endpoints/branches.d.ts.map +1 -0
  31. package/dist/endpoints/branches.js +69 -0
  32. package/dist/endpoints/branches.js.map +1 -0
  33. package/dist/endpoints/checkpoints.d.ts +36 -0
  34. package/dist/endpoints/checkpoints.d.ts.map +1 -0
  35. package/dist/endpoints/checkpoints.js +76 -0
  36. package/dist/endpoints/checkpoints.js.map +1 -0
  37. package/dist/endpoints/documents.d.ts +46 -0
  38. package/dist/endpoints/documents.d.ts.map +1 -0
  39. package/dist/endpoints/documents.js +101 -0
  40. package/dist/endpoints/documents.js.map +1 -0
  41. package/dist/endpoints/index.d.ts +15 -0
  42. package/dist/endpoints/index.d.ts.map +1 -0
  43. package/dist/endpoints/index.js +16 -0
  44. package/dist/endpoints/index.js.map +1 -0
  45. package/dist/endpoints/merge.d.ts +51 -0
  46. package/dist/endpoints/merge.d.ts.map +1 -0
  47. package/dist/endpoints/merge.js +104 -0
  48. package/dist/endpoints/merge.js.map +1 -0
  49. package/dist/endpoints/presence.d.ts +55 -0
  50. package/dist/endpoints/presence.d.ts.map +1 -0
  51. package/dist/endpoints/presence.js +72 -0
  52. package/dist/endpoints/presence.js.map +1 -0
  53. package/dist/endpoints/sites.d.ts +20 -0
  54. package/dist/endpoints/sites.d.ts.map +1 -0
  55. package/dist/endpoints/sites.js +38 -0
  56. package/dist/endpoints/sites.js.map +1 -0
  57. package/dist/endpoints/versions.d.ts +37 -0
  58. package/dist/endpoints/versions.d.ts.map +1 -0
  59. package/dist/endpoints/versions.js +64 -0
  60. package/dist/endpoints/versions.js.map +1 -0
  61. package/dist/errors.d.ts +61 -0
  62. package/dist/errors.d.ts.map +1 -0
  63. package/dist/errors.js +87 -0
  64. package/dist/errors.js.map +1 -0
  65. package/dist/index.d.ts +20 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +20 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/jwt-utils.d.ts +13 -0
  70. package/dist/jwt-utils.d.ts.map +1 -0
  71. package/dist/jwt-utils.js +45 -0
  72. package/dist/jwt-utils.js.map +1 -0
  73. package/dist/oauth.d.ts +64 -0
  74. package/dist/oauth.d.ts.map +1 -0
  75. package/dist/oauth.js +62 -0
  76. package/dist/oauth.js.map +1 -0
  77. package/dist/realtime.d.ts +314 -0
  78. package/dist/realtime.d.ts.map +1 -0
  79. package/dist/realtime.js +615 -0
  80. package/dist/realtime.js.map +1 -0
  81. package/dist/types.d.ts +703 -0
  82. package/dist/types.d.ts.map +1 -0
  83. package/dist/types.js +7 -0
  84. package/dist/types.js.map +1 -0
  85. package/dist/utils.d.ts +3 -0
  86. package/dist/utils.d.ts.map +1 -0
  87. package/dist/utils.js +17 -0
  88. package/dist/utils.js.map +1 -0
  89. package/package.json +56 -0
@@ -0,0 +1,615 @@
1
+ /**
2
+ * Phase 2.1: RealtimeClient
3
+ *
4
+ * WebSocket-based real-time collaboration client using Yjs CRDT.
5
+ * Provides bidirectional sync between client and DocumentSession Durable Object.
6
+ * Uses ReconnectingWebSocket from partysocket for automatic reconnection with exponential backoff.
7
+ */
8
+ import * as Y from 'yjs';
9
+ import { WebSocket as ReconnectingWebSocket } from 'partysocket';
10
+ /**
11
+ * Real-time collaboration client using Yjs CRDT over WebSocket.
12
+ * Uses PartySocket for automatic reconnection with exponential backoff.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const client = new RealtimeClient({
17
+ * baseUrl: 'wss://api.example.com',
18
+ * onUpdate: (snapshot) => {
19
+ * console.log('Document updated:', snapshot);
20
+ * },
21
+ * onReconnecting: (attempt) => {
22
+ * console.log(`Reconnecting... attempt ${attempt}`);
23
+ * },
24
+ * reconnection: {
25
+ * maxRetries: 10,
26
+ * minReconnectionDelay: 1000,
27
+ * maxReconnectionDelay: 30000,
28
+ * },
29
+ * });
30
+ *
31
+ * client.connect({
32
+ * siteId: 'site-123',
33
+ * branchId: 'branch-456',
34
+ * documentPath: 'pages/home',
35
+ * actorId: 'user-789',
36
+ * actorType: 'user',
37
+ * });
38
+ *
39
+ * // When done
40
+ * client.disconnect();
41
+ * ```
42
+ */
43
+ export class RealtimeClient {
44
+ config;
45
+ ydoc;
46
+ ws = null;
47
+ connected = false;
48
+ intentionalDisconnect = false;
49
+ currentApiKey;
50
+ tokenRefreshInFlight = false;
51
+ hasConnectedOnce = false;
52
+ lastReportedRetryCount = 0;
53
+ reconnectCheckInterval = null;
54
+ visibilityHandler = null;
55
+ // Rate limiting state
56
+ sendTimestamps = [];
57
+ pendingUpdates = [];
58
+ flushTimer = null;
59
+ static RATE_THRESHOLD = 40; // Start buffering at this rate
60
+ static RATE_WINDOW_MS = 1000; // 1-second sliding window
61
+ // Echo suppression: track the last JSON snapshot sent to the DO.
62
+ // If a Y.Doc update produces the same JSON snapshot as we last sent,
63
+ // it's a no-op echo (typically from Puck's onChange after receiving
64
+ // a remote sync) and should NOT be sent to avoid overwriting newer data.
65
+ lastSentSnapshot = null;
66
+ // Delivery acknowledgment state
67
+ pendingDeliveryAcks = new Map();
68
+ static DELIVERY_ACK_TIMEOUT_MS = 5000;
69
+ // Publish request state
70
+ pendingPublishRequests = new Map();
71
+ static PUBLISH_TIMEOUT_MS = 30000;
72
+ // Action metadata state — best-effort metadata sent alongside CRDT updates
73
+ pendingActionMetadata = null;
74
+ constructor(config) {
75
+ this.config = config;
76
+ this.ydoc = new Y.Doc();
77
+ // Listen for local changes to broadcast
78
+ this.ydoc.on('update', (update, origin) => {
79
+ // Only broadcast if this update didn't come from remote
80
+ if (origin !== 'remote' && this.ws && this.ws.readyState === WebSocket.OPEN) {
81
+ const root = this.ydoc.getMap('root');
82
+ const currentSnapshot = JSON.stringify(root.toJSON());
83
+ // Echo suppression: if the Y.Doc's JSON snapshot hasn't changed since
84
+ // the last time we sent (or since the last remote update), this is a
85
+ // no-op full-rebuild echo. Don't send it — it would overwrite newer
86
+ // data on the DO if another client has edited since.
87
+ if (currentSnapshot === this.lastSentSnapshot) {
88
+ return;
89
+ }
90
+ this.lastSentSnapshot = currentSnapshot;
91
+ this.rateLimitedSend(update);
92
+ // Send any pending action metadata after the CRDT update
93
+ this.sendPendingActionMetadata();
94
+ }
95
+ else if (origin === 'remote') {
96
+ // Track the remote snapshot so we can detect echoes
97
+ const root = this.ydoc.getMap('root');
98
+ this.lastSentSnapshot = JSON.stringify(root.toJSON());
99
+ }
100
+ });
101
+ }
102
+ /**
103
+ * Connect to a document session via WebSocket.
104
+ * Uses PartySocket for automatic reconnection on connection loss.
105
+ *
106
+ * @param params - Connection parameters including site, branch, document, and actor info
107
+ */
108
+ connect(params) {
109
+ if (this.ws) {
110
+ this.disconnect();
111
+ }
112
+ this.currentApiKey = this.config.apiKey;
113
+ this.intentionalDisconnect = false;
114
+ this.hasConnectedOnce = false;
115
+ this.lastReportedRetryCount = 0;
116
+ // Build WebSocket URL
117
+ const baseUrl = this.config.baseUrl.replace(/^http/, 'ws');
118
+ const encodedPath = encodeURIComponent(params.documentPath);
119
+ const url = new URL(`/api/sites/${params.siteId}/branches/${params.branchId}/documents/${encodedPath}/connect`, baseUrl);
120
+ // Add actor info as query params (WebSocket can't send headers)
121
+ url.searchParams.set('actorId', params.actorId);
122
+ url.searchParams.set('actorType', params.actorType);
123
+ // Note: apiKey is NOT added to the base URL here — it is injected inside
124
+ // urlProvider so that a refreshed token is picked up on each reconnect.
125
+ // Add session ID for agent authorization (obtained from startEdit())
126
+ if (params.sessionId) {
127
+ url.searchParams.set('sessionId', params.sessionId);
128
+ }
129
+ // Get reconnection config with defaults
130
+ const reconnection = this.config.reconnection ?? {};
131
+ const maxRetries = reconnection.maxRetries ?? Infinity;
132
+ const minReconnectionDelay = reconnection.minReconnectionDelay ?? 1000;
133
+ const maxReconnectionDelay = reconnection.maxReconnectionDelay ?? 30000;
134
+ const reconnectionDelayGrowFactor = reconnection.reconnectionDelayGrowFactor ?? 1.5;
135
+ // Build the base WebSocket URL (without state vector)
136
+ const baseWsUrl = url.toString();
137
+ // Create a URL provider function for PartySocket.
138
+ // On the initial connect the state vector is omitted so the server sends
139
+ // the full CRDT history. On every subsequent (re)connect the current Yjs
140
+ // state vector is included so the server can respond with only the delta.
141
+ // Also injects the current API key on every call so that a refreshed token
142
+ // (set by tokenRefresher after an unexpected close) is used for each
143
+ // reconnect attempt.
144
+ const urlProvider = () => {
145
+ const connectUrl = new URL(baseWsUrl);
146
+ // Inject current token — may have been refreshed after an unexpected disconnect
147
+ if (this.currentApiKey) {
148
+ connectUrl.searchParams.set('apiKey', this.currentApiKey);
149
+ }
150
+ // Include the state vector on every call after the first successful
151
+ // connection so the server can send a delta rather than full history.
152
+ if (this.hasConnectedOnce) {
153
+ const sv = Y.encodeStateVector(this.ydoc);
154
+ const svBase64 = btoa(String.fromCharCode(...sv));
155
+ connectUrl.searchParams.set('stateVector', svBase64);
156
+ }
157
+ return connectUrl.toString();
158
+ };
159
+ // Create ReconnectingWebSocket with URL provider and reconnection options
160
+ this.ws = new ReconnectingWebSocket(urlProvider, [], {
161
+ maxRetries,
162
+ minReconnectionDelay,
163
+ maxReconnectionDelay,
164
+ reconnectionDelayGrowFactor,
165
+ });
166
+ this.ws.binaryType = 'arraybuffer';
167
+ // Start monitoring for reconnection attempts
168
+ this.startReconnectMonitoring();
169
+ // Start visibility change monitoring to handle tab backgrounding
170
+ this.startVisibilityMonitoring();
171
+ this.ws.addEventListener('open', () => {
172
+ this.connected = true;
173
+ // Send local state when doc has content (seeded or from prior session).
174
+ // On initial connect with seeded data, this ensures bidirectional sync.
175
+ // On reconnect, this sends any edits made while disconnected.
176
+ if (this.ws) {
177
+ const root = this.ydoc.getMap('root');
178
+ if (root.size > 0) {
179
+ const localState = Y.encodeStateAsUpdate(this.ydoc);
180
+ this.ws.send(localState);
181
+ }
182
+ }
183
+ this.hasConnectedOnce = true;
184
+ this.lastReportedRetryCount = 0;
185
+ this.config.onConnect?.();
186
+ });
187
+ this.ws.addEventListener('message', (event) => {
188
+ try {
189
+ // Distinguish text (presence JSON) from binary (Yjs CRDT) messages
190
+ if (typeof event.data === 'string') {
191
+ // Text frame: JSON presence message
192
+ this.handleTextMessage(event.data);
193
+ return;
194
+ }
195
+ // Binary frame: Yjs CRDT update
196
+ const data = event.data;
197
+ const update = new Uint8Array(data);
198
+ // Apply remote update to local Y.Doc
199
+ Y.applyUpdate(this.ydoc, update, 'remote');
200
+ // Notify listeners of new snapshot
201
+ const root = this.ydoc.getMap('root');
202
+ const snapshot = root.toJSON();
203
+ this.config.onUpdate?.(snapshot);
204
+ }
205
+ catch (error) {
206
+ console.error('[Realtime] Error processing message:', error);
207
+ this.config.onError?.(error instanceof Error ? error : new Error('Failed to process message'));
208
+ }
209
+ });
210
+ this.ws.addEventListener('close', (event) => {
211
+ this.connected = false;
212
+ // Check for authorization failure close codes (4401 Unauthorized, 4403 Forbidden)
213
+ const closeEvent = event;
214
+ if (closeEvent.code === 4401 || closeEvent.code === 4403) {
215
+ // Authorization failure - do not attempt to reconnect
216
+ this.intentionalDisconnect = true;
217
+ this.stopReconnectMonitoring();
218
+ this.ws?.close();
219
+ this.ws = null;
220
+ // Call authorization error callback
221
+ const reason = closeEvent.reason || 'Authorization failed';
222
+ this.config.onAuthorizationError?.(new Error(reason));
223
+ this.config.onDisconnect?.();
224
+ return;
225
+ }
226
+ // Only call onDisconnect for intentional disconnects or when max retries exceeded
227
+ if (this.intentionalDisconnect) {
228
+ this.stopReconnectMonitoring();
229
+ this.ws = null;
230
+ this.config.onDisconnect?.();
231
+ }
232
+ // PartySocket will automatically attempt to reconnect otherwise
233
+ // Fire-and-forget token refresh on unexpected close so the next
234
+ // urlProvider call uses a fresh token for the reconnect URL.
235
+ // Guard prevents concurrent refreshes when the socket flaps rapidly.
236
+ if (!this.intentionalDisconnect && this.config.tokenRefresher && !this.tokenRefreshInFlight) {
237
+ this.tokenRefreshInFlight = true;
238
+ this.config.tokenRefresher().then((freshToken) => {
239
+ if (freshToken) {
240
+ this.currentApiKey = freshToken;
241
+ }
242
+ }).catch(() => {
243
+ // Ignore errors — the reconnect will proceed with the stale token
244
+ }).finally(() => {
245
+ this.tokenRefreshInFlight = false;
246
+ });
247
+ }
248
+ });
249
+ this.ws.addEventListener('error', () => {
250
+ this.config.onError?.(new Error('WebSocket error'));
251
+ });
252
+ }
253
+ /**
254
+ * Start monitoring for reconnection attempts by polling retryCount.
255
+ * This is needed because PartySocket doesn't expose a reconnection callback.
256
+ */
257
+ startReconnectMonitoring() {
258
+ this.stopReconnectMonitoring();
259
+ // Poll every 100ms to detect retry count changes
260
+ this.reconnectCheckInterval = setInterval(() => {
261
+ if (!this.ws || this.intentionalDisconnect) {
262
+ return;
263
+ }
264
+ const currentRetryCount = this.ws.retryCount;
265
+ // If retry count increased and we've connected before, we're reconnecting
266
+ if (currentRetryCount > this.lastReportedRetryCount &&
267
+ this.hasConnectedOnce) {
268
+ this.lastReportedRetryCount = currentRetryCount;
269
+ this.config.onReconnecting?.(currentRetryCount);
270
+ }
271
+ }, 100);
272
+ }
273
+ /**
274
+ * Stop the reconnection monitoring interval.
275
+ */
276
+ stopReconnectMonitoring() {
277
+ if (this.reconnectCheckInterval) {
278
+ clearInterval(this.reconnectCheckInterval);
279
+ this.reconnectCheckInterval = null;
280
+ }
281
+ }
282
+ /**
283
+ * Start monitoring for page visibility changes.
284
+ * When the page becomes visible after being hidden, check connection health
285
+ * and force a reconnection sync if needed.
286
+ */
287
+ startVisibilityMonitoring() {
288
+ this.stopVisibilityMonitoring();
289
+ // Only set up if document is available (browser environment)
290
+ if (typeof document === 'undefined') {
291
+ return;
292
+ }
293
+ this.visibilityHandler = () => {
294
+ if (document.visibilityState === 'visible' && !this.intentionalDisconnect && this.ws) {
295
+ // Force a reconnection by calling reconnect() on PartySocket.
296
+ // This ensures we get a fresh connection even if the old one appears open
297
+ // but is actually stale (server closed it while tab was backgrounded).
298
+ // PartySocket's reconnect() will close the current connection and open a new one.
299
+ // The 'open' event handler will then send local state for bidirectional sync.
300
+ this.ws.reconnect();
301
+ }
302
+ };
303
+ document.addEventListener('visibilitychange', this.visibilityHandler);
304
+ }
305
+ /**
306
+ * Stop visibility change monitoring.
307
+ */
308
+ stopVisibilityMonitoring() {
309
+ if (this.visibilityHandler && typeof document !== 'undefined') {
310
+ document.removeEventListener('visibilitychange', this.visibilityHandler);
311
+ this.visibilityHandler = null;
312
+ }
313
+ }
314
+ /**
315
+ * Disconnect from the current session.
316
+ * This permanently closes the connection and stops any reconnection attempts.
317
+ */
318
+ disconnect() {
319
+ this.intentionalDisconnect = true;
320
+ this.stopReconnectMonitoring();
321
+ this.stopVisibilityMonitoring();
322
+ // Clean up rate limiting state
323
+ if (this.flushTimer !== null) {
324
+ clearTimeout(this.flushTimer);
325
+ this.flushTimer = null;
326
+ }
327
+ this.pendingUpdates = [];
328
+ this.sendTimestamps = [];
329
+ // Reject all pending delivery ack promises
330
+ for (const [requestId, pending] of this.pendingDeliveryAcks) {
331
+ clearTimeout(pending.timer);
332
+ pending.reject(new Error('Disconnected'));
333
+ this.pendingDeliveryAcks.delete(requestId);
334
+ }
335
+ // Reject all pending publish request promises
336
+ for (const [requestId, pending] of this.pendingPublishRequests) {
337
+ clearTimeout(pending.timer);
338
+ pending.reject(new Error('Disconnected'));
339
+ this.pendingPublishRequests.delete(requestId);
340
+ }
341
+ if (this.ws) {
342
+ this.ws.close();
343
+ this.ws = null;
344
+ this.connected = false;
345
+ }
346
+ }
347
+ /**
348
+ * Apply a local Yjs update.
349
+ * This is typically called from Puck-Yjs binding when local edits occur.
350
+ * Uses rate-aware sending to avoid exceeding the server's rate limit.
351
+ *
352
+ * @param update - Raw Yjs update bytes
353
+ */
354
+ applyLocalUpdate(update) {
355
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
356
+ this.rateLimitedSend(update);
357
+ }
358
+ }
359
+ /**
360
+ * Set pending action metadata to be sent after the next CRDT update.
361
+ * The metadata is best-effort — if the WebSocket is not open or the
362
+ * send fails, the CRDT update still goes through without metadata.
363
+ *
364
+ * @param meta - Action type and metadata from Puck's onAction callback
365
+ */
366
+ setActionMetadata(meta) {
367
+ this.pendingActionMetadata = meta;
368
+ }
369
+ /**
370
+ * Send any pending action metadata as a text message and clear it.
371
+ * Called after a CRDT update is sent to associate the metadata with
372
+ * the most recent edit.
373
+ */
374
+ sendPendingActionMetadata() {
375
+ if (this.pendingActionMetadata && this.ws && this.ws.readyState === WebSocket.OPEN) {
376
+ try {
377
+ this.ws.send(JSON.stringify({
378
+ type: 'action_metadata',
379
+ ...this.pendingActionMetadata,
380
+ }));
381
+ }
382
+ catch {
383
+ // Best-effort — don't break the CRDT sync if metadata send fails
384
+ }
385
+ this.pendingActionMetadata = null;
386
+ }
387
+ }
388
+ /**
389
+ * Send a Yjs update with rate awareness.
390
+ * Sends immediately when under the threshold (40 msgs/sec).
391
+ * Buffers and coalesces updates when approaching the server's 50 msg/sec limit.
392
+ */
393
+ rateLimitedSend(update) {
394
+ if (!this.ws)
395
+ return;
396
+ const now = Date.now();
397
+ // Prune timestamps outside the sliding window
398
+ this.sendTimestamps = this.sendTimestamps.filter((ts) => now - ts < RealtimeClient.RATE_WINDOW_MS);
399
+ if (this.sendTimestamps.length < RealtimeClient.RATE_THRESHOLD) {
400
+ // Under threshold — send immediately
401
+ this.sendTimestamps.push(now);
402
+ this.ws.send(update);
403
+ }
404
+ else {
405
+ // At or above threshold — buffer for coalesced flush
406
+ this.pendingUpdates.push(update);
407
+ this.scheduleFlush();
408
+ }
409
+ }
410
+ /**
411
+ * Schedule a flush of buffered updates after the rate window resets.
412
+ */
413
+ scheduleFlush() {
414
+ if (this.flushTimer !== null)
415
+ return;
416
+ this.flushTimer = setTimeout(() => {
417
+ this.flushTimer = null;
418
+ this.flushPendingUpdates();
419
+ }, RealtimeClient.RATE_WINDOW_MS);
420
+ }
421
+ /**
422
+ * Flush all buffered updates as a single coalesced message.
423
+ */
424
+ flushPendingUpdates() {
425
+ if (this.pendingUpdates.length === 0)
426
+ return;
427
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
428
+ this.pendingUpdates = [];
429
+ return;
430
+ }
431
+ // Merge all pending updates into a single Yjs update
432
+ const merged = Y.mergeUpdates(this.pendingUpdates);
433
+ this.pendingUpdates = [];
434
+ // Reset window and send
435
+ this.sendTimestamps = [Date.now()];
436
+ this.ws.send(merged);
437
+ }
438
+ /**
439
+ * Get the current document snapshot as JSON.
440
+ */
441
+ getSnapshot() {
442
+ const root = this.ydoc.getMap('root');
443
+ return root.toJSON();
444
+ }
445
+ /**
446
+ * Get the underlying Y.Doc for direct manipulation.
447
+ * Use with caution - prefer using the Puck-Yjs binding utilities.
448
+ */
449
+ getYDoc() {
450
+ return this.ydoc;
451
+ }
452
+ /**
453
+ * Check if currently connected.
454
+ */
455
+ isConnected() {
456
+ return this.connected;
457
+ }
458
+ /**
459
+ * Check if presence updates are available via WebSocket.
460
+ * Returns true when connected and able to send/receive presence messages.
461
+ */
462
+ get presenceViaWebSocket() {
463
+ return this.connected && this.ws !== null && this.ws.readyState === WebSocket.OPEN;
464
+ }
465
+ /**
466
+ * Send focus regions update to the server.
467
+ * @param focusRegions - JSON paths the actor is focused on
468
+ * @returns true if message was sent, false if not connected
469
+ */
470
+ sendFocusRegions(focusRegions) {
471
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
472
+ return false;
473
+ }
474
+ const message = {
475
+ type: 'focus_region_update',
476
+ focusRegions,
477
+ timestamp: Date.now(),
478
+ };
479
+ this.ws.send(JSON.stringify(message));
480
+ return true;
481
+ }
482
+ /**
483
+ * Send a presence heartbeat to keep the connection alive.
484
+ * @param state - Optional state update (active, idle, editing)
485
+ */
486
+ sendHeartbeat(state) {
487
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
488
+ return;
489
+ }
490
+ const message = {
491
+ type: 'presence_heartbeat',
492
+ timestamp: Date.now(),
493
+ ...(state && { state }),
494
+ };
495
+ this.ws.send(JSON.stringify(message));
496
+ }
497
+ /**
498
+ * Wait for the server to acknowledge that all preceding WebSocket messages
499
+ * have been processed. This uses TCP ordering guarantees: a text frame sent
500
+ * after binary CRDT updates is guaranteed to arrive after those updates.
501
+ * The server echoes back a delivery_ack with the matching requestId.
502
+ *
503
+ * Used before publish to ensure the Durable Object has received and applied
504
+ * the latest edits before the HTTP publish request arrives.
505
+ *
506
+ * @returns Promise that resolves when the server confirms delivery
507
+ * @throws Error if not connected or if timeout expires (5 seconds)
508
+ */
509
+ waitForDelivery() {
510
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
511
+ return Promise.reject(new Error('Not connected'));
512
+ }
513
+ const ws = this.ws;
514
+ const requestId = crypto.randomUUID();
515
+ return new Promise((resolve, reject) => {
516
+ const timer = setTimeout(() => {
517
+ this.pendingDeliveryAcks.delete(requestId);
518
+ reject(new Error('Delivery acknowledgment timed out'));
519
+ }, RealtimeClient.DELIVERY_ACK_TIMEOUT_MS);
520
+ this.pendingDeliveryAcks.set(requestId, { resolve, reject, timer });
521
+ ws.send(JSON.stringify({
522
+ type: 'delivery_ack_request',
523
+ requestId,
524
+ timestamp: Date.now(),
525
+ }));
526
+ });
527
+ }
528
+ /**
529
+ * Request the server to publish the current document via WebSocket.
530
+ * TCP ordering guarantees all preceding binary CRDT updates have been
531
+ * processed before this message is handled, eliminating stale-version races.
532
+ *
533
+ * The Durable Object handles the entire flow: flush to Postgres, then
534
+ * call /internal/publish to create the checkpoint.
535
+ *
536
+ * @returns Promise that resolves with the publish result
537
+ * @throws Error if not connected or if timeout expires (30 seconds)
538
+ */
539
+ requestPublish() {
540
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
541
+ return Promise.reject(new Error('Not connected'));
542
+ }
543
+ const ws = this.ws;
544
+ const requestId = crypto.randomUUID();
545
+ return new Promise((resolve, reject) => {
546
+ const timer = setTimeout(() => {
547
+ this.pendingPublishRequests.delete(requestId);
548
+ reject(new Error('Publish request timed out'));
549
+ }, RealtimeClient.PUBLISH_TIMEOUT_MS);
550
+ this.pendingPublishRequests.set(requestId, { resolve, reject, timer });
551
+ ws.send(JSON.stringify({
552
+ type: 'publish_request',
553
+ requestId,
554
+ timestamp: Date.now(),
555
+ }));
556
+ });
557
+ }
558
+ /**
559
+ * Handle incoming text (JSON) messages for presence protocol.
560
+ * @param data - Raw JSON string from WebSocket
561
+ */
562
+ handleTextMessage(data) {
563
+ try {
564
+ const message = JSON.parse(data);
565
+ switch (message.type) {
566
+ case 'presence_update':
567
+ this.config.onPresenceUpdate?.(message.actors);
568
+ break;
569
+ case 'focus_region_broadcast':
570
+ this.config.onFocusRegionBroadcast?.(message.actorId, message.focusRegions);
571
+ break;
572
+ case 'focus_region_ack':
573
+ // Acknowledgment received
574
+ break;
575
+ case 'delivery_ack': {
576
+ const pending = this.pendingDeliveryAcks.get(message.requestId);
577
+ if (pending) {
578
+ clearTimeout(pending.timer);
579
+ this.pendingDeliveryAcks.delete(message.requestId);
580
+ pending.resolve();
581
+ }
582
+ break;
583
+ }
584
+ case 'publish_result': {
585
+ const pendingPublish = this.pendingPublishRequests.get(message.requestId);
586
+ if (pendingPublish) {
587
+ clearTimeout(pendingPublish.timer);
588
+ this.pendingPublishRequests.delete(message.requestId);
589
+ pendingPublish.resolve({
590
+ success: message.success,
591
+ publishedVersionId: message.publishedVersionId,
592
+ checkpoint: message.checkpoint,
593
+ error: message.error,
594
+ });
595
+ }
596
+ break;
597
+ }
598
+ case 'presence_error':
599
+ if (message.code === 'RATE_LIMITED') {
600
+ this.config.onRateLimited?.();
601
+ }
602
+ else {
603
+ console.error('[Realtime] Presence error:', message.code, message.message);
604
+ }
605
+ break;
606
+ default:
607
+ console.warn('[Realtime] Unknown message type:', message.type);
608
+ }
609
+ }
610
+ catch (error) {
611
+ console.error('[Realtime] Error parsing text message:', error);
612
+ }
613
+ }
614
+ }
615
+ //# sourceMappingURL=realtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"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"}