UNPKG

@tanstack/db-collections

Version:

A collection for (aspirationally) every way of loading your data

148 lines (147 loc) 4.81 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const client = require("@electric-sql/client"); const store = require("@tanstack/store"); function isUpToDateMessage(message) { return client.isControlMessage(message) && message.headers.control === `up-to-date`; } function hasTxids(message) { return `headers` in message && `txids` in message.headers && Array.isArray(message.headers.txids); } function electricCollectionOptions(config) { const seenTxids = new store.Store(/* @__PURE__ */ new Set([`${Math.random()}`])); const sync = createElectricSync(config.shapeOptions, { seenTxids }); const awaitTxId = async (txId, timeout = 3e4) => { const hasTxid = seenTxids.state.has(txId); if (hasTxid) return true; return new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { unsubscribe(); reject(new Error(`Timeout waiting for txId: ${txId}`)); }, timeout); const unsubscribe = seenTxids.subscribe(() => { if (seenTxids.state.has(txId)) { clearTimeout(timeoutId); unsubscribe(); resolve(true); } }); }); }; const wrappedOnInsert = config.onInsert ? async (params) => { const handlerResult = await config.onInsert(params) ?? {}; const txid = handlerResult.txid; if (!txid) { throw new Error( `Electric collection onInsert handler must return a txid` ); } await awaitTxId(txid); return handlerResult; } : void 0; const wrappedOnUpdate = config.onUpdate ? async (params) => { const handlerResult = await config.onUpdate(params); const txid = handlerResult.txid; if (!txid) { throw new Error( `Electric collection onUpdate handler must return a txid` ); } await awaitTxId(txid); return handlerResult; } : void 0; const wrappedOnDelete = config.onDelete ? async (params) => { const handlerResult = await config.onDelete(params); const txid = handlerResult.txid; if (!txid) { throw new Error( `Electric collection onDelete handler must return a txid` ); } await awaitTxId(txid); return handlerResult; } : void 0; const { shapeOptions: _shapeOptions, onInsert: _onInsert, onUpdate: _onUpdate, onDelete: _onDelete, ...restConfig } = config; return { ...restConfig, sync, onInsert: wrappedOnInsert, onUpdate: wrappedOnUpdate, onDelete: wrappedOnDelete, utils: { awaitTxId } }; } function createElectricSync(shapeOptions, options) { const { seenTxids } = options; const relationSchema = new store.Store(void 0); const getSyncMetadata = () => { var _a; const schema = relationSchema.state || `public`; return { relation: ((_a = shapeOptions.params) == null ? void 0 : _a.table) ? [schema, shapeOptions.params.table] : void 0 }; }; return { sync: (params) => { const { begin, write, commit } = params; const stream = new client.ShapeStream(shapeOptions); let transactionStarted = false; let newTxids = /* @__PURE__ */ new Set(); stream.subscribe((messages) => { let hasUpToDate = false; for (const message of messages) { if (hasTxids(message) && message.headers.txids) { message.headers.txids.forEach((txid) => newTxids.add(String(txid))); } if (client.isChangeMessage(message) && message.headers.schema) { if (typeof message.headers.schema === `string`) { const schema = message.headers.schema; relationSchema.setState(() => schema); } } if (client.isChangeMessage(message)) { if (!transactionStarted) { begin(); transactionStarted = true; } const value = message.value; const enhancedMetadata = { ...message.headers }; write({ type: message.headers.operation, value, metadata: enhancedMetadata }); } else if (isUpToDateMessage(message)) { hasUpToDate = true; } } if (hasUpToDate && transactionStarted) { commit(); seenTxids.setState((currentTxids) => { const clonedSeen = new Set(currentTxids); newTxids.forEach((txid) => clonedSeen.add(String(txid))); newTxids = /* @__PURE__ */ new Set(); return clonedSeen; }); transactionStarted = false; } }); }, // Expose the getSyncMetadata function getSyncMetadata }; } exports.electricCollectionOptions = electricCollectionOptions; //# sourceMappingURL=electric.cjs.map