@tanstack/capacitor-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.
@@ -0,0 +1,30 @@
1
+ import { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core';
2
+ import { SQLiteDBConnection } from '@capacitor-community/sqlite';
3
+ export type CapacitorSQLiteDatabaseLike = SQLiteDBConnection;
4
+ export type CapacitorSQLiteDriverOptions = {
5
+ database: CapacitorSQLiteDatabaseLike;
6
+ };
7
+ export declare class CapacitorSQLiteDriver implements SQLiteDriver {
8
+ private readonly database;
9
+ private queue;
10
+ private nextSavepointId;
11
+ private transactionContextStoragePromise;
12
+ constructor(options: CapacitorSQLiteDriverOptions);
13
+ exec(sql: string): Promise<void>;
14
+ query<T>(sql: string, params?: ReadonlyArray<unknown>): Promise<ReadonlyArray<T>>;
15
+ run(sql: string, params?: ReadonlyArray<unknown>): Promise<void>;
16
+ transaction<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T>;
17
+ transactionWithDriver<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T>;
18
+ close(): Promise<void>;
19
+ getDatabase(): CapacitorSQLiteDatabaseLike;
20
+ private enqueue;
21
+ private getTransactionContextStorage;
22
+ private getActiveTransactionDriver;
23
+ private runWithTransactionContext;
24
+ private beginTopLevelTransaction;
25
+ private commitTopLevelTransaction;
26
+ private rollbackTopLevelTransaction;
27
+ private createTransactionDriver;
28
+ private runNestedTransaction;
29
+ }
30
+ export declare function createCapacitorSQLiteDriver(options: CapacitorSQLiteDriverOptions): CapacitorSQLiteDriver;
@@ -0,0 +1,220 @@
1
+ import { InvalidPersistedCollectionConfigError } from "@tanstack/db-sqlite-persistence-core";
2
+ let asyncLocalStorageCtorPromise = null;
3
+ function canAttemptNodeAsyncLocalStorageLoad() {
4
+ if (typeof process === `undefined`) {
5
+ return false;
6
+ }
7
+ return typeof process.versions?.node === `string`;
8
+ }
9
+ function getNodeAsyncHooksSpecifier() {
10
+ const moduleName = `async_hooks`;
11
+ return `node:${moduleName}`;
12
+ }
13
+ async function resolveAsyncLocalStorageCtor() {
14
+ if (asyncLocalStorageCtorPromise) {
15
+ return asyncLocalStorageCtorPromise;
16
+ }
17
+ asyncLocalStorageCtorPromise = (async () => {
18
+ if (!canAttemptNodeAsyncLocalStorageLoad()) {
19
+ return null;
20
+ }
21
+ try {
22
+ const asyncHooksModule = await import(getNodeAsyncHooksSpecifier());
23
+ return typeof asyncHooksModule.AsyncLocalStorage === `function` ? asyncHooksModule.AsyncLocalStorage : null;
24
+ } catch {
25
+ return null;
26
+ }
27
+ })();
28
+ return asyncLocalStorageCtorPromise;
29
+ }
30
+ function assertTransactionCallbackHasDriverArg(fn) {
31
+ if (fn.length > 0) {
32
+ return;
33
+ }
34
+ throw new InvalidPersistedCollectionConfigError(
35
+ `SQLiteDriver.transaction callback must accept the transaction driver argument`
36
+ );
37
+ }
38
+ function isCapacitorSQLiteDatabaseLike(value) {
39
+ const candidate = value;
40
+ return typeof value === `object` && value !== null && typeof candidate.execute === `function` && typeof candidate.query === `function` && typeof candidate.run === `function`;
41
+ }
42
+ function extractQueryRows(result, sql) {
43
+ if (Array.isArray(result.values)) {
44
+ return result.values;
45
+ }
46
+ throw new InvalidPersistedCollectionConfigError(
47
+ `Unsupported Capacitor SQLite query result shape for SQL "${sql}"`
48
+ );
49
+ }
50
+ class CapacitorSQLiteDriver {
51
+ constructor(options) {
52
+ this.queue = Promise.resolve();
53
+ this.nextSavepointId = 1;
54
+ this.transactionContextStoragePromise = null;
55
+ if (!isCapacitorSQLiteDatabaseLike(options.database)) {
56
+ throw new InvalidPersistedCollectionConfigError(
57
+ `Capacitor SQLite database object must provide execute/query/run methods`
58
+ );
59
+ }
60
+ this.database = options.database;
61
+ }
62
+ async exec(sql) {
63
+ const activeTransactionDriver = await this.getActiveTransactionDriver();
64
+ if (activeTransactionDriver) {
65
+ await activeTransactionDriver.exec(sql);
66
+ return;
67
+ }
68
+ await this.enqueue(async () => {
69
+ await this.database.execute(sql, false);
70
+ });
71
+ }
72
+ async query(sql, params = []) {
73
+ const activeTransactionDriver = await this.getActiveTransactionDriver();
74
+ if (activeTransactionDriver) {
75
+ return activeTransactionDriver.query(sql, params);
76
+ }
77
+ return this.enqueue(async () => {
78
+ const result = await this.database.query(sql, [...params]);
79
+ return extractQueryRows(result, sql);
80
+ });
81
+ }
82
+ async run(sql, params = []) {
83
+ const activeTransactionDriver = await this.getActiveTransactionDriver();
84
+ if (activeTransactionDriver) {
85
+ await activeTransactionDriver.run(sql, params);
86
+ return;
87
+ }
88
+ await this.enqueue(async () => {
89
+ await this.database.run(sql, [...params], false);
90
+ });
91
+ }
92
+ async transaction(fn) {
93
+ assertTransactionCallbackHasDriverArg(fn);
94
+ const activeTransactionDriver = await this.getActiveTransactionDriver();
95
+ if (activeTransactionDriver) {
96
+ return activeTransactionDriver.transaction(fn);
97
+ }
98
+ return this.transactionWithDriver(
99
+ (transactionDriver) => fn(transactionDriver)
100
+ );
101
+ }
102
+ async transactionWithDriver(fn) {
103
+ const activeTransactionDriver = await this.getActiveTransactionDriver();
104
+ if (activeTransactionDriver && typeof activeTransactionDriver.transactionWithDriver === `function`) {
105
+ return activeTransactionDriver.transactionWithDriver(fn);
106
+ }
107
+ return this.enqueue(async () => {
108
+ await this.beginTopLevelTransaction();
109
+ const transactionDriver = this.createTransactionDriver();
110
+ try {
111
+ const result = await this.runWithTransactionContext(
112
+ transactionDriver,
113
+ async () => fn(transactionDriver)
114
+ );
115
+ await this.commitTopLevelTransaction();
116
+ return result;
117
+ } catch (error) {
118
+ try {
119
+ await this.rollbackTopLevelTransaction();
120
+ } catch {
121
+ }
122
+ throw error;
123
+ }
124
+ });
125
+ }
126
+ async close() {
127
+ await this.database.close();
128
+ }
129
+ getDatabase() {
130
+ return this.database;
131
+ }
132
+ enqueue(operation) {
133
+ const queuedOperation = this.queue.then(operation, operation);
134
+ this.queue = queuedOperation.then(
135
+ () => void 0,
136
+ () => void 0
137
+ );
138
+ return queuedOperation;
139
+ }
140
+ async getTransactionContextStorage() {
141
+ if (this.transactionContextStoragePromise) {
142
+ return this.transactionContextStoragePromise;
143
+ }
144
+ this.transactionContextStoragePromise = (async () => {
145
+ const asyncLocalStorageCtor = await resolveAsyncLocalStorageCtor();
146
+ if (!asyncLocalStorageCtor) {
147
+ return null;
148
+ }
149
+ return new asyncLocalStorageCtor();
150
+ })();
151
+ return this.transactionContextStoragePromise;
152
+ }
153
+ async getActiveTransactionDriver() {
154
+ const transactionContextStorage = await this.getTransactionContextStorage();
155
+ const store = transactionContextStorage?.getStore();
156
+ return store?.transactionDriver ?? null;
157
+ }
158
+ async runWithTransactionContext(transactionDriver, callback) {
159
+ const transactionContextStorage = await this.getTransactionContextStorage();
160
+ if (!transactionContextStorage) {
161
+ return callback();
162
+ }
163
+ return transactionContextStorage.run({ transactionDriver }, callback);
164
+ }
165
+ async beginTopLevelTransaction() {
166
+ await this.database.execute(`BEGIN IMMEDIATE`, false);
167
+ }
168
+ async commitTopLevelTransaction() {
169
+ await this.database.execute(`COMMIT`, false);
170
+ }
171
+ async rollbackTopLevelTransaction() {
172
+ await this.database.execute(`ROLLBACK`, false);
173
+ }
174
+ createTransactionDriver() {
175
+ const transactionDriver = {
176
+ exec: async (sql) => {
177
+ await this.database.execute(sql, false);
178
+ },
179
+ query: async (sql, params = []) => {
180
+ const result = await this.database.query(sql, [...params]);
181
+ return extractQueryRows(result, sql);
182
+ },
183
+ run: async (sql, params = []) => {
184
+ await this.database.run(sql, [...params], false);
185
+ },
186
+ transaction: async (fn) => {
187
+ assertTransactionCallbackHasDriverArg(fn);
188
+ return this.runNestedTransaction(transactionDriver, async (driver) => {
189
+ return fn(driver);
190
+ });
191
+ },
192
+ transactionWithDriver: async (fn) => this.runNestedTransaction(transactionDriver, fn)
193
+ };
194
+ return transactionDriver;
195
+ }
196
+ async runNestedTransaction(transactionDriver, fn) {
197
+ const savepointName = `tsdb_sp_${this.nextSavepointId}`;
198
+ this.nextSavepointId++;
199
+ await this.database.execute(`SAVEPOINT ${savepointName}`, false);
200
+ try {
201
+ const result = await this.runWithTransactionContext(
202
+ transactionDriver,
203
+ async () => fn(transactionDriver)
204
+ );
205
+ await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`, false);
206
+ return result;
207
+ } catch (error) {
208
+ await this.database.execute(
209
+ `ROLLBACK TO SAVEPOINT ${savepointName}`,
210
+ false
211
+ );
212
+ await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`, false);
213
+ throw error;
214
+ }
215
+ }
216
+ }
217
+ export {
218
+ CapacitorSQLiteDriver
219
+ };
220
+ //# sourceMappingURL=capacitor-sqlite-driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capacitor-sqlite-driver.js","sources":["../../src/capacitor-sqlite-driver.ts"],"sourcesContent":["import { InvalidPersistedCollectionConfigError } from '@tanstack/db-sqlite-persistence-core'\nimport type { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core'\nimport type {\n capSQLiteValues as CapacitorSQLiteValues,\n SQLiteDBConnection,\n} from '@capacitor-community/sqlite'\n\nexport type CapacitorSQLiteDatabaseLike = SQLiteDBConnection\n\ntype TransactionContextStore = {\n transactionDriver: SQLiteDriver\n}\n\ntype AsyncLocalStorageLike<TStore> = {\n getStore: () => TStore | undefined\n run: <TResult>(store: TStore, callback: () => TResult) => TResult\n}\n\ntype AsyncLocalStorageCtor = new <TStore>() => AsyncLocalStorageLike<TStore>\n\nlet asyncLocalStorageCtorPromise: Promise<AsyncLocalStorageCtor | null> | null =\n null\n\nfunction canAttemptNodeAsyncLocalStorageLoad(): boolean {\n if (typeof process === `undefined`) {\n return false\n }\n\n // In Capacitor webviews, process may be polyfilled without versions.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return typeof process.versions?.node === `string`\n}\n\nfunction getNodeAsyncHooksSpecifier(): string {\n const moduleName = `async_hooks`\n return `node:${moduleName}`\n}\n\nasync function resolveAsyncLocalStorageCtor(): Promise<AsyncLocalStorageCtor | null> {\n if (asyncLocalStorageCtorPromise) {\n return asyncLocalStorageCtorPromise\n }\n\n asyncLocalStorageCtorPromise = (async () => {\n if (!canAttemptNodeAsyncLocalStorageLoad()) {\n return null\n }\n\n try {\n const asyncHooksModule = (await import(getNodeAsyncHooksSpecifier())) as {\n AsyncLocalStorage?: AsyncLocalStorageCtor\n }\n\n return typeof asyncHooksModule.AsyncLocalStorage === `function`\n ? asyncHooksModule.AsyncLocalStorage\n : null\n } catch {\n return null\n }\n })()\n\n return asyncLocalStorageCtorPromise\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 isCapacitorSQLiteDatabaseLike(\n value: unknown,\n): value is CapacitorSQLiteDatabaseLike {\n const candidate = value as Partial<CapacitorSQLiteDatabaseLike>\n return (\n typeof value === `object` &&\n value !== null &&\n typeof candidate.execute === `function` &&\n typeof candidate.query === `function` &&\n typeof candidate.run === `function`\n )\n}\n\nfunction extractQueryRows<T>(\n result: CapacitorSQLiteValues,\n sql: string,\n): ReadonlyArray<T> {\n if (Array.isArray(result.values)) {\n return result.values as ReadonlyArray<T>\n }\n\n throw new InvalidPersistedCollectionConfigError(\n `Unsupported Capacitor SQLite query result shape for SQL \"${sql}\"`,\n )\n}\n\nexport type CapacitorSQLiteDriverOptions = {\n database: CapacitorSQLiteDatabaseLike\n}\n\nexport class CapacitorSQLiteDriver implements SQLiteDriver {\n private readonly database: CapacitorSQLiteDatabaseLike\n private queue: Promise<void> = Promise.resolve()\n private nextSavepointId = 1\n private transactionContextStoragePromise: Promise<AsyncLocalStorageLike<TransactionContextStore> | null> | null =\n null\n\n constructor(options: CapacitorSQLiteDriverOptions) {\n if (!isCapacitorSQLiteDatabaseLike(options.database)) {\n throw new InvalidPersistedCollectionConfigError(\n `Capacitor SQLite database object must provide execute/query/run methods`,\n )\n }\n\n this.database = options.database\n }\n\n async exec(sql: string): Promise<void> {\n const activeTransactionDriver = await this.getActiveTransactionDriver()\n if (activeTransactionDriver) {\n await activeTransactionDriver.exec(sql)\n return\n }\n\n await this.enqueue(async () => {\n await this.database.execute(sql, false)\n })\n }\n\n async query<T>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<ReadonlyArray<T>> {\n const activeTransactionDriver = await this.getActiveTransactionDriver()\n if (activeTransactionDriver) {\n return activeTransactionDriver.query<T>(sql, params)\n }\n\n return this.enqueue(async () => {\n const result = await this.database.query(sql, [...params])\n return extractQueryRows<T>(result, sql)\n })\n }\n\n async run(sql: string, params: ReadonlyArray<unknown> = []): Promise<void> {\n const activeTransactionDriver = await this.getActiveTransactionDriver()\n if (activeTransactionDriver) {\n await activeTransactionDriver.run(sql, params)\n return\n }\n\n await this.enqueue(async () => {\n await this.database.run(sql, [...params], false)\n })\n }\n\n async transaction<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n assertTransactionCallbackHasDriverArg(fn)\n\n const activeTransactionDriver = await this.getActiveTransactionDriver()\n if (activeTransactionDriver) {\n return activeTransactionDriver.transaction(fn)\n }\n\n return this.transactionWithDriver((transactionDriver) =>\n fn(transactionDriver),\n )\n }\n\n async transactionWithDriver<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n const activeTransactionDriver = await this.getActiveTransactionDriver()\n if (\n activeTransactionDriver &&\n typeof activeTransactionDriver.transactionWithDriver === `function`\n ) {\n return activeTransactionDriver.transactionWithDriver(fn)\n }\n\n return this.enqueue(async () => {\n await this.beginTopLevelTransaction()\n const transactionDriver = this.createTransactionDriver()\n try {\n const result = await this.runWithTransactionContext(\n transactionDriver,\n async () => fn(transactionDriver),\n )\n await this.commitTopLevelTransaction()\n return result\n } catch (error) {\n try {\n await this.rollbackTopLevelTransaction()\n } catch {\n // Preserve the original transaction failure as the primary error.\n }\n throw error\n }\n })\n }\n\n async close(): Promise<void> {\n await this.database.close()\n }\n\n getDatabase(): CapacitorSQLiteDatabaseLike {\n return this.database\n }\n\n private enqueue<T>(operation: () => Promise<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 private async getTransactionContextStorage(): Promise<AsyncLocalStorageLike<TransactionContextStore> | null> {\n if (this.transactionContextStoragePromise) {\n return this.transactionContextStoragePromise\n }\n\n this.transactionContextStoragePromise = (async () => {\n const asyncLocalStorageCtor = await resolveAsyncLocalStorageCtor()\n if (!asyncLocalStorageCtor) {\n return null\n }\n\n return new asyncLocalStorageCtor<TransactionContextStore>()\n })()\n\n return this.transactionContextStoragePromise\n }\n\n private async getActiveTransactionDriver(): Promise<SQLiteDriver | null> {\n const transactionContextStorage = await this.getTransactionContextStorage()\n const store = transactionContextStorage?.getStore()\n return store?.transactionDriver ?? null\n }\n\n private async runWithTransactionContext<TResult>(\n transactionDriver: SQLiteDriver,\n callback: () => Promise<TResult>,\n ): Promise<TResult> {\n const transactionContextStorage = await this.getTransactionContextStorage()\n if (!transactionContextStorage) {\n return callback()\n }\n\n return transactionContextStorage.run({ transactionDriver }, callback)\n }\n\n private async beginTopLevelTransaction(): Promise<void> {\n await this.database.execute(`BEGIN IMMEDIATE`, false)\n }\n\n private async commitTopLevelTransaction(): Promise<void> {\n await this.database.execute(`COMMIT`, false)\n }\n\n private async rollbackTopLevelTransaction(): Promise<void> {\n await this.database.execute(`ROLLBACK`, false)\n }\n\n private createTransactionDriver(): SQLiteDriver {\n const transactionDriver: SQLiteDriver = {\n exec: async (sql) => {\n await this.database.execute(sql, false)\n },\n query: async <T>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<ReadonlyArray<T>> => {\n const result = await this.database.query(sql, [...params])\n return extractQueryRows<T>(result, sql)\n },\n run: async (sql, params = []) => {\n await this.database.run(sql, [...params], false)\n },\n transaction: async <T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> => {\n assertTransactionCallbackHasDriverArg(fn)\n return this.runNestedTransaction(transactionDriver, async (driver) => {\n return fn(driver)\n })\n },\n transactionWithDriver: async <T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> => this.runNestedTransaction(transactionDriver, fn),\n }\n\n return transactionDriver\n }\n\n private async runNestedTransaction<T>(\n transactionDriver: SQLiteDriver,\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n const savepointName = `tsdb_sp_${this.nextSavepointId}`\n this.nextSavepointId++\n await this.database.execute(`SAVEPOINT ${savepointName}`, false)\n\n try {\n const result = await this.runWithTransactionContext(\n transactionDriver,\n async () => fn(transactionDriver),\n )\n await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`, false)\n return result\n } catch (error) {\n await this.database.execute(\n `ROLLBACK TO SAVEPOINT ${savepointName}`,\n false,\n )\n await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`, false)\n throw error\n }\n }\n}\n\nexport function createCapacitorSQLiteDriver(\n options: CapacitorSQLiteDriverOptions,\n): CapacitorSQLiteDriver {\n return new CapacitorSQLiteDriver(options)\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { CapacitorSQLitePersistenceOptions as CapacitorSQLitePersistenceOptionsBase, CapacitorSQLiteSchemaMismatchPolicy as CapacitorSQLiteSchemaMismatchPolicyBase } from './capacitor-persistence.js';
2
+ import { PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core';
3
+ export type CapacitorSQLitePersistenceOptions = CapacitorSQLitePersistenceOptionsBase;
4
+ export type CapacitorSQLiteSchemaMismatchPolicy = CapacitorSQLiteSchemaMismatchPolicyBase;
5
+ export type { CapacitorSQLiteDatabaseLike } from './capacitor-persistence.js';
6
+ export type { SQLiteDBConnection } from './capacitor-persistence.js';
7
+ export declare function createCapacitorSQLitePersistence<T extends object, TKey extends string | number = string | number>(options: CapacitorSQLitePersistenceOptions): PersistedCollectionPersistence<T, TKey>;
@@ -0,0 +1,8 @@
1
+ import { createCapacitorSQLitePersistence as createCapacitorSQLitePersistence$1 } from "./capacitor-persistence.js";
2
+ function createCapacitorSQLitePersistence(options) {
3
+ return createCapacitorSQLitePersistence$1(options);
4
+ }
5
+ export {
6
+ createCapacitorSQLitePersistence
7
+ };
8
+ //# sourceMappingURL=capacitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capacitor.js","sources":["../../src/capacitor.ts"],"sourcesContent":["import { createCapacitorSQLitePersistence as createPersistence } from './capacitor-persistence'\nimport type {\n CapacitorSQLitePersistenceOptions as CapacitorSQLitePersistenceOptionsBase,\n CapacitorSQLiteSchemaMismatchPolicy as CapacitorSQLiteSchemaMismatchPolicyBase,\n} from './capacitor-persistence'\nimport type { PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core'\n\nexport type CapacitorSQLitePersistenceOptions =\n CapacitorSQLitePersistenceOptionsBase\nexport type CapacitorSQLiteSchemaMismatchPolicy =\n CapacitorSQLiteSchemaMismatchPolicyBase\nexport type { CapacitorSQLiteDatabaseLike } from './capacitor-persistence'\nexport type { SQLiteDBConnection } from './capacitor-persistence'\n\nexport function createCapacitorSQLitePersistence<\n T extends object,\n TKey extends string | number = string | number,\n>(\n options: CapacitorSQLitePersistenceOptions,\n): PersistedCollectionPersistence<T, TKey> {\n return createPersistence<T, TKey>(options)\n}\n"],"names":["createPersistence"],"mappings":";AAcO,SAAS,iCAId,SACyC;AACzC,SAAOA,mCAA2B,OAAO;AAC3C;"}
@@ -0,0 +1,4 @@
1
+ export { createCapacitorSQLitePersistence } from './capacitor.js';
2
+ export type { CapacitorSQLiteDatabaseLike, CapacitorSQLitePersistenceOptions, CapacitorSQLiteSchemaMismatchPolicy, SQLiteDBConnection, } from './capacitor.js';
3
+ export { persistedCollectionOptions } from '@tanstack/db-sqlite-persistence-core';
4
+ export type { PersistedCollectionCoordinator, PersistedCollectionPersistence, } from '@tanstack/db-sqlite-persistence-core';
@@ -0,0 +1,7 @@
1
+ import { createCapacitorSQLitePersistence } from "./capacitor.js";
2
+ import { persistedCollectionOptions } from "@tanstack/db-sqlite-persistence-core";
3
+ export {
4
+ createCapacitorSQLitePersistence,
5
+ persistedCollectionOptions
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@tanstack/capacitor-db-sqlite-persistence",
3
+ "version": "0.1.2",
4
+ "description": "Capacitor 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/capacitor-db-sqlite-persistence"
11
+ },
12
+ "homepage": "https://tanstack.com/db",
13
+ "keywords": [
14
+ "sqlite",
15
+ "capacitor",
16
+ "ionic",
17
+ "persistence",
18
+ "typescript"
19
+ ],
20
+ "scripts": {
21
+ "build": "vite build",
22
+ "dev": "vite build --watch",
23
+ "lint": "eslint . --fix",
24
+ "test": "vitest --run",
25
+ "test:e2e": "pnpm --filter @tanstack/db-ivm build && pnpm --filter @tanstack/db build && pnpm --filter @tanstack/db-sqlite-persistence-core build && pnpm --filter @tanstack/capacitor-db-sqlite-persistence build && vitest --config vitest.e2e.config.ts --run",
26
+ "test:e2e:android": "pnpm exec tsx ./e2e/run-android-e2e.ts",
27
+ "test:e2e:ios": "pnpm exec tsx ./e2e/run-ios-e2e.ts"
28
+ },
29
+ "type": "module",
30
+ "main": "dist/cjs/index.cjs",
31
+ "module": "dist/esm/index.js",
32
+ "types": "dist/esm/index.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "import": {
36
+ "types": "./dist/esm/index.d.ts",
37
+ "default": "./dist/esm/index.js"
38
+ },
39
+ "require": {
40
+ "types": "./dist/cjs/index.d.cts",
41
+ "default": "./dist/cjs/index.cjs"
42
+ }
43
+ },
44
+ "./capacitor": {
45
+ "import": {
46
+ "types": "./dist/esm/capacitor.d.ts",
47
+ "default": "./dist/esm/capacitor.js"
48
+ },
49
+ "require": {
50
+ "types": "./dist/cjs/capacitor.d.cts",
51
+ "default": "./dist/cjs/capacitor.cjs"
52
+ }
53
+ },
54
+ "./package.json": "./package.json"
55
+ },
56
+ "sideEffects": false,
57
+ "files": [
58
+ "dist",
59
+ "src"
60
+ ],
61
+ "dependencies": {
62
+ "@tanstack/db-sqlite-persistence-core": "workspace:*"
63
+ },
64
+ "peerDependencies": {
65
+ "@capacitor-community/sqlite": "^8.0.1",
66
+ "typescript": ">=4.7"
67
+ },
68
+ "devDependencies": {
69
+ "@types/better-sqlite3": "^7.6.13",
70
+ "@vitest/coverage-istanbul": "^3.2.4",
71
+ "better-sqlite3": "^12.6.2"
72
+ }
73
+ }
@@ -0,0 +1,163 @@
1
+ import {
2
+ SingleProcessCoordinator,
3
+ createSQLiteCorePersistenceAdapter,
4
+ } from '@tanstack/db-sqlite-persistence-core'
5
+ import { CapacitorSQLiteDriver } from './capacitor-sqlite-driver'
6
+ import type { SQLiteDBConnection } from '@capacitor-community/sqlite'
7
+ import type {
8
+ PersistedCollectionCoordinator,
9
+ PersistedCollectionMode,
10
+ PersistedCollectionPersistence,
11
+ SQLiteCoreAdapterOptions,
12
+ SQLiteDriver,
13
+ } from '@tanstack/db-sqlite-persistence-core'
14
+
15
+ export type { CapacitorSQLiteDatabaseLike } from './capacitor-sqlite-driver'
16
+ export type { SQLiteDBConnection } from '@capacitor-community/sqlite'
17
+
18
+ type CapacitorSQLiteCoreSchemaMismatchPolicy =
19
+ | `sync-present-reset`
20
+ | `sync-absent-error`
21
+ | `reset`
22
+
23
+ export type CapacitorSQLiteSchemaMismatchPolicy =
24
+ | CapacitorSQLiteCoreSchemaMismatchPolicy
25
+ | `throw`
26
+
27
+ type CapacitorSQLitePersistenceBaseOptions = Omit<
28
+ SQLiteCoreAdapterOptions,
29
+ `driver` | `schemaVersion` | `schemaMismatchPolicy`
30
+ > & {
31
+ database: SQLiteDBConnection
32
+ coordinator?: PersistedCollectionCoordinator
33
+ schemaMismatchPolicy?: CapacitorSQLiteSchemaMismatchPolicy
34
+ }
35
+
36
+ export type CapacitorSQLitePersistenceOptions =
37
+ CapacitorSQLitePersistenceBaseOptions
38
+
39
+ function normalizeSchemaMismatchPolicy(
40
+ policy: CapacitorSQLiteSchemaMismatchPolicy,
41
+ ): CapacitorSQLiteCoreSchemaMismatchPolicy {
42
+ if (policy === `throw`) {
43
+ return `sync-absent-error`
44
+ }
45
+
46
+ return policy
47
+ }
48
+
49
+ function resolveSchemaMismatchPolicy(
50
+ explicitPolicy: CapacitorSQLiteSchemaMismatchPolicy | undefined,
51
+ mode: PersistedCollectionMode,
52
+ ): CapacitorSQLiteCoreSchemaMismatchPolicy {
53
+ if (explicitPolicy) {
54
+ return normalizeSchemaMismatchPolicy(explicitPolicy)
55
+ }
56
+
57
+ return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`
58
+ }
59
+
60
+ function createAdapterCacheKey(
61
+ schemaMismatchPolicy: CapacitorSQLiteCoreSchemaMismatchPolicy,
62
+ schemaVersion: number | undefined,
63
+ ): string {
64
+ const schemaVersionKey =
65
+ schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`
66
+ return `${schemaMismatchPolicy}|${schemaVersionKey}`
67
+ }
68
+
69
+ function createInternalSQLiteDriver(
70
+ options: CapacitorSQLitePersistenceOptions,
71
+ ): SQLiteDriver {
72
+ return new CapacitorSQLiteDriver({
73
+ database: options.database,
74
+ })
75
+ }
76
+
77
+ function resolveAdapterBaseOptions(
78
+ options: CapacitorSQLitePersistenceOptions,
79
+ ): Omit<
80
+ SQLiteCoreAdapterOptions,
81
+ `driver` | `schemaVersion` | `schemaMismatchPolicy`
82
+ > {
83
+ return {
84
+ appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,
85
+ appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,
86
+ pullSinceReloadThreshold: options.pullSinceReloadThreshold,
87
+ }
88
+ }
89
+
90
+ export function createCapacitorSQLitePersistence<
91
+ T extends object,
92
+ TKey extends string | number = string | number,
93
+ >(
94
+ options: CapacitorSQLitePersistenceOptions,
95
+ ): PersistedCollectionPersistence<T, TKey> {
96
+ const { coordinator, schemaMismatchPolicy } = options
97
+ const driver = createInternalSQLiteDriver(options)
98
+ const adapterBaseOptions = resolveAdapterBaseOptions(options)
99
+ const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator()
100
+ const adapterCache = new Map<
101
+ string,
102
+ ReturnType<
103
+ typeof createSQLiteCorePersistenceAdapter<
104
+ Record<string, unknown>,
105
+ string | number
106
+ >
107
+ >
108
+ >()
109
+
110
+ const getAdapterForCollection = (
111
+ mode: PersistedCollectionMode,
112
+ schemaVersion: number | undefined,
113
+ ) => {
114
+ const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(
115
+ schemaMismatchPolicy,
116
+ mode,
117
+ )
118
+ const cacheKey = createAdapterCacheKey(
119
+ resolvedSchemaMismatchPolicy,
120
+ schemaVersion,
121
+ )
122
+ const cachedAdapter = adapterCache.get(cacheKey)
123
+ if (cachedAdapter) {
124
+ return cachedAdapter
125
+ }
126
+
127
+ const adapter = createSQLiteCorePersistenceAdapter<
128
+ Record<string, unknown>,
129
+ string | number
130
+ >({
131
+ ...adapterBaseOptions,
132
+ driver,
133
+ schemaMismatchPolicy: resolvedSchemaMismatchPolicy,
134
+ ...(schemaVersion === undefined ? {} : { schemaVersion }),
135
+ })
136
+ adapterCache.set(cacheKey, adapter)
137
+ return adapter
138
+ }
139
+
140
+ const createCollectionPersistence = (
141
+ mode: PersistedCollectionMode,
142
+ schemaVersion: number | undefined,
143
+ ): PersistedCollectionPersistence<T, TKey> => ({
144
+ adapter: getAdapterForCollection(
145
+ mode,
146
+ schemaVersion,
147
+ ) as unknown as PersistedCollectionPersistence<T, TKey>[`adapter`],
148
+ coordinator: resolvedCoordinator,
149
+ })
150
+
151
+ const defaultPersistence = createCollectionPersistence(
152
+ `sync-absent`,
153
+ undefined,
154
+ )
155
+
156
+ return {
157
+ ...defaultPersistence,
158
+ resolvePersistenceForCollection: ({ mode, schemaVersion }) =>
159
+ createCollectionPersistence(mode, schemaVersion),
160
+ resolvePersistenceForMode: (mode) =>
161
+ createCollectionPersistence(mode, undefined),
162
+ }
163
+ }