@electric-sql/y-electric 0.1.20 → 0.1.21
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/dist/cjs/index.cjs +89 -117
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.browser.mjs +1 -1
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.mjs +89 -117
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/cjs/index.cjs
CHANGED
|
@@ -43,26 +43,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
43
43
|
mod
|
|
44
44
|
));
|
|
45
45
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
46
|
-
var __async = (__this, __arguments, generator) => {
|
|
47
|
-
return new Promise((resolve, reject) => {
|
|
48
|
-
var fulfilled = (value) => {
|
|
49
|
-
try {
|
|
50
|
-
step(generator.next(value));
|
|
51
|
-
} catch (e) {
|
|
52
|
-
reject(e);
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
var rejected = (value) => {
|
|
56
|
-
try {
|
|
57
|
-
step(generator.throw(value));
|
|
58
|
-
} catch (e) {
|
|
59
|
-
reject(e);
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
63
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
46
|
|
|
67
47
|
// src/index.ts
|
|
68
48
|
var src_exports = {};
|
|
@@ -274,85 +254,79 @@ var ElectricProvider = class extends import_observable.ObservableV2 {
|
|
|
274
254
|
}
|
|
275
255
|
// TODO: add an optional throttler that batches updates
|
|
276
256
|
// before pushing to the server
|
|
277
|
-
applyDocumentUpdate(update, origin) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.sendOperations();
|
|
284
|
-
});
|
|
257
|
+
async applyDocumentUpdate(update, origin) {
|
|
258
|
+
if (origin === `server`) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
this.batch(update);
|
|
262
|
+
this.sendOperations();
|
|
285
263
|
}
|
|
286
|
-
sendOperations() {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
this.disconnect();
|
|
308
|
-
}
|
|
264
|
+
async sendOperations() {
|
|
265
|
+
var _a;
|
|
266
|
+
if (!this.connected || this.sendingPendingChanges) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
this.sendingPendingChanges = true;
|
|
271
|
+
while (this.pendingChanges && this.pendingChanges.length > 2 && this.connected) {
|
|
272
|
+
const sending = this.pendingChanges;
|
|
273
|
+
this.pendingChanges = null;
|
|
274
|
+
const encoder = encoding.createEncoder();
|
|
275
|
+
encoding.writeVarUint8Array(encoder, sending);
|
|
276
|
+
const success = await send(
|
|
277
|
+
encoder,
|
|
278
|
+
this.documentUpdates.sendUrl,
|
|
279
|
+
(_a = this.fetchClient) != null ? _a : fetch,
|
|
280
|
+
this.documentUpdates.sendErrorRetryHandler
|
|
281
|
+
);
|
|
282
|
+
if (!success) {
|
|
283
|
+
this.batch(sending);
|
|
284
|
+
this.disconnect();
|
|
309
285
|
}
|
|
310
|
-
this.resumeState.stableStateVector = Y.encodeStateVector(this.doc);
|
|
311
|
-
this.emit(`resumeState`, [this.resumeState]);
|
|
312
|
-
} finally {
|
|
313
|
-
this.sendingPendingChanges = false;
|
|
314
286
|
}
|
|
315
|
-
|
|
287
|
+
this.resumeState.stableStateVector = Y.encodeStateVector(this.doc);
|
|
288
|
+
this.emit(`resumeState`, [this.resumeState]);
|
|
289
|
+
} finally {
|
|
290
|
+
this.sendingPendingChanges = false;
|
|
291
|
+
}
|
|
316
292
|
}
|
|
317
|
-
applyAwarenessUpdate(awarenessUpdate, origin) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
this.disconnect();
|
|
350
|
-
}
|
|
293
|
+
async applyAwarenessUpdate(awarenessUpdate, origin) {
|
|
294
|
+
var _a;
|
|
295
|
+
if (origin !== `local` || !this.connected) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
this.pendingAwarenessUpdate = awarenessUpdate;
|
|
299
|
+
if (this.sendingAwarenessState) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
this.sendingAwarenessState = true;
|
|
303
|
+
try {
|
|
304
|
+
while (this.pendingAwarenessUpdate && this.connected) {
|
|
305
|
+
const update = this.pendingAwarenessUpdate;
|
|
306
|
+
this.pendingAwarenessUpdate = null;
|
|
307
|
+
const { added, updated, removed } = update;
|
|
308
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
309
|
+
const encoder = encoding.createEncoder();
|
|
310
|
+
encoding.writeVarUint8Array(
|
|
311
|
+
encoder,
|
|
312
|
+
awarenessProtocol.encodeAwarenessUpdate(
|
|
313
|
+
this.awarenessUpdates.protocol,
|
|
314
|
+
changedClients
|
|
315
|
+
)
|
|
316
|
+
);
|
|
317
|
+
const success = await send(
|
|
318
|
+
encoder,
|
|
319
|
+
this.awarenessUpdates.sendUrl,
|
|
320
|
+
(_a = this.fetchClient) != null ? _a : fetch,
|
|
321
|
+
this.awarenessUpdates.sendErrorRetryHandler
|
|
322
|
+
);
|
|
323
|
+
if (!success) {
|
|
324
|
+
this.disconnect();
|
|
351
325
|
}
|
|
352
|
-
} finally {
|
|
353
|
-
this.sendingAwarenessState = false;
|
|
354
326
|
}
|
|
355
|
-
}
|
|
327
|
+
} finally {
|
|
328
|
+
this.sendingAwarenessState = false;
|
|
329
|
+
}
|
|
356
330
|
}
|
|
357
331
|
awarenessShapeHandler(messages, offset, handle) {
|
|
358
332
|
for (const message of messages) {
|
|
@@ -381,31 +355,29 @@ var ElectricProvider = class extends import_observable.ObservableV2 {
|
|
|
381
355
|
}
|
|
382
356
|
}
|
|
383
357
|
};
|
|
384
|
-
function send(encoder, endpoint, fetchClient, retryHandler) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
throw new Error(`Server did not return 2xx`);
|
|
399
|
-
}
|
|
400
|
-
return true;
|
|
401
|
-
} catch (error) {
|
|
402
|
-
const shouldRetry = yield (_a = retryHandler == null ? void 0 : retryHandler({
|
|
403
|
-
response,
|
|
404
|
-
error
|
|
405
|
-
})) != null ? _a : false;
|
|
406
|
-
return shouldRetry;
|
|
358
|
+
async function send(encoder, endpoint, fetchClient, retryHandler) {
|
|
359
|
+
var _a;
|
|
360
|
+
let response;
|
|
361
|
+
const op = encoding.toUint8Array(encoder);
|
|
362
|
+
try {
|
|
363
|
+
response = await fetchClient(endpoint, {
|
|
364
|
+
method: `PUT`,
|
|
365
|
+
headers: {
|
|
366
|
+
"Content-Type": `application/octet-stream`
|
|
367
|
+
},
|
|
368
|
+
body: op
|
|
369
|
+
});
|
|
370
|
+
if (!response.ok) {
|
|
371
|
+
throw new Error(`Server did not return 2xx`);
|
|
407
372
|
}
|
|
408
|
-
|
|
373
|
+
return true;
|
|
374
|
+
} catch (error) {
|
|
375
|
+
const shouldRetry = await ((_a = retryHandler == null ? void 0 : retryHandler({
|
|
376
|
+
response,
|
|
377
|
+
error
|
|
378
|
+
})) != null ? _a : false);
|
|
379
|
+
return shouldRetry;
|
|
380
|
+
}
|
|
409
381
|
}
|
|
410
382
|
|
|
411
383
|
// src/local-storage-resume-state.ts
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/y-electric.ts","../../src/local-storage-resume-state.ts","../../src/utils.ts"],"sourcesContent":["export * from './y-electric'\nexport * from './local-storage-resume-state'\nexport * from './utils'\nexport * from './types'\n","import * as encoding from 'lib0/encoding'\nimport * as decoding from 'lib0/decoding'\nimport * as awarenessProtocol from 'y-protocols/awareness'\nimport { ObservableV2 } from 'lib0/observable'\nimport * as env from 'lib0/environment'\nimport * as Y from 'yjs'\nimport {\n GetExtensions,\n isChangeMessage,\n isControlMessage,\n Message,\n Offset,\n Row,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/client'\nimport {\n YProvider,\n ResumeState,\n SendErrorRetryHandler,\n ElectricProviderOptions,\n} from './types'\n\ntype AwarenessUpdate = {\n added: number[]\n updated: number[]\n removed: number[]\n}\n\nexport class ElectricProvider<\n RowWithDocumentUpdate extends Row<decoding.Decoder> = never,\n RowWithAwarenessUpdate extends Row<decoding.Decoder> = never,\n> extends ObservableV2<YProvider> {\n private doc: Y.Doc\n\n private documentUpdates: {\n shape: ShapeStreamOptions<GetExtensions<RowWithDocumentUpdate>>\n sendUrl: string | URL\n getUpdateFromRow: (row: RowWithDocumentUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private awarenessUpdates?: {\n shape: ShapeStreamOptions<GetExtensions<RowWithAwarenessUpdate>>\n sendUrl: string | URL\n protocol: awarenessProtocol.Awareness\n getUpdateFromRow: (row: RowWithAwarenessUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private _connected: boolean = false\n private _synced: boolean = false\n\n private resumeState: ResumeState\n private sendingPendingChanges: boolean = false\n private pendingChanges: Uint8Array | null = null\n private sendingAwarenessState: boolean = false\n private pendingAwarenessUpdate: AwarenessUpdate | null = null\n\n private documentUpdateHandler: (\n update: Uint8Array,\n origin: unknown,\n doc: Y.Doc,\n transaction: Y.Transaction\n ) => void\n private awarenessUpdateHandler?: (\n update: AwarenessUpdate,\n origin: unknown\n ) => void\n\n private exitHandler: () => void\n private unsubscribeShapes?: () => void\n\n private fetchClient?: typeof fetch\n\n /**\n * Creates a new ElectricProvider instance that connects YJS documents to Electric SQL.\n *\n * @constructor\n * @param {ElectricProviderOptions} options - Configuration options for the provider\n * @param {Y.Doc} options.doc - The YJS document to be synchronized\n * @param {Object} options.documentUpdates - Document updates configuration\n * @param {ShapeStreamOptions} options.documentUpdates.shape - Options for the document updates shape stream\n * @param {string|URL} options.documentUpdates.sendUrl - URL endpoint for sending document updates\n * @param {Function} options.documentUpdates.getUpdateFromRow - Function to extract document update from row\n * @param {SendErrorRetryHandler} [options.documentUpdates.sendErrorRetryHandler] - Error handler for retrying document updates\n * @param {Object} [options.awarenessUpdates] - Awareness updates configuration (optional)\n * @param {ShapeStreamOptions} options.awarenessUpdates.shape - Options for the awareness updates shape stream\n * @param {string|URL} options.awarenessUpdates.sendUrl - URL endpoint for sending awareness updates\n * @param {awarenessProtocol.Awareness} options.awarenessUpdates.protocol - Awareness protocol instance\n * @param {Function} options.awarenessUpdates.getUpdateFromRow - Function to extract awareness update from row\n * @param {SendErrorRetryHandler} [options.awarenessUpdates.sendErrorRetryHandler] - Error handler for retrying awareness updates\n * @param {ResumeState} [options.resumeState] - Resume state for the provider\n * @param {boolean} [options.connect=true] - Whether to automatically connect upon initialization\n * @param {typeof fetch} [options.fetchClient] - Custom fetch implementation to use for HTTP requests\n */\n constructor({\n doc,\n documentUpdates: documentUpdatesConfig,\n awarenessUpdates: awarenessUpdatesConfig,\n resumeState,\n connect = true,\n fetchClient,\n }: ElectricProviderOptions<RowWithDocumentUpdate, RowWithAwarenessUpdate>) {\n super()\n\n this.doc = doc\n this.documentUpdates = documentUpdatesConfig\n this.awarenessUpdates = awarenessUpdatesConfig\n this.resumeState = resumeState ?? {}\n\n this.fetchClient = fetchClient\n\n this.exitHandler = () => {\n if (env.isNode && typeof process !== `undefined`) {\n process.on(`exit`, this.destroy.bind(this))\n }\n }\n\n this.documentUpdateHandler = this.doc.on(\n `update`,\n this.applyDocumentUpdate.bind(this)\n )\n if (this.awarenessUpdates) {\n this.awarenessUpdateHandler = this.applyAwarenessUpdate.bind(this)\n this.awarenessUpdates.protocol.on(`update`, this.awarenessUpdateHandler!)\n }\n\n // enqueue unsynced changes from document if the\n // resume state provides the document state vector\n if (this.resumeState?.stableStateVector) {\n this.pendingChanges = Y.encodeStateAsUpdate(\n this.doc,\n this.resumeState.stableStateVector\n )\n }\n\n if (connect) {\n this.connect()\n }\n }\n\n get synced() {\n return this._synced\n }\n\n set synced(state) {\n if (this._synced !== state) {\n this._synced = state\n this.emit(`synced`, [state])\n this.emit(`sync`, [state])\n }\n }\n\n set connected(state) {\n if (this._connected !== state) {\n this._connected = state\n if (state) {\n this.sendOperations()\n }\n this.emit(`status`, [{ status: state ? `connected` : `disconnected` }])\n }\n }\n\n get connected() {\n return this._connected\n }\n\n private batch(update: Uint8Array) {\n if (this.pendingChanges) {\n this.pendingChanges = Y.mergeUpdates([this.pendingChanges, update])\n } else {\n this.pendingChanges = update\n }\n }\n\n destroy() {\n this.disconnect()\n\n this.doc.off(`update`, this.documentUpdateHandler)\n this.awarenessUpdates?.protocol.off(`update`, this.awarenessUpdateHandler!)\n\n if (env.isNode && typeof process !== `undefined`) {\n process.off(`exit`, this.exitHandler!)\n }\n super.destroy()\n }\n\n disconnect() {\n this.unsubscribeShapes?.()\n\n if (!this.connected) {\n return\n }\n\n if (this.awarenessUpdates) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n Array.from(this.awarenessUpdates.protocol.getStates().keys()).filter(\n (client) => client !== this.awarenessUpdates!.protocol.clientID\n ),\n this\n )\n\n // try to notifying other clients that we are disconnecting\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n [this.awarenessUpdates.protocol.clientID],\n `local`\n )\n\n this.awarenessUpdates.protocol.setLocalState({})\n }\n\n // TODO: await for events before closing\n this.emit(`connection-close`, [])\n\n this.pendingAwarenessUpdate = null\n\n this.connected = false\n this.synced = false\n }\n\n connect() {\n if (this.connected) {\n return\n }\n const abortController = new AbortController()\n\n const operationsStream = new ShapeStream<RowWithDocumentUpdate>({\n ...this.documentUpdates.shape,\n ...this.resumeState.document,\n signal: abortController.signal,\n })\n\n const operationsShapeUnsubscribe = operationsStream.subscribe(\n (messages) => {\n this.operationsShapeHandler(\n messages,\n operationsStream.lastOffset,\n operationsStream.shapeHandle!\n )\n }\n )\n\n let awarenessShapeUnsubscribe: () => void | undefined\n if (this.awarenessUpdates) {\n const awarenessStream = new ShapeStream<RowWithAwarenessUpdate>({\n ...this.awarenessUpdates.shape,\n ...this.resumeState.awareness,\n signal: abortController.signal,\n })\n\n awarenessShapeUnsubscribe = awarenessStream.subscribe((messages) => {\n this.awarenessShapeHandler(\n messages,\n awarenessStream.lastOffset,\n awarenessStream.shapeHandle!\n )\n })\n }\n\n this.unsubscribeShapes = () => {\n abortController.abort()\n operationsShapeUnsubscribe()\n awarenessShapeUnsubscribe?.()\n this.unsubscribeShapes = undefined\n }\n\n this.emit(`status`, [{ status: `connecting` }])\n }\n\n private operationsShapeHandler(\n messages: Message<RowWithDocumentUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n const decoder = this.documentUpdates.getUpdateFromRow(message.value)\n while (decoder.pos !== decoder.arr.length) {\n const operation = decoding.readVarUint8Array(decoder)\n Y.applyUpdate(this.doc, operation, `server`)\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.document = {\n offset,\n handle,\n }\n\n if (!this.sendingPendingChanges) {\n this.synced = true\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n }\n this.emit(`resumeState`, [this.resumeState])\n this.connected = true\n }\n }\n }\n\n // TODO: add an optional throttler that batches updates\n // before pushing to the server\n private async applyDocumentUpdate(update: Uint8Array, origin: unknown) {\n // don't re-send updates from electric\n if (origin === `server`) {\n return\n }\n\n this.batch(update)\n this.sendOperations()\n }\n\n private async sendOperations() {\n if (!this.connected || this.sendingPendingChanges) {\n return\n }\n\n try {\n this.sendingPendingChanges = true\n while (\n this.pendingChanges &&\n this.pendingChanges.length > 2 &&\n this.connected\n ) {\n const sending = this.pendingChanges\n this.pendingChanges = null\n\n const encoder = encoding.createEncoder()\n encoding.writeVarUint8Array(encoder, sending)\n\n const success = await send(\n encoder,\n this.documentUpdates.sendUrl,\n this.fetchClient ?? fetch,\n this.documentUpdates.sendErrorRetryHandler\n )\n if (!success) {\n this.batch(sending)\n this.disconnect()\n }\n }\n // no more pending changes, move stableStateVector forward\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n this.emit(`resumeState`, [this.resumeState])\n } finally {\n this.sendingPendingChanges = false\n }\n }\n\n private async applyAwarenessUpdate(\n awarenessUpdate: AwarenessUpdate,\n origin: unknown\n ) {\n if (origin !== `local` || !this.connected) {\n return\n }\n\n this.pendingAwarenessUpdate = awarenessUpdate\n\n if (this.sendingAwarenessState) {\n return\n }\n\n this.sendingAwarenessState = true\n\n try {\n while (this.pendingAwarenessUpdate && this.connected) {\n const update = this.pendingAwarenessUpdate\n this.pendingAwarenessUpdate = null\n\n const { added, updated, removed } = update\n const changedClients = added.concat(updated).concat(removed)\n const encoder = encoding.createEncoder()\n\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n changedClients\n )\n )\n const success = await send(\n encoder,\n this.awarenessUpdates!.sendUrl,\n this.fetchClient ?? fetch,\n this.awarenessUpdates!.sendErrorRetryHandler\n )\n if (!success) {\n this.disconnect()\n }\n }\n } finally {\n this.sendingAwarenessState = false\n }\n }\n\n private awarenessShapeHandler(\n messages: Message<RowWithAwarenessUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n if (message.headers.operation === `delete`) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates!.protocol,\n [Number(message.value.client_id)],\n `remote`\n )\n } else {\n const decoder = this.awarenessUpdates!.getUpdateFromRow(message.value)\n awarenessProtocol.applyAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n decoding.readVarUint8Array(decoder),\n this\n )\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.awareness = {\n offset: offset,\n handle: handle,\n }\n this.emit(`resumeState`, [this.resumeState])\n }\n }\n }\n}\n\nasync function send(\n encoder: encoding.Encoder,\n endpoint: string | URL,\n fetchClient: typeof fetch,\n retryHandler?: SendErrorRetryHandler\n): Promise<boolean> {\n let response: Response | undefined\n const op = encoding.toUint8Array(encoder)\n\n try {\n response = await fetchClient(endpoint!, {\n method: `PUT`,\n headers: {\n 'Content-Type': `application/octet-stream`,\n },\n body: op as BodyInit,\n })\n\n if (!response.ok) {\n throw new Error(`Server did not return 2xx`)\n }\n\n return true\n } catch (error) {\n const shouldRetry = await (retryHandler?.({\n response,\n error,\n }) ?? false)\n return shouldRetry\n }\n}\n","import { ResumeState, ElectricResumeStateProvider } from './types'\nimport { ObservableV2 } from 'lib0/observable.js'\nimport { ElectricProvider } from './y-electric'\nimport * as buffer from 'lib0/buffer'\n\n/**\n * A ResumeStateProvider implementation using LocalStorage.\n * This is a reference implementation that can be used as a starting point\n * for implementing other ResumeStateProviders.\n */\nexport class LocalStorageResumeStateProvider extends ObservableV2<ElectricResumeStateProvider> {\n private key: string\n private resumeState?: ResumeState\n\n constructor(key: string) {\n super()\n this.key = key\n }\n\n subscribeToResumeState(provider: ElectricProvider): () => void {\n const resumeStateHandler = provider.on(`resumeState`, this.save.bind(this))\n return () => provider.off(`resumeState`, resumeStateHandler)\n }\n\n save(resumeState: ResumeState) {\n const jsonPart = JSON.stringify({\n operations: resumeState.document,\n awareness: resumeState.awareness,\n })\n localStorage.setItem(this.key, jsonPart)\n\n if (resumeState.stableStateVector) {\n const vectorBase64 = buffer.toBase64(resumeState.stableStateVector)\n localStorage.setItem(`${this.key}_vector`, vectorBase64)\n } else {\n // ensure vector is removed\n localStorage.removeItem(`${this.key}_vector`)\n }\n }\n\n load(): ResumeState {\n if (this.resumeState) {\n return this.resumeState\n }\n\n const jsonData = localStorage.getItem(this.key)\n if (!jsonData) {\n this.emit(`synced`, [{}])\n } else {\n this.resumeState = JSON.parse(jsonData)\n\n const vectorData = localStorage.getItem(`${this.key}_vector`)\n if (vectorData) {\n this.resumeState!.stableStateVector = buffer.fromBase64(vectorData)\n }\n\n this.emit(`synced`, [this.resumeState!])\n }\n\n return this.resumeState!\n }\n}\n","import * as decoding from 'lib0/decoding'\n\n/**\n * Convert a hex string from PostgreSQL's bytea format to a Uint8Array\n */\nconst hexStringToUint8Array = (hexString: string) => {\n const cleanHexString = hexString.startsWith(`\\\\x`)\n ? hexString.slice(2)\n : hexString\n return new Uint8Array(\n cleanHexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n )\n}\n\n/**\n * Utility to parse hex string bytea data to a decoder for YJS operations\n */\nexport const parseToDecoder = {\n bytea: (hexString: string) => {\n const uint8Array = hexStringToUint8Array(hexString)\n return decoding.createDecoder(uint8Array)\n },\n}\n"],"mappings":"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","names":["import_observable","decoding"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/y-electric.ts","../../src/local-storage-resume-state.ts","../../src/utils.ts"],"sourcesContent":["export * from './y-electric'\nexport * from './local-storage-resume-state'\nexport * from './utils'\nexport * from './types'\n","import * as encoding from 'lib0/encoding'\nimport * as decoding from 'lib0/decoding'\nimport * as awarenessProtocol from 'y-protocols/awareness'\nimport { ObservableV2 } from 'lib0/observable'\nimport * as env from 'lib0/environment'\nimport * as Y from 'yjs'\nimport {\n GetExtensions,\n isChangeMessage,\n isControlMessage,\n Message,\n Offset,\n Row,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/client'\nimport {\n YProvider,\n ResumeState,\n SendErrorRetryHandler,\n ElectricProviderOptions,\n} from './types'\n\ntype AwarenessUpdate = {\n added: number[]\n updated: number[]\n removed: number[]\n}\n\nexport class ElectricProvider<\n RowWithDocumentUpdate extends Row<decoding.Decoder> = never,\n RowWithAwarenessUpdate extends Row<decoding.Decoder> = never,\n> extends ObservableV2<YProvider> {\n private doc: Y.Doc\n\n private documentUpdates: {\n shape: ShapeStreamOptions<GetExtensions<RowWithDocumentUpdate>>\n sendUrl: string | URL\n getUpdateFromRow: (row: RowWithDocumentUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private awarenessUpdates?: {\n shape: ShapeStreamOptions<GetExtensions<RowWithAwarenessUpdate>>\n sendUrl: string | URL\n protocol: awarenessProtocol.Awareness\n getUpdateFromRow: (row: RowWithAwarenessUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private _connected: boolean = false\n private _synced: boolean = false\n\n private resumeState: ResumeState\n private sendingPendingChanges: boolean = false\n private pendingChanges: Uint8Array | null = null\n private sendingAwarenessState: boolean = false\n private pendingAwarenessUpdate: AwarenessUpdate | null = null\n\n private documentUpdateHandler: (\n update: Uint8Array,\n origin: unknown,\n doc: Y.Doc,\n transaction: Y.Transaction\n ) => void\n private awarenessUpdateHandler?: (\n update: AwarenessUpdate,\n origin: unknown\n ) => void\n\n private exitHandler: () => void\n private unsubscribeShapes?: () => void\n\n private fetchClient?: typeof fetch\n\n /**\n * Creates a new ElectricProvider instance that connects YJS documents to Electric SQL.\n *\n * @constructor\n * @param {ElectricProviderOptions} options - Configuration options for the provider\n * @param {Y.Doc} options.doc - The YJS document to be synchronized\n * @param {Object} options.documentUpdates - Document updates configuration\n * @param {ShapeStreamOptions} options.documentUpdates.shape - Options for the document updates shape stream\n * @param {string|URL} options.documentUpdates.sendUrl - URL endpoint for sending document updates\n * @param {Function} options.documentUpdates.getUpdateFromRow - Function to extract document update from row\n * @param {SendErrorRetryHandler} [options.documentUpdates.sendErrorRetryHandler] - Error handler for retrying document updates\n * @param {Object} [options.awarenessUpdates] - Awareness updates configuration (optional)\n * @param {ShapeStreamOptions} options.awarenessUpdates.shape - Options for the awareness updates shape stream\n * @param {string|URL} options.awarenessUpdates.sendUrl - URL endpoint for sending awareness updates\n * @param {awarenessProtocol.Awareness} options.awarenessUpdates.protocol - Awareness protocol instance\n * @param {Function} options.awarenessUpdates.getUpdateFromRow - Function to extract awareness update from row\n * @param {SendErrorRetryHandler} [options.awarenessUpdates.sendErrorRetryHandler] - Error handler for retrying awareness updates\n * @param {ResumeState} [options.resumeState] - Resume state for the provider\n * @param {boolean} [options.connect=true] - Whether to automatically connect upon initialization\n * @param {typeof fetch} [options.fetchClient] - Custom fetch implementation to use for HTTP requests\n */\n constructor({\n doc,\n documentUpdates: documentUpdatesConfig,\n awarenessUpdates: awarenessUpdatesConfig,\n resumeState,\n connect = true,\n fetchClient,\n }: ElectricProviderOptions<RowWithDocumentUpdate, RowWithAwarenessUpdate>) {\n super()\n\n this.doc = doc\n this.documentUpdates = documentUpdatesConfig\n this.awarenessUpdates = awarenessUpdatesConfig\n this.resumeState = resumeState ?? {}\n\n this.fetchClient = fetchClient\n\n this.exitHandler = () => {\n if (env.isNode && typeof process !== `undefined`) {\n process.on(`exit`, this.destroy.bind(this))\n }\n }\n\n this.documentUpdateHandler = this.doc.on(\n `update`,\n this.applyDocumentUpdate.bind(this)\n )\n if (this.awarenessUpdates) {\n this.awarenessUpdateHandler = this.applyAwarenessUpdate.bind(this)\n this.awarenessUpdates.protocol.on(`update`, this.awarenessUpdateHandler!)\n }\n\n // enqueue unsynced changes from document if the\n // resume state provides the document state vector\n if (this.resumeState?.stableStateVector) {\n this.pendingChanges = Y.encodeStateAsUpdate(\n this.doc,\n this.resumeState.stableStateVector\n )\n }\n\n if (connect) {\n this.connect()\n }\n }\n\n get synced() {\n return this._synced\n }\n\n set synced(state) {\n if (this._synced !== state) {\n this._synced = state\n this.emit(`synced`, [state])\n this.emit(`sync`, [state])\n }\n }\n\n set connected(state) {\n if (this._connected !== state) {\n this._connected = state\n if (state) {\n this.sendOperations()\n }\n this.emit(`status`, [{ status: state ? `connected` : `disconnected` }])\n }\n }\n\n get connected() {\n return this._connected\n }\n\n private batch(update: Uint8Array) {\n if (this.pendingChanges) {\n this.pendingChanges = Y.mergeUpdates([this.pendingChanges, update])\n } else {\n this.pendingChanges = update\n }\n }\n\n destroy() {\n this.disconnect()\n\n this.doc.off(`update`, this.documentUpdateHandler)\n this.awarenessUpdates?.protocol.off(`update`, this.awarenessUpdateHandler!)\n\n if (env.isNode && typeof process !== `undefined`) {\n process.off(`exit`, this.exitHandler!)\n }\n super.destroy()\n }\n\n disconnect() {\n this.unsubscribeShapes?.()\n\n if (!this.connected) {\n return\n }\n\n if (this.awarenessUpdates) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n Array.from(this.awarenessUpdates.protocol.getStates().keys()).filter(\n (client) => client !== this.awarenessUpdates!.protocol.clientID\n ),\n this\n )\n\n // try to notifying other clients that we are disconnecting\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n [this.awarenessUpdates.protocol.clientID],\n `local`\n )\n\n this.awarenessUpdates.protocol.setLocalState({})\n }\n\n // TODO: await for events before closing\n this.emit(`connection-close`, [])\n\n this.pendingAwarenessUpdate = null\n\n this.connected = false\n this.synced = false\n }\n\n connect() {\n if (this.connected) {\n return\n }\n const abortController = new AbortController()\n\n const operationsStream = new ShapeStream<RowWithDocumentUpdate>({\n ...this.documentUpdates.shape,\n ...this.resumeState.document,\n signal: abortController.signal,\n })\n\n const operationsShapeUnsubscribe = operationsStream.subscribe(\n (messages) => {\n this.operationsShapeHandler(\n messages,\n operationsStream.lastOffset,\n operationsStream.shapeHandle!\n )\n }\n )\n\n let awarenessShapeUnsubscribe: () => void | undefined\n if (this.awarenessUpdates) {\n const awarenessStream = new ShapeStream<RowWithAwarenessUpdate>({\n ...this.awarenessUpdates.shape,\n ...this.resumeState.awareness,\n signal: abortController.signal,\n })\n\n awarenessShapeUnsubscribe = awarenessStream.subscribe((messages) => {\n this.awarenessShapeHandler(\n messages,\n awarenessStream.lastOffset,\n awarenessStream.shapeHandle!\n )\n })\n }\n\n this.unsubscribeShapes = () => {\n abortController.abort()\n operationsShapeUnsubscribe()\n awarenessShapeUnsubscribe?.()\n this.unsubscribeShapes = undefined\n }\n\n this.emit(`status`, [{ status: `connecting` }])\n }\n\n private operationsShapeHandler(\n messages: Message<RowWithDocumentUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n const decoder = this.documentUpdates.getUpdateFromRow(message.value)\n while (decoder.pos !== decoder.arr.length) {\n const operation = decoding.readVarUint8Array(decoder)\n Y.applyUpdate(this.doc, operation, `server`)\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.document = {\n offset,\n handle,\n }\n\n if (!this.sendingPendingChanges) {\n this.synced = true\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n }\n this.emit(`resumeState`, [this.resumeState])\n this.connected = true\n }\n }\n }\n\n // TODO: add an optional throttler that batches updates\n // before pushing to the server\n private async applyDocumentUpdate(update: Uint8Array, origin: unknown) {\n // don't re-send updates from electric\n if (origin === `server`) {\n return\n }\n\n this.batch(update)\n this.sendOperations()\n }\n\n private async sendOperations() {\n if (!this.connected || this.sendingPendingChanges) {\n return\n }\n\n try {\n this.sendingPendingChanges = true\n while (\n this.pendingChanges &&\n this.pendingChanges.length > 2 &&\n this.connected\n ) {\n const sending = this.pendingChanges\n this.pendingChanges = null\n\n const encoder = encoding.createEncoder()\n encoding.writeVarUint8Array(encoder, sending)\n\n const success = await send(\n encoder,\n this.documentUpdates.sendUrl,\n this.fetchClient ?? fetch,\n this.documentUpdates.sendErrorRetryHandler\n )\n if (!success) {\n this.batch(sending)\n this.disconnect()\n }\n }\n // no more pending changes, move stableStateVector forward\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n this.emit(`resumeState`, [this.resumeState])\n } finally {\n this.sendingPendingChanges = false\n }\n }\n\n private async applyAwarenessUpdate(\n awarenessUpdate: AwarenessUpdate,\n origin: unknown\n ) {\n if (origin !== `local` || !this.connected) {\n return\n }\n\n this.pendingAwarenessUpdate = awarenessUpdate\n\n if (this.sendingAwarenessState) {\n return\n }\n\n this.sendingAwarenessState = true\n\n try {\n while (this.pendingAwarenessUpdate && this.connected) {\n const update = this.pendingAwarenessUpdate\n this.pendingAwarenessUpdate = null\n\n const { added, updated, removed } = update\n const changedClients = added.concat(updated).concat(removed)\n const encoder = encoding.createEncoder()\n\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n changedClients\n )\n )\n const success = await send(\n encoder,\n this.awarenessUpdates!.sendUrl,\n this.fetchClient ?? fetch,\n this.awarenessUpdates!.sendErrorRetryHandler\n )\n if (!success) {\n this.disconnect()\n }\n }\n } finally {\n this.sendingAwarenessState = false\n }\n }\n\n private awarenessShapeHandler(\n messages: Message<RowWithAwarenessUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n if (message.headers.operation === `delete`) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates!.protocol,\n [Number(message.value.client_id)],\n `remote`\n )\n } else {\n const decoder = this.awarenessUpdates!.getUpdateFromRow(message.value)\n awarenessProtocol.applyAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n decoding.readVarUint8Array(decoder),\n this\n )\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.awareness = {\n offset: offset,\n handle: handle,\n }\n this.emit(`resumeState`, [this.resumeState])\n }\n }\n }\n}\n\nasync function send(\n encoder: encoding.Encoder,\n endpoint: string | URL,\n fetchClient: typeof fetch,\n retryHandler?: SendErrorRetryHandler\n): Promise<boolean> {\n let response: Response | undefined\n const op = encoding.toUint8Array(encoder)\n\n try {\n response = await fetchClient(endpoint!, {\n method: `PUT`,\n headers: {\n 'Content-Type': `application/octet-stream`,\n },\n body: op as BodyInit,\n })\n\n if (!response.ok) {\n throw new Error(`Server did not return 2xx`)\n }\n\n return true\n } catch (error) {\n const shouldRetry = await (retryHandler?.({\n response,\n error,\n }) ?? false)\n return shouldRetry\n }\n}\n","import { ResumeState, ElectricResumeStateProvider } from './types'\nimport { ObservableV2 } from 'lib0/observable.js'\nimport { ElectricProvider } from './y-electric'\nimport * as buffer from 'lib0/buffer'\n\n/**\n * A ResumeStateProvider implementation using LocalStorage.\n * This is a reference implementation that can be used as a starting point\n * for implementing other ResumeStateProviders.\n */\nexport class LocalStorageResumeStateProvider extends ObservableV2<ElectricResumeStateProvider> {\n private key: string\n private resumeState?: ResumeState\n\n constructor(key: string) {\n super()\n this.key = key\n }\n\n subscribeToResumeState(provider: ElectricProvider): () => void {\n const resumeStateHandler = provider.on(`resumeState`, this.save.bind(this))\n return () => provider.off(`resumeState`, resumeStateHandler)\n }\n\n save(resumeState: ResumeState) {\n const jsonPart = JSON.stringify({\n operations: resumeState.document,\n awareness: resumeState.awareness,\n })\n localStorage.setItem(this.key, jsonPart)\n\n if (resumeState.stableStateVector) {\n const vectorBase64 = buffer.toBase64(resumeState.stableStateVector)\n localStorage.setItem(`${this.key}_vector`, vectorBase64)\n } else {\n // ensure vector is removed\n localStorage.removeItem(`${this.key}_vector`)\n }\n }\n\n load(): ResumeState {\n if (this.resumeState) {\n return this.resumeState\n }\n\n const jsonData = localStorage.getItem(this.key)\n if (!jsonData) {\n this.emit(`synced`, [{}])\n } else {\n this.resumeState = JSON.parse(jsonData)\n\n const vectorData = localStorage.getItem(`${this.key}_vector`)\n if (vectorData) {\n this.resumeState!.stableStateVector = buffer.fromBase64(vectorData)\n }\n\n this.emit(`synced`, [this.resumeState!])\n }\n\n return this.resumeState!\n }\n}\n","import * as decoding from 'lib0/decoding'\n\n/**\n * Convert a hex string from PostgreSQL's bytea format to a Uint8Array\n */\nconst hexStringToUint8Array = (hexString: string) => {\n const cleanHexString = hexString.startsWith(`\\\\x`)\n ? hexString.slice(2)\n : hexString\n return new Uint8Array(\n cleanHexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n )\n}\n\n/**\n * Utility to parse hex string bytea data to a decoder for YJS operations\n */\nexport const parseToDecoder = {\n bytea: (hexString: string) => {\n const uint8Array = hexStringToUint8Array(hexString)\n return decoding.createDecoder(uint8Array)\n },\n}\n"],"mappings":"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","names":["import_observable","decoding"]}
|
package/dist/index.browser.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var P=Object.defineProperty,D=Object.defineProperties;var E=Object.getOwnPropertyDescriptors;var U=Object.getOwnPropertySymbols;var O=Object.prototype.hasOwnProperty,V=Object.prototype.propertyIsEnumerable;var S=(a,t,e)=>t in a?P(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e,l=(a,t)=>{for(var e in t||(t={}))O.call(t,e)&&S(a,e,t[e]);if(U)for(var e of U(t))V.call(t,e)&&S(a,e,t[e]);return a},m=(a,t)=>D(a,E(t));import*as p from"lib0/encoding";import*as f from"lib0/decoding";import*as c from"y-protocols/awareness";import{ObservableV2 as k}from"lib0/observable";import*as w from"lib0/environment";import*as d from"yjs";import{isChangeMessage as y,isControlMessage as v,ShapeStream as b}from"@electric-sql/client";var A=class extends k{constructor({doc:e,documentUpdates:n,awarenessUpdates:o,resumeState:s,connect:r=!0,fetchClient:i}){var h;super();this._connected=!1;this._synced=!1;this.sendingPendingChanges=!1;this.pendingChanges=null;this.sendingAwarenessState=!1;this.pendingAwarenessUpdate=null;this.doc=e,this.documentUpdates=n,this.awarenessUpdates=o,this.resumeState=s!=null?s:{},this.fetchClient=i,this.exitHandler=()=>{w.isNode&&typeof process!="undefined"&&process.on("exit",this.destroy.bind(this))},this.documentUpdateHandler=this.doc.on("update",this.applyDocumentUpdate.bind(this)),this.awarenessUpdates&&(this.awarenessUpdateHandler=this.applyAwarenessUpdate.bind(this),this.awarenessUpdates.protocol.on("update",this.awarenessUpdateHandler)),(h=this.resumeState)!=null&&h.stableStateVector&&(this.pendingChanges=d.encodeStateAsUpdate(this.doc,this.resumeState.stableStateVector)),r&&this.connect()}get synced(){return this._synced}set synced(e){this._synced!==e&&(this._synced=e,this.emit("synced",[e]),this.emit("sync",[e]))}set connected(e){this._connected!==e&&(this._connected=e,e&&this.sendOperations(),this.emit("status",[{status:e?"connected":"disconnected"}]))}get connected(){return this._connected}batch(e){this.pendingChanges?this.pendingChanges=d.mergeUpdates([this.pendingChanges,e]):this.pendingChanges=e}destroy(){var e;this.disconnect(),this.doc.off("update",this.documentUpdateHandler),(e=this.awarenessUpdates)==null||e.protocol.off("update",this.awarenessUpdateHandler),w.isNode&&typeof process!="undefined"&&process.off("exit",this.exitHandler),super.destroy()}disconnect(){var e;(e=this.unsubscribeShapes)==null||e.call(this),this.connected&&(this.awarenessUpdates&&(c.removeAwarenessStates(this.awarenessUpdates.protocol,Array.from(this.awarenessUpdates.protocol.getStates().keys()).filter(n=>n!==this.awarenessUpdates.protocol.clientID),this),c.removeAwarenessStates(this.awarenessUpdates.protocol,[this.awarenessUpdates.protocol.clientID],"local"),this.awarenessUpdates.protocol.setLocalState({})),this.emit("connection-close",[]),this.pendingAwarenessUpdate=null,this.connected=!1,this.synced=!1)}connect(){if(this.connected)return;let e=new AbortController,n=new b(m(l(l({},this.documentUpdates.shape),this.resumeState.document),{signal:e.signal})),o=n.subscribe(r=>{this.operationsShapeHandler(r,n.lastOffset,n.shapeHandle)}),s;if(this.awarenessUpdates){let r=new b(m(l(l({},this.awarenessUpdates.shape),this.resumeState.awareness),{signal:e.signal}));s=r.subscribe(i=>{this.awarenessShapeHandler(i,r.lastOffset,r.shapeHandle)})}this.unsubscribeShapes=()=>{e.abort(),o(),s==null||s(),this.unsubscribeShapes=void 0},this.emit("status",[{status:"connecting"}])}operationsShapeHandler(e,n,o){for(let s of e)if(y(s)){let r=this.documentUpdates.getUpdateFromRow(s.value);for(;r.pos!==r.arr.length;){let i=f.readVarUint8Array(r);d.applyUpdate(this.doc,i,"server")}}else v(s)&&s.headers.control==="up-to-date"&&(this.resumeState.document={offset:n,handle:o},this.sendingPendingChanges||(this.synced=!0,this.resumeState.stableStateVector=d.encodeStateVector(this.doc)),this.emit("resumeState",[this.resumeState]),this.connected=!0)}async applyDocumentUpdate(e,n){n!=="server"&&(this.batch(e),this.sendOperations())}async sendOperations(){var e;if(!(!this.connected||this.sendingPendingChanges))try{for(this.sendingPendingChanges=!0;this.pendingChanges&&this.pendingChanges.length>2&&this.connected;){let n=this.pendingChanges;this.pendingChanges=null;let o=p.createEncoder();p.writeVarUint8Array(o,n),await R(o,this.documentUpdates.sendUrl,(e=this.fetchClient)!=null?e:fetch,this.documentUpdates.sendErrorRetryHandler)||(this.batch(n),this.disconnect())}this.resumeState.stableStateVector=d.encodeStateVector(this.doc),this.emit("resumeState",[this.resumeState])}finally{this.sendingPendingChanges=!1}}async applyAwarenessUpdate(e,n){var o;if(!(n!=="local"||!this.connected)&&(this.pendingAwarenessUpdate=e,!this.sendingAwarenessState)){this.sendingAwarenessState=!0;try{for(;this.pendingAwarenessUpdate&&this.connected;){let s=this.pendingAwarenessUpdate;this.pendingAwarenessUpdate=null;let{added:r,updated:i,removed:h}=s,H=r.concat(i).concat(h),g=p.createEncoder();p.writeVarUint8Array(g,c.encodeAwarenessUpdate(this.awarenessUpdates.protocol,H)),await R(g,this.awarenessUpdates.sendUrl,(o=this.fetchClient)!=null?o:fetch,this.awarenessUpdates.sendErrorRetryHandler)||this.disconnect()}}finally{this.sendingAwarenessState=!1}}}awarenessShapeHandler(e,n,o){for(let s of e)if(y(s))if(s.headers.operation==="delete")c.removeAwarenessStates(this.awarenessUpdates.protocol,[Number(s.value.client_id)],"remote");else{let r=this.awarenessUpdates.getUpdateFromRow(s.value);c.applyAwarenessUpdate(this.awarenessUpdates.protocol,f.readVarUint8Array(r),this)}else v(s)&&s.headers.control==="up-to-date"&&(this.resumeState.awareness={offset:n,handle:o},this.emit("resumeState",[this.resumeState]))}};async function R(a,t,e,n){var r;let o,s=p.toUint8Array(a);try{if(o=await e(t,{method:"PUT",headers:{"Content-Type":"application/octet-stream"},body:s}),!o.ok)throw new Error("Server did not return 2xx");return!0}catch(i){return await((r=n==null?void 0:n({response:o,error:i}))!=null?r:!1)}}import{ObservableV2 as W}from"lib0/observable.js";import*as u from"lib0/buffer";var C=class extends W{constructor(t){super(),this.key=t}subscribeToResumeState(t){let e=t.on("resumeState",this.save.bind(this));return()=>t.off("resumeState",e)}save(t){let e=JSON.stringify({operations:t.document,awareness:t.awareness});if(localStorage.setItem(this.key,e),t.stableStateVector){let n=u.toBase64(t.stableStateVector);localStorage.setItem(`${this.key}_vector`,n)}else localStorage.removeItem(`${this.key}_vector`)}load(){if(this.resumeState)return this.resumeState;let t=localStorage.getItem(this.key);if(!t)this.emit("synced",[{}]);else{this.resumeState=JSON.parse(t);let e=localStorage.getItem(`${this.key}_vector`);e&&(this.resumeState.stableStateVector=u.fromBase64(e)),this.emit("synced",[this.resumeState])}return this.resumeState}};import*as x from"lib0/decoding";var _=a=>{let t=a.startsWith("\\x")?a.slice(2):a;return new Uint8Array(t.match(/.{1,2}/g).map(e=>parseInt(e,16)))},z={bytea:a=>{let t=_(a);return x.createDecoder(t)}};export{A as ElectricProvider,C as LocalStorageResumeStateProvider,z as parseToDecoder};
|
|
2
2
|
//# sourceMappingURL=index.browser.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/y-electric.ts","../src/local-storage-resume-state.ts","../src/utils.ts"],"sourcesContent":["import * as encoding from 'lib0/encoding'\nimport * as decoding from 'lib0/decoding'\nimport * as awarenessProtocol from 'y-protocols/awareness'\nimport { ObservableV2 } from 'lib0/observable'\nimport * as env from 'lib0/environment'\nimport * as Y from 'yjs'\nimport {\n GetExtensions,\n isChangeMessage,\n isControlMessage,\n Message,\n Offset,\n Row,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/client'\nimport {\n YProvider,\n ResumeState,\n SendErrorRetryHandler,\n ElectricProviderOptions,\n} from './types'\n\ntype AwarenessUpdate = {\n added: number[]\n updated: number[]\n removed: number[]\n}\n\nexport class ElectricProvider<\n RowWithDocumentUpdate extends Row<decoding.Decoder> = never,\n RowWithAwarenessUpdate extends Row<decoding.Decoder> = never,\n> extends ObservableV2<YProvider> {\n private doc: Y.Doc\n\n private documentUpdates: {\n shape: ShapeStreamOptions<GetExtensions<RowWithDocumentUpdate>>\n sendUrl: string | URL\n getUpdateFromRow: (row: RowWithDocumentUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private awarenessUpdates?: {\n shape: ShapeStreamOptions<GetExtensions<RowWithAwarenessUpdate>>\n sendUrl: string | URL\n protocol: awarenessProtocol.Awareness\n getUpdateFromRow: (row: RowWithAwarenessUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private _connected: boolean = false\n private _synced: boolean = false\n\n private resumeState: ResumeState\n private sendingPendingChanges: boolean = false\n private pendingChanges: Uint8Array | null = null\n private sendingAwarenessState: boolean = false\n private pendingAwarenessUpdate: AwarenessUpdate | null = null\n\n private documentUpdateHandler: (\n update: Uint8Array,\n origin: unknown,\n doc: Y.Doc,\n transaction: Y.Transaction\n ) => void\n private awarenessUpdateHandler?: (\n update: AwarenessUpdate,\n origin: unknown\n ) => void\n\n private exitHandler: () => void\n private unsubscribeShapes?: () => void\n\n private fetchClient?: typeof fetch\n\n /**\n * Creates a new ElectricProvider instance that connects YJS documents to Electric SQL.\n *\n * @constructor\n * @param {ElectricProviderOptions} options - Configuration options for the provider\n * @param {Y.Doc} options.doc - The YJS document to be synchronized\n * @param {Object} options.documentUpdates - Document updates configuration\n * @param {ShapeStreamOptions} options.documentUpdates.shape - Options for the document updates shape stream\n * @param {string|URL} options.documentUpdates.sendUrl - URL endpoint for sending document updates\n * @param {Function} options.documentUpdates.getUpdateFromRow - Function to extract document update from row\n * @param {SendErrorRetryHandler} [options.documentUpdates.sendErrorRetryHandler] - Error handler for retrying document updates\n * @param {Object} [options.awarenessUpdates] - Awareness updates configuration (optional)\n * @param {ShapeStreamOptions} options.awarenessUpdates.shape - Options for the awareness updates shape stream\n * @param {string|URL} options.awarenessUpdates.sendUrl - URL endpoint for sending awareness updates\n * @param {awarenessProtocol.Awareness} options.awarenessUpdates.protocol - Awareness protocol instance\n * @param {Function} options.awarenessUpdates.getUpdateFromRow - Function to extract awareness update from row\n * @param {SendErrorRetryHandler} [options.awarenessUpdates.sendErrorRetryHandler] - Error handler for retrying awareness updates\n * @param {ResumeState} [options.resumeState] - Resume state for the provider\n * @param {boolean} [options.connect=true] - Whether to automatically connect upon initialization\n * @param {typeof fetch} [options.fetchClient] - Custom fetch implementation to use for HTTP requests\n */\n constructor({\n doc,\n documentUpdates: documentUpdatesConfig,\n awarenessUpdates: awarenessUpdatesConfig,\n resumeState,\n connect = true,\n fetchClient,\n }: ElectricProviderOptions<RowWithDocumentUpdate, RowWithAwarenessUpdate>) {\n super()\n\n this.doc = doc\n this.documentUpdates = documentUpdatesConfig\n this.awarenessUpdates = awarenessUpdatesConfig\n this.resumeState = resumeState ?? {}\n\n this.fetchClient = fetchClient\n\n this.exitHandler = () => {\n if (env.isNode && typeof process !== `undefined`) {\n process.on(`exit`, this.destroy.bind(this))\n }\n }\n\n this.documentUpdateHandler = this.doc.on(\n `update`,\n this.applyDocumentUpdate.bind(this)\n )\n if (this.awarenessUpdates) {\n this.awarenessUpdateHandler = this.applyAwarenessUpdate.bind(this)\n this.awarenessUpdates.protocol.on(`update`, this.awarenessUpdateHandler!)\n }\n\n // enqueue unsynced changes from document if the\n // resume state provides the document state vector\n if (this.resumeState?.stableStateVector) {\n this.pendingChanges = Y.encodeStateAsUpdate(\n this.doc,\n this.resumeState.stableStateVector\n )\n }\n\n if (connect) {\n this.connect()\n }\n }\n\n get synced() {\n return this._synced\n }\n\n set synced(state) {\n if (this._synced !== state) {\n this._synced = state\n this.emit(`synced`, [state])\n this.emit(`sync`, [state])\n }\n }\n\n set connected(state) {\n if (this._connected !== state) {\n this._connected = state\n if (state) {\n this.sendOperations()\n }\n this.emit(`status`, [{ status: state ? `connected` : `disconnected` }])\n }\n }\n\n get connected() {\n return this._connected\n }\n\n private batch(update: Uint8Array) {\n if (this.pendingChanges) {\n this.pendingChanges = Y.mergeUpdates([this.pendingChanges, update])\n } else {\n this.pendingChanges = update\n }\n }\n\n destroy() {\n this.disconnect()\n\n this.doc.off(`update`, this.documentUpdateHandler)\n this.awarenessUpdates?.protocol.off(`update`, this.awarenessUpdateHandler!)\n\n if (env.isNode && typeof process !== `undefined`) {\n process.off(`exit`, this.exitHandler!)\n }\n super.destroy()\n }\n\n disconnect() {\n this.unsubscribeShapes?.()\n\n if (!this.connected) {\n return\n }\n\n if (this.awarenessUpdates) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n Array.from(this.awarenessUpdates.protocol.getStates().keys()).filter(\n (client) => client !== this.awarenessUpdates!.protocol.clientID\n ),\n this\n )\n\n // try to notifying other clients that we are disconnecting\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n [this.awarenessUpdates.protocol.clientID],\n `local`\n )\n\n this.awarenessUpdates.protocol.setLocalState({})\n }\n\n // TODO: await for events before closing\n this.emit(`connection-close`, [])\n\n this.pendingAwarenessUpdate = null\n\n this.connected = false\n this.synced = false\n }\n\n connect() {\n if (this.connected) {\n return\n }\n const abortController = new AbortController()\n\n const operationsStream = new ShapeStream<RowWithDocumentUpdate>({\n ...this.documentUpdates.shape,\n ...this.resumeState.document,\n signal: abortController.signal,\n })\n\n const operationsShapeUnsubscribe = operationsStream.subscribe(\n (messages) => {\n this.operationsShapeHandler(\n messages,\n operationsStream.lastOffset,\n operationsStream.shapeHandle!\n )\n }\n )\n\n let awarenessShapeUnsubscribe: () => void | undefined\n if (this.awarenessUpdates) {\n const awarenessStream = new ShapeStream<RowWithAwarenessUpdate>({\n ...this.awarenessUpdates.shape,\n ...this.resumeState.awareness,\n signal: abortController.signal,\n })\n\n awarenessShapeUnsubscribe = awarenessStream.subscribe((messages) => {\n this.awarenessShapeHandler(\n messages,\n awarenessStream.lastOffset,\n awarenessStream.shapeHandle!\n )\n })\n }\n\n this.unsubscribeShapes = () => {\n abortController.abort()\n operationsShapeUnsubscribe()\n awarenessShapeUnsubscribe?.()\n this.unsubscribeShapes = undefined\n }\n\n this.emit(`status`, [{ status: `connecting` }])\n }\n\n private operationsShapeHandler(\n messages: Message<RowWithDocumentUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n const decoder = this.documentUpdates.getUpdateFromRow(message.value)\n while (decoder.pos !== decoder.arr.length) {\n const operation = decoding.readVarUint8Array(decoder)\n Y.applyUpdate(this.doc, operation, `server`)\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.document = {\n offset,\n handle,\n }\n\n if (!this.sendingPendingChanges) {\n this.synced = true\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n }\n this.emit(`resumeState`, [this.resumeState])\n this.connected = true\n }\n }\n }\n\n // TODO: add an optional throttler that batches updates\n // before pushing to the server\n private async applyDocumentUpdate(update: Uint8Array, origin: unknown) {\n // don't re-send updates from electric\n if (origin === `server`) {\n return\n }\n\n this.batch(update)\n this.sendOperations()\n }\n\n private async sendOperations() {\n if (!this.connected || this.sendingPendingChanges) {\n return\n }\n\n try {\n this.sendingPendingChanges = true\n while (\n this.pendingChanges &&\n this.pendingChanges.length > 2 &&\n this.connected\n ) {\n const sending = this.pendingChanges\n this.pendingChanges = null\n\n const encoder = encoding.createEncoder()\n encoding.writeVarUint8Array(encoder, sending)\n\n const success = await send(\n encoder,\n this.documentUpdates.sendUrl,\n this.fetchClient ?? fetch,\n this.documentUpdates.sendErrorRetryHandler\n )\n if (!success) {\n this.batch(sending)\n this.disconnect()\n }\n }\n // no more pending changes, move stableStateVector forward\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n this.emit(`resumeState`, [this.resumeState])\n } finally {\n this.sendingPendingChanges = false\n }\n }\n\n private async applyAwarenessUpdate(\n awarenessUpdate: AwarenessUpdate,\n origin: unknown\n ) {\n if (origin !== `local` || !this.connected) {\n return\n }\n\n this.pendingAwarenessUpdate = awarenessUpdate\n\n if (this.sendingAwarenessState) {\n return\n }\n\n this.sendingAwarenessState = true\n\n try {\n while (this.pendingAwarenessUpdate && this.connected) {\n const update = this.pendingAwarenessUpdate\n this.pendingAwarenessUpdate = null\n\n const { added, updated, removed } = update\n const changedClients = added.concat(updated).concat(removed)\n const encoder = encoding.createEncoder()\n\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n changedClients\n )\n )\n const success = await send(\n encoder,\n this.awarenessUpdates!.sendUrl,\n this.fetchClient ?? fetch,\n this.awarenessUpdates!.sendErrorRetryHandler\n )\n if (!success) {\n this.disconnect()\n }\n }\n } finally {\n this.sendingAwarenessState = false\n }\n }\n\n private awarenessShapeHandler(\n messages: Message<RowWithAwarenessUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n if (message.headers.operation === `delete`) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates!.protocol,\n [Number(message.value.client_id)],\n `remote`\n )\n } else {\n const decoder = this.awarenessUpdates!.getUpdateFromRow(message.value)\n awarenessProtocol.applyAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n decoding.readVarUint8Array(decoder),\n this\n )\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.awareness = {\n offset: offset,\n handle: handle,\n }\n this.emit(`resumeState`, [this.resumeState])\n }\n }\n }\n}\n\nasync function send(\n encoder: encoding.Encoder,\n endpoint: string | URL,\n fetchClient: typeof fetch,\n retryHandler?: SendErrorRetryHandler\n): Promise<boolean> {\n let response: Response | undefined\n const op = encoding.toUint8Array(encoder)\n\n try {\n response = await fetchClient(endpoint!, {\n method: `PUT`,\n headers: {\n 'Content-Type': `application/octet-stream`,\n },\n body: op as BodyInit,\n })\n\n if (!response.ok) {\n throw new Error(`Server did not return 2xx`)\n }\n\n return true\n } catch (error) {\n const shouldRetry = await (retryHandler?.({\n response,\n error,\n }) ?? false)\n return shouldRetry\n }\n}\n","import { ResumeState, ElectricResumeStateProvider } from './types'\nimport { ObservableV2 } from 'lib0/observable.js'\nimport { ElectricProvider } from './y-electric'\nimport * as buffer from 'lib0/buffer'\n\n/**\n * A ResumeStateProvider implementation using LocalStorage.\n * This is a reference implementation that can be used as a starting point\n * for implementing other ResumeStateProviders.\n */\nexport class LocalStorageResumeStateProvider extends ObservableV2<ElectricResumeStateProvider> {\n private key: string\n private resumeState?: ResumeState\n\n constructor(key: string) {\n super()\n this.key = key\n }\n\n subscribeToResumeState(provider: ElectricProvider): () => void {\n const resumeStateHandler = provider.on(`resumeState`, this.save.bind(this))\n return () => provider.off(`resumeState`, resumeStateHandler)\n }\n\n save(resumeState: ResumeState) {\n const jsonPart = JSON.stringify({\n operations: resumeState.document,\n awareness: resumeState.awareness,\n })\n localStorage.setItem(this.key, jsonPart)\n\n if (resumeState.stableStateVector) {\n const vectorBase64 = buffer.toBase64(resumeState.stableStateVector)\n localStorage.setItem(`${this.key}_vector`, vectorBase64)\n } else {\n // ensure vector is removed\n localStorage.removeItem(`${this.key}_vector`)\n }\n }\n\n load(): ResumeState {\n if (this.resumeState) {\n return this.resumeState\n }\n\n const jsonData = localStorage.getItem(this.key)\n if (!jsonData) {\n this.emit(`synced`, [{}])\n } else {\n this.resumeState = JSON.parse(jsonData)\n\n const vectorData = localStorage.getItem(`${this.key}_vector`)\n if (vectorData) {\n this.resumeState!.stableStateVector = buffer.fromBase64(vectorData)\n }\n\n this.emit(`synced`, [this.resumeState!])\n }\n\n return this.resumeState!\n }\n}\n","import * as decoding from 'lib0/decoding'\n\n/**\n * Convert a hex string from PostgreSQL's bytea format to a Uint8Array\n */\nconst hexStringToUint8Array = (hexString: string) => {\n const cleanHexString = hexString.startsWith(`\\\\x`)\n ? hexString.slice(2)\n : hexString\n return new Uint8Array(\n cleanHexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n )\n}\n\n/**\n * Utility to parse hex string bytea data to a decoder for YJS operations\n */\nexport const parseToDecoder = {\n bytea: (hexString: string) => {\n const uint8Array = hexStringToUint8Array(hexString)\n return decoding.createDecoder(uint8Array)\n },\n}\n"],"mappings":"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","names":["encoding","decoding","awarenessProtocol","ObservableV2","env","Y","isChangeMessage","isControlMessage","ShapeStream","ElectricProvider","ObservableV2","doc","documentUpdatesConfig","awarenessUpdatesConfig","resumeState","connect","fetchClient","_a","state","update","client","abortController","operationsStream","ShapeStream","__spreadProps","__spreadValues","operationsShapeUnsubscribe","messages","awarenessShapeUnsubscribe","awarenessStream","offset","handle","message","isChangeMessage","decoder","operation","isControlMessage","origin","__async","sending","encoder","send","awarenessUpdate","added","updated","removed","changedClients","endpoint","retryHandler","response","op","error","ObservableV2","buffer","LocalStorageResumeStateProvider","key","provider","resumeStateHandler","resumeState","jsonPart","vectorBase64","jsonData","vectorData","decoding","hexStringToUint8Array","hexString","cleanHexString","byte","parseToDecoder","uint8Array"]}
|
|
1
|
+
{"version":3,"sources":["../src/y-electric.ts","../src/local-storage-resume-state.ts","../src/utils.ts"],"sourcesContent":["import * as encoding from 'lib0/encoding'\nimport * as decoding from 'lib0/decoding'\nimport * as awarenessProtocol from 'y-protocols/awareness'\nimport { ObservableV2 } from 'lib0/observable'\nimport * as env from 'lib0/environment'\nimport * as Y from 'yjs'\nimport {\n GetExtensions,\n isChangeMessage,\n isControlMessage,\n Message,\n Offset,\n Row,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/client'\nimport {\n YProvider,\n ResumeState,\n SendErrorRetryHandler,\n ElectricProviderOptions,\n} from './types'\n\ntype AwarenessUpdate = {\n added: number[]\n updated: number[]\n removed: number[]\n}\n\nexport class ElectricProvider<\n RowWithDocumentUpdate extends Row<decoding.Decoder> = never,\n RowWithAwarenessUpdate extends Row<decoding.Decoder> = never,\n> extends ObservableV2<YProvider> {\n private doc: Y.Doc\n\n private documentUpdates: {\n shape: ShapeStreamOptions<GetExtensions<RowWithDocumentUpdate>>\n sendUrl: string | URL\n getUpdateFromRow: (row: RowWithDocumentUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private awarenessUpdates?: {\n shape: ShapeStreamOptions<GetExtensions<RowWithAwarenessUpdate>>\n sendUrl: string | URL\n protocol: awarenessProtocol.Awareness\n getUpdateFromRow: (row: RowWithAwarenessUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private _connected: boolean = false\n private _synced: boolean = false\n\n private resumeState: ResumeState\n private sendingPendingChanges: boolean = false\n private pendingChanges: Uint8Array | null = null\n private sendingAwarenessState: boolean = false\n private pendingAwarenessUpdate: AwarenessUpdate | null = null\n\n private documentUpdateHandler: (\n update: Uint8Array,\n origin: unknown,\n doc: Y.Doc,\n transaction: Y.Transaction\n ) => void\n private awarenessUpdateHandler?: (\n update: AwarenessUpdate,\n origin: unknown\n ) => void\n\n private exitHandler: () => void\n private unsubscribeShapes?: () => void\n\n private fetchClient?: typeof fetch\n\n /**\n * Creates a new ElectricProvider instance that connects YJS documents to Electric SQL.\n *\n * @constructor\n * @param {ElectricProviderOptions} options - Configuration options for the provider\n * @param {Y.Doc} options.doc - The YJS document to be synchronized\n * @param {Object} options.documentUpdates - Document updates configuration\n * @param {ShapeStreamOptions} options.documentUpdates.shape - Options for the document updates shape stream\n * @param {string|URL} options.documentUpdates.sendUrl - URL endpoint for sending document updates\n * @param {Function} options.documentUpdates.getUpdateFromRow - Function to extract document update from row\n * @param {SendErrorRetryHandler} [options.documentUpdates.sendErrorRetryHandler] - Error handler for retrying document updates\n * @param {Object} [options.awarenessUpdates] - Awareness updates configuration (optional)\n * @param {ShapeStreamOptions} options.awarenessUpdates.shape - Options for the awareness updates shape stream\n * @param {string|URL} options.awarenessUpdates.sendUrl - URL endpoint for sending awareness updates\n * @param {awarenessProtocol.Awareness} options.awarenessUpdates.protocol - Awareness protocol instance\n * @param {Function} options.awarenessUpdates.getUpdateFromRow - Function to extract awareness update from row\n * @param {SendErrorRetryHandler} [options.awarenessUpdates.sendErrorRetryHandler] - Error handler for retrying awareness updates\n * @param {ResumeState} [options.resumeState] - Resume state for the provider\n * @param {boolean} [options.connect=true] - Whether to automatically connect upon initialization\n * @param {typeof fetch} [options.fetchClient] - Custom fetch implementation to use for HTTP requests\n */\n constructor({\n doc,\n documentUpdates: documentUpdatesConfig,\n awarenessUpdates: awarenessUpdatesConfig,\n resumeState,\n connect = true,\n fetchClient,\n }: ElectricProviderOptions<RowWithDocumentUpdate, RowWithAwarenessUpdate>) {\n super()\n\n this.doc = doc\n this.documentUpdates = documentUpdatesConfig\n this.awarenessUpdates = awarenessUpdatesConfig\n this.resumeState = resumeState ?? {}\n\n this.fetchClient = fetchClient\n\n this.exitHandler = () => {\n if (env.isNode && typeof process !== `undefined`) {\n process.on(`exit`, this.destroy.bind(this))\n }\n }\n\n this.documentUpdateHandler = this.doc.on(\n `update`,\n this.applyDocumentUpdate.bind(this)\n )\n if (this.awarenessUpdates) {\n this.awarenessUpdateHandler = this.applyAwarenessUpdate.bind(this)\n this.awarenessUpdates.protocol.on(`update`, this.awarenessUpdateHandler!)\n }\n\n // enqueue unsynced changes from document if the\n // resume state provides the document state vector\n if (this.resumeState?.stableStateVector) {\n this.pendingChanges = Y.encodeStateAsUpdate(\n this.doc,\n this.resumeState.stableStateVector\n )\n }\n\n if (connect) {\n this.connect()\n }\n }\n\n get synced() {\n return this._synced\n }\n\n set synced(state) {\n if (this._synced !== state) {\n this._synced = state\n this.emit(`synced`, [state])\n this.emit(`sync`, [state])\n }\n }\n\n set connected(state) {\n if (this._connected !== state) {\n this._connected = state\n if (state) {\n this.sendOperations()\n }\n this.emit(`status`, [{ status: state ? `connected` : `disconnected` }])\n }\n }\n\n get connected() {\n return this._connected\n }\n\n private batch(update: Uint8Array) {\n if (this.pendingChanges) {\n this.pendingChanges = Y.mergeUpdates([this.pendingChanges, update])\n } else {\n this.pendingChanges = update\n }\n }\n\n destroy() {\n this.disconnect()\n\n this.doc.off(`update`, this.documentUpdateHandler)\n this.awarenessUpdates?.protocol.off(`update`, this.awarenessUpdateHandler!)\n\n if (env.isNode && typeof process !== `undefined`) {\n process.off(`exit`, this.exitHandler!)\n }\n super.destroy()\n }\n\n disconnect() {\n this.unsubscribeShapes?.()\n\n if (!this.connected) {\n return\n }\n\n if (this.awarenessUpdates) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n Array.from(this.awarenessUpdates.protocol.getStates().keys()).filter(\n (client) => client !== this.awarenessUpdates!.protocol.clientID\n ),\n this\n )\n\n // try to notifying other clients that we are disconnecting\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n [this.awarenessUpdates.protocol.clientID],\n `local`\n )\n\n this.awarenessUpdates.protocol.setLocalState({})\n }\n\n // TODO: await for events before closing\n this.emit(`connection-close`, [])\n\n this.pendingAwarenessUpdate = null\n\n this.connected = false\n this.synced = false\n }\n\n connect() {\n if (this.connected) {\n return\n }\n const abortController = new AbortController()\n\n const operationsStream = new ShapeStream<RowWithDocumentUpdate>({\n ...this.documentUpdates.shape,\n ...this.resumeState.document,\n signal: abortController.signal,\n })\n\n const operationsShapeUnsubscribe = operationsStream.subscribe(\n (messages) => {\n this.operationsShapeHandler(\n messages,\n operationsStream.lastOffset,\n operationsStream.shapeHandle!\n )\n }\n )\n\n let awarenessShapeUnsubscribe: () => void | undefined\n if (this.awarenessUpdates) {\n const awarenessStream = new ShapeStream<RowWithAwarenessUpdate>({\n ...this.awarenessUpdates.shape,\n ...this.resumeState.awareness,\n signal: abortController.signal,\n })\n\n awarenessShapeUnsubscribe = awarenessStream.subscribe((messages) => {\n this.awarenessShapeHandler(\n messages,\n awarenessStream.lastOffset,\n awarenessStream.shapeHandle!\n )\n })\n }\n\n this.unsubscribeShapes = () => {\n abortController.abort()\n operationsShapeUnsubscribe()\n awarenessShapeUnsubscribe?.()\n this.unsubscribeShapes = undefined\n }\n\n this.emit(`status`, [{ status: `connecting` }])\n }\n\n private operationsShapeHandler(\n messages: Message<RowWithDocumentUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n const decoder = this.documentUpdates.getUpdateFromRow(message.value)\n while (decoder.pos !== decoder.arr.length) {\n const operation = decoding.readVarUint8Array(decoder)\n Y.applyUpdate(this.doc, operation, `server`)\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.document = {\n offset,\n handle,\n }\n\n if (!this.sendingPendingChanges) {\n this.synced = true\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n }\n this.emit(`resumeState`, [this.resumeState])\n this.connected = true\n }\n }\n }\n\n // TODO: add an optional throttler that batches updates\n // before pushing to the server\n private async applyDocumentUpdate(update: Uint8Array, origin: unknown) {\n // don't re-send updates from electric\n if (origin === `server`) {\n return\n }\n\n this.batch(update)\n this.sendOperations()\n }\n\n private async sendOperations() {\n if (!this.connected || this.sendingPendingChanges) {\n return\n }\n\n try {\n this.sendingPendingChanges = true\n while (\n this.pendingChanges &&\n this.pendingChanges.length > 2 &&\n this.connected\n ) {\n const sending = this.pendingChanges\n this.pendingChanges = null\n\n const encoder = encoding.createEncoder()\n encoding.writeVarUint8Array(encoder, sending)\n\n const success = await send(\n encoder,\n this.documentUpdates.sendUrl,\n this.fetchClient ?? fetch,\n this.documentUpdates.sendErrorRetryHandler\n )\n if (!success) {\n this.batch(sending)\n this.disconnect()\n }\n }\n // no more pending changes, move stableStateVector forward\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n this.emit(`resumeState`, [this.resumeState])\n } finally {\n this.sendingPendingChanges = false\n }\n }\n\n private async applyAwarenessUpdate(\n awarenessUpdate: AwarenessUpdate,\n origin: unknown\n ) {\n if (origin !== `local` || !this.connected) {\n return\n }\n\n this.pendingAwarenessUpdate = awarenessUpdate\n\n if (this.sendingAwarenessState) {\n return\n }\n\n this.sendingAwarenessState = true\n\n try {\n while (this.pendingAwarenessUpdate && this.connected) {\n const update = this.pendingAwarenessUpdate\n this.pendingAwarenessUpdate = null\n\n const { added, updated, removed } = update\n const changedClients = added.concat(updated).concat(removed)\n const encoder = encoding.createEncoder()\n\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n changedClients\n )\n )\n const success = await send(\n encoder,\n this.awarenessUpdates!.sendUrl,\n this.fetchClient ?? fetch,\n this.awarenessUpdates!.sendErrorRetryHandler\n )\n if (!success) {\n this.disconnect()\n }\n }\n } finally {\n this.sendingAwarenessState = false\n }\n }\n\n private awarenessShapeHandler(\n messages: Message<RowWithAwarenessUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n if (message.headers.operation === `delete`) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates!.protocol,\n [Number(message.value.client_id)],\n `remote`\n )\n } else {\n const decoder = this.awarenessUpdates!.getUpdateFromRow(message.value)\n awarenessProtocol.applyAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n decoding.readVarUint8Array(decoder),\n this\n )\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.awareness = {\n offset: offset,\n handle: handle,\n }\n this.emit(`resumeState`, [this.resumeState])\n }\n }\n }\n}\n\nasync function send(\n encoder: encoding.Encoder,\n endpoint: string | URL,\n fetchClient: typeof fetch,\n retryHandler?: SendErrorRetryHandler\n): Promise<boolean> {\n let response: Response | undefined\n const op = encoding.toUint8Array(encoder)\n\n try {\n response = await fetchClient(endpoint!, {\n method: `PUT`,\n headers: {\n 'Content-Type': `application/octet-stream`,\n },\n body: op as BodyInit,\n })\n\n if (!response.ok) {\n throw new Error(`Server did not return 2xx`)\n }\n\n return true\n } catch (error) {\n const shouldRetry = await (retryHandler?.({\n response,\n error,\n }) ?? false)\n return shouldRetry\n }\n}\n","import { ResumeState, ElectricResumeStateProvider } from './types'\nimport { ObservableV2 } from 'lib0/observable.js'\nimport { ElectricProvider } from './y-electric'\nimport * as buffer from 'lib0/buffer'\n\n/**\n * A ResumeStateProvider implementation using LocalStorage.\n * This is a reference implementation that can be used as a starting point\n * for implementing other ResumeStateProviders.\n */\nexport class LocalStorageResumeStateProvider extends ObservableV2<ElectricResumeStateProvider> {\n private key: string\n private resumeState?: ResumeState\n\n constructor(key: string) {\n super()\n this.key = key\n }\n\n subscribeToResumeState(provider: ElectricProvider): () => void {\n const resumeStateHandler = provider.on(`resumeState`, this.save.bind(this))\n return () => provider.off(`resumeState`, resumeStateHandler)\n }\n\n save(resumeState: ResumeState) {\n const jsonPart = JSON.stringify({\n operations: resumeState.document,\n awareness: resumeState.awareness,\n })\n localStorage.setItem(this.key, jsonPart)\n\n if (resumeState.stableStateVector) {\n const vectorBase64 = buffer.toBase64(resumeState.stableStateVector)\n localStorage.setItem(`${this.key}_vector`, vectorBase64)\n } else {\n // ensure vector is removed\n localStorage.removeItem(`${this.key}_vector`)\n }\n }\n\n load(): ResumeState {\n if (this.resumeState) {\n return this.resumeState\n }\n\n const jsonData = localStorage.getItem(this.key)\n if (!jsonData) {\n this.emit(`synced`, [{}])\n } else {\n this.resumeState = JSON.parse(jsonData)\n\n const vectorData = localStorage.getItem(`${this.key}_vector`)\n if (vectorData) {\n this.resumeState!.stableStateVector = buffer.fromBase64(vectorData)\n }\n\n this.emit(`synced`, [this.resumeState!])\n }\n\n return this.resumeState!\n }\n}\n","import * as decoding from 'lib0/decoding'\n\n/**\n * Convert a hex string from PostgreSQL's bytea format to a Uint8Array\n */\nconst hexStringToUint8Array = (hexString: string) => {\n const cleanHexString = hexString.startsWith(`\\\\x`)\n ? hexString.slice(2)\n : hexString\n return new Uint8Array(\n cleanHexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n )\n}\n\n/**\n * Utility to parse hex string bytea data to a decoder for YJS operations\n */\nexport const parseToDecoder = {\n bytea: (hexString: string) => {\n const uint8Array = hexStringToUint8Array(hexString)\n return decoding.createDecoder(uint8Array)\n },\n}\n"],"mappings":"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","names":["encoding","decoding","awarenessProtocol","ObservableV2","env","Y","isChangeMessage","isControlMessage","ShapeStream","ElectricProvider","ObservableV2","doc","documentUpdatesConfig","awarenessUpdatesConfig","resumeState","connect","fetchClient","_a","state","update","client","abortController","operationsStream","ShapeStream","__spreadProps","__spreadValues","operationsShapeUnsubscribe","messages","awarenessShapeUnsubscribe","awarenessStream","offset","handle","message","isChangeMessage","decoder","operation","isControlMessage","origin","sending","encoder","send","awarenessUpdate","added","updated","removed","changedClients","endpoint","retryHandler","response","op","error","ObservableV2","buffer","LocalStorageResumeStateProvider","key","provider","resumeStateHandler","resumeState","jsonPart","vectorBase64","jsonData","vectorData","decoding","hexStringToUint8Array","hexString","cleanHexString","byte","parseToDecoder","uint8Array"]}
|
package/dist/index.mjs
CHANGED
|
@@ -17,26 +17,6 @@ var __spreadValues = (a, b) => {
|
|
|
17
17
|
return a;
|
|
18
18
|
};
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
-
var __async = (__this, __arguments, generator) => {
|
|
21
|
-
return new Promise((resolve, reject) => {
|
|
22
|
-
var fulfilled = (value) => {
|
|
23
|
-
try {
|
|
24
|
-
step(generator.next(value));
|
|
25
|
-
} catch (e) {
|
|
26
|
-
reject(e);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
var rejected = (value) => {
|
|
30
|
-
try {
|
|
31
|
-
step(generator.throw(value));
|
|
32
|
-
} catch (e) {
|
|
33
|
-
reject(e);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
37
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
38
|
-
});
|
|
39
|
-
};
|
|
40
20
|
|
|
41
21
|
// src/y-electric.ts
|
|
42
22
|
import * as encoding from "lib0/encoding";
|
|
@@ -243,85 +223,79 @@ var ElectricProvider = class extends ObservableV2 {
|
|
|
243
223
|
}
|
|
244
224
|
// TODO: add an optional throttler that batches updates
|
|
245
225
|
// before pushing to the server
|
|
246
|
-
applyDocumentUpdate(update, origin) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
this.sendOperations();
|
|
253
|
-
});
|
|
226
|
+
async applyDocumentUpdate(update, origin) {
|
|
227
|
+
if (origin === `server`) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
this.batch(update);
|
|
231
|
+
this.sendOperations();
|
|
254
232
|
}
|
|
255
|
-
sendOperations() {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
this.disconnect();
|
|
277
|
-
}
|
|
233
|
+
async sendOperations() {
|
|
234
|
+
var _a;
|
|
235
|
+
if (!this.connected || this.sendingPendingChanges) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
this.sendingPendingChanges = true;
|
|
240
|
+
while (this.pendingChanges && this.pendingChanges.length > 2 && this.connected) {
|
|
241
|
+
const sending = this.pendingChanges;
|
|
242
|
+
this.pendingChanges = null;
|
|
243
|
+
const encoder = encoding.createEncoder();
|
|
244
|
+
encoding.writeVarUint8Array(encoder, sending);
|
|
245
|
+
const success = await send(
|
|
246
|
+
encoder,
|
|
247
|
+
this.documentUpdates.sendUrl,
|
|
248
|
+
(_a = this.fetchClient) != null ? _a : fetch,
|
|
249
|
+
this.documentUpdates.sendErrorRetryHandler
|
|
250
|
+
);
|
|
251
|
+
if (!success) {
|
|
252
|
+
this.batch(sending);
|
|
253
|
+
this.disconnect();
|
|
278
254
|
}
|
|
279
|
-
this.resumeState.stableStateVector = Y.encodeStateVector(this.doc);
|
|
280
|
-
this.emit(`resumeState`, [this.resumeState]);
|
|
281
|
-
} finally {
|
|
282
|
-
this.sendingPendingChanges = false;
|
|
283
255
|
}
|
|
284
|
-
|
|
256
|
+
this.resumeState.stableStateVector = Y.encodeStateVector(this.doc);
|
|
257
|
+
this.emit(`resumeState`, [this.resumeState]);
|
|
258
|
+
} finally {
|
|
259
|
+
this.sendingPendingChanges = false;
|
|
260
|
+
}
|
|
285
261
|
}
|
|
286
|
-
applyAwarenessUpdate(awarenessUpdate, origin) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
this.disconnect();
|
|
319
|
-
}
|
|
262
|
+
async applyAwarenessUpdate(awarenessUpdate, origin) {
|
|
263
|
+
var _a;
|
|
264
|
+
if (origin !== `local` || !this.connected) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
this.pendingAwarenessUpdate = awarenessUpdate;
|
|
268
|
+
if (this.sendingAwarenessState) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
this.sendingAwarenessState = true;
|
|
272
|
+
try {
|
|
273
|
+
while (this.pendingAwarenessUpdate && this.connected) {
|
|
274
|
+
const update = this.pendingAwarenessUpdate;
|
|
275
|
+
this.pendingAwarenessUpdate = null;
|
|
276
|
+
const { added, updated, removed } = update;
|
|
277
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
278
|
+
const encoder = encoding.createEncoder();
|
|
279
|
+
encoding.writeVarUint8Array(
|
|
280
|
+
encoder,
|
|
281
|
+
awarenessProtocol.encodeAwarenessUpdate(
|
|
282
|
+
this.awarenessUpdates.protocol,
|
|
283
|
+
changedClients
|
|
284
|
+
)
|
|
285
|
+
);
|
|
286
|
+
const success = await send(
|
|
287
|
+
encoder,
|
|
288
|
+
this.awarenessUpdates.sendUrl,
|
|
289
|
+
(_a = this.fetchClient) != null ? _a : fetch,
|
|
290
|
+
this.awarenessUpdates.sendErrorRetryHandler
|
|
291
|
+
);
|
|
292
|
+
if (!success) {
|
|
293
|
+
this.disconnect();
|
|
320
294
|
}
|
|
321
|
-
} finally {
|
|
322
|
-
this.sendingAwarenessState = false;
|
|
323
295
|
}
|
|
324
|
-
}
|
|
296
|
+
} finally {
|
|
297
|
+
this.sendingAwarenessState = false;
|
|
298
|
+
}
|
|
325
299
|
}
|
|
326
300
|
awarenessShapeHandler(messages, offset, handle) {
|
|
327
301
|
for (const message of messages) {
|
|
@@ -350,31 +324,29 @@ var ElectricProvider = class extends ObservableV2 {
|
|
|
350
324
|
}
|
|
351
325
|
}
|
|
352
326
|
};
|
|
353
|
-
function send(encoder, endpoint, fetchClient, retryHandler) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
throw new Error(`Server did not return 2xx`);
|
|
368
|
-
}
|
|
369
|
-
return true;
|
|
370
|
-
} catch (error) {
|
|
371
|
-
const shouldRetry = yield (_a = retryHandler == null ? void 0 : retryHandler({
|
|
372
|
-
response,
|
|
373
|
-
error
|
|
374
|
-
})) != null ? _a : false;
|
|
375
|
-
return shouldRetry;
|
|
327
|
+
async function send(encoder, endpoint, fetchClient, retryHandler) {
|
|
328
|
+
var _a;
|
|
329
|
+
let response;
|
|
330
|
+
const op = encoding.toUint8Array(encoder);
|
|
331
|
+
try {
|
|
332
|
+
response = await fetchClient(endpoint, {
|
|
333
|
+
method: `PUT`,
|
|
334
|
+
headers: {
|
|
335
|
+
"Content-Type": `application/octet-stream`
|
|
336
|
+
},
|
|
337
|
+
body: op
|
|
338
|
+
});
|
|
339
|
+
if (!response.ok) {
|
|
340
|
+
throw new Error(`Server did not return 2xx`);
|
|
376
341
|
}
|
|
377
|
-
|
|
342
|
+
return true;
|
|
343
|
+
} catch (error) {
|
|
344
|
+
const shouldRetry = await ((_a = retryHandler == null ? void 0 : retryHandler({
|
|
345
|
+
response,
|
|
346
|
+
error
|
|
347
|
+
})) != null ? _a : false);
|
|
348
|
+
return shouldRetry;
|
|
349
|
+
}
|
|
378
350
|
}
|
|
379
351
|
|
|
380
352
|
// src/local-storage-resume-state.ts
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/y-electric.ts","../src/local-storage-resume-state.ts","../src/utils.ts"],"sourcesContent":["import * as encoding from 'lib0/encoding'\nimport * as decoding from 'lib0/decoding'\nimport * as awarenessProtocol from 'y-protocols/awareness'\nimport { ObservableV2 } from 'lib0/observable'\nimport * as env from 'lib0/environment'\nimport * as Y from 'yjs'\nimport {\n GetExtensions,\n isChangeMessage,\n isControlMessage,\n Message,\n Offset,\n Row,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/client'\nimport {\n YProvider,\n ResumeState,\n SendErrorRetryHandler,\n ElectricProviderOptions,\n} from './types'\n\ntype AwarenessUpdate = {\n added: number[]\n updated: number[]\n removed: number[]\n}\n\nexport class ElectricProvider<\n RowWithDocumentUpdate extends Row<decoding.Decoder> = never,\n RowWithAwarenessUpdate extends Row<decoding.Decoder> = never,\n> extends ObservableV2<YProvider> {\n private doc: Y.Doc\n\n private documentUpdates: {\n shape: ShapeStreamOptions<GetExtensions<RowWithDocumentUpdate>>\n sendUrl: string | URL\n getUpdateFromRow: (row: RowWithDocumentUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private awarenessUpdates?: {\n shape: ShapeStreamOptions<GetExtensions<RowWithAwarenessUpdate>>\n sendUrl: string | URL\n protocol: awarenessProtocol.Awareness\n getUpdateFromRow: (row: RowWithAwarenessUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private _connected: boolean = false\n private _synced: boolean = false\n\n private resumeState: ResumeState\n private sendingPendingChanges: boolean = false\n private pendingChanges: Uint8Array | null = null\n private sendingAwarenessState: boolean = false\n private pendingAwarenessUpdate: AwarenessUpdate | null = null\n\n private documentUpdateHandler: (\n update: Uint8Array,\n origin: unknown,\n doc: Y.Doc,\n transaction: Y.Transaction\n ) => void\n private awarenessUpdateHandler?: (\n update: AwarenessUpdate,\n origin: unknown\n ) => void\n\n private exitHandler: () => void\n private unsubscribeShapes?: () => void\n\n private fetchClient?: typeof fetch\n\n /**\n * Creates a new ElectricProvider instance that connects YJS documents to Electric SQL.\n *\n * @constructor\n * @param {ElectricProviderOptions} options - Configuration options for the provider\n * @param {Y.Doc} options.doc - The YJS document to be synchronized\n * @param {Object} options.documentUpdates - Document updates configuration\n * @param {ShapeStreamOptions} options.documentUpdates.shape - Options for the document updates shape stream\n * @param {string|URL} options.documentUpdates.sendUrl - URL endpoint for sending document updates\n * @param {Function} options.documentUpdates.getUpdateFromRow - Function to extract document update from row\n * @param {SendErrorRetryHandler} [options.documentUpdates.sendErrorRetryHandler] - Error handler for retrying document updates\n * @param {Object} [options.awarenessUpdates] - Awareness updates configuration (optional)\n * @param {ShapeStreamOptions} options.awarenessUpdates.shape - Options for the awareness updates shape stream\n * @param {string|URL} options.awarenessUpdates.sendUrl - URL endpoint for sending awareness updates\n * @param {awarenessProtocol.Awareness} options.awarenessUpdates.protocol - Awareness protocol instance\n * @param {Function} options.awarenessUpdates.getUpdateFromRow - Function to extract awareness update from row\n * @param {SendErrorRetryHandler} [options.awarenessUpdates.sendErrorRetryHandler] - Error handler for retrying awareness updates\n * @param {ResumeState} [options.resumeState] - Resume state for the provider\n * @param {boolean} [options.connect=true] - Whether to automatically connect upon initialization\n * @param {typeof fetch} [options.fetchClient] - Custom fetch implementation to use for HTTP requests\n */\n constructor({\n doc,\n documentUpdates: documentUpdatesConfig,\n awarenessUpdates: awarenessUpdatesConfig,\n resumeState,\n connect = true,\n fetchClient,\n }: ElectricProviderOptions<RowWithDocumentUpdate, RowWithAwarenessUpdate>) {\n super()\n\n this.doc = doc\n this.documentUpdates = documentUpdatesConfig\n this.awarenessUpdates = awarenessUpdatesConfig\n this.resumeState = resumeState ?? {}\n\n this.fetchClient = fetchClient\n\n this.exitHandler = () => {\n if (env.isNode && typeof process !== `undefined`) {\n process.on(`exit`, this.destroy.bind(this))\n }\n }\n\n this.documentUpdateHandler = this.doc.on(\n `update`,\n this.applyDocumentUpdate.bind(this)\n )\n if (this.awarenessUpdates) {\n this.awarenessUpdateHandler = this.applyAwarenessUpdate.bind(this)\n this.awarenessUpdates.protocol.on(`update`, this.awarenessUpdateHandler!)\n }\n\n // enqueue unsynced changes from document if the\n // resume state provides the document state vector\n if (this.resumeState?.stableStateVector) {\n this.pendingChanges = Y.encodeStateAsUpdate(\n this.doc,\n this.resumeState.stableStateVector\n )\n }\n\n if (connect) {\n this.connect()\n }\n }\n\n get synced() {\n return this._synced\n }\n\n set synced(state) {\n if (this._synced !== state) {\n this._synced = state\n this.emit(`synced`, [state])\n this.emit(`sync`, [state])\n }\n }\n\n set connected(state) {\n if (this._connected !== state) {\n this._connected = state\n if (state) {\n this.sendOperations()\n }\n this.emit(`status`, [{ status: state ? `connected` : `disconnected` }])\n }\n }\n\n get connected() {\n return this._connected\n }\n\n private batch(update: Uint8Array) {\n if (this.pendingChanges) {\n this.pendingChanges = Y.mergeUpdates([this.pendingChanges, update])\n } else {\n this.pendingChanges = update\n }\n }\n\n destroy() {\n this.disconnect()\n\n this.doc.off(`update`, this.documentUpdateHandler)\n this.awarenessUpdates?.protocol.off(`update`, this.awarenessUpdateHandler!)\n\n if (env.isNode && typeof process !== `undefined`) {\n process.off(`exit`, this.exitHandler!)\n }\n super.destroy()\n }\n\n disconnect() {\n this.unsubscribeShapes?.()\n\n if (!this.connected) {\n return\n }\n\n if (this.awarenessUpdates) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n Array.from(this.awarenessUpdates.protocol.getStates().keys()).filter(\n (client) => client !== this.awarenessUpdates!.protocol.clientID\n ),\n this\n )\n\n // try to notifying other clients that we are disconnecting\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n [this.awarenessUpdates.protocol.clientID],\n `local`\n )\n\n this.awarenessUpdates.protocol.setLocalState({})\n }\n\n // TODO: await for events before closing\n this.emit(`connection-close`, [])\n\n this.pendingAwarenessUpdate = null\n\n this.connected = false\n this.synced = false\n }\n\n connect() {\n if (this.connected) {\n return\n }\n const abortController = new AbortController()\n\n const operationsStream = new ShapeStream<RowWithDocumentUpdate>({\n ...this.documentUpdates.shape,\n ...this.resumeState.document,\n signal: abortController.signal,\n })\n\n const operationsShapeUnsubscribe = operationsStream.subscribe(\n (messages) => {\n this.operationsShapeHandler(\n messages,\n operationsStream.lastOffset,\n operationsStream.shapeHandle!\n )\n }\n )\n\n let awarenessShapeUnsubscribe: () => void | undefined\n if (this.awarenessUpdates) {\n const awarenessStream = new ShapeStream<RowWithAwarenessUpdate>({\n ...this.awarenessUpdates.shape,\n ...this.resumeState.awareness,\n signal: abortController.signal,\n })\n\n awarenessShapeUnsubscribe = awarenessStream.subscribe((messages) => {\n this.awarenessShapeHandler(\n messages,\n awarenessStream.lastOffset,\n awarenessStream.shapeHandle!\n )\n })\n }\n\n this.unsubscribeShapes = () => {\n abortController.abort()\n operationsShapeUnsubscribe()\n awarenessShapeUnsubscribe?.()\n this.unsubscribeShapes = undefined\n }\n\n this.emit(`status`, [{ status: `connecting` }])\n }\n\n private operationsShapeHandler(\n messages: Message<RowWithDocumentUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n const decoder = this.documentUpdates.getUpdateFromRow(message.value)\n while (decoder.pos !== decoder.arr.length) {\n const operation = decoding.readVarUint8Array(decoder)\n Y.applyUpdate(this.doc, operation, `server`)\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.document = {\n offset,\n handle,\n }\n\n if (!this.sendingPendingChanges) {\n this.synced = true\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n }\n this.emit(`resumeState`, [this.resumeState])\n this.connected = true\n }\n }\n }\n\n // TODO: add an optional throttler that batches updates\n // before pushing to the server\n private async applyDocumentUpdate(update: Uint8Array, origin: unknown) {\n // don't re-send updates from electric\n if (origin === `server`) {\n return\n }\n\n this.batch(update)\n this.sendOperations()\n }\n\n private async sendOperations() {\n if (!this.connected || this.sendingPendingChanges) {\n return\n }\n\n try {\n this.sendingPendingChanges = true\n while (\n this.pendingChanges &&\n this.pendingChanges.length > 2 &&\n this.connected\n ) {\n const sending = this.pendingChanges\n this.pendingChanges = null\n\n const encoder = encoding.createEncoder()\n encoding.writeVarUint8Array(encoder, sending)\n\n const success = await send(\n encoder,\n this.documentUpdates.sendUrl,\n this.fetchClient ?? fetch,\n this.documentUpdates.sendErrorRetryHandler\n )\n if (!success) {\n this.batch(sending)\n this.disconnect()\n }\n }\n // no more pending changes, move stableStateVector forward\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n this.emit(`resumeState`, [this.resumeState])\n } finally {\n this.sendingPendingChanges = false\n }\n }\n\n private async applyAwarenessUpdate(\n awarenessUpdate: AwarenessUpdate,\n origin: unknown\n ) {\n if (origin !== `local` || !this.connected) {\n return\n }\n\n this.pendingAwarenessUpdate = awarenessUpdate\n\n if (this.sendingAwarenessState) {\n return\n }\n\n this.sendingAwarenessState = true\n\n try {\n while (this.pendingAwarenessUpdate && this.connected) {\n const update = this.pendingAwarenessUpdate\n this.pendingAwarenessUpdate = null\n\n const { added, updated, removed } = update\n const changedClients = added.concat(updated).concat(removed)\n const encoder = encoding.createEncoder()\n\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n changedClients\n )\n )\n const success = await send(\n encoder,\n this.awarenessUpdates!.sendUrl,\n this.fetchClient ?? fetch,\n this.awarenessUpdates!.sendErrorRetryHandler\n )\n if (!success) {\n this.disconnect()\n }\n }\n } finally {\n this.sendingAwarenessState = false\n }\n }\n\n private awarenessShapeHandler(\n messages: Message<RowWithAwarenessUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n if (message.headers.operation === `delete`) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates!.protocol,\n [Number(message.value.client_id)],\n `remote`\n )\n } else {\n const decoder = this.awarenessUpdates!.getUpdateFromRow(message.value)\n awarenessProtocol.applyAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n decoding.readVarUint8Array(decoder),\n this\n )\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.awareness = {\n offset: offset,\n handle: handle,\n }\n this.emit(`resumeState`, [this.resumeState])\n }\n }\n }\n}\n\nasync function send(\n encoder: encoding.Encoder,\n endpoint: string | URL,\n fetchClient: typeof fetch,\n retryHandler?: SendErrorRetryHandler\n): Promise<boolean> {\n let response: Response | undefined\n const op = encoding.toUint8Array(encoder)\n\n try {\n response = await fetchClient(endpoint!, {\n method: `PUT`,\n headers: {\n 'Content-Type': `application/octet-stream`,\n },\n body: op as BodyInit,\n })\n\n if (!response.ok) {\n throw new Error(`Server did not return 2xx`)\n }\n\n return true\n } catch (error) {\n const shouldRetry = await (retryHandler?.({\n response,\n error,\n }) ?? false)\n return shouldRetry\n }\n}\n","import { ResumeState, ElectricResumeStateProvider } from './types'\nimport { ObservableV2 } from 'lib0/observable.js'\nimport { ElectricProvider } from './y-electric'\nimport * as buffer from 'lib0/buffer'\n\n/**\n * A ResumeStateProvider implementation using LocalStorage.\n * This is a reference implementation that can be used as a starting point\n * for implementing other ResumeStateProviders.\n */\nexport class LocalStorageResumeStateProvider extends ObservableV2<ElectricResumeStateProvider> {\n private key: string\n private resumeState?: ResumeState\n\n constructor(key: string) {\n super()\n this.key = key\n }\n\n subscribeToResumeState(provider: ElectricProvider): () => void {\n const resumeStateHandler = provider.on(`resumeState`, this.save.bind(this))\n return () => provider.off(`resumeState`, resumeStateHandler)\n }\n\n save(resumeState: ResumeState) {\n const jsonPart = JSON.stringify({\n operations: resumeState.document,\n awareness: resumeState.awareness,\n })\n localStorage.setItem(this.key, jsonPart)\n\n if (resumeState.stableStateVector) {\n const vectorBase64 = buffer.toBase64(resumeState.stableStateVector)\n localStorage.setItem(`${this.key}_vector`, vectorBase64)\n } else {\n // ensure vector is removed\n localStorage.removeItem(`${this.key}_vector`)\n }\n }\n\n load(): ResumeState {\n if (this.resumeState) {\n return this.resumeState\n }\n\n const jsonData = localStorage.getItem(this.key)\n if (!jsonData) {\n this.emit(`synced`, [{}])\n } else {\n this.resumeState = JSON.parse(jsonData)\n\n const vectorData = localStorage.getItem(`${this.key}_vector`)\n if (vectorData) {\n this.resumeState!.stableStateVector = buffer.fromBase64(vectorData)\n }\n\n this.emit(`synced`, [this.resumeState!])\n }\n\n return this.resumeState!\n }\n}\n","import * as decoding from 'lib0/decoding'\n\n/**\n * Convert a hex string from PostgreSQL's bytea format to a Uint8Array\n */\nconst hexStringToUint8Array = (hexString: string) => {\n const cleanHexString = hexString.startsWith(`\\\\x`)\n ? hexString.slice(2)\n : hexString\n return new Uint8Array(\n cleanHexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n )\n}\n\n/**\n * Utility to parse hex string bytea data to a decoder for YJS operations\n */\nexport const parseToDecoder = {\n bytea: (hexString: string) => {\n const uint8Array = hexStringToUint8Array(hexString)\n return decoding.createDecoder(uint8Array)\n },\n}\n"],"mappings":"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","names":["ObservableV2","decoding"]}
|
|
1
|
+
{"version":3,"sources":["../src/y-electric.ts","../src/local-storage-resume-state.ts","../src/utils.ts"],"sourcesContent":["import * as encoding from 'lib0/encoding'\nimport * as decoding from 'lib0/decoding'\nimport * as awarenessProtocol from 'y-protocols/awareness'\nimport { ObservableV2 } from 'lib0/observable'\nimport * as env from 'lib0/environment'\nimport * as Y from 'yjs'\nimport {\n GetExtensions,\n isChangeMessage,\n isControlMessage,\n Message,\n Offset,\n Row,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/client'\nimport {\n YProvider,\n ResumeState,\n SendErrorRetryHandler,\n ElectricProviderOptions,\n} from './types'\n\ntype AwarenessUpdate = {\n added: number[]\n updated: number[]\n removed: number[]\n}\n\nexport class ElectricProvider<\n RowWithDocumentUpdate extends Row<decoding.Decoder> = never,\n RowWithAwarenessUpdate extends Row<decoding.Decoder> = never,\n> extends ObservableV2<YProvider> {\n private doc: Y.Doc\n\n private documentUpdates: {\n shape: ShapeStreamOptions<GetExtensions<RowWithDocumentUpdate>>\n sendUrl: string | URL\n getUpdateFromRow: (row: RowWithDocumentUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private awarenessUpdates?: {\n shape: ShapeStreamOptions<GetExtensions<RowWithAwarenessUpdate>>\n sendUrl: string | URL\n protocol: awarenessProtocol.Awareness\n getUpdateFromRow: (row: RowWithAwarenessUpdate) => decoding.Decoder\n sendErrorRetryHandler?: SendErrorRetryHandler\n }\n\n private _connected: boolean = false\n private _synced: boolean = false\n\n private resumeState: ResumeState\n private sendingPendingChanges: boolean = false\n private pendingChanges: Uint8Array | null = null\n private sendingAwarenessState: boolean = false\n private pendingAwarenessUpdate: AwarenessUpdate | null = null\n\n private documentUpdateHandler: (\n update: Uint8Array,\n origin: unknown,\n doc: Y.Doc,\n transaction: Y.Transaction\n ) => void\n private awarenessUpdateHandler?: (\n update: AwarenessUpdate,\n origin: unknown\n ) => void\n\n private exitHandler: () => void\n private unsubscribeShapes?: () => void\n\n private fetchClient?: typeof fetch\n\n /**\n * Creates a new ElectricProvider instance that connects YJS documents to Electric SQL.\n *\n * @constructor\n * @param {ElectricProviderOptions} options - Configuration options for the provider\n * @param {Y.Doc} options.doc - The YJS document to be synchronized\n * @param {Object} options.documentUpdates - Document updates configuration\n * @param {ShapeStreamOptions} options.documentUpdates.shape - Options for the document updates shape stream\n * @param {string|URL} options.documentUpdates.sendUrl - URL endpoint for sending document updates\n * @param {Function} options.documentUpdates.getUpdateFromRow - Function to extract document update from row\n * @param {SendErrorRetryHandler} [options.documentUpdates.sendErrorRetryHandler] - Error handler for retrying document updates\n * @param {Object} [options.awarenessUpdates] - Awareness updates configuration (optional)\n * @param {ShapeStreamOptions} options.awarenessUpdates.shape - Options for the awareness updates shape stream\n * @param {string|URL} options.awarenessUpdates.sendUrl - URL endpoint for sending awareness updates\n * @param {awarenessProtocol.Awareness} options.awarenessUpdates.protocol - Awareness protocol instance\n * @param {Function} options.awarenessUpdates.getUpdateFromRow - Function to extract awareness update from row\n * @param {SendErrorRetryHandler} [options.awarenessUpdates.sendErrorRetryHandler] - Error handler for retrying awareness updates\n * @param {ResumeState} [options.resumeState] - Resume state for the provider\n * @param {boolean} [options.connect=true] - Whether to automatically connect upon initialization\n * @param {typeof fetch} [options.fetchClient] - Custom fetch implementation to use for HTTP requests\n */\n constructor({\n doc,\n documentUpdates: documentUpdatesConfig,\n awarenessUpdates: awarenessUpdatesConfig,\n resumeState,\n connect = true,\n fetchClient,\n }: ElectricProviderOptions<RowWithDocumentUpdate, RowWithAwarenessUpdate>) {\n super()\n\n this.doc = doc\n this.documentUpdates = documentUpdatesConfig\n this.awarenessUpdates = awarenessUpdatesConfig\n this.resumeState = resumeState ?? {}\n\n this.fetchClient = fetchClient\n\n this.exitHandler = () => {\n if (env.isNode && typeof process !== `undefined`) {\n process.on(`exit`, this.destroy.bind(this))\n }\n }\n\n this.documentUpdateHandler = this.doc.on(\n `update`,\n this.applyDocumentUpdate.bind(this)\n )\n if (this.awarenessUpdates) {\n this.awarenessUpdateHandler = this.applyAwarenessUpdate.bind(this)\n this.awarenessUpdates.protocol.on(`update`, this.awarenessUpdateHandler!)\n }\n\n // enqueue unsynced changes from document if the\n // resume state provides the document state vector\n if (this.resumeState?.stableStateVector) {\n this.pendingChanges = Y.encodeStateAsUpdate(\n this.doc,\n this.resumeState.stableStateVector\n )\n }\n\n if (connect) {\n this.connect()\n }\n }\n\n get synced() {\n return this._synced\n }\n\n set synced(state) {\n if (this._synced !== state) {\n this._synced = state\n this.emit(`synced`, [state])\n this.emit(`sync`, [state])\n }\n }\n\n set connected(state) {\n if (this._connected !== state) {\n this._connected = state\n if (state) {\n this.sendOperations()\n }\n this.emit(`status`, [{ status: state ? `connected` : `disconnected` }])\n }\n }\n\n get connected() {\n return this._connected\n }\n\n private batch(update: Uint8Array) {\n if (this.pendingChanges) {\n this.pendingChanges = Y.mergeUpdates([this.pendingChanges, update])\n } else {\n this.pendingChanges = update\n }\n }\n\n destroy() {\n this.disconnect()\n\n this.doc.off(`update`, this.documentUpdateHandler)\n this.awarenessUpdates?.protocol.off(`update`, this.awarenessUpdateHandler!)\n\n if (env.isNode && typeof process !== `undefined`) {\n process.off(`exit`, this.exitHandler!)\n }\n super.destroy()\n }\n\n disconnect() {\n this.unsubscribeShapes?.()\n\n if (!this.connected) {\n return\n }\n\n if (this.awarenessUpdates) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n Array.from(this.awarenessUpdates.protocol.getStates().keys()).filter(\n (client) => client !== this.awarenessUpdates!.protocol.clientID\n ),\n this\n )\n\n // try to notifying other clients that we are disconnecting\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates.protocol,\n [this.awarenessUpdates.protocol.clientID],\n `local`\n )\n\n this.awarenessUpdates.protocol.setLocalState({})\n }\n\n // TODO: await for events before closing\n this.emit(`connection-close`, [])\n\n this.pendingAwarenessUpdate = null\n\n this.connected = false\n this.synced = false\n }\n\n connect() {\n if (this.connected) {\n return\n }\n const abortController = new AbortController()\n\n const operationsStream = new ShapeStream<RowWithDocumentUpdate>({\n ...this.documentUpdates.shape,\n ...this.resumeState.document,\n signal: abortController.signal,\n })\n\n const operationsShapeUnsubscribe = operationsStream.subscribe(\n (messages) => {\n this.operationsShapeHandler(\n messages,\n operationsStream.lastOffset,\n operationsStream.shapeHandle!\n )\n }\n )\n\n let awarenessShapeUnsubscribe: () => void | undefined\n if (this.awarenessUpdates) {\n const awarenessStream = new ShapeStream<RowWithAwarenessUpdate>({\n ...this.awarenessUpdates.shape,\n ...this.resumeState.awareness,\n signal: abortController.signal,\n })\n\n awarenessShapeUnsubscribe = awarenessStream.subscribe((messages) => {\n this.awarenessShapeHandler(\n messages,\n awarenessStream.lastOffset,\n awarenessStream.shapeHandle!\n )\n })\n }\n\n this.unsubscribeShapes = () => {\n abortController.abort()\n operationsShapeUnsubscribe()\n awarenessShapeUnsubscribe?.()\n this.unsubscribeShapes = undefined\n }\n\n this.emit(`status`, [{ status: `connecting` }])\n }\n\n private operationsShapeHandler(\n messages: Message<RowWithDocumentUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n const decoder = this.documentUpdates.getUpdateFromRow(message.value)\n while (decoder.pos !== decoder.arr.length) {\n const operation = decoding.readVarUint8Array(decoder)\n Y.applyUpdate(this.doc, operation, `server`)\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.document = {\n offset,\n handle,\n }\n\n if (!this.sendingPendingChanges) {\n this.synced = true\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n }\n this.emit(`resumeState`, [this.resumeState])\n this.connected = true\n }\n }\n }\n\n // TODO: add an optional throttler that batches updates\n // before pushing to the server\n private async applyDocumentUpdate(update: Uint8Array, origin: unknown) {\n // don't re-send updates from electric\n if (origin === `server`) {\n return\n }\n\n this.batch(update)\n this.sendOperations()\n }\n\n private async sendOperations() {\n if (!this.connected || this.sendingPendingChanges) {\n return\n }\n\n try {\n this.sendingPendingChanges = true\n while (\n this.pendingChanges &&\n this.pendingChanges.length > 2 &&\n this.connected\n ) {\n const sending = this.pendingChanges\n this.pendingChanges = null\n\n const encoder = encoding.createEncoder()\n encoding.writeVarUint8Array(encoder, sending)\n\n const success = await send(\n encoder,\n this.documentUpdates.sendUrl,\n this.fetchClient ?? fetch,\n this.documentUpdates.sendErrorRetryHandler\n )\n if (!success) {\n this.batch(sending)\n this.disconnect()\n }\n }\n // no more pending changes, move stableStateVector forward\n this.resumeState.stableStateVector = Y.encodeStateVector(this.doc)\n this.emit(`resumeState`, [this.resumeState])\n } finally {\n this.sendingPendingChanges = false\n }\n }\n\n private async applyAwarenessUpdate(\n awarenessUpdate: AwarenessUpdate,\n origin: unknown\n ) {\n if (origin !== `local` || !this.connected) {\n return\n }\n\n this.pendingAwarenessUpdate = awarenessUpdate\n\n if (this.sendingAwarenessState) {\n return\n }\n\n this.sendingAwarenessState = true\n\n try {\n while (this.pendingAwarenessUpdate && this.connected) {\n const update = this.pendingAwarenessUpdate\n this.pendingAwarenessUpdate = null\n\n const { added, updated, removed } = update\n const changedClients = added.concat(updated).concat(removed)\n const encoder = encoding.createEncoder()\n\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n changedClients\n )\n )\n const success = await send(\n encoder,\n this.awarenessUpdates!.sendUrl,\n this.fetchClient ?? fetch,\n this.awarenessUpdates!.sendErrorRetryHandler\n )\n if (!success) {\n this.disconnect()\n }\n }\n } finally {\n this.sendingAwarenessState = false\n }\n }\n\n private awarenessShapeHandler(\n messages: Message<RowWithAwarenessUpdate>[],\n offset: Offset,\n handle: string\n ) {\n for (const message of messages) {\n if (isChangeMessage(message)) {\n if (message.headers.operation === `delete`) {\n awarenessProtocol.removeAwarenessStates(\n this.awarenessUpdates!.protocol,\n [Number(message.value.client_id)],\n `remote`\n )\n } else {\n const decoder = this.awarenessUpdates!.getUpdateFromRow(message.value)\n awarenessProtocol.applyAwarenessUpdate(\n this.awarenessUpdates!.protocol,\n decoding.readVarUint8Array(decoder),\n this\n )\n }\n } else if (\n isControlMessage(message) &&\n message.headers.control === `up-to-date`\n ) {\n this.resumeState.awareness = {\n offset: offset,\n handle: handle,\n }\n this.emit(`resumeState`, [this.resumeState])\n }\n }\n }\n}\n\nasync function send(\n encoder: encoding.Encoder,\n endpoint: string | URL,\n fetchClient: typeof fetch,\n retryHandler?: SendErrorRetryHandler\n): Promise<boolean> {\n let response: Response | undefined\n const op = encoding.toUint8Array(encoder)\n\n try {\n response = await fetchClient(endpoint!, {\n method: `PUT`,\n headers: {\n 'Content-Type': `application/octet-stream`,\n },\n body: op as BodyInit,\n })\n\n if (!response.ok) {\n throw new Error(`Server did not return 2xx`)\n }\n\n return true\n } catch (error) {\n const shouldRetry = await (retryHandler?.({\n response,\n error,\n }) ?? false)\n return shouldRetry\n }\n}\n","import { ResumeState, ElectricResumeStateProvider } from './types'\nimport { ObservableV2 } from 'lib0/observable.js'\nimport { ElectricProvider } from './y-electric'\nimport * as buffer from 'lib0/buffer'\n\n/**\n * A ResumeStateProvider implementation using LocalStorage.\n * This is a reference implementation that can be used as a starting point\n * for implementing other ResumeStateProviders.\n */\nexport class LocalStorageResumeStateProvider extends ObservableV2<ElectricResumeStateProvider> {\n private key: string\n private resumeState?: ResumeState\n\n constructor(key: string) {\n super()\n this.key = key\n }\n\n subscribeToResumeState(provider: ElectricProvider): () => void {\n const resumeStateHandler = provider.on(`resumeState`, this.save.bind(this))\n return () => provider.off(`resumeState`, resumeStateHandler)\n }\n\n save(resumeState: ResumeState) {\n const jsonPart = JSON.stringify({\n operations: resumeState.document,\n awareness: resumeState.awareness,\n })\n localStorage.setItem(this.key, jsonPart)\n\n if (resumeState.stableStateVector) {\n const vectorBase64 = buffer.toBase64(resumeState.stableStateVector)\n localStorage.setItem(`${this.key}_vector`, vectorBase64)\n } else {\n // ensure vector is removed\n localStorage.removeItem(`${this.key}_vector`)\n }\n }\n\n load(): ResumeState {\n if (this.resumeState) {\n return this.resumeState\n }\n\n const jsonData = localStorage.getItem(this.key)\n if (!jsonData) {\n this.emit(`synced`, [{}])\n } else {\n this.resumeState = JSON.parse(jsonData)\n\n const vectorData = localStorage.getItem(`${this.key}_vector`)\n if (vectorData) {\n this.resumeState!.stableStateVector = buffer.fromBase64(vectorData)\n }\n\n this.emit(`synced`, [this.resumeState!])\n }\n\n return this.resumeState!\n }\n}\n","import * as decoding from 'lib0/decoding'\n\n/**\n * Convert a hex string from PostgreSQL's bytea format to a Uint8Array\n */\nconst hexStringToUint8Array = (hexString: string) => {\n const cleanHexString = hexString.startsWith(`\\\\x`)\n ? hexString.slice(2)\n : hexString\n return new Uint8Array(\n cleanHexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n )\n}\n\n/**\n * Utility to parse hex string bytea data to a decoder for YJS operations\n */\nexport const parseToDecoder = {\n bytea: (hexString: string) => {\n const uint8Array = hexStringToUint8Array(hexString)\n return decoding.createDecoder(uint8Array)\n },\n}\n"],"mappings":"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","names":["ObservableV2","decoding"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@electric-sql/y-electric",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "YJS network provider for ElectricSQL",
|
|
5
5
|
"author": "ElectricSQL team and contributors.",
|
|
6
6
|
"bugs": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"lib0": "^0.2.65",
|
|
12
12
|
"y-protocols": "^1.0.5",
|
|
13
13
|
"yjs": "^13.6.6",
|
|
14
|
-
"@electric-sql/client": "1.
|
|
14
|
+
"@electric-sql/client": "1.3.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@types/node": "^22.0.0",
|