@tanstack/browser-db-sqlite-persistence 0.2.28 → 0.2.30

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.
@@ -1 +1 @@
1
- {"version":3,"file":"browser-persistence.cjs","sources":["../../src/browser-persistence.ts"],"sourcesContent":["import {\n DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS,\n DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS,\n SingleProcessCoordinator,\n createSQLiteCorePersistenceAdapter,\n} from '@tanstack/db-sqlite-persistence-core'\nimport { BrowserCollectionCoordinator } from './browser-coordinator'\nimport { BrowserWASQLiteDriver } from './wa-sqlite-driver'\nimport type {\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n SQLiteCoreAdapterOptions,\n SQLiteDriver,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\n\nexport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\n\ntype BrowserSQLiteCoreSchemaMismatchPolicy =\n | `sync-present-reset`\n | `sync-absent-error`\n | `reset`\n\nexport type BrowserWASQLiteSchemaMismatchPolicy =\n | BrowserSQLiteCoreSchemaMismatchPolicy\n | `throw`\n\nexport type BrowserWASQLitePersistenceOptions = Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> & {\n database: BrowserWASQLiteDatabase\n coordinator?: PersistedCollectionCoordinator\n schemaMismatchPolicy?: BrowserWASQLiteSchemaMismatchPolicy\n}\n\nfunction normalizeSchemaMismatchPolicy(\n policy: BrowserWASQLiteSchemaMismatchPolicy,\n): BrowserSQLiteCoreSchemaMismatchPolicy {\n if (policy === `throw`) {\n return `sync-absent-error`\n }\n\n return policy\n}\n\nfunction resolveSchemaMismatchPolicy(\n explicitPolicy: BrowserWASQLiteSchemaMismatchPolicy | undefined,\n mode: PersistedCollectionMode,\n): BrowserSQLiteCoreSchemaMismatchPolicy {\n if (explicitPolicy) {\n return normalizeSchemaMismatchPolicy(explicitPolicy)\n }\n\n return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`\n}\n\nfunction createAdapterCacheKey(\n schemaMismatchPolicy: BrowserSQLiteCoreSchemaMismatchPolicy,\n schemaVersion: number | undefined,\n): string {\n const schemaVersionKey =\n schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`\n return `${schemaMismatchPolicy}|${schemaVersionKey}`\n}\n\nfunction createInternalSQLiteDriver(\n options: BrowserWASQLitePersistenceOptions,\n): SQLiteDriver {\n return new BrowserWASQLiteDriver({\n database: options.database,\n })\n}\n\nfunction resolveAdapterBaseOptions(\n options: BrowserWASQLitePersistenceOptions,\n): Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> {\n return {\n appliedTxPruneMaxRows:\n options.appliedTxPruneMaxRows ?? DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS,\n appliedTxPruneMaxAgeSeconds:\n options.appliedTxPruneMaxAgeSeconds ??\n DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS,\n pullSinceReloadThreshold: options.pullSinceReloadThreshold,\n }\n}\n\n/**\n * Creates a shared browser wa-sqlite persistence instance that can be reused by\n * many collections on the same database. This is single-tab wiring using\n * SingleProcessCoordinator semantics (no election required).\n */\nexport function createBrowserWASQLitePersistence(\n options: BrowserWASQLitePersistenceOptions,\n): PersistedCollectionPersistence {\n const { coordinator, schemaMismatchPolicy } = options\n const driver = createInternalSQLiteDriver(options)\n const adapterBaseOptions = resolveAdapterBaseOptions(options)\n const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator()\n const adapterCache = new Map<\n string,\n ReturnType<typeof createSQLiteCorePersistenceAdapter>\n >()\n\n const getAdapterForCollection = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ) => {\n const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(\n schemaMismatchPolicy,\n mode,\n )\n const cacheKey = createAdapterCacheKey(\n resolvedSchemaMismatchPolicy,\n schemaVersion,\n )\n const cachedAdapter = adapterCache.get(cacheKey)\n if (cachedAdapter) {\n return cachedAdapter\n }\n\n const adapter = createSQLiteCorePersistenceAdapter({\n ...adapterBaseOptions,\n driver,\n schemaMismatchPolicy: resolvedSchemaMismatchPolicy,\n ...(schemaVersion === undefined ? {} : { schemaVersion }),\n })\n adapterCache.set(cacheKey, adapter)\n return adapter\n }\n\n const createCollectionPersistence = (\n collectionId: string | undefined,\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence => {\n const adapter = getAdapterForCollection(mode, schemaVersion)\n if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {\n if (collectionId === undefined) {\n resolvedCoordinator.setAdapter(adapter)\n } else {\n resolvedCoordinator.setAdapterForCollection(collectionId, adapter)\n }\n }\n return {\n adapter,\n coordinator: resolvedCoordinator,\n }\n }\n\n const defaultPersistence = createCollectionPersistence(\n undefined,\n `sync-absent`,\n undefined,\n )\n if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {\n resolvedCoordinator.setAdapter(defaultPersistence.adapter)\n }\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ collectionId, mode, schemaVersion }) =>\n createCollectionPersistence(collectionId, mode, schemaVersion),\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(undefined, mode, undefined),\n }\n}\n"],"names":["BrowserWASQLiteDriver","DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS","DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS","SingleProcessCoordinator","createSQLiteCorePersistenceAdapter","BrowserCollectionCoordinator"],"mappings":";;;;;AAqCA,SAAS,8BACP,QACuC;AACvC,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,4BACP,gBACA,MACuC;AACvC,MAAI,gBAAgB;AAClB,WAAO,8BAA8B,cAAc;AAAA,EACrD;AAEA,SAAO,SAAS,iBAAiB,uBAAuB;AAC1D;AAEA,SAAS,sBACP,sBACA,eACQ;AACR,QAAM,mBACJ,kBAAkB,SAAY,mBAAmB,UAAU,aAAa;AAC1E,SAAO,GAAG,oBAAoB,IAAI,gBAAgB;AACpD;AAEA,SAAS,2BACP,SACc;AACd,SAAO,IAAIA,eAAAA,sBAAsB;AAAA,IAC/B,UAAU,QAAQ;AAAA,EAAA,CACnB;AACH;AAEA,SAAS,0BACP,SAIA;AACA,SAAO;AAAA,IACL,uBACE,QAAQ,yBAAyBC,wBAAAA;AAAAA,IACnC,6BACE,QAAQ,+BACRC,wBAAAA;AAAAA,IACF,0BAA0B,QAAQ;AAAA,EAAA;AAEtC;AAOO,SAAS,iCACd,SACgC;AAChC,QAAM,EAAE,aAAa,qBAAA,IAAyB;AAC9C,QAAM,SAAS,2BAA2B,OAAO;AACjD,QAAM,qBAAqB,0BAA0B,OAAO;AAC5D,QAAM,sBAAsB,eAAe,IAAIC,iDAAA;AAC/C,QAAM,mCAAmB,IAAA;AAKzB,QAAM,0BAA0B,CAC9B,MACA,kBACG;AACH,UAAM,+BAA+B;AAAA,MACnC;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,gBAAgB,aAAa,IAAI,QAAQ;AAC/C,QAAI,eAAe;AACjB,aAAO;AAAA,IACT;AAEA,UAAM,UAAUC,wBAAAA,mCAAmC;AAAA,MACjD,GAAG;AAAA,MACH;AAAA,MACA,sBAAsB;AAAA,MACtB,GAAI,kBAAkB,SAAY,CAAA,IAAK,EAAE,cAAA;AAAA,IAAc,CACxD;AACD,iBAAa,IAAI,UAAU,OAAO;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,8BAA8B,CAClC,cACA,MACA,kBACmC;AACnC,UAAM,UAAU,wBAAwB,MAAM,aAAa;AAC3D,QAAI,+BAA+BC,mBAAAA,8BAA8B;AAC/D,UAAI,iBAAiB,QAAW;AAC9B,4BAAoB,WAAW,OAAO;AAAA,MACxC,OAAO;AACL,4BAAoB,wBAAwB,cAAc,OAAO;AAAA,MACnE;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA,aAAa;AAAA,IAAA;AAAA,EAEjB;AAEA,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEF,MAAI,+BAA+BA,mBAAAA,8BAA8B;AAC/D,wBAAoB,WAAW,mBAAmB,OAAO;AAAA,EAC3D;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,iCAAiC,CAAC,EAAE,cAAc,MAAM,oBACtD,4BAA4B,cAAc,MAAM,aAAa;AAAA,IAC/D,2BAA2B,CAAC,SAC1B,4BAA4B,QAAW,MAAM,MAAS;AAAA,EAAA;AAE5D;;"}
1
+ {"version":3,"file":"browser-persistence.cjs","sources":["../../src/browser-persistence.ts"],"sourcesContent":["import {\n DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS,\n DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS,\n SingleProcessCoordinator,\n createSQLiteCorePersistenceAdapter,\n} from '@tanstack/db-sqlite-persistence-core'\nimport { BrowserCollectionCoordinator } from './browser-coordinator'\nimport { BrowserWASQLiteDriver } from './wa-sqlite-driver'\nimport type {\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n SQLiteCoreAdapterOptions,\n SQLiteDriver,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\n\nexport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\n\ntype BrowserSQLiteCoreSchemaMismatchPolicy =\n `sync-present-reset` | `sync-absent-error` | `reset`\n\nexport type BrowserWASQLiteSchemaMismatchPolicy =\n BrowserSQLiteCoreSchemaMismatchPolicy | `throw`\n\nexport type BrowserWASQLitePersistenceOptions = Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> & {\n database: BrowserWASQLiteDatabase\n coordinator?: PersistedCollectionCoordinator\n schemaMismatchPolicy?: BrowserWASQLiteSchemaMismatchPolicy\n}\n\nfunction normalizeSchemaMismatchPolicy(\n policy: BrowserWASQLiteSchemaMismatchPolicy,\n): BrowserSQLiteCoreSchemaMismatchPolicy {\n if (policy === `throw`) {\n return `sync-absent-error`\n }\n\n return policy\n}\n\nfunction resolveSchemaMismatchPolicy(\n explicitPolicy: BrowserWASQLiteSchemaMismatchPolicy | undefined,\n mode: PersistedCollectionMode,\n): BrowserSQLiteCoreSchemaMismatchPolicy {\n if (explicitPolicy) {\n return normalizeSchemaMismatchPolicy(explicitPolicy)\n }\n\n return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`\n}\n\nfunction createAdapterCacheKey(\n schemaMismatchPolicy: BrowserSQLiteCoreSchemaMismatchPolicy,\n schemaVersion: number | undefined,\n): string {\n const schemaVersionKey =\n schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`\n return `${schemaMismatchPolicy}|${schemaVersionKey}`\n}\n\nfunction createInternalSQLiteDriver(\n options: BrowserWASQLitePersistenceOptions,\n): SQLiteDriver {\n return new BrowserWASQLiteDriver({\n database: options.database,\n })\n}\n\nfunction resolveAdapterBaseOptions(\n options: BrowserWASQLitePersistenceOptions,\n): Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> {\n return {\n appliedTxPruneMaxRows:\n options.appliedTxPruneMaxRows ?? DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS,\n appliedTxPruneMaxAgeSeconds:\n options.appliedTxPruneMaxAgeSeconds ??\n DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS,\n pullSinceReloadThreshold: options.pullSinceReloadThreshold,\n }\n}\n\n/**\n * Creates a shared browser wa-sqlite persistence instance that can be reused by\n * many collections on the same database. This is single-tab wiring using\n * SingleProcessCoordinator semantics (no election required).\n */\nexport function createBrowserWASQLitePersistence(\n options: BrowserWASQLitePersistenceOptions,\n): PersistedCollectionPersistence {\n const { coordinator, schemaMismatchPolicy } = options\n const driver = createInternalSQLiteDriver(options)\n const adapterBaseOptions = resolveAdapterBaseOptions(options)\n const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator()\n const adapterCache = new Map<\n string,\n ReturnType<typeof createSQLiteCorePersistenceAdapter>\n >()\n\n const getAdapterForCollection = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ) => {\n const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(\n schemaMismatchPolicy,\n mode,\n )\n const cacheKey = createAdapterCacheKey(\n resolvedSchemaMismatchPolicy,\n schemaVersion,\n )\n const cachedAdapter = adapterCache.get(cacheKey)\n if (cachedAdapter) {\n return cachedAdapter\n }\n\n const adapter = createSQLiteCorePersistenceAdapter({\n ...adapterBaseOptions,\n driver,\n schemaMismatchPolicy: resolvedSchemaMismatchPolicy,\n ...(schemaVersion === undefined ? {} : { schemaVersion }),\n })\n adapterCache.set(cacheKey, adapter)\n return adapter\n }\n\n const createCollectionPersistence = (\n collectionId: string | undefined,\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence => {\n const adapter = getAdapterForCollection(mode, schemaVersion)\n if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {\n if (collectionId === undefined) {\n resolvedCoordinator.setAdapter(adapter)\n } else {\n resolvedCoordinator.setAdapterForCollection(collectionId, adapter)\n }\n }\n return {\n adapter,\n coordinator: resolvedCoordinator,\n }\n }\n\n const defaultPersistence = createCollectionPersistence(\n undefined,\n `sync-absent`,\n undefined,\n )\n if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {\n resolvedCoordinator.setAdapter(defaultPersistence.adapter)\n }\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ collectionId, mode, schemaVersion }) =>\n createCollectionPersistence(collectionId, mode, schemaVersion),\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(undefined, mode, undefined),\n }\n}\n"],"names":["BrowserWASQLiteDriver","DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS","DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS","SingleProcessCoordinator","createSQLiteCorePersistenceAdapter","BrowserCollectionCoordinator"],"mappings":";;;;;AAkCA,SAAS,8BACP,QACuC;AACvC,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,4BACP,gBACA,MACuC;AACvC,MAAI,gBAAgB;AAClB,WAAO,8BAA8B,cAAc;AAAA,EACrD;AAEA,SAAO,SAAS,iBAAiB,uBAAuB;AAC1D;AAEA,SAAS,sBACP,sBACA,eACQ;AACR,QAAM,mBACJ,kBAAkB,SAAY,mBAAmB,UAAU,aAAa;AAC1E,SAAO,GAAG,oBAAoB,IAAI,gBAAgB;AACpD;AAEA,SAAS,2BACP,SACc;AACd,SAAO,IAAIA,eAAAA,sBAAsB;AAAA,IAC/B,UAAU,QAAQ;AAAA,EAAA,CACnB;AACH;AAEA,SAAS,0BACP,SAIA;AACA,SAAO;AAAA,IACL,uBACE,QAAQ,yBAAyBC,wBAAAA;AAAAA,IACnC,6BACE,QAAQ,+BACRC,wBAAAA;AAAAA,IACF,0BAA0B,QAAQ;AAAA,EAAA;AAEtC;AAOO,SAAS,iCACd,SACgC;AAChC,QAAM,EAAE,aAAa,qBAAA,IAAyB;AAC9C,QAAM,SAAS,2BAA2B,OAAO;AACjD,QAAM,qBAAqB,0BAA0B,OAAO;AAC5D,QAAM,sBAAsB,eAAe,IAAIC,iDAAA;AAC/C,QAAM,mCAAmB,IAAA;AAKzB,QAAM,0BAA0B,CAC9B,MACA,kBACG;AACH,UAAM,+BAA+B;AAAA,MACnC;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,gBAAgB,aAAa,IAAI,QAAQ;AAC/C,QAAI,eAAe;AACjB,aAAO;AAAA,IACT;AAEA,UAAM,UAAUC,wBAAAA,mCAAmC;AAAA,MACjD,GAAG;AAAA,MACH;AAAA,MACA,sBAAsB;AAAA,MACtB,GAAI,kBAAkB,SAAY,CAAA,IAAK,EAAE,cAAA;AAAA,IAAc,CACxD;AACD,iBAAa,IAAI,UAAU,OAAO;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,8BAA8B,CAClC,cACA,MACA,kBACmC;AACnC,UAAM,UAAU,wBAAwB,MAAM,aAAa;AAC3D,QAAI,+BAA+BC,mBAAAA,8BAA8B;AAC/D,UAAI,iBAAiB,QAAW;AAC9B,4BAAoB,WAAW,OAAO;AAAA,MACxC,OAAO;AACL,4BAAoB,wBAAwB,cAAc,OAAO;AAAA,MACnE;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA,aAAa;AAAA,IAAA;AAAA,EAEjB;AAEA,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEF,MAAI,+BAA+BA,mBAAAA,8BAA8B;AAC/D,wBAAoB,WAAW,mBAAmB,OAAO;AAAA,EAC3D;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,iCAAiC,CAAC,EAAE,cAAc,MAAM,oBACtD,4BAA4B,cAAc,MAAM,aAAa;AAAA,IAC/D,2BAA2B,CAAC,SAC1B,4BAA4B,QAAW,MAAM,MAAS;AAAA,EAAA;AAE5D;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"opfs-database.cjs","sources":["../../src/opfs-database.ts"],"sourcesContent":["import {\n InvalidPersistedCollectionConfigError,\n PersistenceUnavailableError,\n} from '@tanstack/db-sqlite-persistence-core'\nimport OPFSWorkerConstructor from './opfs-worker?worker'\nimport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\nimport type {\n BrowserOPFSWorkerErrorCode,\n BrowserOPFSWorkerRequest,\n BrowserOPFSWorkerResponse,\n} from './opfs-worker-protocol'\n\nconst DEFAULT_VFS_NAME = `opfs`\nconst DEFAULT_OPEN_TIMEOUT_MS = 30_000\nconst MAX_OPEN_TIMEOUT_MS = 2_147_483_647\ntype BrowserOPFSFeatureGlobal = {\n navigator?: {\n storage?: {\n getDirectory?: () => Promise<unknown>\n }\n }\n Worker?: typeof Worker\n}\n\nexport type OpenBrowserWASQLiteOPFSDatabaseOptions = {\n databaseName: string\n vfsName?: string\n /** Defaults to 30 seconds. Set to 0 to wait without a deadline. */\n timeoutMs?: number\n /** Cancels only the database open, not an established connection. */\n signal?: AbortSignal\n}\n\ntype BrowserOPFSWorkerLike = Pick<\n Worker,\n `postMessage` | `terminate` | `addEventListener` | `removeEventListener`\n>\n\ntype PendingWorkerRequest = {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n}\n\ntype BrowserOPFSWorkerRequestWithoutId =\n BrowserOPFSWorkerRequest extends infer TRequest\n ? TRequest extends { requestId: string }\n ? Omit<TRequest, `requestId`>\n : never\n : never\n\nclass OPFSWorkerRequestError extends Error {\n constructor(\n readonly code: BrowserOPFSWorkerErrorCode,\n message: string,\n ) {\n super(message)\n this.name = `OPFSWorkerRequestError`\n }\n}\n\nfunction hasOPFSBrowserPrerequisites(globalObject: unknown): boolean {\n const candidate = globalObject as BrowserOPFSFeatureGlobal\n const getDirectory = candidate.navigator?.storage?.getDirectory\n const WorkerConstructor = candidate.Worker\n\n return (\n typeof getDirectory === `function` &&\n typeof WorkerConstructor === `function`\n )\n}\n\nfunction createWorkerError(\n code: BrowserOPFSWorkerErrorCode,\n message: string,\n): Error {\n if (code === `PERSISTENCE_UNAVAILABLE`) {\n return new PersistenceUnavailableError(message)\n }\n if (code === `INVALID_CONFIG`) {\n return new InvalidPersistedCollectionConfigError(message)\n }\n return new OPFSWorkerRequestError(code, message)\n}\n\nfunction createWorkerRequestIdFactory(): () => string {\n let sequence = 0\n const prefix = `opfs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`\n\n return () => {\n sequence++\n return `${prefix}-${sequence}`\n }\n}\n\nfunction createOPFSWorkerInstance(): BrowserOPFSWorkerLike {\n const WorkerConstructor = (\n globalThis as typeof globalThis & { Worker?: typeof Worker }\n ).Worker\n if (typeof WorkerConstructor !== `function`) {\n throw new PersistenceUnavailableError(\n `Web Worker support is required for browser OPFS persistence`,\n )\n }\n\n return new (OPFSWorkerConstructor as unknown as new () => BrowserOPFSWorkerLike)()\n}\n\nfunction getOpenAbortError(signal: AbortSignal): Error {\n const reason: unknown = signal.reason\n return reason instanceof Error\n ? reason\n : new DOMException(\n `Opening browser OPFS database was aborted`,\n `AbortError`,\n )\n}\n\n/**\n * Creates a browser wa-sqlite database handle backed by OPFS and\n * OPFSCoopSyncVFS.\n */\nexport async function openBrowserWASQLiteOPFSDatabase(\n options: OpenBrowserWASQLiteOPFSDatabaseOptions,\n): Promise<BrowserWASQLiteDatabase> {\n const databaseName = options.databaseName.trim()\n if (databaseName.length === 0) {\n throw new InvalidPersistedCollectionConfigError(\n `Browser wa-sqlite databaseName cannot be empty`,\n )\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_OPEN_TIMEOUT_MS\n if (\n !Number.isSafeInteger(timeoutMs) ||\n timeoutMs < 0 ||\n timeoutMs > MAX_OPEN_TIMEOUT_MS\n ) {\n throw new InvalidPersistedCollectionConfigError(\n `Browser wa-sqlite timeoutMs must be an integer between 0 and ${MAX_OPEN_TIMEOUT_MS}`,\n )\n }\n\n const signal = options.signal\n if (signal?.aborted) {\n throw getOpenAbortError(signal)\n }\n\n if (!hasOPFSBrowserPrerequisites(globalThis)) {\n throw new PersistenceUnavailableError(\n `Browser OPFS prerequisites are not available in this runtime`,\n )\n }\n\n const vfsName = options.vfsName ?? DEFAULT_VFS_NAME\n const worker = createOPFSWorkerInstance()\n const nextRequestId = createWorkerRequestIdFactory()\n const pendingRequests = new Map<string, PendingWorkerRequest>()\n let disposed = false\n\n const disposeWorker = (): void => {\n if (disposed) {\n return\n }\n disposed = true\n globalThis.removeEventListener(`pagehide`, onPageHide)\n worker.removeEventListener(`message`, onMessage)\n worker.removeEventListener(`error`, onError)\n worker.removeEventListener(`messageerror`, onMessageError)\n worker.terminate()\n }\n\n const rejectAllPendingRequests = (error: Error): void => {\n for (const pendingRequest of pendingRequests.values()) {\n pendingRequest.reject(error)\n }\n pendingRequests.clear()\n }\n\n const onMessage = (event: Event): void => {\n const messageEvent = event as MessageEvent<BrowserOPFSWorkerResponse>\n const response = messageEvent.data\n\n const pendingRequest = pendingRequests.get(response.requestId)\n if (!pendingRequest) {\n return\n }\n pendingRequests.delete(response.requestId)\n\n if (response.ok) {\n pendingRequest.resolve(response.rows ?? [])\n return\n }\n\n pendingRequest.reject(createWorkerError(response.code, response.error))\n }\n\n const onError = (): void => {\n rejectAllPendingRequests(\n new PersistenceUnavailableError(`OPFS worker terminated unexpectedly`),\n )\n disposeWorker()\n }\n\n const onMessageError = (): void => {\n rejectAllPendingRequests(\n new PersistenceUnavailableError(\n `OPFS worker message serialization failed`,\n ),\n )\n disposeWorker()\n }\n\n const onPageHide = (): void => {\n // A document entering the back/forward cache can be frozen before an\n // asynchronous close completes, including while initialization is pending.\n rejectAllPendingRequests(\n new DOMException(\n `The page is closing its SQLite connection`,\n `AbortError`,\n ),\n )\n disposeWorker()\n }\n\n worker.addEventListener(`message`, onMessage)\n worker.addEventListener(`error`, onError)\n worker.addEventListener(`messageerror`, onMessageError)\n globalThis.addEventListener(`pagehide`, onPageHide)\n\n const sendWorkerRequest = <T>(\n request: BrowserOPFSWorkerRequestWithoutId,\n ): Promise<T> => {\n if (disposed) {\n return Promise.reject(\n new InvalidPersistedCollectionConfigError(\n `Browser OPFS worker connection is closed`,\n ),\n )\n }\n\n const requestId = nextRequestId()\n return new Promise<T>((resolve, reject) => {\n pendingRequests.set(requestId, {\n resolve: (value) => {\n resolve(value as T)\n },\n reject,\n })\n try {\n const payload = {\n ...request,\n requestId,\n } as BrowserOPFSWorkerRequest\n worker.postMessage(payload)\n } catch (error) {\n pendingRequests.delete(requestId)\n reject(\n new PersistenceUnavailableError(\n `Failed to send message to OPFS worker: ${(error as Error).message}`,\n ),\n )\n }\n })\n }\n\n let openTimeout: ReturnType<typeof setTimeout> | undefined\n const onAbort = (): void => {\n if (!signal) return\n rejectAllPendingRequests(getOpenAbortError(signal))\n disposeWorker()\n }\n const onTimeout = (): void => {\n rejectAllPendingRequests(\n new DOMException(\n `Opening browser OPFS database timed out after ${timeoutMs} ms`,\n `TimeoutError`,\n ),\n )\n disposeWorker()\n }\n\n try {\n const initialization = sendWorkerRequest<void>({\n type: `init`,\n databaseName,\n vfsName,\n })\n signal?.addEventListener(`abort`, onAbort, { once: true })\n if (signal?.aborted) onAbort()\n if (timeoutMs > 0) {\n openTimeout = setTimeout(onTimeout, timeoutMs)\n }\n await initialization\n } catch (error) {\n disposeWorker()\n throw error\n } finally {\n if (openTimeout !== undefined) clearTimeout(openTimeout)\n signal?.removeEventListener(`abort`, onAbort)\n }\n\n return {\n execute: <TRow = unknown>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ) =>\n sendWorkerRequest<ReadonlyArray<TRow>>({\n type: `execute`,\n sql,\n params,\n }),\n close: async () => {\n if (disposed) {\n return\n }\n\n let closeError: unknown\n try {\n await sendWorkerRequest<void>({\n type: `close`,\n })\n } catch (error) {\n closeError = error\n } finally {\n disposeWorker()\n }\n\n if (closeError) {\n throw closeError\n }\n },\n }\n}\n"],"names":["PersistenceUnavailableError","InvalidPersistedCollectionConfigError","OPFSWorkerConstructor"],"mappings":"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"}
1
+ {"version":3,"file":"opfs-database.cjs","sources":["../../src/opfs-database.ts"],"sourcesContent":["import {\n InvalidPersistedCollectionConfigError,\n PersistenceUnavailableError,\n} from '@tanstack/db-sqlite-persistence-core'\nimport OPFSWorkerConstructor from './opfs-worker?worker'\nimport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\nimport type {\n BrowserOPFSWorkerErrorCode,\n BrowserOPFSWorkerRequest,\n BrowserOPFSWorkerResponse,\n} from './opfs-worker-protocol'\n\nconst DEFAULT_VFS_NAME = `opfs`\nconst DEFAULT_OPEN_TIMEOUT_MS = 30_000\nconst MAX_OPEN_TIMEOUT_MS = 2_147_483_647\ntype BrowserOPFSFeatureGlobal = {\n navigator?: {\n storage?: {\n getDirectory?: () => Promise<unknown>\n }\n }\n Worker?: typeof Worker\n}\n\nexport type OpenBrowserWASQLiteOPFSDatabaseOptions = {\n databaseName: string\n vfsName?: string\n /** Defaults to 30 seconds. Set to 0 to wait without a deadline. */\n timeoutMs?: number\n /** Cancels only the database open, not an established connection. */\n signal?: AbortSignal\n}\n\ntype BrowserOPFSWorkerLike = Pick<\n Worker,\n `postMessage` | `terminate` | `addEventListener` | `removeEventListener`\n>\n\ntype PendingWorkerRequest = {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n}\n\ntype BrowserOPFSWorkerRequestWithoutId =\n BrowserOPFSWorkerRequest extends infer TRequest\n ? TRequest extends { requestId: string }\n ? Omit<TRequest, `requestId`>\n : never\n : never\n\nclass OPFSWorkerRequestError extends Error {\n constructor(\n readonly code: BrowserOPFSWorkerErrorCode,\n message: string,\n ) {\n super(message)\n this.name = `OPFSWorkerRequestError`\n }\n}\n\nfunction hasOPFSBrowserPrerequisites(globalObject: unknown): boolean {\n const candidate = globalObject as BrowserOPFSFeatureGlobal\n const getDirectory = candidate.navigator?.storage?.getDirectory\n const WorkerConstructor = candidate.Worker\n\n return (\n typeof getDirectory === `function` &&\n typeof WorkerConstructor === `function`\n )\n}\n\nfunction createWorkerError(\n code: BrowserOPFSWorkerErrorCode,\n message: string,\n): Error {\n if (code === `PERSISTENCE_UNAVAILABLE`) {\n return new PersistenceUnavailableError(message)\n }\n if (code === `INVALID_CONFIG`) {\n return new InvalidPersistedCollectionConfigError(message)\n }\n return new OPFSWorkerRequestError(code, message)\n}\n\nfunction createWorkerRequestIdFactory(): () => string {\n let sequence = 0\n const prefix = `opfs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`\n\n return () => {\n sequence++\n return `${prefix}-${sequence}`\n }\n}\n\nfunction createOPFSWorkerInstance(): BrowserOPFSWorkerLike {\n const WorkerConstructor = (\n globalThis as typeof globalThis & { Worker?: typeof Worker }\n ).Worker\n if (typeof WorkerConstructor !== `function`) {\n throw new PersistenceUnavailableError(\n `Web Worker support is required for browser OPFS persistence`,\n )\n }\n\n return new (\n OPFSWorkerConstructor as unknown as new () => BrowserOPFSWorkerLike\n )()\n}\n\nfunction getOpenAbortError(signal: AbortSignal): Error {\n const reason: unknown = signal.reason\n return reason instanceof Error\n ? reason\n : new DOMException(\n `Opening browser OPFS database was aborted`,\n `AbortError`,\n )\n}\n\n/**\n * Creates a browser wa-sqlite database handle backed by OPFS and\n * OPFSCoopSyncVFS.\n */\nexport async function openBrowserWASQLiteOPFSDatabase(\n options: OpenBrowserWASQLiteOPFSDatabaseOptions,\n): Promise<BrowserWASQLiteDatabase> {\n const databaseName = options.databaseName.trim()\n if (databaseName.length === 0) {\n throw new InvalidPersistedCollectionConfigError(\n `Browser wa-sqlite databaseName cannot be empty`,\n )\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_OPEN_TIMEOUT_MS\n if (\n !Number.isSafeInteger(timeoutMs) ||\n timeoutMs < 0 ||\n timeoutMs > MAX_OPEN_TIMEOUT_MS\n ) {\n throw new InvalidPersistedCollectionConfigError(\n `Browser wa-sqlite timeoutMs must be an integer between 0 and ${MAX_OPEN_TIMEOUT_MS}`,\n )\n }\n\n const signal = options.signal\n if (signal?.aborted) {\n throw getOpenAbortError(signal)\n }\n\n if (!hasOPFSBrowserPrerequisites(globalThis)) {\n throw new PersistenceUnavailableError(\n `Browser OPFS prerequisites are not available in this runtime`,\n )\n }\n\n const vfsName = options.vfsName ?? DEFAULT_VFS_NAME\n const worker = createOPFSWorkerInstance()\n const nextRequestId = createWorkerRequestIdFactory()\n const pendingRequests = new Map<string, PendingWorkerRequest>()\n let disposed = false\n\n const disposeWorker = (): void => {\n if (disposed) {\n return\n }\n disposed = true\n globalThis.removeEventListener(`pagehide`, onPageHide)\n worker.removeEventListener(`message`, onMessage)\n worker.removeEventListener(`error`, onError)\n worker.removeEventListener(`messageerror`, onMessageError)\n worker.terminate()\n }\n\n const rejectAllPendingRequests = (error: Error): void => {\n for (const pendingRequest of pendingRequests.values()) {\n pendingRequest.reject(error)\n }\n pendingRequests.clear()\n }\n\n const onMessage = (event: Event): void => {\n const messageEvent = event as MessageEvent<BrowserOPFSWorkerResponse>\n const response = messageEvent.data\n\n const pendingRequest = pendingRequests.get(response.requestId)\n if (!pendingRequest) {\n return\n }\n pendingRequests.delete(response.requestId)\n\n if (response.ok) {\n pendingRequest.resolve(response.rows ?? [])\n return\n }\n\n pendingRequest.reject(createWorkerError(response.code, response.error))\n }\n\n const onError = (): void => {\n rejectAllPendingRequests(\n new PersistenceUnavailableError(`OPFS worker terminated unexpectedly`),\n )\n disposeWorker()\n }\n\n const onMessageError = (): void => {\n rejectAllPendingRequests(\n new PersistenceUnavailableError(\n `OPFS worker message serialization failed`,\n ),\n )\n disposeWorker()\n }\n\n const onPageHide = (): void => {\n // A document entering the back/forward cache can be frozen before an\n // asynchronous close completes, including while initialization is pending.\n rejectAllPendingRequests(\n new DOMException(\n `The page is closing its SQLite connection`,\n `AbortError`,\n ),\n )\n disposeWorker()\n }\n\n worker.addEventListener(`message`, onMessage)\n worker.addEventListener(`error`, onError)\n worker.addEventListener(`messageerror`, onMessageError)\n globalThis.addEventListener(`pagehide`, onPageHide)\n\n const sendWorkerRequest = <T>(\n request: BrowserOPFSWorkerRequestWithoutId,\n ): Promise<T> => {\n if (disposed) {\n return Promise.reject(\n new InvalidPersistedCollectionConfigError(\n `Browser OPFS worker connection is closed`,\n ),\n )\n }\n\n const requestId = nextRequestId()\n return new Promise<T>((resolve, reject) => {\n pendingRequests.set(requestId, {\n resolve: (value) => {\n resolve(value as T)\n },\n reject,\n })\n try {\n const payload = {\n ...request,\n requestId,\n } as BrowserOPFSWorkerRequest\n worker.postMessage(payload)\n } catch (error) {\n pendingRequests.delete(requestId)\n reject(\n new PersistenceUnavailableError(\n `Failed to send message to OPFS worker: ${(error as Error).message}`,\n ),\n )\n }\n })\n }\n\n let openTimeout: ReturnType<typeof setTimeout> | undefined\n const onAbort = (): void => {\n if (!signal) return\n rejectAllPendingRequests(getOpenAbortError(signal))\n disposeWorker()\n }\n const onTimeout = (): void => {\n rejectAllPendingRequests(\n new DOMException(\n `Opening browser OPFS database timed out after ${timeoutMs} ms`,\n `TimeoutError`,\n ),\n )\n disposeWorker()\n }\n\n try {\n const initialization = sendWorkerRequest<void>({\n type: `init`,\n databaseName,\n vfsName,\n })\n signal?.addEventListener(`abort`, onAbort, { once: true })\n if (signal?.aborted) onAbort()\n if (timeoutMs > 0) {\n openTimeout = setTimeout(onTimeout, timeoutMs)\n }\n await initialization\n } catch (error) {\n disposeWorker()\n throw error\n } finally {\n if (openTimeout !== undefined) clearTimeout(openTimeout)\n signal?.removeEventListener(`abort`, onAbort)\n }\n\n return {\n execute: <TRow = unknown>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ) =>\n sendWorkerRequest<ReadonlyArray<TRow>>({\n type: `execute`,\n sql,\n params,\n }),\n close: async () => {\n if (disposed) {\n return\n }\n\n let closeError: unknown\n try {\n await sendWorkerRequest<void>({\n type: `close`,\n })\n } catch (error) {\n closeError = error\n } finally {\n disposeWorker()\n }\n\n if (closeError) {\n throw closeError\n }\n },\n }\n}\n"],"names":["PersistenceUnavailableError","InvalidPersistedCollectionConfigError","OPFSWorkerConstructor"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"browser-persistence.js","sources":["../../src/browser-persistence.ts"],"sourcesContent":["import {\n DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS,\n DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS,\n SingleProcessCoordinator,\n createSQLiteCorePersistenceAdapter,\n} from '@tanstack/db-sqlite-persistence-core'\nimport { BrowserCollectionCoordinator } from './browser-coordinator'\nimport { BrowserWASQLiteDriver } from './wa-sqlite-driver'\nimport type {\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n SQLiteCoreAdapterOptions,\n SQLiteDriver,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\n\nexport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\n\ntype BrowserSQLiteCoreSchemaMismatchPolicy =\n | `sync-present-reset`\n | `sync-absent-error`\n | `reset`\n\nexport type BrowserWASQLiteSchemaMismatchPolicy =\n | BrowserSQLiteCoreSchemaMismatchPolicy\n | `throw`\n\nexport type BrowserWASQLitePersistenceOptions = Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> & {\n database: BrowserWASQLiteDatabase\n coordinator?: PersistedCollectionCoordinator\n schemaMismatchPolicy?: BrowserWASQLiteSchemaMismatchPolicy\n}\n\nfunction normalizeSchemaMismatchPolicy(\n policy: BrowserWASQLiteSchemaMismatchPolicy,\n): BrowserSQLiteCoreSchemaMismatchPolicy {\n if (policy === `throw`) {\n return `sync-absent-error`\n }\n\n return policy\n}\n\nfunction resolveSchemaMismatchPolicy(\n explicitPolicy: BrowserWASQLiteSchemaMismatchPolicy | undefined,\n mode: PersistedCollectionMode,\n): BrowserSQLiteCoreSchemaMismatchPolicy {\n if (explicitPolicy) {\n return normalizeSchemaMismatchPolicy(explicitPolicy)\n }\n\n return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`\n}\n\nfunction createAdapterCacheKey(\n schemaMismatchPolicy: BrowserSQLiteCoreSchemaMismatchPolicy,\n schemaVersion: number | undefined,\n): string {\n const schemaVersionKey =\n schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`\n return `${schemaMismatchPolicy}|${schemaVersionKey}`\n}\n\nfunction createInternalSQLiteDriver(\n options: BrowserWASQLitePersistenceOptions,\n): SQLiteDriver {\n return new BrowserWASQLiteDriver({\n database: options.database,\n })\n}\n\nfunction resolveAdapterBaseOptions(\n options: BrowserWASQLitePersistenceOptions,\n): Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> {\n return {\n appliedTxPruneMaxRows:\n options.appliedTxPruneMaxRows ?? DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS,\n appliedTxPruneMaxAgeSeconds:\n options.appliedTxPruneMaxAgeSeconds ??\n DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS,\n pullSinceReloadThreshold: options.pullSinceReloadThreshold,\n }\n}\n\n/**\n * Creates a shared browser wa-sqlite persistence instance that can be reused by\n * many collections on the same database. This is single-tab wiring using\n * SingleProcessCoordinator semantics (no election required).\n */\nexport function createBrowserWASQLitePersistence(\n options: BrowserWASQLitePersistenceOptions,\n): PersistedCollectionPersistence {\n const { coordinator, schemaMismatchPolicy } = options\n const driver = createInternalSQLiteDriver(options)\n const adapterBaseOptions = resolveAdapterBaseOptions(options)\n const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator()\n const adapterCache = new Map<\n string,\n ReturnType<typeof createSQLiteCorePersistenceAdapter>\n >()\n\n const getAdapterForCollection = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ) => {\n const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(\n schemaMismatchPolicy,\n mode,\n )\n const cacheKey = createAdapterCacheKey(\n resolvedSchemaMismatchPolicy,\n schemaVersion,\n )\n const cachedAdapter = adapterCache.get(cacheKey)\n if (cachedAdapter) {\n return cachedAdapter\n }\n\n const adapter = createSQLiteCorePersistenceAdapter({\n ...adapterBaseOptions,\n driver,\n schemaMismatchPolicy: resolvedSchemaMismatchPolicy,\n ...(schemaVersion === undefined ? {} : { schemaVersion }),\n })\n adapterCache.set(cacheKey, adapter)\n return adapter\n }\n\n const createCollectionPersistence = (\n collectionId: string | undefined,\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence => {\n const adapter = getAdapterForCollection(mode, schemaVersion)\n if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {\n if (collectionId === undefined) {\n resolvedCoordinator.setAdapter(adapter)\n } else {\n resolvedCoordinator.setAdapterForCollection(collectionId, adapter)\n }\n }\n return {\n adapter,\n coordinator: resolvedCoordinator,\n }\n }\n\n const defaultPersistence = createCollectionPersistence(\n undefined,\n `sync-absent`,\n undefined,\n )\n if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {\n resolvedCoordinator.setAdapter(defaultPersistence.adapter)\n }\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ collectionId, mode, schemaVersion }) =>\n createCollectionPersistence(collectionId, mode, schemaVersion),\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(undefined, mode, undefined),\n }\n}\n"],"names":[],"mappings":";;;AAqCA,SAAS,8BACP,QACuC;AACvC,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,4BACP,gBACA,MACuC;AACvC,MAAI,gBAAgB;AAClB,WAAO,8BAA8B,cAAc;AAAA,EACrD;AAEA,SAAO,SAAS,iBAAiB,uBAAuB;AAC1D;AAEA,SAAS,sBACP,sBACA,eACQ;AACR,QAAM,mBACJ,kBAAkB,SAAY,mBAAmB,UAAU,aAAa;AAC1E,SAAO,GAAG,oBAAoB,IAAI,gBAAgB;AACpD;AAEA,SAAS,2BACP,SACc;AACd,SAAO,IAAI,sBAAsB;AAAA,IAC/B,UAAU,QAAQ;AAAA,EAAA,CACnB;AACH;AAEA,SAAS,0BACP,SAIA;AACA,SAAO;AAAA,IACL,uBACE,QAAQ,yBAAyB;AAAA,IACnC,6BACE,QAAQ,+BACR;AAAA,IACF,0BAA0B,QAAQ;AAAA,EAAA;AAEtC;AAOO,SAAS,iCACd,SACgC;AAChC,QAAM,EAAE,aAAa,qBAAA,IAAyB;AAC9C,QAAM,SAAS,2BAA2B,OAAO;AACjD,QAAM,qBAAqB,0BAA0B,OAAO;AAC5D,QAAM,sBAAsB,eAAe,IAAI,yBAAA;AAC/C,QAAM,mCAAmB,IAAA;AAKzB,QAAM,0BAA0B,CAC9B,MACA,kBACG;AACH,UAAM,+BAA+B;AAAA,MACnC;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,gBAAgB,aAAa,IAAI,QAAQ;AAC/C,QAAI,eAAe;AACjB,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,mCAAmC;AAAA,MACjD,GAAG;AAAA,MACH;AAAA,MACA,sBAAsB;AAAA,MACtB,GAAI,kBAAkB,SAAY,CAAA,IAAK,EAAE,cAAA;AAAA,IAAc,CACxD;AACD,iBAAa,IAAI,UAAU,OAAO;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,8BAA8B,CAClC,cACA,MACA,kBACmC;AACnC,UAAM,UAAU,wBAAwB,MAAM,aAAa;AAC3D,QAAI,+BAA+B,8BAA8B;AAC/D,UAAI,iBAAiB,QAAW;AAC9B,4BAAoB,WAAW,OAAO;AAAA,MACxC,OAAO;AACL,4BAAoB,wBAAwB,cAAc,OAAO;AAAA,MACnE;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA,aAAa;AAAA,IAAA;AAAA,EAEjB;AAEA,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEF,MAAI,+BAA+B,8BAA8B;AAC/D,wBAAoB,WAAW,mBAAmB,OAAO;AAAA,EAC3D;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,iCAAiC,CAAC,EAAE,cAAc,MAAM,oBACtD,4BAA4B,cAAc,MAAM,aAAa;AAAA,IAC/D,2BAA2B,CAAC,SAC1B,4BAA4B,QAAW,MAAM,MAAS;AAAA,EAAA;AAE5D;"}
1
+ {"version":3,"file":"browser-persistence.js","sources":["../../src/browser-persistence.ts"],"sourcesContent":["import {\n DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS,\n DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS,\n SingleProcessCoordinator,\n createSQLiteCorePersistenceAdapter,\n} from '@tanstack/db-sqlite-persistence-core'\nimport { BrowserCollectionCoordinator } from './browser-coordinator'\nimport { BrowserWASQLiteDriver } from './wa-sqlite-driver'\nimport type {\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n SQLiteCoreAdapterOptions,\n SQLiteDriver,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\n\nexport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\n\ntype BrowserSQLiteCoreSchemaMismatchPolicy =\n `sync-present-reset` | `sync-absent-error` | `reset`\n\nexport type BrowserWASQLiteSchemaMismatchPolicy =\n BrowserSQLiteCoreSchemaMismatchPolicy | `throw`\n\nexport type BrowserWASQLitePersistenceOptions = Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> & {\n database: BrowserWASQLiteDatabase\n coordinator?: PersistedCollectionCoordinator\n schemaMismatchPolicy?: BrowserWASQLiteSchemaMismatchPolicy\n}\n\nfunction normalizeSchemaMismatchPolicy(\n policy: BrowserWASQLiteSchemaMismatchPolicy,\n): BrowserSQLiteCoreSchemaMismatchPolicy {\n if (policy === `throw`) {\n return `sync-absent-error`\n }\n\n return policy\n}\n\nfunction resolveSchemaMismatchPolicy(\n explicitPolicy: BrowserWASQLiteSchemaMismatchPolicy | undefined,\n mode: PersistedCollectionMode,\n): BrowserSQLiteCoreSchemaMismatchPolicy {\n if (explicitPolicy) {\n return normalizeSchemaMismatchPolicy(explicitPolicy)\n }\n\n return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`\n}\n\nfunction createAdapterCacheKey(\n schemaMismatchPolicy: BrowserSQLiteCoreSchemaMismatchPolicy,\n schemaVersion: number | undefined,\n): string {\n const schemaVersionKey =\n schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`\n return `${schemaMismatchPolicy}|${schemaVersionKey}`\n}\n\nfunction createInternalSQLiteDriver(\n options: BrowserWASQLitePersistenceOptions,\n): SQLiteDriver {\n return new BrowserWASQLiteDriver({\n database: options.database,\n })\n}\n\nfunction resolveAdapterBaseOptions(\n options: BrowserWASQLitePersistenceOptions,\n): Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> {\n return {\n appliedTxPruneMaxRows:\n options.appliedTxPruneMaxRows ?? DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS,\n appliedTxPruneMaxAgeSeconds:\n options.appliedTxPruneMaxAgeSeconds ??\n DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS,\n pullSinceReloadThreshold: options.pullSinceReloadThreshold,\n }\n}\n\n/**\n * Creates a shared browser wa-sqlite persistence instance that can be reused by\n * many collections on the same database. This is single-tab wiring using\n * SingleProcessCoordinator semantics (no election required).\n */\nexport function createBrowserWASQLitePersistence(\n options: BrowserWASQLitePersistenceOptions,\n): PersistedCollectionPersistence {\n const { coordinator, schemaMismatchPolicy } = options\n const driver = createInternalSQLiteDriver(options)\n const adapterBaseOptions = resolveAdapterBaseOptions(options)\n const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator()\n const adapterCache = new Map<\n string,\n ReturnType<typeof createSQLiteCorePersistenceAdapter>\n >()\n\n const getAdapterForCollection = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ) => {\n const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(\n schemaMismatchPolicy,\n mode,\n )\n const cacheKey = createAdapterCacheKey(\n resolvedSchemaMismatchPolicy,\n schemaVersion,\n )\n const cachedAdapter = adapterCache.get(cacheKey)\n if (cachedAdapter) {\n return cachedAdapter\n }\n\n const adapter = createSQLiteCorePersistenceAdapter({\n ...adapterBaseOptions,\n driver,\n schemaMismatchPolicy: resolvedSchemaMismatchPolicy,\n ...(schemaVersion === undefined ? {} : { schemaVersion }),\n })\n adapterCache.set(cacheKey, adapter)\n return adapter\n }\n\n const createCollectionPersistence = (\n collectionId: string | undefined,\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence => {\n const adapter = getAdapterForCollection(mode, schemaVersion)\n if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {\n if (collectionId === undefined) {\n resolvedCoordinator.setAdapter(adapter)\n } else {\n resolvedCoordinator.setAdapterForCollection(collectionId, adapter)\n }\n }\n return {\n adapter,\n coordinator: resolvedCoordinator,\n }\n }\n\n const defaultPersistence = createCollectionPersistence(\n undefined,\n `sync-absent`,\n undefined,\n )\n if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {\n resolvedCoordinator.setAdapter(defaultPersistence.adapter)\n }\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ collectionId, mode, schemaVersion }) =>\n createCollectionPersistence(collectionId, mode, schemaVersion),\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(undefined, mode, undefined),\n }\n}\n"],"names":[],"mappings":";;;AAkCA,SAAS,8BACP,QACuC;AACvC,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,4BACP,gBACA,MACuC;AACvC,MAAI,gBAAgB;AAClB,WAAO,8BAA8B,cAAc;AAAA,EACrD;AAEA,SAAO,SAAS,iBAAiB,uBAAuB;AAC1D;AAEA,SAAS,sBACP,sBACA,eACQ;AACR,QAAM,mBACJ,kBAAkB,SAAY,mBAAmB,UAAU,aAAa;AAC1E,SAAO,GAAG,oBAAoB,IAAI,gBAAgB;AACpD;AAEA,SAAS,2BACP,SACc;AACd,SAAO,IAAI,sBAAsB;AAAA,IAC/B,UAAU,QAAQ;AAAA,EAAA,CACnB;AACH;AAEA,SAAS,0BACP,SAIA;AACA,SAAO;AAAA,IACL,uBACE,QAAQ,yBAAyB;AAAA,IACnC,6BACE,QAAQ,+BACR;AAAA,IACF,0BAA0B,QAAQ;AAAA,EAAA;AAEtC;AAOO,SAAS,iCACd,SACgC;AAChC,QAAM,EAAE,aAAa,qBAAA,IAAyB;AAC9C,QAAM,SAAS,2BAA2B,OAAO;AACjD,QAAM,qBAAqB,0BAA0B,OAAO;AAC5D,QAAM,sBAAsB,eAAe,IAAI,yBAAA;AAC/C,QAAM,mCAAmB,IAAA;AAKzB,QAAM,0BAA0B,CAC9B,MACA,kBACG;AACH,UAAM,+BAA+B;AAAA,MACnC;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,gBAAgB,aAAa,IAAI,QAAQ;AAC/C,QAAI,eAAe;AACjB,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,mCAAmC;AAAA,MACjD,GAAG;AAAA,MACH;AAAA,MACA,sBAAsB;AAAA,MACtB,GAAI,kBAAkB,SAAY,CAAA,IAAK,EAAE,cAAA;AAAA,IAAc,CACxD;AACD,iBAAa,IAAI,UAAU,OAAO;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,8BAA8B,CAClC,cACA,MACA,kBACmC;AACnC,UAAM,UAAU,wBAAwB,MAAM,aAAa;AAC3D,QAAI,+BAA+B,8BAA8B;AAC/D,UAAI,iBAAiB,QAAW;AAC9B,4BAAoB,WAAW,OAAO;AAAA,MACxC,OAAO;AACL,4BAAoB,wBAAwB,cAAc,OAAO;AAAA,MACnE;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA,aAAa;AAAA,IAAA;AAAA,EAEjB;AAEA,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEF,MAAI,+BAA+B,8BAA8B;AAC/D,wBAAoB,WAAW,mBAAmB,OAAO;AAAA,EAC3D;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,iCAAiC,CAAC,EAAE,cAAc,MAAM,oBACtD,4BAA4B,cAAc,MAAM,aAAa;AAAA,IAC/D,2BAA2B,CAAC,SAC1B,4BAA4B,QAAW,MAAM,MAAS;AAAA,EAAA;AAE5D;"}
@@ -1 +1 @@
1
- {"version":3,"file":"opfs-database.js","sources":["../../src/opfs-database.ts"],"sourcesContent":["import {\n InvalidPersistedCollectionConfigError,\n PersistenceUnavailableError,\n} from '@tanstack/db-sqlite-persistence-core'\nimport OPFSWorkerConstructor from './opfs-worker?worker'\nimport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\nimport type {\n BrowserOPFSWorkerErrorCode,\n BrowserOPFSWorkerRequest,\n BrowserOPFSWorkerResponse,\n} from './opfs-worker-protocol'\n\nconst DEFAULT_VFS_NAME = `opfs`\nconst DEFAULT_OPEN_TIMEOUT_MS = 30_000\nconst MAX_OPEN_TIMEOUT_MS = 2_147_483_647\ntype BrowserOPFSFeatureGlobal = {\n navigator?: {\n storage?: {\n getDirectory?: () => Promise<unknown>\n }\n }\n Worker?: typeof Worker\n}\n\nexport type OpenBrowserWASQLiteOPFSDatabaseOptions = {\n databaseName: string\n vfsName?: string\n /** Defaults to 30 seconds. Set to 0 to wait without a deadline. */\n timeoutMs?: number\n /** Cancels only the database open, not an established connection. */\n signal?: AbortSignal\n}\n\ntype BrowserOPFSWorkerLike = Pick<\n Worker,\n `postMessage` | `terminate` | `addEventListener` | `removeEventListener`\n>\n\ntype PendingWorkerRequest = {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n}\n\ntype BrowserOPFSWorkerRequestWithoutId =\n BrowserOPFSWorkerRequest extends infer TRequest\n ? TRequest extends { requestId: string }\n ? Omit<TRequest, `requestId`>\n : never\n : never\n\nclass OPFSWorkerRequestError extends Error {\n constructor(\n readonly code: BrowserOPFSWorkerErrorCode,\n message: string,\n ) {\n super(message)\n this.name = `OPFSWorkerRequestError`\n }\n}\n\nfunction hasOPFSBrowserPrerequisites(globalObject: unknown): boolean {\n const candidate = globalObject as BrowserOPFSFeatureGlobal\n const getDirectory = candidate.navigator?.storage?.getDirectory\n const WorkerConstructor = candidate.Worker\n\n return (\n typeof getDirectory === `function` &&\n typeof WorkerConstructor === `function`\n )\n}\n\nfunction createWorkerError(\n code: BrowserOPFSWorkerErrorCode,\n message: string,\n): Error {\n if (code === `PERSISTENCE_UNAVAILABLE`) {\n return new PersistenceUnavailableError(message)\n }\n if (code === `INVALID_CONFIG`) {\n return new InvalidPersistedCollectionConfigError(message)\n }\n return new OPFSWorkerRequestError(code, message)\n}\n\nfunction createWorkerRequestIdFactory(): () => string {\n let sequence = 0\n const prefix = `opfs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`\n\n return () => {\n sequence++\n return `${prefix}-${sequence}`\n }\n}\n\nfunction createOPFSWorkerInstance(): BrowserOPFSWorkerLike {\n const WorkerConstructor = (\n globalThis as typeof globalThis & { Worker?: typeof Worker }\n ).Worker\n if (typeof WorkerConstructor !== `function`) {\n throw new PersistenceUnavailableError(\n `Web Worker support is required for browser OPFS persistence`,\n )\n }\n\n return new (OPFSWorkerConstructor as unknown as new () => BrowserOPFSWorkerLike)()\n}\n\nfunction getOpenAbortError(signal: AbortSignal): Error {\n const reason: unknown = signal.reason\n return reason instanceof Error\n ? reason\n : new DOMException(\n `Opening browser OPFS database was aborted`,\n `AbortError`,\n )\n}\n\n/**\n * Creates a browser wa-sqlite database handle backed by OPFS and\n * OPFSCoopSyncVFS.\n */\nexport async function openBrowserWASQLiteOPFSDatabase(\n options: OpenBrowserWASQLiteOPFSDatabaseOptions,\n): Promise<BrowserWASQLiteDatabase> {\n const databaseName = options.databaseName.trim()\n if (databaseName.length === 0) {\n throw new InvalidPersistedCollectionConfigError(\n `Browser wa-sqlite databaseName cannot be empty`,\n )\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_OPEN_TIMEOUT_MS\n if (\n !Number.isSafeInteger(timeoutMs) ||\n timeoutMs < 0 ||\n timeoutMs > MAX_OPEN_TIMEOUT_MS\n ) {\n throw new InvalidPersistedCollectionConfigError(\n `Browser wa-sqlite timeoutMs must be an integer between 0 and ${MAX_OPEN_TIMEOUT_MS}`,\n )\n }\n\n const signal = options.signal\n if (signal?.aborted) {\n throw getOpenAbortError(signal)\n }\n\n if (!hasOPFSBrowserPrerequisites(globalThis)) {\n throw new PersistenceUnavailableError(\n `Browser OPFS prerequisites are not available in this runtime`,\n )\n }\n\n const vfsName = options.vfsName ?? DEFAULT_VFS_NAME\n const worker = createOPFSWorkerInstance()\n const nextRequestId = createWorkerRequestIdFactory()\n const pendingRequests = new Map<string, PendingWorkerRequest>()\n let disposed = false\n\n const disposeWorker = (): void => {\n if (disposed) {\n return\n }\n disposed = true\n globalThis.removeEventListener(`pagehide`, onPageHide)\n worker.removeEventListener(`message`, onMessage)\n worker.removeEventListener(`error`, onError)\n worker.removeEventListener(`messageerror`, onMessageError)\n worker.terminate()\n }\n\n const rejectAllPendingRequests = (error: Error): void => {\n for (const pendingRequest of pendingRequests.values()) {\n pendingRequest.reject(error)\n }\n pendingRequests.clear()\n }\n\n const onMessage = (event: Event): void => {\n const messageEvent = event as MessageEvent<BrowserOPFSWorkerResponse>\n const response = messageEvent.data\n\n const pendingRequest = pendingRequests.get(response.requestId)\n if (!pendingRequest) {\n return\n }\n pendingRequests.delete(response.requestId)\n\n if (response.ok) {\n pendingRequest.resolve(response.rows ?? [])\n return\n }\n\n pendingRequest.reject(createWorkerError(response.code, response.error))\n }\n\n const onError = (): void => {\n rejectAllPendingRequests(\n new PersistenceUnavailableError(`OPFS worker terminated unexpectedly`),\n )\n disposeWorker()\n }\n\n const onMessageError = (): void => {\n rejectAllPendingRequests(\n new PersistenceUnavailableError(\n `OPFS worker message serialization failed`,\n ),\n )\n disposeWorker()\n }\n\n const onPageHide = (): void => {\n // A document entering the back/forward cache can be frozen before an\n // asynchronous close completes, including while initialization is pending.\n rejectAllPendingRequests(\n new DOMException(\n `The page is closing its SQLite connection`,\n `AbortError`,\n ),\n )\n disposeWorker()\n }\n\n worker.addEventListener(`message`, onMessage)\n worker.addEventListener(`error`, onError)\n worker.addEventListener(`messageerror`, onMessageError)\n globalThis.addEventListener(`pagehide`, onPageHide)\n\n const sendWorkerRequest = <T>(\n request: BrowserOPFSWorkerRequestWithoutId,\n ): Promise<T> => {\n if (disposed) {\n return Promise.reject(\n new InvalidPersistedCollectionConfigError(\n `Browser OPFS worker connection is closed`,\n ),\n )\n }\n\n const requestId = nextRequestId()\n return new Promise<T>((resolve, reject) => {\n pendingRequests.set(requestId, {\n resolve: (value) => {\n resolve(value as T)\n },\n reject,\n })\n try {\n const payload = {\n ...request,\n requestId,\n } as BrowserOPFSWorkerRequest\n worker.postMessage(payload)\n } catch (error) {\n pendingRequests.delete(requestId)\n reject(\n new PersistenceUnavailableError(\n `Failed to send message to OPFS worker: ${(error as Error).message}`,\n ),\n )\n }\n })\n }\n\n let openTimeout: ReturnType<typeof setTimeout> | undefined\n const onAbort = (): void => {\n if (!signal) return\n rejectAllPendingRequests(getOpenAbortError(signal))\n disposeWorker()\n }\n const onTimeout = (): void => {\n rejectAllPendingRequests(\n new DOMException(\n `Opening browser OPFS database timed out after ${timeoutMs} ms`,\n `TimeoutError`,\n ),\n )\n disposeWorker()\n }\n\n try {\n const initialization = sendWorkerRequest<void>({\n type: `init`,\n databaseName,\n vfsName,\n })\n signal?.addEventListener(`abort`, onAbort, { once: true })\n if (signal?.aborted) onAbort()\n if (timeoutMs > 0) {\n openTimeout = setTimeout(onTimeout, timeoutMs)\n }\n await initialization\n } catch (error) {\n disposeWorker()\n throw error\n } finally {\n if (openTimeout !== undefined) clearTimeout(openTimeout)\n signal?.removeEventListener(`abort`, onAbort)\n }\n\n return {\n execute: <TRow = unknown>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ) =>\n sendWorkerRequest<ReadonlyArray<TRow>>({\n type: `execute`,\n sql,\n params,\n }),\n close: async () => {\n if (disposed) {\n return\n }\n\n let closeError: unknown\n try {\n await sendWorkerRequest<void>({\n type: `close`,\n })\n } catch (error) {\n closeError = error\n } finally {\n disposeWorker()\n }\n\n if (closeError) {\n throw closeError\n }\n },\n }\n}\n"],"names":["OPFSWorkerConstructor"],"mappings":"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"}
1
+ {"version":3,"file":"opfs-database.js","sources":["../../src/opfs-database.ts"],"sourcesContent":["import {\n InvalidPersistedCollectionConfigError,\n PersistenceUnavailableError,\n} from '@tanstack/db-sqlite-persistence-core'\nimport OPFSWorkerConstructor from './opfs-worker?worker'\nimport type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'\nimport type {\n BrowserOPFSWorkerErrorCode,\n BrowserOPFSWorkerRequest,\n BrowserOPFSWorkerResponse,\n} from './opfs-worker-protocol'\n\nconst DEFAULT_VFS_NAME = `opfs`\nconst DEFAULT_OPEN_TIMEOUT_MS = 30_000\nconst MAX_OPEN_TIMEOUT_MS = 2_147_483_647\ntype BrowserOPFSFeatureGlobal = {\n navigator?: {\n storage?: {\n getDirectory?: () => Promise<unknown>\n }\n }\n Worker?: typeof Worker\n}\n\nexport type OpenBrowserWASQLiteOPFSDatabaseOptions = {\n databaseName: string\n vfsName?: string\n /** Defaults to 30 seconds. Set to 0 to wait without a deadline. */\n timeoutMs?: number\n /** Cancels only the database open, not an established connection. */\n signal?: AbortSignal\n}\n\ntype BrowserOPFSWorkerLike = Pick<\n Worker,\n `postMessage` | `terminate` | `addEventListener` | `removeEventListener`\n>\n\ntype PendingWorkerRequest = {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n}\n\ntype BrowserOPFSWorkerRequestWithoutId =\n BrowserOPFSWorkerRequest extends infer TRequest\n ? TRequest extends { requestId: string }\n ? Omit<TRequest, `requestId`>\n : never\n : never\n\nclass OPFSWorkerRequestError extends Error {\n constructor(\n readonly code: BrowserOPFSWorkerErrorCode,\n message: string,\n ) {\n super(message)\n this.name = `OPFSWorkerRequestError`\n }\n}\n\nfunction hasOPFSBrowserPrerequisites(globalObject: unknown): boolean {\n const candidate = globalObject as BrowserOPFSFeatureGlobal\n const getDirectory = candidate.navigator?.storage?.getDirectory\n const WorkerConstructor = candidate.Worker\n\n return (\n typeof getDirectory === `function` &&\n typeof WorkerConstructor === `function`\n )\n}\n\nfunction createWorkerError(\n code: BrowserOPFSWorkerErrorCode,\n message: string,\n): Error {\n if (code === `PERSISTENCE_UNAVAILABLE`) {\n return new PersistenceUnavailableError(message)\n }\n if (code === `INVALID_CONFIG`) {\n return new InvalidPersistedCollectionConfigError(message)\n }\n return new OPFSWorkerRequestError(code, message)\n}\n\nfunction createWorkerRequestIdFactory(): () => string {\n let sequence = 0\n const prefix = `opfs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`\n\n return () => {\n sequence++\n return `${prefix}-${sequence}`\n }\n}\n\nfunction createOPFSWorkerInstance(): BrowserOPFSWorkerLike {\n const WorkerConstructor = (\n globalThis as typeof globalThis & { Worker?: typeof Worker }\n ).Worker\n if (typeof WorkerConstructor !== `function`) {\n throw new PersistenceUnavailableError(\n `Web Worker support is required for browser OPFS persistence`,\n )\n }\n\n return new (\n OPFSWorkerConstructor as unknown as new () => BrowserOPFSWorkerLike\n )()\n}\n\nfunction getOpenAbortError(signal: AbortSignal): Error {\n const reason: unknown = signal.reason\n return reason instanceof Error\n ? reason\n : new DOMException(\n `Opening browser OPFS database was aborted`,\n `AbortError`,\n )\n}\n\n/**\n * Creates a browser wa-sqlite database handle backed by OPFS and\n * OPFSCoopSyncVFS.\n */\nexport async function openBrowserWASQLiteOPFSDatabase(\n options: OpenBrowserWASQLiteOPFSDatabaseOptions,\n): Promise<BrowserWASQLiteDatabase> {\n const databaseName = options.databaseName.trim()\n if (databaseName.length === 0) {\n throw new InvalidPersistedCollectionConfigError(\n `Browser wa-sqlite databaseName cannot be empty`,\n )\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_OPEN_TIMEOUT_MS\n if (\n !Number.isSafeInteger(timeoutMs) ||\n timeoutMs < 0 ||\n timeoutMs > MAX_OPEN_TIMEOUT_MS\n ) {\n throw new InvalidPersistedCollectionConfigError(\n `Browser wa-sqlite timeoutMs must be an integer between 0 and ${MAX_OPEN_TIMEOUT_MS}`,\n )\n }\n\n const signal = options.signal\n if (signal?.aborted) {\n throw getOpenAbortError(signal)\n }\n\n if (!hasOPFSBrowserPrerequisites(globalThis)) {\n throw new PersistenceUnavailableError(\n `Browser OPFS prerequisites are not available in this runtime`,\n )\n }\n\n const vfsName = options.vfsName ?? DEFAULT_VFS_NAME\n const worker = createOPFSWorkerInstance()\n const nextRequestId = createWorkerRequestIdFactory()\n const pendingRequests = new Map<string, PendingWorkerRequest>()\n let disposed = false\n\n const disposeWorker = (): void => {\n if (disposed) {\n return\n }\n disposed = true\n globalThis.removeEventListener(`pagehide`, onPageHide)\n worker.removeEventListener(`message`, onMessage)\n worker.removeEventListener(`error`, onError)\n worker.removeEventListener(`messageerror`, onMessageError)\n worker.terminate()\n }\n\n const rejectAllPendingRequests = (error: Error): void => {\n for (const pendingRequest of pendingRequests.values()) {\n pendingRequest.reject(error)\n }\n pendingRequests.clear()\n }\n\n const onMessage = (event: Event): void => {\n const messageEvent = event as MessageEvent<BrowserOPFSWorkerResponse>\n const response = messageEvent.data\n\n const pendingRequest = pendingRequests.get(response.requestId)\n if (!pendingRequest) {\n return\n }\n pendingRequests.delete(response.requestId)\n\n if (response.ok) {\n pendingRequest.resolve(response.rows ?? [])\n return\n }\n\n pendingRequest.reject(createWorkerError(response.code, response.error))\n }\n\n const onError = (): void => {\n rejectAllPendingRequests(\n new PersistenceUnavailableError(`OPFS worker terminated unexpectedly`),\n )\n disposeWorker()\n }\n\n const onMessageError = (): void => {\n rejectAllPendingRequests(\n new PersistenceUnavailableError(\n `OPFS worker message serialization failed`,\n ),\n )\n disposeWorker()\n }\n\n const onPageHide = (): void => {\n // A document entering the back/forward cache can be frozen before an\n // asynchronous close completes, including while initialization is pending.\n rejectAllPendingRequests(\n new DOMException(\n `The page is closing its SQLite connection`,\n `AbortError`,\n ),\n )\n disposeWorker()\n }\n\n worker.addEventListener(`message`, onMessage)\n worker.addEventListener(`error`, onError)\n worker.addEventListener(`messageerror`, onMessageError)\n globalThis.addEventListener(`pagehide`, onPageHide)\n\n const sendWorkerRequest = <T>(\n request: BrowserOPFSWorkerRequestWithoutId,\n ): Promise<T> => {\n if (disposed) {\n return Promise.reject(\n new InvalidPersistedCollectionConfigError(\n `Browser OPFS worker connection is closed`,\n ),\n )\n }\n\n const requestId = nextRequestId()\n return new Promise<T>((resolve, reject) => {\n pendingRequests.set(requestId, {\n resolve: (value) => {\n resolve(value as T)\n },\n reject,\n })\n try {\n const payload = {\n ...request,\n requestId,\n } as BrowserOPFSWorkerRequest\n worker.postMessage(payload)\n } catch (error) {\n pendingRequests.delete(requestId)\n reject(\n new PersistenceUnavailableError(\n `Failed to send message to OPFS worker: ${(error as Error).message}`,\n ),\n )\n }\n })\n }\n\n let openTimeout: ReturnType<typeof setTimeout> | undefined\n const onAbort = (): void => {\n if (!signal) return\n rejectAllPendingRequests(getOpenAbortError(signal))\n disposeWorker()\n }\n const onTimeout = (): void => {\n rejectAllPendingRequests(\n new DOMException(\n `Opening browser OPFS database timed out after ${timeoutMs} ms`,\n `TimeoutError`,\n ),\n )\n disposeWorker()\n }\n\n try {\n const initialization = sendWorkerRequest<void>({\n type: `init`,\n databaseName,\n vfsName,\n })\n signal?.addEventListener(`abort`, onAbort, { once: true })\n if (signal?.aborted) onAbort()\n if (timeoutMs > 0) {\n openTimeout = setTimeout(onTimeout, timeoutMs)\n }\n await initialization\n } catch (error) {\n disposeWorker()\n throw error\n } finally {\n if (openTimeout !== undefined) clearTimeout(openTimeout)\n signal?.removeEventListener(`abort`, onAbort)\n }\n\n return {\n execute: <TRow = unknown>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ) =>\n sendWorkerRequest<ReadonlyArray<TRow>>({\n type: `execute`,\n sql,\n params,\n }),\n close: async () => {\n if (disposed) {\n return\n }\n\n let closeError: unknown\n try {\n await sendWorkerRequest<void>({\n type: `close`,\n })\n } catch (error) {\n closeError = error\n } finally {\n disposeWorker()\n }\n\n if (closeError) {\n throw closeError\n }\n },\n }\n}\n"],"names":["OPFSWorkerConstructor"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/browser-db-sqlite-persistence",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "description": "Browser wa-sqlite persisted collection adapter for TanStack DB",
5
5
  "author": "TanStack Team",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "src"
42
42
  ],
43
43
  "dependencies": {
44
- "@tanstack/db-sqlite-persistence-core": "0.4.3"
44
+ "@tanstack/db-sqlite-persistence-core": "0.4.5"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@journeyapps/wa-sqlite": "^1.4.1",
@@ -54,7 +54,7 @@
54
54
  "@types/better-sqlite3": "^7.6.13",
55
55
  "@vitest/coverage-istanbul": "^3.2.4",
56
56
  "better-sqlite3": "^12.6.2",
57
- "@tanstack/query-db-collection": "1.3.4"
57
+ "@tanstack/query-db-collection": "1.4.0"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "vite build",
@@ -18,13 +18,10 @@ import type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'
18
18
  export type { BrowserWASQLiteDatabase } from './wa-sqlite-driver'
19
19
 
20
20
  type BrowserSQLiteCoreSchemaMismatchPolicy =
21
- | `sync-present-reset`
22
- | `sync-absent-error`
23
- | `reset`
21
+ `sync-present-reset` | `sync-absent-error` | `reset`
24
22
 
25
23
  export type BrowserWASQLiteSchemaMismatchPolicy =
26
- | BrowserSQLiteCoreSchemaMismatchPolicy
27
- | `throw`
24
+ BrowserSQLiteCoreSchemaMismatchPolicy | `throw`
28
25
 
29
26
  export type BrowserWASQLitePersistenceOptions = Omit<
30
27
  SQLiteCoreAdapterOptions,
@@ -102,7 +102,9 @@ function createOPFSWorkerInstance(): BrowserOPFSWorkerLike {
102
102
  )
103
103
  }
104
104
 
105
- return new (OPFSWorkerConstructor as unknown as new () => BrowserOPFSWorkerLike)()
105
+ return new (
106
+ OPFSWorkerConstructor as unknown as new () => BrowserOPFSWorkerLike
107
+ )()
106
108
  }
107
109
 
108
110
  function getOpenAbortError(signal: AbortSignal): Error {
@@ -1,7 +1,5 @@
1
1
  export type BrowserOPFSWorkerErrorCode =
2
- | `INVALID_CONFIG`
3
- | `PERSISTENCE_UNAVAILABLE`
4
- | `INTERNAL`
2
+ `INVALID_CONFIG` | `PERSISTENCE_UNAVAILABLE` | `INTERNAL`
5
3
 
6
4
  export type BrowserOPFSWorkerInitRequest = {
7
5
  type: `init`
@@ -43,5 +41,4 @@ export type BrowserOPFSWorkerErrorResponse = {
43
41
  }
44
42
 
45
43
  export type BrowserOPFSWorkerResponse =
46
- | BrowserOPFSWorkerSuccessResponse
47
- | BrowserOPFSWorkerErrorResponse
44
+ BrowserOPFSWorkerSuccessResponse | BrowserOPFSWorkerErrorResponse
@@ -23,12 +23,7 @@ const SQLITE_ROW = 100
23
23
  const SQLITE_DONE = 101
24
24
 
25
25
  type SQLiteCompatibleBinding =
26
- | null
27
- | string
28
- | number
29
- | bigint
30
- | Uint8Array
31
- | Array<number>
26
+ null | string | number | bigint | Uint8Array | Array<number>
32
27
 
33
28
  type WorkerOPFSGlobal = {
34
29
  navigator?: {