@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
+ import { SingleProcessCoordinator, createSQLiteCorePersistenceAdapter } from "@tanstack/db-sqlite-persistence-core";
2
+ import { BrowserCollectionCoordinator } from "./browser-coordinator.js";
3
+ import { BrowserWASQLiteDriver } from "./wa-sqlite-driver.js";
4
+ function normalizeSchemaMismatchPolicy(policy) {
5
+ if (policy === `throw`) {
6
+ return `sync-absent-error`;
7
+ }
8
+ return policy;
9
+ }
10
+ function resolveSchemaMismatchPolicy(explicitPolicy, mode) {
11
+ if (explicitPolicy) {
12
+ return normalizeSchemaMismatchPolicy(explicitPolicy);
13
+ }
14
+ return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`;
15
+ }
16
+ function createAdapterCacheKey(schemaMismatchPolicy, schemaVersion) {
17
+ const schemaVersionKey = schemaVersion === void 0 ? `schema:default` : `schema:${schemaVersion}`;
18
+ return `${schemaMismatchPolicy}|${schemaVersionKey}`;
19
+ }
20
+ function createInternalSQLiteDriver(options) {
21
+ return new BrowserWASQLiteDriver({
22
+ database: options.database
23
+ });
24
+ }
25
+ function resolveAdapterBaseOptions(options) {
26
+ return {
27
+ appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,
28
+ appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,
29
+ pullSinceReloadThreshold: options.pullSinceReloadThreshold
30
+ };
31
+ }
32
+ function createBrowserWASQLitePersistence(options) {
33
+ const { coordinator, schemaMismatchPolicy } = options;
34
+ const driver = createInternalSQLiteDriver(options);
35
+ const adapterBaseOptions = resolveAdapterBaseOptions(options);
36
+ const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator();
37
+ const adapterCache = /* @__PURE__ */ new Map();
38
+ const getAdapterForCollection = (mode, schemaVersion) => {
39
+ const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(
40
+ schemaMismatchPolicy,
41
+ mode
42
+ );
43
+ const cacheKey = createAdapterCacheKey(
44
+ resolvedSchemaMismatchPolicy,
45
+ schemaVersion
46
+ );
47
+ const cachedAdapter = adapterCache.get(cacheKey);
48
+ if (cachedAdapter) {
49
+ return cachedAdapter;
50
+ }
51
+ const adapter = createSQLiteCorePersistenceAdapter({
52
+ ...adapterBaseOptions,
53
+ driver,
54
+ schemaMismatchPolicy: resolvedSchemaMismatchPolicy,
55
+ ...schemaVersion === void 0 ? {} : { schemaVersion }
56
+ });
57
+ adapterCache.set(cacheKey, adapter);
58
+ if (resolvedCoordinator instanceof BrowserCollectionCoordinator) {
59
+ resolvedCoordinator.setAdapter(adapter);
60
+ }
61
+ return adapter;
62
+ };
63
+ const createCollectionPersistence = (mode, schemaVersion) => ({
64
+ adapter: getAdapterForCollection(
65
+ mode,
66
+ schemaVersion
67
+ ),
68
+ coordinator: resolvedCoordinator
69
+ });
70
+ const defaultPersistence = createCollectionPersistence(
71
+ `sync-absent`,
72
+ void 0
73
+ );
74
+ return {
75
+ ...defaultPersistence,
76
+ resolvePersistenceForCollection: ({ mode, schemaVersion }) => createCollectionPersistence(mode, schemaVersion),
77
+ resolvePersistenceForMode: (mode) => createCollectionPersistence(mode, void 0)
78
+ };
79
+ }
80
+ export {
81
+ createBrowserWASQLitePersistence
82
+ };
83
+ //# sourceMappingURL=browser-persistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-persistence.js","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":[],"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,IAAI,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,IAAI,yBAAA;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,UAAU,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+B,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,8 @@
1
+ export { createBrowserWASQLitePersistence } from './browser-persistence.js';
2
+ export { openBrowserWASQLiteOPFSDatabase } from './opfs-database.js';
3
+ export { BrowserCollectionCoordinator } from './browser-coordinator.js';
4
+ export type { BrowserWASQLiteDatabase, BrowserWASQLitePersistenceOptions, BrowserWASQLiteSchemaMismatchPolicy, } from './browser-persistence.js';
5
+ export type { OpenBrowserWASQLiteOPFSDatabaseOptions } from './opfs-database.js';
6
+ export type { BrowserCollectionCoordinatorOptions } from './browser-coordinator.js';
7
+ export { persistedCollectionOptions } from '@tanstack/db-sqlite-persistence-core';
8
+ export type { PersistedCollectionCoordinator, PersistedCollectionPersistence, } from '@tanstack/db-sqlite-persistence-core';
@@ -0,0 +1,11 @@
1
+ import { createBrowserWASQLitePersistence } from "./browser-persistence.js";
2
+ import { openBrowserWASQLiteOPFSDatabase } from "./opfs-database.js";
3
+ import { BrowserCollectionCoordinator } from "./browser-coordinator.js";
4
+ import { persistedCollectionOptions } from "@tanstack/db-sqlite-persistence-core";
5
+ export {
6
+ BrowserCollectionCoordinator,
7
+ createBrowserWASQLitePersistence,
8
+ openBrowserWASQLiteOPFSDatabase,
9
+ persistedCollectionOptions
10
+ };
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,10 @@
1
+ import { BrowserWASQLiteDatabase } from './wa-sqlite-driver.js';
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,175 @@
1
+ import { InvalidPersistedCollectionConfigError, PersistenceUnavailableError } from "@tanstack/db-sqlite-persistence-core";
2
+ import WorkerWrapper from "./opfs-worker.js";
3
+ const DEFAULT_VFS_NAME = `opfs`;
4
+ class OPFSWorkerRequestError extends Error {
5
+ constructor(code, message) {
6
+ super(message);
7
+ this.code = code;
8
+ this.name = `OPFSWorkerRequestError`;
9
+ }
10
+ }
11
+ function hasOPFSBrowserPrerequisites(globalObject) {
12
+ const candidate = globalObject;
13
+ const getDirectory = candidate.navigator?.storage?.getDirectory;
14
+ const WorkerConstructor = candidate.Worker;
15
+ return typeof getDirectory === `function` && typeof WorkerConstructor === `function`;
16
+ }
17
+ function createWorkerError(code, message) {
18
+ if (code === `PERSISTENCE_UNAVAILABLE`) {
19
+ return new PersistenceUnavailableError(message);
20
+ }
21
+ if (code === `INVALID_CONFIG`) {
22
+ return new InvalidPersistedCollectionConfigError(message);
23
+ }
24
+ return new OPFSWorkerRequestError(code, message);
25
+ }
26
+ function createWorkerRequestIdFactory() {
27
+ let sequence = 0;
28
+ const prefix = `opfs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
29
+ return () => {
30
+ sequence++;
31
+ return `${prefix}-${sequence}`;
32
+ };
33
+ }
34
+ function createOPFSWorkerInstance() {
35
+ const WorkerConstructor = globalThis.Worker;
36
+ if (typeof WorkerConstructor !== `function`) {
37
+ throw new PersistenceUnavailableError(
38
+ `Web Worker support is required for browser OPFS persistence`
39
+ );
40
+ }
41
+ return new WorkerWrapper();
42
+ }
43
+ async function openBrowserWASQLiteOPFSDatabase(options) {
44
+ const databaseName = options.databaseName.trim();
45
+ if (databaseName.length === 0) {
46
+ throw new InvalidPersistedCollectionConfigError(
47
+ `Browser wa-sqlite databaseName cannot be empty`
48
+ );
49
+ }
50
+ if (!hasOPFSBrowserPrerequisites(globalThis)) {
51
+ throw new PersistenceUnavailableError(
52
+ `Browser OPFS prerequisites are not available in this runtime`
53
+ );
54
+ }
55
+ const vfsName = options.vfsName ?? DEFAULT_VFS_NAME;
56
+ const worker = createOPFSWorkerInstance();
57
+ const nextRequestId = createWorkerRequestIdFactory();
58
+ const pendingRequests = /* @__PURE__ */ new Map();
59
+ let disposed = false;
60
+ const disposeWorker = () => {
61
+ if (disposed) {
62
+ return;
63
+ }
64
+ disposed = true;
65
+ worker.removeEventListener(`message`, onMessage);
66
+ worker.removeEventListener(`error`, onError);
67
+ worker.removeEventListener(`messageerror`, onMessageError);
68
+ worker.terminate();
69
+ };
70
+ const rejectAllPendingRequests = (error) => {
71
+ for (const pendingRequest of pendingRequests.values()) {
72
+ pendingRequest.reject(error);
73
+ }
74
+ pendingRequests.clear();
75
+ };
76
+ const onMessage = (event) => {
77
+ const messageEvent = event;
78
+ const response = messageEvent.data;
79
+ const pendingRequest = pendingRequests.get(response.requestId);
80
+ if (!pendingRequest) {
81
+ return;
82
+ }
83
+ pendingRequests.delete(response.requestId);
84
+ if (response.ok) {
85
+ pendingRequest.resolve(response.rows ?? []);
86
+ return;
87
+ }
88
+ pendingRequest.reject(createWorkerError(response.code, response.error));
89
+ };
90
+ const onError = () => {
91
+ rejectAllPendingRequests(
92
+ new PersistenceUnavailableError(`OPFS worker terminated unexpectedly`)
93
+ );
94
+ disposeWorker();
95
+ };
96
+ const onMessageError = () => {
97
+ rejectAllPendingRequests(
98
+ new PersistenceUnavailableError(
99
+ `OPFS worker message serialization failed`
100
+ )
101
+ );
102
+ disposeWorker();
103
+ };
104
+ worker.addEventListener(`message`, onMessage);
105
+ worker.addEventListener(`error`, onError);
106
+ worker.addEventListener(`messageerror`, onMessageError);
107
+ const sendWorkerRequest = (request) => {
108
+ if (disposed) {
109
+ return Promise.reject(
110
+ new InvalidPersistedCollectionConfigError(
111
+ `Browser OPFS worker connection is closed`
112
+ )
113
+ );
114
+ }
115
+ const requestId = nextRequestId();
116
+ return new Promise((resolve, reject) => {
117
+ pendingRequests.set(requestId, {
118
+ resolve: (value) => {
119
+ resolve(value);
120
+ },
121
+ reject
122
+ });
123
+ try {
124
+ const payload = {
125
+ ...request,
126
+ requestId
127
+ };
128
+ worker.postMessage(payload);
129
+ } catch (error) {
130
+ pendingRequests.delete(requestId);
131
+ reject(
132
+ new PersistenceUnavailableError(
133
+ `Failed to send message to OPFS worker: ${error.message}`
134
+ )
135
+ );
136
+ }
137
+ });
138
+ };
139
+ try {
140
+ await sendWorkerRequest({
141
+ type: `init`,
142
+ databaseName,
143
+ vfsName
144
+ });
145
+ } catch (error) {
146
+ disposeWorker();
147
+ throw error;
148
+ }
149
+ return {
150
+ execute: (sql, params = []) => sendWorkerRequest({
151
+ type: `execute`,
152
+ sql,
153
+ params
154
+ }),
155
+ close: async () => {
156
+ let closeError;
157
+ try {
158
+ await sendWorkerRequest({
159
+ type: `close`
160
+ });
161
+ } catch (error) {
162
+ closeError = error;
163
+ } finally {
164
+ disposeWorker();
165
+ }
166
+ if (closeError) {
167
+ throw closeError;
168
+ }
169
+ }
170
+ };
171
+ }
172
+ export {
173
+ openBrowserWASQLiteOPFSDatabase
174
+ };
175
+ //# sourceMappingURL=opfs-database.js.map
@@ -0,0 +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`\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":["OPFSWorkerConstructor"],"mappings":";;AAYA,MAAM,mBAAmB;AAgCzB,MAAM,+BAA+B,MAAM;AAAA,EACzC,YACW,MACT,SACA;AACA,UAAM,OAAO;AAHJ,SAAA,OAAA;AAIT,SAAK,OAAO;AAAA,EACd;AACF;AAEA,SAAS,4BAA4B,cAAgC;AACnE,QAAM,YAAY;AAClB,QAAM,eAAe,UAAU,WAAW,SAAS;AACnD,QAAM,oBAAoB,UAAU;AAEpC,SACE,OAAO,iBAAiB,cACxB,OAAO,sBAAsB;AAEjC;AAEA,SAAS,kBACP,MACA,SACO;AACP,MAAI,SAAS,2BAA2B;AACtC,WAAO,IAAI,4BAA4B,OAAO;AAAA,EAChD;AACA,MAAI,SAAS,kBAAkB;AAC7B,WAAO,IAAI,sCAAsC,OAAO;AAAA,EAC1D;AACA,SAAO,IAAI,uBAAuB,MAAM,OAAO;AACjD;AAEA,SAAS,+BAA6C;AACpD,MAAI,WAAW;AACf,QAAM,SAAS,QAAQ,KAAK,IAAA,EAAM,SAAS,EAAE,CAAC,IAAI,KAAK,SAAS,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AAErF,SAAO,MAAM;AACX;AACA,WAAO,GAAG,MAAM,IAAI,QAAQ;AAAA,EAC9B;AACF;AAEA,SAAS,2BAAkD;AACzD,QAAM,oBACJ,WACA;AACF,MAAI,OAAO,sBAAsB,YAAY;AAC3C,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAEA,SAAO,IAAKA,cAAA;AACd;AAMA,eAAsB,gCACpB,SACkC;AAClC,QAAM,eAAe,QAAQ,aAAa,KAAA;AAC1C,MAAI,aAAa,WAAW,GAAG;AAC7B,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAEA,MAAI,CAAC,4BAA4B,UAAU,GAAG;AAC5C,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAEA,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,SAAS,yBAAA;AACf,QAAM,gBAAgB,6BAAA;AACtB,QAAM,sCAAsB,IAAA;AAC5B,MAAI,WAAW;AAEf,QAAM,gBAAgB,MAAY;AAChC,QAAI,UAAU;AACZ;AAAA,IACF;AACA,eAAW;AACX,WAAO,oBAAoB,WAAW,SAAS;AAC/C,WAAO,oBAAoB,SAAS,OAAO;AAC3C,WAAO,oBAAoB,gBAAgB,cAAc;AACzD,WAAO,UAAA;AAAA,EACT;AAEA,QAAM,2BAA2B,CAAC,UAAuB;AACvD,eAAW,kBAAkB,gBAAgB,UAAU;AACrD,qBAAe,OAAO,KAAK;AAAA,IAC7B;AACA,oBAAgB,MAAA;AAAA,EAClB;AAEA,QAAM,YAAY,CAAC,UAAuB;AACxC,UAAM,eAAe;AACrB,UAAM,WAAW,aAAa;AAE9B,UAAM,iBAAiB,gBAAgB,IAAI,SAAS,SAAS;AAC7D,QAAI,CAAC,gBAAgB;AACnB;AAAA,IACF;AACA,oBAAgB,OAAO,SAAS,SAAS;AAEzC,QAAI,SAAS,IAAI;AACf,qBAAe,QAAQ,SAAS,QAAQ,CAAA,CAAE;AAC1C;AAAA,IACF;AAEA,mBAAe,OAAO,kBAAkB,SAAS,MAAM,SAAS,KAAK,CAAC;AAAA,EACxE;AAEA,QAAM,UAAU,MAAY;AAC1B;AAAA,MACE,IAAI,4BAA4B,qCAAqC;AAAA,IAAA;AAEvE,kBAAA;AAAA,EACF;AAEA,QAAM,iBAAiB,MAAY;AACjC;AAAA,MACE,IAAI;AAAA,QACF;AAAA,MAAA;AAAA,IACF;AAEF,kBAAA;AAAA,EACF;AAEA,SAAO,iBAAiB,WAAW,SAAS;AAC5C,SAAO,iBAAiB,SAAS,OAAO;AACxC,SAAO,iBAAiB,gBAAgB,cAAc;AAEtD,QAAM,oBAAoB,CACxB,YACe;AACf,QAAI,UAAU;AACZ,aAAO,QAAQ;AAAA,QACb,IAAI;AAAA,UACF;AAAA,QAAA;AAAA,MACF;AAAA,IAEJ;AAEA,UAAM,YAAY,cAAA;AAClB,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,sBAAgB,IAAI,WAAW;AAAA,QAC7B,SAAS,CAAC,UAAU;AAClB,kBAAQ,KAAU;AAAA,QACpB;AAAA,QACA;AAAA,MAAA,CACD;AACD,UAAI;AACF,cAAM,UAAU;AAAA,UACd,GAAG;AAAA,UACH;AAAA,QAAA;AAEF,eAAO,YAAY,OAAO;AAAA,MAC5B,SAAS,OAAO;AACd,wBAAgB,OAAO,SAAS;AAChC;AAAA,UACE,IAAI;AAAA,YACF,0CAA2C,MAAgB,OAAO;AAAA,UAAA;AAAA,QACpE;AAAA,MAEJ;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI;AACF,UAAM,kBAAwB;AAAA,MAC5B,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH,SAAS,OAAO;AACd,kBAAA;AACA,UAAM;AAAA,EACR;AAEA,SAAO;AAAA,IACL,SAAS,CACP,KACA,SAAiC,CAAA,MAEjC,kBAAuC;AAAA,MACrC,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IAAA,CACD;AAAA,IACH,OAAO,YAAY;AACjB,UAAI;AACJ,UAAI;AACF,cAAM,kBAAwB;AAAA,UAC5B,MAAM;AAAA,QAAA,CACP;AAAA,MACH,SAAS,OAAO;AACd,qBAAa;AAAA,MACf,UAAA;AACE,sBAAA;AAAA,MACF;AAEA,UAAI,YAAY;AACd,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EAAA;AAEJ;"}
@@ -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 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ function WorkerWrapper(options) {
2
+ return new Worker(
3
+ "/assets/opfs-worker-CCciqEMo.js",
4
+ {
5
+ name: options?.name
6
+ }
7
+ );
8
+ }
9
+ export {
10
+ WorkerWrapper as default
11
+ };
12
+ //# sourceMappingURL=opfs-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opfs-worker.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -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,106 @@
1
+ import { InvalidPersistedCollectionConfigError } from "@tanstack/db-sqlite-persistence-core";
2
+ function assertTransactionCallbackHasDriverArg(fn) {
3
+ if (fn.length > 0) {
4
+ return;
5
+ }
6
+ throw new InvalidPersistedCollectionConfigError(
7
+ `SQLiteDriver.transaction callback must accept the transaction driver argument`
8
+ );
9
+ }
10
+ function assertDatabaseShape(database) {
11
+ if (typeof database.execute !== `function`) {
12
+ throw new InvalidPersistedCollectionConfigError(
13
+ `Browser wa-sqlite database handle must provide execute(sql, params?)`
14
+ );
15
+ }
16
+ }
17
+ class BrowserWASQLiteDriver {
18
+ constructor(options) {
19
+ this.queue = Promise.resolve();
20
+ this.nextSavepointId = 1;
21
+ this.closed = false;
22
+ assertDatabaseShape(options.database);
23
+ this.database = options.database;
24
+ }
25
+ async exec(sql) {
26
+ await this.enqueue(async () => {
27
+ await this.database.execute(sql);
28
+ });
29
+ }
30
+ async query(sql, params = []) {
31
+ return this.enqueue(() => this.database.execute(sql, params));
32
+ }
33
+ async run(sql, params = []) {
34
+ await this.enqueue(async () => {
35
+ await this.database.execute(sql, params);
36
+ });
37
+ }
38
+ async transaction(fn) {
39
+ assertTransactionCallbackHasDriverArg(fn);
40
+ return this.enqueue(async () => {
41
+ await this.database.execute(`BEGIN IMMEDIATE`);
42
+ try {
43
+ const result = await fn(this.createTransactionDriver());
44
+ await this.database.execute(`COMMIT`);
45
+ return result;
46
+ } catch (error) {
47
+ try {
48
+ await this.database.execute(`ROLLBACK`);
49
+ } catch {
50
+ }
51
+ throw error;
52
+ }
53
+ });
54
+ }
55
+ async transactionWithDriver(fn) {
56
+ return this.transaction(fn);
57
+ }
58
+ async close() {
59
+ if (this.closed) {
60
+ return;
61
+ }
62
+ this.closed = true;
63
+ if (typeof this.database.close === `function`) {
64
+ await Promise.resolve(this.database.close());
65
+ }
66
+ }
67
+ getDatabase() {
68
+ return this.database;
69
+ }
70
+ createTransactionDriver() {
71
+ return {
72
+ exec: (sql) => this.database.execute(sql).then(() => void 0),
73
+ query: (sql, params = []) => this.database.execute(sql, params),
74
+ run: (sql, params = []) => this.database.execute(sql, params).then(() => void 0),
75
+ transaction: (fn) => this.runNestedTransaction(fn),
76
+ transactionWithDriver: (fn) => this.runNestedTransaction(fn)
77
+ };
78
+ }
79
+ async runNestedTransaction(fn) {
80
+ assertTransactionCallbackHasDriverArg(fn);
81
+ const savepointName = `tsdb_sp_${this.nextSavepointId}`;
82
+ this.nextSavepointId++;
83
+ await this.database.execute(`SAVEPOINT ${savepointName}`);
84
+ try {
85
+ const result = await fn(this.createTransactionDriver());
86
+ await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`);
87
+ return result;
88
+ } catch (error) {
89
+ await this.database.execute(`ROLLBACK TO SAVEPOINT ${savepointName}`);
90
+ await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`);
91
+ throw error;
92
+ }
93
+ }
94
+ enqueue(operation) {
95
+ const queuedOperation = this.queue.then(operation, operation);
96
+ this.queue = queuedOperation.then(
97
+ () => void 0,
98
+ () => void 0
99
+ );
100
+ return queuedOperation;
101
+ }
102
+ }
103
+ export {
104
+ BrowserWASQLiteDriver
105
+ };
106
+ //# sourceMappingURL=wa-sqlite-driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wa-sqlite-driver.js","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":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@tanstack/browser-db-sqlite-persistence",
3
+ "version": "0.1.2",
4
+ "description": "Browser wa-sqlite persisted collection adapter for TanStack DB",
5
+ "author": "TanStack Team",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/TanStack/db.git",
10
+ "directory": "packages/browser-db-sqlite-persistence"
11
+ },
12
+ "homepage": "https://tanstack.com/db",
13
+ "keywords": [
14
+ "sqlite",
15
+ "wa-sqlite",
16
+ "browser",
17
+ "opfs",
18
+ "persistence",
19
+ "typescript"
20
+ ],
21
+ "scripts": {
22
+ "build": "vite build",
23
+ "dev": "vite build --watch",
24
+ "lint": "eslint . --fix",
25
+ "test": "vitest --run",
26
+ "test:e2e": "pnpm --filter @tanstack/db-ivm build && pnpm --filter @tanstack/db build && pnpm --filter @tanstack/db-sqlite-persistence-core build && pnpm --filter @tanstack/browser-db-sqlite-persistence build && vitest --config vitest.e2e.config.ts --run"
27
+ },
28
+ "type": "module",
29
+ "main": "dist/cjs/index.cjs",
30
+ "module": "dist/esm/index.js",
31
+ "types": "dist/esm/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "import": {
35
+ "types": "./dist/esm/index.d.ts",
36
+ "default": "./dist/esm/index.js"
37
+ },
38
+ "require": {
39
+ "types": "./dist/cjs/index.d.cts",
40
+ "default": "./dist/cjs/index.cjs"
41
+ }
42
+ },
43
+ "./package.json": "./package.json"
44
+ },
45
+ "sideEffects": false,
46
+ "files": [
47
+ "dist",
48
+ "src"
49
+ ],
50
+ "dependencies": {
51
+ "@tanstack/db-sqlite-persistence-core": "workspace:*"
52
+ },
53
+ "peerDependencies": {
54
+ "@journeyapps/wa-sqlite": "^1.4.1",
55
+ "typescript": ">=4.7"
56
+ },
57
+ "devDependencies": {
58
+ "@journeyapps/wa-sqlite": "^1.4.1",
59
+ "@types/better-sqlite3": "^7.6.13",
60
+ "@vitest/coverage-istanbul": "^3.2.4",
61
+ "better-sqlite3": "^12.6.2"
62
+ }
63
+ }