@tanstack/expo-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.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # @tanstack/expo-db-sqlite-persistence
2
+
3
+ Thin SQLite persistence for Expo apps using the official `expo-sqlite` adapter.
4
+
5
+ ## Public API
6
+
7
+ - `createExpoSQLitePersistence(...)`
8
+ - `persistedCollectionOptions(...)` (re-exported from core)
9
+ - Advanced driver entrypoint:
10
+ `@tanstack/expo-db-sqlite-persistence/expo-sqlite-driver`
11
+
12
+ ## Quick start
13
+
14
+ ```ts
15
+ import * as SQLite from 'expo-sqlite'
16
+ import { createCollection } from '@tanstack/db'
17
+ import {
18
+ createExpoSQLitePersistence,
19
+ persistedCollectionOptions,
20
+ } from '@tanstack/expo-db-sqlite-persistence'
21
+
22
+ type Todo = {
23
+ id: string
24
+ title: string
25
+ completed: boolean
26
+ }
27
+
28
+ const database = await SQLite.openDatabaseAsync(`tanstack-db.sqlite`)
29
+
30
+ // One shared persistence instance for the whole database.
31
+ const persistence = createExpoSQLitePersistence({
32
+ database,
33
+ })
34
+
35
+ export const todosCollection = createCollection(
36
+ persistedCollectionOptions<Todo, string>({
37
+ id: `todos`,
38
+ getKey: (todo) => todo.id,
39
+ persistence,
40
+ schemaVersion: 1, // Per-collection schema version
41
+ }),
42
+ )
43
+ ```
44
+
45
+ ## Notes
46
+
47
+ - This package targets the official `expo-sqlite` async database API.
48
+ - Requires `expo-sqlite` `^55.0.10` (documented as a peer dependency).
49
+ - `createExpoSQLitePersistence` is shared across collections.
50
+ - Mode defaults (`sync-present` vs `sync-absent`) are inferred from whether a
51
+ `sync` config is present in `persistedCollectionOptions`.
52
+ - The React Native `op-sqlite` wrapper remains available in
53
+ `@tanstack/react-native-db-sqlite-persistence`.
54
+ - Expo web is not part of the emulator-backed E2E path in this package. Use the
55
+ browser SQLite package for browser-focused persistence coverage.
56
+
57
+ ## E2E
58
+
59
+ - `pnpm --filter @tanstack/expo-db-sqlite-persistence test:e2e`
60
+ runs the shared Node-backed conformance suite.
61
+ - `pnpm --filter @tanstack/expo-db-sqlite-persistence test:e2e:expo:ios`
62
+ runs the real Expo iOS Simulator path.
63
+ - `pnpm --filter @tanstack/expo-db-sqlite-persistence test:e2e:expo:android`
64
+ runs the real Expo Android Emulator path.
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
4
+ function hasExistingDatabase(options) {
5
+ return `database` in options;
6
+ }
7
+ function assertTransactionCallbackHasDriverArg(fn) {
8
+ if (fn.length > 0) {
9
+ return;
10
+ }
11
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
12
+ `SQLiteDriver.transaction callback must accept the transaction driver argument`
13
+ );
14
+ }
15
+ function isExpoSQLiteDatabaseLike(value) {
16
+ return typeof value === `object` && value !== null && typeof value.execAsync === `function` && typeof value.getAllAsync === `function` && typeof value.runAsync === `function` && typeof value.withExclusiveTransactionAsync === `function`;
17
+ }
18
+ class ExpoSQLiteDriver {
19
+ constructor(options) {
20
+ this.queue = Promise.resolve();
21
+ this.nextSavepointId = 1;
22
+ if (hasExistingDatabase(options)) {
23
+ if (!isExpoSQLiteDatabaseLike(options.database)) {
24
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
25
+ `Expo SQLite database must provide execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`
26
+ );
27
+ }
28
+ this.databasePromise = Promise.resolve(options.database);
29
+ this.ownsDatabase = false;
30
+ return;
31
+ }
32
+ this.databasePromise = Promise.resolve(options.openDatabase()).then(
33
+ (database) => {
34
+ if (!isExpoSQLiteDatabaseLike(database)) {
35
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
36
+ `Expo SQLite openDatabase() must resolve a database with execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`
37
+ );
38
+ }
39
+ return database;
40
+ }
41
+ );
42
+ this.ownsDatabase = true;
43
+ }
44
+ async exec(sql) {
45
+ await this.enqueue(async () => {
46
+ const database = await this.getDatabase();
47
+ await database.execAsync(sql);
48
+ });
49
+ }
50
+ async query(sql, params = []) {
51
+ return this.enqueue(async () => {
52
+ const database = await this.getDatabase();
53
+ return database.getAllAsync(sql, normalizeParams(params));
54
+ });
55
+ }
56
+ async run(sql, params = []) {
57
+ await this.enqueue(async () => {
58
+ const database = await this.getDatabase();
59
+ await database.runAsync(sql, normalizeParams(params));
60
+ });
61
+ }
62
+ async transaction(fn) {
63
+ assertTransactionCallbackHasDriverArg(fn);
64
+ return this.transactionWithDriver(fn);
65
+ }
66
+ async transactionWithDriver(fn) {
67
+ return this.enqueue(async () => {
68
+ const database = await this.getDatabase();
69
+ return database.withExclusiveTransactionAsync(async (transaction) => {
70
+ const transactionDriver = this.createTransactionDriver(transaction);
71
+ return fn(transactionDriver);
72
+ });
73
+ });
74
+ }
75
+ async close() {
76
+ const database = await this.getDatabase();
77
+ if (!this.ownsDatabase || typeof database.closeAsync !== `function`) {
78
+ return;
79
+ }
80
+ await database.closeAsync();
81
+ }
82
+ async getDatabase() {
83
+ return this.databasePromise;
84
+ }
85
+ enqueue(operation) {
86
+ const queuedOperation = this.queue.then(operation, operation);
87
+ this.queue = queuedOperation.then(
88
+ () => void 0,
89
+ () => void 0
90
+ );
91
+ return queuedOperation;
92
+ }
93
+ createTransactionDriver(transaction) {
94
+ const transactionDriver = {
95
+ exec: async (sql) => {
96
+ await transaction.execAsync(sql);
97
+ },
98
+ query: async (sql, params = []) => {
99
+ return transaction.getAllAsync(sql, normalizeParams(params));
100
+ },
101
+ run: async (sql, params = []) => {
102
+ await transaction.runAsync(sql, normalizeParams(params));
103
+ },
104
+ transaction: async (fn) => {
105
+ assertTransactionCallbackHasDriverArg(fn);
106
+ return this.runNestedTransaction(transaction, transactionDriver, fn);
107
+ },
108
+ transactionWithDriver: async (fn) => this.runNestedTransaction(transaction, transactionDriver, fn)
109
+ };
110
+ return transactionDriver;
111
+ }
112
+ async runNestedTransaction(transaction, transactionDriver, fn) {
113
+ const savepointName = `tsdb_sp_${this.nextSavepointId}`;
114
+ this.nextSavepointId++;
115
+ await transaction.execAsync(`SAVEPOINT ${savepointName}`);
116
+ try {
117
+ const result = await fn(transactionDriver);
118
+ await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`);
119
+ return result;
120
+ } catch (error) {
121
+ await transaction.execAsync(`ROLLBACK TO SAVEPOINT ${savepointName}`);
122
+ await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`);
123
+ throw error;
124
+ }
125
+ }
126
+ }
127
+ function normalizeParams(params) {
128
+ return params.length > 0 ? [...params] : void 0;
129
+ }
130
+ exports.ExpoSQLiteDriver = ExpoSQLiteDriver;
131
+ //# sourceMappingURL=expo-sqlite-driver.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo-sqlite-driver.cjs","sources":["../../src/expo-sqlite-driver.ts"],"sourcesContent":["import { InvalidPersistedCollectionConfigError } from '@tanstack/db-sqlite-persistence-core'\nimport type { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core'\n\nexport type ExpoSQLiteBindParams =\n | ReadonlyArray<unknown>\n | Record<string, unknown>\n\nexport type ExpoSQLiteRunResult = {\n changes: number\n lastInsertRowId: number\n}\n\nexport type ExpoSQLiteQueryable = {\n execAsync: (sql: string) => Promise<void>\n getAllAsync: <T>(\n sql: string,\n params?: ExpoSQLiteBindParams,\n ) => Promise<ReadonlyArray<T>>\n runAsync: (\n sql: string,\n params?: ExpoSQLiteBindParams,\n ) => Promise<ExpoSQLiteRunResult>\n}\n\nexport type ExpoSQLiteTransaction = ExpoSQLiteQueryable\n\nexport type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {\n withExclusiveTransactionAsync: <T>(\n task: (transaction: ExpoSQLiteTransaction) => Promise<T>,\n ) => Promise<T>\n closeAsync?: () => Promise<void>\n}\n\ntype ExpoSQLiteExistingDatabaseOptions = {\n database: ExpoSQLiteDatabaseLike\n}\n\ntype ExpoSQLiteOpenDatabaseOptions = {\n openDatabase: () => Promise<ExpoSQLiteDatabaseLike> | ExpoSQLiteDatabaseLike\n}\n\nexport type ExpoSQLiteDriverOptions =\n | ExpoSQLiteExistingDatabaseOptions\n | ExpoSQLiteOpenDatabaseOptions\n\nfunction hasExistingDatabase(\n options: ExpoSQLiteDriverOptions,\n): options is ExpoSQLiteExistingDatabaseOptions {\n return `database` in options\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 isExpoSQLiteDatabaseLike(\n value: unknown,\n): value is ExpoSQLiteDatabaseLike {\n return (\n typeof value === `object` &&\n value !== null &&\n typeof (value as ExpoSQLiteDatabaseLike).execAsync === `function` &&\n typeof (value as ExpoSQLiteDatabaseLike).getAllAsync === `function` &&\n typeof (value as ExpoSQLiteDatabaseLike).runAsync === `function` &&\n typeof (value as ExpoSQLiteDatabaseLike).withExclusiveTransactionAsync ===\n `function`\n )\n}\n\nexport class ExpoSQLiteDriver implements SQLiteDriver {\n private readonly databasePromise: Promise<ExpoSQLiteDatabaseLike>\n private readonly ownsDatabase: boolean\n private queue: Promise<void> = Promise.resolve()\n private nextSavepointId = 1\n\n constructor(options: ExpoSQLiteDriverOptions) {\n if (hasExistingDatabase(options)) {\n if (!isExpoSQLiteDatabaseLike(options.database)) {\n throw new InvalidPersistedCollectionConfigError(\n `Expo SQLite database must provide execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`,\n )\n }\n\n this.databasePromise = Promise.resolve(options.database)\n this.ownsDatabase = false\n return\n }\n\n this.databasePromise = Promise.resolve(options.openDatabase()).then(\n (database) => {\n if (!isExpoSQLiteDatabaseLike(database)) {\n throw new InvalidPersistedCollectionConfigError(\n `Expo SQLite openDatabase() must resolve a database with execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`,\n )\n }\n\n return database\n },\n )\n this.ownsDatabase = true\n }\n\n async exec(sql: string): Promise<void> {\n await this.enqueue(async () => {\n const database = await this.getDatabase()\n await database.execAsync(sql)\n })\n }\n\n async query<T>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<ReadonlyArray<T>> {\n return this.enqueue(async () => {\n const database = await this.getDatabase()\n return database.getAllAsync<T>(sql, normalizeParams(params))\n })\n }\n\n async run(sql: string, params: ReadonlyArray<unknown> = []): Promise<void> {\n await this.enqueue(async () => {\n const database = await this.getDatabase()\n await database.runAsync(sql, normalizeParams(params))\n })\n }\n\n async transaction<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n assertTransactionCallbackHasDriverArg(fn)\n return this.transactionWithDriver(fn)\n }\n\n async transactionWithDriver<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n return this.enqueue(async () => {\n const database = await this.getDatabase()\n return database.withExclusiveTransactionAsync(async (transaction) => {\n const transactionDriver = this.createTransactionDriver(transaction)\n return fn(transactionDriver)\n })\n })\n }\n\n async close(): Promise<void> {\n const database = await this.getDatabase()\n if (!this.ownsDatabase || typeof database.closeAsync !== `function`) {\n return\n }\n\n await database.closeAsync()\n }\n\n async getDatabase(): Promise<ExpoSQLiteDatabaseLike> {\n return this.databasePromise\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 createTransactionDriver(\n transaction: ExpoSQLiteTransaction,\n ): SQLiteDriver {\n const transactionDriver: SQLiteDriver = {\n exec: async (sql) => {\n await transaction.execAsync(sql)\n },\n query: async <T>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<ReadonlyArray<T>> => {\n return transaction.getAllAsync<T>(sql, normalizeParams(params))\n },\n run: async (sql: string, params: ReadonlyArray<unknown> = []) => {\n await transaction.runAsync(sql, normalizeParams(params))\n },\n transaction: async <T>(\n fn: (nestedTransactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> => {\n assertTransactionCallbackHasDriverArg(fn)\n return this.runNestedTransaction(transaction, transactionDriver, fn)\n },\n transactionWithDriver: async <T>(\n fn: (nestedTransactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> =>\n this.runNestedTransaction(transaction, transactionDriver, fn),\n }\n\n return transactionDriver\n }\n\n private async runNestedTransaction<T>(\n transaction: ExpoSQLiteTransaction,\n transactionDriver: SQLiteDriver,\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n const savepointName = `tsdb_sp_${this.nextSavepointId}`\n this.nextSavepointId++\n await transaction.execAsync(`SAVEPOINT ${savepointName}`)\n\n try {\n const result = await fn(transactionDriver)\n await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`)\n return result\n } catch (error) {\n await transaction.execAsync(`ROLLBACK TO SAVEPOINT ${savepointName}`)\n await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`)\n throw error\n }\n }\n}\n\nfunction normalizeParams(\n params: ReadonlyArray<unknown>,\n): ExpoSQLiteBindParams | undefined {\n return params.length > 0 ? [...params] : undefined\n}\n\nexport function createExpoSQLiteDriver(\n options: ExpoSQLiteDriverOptions,\n): ExpoSQLiteDriver {\n return new ExpoSQLiteDriver(options)\n}\n"],"names":["InvalidPersistedCollectionConfigError"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core';
2
+ export type ExpoSQLiteBindParams = ReadonlyArray<unknown> | Record<string, unknown>;
3
+ export type ExpoSQLiteRunResult = {
4
+ changes: number;
5
+ lastInsertRowId: number;
6
+ };
7
+ export type ExpoSQLiteQueryable = {
8
+ execAsync: (sql: string) => Promise<void>;
9
+ getAllAsync: <T>(sql: string, params?: ExpoSQLiteBindParams) => Promise<ReadonlyArray<T>>;
10
+ runAsync: (sql: string, params?: ExpoSQLiteBindParams) => Promise<ExpoSQLiteRunResult>;
11
+ };
12
+ export type ExpoSQLiteTransaction = ExpoSQLiteQueryable;
13
+ export type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {
14
+ withExclusiveTransactionAsync: <T>(task: (transaction: ExpoSQLiteTransaction) => Promise<T>) => Promise<T>;
15
+ closeAsync?: () => Promise<void>;
16
+ };
17
+ type ExpoSQLiteExistingDatabaseOptions = {
18
+ database: ExpoSQLiteDatabaseLike;
19
+ };
20
+ type ExpoSQLiteOpenDatabaseOptions = {
21
+ openDatabase: () => Promise<ExpoSQLiteDatabaseLike> | ExpoSQLiteDatabaseLike;
22
+ };
23
+ export type ExpoSQLiteDriverOptions = ExpoSQLiteExistingDatabaseOptions | ExpoSQLiteOpenDatabaseOptions;
24
+ export declare class ExpoSQLiteDriver implements SQLiteDriver {
25
+ private readonly databasePromise;
26
+ private readonly ownsDatabase;
27
+ private queue;
28
+ private nextSavepointId;
29
+ constructor(options: ExpoSQLiteDriverOptions);
30
+ exec(sql: string): Promise<void>;
31
+ query<T>(sql: string, params?: ReadonlyArray<unknown>): Promise<ReadonlyArray<T>>;
32
+ run(sql: string, params?: ReadonlyArray<unknown>): Promise<void>;
33
+ transaction<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T>;
34
+ transactionWithDriver<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T>;
35
+ close(): Promise<void>;
36
+ getDatabase(): Promise<ExpoSQLiteDatabaseLike>;
37
+ private enqueue;
38
+ private createTransactionDriver;
39
+ private runNestedTransaction;
40
+ }
41
+ export declare function createExpoSQLiteDriver(options: ExpoSQLiteDriverOptions): ExpoSQLiteDriver;
42
+ export {};
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
4
+ const expoSqliteDriver = require("./expo-sqlite-driver.cjs");
5
+ function normalizeSchemaMismatchPolicy(policy) {
6
+ if (policy === `throw`) {
7
+ return `sync-absent-error`;
8
+ }
9
+ return policy;
10
+ }
11
+ function resolveSchemaMismatchPolicy(explicitPolicy, mode) {
12
+ if (explicitPolicy) {
13
+ return normalizeSchemaMismatchPolicy(explicitPolicy);
14
+ }
15
+ return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`;
16
+ }
17
+ function createAdapterCacheKey(schemaMismatchPolicy, schemaVersion) {
18
+ const schemaVersionKey = schemaVersion === void 0 ? `schema:default` : `schema:${schemaVersion}`;
19
+ return `${schemaMismatchPolicy}|${schemaVersionKey}`;
20
+ }
21
+ function createInternalSQLiteDriver(options) {
22
+ return new expoSqliteDriver.ExpoSQLiteDriver({
23
+ database: options.database
24
+ });
25
+ }
26
+ function resolveAdapterBaseOptions(options) {
27
+ return {
28
+ appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,
29
+ appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,
30
+ pullSinceReloadThreshold: options.pullSinceReloadThreshold
31
+ };
32
+ }
33
+ function createExpoSQLitePersistence(options) {
34
+ const { coordinator, schemaMismatchPolicy } = options;
35
+ const driver = createInternalSQLiteDriver(options);
36
+ const adapterBaseOptions = resolveAdapterBaseOptions(options);
37
+ const resolvedCoordinator = coordinator ?? new dbSqlitePersistenceCore.SingleProcessCoordinator();
38
+ const adapterCache = /* @__PURE__ */ new Map();
39
+ const getAdapterForCollection = (mode, schemaVersion) => {
40
+ const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(
41
+ schemaMismatchPolicy,
42
+ mode
43
+ );
44
+ const cacheKey = createAdapterCacheKey(
45
+ resolvedSchemaMismatchPolicy,
46
+ schemaVersion
47
+ );
48
+ const cachedAdapter = adapterCache.get(cacheKey);
49
+ if (cachedAdapter) {
50
+ return cachedAdapter;
51
+ }
52
+ const adapter = dbSqlitePersistenceCore.createSQLiteCorePersistenceAdapter({
53
+ ...adapterBaseOptions,
54
+ driver,
55
+ schemaMismatchPolicy: resolvedSchemaMismatchPolicy,
56
+ ...schemaVersion === void 0 ? {} : { schemaVersion }
57
+ });
58
+ adapterCache.set(cacheKey, adapter);
59
+ return adapter;
60
+ };
61
+ const createCollectionPersistence = (mode, schemaVersion) => ({
62
+ adapter: getAdapterForCollection(
63
+ mode,
64
+ schemaVersion
65
+ ),
66
+ coordinator: resolvedCoordinator
67
+ });
68
+ const defaultPersistence = createCollectionPersistence(
69
+ `sync-absent`,
70
+ void 0
71
+ );
72
+ return {
73
+ ...defaultPersistence,
74
+ resolvePersistenceForCollection: ({ mode, schemaVersion }) => createCollectionPersistence(mode, schemaVersion),
75
+ resolvePersistenceForMode: (mode) => createCollectionPersistence(mode, void 0)
76
+ };
77
+ }
78
+ exports.createExpoSQLitePersistence = createExpoSQLitePersistence;
79
+ //# sourceMappingURL=expo.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo.cjs","sources":["../../src/expo.ts"],"sourcesContent":["import {\n SingleProcessCoordinator,\n createSQLiteCorePersistenceAdapter,\n} from '@tanstack/db-sqlite-persistence-core'\nimport { ExpoSQLiteDriver } from './expo-sqlite-driver'\nimport type {\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n SQLiteCoreAdapterOptions,\n SQLiteDriver,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver'\n\nexport type { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver'\n\ntype ExpoSQLiteCoreSchemaMismatchPolicy =\n | `sync-present-reset`\n | `sync-absent-error`\n | `reset`\n\nexport type ExpoSQLiteSchemaMismatchPolicy =\n | ExpoSQLiteCoreSchemaMismatchPolicy\n | `throw`\n\nexport type ExpoSQLitePersistenceOptions = Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> & {\n database: ExpoSQLiteDatabaseLike\n coordinator?: PersistedCollectionCoordinator\n schemaMismatchPolicy?: ExpoSQLiteSchemaMismatchPolicy\n}\n\nfunction normalizeSchemaMismatchPolicy(\n policy: ExpoSQLiteSchemaMismatchPolicy,\n): ExpoSQLiteCoreSchemaMismatchPolicy {\n if (policy === `throw`) {\n return `sync-absent-error`\n }\n\n return policy\n}\n\nfunction resolveSchemaMismatchPolicy(\n explicitPolicy: ExpoSQLiteSchemaMismatchPolicy | undefined,\n mode: PersistedCollectionMode,\n): ExpoSQLiteCoreSchemaMismatchPolicy {\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: ExpoSQLiteCoreSchemaMismatchPolicy,\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: ExpoSQLitePersistenceOptions,\n): SQLiteDriver {\n return new ExpoSQLiteDriver({\n database: options.database,\n })\n}\n\nfunction resolveAdapterBaseOptions(\n options: ExpoSQLitePersistenceOptions,\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\nexport function createExpoSQLitePersistence<\n T extends object,\n TKey extends string | number = string | number,\n>(\n options: ExpoSQLitePersistenceOptions,\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 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":["ExpoSQLiteDriver","SingleProcessCoordinator","createSQLiteCorePersistenceAdapter"],"mappings":";;;;AAkCA,SAAS,8BACP,QACoC;AACpC,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,4BACP,gBACA,MACoC;AACpC,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,iBAAAA,iBAAiB;AAAA,IAC1B,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;AAEO,SAAS,4BAId,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;AAClC,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,11 @@
1
+ import { PersistedCollectionCoordinator, PersistedCollectionPersistence, SQLiteCoreAdapterOptions } from '@tanstack/db-sqlite-persistence-core';
2
+ import { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver.cjs';
3
+ export type { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver.cjs';
4
+ type ExpoSQLiteCoreSchemaMismatchPolicy = `sync-present-reset` | `sync-absent-error` | `reset`;
5
+ export type ExpoSQLiteSchemaMismatchPolicy = ExpoSQLiteCoreSchemaMismatchPolicy | `throw`;
6
+ export type ExpoSQLitePersistenceOptions = Omit<SQLiteCoreAdapterOptions, `driver` | `schemaVersion` | `schemaMismatchPolicy`> & {
7
+ database: ExpoSQLiteDatabaseLike;
8
+ coordinator?: PersistedCollectionCoordinator;
9
+ schemaMismatchPolicy?: ExpoSQLiteSchemaMismatchPolicy;
10
+ };
11
+ export declare function createExpoSQLitePersistence<T extends object, TKey extends string | number = string | number>(options: ExpoSQLitePersistenceOptions): PersistedCollectionPersistence<T, TKey>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const expo = require("./expo.cjs");
4
+ const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
5
+ exports.createExpoSQLitePersistence = expo.createExpoSQLitePersistence;
6
+ Object.defineProperty(exports, "persistedCollectionOptions", {
7
+ enumerable: true,
8
+ get: () => dbSqlitePersistenceCore.persistedCollectionOptions
9
+ });
10
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
@@ -0,0 +1,4 @@
1
+ export { createExpoSQLitePersistence } from './expo.cjs';
2
+ export type { ExpoSQLiteDatabaseLike, ExpoSQLitePersistenceOptions, ExpoSQLiteSchemaMismatchPolicy, } from './expo.cjs';
3
+ export { persistedCollectionOptions } from '@tanstack/db-sqlite-persistence-core';
4
+ export type { PersistedCollectionCoordinator, PersistedCollectionPersistence, } from '@tanstack/db-sqlite-persistence-core';
@@ -0,0 +1,42 @@
1
+ import { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core';
2
+ export type ExpoSQLiteBindParams = ReadonlyArray<unknown> | Record<string, unknown>;
3
+ export type ExpoSQLiteRunResult = {
4
+ changes: number;
5
+ lastInsertRowId: number;
6
+ };
7
+ export type ExpoSQLiteQueryable = {
8
+ execAsync: (sql: string) => Promise<void>;
9
+ getAllAsync: <T>(sql: string, params?: ExpoSQLiteBindParams) => Promise<ReadonlyArray<T>>;
10
+ runAsync: (sql: string, params?: ExpoSQLiteBindParams) => Promise<ExpoSQLiteRunResult>;
11
+ };
12
+ export type ExpoSQLiteTransaction = ExpoSQLiteQueryable;
13
+ export type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {
14
+ withExclusiveTransactionAsync: <T>(task: (transaction: ExpoSQLiteTransaction) => Promise<T>) => Promise<T>;
15
+ closeAsync?: () => Promise<void>;
16
+ };
17
+ type ExpoSQLiteExistingDatabaseOptions = {
18
+ database: ExpoSQLiteDatabaseLike;
19
+ };
20
+ type ExpoSQLiteOpenDatabaseOptions = {
21
+ openDatabase: () => Promise<ExpoSQLiteDatabaseLike> | ExpoSQLiteDatabaseLike;
22
+ };
23
+ export type ExpoSQLiteDriverOptions = ExpoSQLiteExistingDatabaseOptions | ExpoSQLiteOpenDatabaseOptions;
24
+ export declare class ExpoSQLiteDriver implements SQLiteDriver {
25
+ private readonly databasePromise;
26
+ private readonly ownsDatabase;
27
+ private queue;
28
+ private nextSavepointId;
29
+ constructor(options: ExpoSQLiteDriverOptions);
30
+ exec(sql: string): Promise<void>;
31
+ query<T>(sql: string, params?: ReadonlyArray<unknown>): Promise<ReadonlyArray<T>>;
32
+ run(sql: string, params?: ReadonlyArray<unknown>): Promise<void>;
33
+ transaction<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T>;
34
+ transactionWithDriver<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T>;
35
+ close(): Promise<void>;
36
+ getDatabase(): Promise<ExpoSQLiteDatabaseLike>;
37
+ private enqueue;
38
+ private createTransactionDriver;
39
+ private runNestedTransaction;
40
+ }
41
+ export declare function createExpoSQLiteDriver(options: ExpoSQLiteDriverOptions): ExpoSQLiteDriver;
42
+ export {};
@@ -0,0 +1,131 @@
1
+ import { InvalidPersistedCollectionConfigError } from "@tanstack/db-sqlite-persistence-core";
2
+ function hasExistingDatabase(options) {
3
+ return `database` in options;
4
+ }
5
+ function assertTransactionCallbackHasDriverArg(fn) {
6
+ if (fn.length > 0) {
7
+ return;
8
+ }
9
+ throw new InvalidPersistedCollectionConfigError(
10
+ `SQLiteDriver.transaction callback must accept the transaction driver argument`
11
+ );
12
+ }
13
+ function isExpoSQLiteDatabaseLike(value) {
14
+ return typeof value === `object` && value !== null && typeof value.execAsync === `function` && typeof value.getAllAsync === `function` && typeof value.runAsync === `function` && typeof value.withExclusiveTransactionAsync === `function`;
15
+ }
16
+ class ExpoSQLiteDriver {
17
+ constructor(options) {
18
+ this.queue = Promise.resolve();
19
+ this.nextSavepointId = 1;
20
+ if (hasExistingDatabase(options)) {
21
+ if (!isExpoSQLiteDatabaseLike(options.database)) {
22
+ throw new InvalidPersistedCollectionConfigError(
23
+ `Expo SQLite database must provide execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`
24
+ );
25
+ }
26
+ this.databasePromise = Promise.resolve(options.database);
27
+ this.ownsDatabase = false;
28
+ return;
29
+ }
30
+ this.databasePromise = Promise.resolve(options.openDatabase()).then(
31
+ (database) => {
32
+ if (!isExpoSQLiteDatabaseLike(database)) {
33
+ throw new InvalidPersistedCollectionConfigError(
34
+ `Expo SQLite openDatabase() must resolve a database with execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`
35
+ );
36
+ }
37
+ return database;
38
+ }
39
+ );
40
+ this.ownsDatabase = true;
41
+ }
42
+ async exec(sql) {
43
+ await this.enqueue(async () => {
44
+ const database = await this.getDatabase();
45
+ await database.execAsync(sql);
46
+ });
47
+ }
48
+ async query(sql, params = []) {
49
+ return this.enqueue(async () => {
50
+ const database = await this.getDatabase();
51
+ return database.getAllAsync(sql, normalizeParams(params));
52
+ });
53
+ }
54
+ async run(sql, params = []) {
55
+ await this.enqueue(async () => {
56
+ const database = await this.getDatabase();
57
+ await database.runAsync(sql, normalizeParams(params));
58
+ });
59
+ }
60
+ async transaction(fn) {
61
+ assertTransactionCallbackHasDriverArg(fn);
62
+ return this.transactionWithDriver(fn);
63
+ }
64
+ async transactionWithDriver(fn) {
65
+ return this.enqueue(async () => {
66
+ const database = await this.getDatabase();
67
+ return database.withExclusiveTransactionAsync(async (transaction) => {
68
+ const transactionDriver = this.createTransactionDriver(transaction);
69
+ return fn(transactionDriver);
70
+ });
71
+ });
72
+ }
73
+ async close() {
74
+ const database = await this.getDatabase();
75
+ if (!this.ownsDatabase || typeof database.closeAsync !== `function`) {
76
+ return;
77
+ }
78
+ await database.closeAsync();
79
+ }
80
+ async getDatabase() {
81
+ return this.databasePromise;
82
+ }
83
+ enqueue(operation) {
84
+ const queuedOperation = this.queue.then(operation, operation);
85
+ this.queue = queuedOperation.then(
86
+ () => void 0,
87
+ () => void 0
88
+ );
89
+ return queuedOperation;
90
+ }
91
+ createTransactionDriver(transaction) {
92
+ const transactionDriver = {
93
+ exec: async (sql) => {
94
+ await transaction.execAsync(sql);
95
+ },
96
+ query: async (sql, params = []) => {
97
+ return transaction.getAllAsync(sql, normalizeParams(params));
98
+ },
99
+ run: async (sql, params = []) => {
100
+ await transaction.runAsync(sql, normalizeParams(params));
101
+ },
102
+ transaction: async (fn) => {
103
+ assertTransactionCallbackHasDriverArg(fn);
104
+ return this.runNestedTransaction(transaction, transactionDriver, fn);
105
+ },
106
+ transactionWithDriver: async (fn) => this.runNestedTransaction(transaction, transactionDriver, fn)
107
+ };
108
+ return transactionDriver;
109
+ }
110
+ async runNestedTransaction(transaction, transactionDriver, fn) {
111
+ const savepointName = `tsdb_sp_${this.nextSavepointId}`;
112
+ this.nextSavepointId++;
113
+ await transaction.execAsync(`SAVEPOINT ${savepointName}`);
114
+ try {
115
+ const result = await fn(transactionDriver);
116
+ await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`);
117
+ return result;
118
+ } catch (error) {
119
+ await transaction.execAsync(`ROLLBACK TO SAVEPOINT ${savepointName}`);
120
+ await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`);
121
+ throw error;
122
+ }
123
+ }
124
+ }
125
+ function normalizeParams(params) {
126
+ return params.length > 0 ? [...params] : void 0;
127
+ }
128
+ export {
129
+ ExpoSQLiteDriver
130
+ };
131
+ //# sourceMappingURL=expo-sqlite-driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo-sqlite-driver.js","sources":["../../src/expo-sqlite-driver.ts"],"sourcesContent":["import { InvalidPersistedCollectionConfigError } from '@tanstack/db-sqlite-persistence-core'\nimport type { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core'\n\nexport type ExpoSQLiteBindParams =\n | ReadonlyArray<unknown>\n | Record<string, unknown>\n\nexport type ExpoSQLiteRunResult = {\n changes: number\n lastInsertRowId: number\n}\n\nexport type ExpoSQLiteQueryable = {\n execAsync: (sql: string) => Promise<void>\n getAllAsync: <T>(\n sql: string,\n params?: ExpoSQLiteBindParams,\n ) => Promise<ReadonlyArray<T>>\n runAsync: (\n sql: string,\n params?: ExpoSQLiteBindParams,\n ) => Promise<ExpoSQLiteRunResult>\n}\n\nexport type ExpoSQLiteTransaction = ExpoSQLiteQueryable\n\nexport type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {\n withExclusiveTransactionAsync: <T>(\n task: (transaction: ExpoSQLiteTransaction) => Promise<T>,\n ) => Promise<T>\n closeAsync?: () => Promise<void>\n}\n\ntype ExpoSQLiteExistingDatabaseOptions = {\n database: ExpoSQLiteDatabaseLike\n}\n\ntype ExpoSQLiteOpenDatabaseOptions = {\n openDatabase: () => Promise<ExpoSQLiteDatabaseLike> | ExpoSQLiteDatabaseLike\n}\n\nexport type ExpoSQLiteDriverOptions =\n | ExpoSQLiteExistingDatabaseOptions\n | ExpoSQLiteOpenDatabaseOptions\n\nfunction hasExistingDatabase(\n options: ExpoSQLiteDriverOptions,\n): options is ExpoSQLiteExistingDatabaseOptions {\n return `database` in options\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 isExpoSQLiteDatabaseLike(\n value: unknown,\n): value is ExpoSQLiteDatabaseLike {\n return (\n typeof value === `object` &&\n value !== null &&\n typeof (value as ExpoSQLiteDatabaseLike).execAsync === `function` &&\n typeof (value as ExpoSQLiteDatabaseLike).getAllAsync === `function` &&\n typeof (value as ExpoSQLiteDatabaseLike).runAsync === `function` &&\n typeof (value as ExpoSQLiteDatabaseLike).withExclusiveTransactionAsync ===\n `function`\n )\n}\n\nexport class ExpoSQLiteDriver implements SQLiteDriver {\n private readonly databasePromise: Promise<ExpoSQLiteDatabaseLike>\n private readonly ownsDatabase: boolean\n private queue: Promise<void> = Promise.resolve()\n private nextSavepointId = 1\n\n constructor(options: ExpoSQLiteDriverOptions) {\n if (hasExistingDatabase(options)) {\n if (!isExpoSQLiteDatabaseLike(options.database)) {\n throw new InvalidPersistedCollectionConfigError(\n `Expo SQLite database must provide execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`,\n )\n }\n\n this.databasePromise = Promise.resolve(options.database)\n this.ownsDatabase = false\n return\n }\n\n this.databasePromise = Promise.resolve(options.openDatabase()).then(\n (database) => {\n if (!isExpoSQLiteDatabaseLike(database)) {\n throw new InvalidPersistedCollectionConfigError(\n `Expo SQLite openDatabase() must resolve a database with execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`,\n )\n }\n\n return database\n },\n )\n this.ownsDatabase = true\n }\n\n async exec(sql: string): Promise<void> {\n await this.enqueue(async () => {\n const database = await this.getDatabase()\n await database.execAsync(sql)\n })\n }\n\n async query<T>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<ReadonlyArray<T>> {\n return this.enqueue(async () => {\n const database = await this.getDatabase()\n return database.getAllAsync<T>(sql, normalizeParams(params))\n })\n }\n\n async run(sql: string, params: ReadonlyArray<unknown> = []): Promise<void> {\n await this.enqueue(async () => {\n const database = await this.getDatabase()\n await database.runAsync(sql, normalizeParams(params))\n })\n }\n\n async transaction<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n assertTransactionCallbackHasDriverArg(fn)\n return this.transactionWithDriver(fn)\n }\n\n async transactionWithDriver<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n return this.enqueue(async () => {\n const database = await this.getDatabase()\n return database.withExclusiveTransactionAsync(async (transaction) => {\n const transactionDriver = this.createTransactionDriver(transaction)\n return fn(transactionDriver)\n })\n })\n }\n\n async close(): Promise<void> {\n const database = await this.getDatabase()\n if (!this.ownsDatabase || typeof database.closeAsync !== `function`) {\n return\n }\n\n await database.closeAsync()\n }\n\n async getDatabase(): Promise<ExpoSQLiteDatabaseLike> {\n return this.databasePromise\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 createTransactionDriver(\n transaction: ExpoSQLiteTransaction,\n ): SQLiteDriver {\n const transactionDriver: SQLiteDriver = {\n exec: async (sql) => {\n await transaction.execAsync(sql)\n },\n query: async <T>(\n sql: string,\n params: ReadonlyArray<unknown> = [],\n ): Promise<ReadonlyArray<T>> => {\n return transaction.getAllAsync<T>(sql, normalizeParams(params))\n },\n run: async (sql: string, params: ReadonlyArray<unknown> = []) => {\n await transaction.runAsync(sql, normalizeParams(params))\n },\n transaction: async <T>(\n fn: (nestedTransactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> => {\n assertTransactionCallbackHasDriverArg(fn)\n return this.runNestedTransaction(transaction, transactionDriver, fn)\n },\n transactionWithDriver: async <T>(\n fn: (nestedTransactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> =>\n this.runNestedTransaction(transaction, transactionDriver, fn),\n }\n\n return transactionDriver\n }\n\n private async runNestedTransaction<T>(\n transaction: ExpoSQLiteTransaction,\n transactionDriver: SQLiteDriver,\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n const savepointName = `tsdb_sp_${this.nextSavepointId}`\n this.nextSavepointId++\n await transaction.execAsync(`SAVEPOINT ${savepointName}`)\n\n try {\n const result = await fn(transactionDriver)\n await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`)\n return result\n } catch (error) {\n await transaction.execAsync(`ROLLBACK TO SAVEPOINT ${savepointName}`)\n await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`)\n throw error\n }\n }\n}\n\nfunction normalizeParams(\n params: ReadonlyArray<unknown>,\n): ExpoSQLiteBindParams | undefined {\n return params.length > 0 ? [...params] : undefined\n}\n\nexport function createExpoSQLiteDriver(\n options: ExpoSQLiteDriverOptions,\n): ExpoSQLiteDriver {\n return new ExpoSQLiteDriver(options)\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { PersistedCollectionCoordinator, PersistedCollectionPersistence, SQLiteCoreAdapterOptions } from '@tanstack/db-sqlite-persistence-core';
2
+ import { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver.js';
3
+ export type { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver.js';
4
+ type ExpoSQLiteCoreSchemaMismatchPolicy = `sync-present-reset` | `sync-absent-error` | `reset`;
5
+ export type ExpoSQLiteSchemaMismatchPolicy = ExpoSQLiteCoreSchemaMismatchPolicy | `throw`;
6
+ export type ExpoSQLitePersistenceOptions = Omit<SQLiteCoreAdapterOptions, `driver` | `schemaVersion` | `schemaMismatchPolicy`> & {
7
+ database: ExpoSQLiteDatabaseLike;
8
+ coordinator?: PersistedCollectionCoordinator;
9
+ schemaMismatchPolicy?: ExpoSQLiteSchemaMismatchPolicy;
10
+ };
11
+ export declare function createExpoSQLitePersistence<T extends object, TKey extends string | number = string | number>(options: ExpoSQLitePersistenceOptions): PersistedCollectionPersistence<T, TKey>;
@@ -0,0 +1,79 @@
1
+ import { SingleProcessCoordinator, createSQLiteCorePersistenceAdapter } from "@tanstack/db-sqlite-persistence-core";
2
+ import { ExpoSQLiteDriver } from "./expo-sqlite-driver.js";
3
+ function normalizeSchemaMismatchPolicy(policy) {
4
+ if (policy === `throw`) {
5
+ return `sync-absent-error`;
6
+ }
7
+ return policy;
8
+ }
9
+ function resolveSchemaMismatchPolicy(explicitPolicy, mode) {
10
+ if (explicitPolicy) {
11
+ return normalizeSchemaMismatchPolicy(explicitPolicy);
12
+ }
13
+ return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`;
14
+ }
15
+ function createAdapterCacheKey(schemaMismatchPolicy, schemaVersion) {
16
+ const schemaVersionKey = schemaVersion === void 0 ? `schema:default` : `schema:${schemaVersion}`;
17
+ return `${schemaMismatchPolicy}|${schemaVersionKey}`;
18
+ }
19
+ function createInternalSQLiteDriver(options) {
20
+ return new ExpoSQLiteDriver({
21
+ database: options.database
22
+ });
23
+ }
24
+ function resolveAdapterBaseOptions(options) {
25
+ return {
26
+ appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,
27
+ appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,
28
+ pullSinceReloadThreshold: options.pullSinceReloadThreshold
29
+ };
30
+ }
31
+ function createExpoSQLitePersistence(options) {
32
+ const { coordinator, schemaMismatchPolicy } = options;
33
+ const driver = createInternalSQLiteDriver(options);
34
+ const adapterBaseOptions = resolveAdapterBaseOptions(options);
35
+ const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator();
36
+ const adapterCache = /* @__PURE__ */ new Map();
37
+ const getAdapterForCollection = (mode, schemaVersion) => {
38
+ const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(
39
+ schemaMismatchPolicy,
40
+ mode
41
+ );
42
+ const cacheKey = createAdapterCacheKey(
43
+ resolvedSchemaMismatchPolicy,
44
+ schemaVersion
45
+ );
46
+ const cachedAdapter = adapterCache.get(cacheKey);
47
+ if (cachedAdapter) {
48
+ return cachedAdapter;
49
+ }
50
+ const adapter = createSQLiteCorePersistenceAdapter({
51
+ ...adapterBaseOptions,
52
+ driver,
53
+ schemaMismatchPolicy: resolvedSchemaMismatchPolicy,
54
+ ...schemaVersion === void 0 ? {} : { schemaVersion }
55
+ });
56
+ adapterCache.set(cacheKey, adapter);
57
+ return adapter;
58
+ };
59
+ const createCollectionPersistence = (mode, schemaVersion) => ({
60
+ adapter: getAdapterForCollection(
61
+ mode,
62
+ schemaVersion
63
+ ),
64
+ coordinator: resolvedCoordinator
65
+ });
66
+ const defaultPersistence = createCollectionPersistence(
67
+ `sync-absent`,
68
+ void 0
69
+ );
70
+ return {
71
+ ...defaultPersistence,
72
+ resolvePersistenceForCollection: ({ mode, schemaVersion }) => createCollectionPersistence(mode, schemaVersion),
73
+ resolvePersistenceForMode: (mode) => createCollectionPersistence(mode, void 0)
74
+ };
75
+ }
76
+ export {
77
+ createExpoSQLitePersistence
78
+ };
79
+ //# sourceMappingURL=expo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo.js","sources":["../../src/expo.ts"],"sourcesContent":["import {\n SingleProcessCoordinator,\n createSQLiteCorePersistenceAdapter,\n} from '@tanstack/db-sqlite-persistence-core'\nimport { ExpoSQLiteDriver } from './expo-sqlite-driver'\nimport type {\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n SQLiteCoreAdapterOptions,\n SQLiteDriver,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver'\n\nexport type { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver'\n\ntype ExpoSQLiteCoreSchemaMismatchPolicy =\n | `sync-present-reset`\n | `sync-absent-error`\n | `reset`\n\nexport type ExpoSQLiteSchemaMismatchPolicy =\n | ExpoSQLiteCoreSchemaMismatchPolicy\n | `throw`\n\nexport type ExpoSQLitePersistenceOptions = Omit<\n SQLiteCoreAdapterOptions,\n `driver` | `schemaVersion` | `schemaMismatchPolicy`\n> & {\n database: ExpoSQLiteDatabaseLike\n coordinator?: PersistedCollectionCoordinator\n schemaMismatchPolicy?: ExpoSQLiteSchemaMismatchPolicy\n}\n\nfunction normalizeSchemaMismatchPolicy(\n policy: ExpoSQLiteSchemaMismatchPolicy,\n): ExpoSQLiteCoreSchemaMismatchPolicy {\n if (policy === `throw`) {\n return `sync-absent-error`\n }\n\n return policy\n}\n\nfunction resolveSchemaMismatchPolicy(\n explicitPolicy: ExpoSQLiteSchemaMismatchPolicy | undefined,\n mode: PersistedCollectionMode,\n): ExpoSQLiteCoreSchemaMismatchPolicy {\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: ExpoSQLiteCoreSchemaMismatchPolicy,\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: ExpoSQLitePersistenceOptions,\n): SQLiteDriver {\n return new ExpoSQLiteDriver({\n database: options.database,\n })\n}\n\nfunction resolveAdapterBaseOptions(\n options: ExpoSQLitePersistenceOptions,\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\nexport function createExpoSQLitePersistence<\n T extends object,\n TKey extends string | number = string | number,\n>(\n options: ExpoSQLitePersistenceOptions,\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 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":";;AAkCA,SAAS,8BACP,QACoC;AACpC,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,4BACP,gBACA,MACoC;AACpC,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,iBAAiB;AAAA,IAC1B,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;AAEO,SAAS,4BAId,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;AAClC,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,4 @@
1
+ export { createExpoSQLitePersistence } from './expo.js';
2
+ export type { ExpoSQLiteDatabaseLike, ExpoSQLitePersistenceOptions, ExpoSQLiteSchemaMismatchPolicy, } from './expo.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 { createExpoSQLitePersistence } from "./expo.js";
2
+ import { persistedCollectionOptions } from "@tanstack/db-sqlite-persistence-core";
3
+ export {
4
+ createExpoSQLitePersistence,
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/expo-db-sqlite-persistence",
3
+ "version": "0.1.2",
4
+ "description": "Expo 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/expo-db-sqlite-persistence"
11
+ },
12
+ "homepage": "https://tanstack.com/db",
13
+ "keywords": [
14
+ "sqlite",
15
+ "expo",
16
+ "expo-sqlite",
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/expo-db-sqlite-persistence build && vitest --config vitest.e2e.config.ts --run",
26
+ "test:e2e:expo:ios": "pnpm --filter @tanstack/db-ivm build && pnpm --filter @tanstack/db build && pnpm --filter @tanstack/db-sqlite-persistence-core build && pnpm --filter @tanstack/expo-db-sqlite-persistence build && TANSTACK_DB_MOBILE_REQUIRE_RUNTIME_FACTORY=1 TANSTACK_DB_MOBILE_SQLITE_FACTORY_MODULE=./tests/helpers/expo-emulator-database-factory.ts TANSTACK_DB_EXPO_RUNTIME_PLATFORM=ios vitest --config vitest.e2e.config.ts --run",
27
+ "test:e2e:expo:android": "pnpm --filter @tanstack/db-ivm build && pnpm --filter @tanstack/db build && pnpm --filter @tanstack/db-sqlite-persistence-core build && pnpm --filter @tanstack/expo-db-sqlite-persistence build && TANSTACK_DB_MOBILE_REQUIRE_RUNTIME_FACTORY=1 TANSTACK_DB_MOBILE_SQLITE_FACTORY_MODULE=./tests/helpers/expo-emulator-database-factory.ts TANSTACK_DB_EXPO_RUNTIME_PLATFORM=android vitest --config vitest.e2e.config.ts --run"
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
+ "./expo-sqlite-driver": {
45
+ "import": {
46
+ "types": "./dist/esm/expo-sqlite-driver.d.ts",
47
+ "default": "./dist/esm/expo-sqlite-driver.js"
48
+ },
49
+ "require": {
50
+ "types": "./dist/cjs/expo-sqlite-driver.d.cts",
51
+ "default": "./dist/cjs/expo-sqlite-driver.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
+ "expo-sqlite": "^55.0.10",
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,238 @@
1
+ import { InvalidPersistedCollectionConfigError } from '@tanstack/db-sqlite-persistence-core'
2
+ import type { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core'
3
+
4
+ export type ExpoSQLiteBindParams =
5
+ | ReadonlyArray<unknown>
6
+ | Record<string, unknown>
7
+
8
+ export type ExpoSQLiteRunResult = {
9
+ changes: number
10
+ lastInsertRowId: number
11
+ }
12
+
13
+ export type ExpoSQLiteQueryable = {
14
+ execAsync: (sql: string) => Promise<void>
15
+ getAllAsync: <T>(
16
+ sql: string,
17
+ params?: ExpoSQLiteBindParams,
18
+ ) => Promise<ReadonlyArray<T>>
19
+ runAsync: (
20
+ sql: string,
21
+ params?: ExpoSQLiteBindParams,
22
+ ) => Promise<ExpoSQLiteRunResult>
23
+ }
24
+
25
+ export type ExpoSQLiteTransaction = ExpoSQLiteQueryable
26
+
27
+ export type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {
28
+ withExclusiveTransactionAsync: <T>(
29
+ task: (transaction: ExpoSQLiteTransaction) => Promise<T>,
30
+ ) => Promise<T>
31
+ closeAsync?: () => Promise<void>
32
+ }
33
+
34
+ type ExpoSQLiteExistingDatabaseOptions = {
35
+ database: ExpoSQLiteDatabaseLike
36
+ }
37
+
38
+ type ExpoSQLiteOpenDatabaseOptions = {
39
+ openDatabase: () => Promise<ExpoSQLiteDatabaseLike> | ExpoSQLiteDatabaseLike
40
+ }
41
+
42
+ export type ExpoSQLiteDriverOptions =
43
+ | ExpoSQLiteExistingDatabaseOptions
44
+ | ExpoSQLiteOpenDatabaseOptions
45
+
46
+ function hasExistingDatabase(
47
+ options: ExpoSQLiteDriverOptions,
48
+ ): options is ExpoSQLiteExistingDatabaseOptions {
49
+ return `database` in options
50
+ }
51
+
52
+ function assertTransactionCallbackHasDriverArg(
53
+ fn: (transactionDriver: SQLiteDriver) => Promise<unknown>,
54
+ ): void {
55
+ if (fn.length > 0) {
56
+ return
57
+ }
58
+
59
+ throw new InvalidPersistedCollectionConfigError(
60
+ `SQLiteDriver.transaction callback must accept the transaction driver argument`,
61
+ )
62
+ }
63
+
64
+ function isExpoSQLiteDatabaseLike(
65
+ value: unknown,
66
+ ): value is ExpoSQLiteDatabaseLike {
67
+ return (
68
+ typeof value === `object` &&
69
+ value !== null &&
70
+ typeof (value as ExpoSQLiteDatabaseLike).execAsync === `function` &&
71
+ typeof (value as ExpoSQLiteDatabaseLike).getAllAsync === `function` &&
72
+ typeof (value as ExpoSQLiteDatabaseLike).runAsync === `function` &&
73
+ typeof (value as ExpoSQLiteDatabaseLike).withExclusiveTransactionAsync ===
74
+ `function`
75
+ )
76
+ }
77
+
78
+ export class ExpoSQLiteDriver implements SQLiteDriver {
79
+ private readonly databasePromise: Promise<ExpoSQLiteDatabaseLike>
80
+ private readonly ownsDatabase: boolean
81
+ private queue: Promise<void> = Promise.resolve()
82
+ private nextSavepointId = 1
83
+
84
+ constructor(options: ExpoSQLiteDriverOptions) {
85
+ if (hasExistingDatabase(options)) {
86
+ if (!isExpoSQLiteDatabaseLike(options.database)) {
87
+ throw new InvalidPersistedCollectionConfigError(
88
+ `Expo SQLite database must provide execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`,
89
+ )
90
+ }
91
+
92
+ this.databasePromise = Promise.resolve(options.database)
93
+ this.ownsDatabase = false
94
+ return
95
+ }
96
+
97
+ this.databasePromise = Promise.resolve(options.openDatabase()).then(
98
+ (database) => {
99
+ if (!isExpoSQLiteDatabaseLike(database)) {
100
+ throw new InvalidPersistedCollectionConfigError(
101
+ `Expo SQLite openDatabase() must resolve a database with execAsync/getAllAsync/runAsync/withExclusiveTransactionAsync`,
102
+ )
103
+ }
104
+
105
+ return database
106
+ },
107
+ )
108
+ this.ownsDatabase = true
109
+ }
110
+
111
+ async exec(sql: string): Promise<void> {
112
+ await this.enqueue(async () => {
113
+ const database = await this.getDatabase()
114
+ await database.execAsync(sql)
115
+ })
116
+ }
117
+
118
+ async query<T>(
119
+ sql: string,
120
+ params: ReadonlyArray<unknown> = [],
121
+ ): Promise<ReadonlyArray<T>> {
122
+ return this.enqueue(async () => {
123
+ const database = await this.getDatabase()
124
+ return database.getAllAsync<T>(sql, normalizeParams(params))
125
+ })
126
+ }
127
+
128
+ async run(sql: string, params: ReadonlyArray<unknown> = []): Promise<void> {
129
+ await this.enqueue(async () => {
130
+ const database = await this.getDatabase()
131
+ await database.runAsync(sql, normalizeParams(params))
132
+ })
133
+ }
134
+
135
+ async transaction<T>(
136
+ fn: (transactionDriver: SQLiteDriver) => Promise<T>,
137
+ ): Promise<T> {
138
+ assertTransactionCallbackHasDriverArg(fn)
139
+ return this.transactionWithDriver(fn)
140
+ }
141
+
142
+ async transactionWithDriver<T>(
143
+ fn: (transactionDriver: SQLiteDriver) => Promise<T>,
144
+ ): Promise<T> {
145
+ return this.enqueue(async () => {
146
+ const database = await this.getDatabase()
147
+ return database.withExclusiveTransactionAsync(async (transaction) => {
148
+ const transactionDriver = this.createTransactionDriver(transaction)
149
+ return fn(transactionDriver)
150
+ })
151
+ })
152
+ }
153
+
154
+ async close(): Promise<void> {
155
+ const database = await this.getDatabase()
156
+ if (!this.ownsDatabase || typeof database.closeAsync !== `function`) {
157
+ return
158
+ }
159
+
160
+ await database.closeAsync()
161
+ }
162
+
163
+ async getDatabase(): Promise<ExpoSQLiteDatabaseLike> {
164
+ return this.databasePromise
165
+ }
166
+
167
+ private enqueue<T>(operation: () => Promise<T>): Promise<T> {
168
+ const queuedOperation = this.queue.then(operation, operation)
169
+ this.queue = queuedOperation.then(
170
+ () => undefined,
171
+ () => undefined,
172
+ )
173
+ return queuedOperation
174
+ }
175
+
176
+ private createTransactionDriver(
177
+ transaction: ExpoSQLiteTransaction,
178
+ ): SQLiteDriver {
179
+ const transactionDriver: SQLiteDriver = {
180
+ exec: async (sql) => {
181
+ await transaction.execAsync(sql)
182
+ },
183
+ query: async <T>(
184
+ sql: string,
185
+ params: ReadonlyArray<unknown> = [],
186
+ ): Promise<ReadonlyArray<T>> => {
187
+ return transaction.getAllAsync<T>(sql, normalizeParams(params))
188
+ },
189
+ run: async (sql: string, params: ReadonlyArray<unknown> = []) => {
190
+ await transaction.runAsync(sql, normalizeParams(params))
191
+ },
192
+ transaction: async <T>(
193
+ fn: (nestedTransactionDriver: SQLiteDriver) => Promise<T>,
194
+ ): Promise<T> => {
195
+ assertTransactionCallbackHasDriverArg(fn)
196
+ return this.runNestedTransaction(transaction, transactionDriver, fn)
197
+ },
198
+ transactionWithDriver: async <T>(
199
+ fn: (nestedTransactionDriver: SQLiteDriver) => Promise<T>,
200
+ ): Promise<T> =>
201
+ this.runNestedTransaction(transaction, transactionDriver, fn),
202
+ }
203
+
204
+ return transactionDriver
205
+ }
206
+
207
+ private async runNestedTransaction<T>(
208
+ transaction: ExpoSQLiteTransaction,
209
+ transactionDriver: SQLiteDriver,
210
+ fn: (transactionDriver: SQLiteDriver) => Promise<T>,
211
+ ): Promise<T> {
212
+ const savepointName = `tsdb_sp_${this.nextSavepointId}`
213
+ this.nextSavepointId++
214
+ await transaction.execAsync(`SAVEPOINT ${savepointName}`)
215
+
216
+ try {
217
+ const result = await fn(transactionDriver)
218
+ await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`)
219
+ return result
220
+ } catch (error) {
221
+ await transaction.execAsync(`ROLLBACK TO SAVEPOINT ${savepointName}`)
222
+ await transaction.execAsync(`RELEASE SAVEPOINT ${savepointName}`)
223
+ throw error
224
+ }
225
+ }
226
+ }
227
+
228
+ function normalizeParams(
229
+ params: ReadonlyArray<unknown>,
230
+ ): ExpoSQLiteBindParams | undefined {
231
+ return params.length > 0 ? [...params] : undefined
232
+ }
233
+
234
+ export function createExpoSQLiteDriver(
235
+ options: ExpoSQLiteDriverOptions,
236
+ ): ExpoSQLiteDriver {
237
+ return new ExpoSQLiteDriver(options)
238
+ }
package/src/expo.ts ADDED
@@ -0,0 +1,159 @@
1
+ import {
2
+ SingleProcessCoordinator,
3
+ createSQLiteCorePersistenceAdapter,
4
+ } from '@tanstack/db-sqlite-persistence-core'
5
+ import { ExpoSQLiteDriver } from './expo-sqlite-driver'
6
+ import type {
7
+ PersistedCollectionCoordinator,
8
+ PersistedCollectionMode,
9
+ PersistedCollectionPersistence,
10
+ SQLiteCoreAdapterOptions,
11
+ SQLiteDriver,
12
+ } from '@tanstack/db-sqlite-persistence-core'
13
+ import type { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver'
14
+
15
+ export type { ExpoSQLiteDatabaseLike } from './expo-sqlite-driver'
16
+
17
+ type ExpoSQLiteCoreSchemaMismatchPolicy =
18
+ | `sync-present-reset`
19
+ | `sync-absent-error`
20
+ | `reset`
21
+
22
+ export type ExpoSQLiteSchemaMismatchPolicy =
23
+ | ExpoSQLiteCoreSchemaMismatchPolicy
24
+ | `throw`
25
+
26
+ export type ExpoSQLitePersistenceOptions = Omit<
27
+ SQLiteCoreAdapterOptions,
28
+ `driver` | `schemaVersion` | `schemaMismatchPolicy`
29
+ > & {
30
+ database: ExpoSQLiteDatabaseLike
31
+ coordinator?: PersistedCollectionCoordinator
32
+ schemaMismatchPolicy?: ExpoSQLiteSchemaMismatchPolicy
33
+ }
34
+
35
+ function normalizeSchemaMismatchPolicy(
36
+ policy: ExpoSQLiteSchemaMismatchPolicy,
37
+ ): ExpoSQLiteCoreSchemaMismatchPolicy {
38
+ if (policy === `throw`) {
39
+ return `sync-absent-error`
40
+ }
41
+
42
+ return policy
43
+ }
44
+
45
+ function resolveSchemaMismatchPolicy(
46
+ explicitPolicy: ExpoSQLiteSchemaMismatchPolicy | undefined,
47
+ mode: PersistedCollectionMode,
48
+ ): ExpoSQLiteCoreSchemaMismatchPolicy {
49
+ if (explicitPolicy) {
50
+ return normalizeSchemaMismatchPolicy(explicitPolicy)
51
+ }
52
+
53
+ return mode === `sync-present` ? `sync-present-reset` : `sync-absent-error`
54
+ }
55
+
56
+ function createAdapterCacheKey(
57
+ schemaMismatchPolicy: ExpoSQLiteCoreSchemaMismatchPolicy,
58
+ schemaVersion: number | undefined,
59
+ ): string {
60
+ const schemaVersionKey =
61
+ schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`
62
+ return `${schemaMismatchPolicy}|${schemaVersionKey}`
63
+ }
64
+
65
+ function createInternalSQLiteDriver(
66
+ options: ExpoSQLitePersistenceOptions,
67
+ ): SQLiteDriver {
68
+ return new ExpoSQLiteDriver({
69
+ database: options.database,
70
+ })
71
+ }
72
+
73
+ function resolveAdapterBaseOptions(
74
+ options: ExpoSQLitePersistenceOptions,
75
+ ): Omit<
76
+ SQLiteCoreAdapterOptions,
77
+ `driver` | `schemaVersion` | `schemaMismatchPolicy`
78
+ > {
79
+ return {
80
+ appliedTxPruneMaxRows: options.appliedTxPruneMaxRows,
81
+ appliedTxPruneMaxAgeSeconds: options.appliedTxPruneMaxAgeSeconds,
82
+ pullSinceReloadThreshold: options.pullSinceReloadThreshold,
83
+ }
84
+ }
85
+
86
+ export function createExpoSQLitePersistence<
87
+ T extends object,
88
+ TKey extends string | number = string | number,
89
+ >(
90
+ options: ExpoSQLitePersistenceOptions,
91
+ ): PersistedCollectionPersistence<T, TKey> {
92
+ const { coordinator, schemaMismatchPolicy } = options
93
+ const driver = createInternalSQLiteDriver(options)
94
+ const adapterBaseOptions = resolveAdapterBaseOptions(options)
95
+ const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator()
96
+ const adapterCache = new Map<
97
+ string,
98
+ ReturnType<
99
+ typeof createSQLiteCorePersistenceAdapter<
100
+ Record<string, unknown>,
101
+ string | number
102
+ >
103
+ >
104
+ >()
105
+
106
+ const getAdapterForCollection = (
107
+ mode: PersistedCollectionMode,
108
+ schemaVersion: number | undefined,
109
+ ) => {
110
+ const resolvedSchemaMismatchPolicy = resolveSchemaMismatchPolicy(
111
+ schemaMismatchPolicy,
112
+ mode,
113
+ )
114
+ const cacheKey = createAdapterCacheKey(
115
+ resolvedSchemaMismatchPolicy,
116
+ schemaVersion,
117
+ )
118
+ const cachedAdapter = adapterCache.get(cacheKey)
119
+ if (cachedAdapter) {
120
+ return cachedAdapter
121
+ }
122
+
123
+ const adapter = createSQLiteCorePersistenceAdapter<
124
+ Record<string, unknown>,
125
+ string | number
126
+ >({
127
+ ...adapterBaseOptions,
128
+ driver,
129
+ schemaMismatchPolicy: resolvedSchemaMismatchPolicy,
130
+ ...(schemaVersion === undefined ? {} : { schemaVersion }),
131
+ })
132
+ adapterCache.set(cacheKey, adapter)
133
+ return adapter
134
+ }
135
+
136
+ const createCollectionPersistence = (
137
+ mode: PersistedCollectionMode,
138
+ schemaVersion: number | undefined,
139
+ ): PersistedCollectionPersistence<T, TKey> => ({
140
+ adapter: getAdapterForCollection(
141
+ mode,
142
+ schemaVersion,
143
+ ) as unknown as PersistedCollectionPersistence<T, TKey>[`adapter`],
144
+ coordinator: resolvedCoordinator,
145
+ })
146
+
147
+ const defaultPersistence = createCollectionPersistence(
148
+ `sync-absent`,
149
+ undefined,
150
+ )
151
+
152
+ return {
153
+ ...defaultPersistence,
154
+ resolvePersistenceForCollection: ({ mode, schemaVersion }) =>
155
+ createCollectionPersistence(mode, schemaVersion),
156
+ resolvePersistenceForMode: (mode) =>
157
+ createCollectionPersistence(mode, undefined),
158
+ }
159
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export { createExpoSQLitePersistence } from './expo'
2
+ export type {
3
+ ExpoSQLiteDatabaseLike,
4
+ ExpoSQLitePersistenceOptions,
5
+ ExpoSQLiteSchemaMismatchPolicy,
6
+ } from './expo'
7
+ export { persistedCollectionOptions } from '@tanstack/db-sqlite-persistence-core'
8
+ export type {
9
+ PersistedCollectionCoordinator,
10
+ PersistedCollectionPersistence,
11
+ } from '@tanstack/db-sqlite-persistence-core'