@tanstack/browser-db-sqlite-persistence 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +54 -0
  2. package/dist/assets/opfs-worker-CCciqEMo.js +5879 -0
  3. package/dist/assets/opfs-worker-CCciqEMo.js.map +1 -0
  4. package/dist/cjs/browser-coordinator.cjs +541 -0
  5. package/dist/cjs/browser-coordinator.cjs.map +1 -0
  6. package/dist/cjs/browser-coordinator.d.cts +67 -0
  7. package/dist/cjs/browser-persistence.cjs +83 -0
  8. package/dist/cjs/browser-persistence.cjs.map +1 -0
  9. package/dist/cjs/browser-persistence.d.cts +16 -0
  10. package/dist/cjs/index.cjs +14 -0
  11. package/dist/cjs/index.cjs.map +1 -0
  12. package/dist/cjs/index.d.cts +8 -0
  13. package/dist/cjs/opfs-database.cjs +175 -0
  14. package/dist/cjs/opfs-database.cjs.map +1 -0
  15. package/dist/cjs/opfs-database.d.cts +10 -0
  16. package/dist/cjs/opfs-worker-protocol.d.cts +32 -0
  17. package/dist/cjs/opfs-worker.cjs +11 -0
  18. package/dist/cjs/opfs-worker.cjs.map +1 -0
  19. package/dist/cjs/opfs-worker.d.cts +1 -0
  20. package/dist/cjs/wa-sqlite-driver.cjs +106 -0
  21. package/dist/cjs/wa-sqlite-driver.cjs.map +1 -0
  22. package/dist/cjs/wa-sqlite-driver.d.cts +25 -0
  23. package/dist/esm/browser-coordinator.d.ts +67 -0
  24. package/dist/esm/browser-coordinator.js +541 -0
  25. package/dist/esm/browser-coordinator.js.map +1 -0
  26. package/dist/esm/browser-persistence.d.ts +16 -0
  27. package/dist/esm/browser-persistence.js +83 -0
  28. package/dist/esm/browser-persistence.js.map +1 -0
  29. package/dist/esm/index.d.ts +8 -0
  30. package/dist/esm/index.js +11 -0
  31. package/dist/esm/index.js.map +1 -0
  32. package/dist/esm/opfs-database.d.ts +10 -0
  33. package/dist/esm/opfs-database.js +175 -0
  34. package/dist/esm/opfs-database.js.map +1 -0
  35. package/dist/esm/opfs-worker-protocol.d.ts +32 -0
  36. package/dist/esm/opfs-worker.d.ts +1 -0
  37. package/dist/esm/opfs-worker.js +12 -0
  38. package/dist/esm/opfs-worker.js.map +1 -0
  39. package/dist/esm/wa-sqlite-driver.d.ts +25 -0
  40. package/dist/esm/wa-sqlite-driver.js +106 -0
  41. package/dist/esm/wa-sqlite-driver.js.map +1 -0
  42. package/package.json +63 -0
  43. package/src/browser-coordinator.ts +842 -0
  44. package/src/browser-persistence.ts +172 -0
  45. package/src/index.ts +15 -0
  46. package/src/opfs-database.ts +258 -0
  47. package/src/opfs-worker-protocol.ts +47 -0
  48. package/src/opfs-worker.ts +305 -0
  49. package/src/vite-env.d.ts +1 -0
  50. package/src/wa-sqlite-driver.ts +153 -0
  51. package/src/wa-sqlite-examples.d.ts +10 -0
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
4
+ const browserCoordinator = require("./browser-coordinator.cjs");
5
+ const waSqliteDriver = require("./wa-sqlite-driver.cjs");
6
+ function normalizeSchemaMismatchPolicy(policy) {
7
+ if (policy === `throw`) {
8
+ return `sync-absent-error`;
9
+ }
10
+ return policy;
11
+ }
12
+ function resolveSchemaMismatchPolicy(explicitPolicy, mode) {
13
+ if (explicitPolicy) {
14
+ return normalizeSchemaMismatchPolicy(explicitPolicy);
15
+ }
16
+ return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`;
17
+ }
18
+ function createAdapterCacheKey(schemaMismatchPolicy, schemaVersion) {
19
+ const schemaVersionKey = schemaVersion === void 0 ? `schema:default` : `schema:${schemaVersion}`;
20
+ return `${schemaMismatchPolicy}|${schemaVersionKey}`;
21
+ }
22
+ function createInternalSQLiteDriver(options) {
23
+ return new waSqliteDriver.BrowserWASQLiteDriver({
24
+ database: options.database
25
+ });
26
+ }
27
+ function resolveAdapterBaseOptions(options) {
28
+ return {
29
+ appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,
30
+ appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,
31
+ pullSinceReloadThreshold: options.pullSinceReloadThreshold
32
+ };
33
+ }
34
+ function createBrowserWASQLitePersistence(options) {
35
+ const { coordinator, schemaMismatchPolicy } = options;
36
+ const driver = createInternalSQLiteDriver(options);
37
+ const adapterBaseOptions = resolveAdapterBaseOptions(options);
38
+ const resolvedCoordinator = coordinator ?? new dbSqlitePersistenceCore.SingleProcessCoordinator();
39
+ const adapterCache = /* @__PURE__ */ new Map();
40
+ const getAdapterForCollection = (mode, schemaVersion) => {
41
+ const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(
42
+ schemaMismatchPolicy,
43
+ mode
44
+ );
45
+ const cacheKey = createAdapterCacheKey(
46
+ resolvedSchemaMismatchPolicy,
47
+ schemaVersion
48
+ );
49
+ const cachedAdapter = adapterCache.get(cacheKey);
50
+ if (cachedAdapter) {
51
+ return cachedAdapter;
52
+ }
53
+ const adapter = dbSqlitePersistenceCore.createSQLiteCorePersistenceAdapter({
54
+ ...adapterBaseOptions,
55
+ driver,
56
+ schemaMismatchPolicy: resolvedSchemaMismatchPolicy,
57
+ ...schemaVersion === void 0 ? {} : { schemaVersion }
58
+ });
59
+ adapterCache.set(cacheKey, adapter);
60
+ if (resolvedCoordinator instanceof browserCoordinator.BrowserCollectionCoordinator) {
61
+ resolvedCoordinator.setAdapter(adapter);
62
+ }
63
+ return adapter;
64
+ };
65
+ const createCollectionPersistence = (mode, schemaVersion) => ({
66
+ adapter: getAdapterForCollection(
67
+ mode,
68
+ schemaVersion
69
+ ),
70
+ coordinator: resolvedCoordinator
71
+ });
72
+ const defaultPersistence = createCollectionPersistence(
73
+ `sync-absent`,
74
+ void 0
75
+ );
76
+ return {
77
+ ...defaultPersistence,
78
+ resolvePersistenceForCollection: ({ mode, schemaVersion }) => createCollectionPersistence(mode, schemaVersion),
79
+ resolvePersistenceForMode: (mode) => createCollectionPersistence(mode, void 0)
80
+ };
81
+ }
82
+ exports.createBrowserWASQLitePersistence = createBrowserWASQLitePersistence;
83
+ //# sourceMappingURL=browser-persistence.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-persistence.cjs","sources":["../../src/browser-persistence.ts"],"sourcesContent":["import {\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: options.appliedTxPruneMaxRows,\n appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,\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 T extends object,\n TKey extends string | number = string | number,\n>(\n options: BrowserWASQLitePersistenceOptions,\n): PersistedCollectionPersistence<T, TKey> {\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<\n typeof createSQLiteCorePersistenceAdapter<\n Record<string, unknown>,\n string | number\n >\n >\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 Record<string, unknown>,\n string | number\n >({\n ...adapterBaseOptions,\n driver,\n schemaMismatchPolicy: resolvedSchemaMismatchPolicy,\n ...(schemaVersion === undefined ? {} : { schemaVersion }),\n })\n adapterCache.set(cacheKey, adapter)\n\n // Wire the adapter into the multi-tab coordinator so it can handle\n // leader-side RPCs (applyCommittedTx, pullSince, ensureIndex, etc.)\n if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {\n resolvedCoordinator.setAdapter(adapter)\n }\n\n return adapter\n }\n\n const createCollectionPersistence = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence<T, TKey> => ({\n adapter: getAdapterForCollection(\n mode,\n schemaVersion,\n ) as unknown as PersistedCollectionPersistence<T, TKey>[`adapter`],\n coordinator: resolvedCoordinator,\n })\n\n const defaultPersistence = createCollectionPersistence(\n `sync-absent`,\n undefined,\n )\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ mode, schemaVersion }) =>\n createCollectionPersistence(mode, schemaVersion),\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(mode, undefined),\n }\n}\n"],"names":["BrowserWASQLiteDriver","SingleProcessCoordinator","createSQLiteCorePersistenceAdapter","BrowserCollectionCoordinator"],"mappings":";;;;;AAmCA,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,uBAAuB,QAAQ;AAAA,IAC/B,6BAA6B,QAAQ;AAAA,IACrC,0BAA0B,QAAQ;AAAA,EAAA;AAEtC;AAOO,SAAS,iCAId,SACyC;AACzC,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;AAUzB,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,mCAGd;AAAA,MACA,GAAG;AAAA,MACH;AAAA,MACA,sBAAsB;AAAA,MACtB,GAAI,kBAAkB,SAAY,CAAA,IAAK,EAAE,cAAA;AAAA,IAAc,CACxD;AACD,iBAAa,IAAI,UAAU,OAAO;AAIlC,QAAI,+BAA+BC,mBAAAA,8BAA8B;AAC/D,0BAAoB,WAAW,OAAO;AAAA,IACxC;AAEA,WAAO;AAAA,EACT;AAEA,QAAM,8BAA8B,CAClC,MACA,mBAC6C;AAAA,IAC7C,SAAS;AAAA,MACP;AAAA,MACA;AAAA,IAAA;AAAA,IAEF,aAAa;AAAA,EAAA;AAGf,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,EAAA;AAGF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,iCAAiC,CAAC,EAAE,MAAM,oBACxC,4BAA4B,MAAM,aAAa;AAAA,IACjD,2BAA2B,CAAC,SAC1B,4BAA4B,MAAM,MAAS;AAAA,EAAA;AAEjD;;"}
@@ -0,0 +1,16 @@
1
+ import { PersistedCollectionCoordinator, PersistedCollectionPersistence, SQLiteCoreAdapterOptions } from '@tanstack/db-sqlite-persistence-core';
2
+ import { BrowserWASQLiteDatabase } from './wa-sqlite-driver.cjs';
3
+ export type { BrowserWASQLiteDatabase } from './wa-sqlite-driver.cjs';
4
+ type BrowserSQLiteCoreSchemaMismatchPolicy = `sync-present-reset` | `sync-absent-error` | `reset`;
5
+ export type BrowserWASQLiteSchemaMismatchPolicy = BrowserSQLiteCoreSchemaMismatchPolicy | `throw`;
6
+ export type BrowserWASQLitePersistenceOptions = Omit<SQLiteCoreAdapterOptions, `driver` | `schemaVersion` | `schemaMismatchPolicy`> & {
7
+ database: BrowserWASQLiteDatabase;
8
+ coordinator?: PersistedCollectionCoordinator;
9
+ schemaMismatchPolicy?: BrowserWASQLiteSchemaMismatchPolicy;
10
+ };
11
+ /**
12
+ * Creates a shared browser wa-sqlite persistence instance that can be reused by
13
+ * many collections on the same database. This is single-tab wiring using
14
+ * SingleProcessCoordinator semantics (no election required).
15
+ */
16
+ export declare function createBrowserWASQLitePersistence<T extends object, TKey extends string | number = string | number>(options: BrowserWASQLitePersistenceOptions): PersistedCollectionPersistence<T, TKey>;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const browserPersistence = require("./browser-persistence.cjs");
4
+ const opfsDatabase = require("./opfs-database.cjs");
5
+ const browserCoordinator = require("./browser-coordinator.cjs");
6
+ const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
7
+ exports.createBrowserWASQLitePersistence = browserPersistence.createBrowserWASQLitePersistence;
8
+ exports.openBrowserWASQLiteOPFSDatabase = opfsDatabase.openBrowserWASQLiteOPFSDatabase;
9
+ exports.BrowserCollectionCoordinator = browserCoordinator.BrowserCollectionCoordinator;
10
+ Object.defineProperty(exports, "persistedCollectionOptions", {
11
+ enumerable: true,
12
+ get: () => dbSqlitePersistenceCore.persistedCollectionOptions
13
+ });
14
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
@@ -0,0 +1,8 @@
1
+ export { createBrowserWASQLitePersistence } from './browser-persistence.cjs';
2
+ export { openBrowserWASQLiteOPFSDatabase } from './opfs-database.cjs';
3
+ export { BrowserCollectionCoordinator } from './browser-coordinator.cjs';
4
+ export type { BrowserWASQLiteDatabase, BrowserWASQLitePersistenceOptions, BrowserWASQLiteSchemaMismatchPolicy, } from './browser-persistence.cjs';
5
+ export type { OpenBrowserWASQLiteOPFSDatabaseOptions } from './opfs-database.cjs';
6
+ export type { BrowserCollectionCoordinatorOptions } from './browser-coordinator.cjs';
7
+ export { persistedCollectionOptions } from '@tanstack/db-sqlite-persistence-core';
8
+ export type { PersistedCollectionCoordinator, PersistedCollectionPersistence, } from '@tanstack/db-sqlite-persistence-core';
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
4
+ const opfsWorker = require("./opfs-worker.cjs");
5
+ const DEFAULT_VFS_NAME = `opfs`;
6
+ class OPFSWorkerRequestError extends Error {
7
+ constructor(code, message) {
8
+ super(message);
9
+ this.code = code;
10
+ this.name = `OPFSWorkerRequestError`;
11
+ }
12
+ }
13
+ function hasOPFSBrowserPrerequisites(globalObject) {
14
+ const candidate = globalObject;
15
+ const getDirectory = candidate.navigator?.storage?.getDirectory;
16
+ const WorkerConstructor = candidate.Worker;
17
+ return typeof getDirectory === `function` && typeof WorkerConstructor === `function`;
18
+ }
19
+ function createWorkerError(code, message) {
20
+ if (code === `PERSISTENCE_UNAVAILABLE`) {
21
+ return new dbSqlitePersistenceCore.PersistenceUnavailableError(message);
22
+ }
23
+ if (code === `INVALID_CONFIG`) {
24
+ return new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(message);
25
+ }
26
+ return new OPFSWorkerRequestError(code, message);
27
+ }
28
+ function createWorkerRequestIdFactory() {
29
+ let sequence = 0;
30
+ const prefix = `opfs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
31
+ return () => {
32
+ sequence++;
33
+ return `${prefix}-${sequence}`;
34
+ };
35
+ }
36
+ function createOPFSWorkerInstance() {
37
+ const WorkerConstructor = globalThis.Worker;
38
+ if (typeof WorkerConstructor !== `function`) {
39
+ throw new dbSqlitePersistenceCore.PersistenceUnavailableError(
40
+ `Web Worker support is required for browser OPFS persistence`
41
+ );
42
+ }
43
+ return new opfsWorker();
44
+ }
45
+ async function openBrowserWASQLiteOPFSDatabase(options) {
46
+ const databaseName = options.databaseName.trim();
47
+ if (databaseName.length === 0) {
48
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
49
+ `Browser wa-sqlite databaseName cannot be empty`
50
+ );
51
+ }
52
+ if (!hasOPFSBrowserPrerequisites(globalThis)) {
53
+ throw new dbSqlitePersistenceCore.PersistenceUnavailableError(
54
+ `Browser OPFS prerequisites are not available in this runtime`
55
+ );
56
+ }
57
+ const vfsName = options.vfsName ?? DEFAULT_VFS_NAME;
58
+ const worker = createOPFSWorkerInstance();
59
+ const nextRequestId = createWorkerRequestIdFactory();
60
+ const pendingRequests = /* @__PURE__ */ new Map();
61
+ let disposed = false;
62
+ const disposeWorker = () => {
63
+ if (disposed) {
64
+ return;
65
+ }
66
+ disposed = true;
67
+ worker.removeEventListener(`message`, onMessage);
68
+ worker.removeEventListener(`error`, onError);
69
+ worker.removeEventListener(`messageerror`, onMessageError);
70
+ worker.terminate();
71
+ };
72
+ const rejectAllPendingRequests = (error) => {
73
+ for (const pendingRequest of pendingRequests.values()) {
74
+ pendingRequest.reject(error);
75
+ }
76
+ pendingRequests.clear();
77
+ };
78
+ const onMessage = (event) => {
79
+ const messageEvent = event;
80
+ const response = messageEvent.data;
81
+ const pendingRequest = pendingRequests.get(response.requestId);
82
+ if (!pendingRequest) {
83
+ return;
84
+ }
85
+ pendingRequests.delete(response.requestId);
86
+ if (response.ok) {
87
+ pendingRequest.resolve(response.rows ?? []);
88
+ return;
89
+ }
90
+ pendingRequest.reject(createWorkerError(response.code, response.error));
91
+ };
92
+ const onError = () => {
93
+ rejectAllPendingRequests(
94
+ new dbSqlitePersistenceCore.PersistenceUnavailableError(`OPFS worker terminated unexpectedly`)
95
+ );
96
+ disposeWorker();
97
+ };
98
+ const onMessageError = () => {
99
+ rejectAllPendingRequests(
100
+ new dbSqlitePersistenceCore.PersistenceUnavailableError(
101
+ `OPFS worker message serialization failed`
102
+ )
103
+ );
104
+ disposeWorker();
105
+ };
106
+ worker.addEventListener(`message`, onMessage);
107
+ worker.addEventListener(`error`, onError);
108
+ worker.addEventListener(`messageerror`, onMessageError);
109
+ const sendWorkerRequest = (request) => {
110
+ if (disposed) {
111
+ return Promise.reject(
112
+ new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
113
+ `Browser OPFS worker connection is closed`
114
+ )
115
+ );
116
+ }
117
+ const requestId = nextRequestId();
118
+ return new Promise((resolve, reject) => {
119
+ pendingRequests.set(requestId, {
120
+ resolve: (value) => {
121
+ resolve(value);
122
+ },
123
+ reject
124
+ });
125
+ try {
126
+ const payload = {
127
+ ...request,
128
+ requestId
129
+ };
130
+ worker.postMessage(payload);
131
+ } catch (error) {
132
+ pendingRequests.delete(requestId);
133
+ reject(
134
+ new dbSqlitePersistenceCore.PersistenceUnavailableError(
135
+ `Failed to send message to OPFS worker: ${error.message}`
136
+ )
137
+ );
138
+ }
139
+ });
140
+ };
141
+ try {
142
+ await sendWorkerRequest({
143
+ type: `init`,
144
+ databaseName,
145
+ vfsName
146
+ });
147
+ } catch (error) {
148
+ disposeWorker();
149
+ throw error;
150
+ }
151
+ return {
152
+ execute: (sql, params = []) => sendWorkerRequest({
153
+ type: `execute`,
154
+ sql,
155
+ params
156
+ }),
157
+ close: async () => {
158
+ let closeError;
159
+ try {
160
+ await sendWorkerRequest({
161
+ type: `close`
162
+ });
163
+ } catch (error) {
164
+ closeError = error;
165
+ } finally {
166
+ disposeWorker();
167
+ }
168
+ if (closeError) {
169
+ throw closeError;
170
+ }
171
+ }
172
+ };
173
+ }
174
+ exports.openBrowserWASQLiteOPFSDatabase = openBrowserWASQLiteOPFSDatabase;
175
+ //# sourceMappingURL=opfs-database.cjs.map
@@ -0,0 +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`\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}\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\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 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 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 worker.addEventListener(`message`, onMessage)\n worker.addEventListener(`error`, onError)\n worker.addEventListener(`messageerror`, onMessageError)\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 try {\n await sendWorkerRequest<void>({\n type: `init`,\n databaseName,\n vfsName,\n })\n } catch (error) {\n disposeWorker()\n throw error\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 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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"}
@@ -0,0 +1,10 @@
1
+ import { BrowserWASQLiteDatabase } from './wa-sqlite-driver.cjs';
2
+ export type OpenBrowserWASQLiteOPFSDatabaseOptions = {
3
+ databaseName: string;
4
+ vfsName?: string;
5
+ };
6
+ /**
7
+ * Creates a browser wa-sqlite database handle backed by OPFS and
8
+ * OPFSCoopSyncVFS.
9
+ */
10
+ export declare function openBrowserWASQLiteOPFSDatabase(options: OpenBrowserWASQLiteOPFSDatabaseOptions): Promise<BrowserWASQLiteDatabase>;
@@ -0,0 +1,32 @@
1
+ export type BrowserOPFSWorkerErrorCode = `INVALID_CONFIG` | `PERSISTENCE_UNAVAILABLE` | `INTERNAL`;
2
+ export type BrowserOPFSWorkerInitRequest = {
3
+ type: `init`;
4
+ requestId: string;
5
+ databaseName: string;
6
+ vfsName: string;
7
+ };
8
+ export type BrowserOPFSWorkerExecuteRequest = {
9
+ type: `execute`;
10
+ requestId: string;
11
+ sql: string;
12
+ params: ReadonlyArray<unknown>;
13
+ };
14
+ export type BrowserOPFSWorkerCloseRequest = {
15
+ type: `close`;
16
+ requestId: string;
17
+ };
18
+ export type BrowserOPFSWorkerRequest = BrowserOPFSWorkerInitRequest | BrowserOPFSWorkerExecuteRequest | BrowserOPFSWorkerCloseRequest;
19
+ export type BrowserOPFSWorkerSuccessResponse = {
20
+ type: `response`;
21
+ requestId: string;
22
+ ok: true;
23
+ rows?: ReadonlyArray<Record<string, unknown>>;
24
+ };
25
+ export type BrowserOPFSWorkerErrorResponse = {
26
+ type: `response`;
27
+ requestId: string;
28
+ ok: false;
29
+ code: BrowserOPFSWorkerErrorCode;
30
+ error: string;
31
+ };
32
+ export type BrowserOPFSWorkerResponse = BrowserOPFSWorkerSuccessResponse | BrowserOPFSWorkerErrorResponse;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ function WorkerWrapper(options) {
3
+ return new Worker(
4
+ "/assets/opfs-worker-CCciqEMo.js",
5
+ {
6
+ name: options?.name
7
+ }
8
+ );
9
+ }
10
+ module.exports = WorkerWrapper;
11
+ //# sourceMappingURL=opfs-worker.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opfs-worker.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
4
+ function assertTransactionCallbackHasDriverArg(fn) {
5
+ if (fn.length > 0) {
6
+ return;
7
+ }
8
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
9
+ `SQLiteDriver.transaction callback must accept the transaction driver argument`
10
+ );
11
+ }
12
+ function assertDatabaseShape(database) {
13
+ if (typeof database.execute !== `function`) {
14
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
15
+ `Browser wa-sqlite database handle must provide execute(sql, params?)`
16
+ );
17
+ }
18
+ }
19
+ class BrowserWASQLiteDriver {
20
+ constructor(options) {
21
+ this.queue = Promise.resolve();
22
+ this.nextSavepointId = 1;
23
+ this.closed = false;
24
+ assertDatabaseShape(options.database);
25
+ this.database = options.database;
26
+ }
27
+ async exec(sql) {
28
+ await this.enqueue(async () => {
29
+ await this.database.execute(sql);
30
+ });
31
+ }
32
+ async query(sql, params = []) {
33
+ return this.enqueue(() => this.database.execute(sql, params));
34
+ }
35
+ async run(sql, params = []) {
36
+ await this.enqueue(async () => {
37
+ await this.database.execute(sql, params);
38
+ });
39
+ }
40
+ async transaction(fn) {
41
+ assertTransactionCallbackHasDriverArg(fn);
42
+ return this.enqueue(async () => {
43
+ await this.database.execute(`BEGIN IMMEDIATE`);
44
+ try {
45
+ const result = await fn(this.createTransactionDriver());
46
+ await this.database.execute(`COMMIT`);
47
+ return result;
48
+ } catch (error) {
49
+ try {
50
+ await this.database.execute(`ROLLBACK`);
51
+ } catch {
52
+ }
53
+ throw error;
54
+ }
55
+ });
56
+ }
57
+ async transactionWithDriver(fn) {
58
+ return this.transaction(fn);
59
+ }
60
+ async close() {
61
+ if (this.closed) {
62
+ return;
63
+ }
64
+ this.closed = true;
65
+ if (typeof this.database.close === `function`) {
66
+ await Promise.resolve(this.database.close());
67
+ }
68
+ }
69
+ getDatabase() {
70
+ return this.database;
71
+ }
72
+ createTransactionDriver() {
73
+ return {
74
+ exec: (sql) => this.database.execute(sql).then(() => void 0),
75
+ query: (sql, params = []) => this.database.execute(sql, params),
76
+ run: (sql, params = []) => this.database.execute(sql, params).then(() => void 0),
77
+ transaction: (fn) => this.runNestedTransaction(fn),
78
+ transactionWithDriver: (fn) => this.runNestedTransaction(fn)
79
+ };
80
+ }
81
+ async runNestedTransaction(fn) {
82
+ assertTransactionCallbackHasDriverArg(fn);
83
+ const savepointName = `tsdb_sp_${this.nextSavepointId}`;
84
+ this.nextSavepointId++;
85
+ await this.database.execute(`SAVEPOINT ${savepointName}`);
86
+ try {
87
+ const result = await fn(this.createTransactionDriver());
88
+ await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`);
89
+ return result;
90
+ } catch (error) {
91
+ await this.database.execute(`ROLLBACK TO SAVEPOINT ${savepointName}`);
92
+ await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`);
93
+ throw error;
94
+ }
95
+ }
96
+ enqueue(operation) {
97
+ const queuedOperation = this.queue.then(operation, operation);
98
+ this.queue = queuedOperation.then(
99
+ () => void 0,
100
+ () => void 0
101
+ );
102
+ return queuedOperation;
103
+ }
104
+ }
105
+ exports.BrowserWASQLiteDriver = BrowserWASQLiteDriver;
106
+ //# sourceMappingURL=wa-sqlite-driver.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wa-sqlite-driver.cjs","sources":["../../src/wa-sqlite-driver.ts"],"sourcesContent":["import { InvalidPersistedCollectionConfigError } from '@tanstack/db-sqlite-persistence-core'\nimport type { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core'\n\nexport type BrowserWASQLiteDatabase = {\n execute: <TRow = unknown>(\n sql: string,\n params?: ReadonlyArray<unknown>,\n ) => Promise<ReadonlyArray<TRow>>\n close?: () => Promise<void> | void\n}\n\nexport type BrowserWASQLiteDriverOptions = {\n database: BrowserWASQLiteDatabase\n}\n\nfunction assertTransactionCallbackHasDriverArg(\n fn: (transactionDriver: SQLiteDriver) => Promise<unknown>,\n): void {\n if (fn.length > 0) {\n return\n }\n\n throw new InvalidPersistedCollectionConfigError(\n `SQLiteDriver.transaction callback must accept the transaction driver argument`,\n )\n}\n\nfunction assertDatabaseShape(\n database: BrowserWASQLiteDatabase,\n): asserts database is BrowserWASQLiteDatabase {\n if (typeof database.execute !== `function`) {\n throw new InvalidPersistedCollectionConfigError(\n `Browser wa-sqlite database handle must provide execute(sql, params?)`,\n )\n }\n}\n\nexport class BrowserWASQLiteDriver implements SQLiteDriver {\n private readonly database: BrowserWASQLiteDatabase\n private queue: Promise<void> = Promise.resolve()\n private nextSavepointId = 1\n private closed = false\n\n constructor(options: BrowserWASQLiteDriverOptions) {\n assertDatabaseShape(options.database)\n this.database = options.database\n }\n\n async exec(sql: string): Promise<void> {\n await this.enqueue(async () => {\n await this.database.execute(sql)\n })\n }\n\n async query<T>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<ReadonlyArray<T>> {\n return this.enqueue(() => this.database.execute<T>(sql, params))\n }\n\n async run(sql: string, params: ReadonlyArray<unknown> = []): Promise<void> {\n await this.enqueue(async () => {\n await this.database.execute(sql, params)\n })\n }\n\n async transaction<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n assertTransactionCallbackHasDriverArg(fn)\n\n return this.enqueue(async () => {\n await this.database.execute(`BEGIN IMMEDIATE`)\n try {\n const result = await fn(this.createTransactionDriver())\n await this.database.execute(`COMMIT`)\n return result\n } catch (error) {\n try {\n await this.database.execute(`ROLLBACK`)\n } catch {\n // Preserve original transaction error.\n }\n throw error\n }\n })\n }\n\n async transactionWithDriver<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n return this.transaction(fn)\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return\n }\n this.closed = true\n\n if (typeof this.database.close === `function`) {\n await Promise.resolve(this.database.close())\n }\n }\n\n getDatabase(): BrowserWASQLiteDatabase {\n return this.database\n }\n\n private createTransactionDriver(): SQLiteDriver {\n return {\n exec: (sql) => this.database.execute(sql).then(() => undefined),\n query: <T>(sql: string, params: ReadonlyArray<unknown> = []) =>\n this.database.execute<T>(sql, params),\n run: (sql: string, params: ReadonlyArray<unknown> = []) =>\n this.database.execute(sql, params).then(() => undefined),\n transaction: <T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>) =>\n this.runNestedTransaction(fn),\n transactionWithDriver: <T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ) => this.runNestedTransaction(fn),\n }\n }\n\n private async runNestedTransaction<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n assertTransactionCallbackHasDriverArg(fn)\n\n const savepointName = `tsdb_sp_${this.nextSavepointId}`\n this.nextSavepointId++\n await this.database.execute(`SAVEPOINT ${savepointName}`)\n try {\n const result = await fn(this.createTransactionDriver())\n await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`)\n return result\n } catch (error) {\n await this.database.execute(`ROLLBACK TO SAVEPOINT ${savepointName}`)\n await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`)\n throw error\n }\n }\n\n private enqueue<T>(operation: () => Promise<T> | T): Promise<T> {\n const queuedOperation = this.queue.then(operation, operation)\n this.queue = queuedOperation.then(\n () => undefined,\n () => undefined,\n )\n return queuedOperation\n }\n}\n"],"names":["InvalidPersistedCollectionConfigError"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core';
2
+ export type BrowserWASQLiteDatabase = {
3
+ execute: <TRow = unknown>(sql: string, params?: ReadonlyArray<unknown>) => Promise<ReadonlyArray<TRow>>;
4
+ close?: () => Promise<void> | void;
5
+ };
6
+ export type BrowserWASQLiteDriverOptions = {
7
+ database: BrowserWASQLiteDatabase;
8
+ };
9
+ export declare class BrowserWASQLiteDriver implements SQLiteDriver {
10
+ private readonly database;
11
+ private queue;
12
+ private nextSavepointId;
13
+ private closed;
14
+ constructor(options: BrowserWASQLiteDriverOptions);
15
+ exec(sql: string): Promise<void>;
16
+ query<T>(sql: string, params?: ReadonlyArray<unknown>): Promise<ReadonlyArray<T>>;
17
+ run(sql: string, params?: ReadonlyArray<unknown>): Promise<void>;
18
+ transaction<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T>;
19
+ transactionWithDriver<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T>;
20
+ close(): Promise<void>;
21
+ getDatabase(): BrowserWASQLiteDatabase;
22
+ private createTransactionDriver;
23
+ private runNestedTransaction;
24
+ private enqueue;
25
+ }
@@ -0,0 +1,67 @@
1
+ import { ApplyLocalMutationsResponse, PersistedCollectionCoordinator, PersistedIndexSpec, PersistedMutationEnvelope, PersistenceAdapter, ProtocolEnvelope, PullSinceResponse } from '@tanstack/db-sqlite-persistence-core';
2
+ import { LoadSubsetOptions } from '@tanstack/db';
3
+ type AdapterWithPullSince = PersistenceAdapter<Record<string, unknown>, string | number> & {
4
+ pullSince?: (collectionId: string, fromRowVersion: number) => Promise<{
5
+ latestRowVersion: number;
6
+ requiresFullReload: true;
7
+ } | {
8
+ latestRowVersion: number;
9
+ requiresFullReload: false;
10
+ changedKeys: Array<string | number>;
11
+ deletedKeys: Array<string | number>;
12
+ }>;
13
+ getStreamPosition?: (collectionId: string) => Promise<{
14
+ latestTerm: number;
15
+ latestSeq: number;
16
+ latestRowVersion: number;
17
+ }>;
18
+ };
19
+ export type BrowserCollectionCoordinatorOptions = {
20
+ dbName: string;
21
+ adapter?: AdapterWithPullSince;
22
+ };
23
+ export declare class BrowserCollectionCoordinator implements PersistedCollectionCoordinator {
24
+ private readonly nodeId;
25
+ private readonly dbName;
26
+ private adapter;
27
+ private readonly channel;
28
+ private readonly collections;
29
+ private readonly pendingRPCs;
30
+ private readonly appliedEnvelopeIds;
31
+ private disposed;
32
+ /** Method indirection to prevent TypeScript from narrowing `disposed` across awaits */
33
+ private isDisposed;
34
+ private requireAdapter;
35
+ constructor(options: BrowserCollectionCoordinatorOptions);
36
+ /**
37
+ * Set or replace the persistence adapter used for leader-side RPC handling.
38
+ * Called by `createBrowserWASQLitePersistence` to wire the internally-created
39
+ * adapter into the coordinator.
40
+ */
41
+ setAdapter(adapter: AdapterWithPullSince): void;
42
+ getNodeId(): string;
43
+ subscribe(collectionId: string, onMessage: (message: ProtocolEnvelope<unknown>) => void): () => void;
44
+ publish(_collectionId: string, message: ProtocolEnvelope<unknown>): void;
45
+ isLeader(collectionId: string): boolean;
46
+ ensureLeadership(collectionId: string): Promise<void>;
47
+ requestEnsureRemoteSubset(collectionId: string, options: LoadSubsetOptions): Promise<void>;
48
+ requestEnsurePersistedIndex(collectionId: string, signature: string, spec: PersistedIndexSpec): Promise<void>;
49
+ requestApplyLocalMutations(collectionId: string, mutations: Array<PersistedMutationEnvelope>): Promise<ApplyLocalMutationsResponse>;
50
+ pullSince(collectionId: string, fromRowVersion: number): Promise<PullSinceResponse>;
51
+ dispose(): void;
52
+ private ensureCollectionState;
53
+ private acquireLeadership;
54
+ private releaseLeadership;
55
+ private emitHeartbeat;
56
+ private onChannelMessage;
57
+ private sendRPC;
58
+ private sendRPCOnce;
59
+ private handleRPCRequest;
60
+ private handleEnsureRemoteSubset;
61
+ private handleEnsurePersistedIndex;
62
+ private handleApplyLocalMutations;
63
+ private handlePullSince;
64
+ private withWriterLock;
65
+ private pruneAppliedEnvelopeIds;
66
+ }
67
+ export {};