@electric-sql/y-electric
Version:
YJS network provider for ElectricSQL
1 lines • 24.6 kB
Source Map (JSON)
{"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 private debounceMs: number\n private debounceTimer: ReturnType<typeof setTimeout> | 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 * @param {number} [options.debounceMs] - Debounce window in milliseconds for sending document updates. If 0 or undefined, debouncing is disabled.\n */\n constructor({\n doc,\n documentUpdates: documentUpdatesConfig,\n awarenessUpdates: awarenessUpdatesConfig,\n resumeState,\n connect = true,\n fetchClient,\n debounceMs,\n }: ElectricProviderOptions<RowWithDocumentUpdate, RowWithAwarenessUpdate>) {\n super()\n\n this.doc = doc\n this.documentUpdates = documentUpdatesConfig\n this.awarenessUpdates = awarenessUpdatesConfig\n this.resumeState = resumeState ?? {}\n this.debounceMs = debounceMs ?? 0\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 private clearDebounceTimer() {\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer)\n this.debounceTimer = null\n }\n }\n\n private scheduleSendOperations() {\n if (this.debounceMs > 0) {\n if (this.debounceTimer === null) {\n this.debounceTimer = setTimeout(async () => {\n this.debounceTimer = null\n await this.sendOperations()\n if (\n this.pendingChanges &&\n this.connected &&\n !this.sendingPendingChanges\n ) {\n this.scheduleSendOperations()\n }\n }, this.debounceMs)\n }\n } else {\n this.sendOperations()\n }\n }\n\n destroy() {\n this.clearDebounceTimer()\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 // Flush any pending changes before disconnecting\n this.clearDebounceTimer()\n if (this.pendingChanges && this.connected) {\n this.sendOperations()\n }\n\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: Message<RowWithDocumentUpdate>[]) => {\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 signal: abortController.signal,\n offset: `now`,\n })\n\n awarenessShapeUnsubscribe = awarenessStream.subscribe(\n (messages: Message<RowWithAwarenessUpdate>[]) => {\n this.awarenessShapeHandler(messages)\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 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.scheduleSendOperations()\n }\n\n private async sendOperations() {\n this.clearDebounceTimer()\n\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(messages: Message<RowWithAwarenessUpdate>[]) {\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 }\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 })\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","debounceMs","_a","state","update","client","abortController","operationsStream","ShapeStream","__spreadProps","__spreadValues","operationsShapeUnsubscribe","messages","awarenessShapeUnsubscribe","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"]}