@tanstack/expo-db-sqlite-persistence 0.2.23 → 0.2.25

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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Thin SQLite persistence for Expo apps using the official `expo-sqlite` adapter.
4
4
 
5
+ Start with the [SQLite Persistence guide](../../docs/guides/sqlite-persistence.md) for Collection setup and lifecycle behavior.
6
+
5
7
  ## Public API
6
8
 
7
9
  - `createExpoSQLitePersistence(...)`
@@ -45,7 +47,7 @@ export const todosCollection = createCollection(
45
47
  ## Notes
46
48
 
47
49
  - This package targets the official `expo-sqlite` async database API.
48
- - Requires `expo-sqlite` `^55.0.10` (documented as a peer dependency).
50
+ - Requires `expo-sqlite` `^55.0.10` or `^57.0.0` (documented as a peer dependency).
49
51
  - `createExpoSQLitePersistence` is shared across collections.
50
52
  - Mode defaults (`sync-present` vs `sync-absent`) are inferred from whether a
51
53
  `sync` config is present in `persistedCollectionOptions`.
@@ -66,10 +66,12 @@ class ExpoSQLiteDriver {
66
66
  async transactionWithDriver(fn) {
67
67
  return this.enqueue(async () => {
68
68
  const database = await this.getDatabase();
69
- return database.withExclusiveTransactionAsync(async (transaction) => {
69
+ let result;
70
+ await database.withExclusiveTransactionAsync(async (transaction) => {
70
71
  const transactionDriver = this.createTransactionDriver(transaction);
71
- return fn(transactionDriver);
72
+ result = await fn(transactionDriver);
72
73
  });
74
+ return result;
73
75
  });
74
76
  }
75
77
  async close() {
@@ -125,7 +127,14 @@ class ExpoSQLiteDriver {
125
127
  }
126
128
  }
127
129
  function normalizeParams(params) {
128
- return params.length > 0 ? [...params] : void 0;
130
+ return params.map((value) => {
131
+ if (value === null || typeof value === `string` || typeof value === `number` || typeof value === `boolean` || value instanceof Uint8Array) {
132
+ return value;
133
+ }
134
+ throw new TypeError(
135
+ `Expo SQLite bind parameters must be strings, numbers, booleans, null, or Uint8Array values`
136
+ );
137
+ });
129
138
  }
130
139
  exports.ExpoSQLiteDriver = ExpoSQLiteDriver;
131
140
  //# sourceMappingURL=expo-sqlite-driver.cjs.map
@@ -1 +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":";;;AA6CA,SAAS,oBACP,SAC8C;AAC9C,SAAO,cAAc;AACvB;AAEA,SAAS,sCACP,IACM;AACN,MAAI,GAAG,SAAS,GAAG;AACjB;AAAA,EACF;AAEA,QAAM,IAAIA,wBAAAA;AAAAA,IACR;AAAA,EAAA;AAEJ;AAEA,SAAS,yBACP,OACiC;AACjC,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAiC,cAAc,cACvD,OAAQ,MAAiC,gBAAgB,cACzD,OAAQ,MAAiC,aAAa,cACtD,OAAQ,MAAiC,kCACvC;AAEN;AAEO,MAAM,iBAAyC;AAAA,EAMpD,YAAY,SAAkC;AAH9C,SAAQ,QAAuB,QAAQ,QAAA;AACvC,SAAQ,kBAAkB;AAGxB,QAAI,oBAAoB,OAAO,GAAG;AAChC,UAAI,CAAC,yBAAyB,QAAQ,QAAQ,GAAG;AAC/C,cAAM,IAAIA,wBAAAA;AAAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAEA,WAAK,kBAAkB,QAAQ,QAAQ,QAAQ,QAAQ;AACvD,WAAK,eAAe;AACpB;AAAA,IACF;AAEA,SAAK,kBAAkB,QAAQ,QAAQ,QAAQ,aAAA,CAAc,EAAE;AAAA,MAC7D,CAAC,aAAa;AACZ,YAAI,CAAC,yBAAyB,QAAQ,GAAG;AACvC,gBAAM,IAAIA,wBAAAA;AAAAA,YACR;AAAA,UAAA;AAAA,QAEJ;AAEA,eAAO;AAAA,MACT;AAAA,IAAA;AAEF,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,MAAM,KAAK,KAA4B;AACrC,UAAM,KAAK,QAAQ,YAAY;AAC7B,YAAM,WAAW,MAAM,KAAK,YAAA;AAC5B,YAAM,SAAS,UAAU,GAAG;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MACJ,KACA,SAAiC,IACN;AAC3B,WAAO,KAAK,QAAQ,YAAY;AAC9B,YAAM,WAAW,MAAM,KAAK,YAAA;AAC5B,aAAO,SAAS,YAAe,KAAK,gBAAgB,MAAM,CAAC;AAAA,IAC7D,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,KAAa,SAAiC,IAAmB;AACzE,UAAM,KAAK,QAAQ,YAAY;AAC7B,YAAM,WAAW,MAAM,KAAK,YAAA;AAC5B,YAAM,SAAS,SAAS,KAAK,gBAAgB,MAAM,CAAC;AAAA,IACtD,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YACJ,IACY;AACZ,0CAAsC,EAAE;AACxC,WAAO,KAAK,sBAAsB,EAAE;AAAA,EACtC;AAAA,EAEA,MAAM,sBACJ,IACY;AACZ,WAAO,KAAK,QAAQ,YAAY;AAC9B,YAAM,WAAW,MAAM,KAAK,YAAA;AAC5B,aAAO,SAAS,8BAA8B,OAAO,gBAAgB;AACnE,cAAM,oBAAoB,KAAK,wBAAwB,WAAW;AAClE,eAAO,GAAG,iBAAiB;AAAA,MAC7B,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,WAAW,MAAM,KAAK,YAAA;AAC5B,QAAI,CAAC,KAAK,gBAAgB,OAAO,SAAS,eAAe,YAAY;AACnE;AAAA,IACF;AAEA,UAAM,SAAS,WAAA;AAAA,EACjB;AAAA,EAEA,MAAM,cAA+C;AACnD,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,QAAW,WAAyC;AAC1D,UAAM,kBAAkB,KAAK,MAAM,KAAK,WAAW,SAAS;AAC5D,SAAK,QAAQ,gBAAgB;AAAA,MAC3B,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAER,WAAO;AAAA,EACT;AAAA,EAEQ,wBACN,aACc;AACd,UAAM,oBAAkC;AAAA,MACtC,MAAM,OAAO,QAAQ;AACnB,cAAM,YAAY,UAAU,GAAG;AAAA,MACjC;AAAA,MACA,OAAO,OACL,KACA,SAAiC,OACH;AAC9B,eAAO,YAAY,YAAe,KAAK,gBAAgB,MAAM,CAAC;AAAA,MAChE;AAAA,MACA,KAAK,OAAO,KAAa,SAAiC,OAAO;AAC/D,cAAM,YAAY,SAAS,KAAK,gBAAgB,MAAM,CAAC;AAAA,MACzD;AAAA,MACA,aAAa,OACX,OACe;AACf,8CAAsC,EAAE;AACxC,eAAO,KAAK,qBAAqB,aAAa,mBAAmB,EAAE;AAAA,MACrE;AAAA,MACA,uBAAuB,OACrB,OAEA,KAAK,qBAAqB,aAAa,mBAAmB,EAAE;AAAA,IAAA;AAGhE,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,qBACZ,aACA,mBACA,IACY;AACZ,UAAM,gBAAgB,WAAW,KAAK,eAAe;AACrD,SAAK;AACL,UAAM,YAAY,UAAU,aAAa,aAAa,EAAE;AAExD,QAAI;AACF,YAAM,SAAS,MAAM,GAAG,iBAAiB;AACzC,YAAM,YAAY,UAAU,qBAAqB,aAAa,EAAE;AAChE,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,YAAY,UAAU,yBAAyB,aAAa,EAAE;AACpE,YAAM,YAAY,UAAU,qBAAqB,aAAa,EAAE;AAChE,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,SAAS,gBACP,QACkC;AAClC,SAAO,OAAO,SAAS,IAAI,CAAC,GAAG,MAAM,IAAI;AAC3C;;"}
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'\nimport type {\n SQLiteBindParams,\n SQLiteBindValue,\n SQLiteRunResult,\n} from 'expo-sqlite'\n\nexport type ExpoSQLiteBindParams = SQLiteBindParams\n\nexport type ExpoSQLiteRunResult = SQLiteRunResult\n\nexport type ExpoSQLiteQueryable = {\n execAsync: (sql: string) => Promise<void>\n getAllAsync: {\n <T>(sql: string, params: SQLiteBindParams): Promise<ReadonlyArray<T>>\n <T>(sql: string): Promise<ReadonlyArray<T>>\n }\n runAsync: {\n (sql: string, params: SQLiteBindParams): Promise<SQLiteRunResult>\n (sql: string): Promise<SQLiteRunResult>\n }\n}\n\nexport type ExpoSQLiteTransaction = ExpoSQLiteQueryable\n\nexport type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {\n withExclusiveTransactionAsync: (\n task: (transaction: ExpoSQLiteTransaction) => Promise<void>,\n ) => Promise<void>\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 let result: T | undefined\n await database.withExclusiveTransactionAsync(async (transaction) => {\n const transactionDriver = this.createTransactionDriver(transaction)\n result = await fn(transactionDriver)\n })\n return result as T\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(params: ReadonlyArray<unknown>): SQLiteBindParams {\n return params.map((value) => {\n if (\n value === null ||\n typeof value === `string` ||\n typeof value === `number` ||\n typeof value === `boolean` ||\n value instanceof Uint8Array\n ) {\n return value satisfies SQLiteBindValue\n }\n\n throw new TypeError(\n `Expo SQLite bind parameters must be strings, numbers, booleans, null, or Uint8Array values`,\n )\n })\n}\n\nexport function createExpoSQLiteDriver(\n options: ExpoSQLiteDriverOptions,\n): ExpoSQLiteDriver {\n return new ExpoSQLiteDriver(options)\n}\n"],"names":["InvalidPersistedCollectionConfigError"],"mappings":"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"}
@@ -1,17 +1,21 @@
1
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
- };
2
+ import { SQLiteBindParams, SQLiteRunResult } from 'expo-sqlite';
3
+ export type ExpoSQLiteBindParams = SQLiteBindParams;
4
+ export type ExpoSQLiteRunResult = SQLiteRunResult;
7
5
  export type ExpoSQLiteQueryable = {
8
6
  execAsync: (sql: string) => Promise<void>;
9
- getAllAsync: <T>(sql: string, params?: ExpoSQLiteBindParams) => Promise<ReadonlyArray<T>>;
10
- runAsync: (sql: string, params?: ExpoSQLiteBindParams) => Promise<ExpoSQLiteRunResult>;
7
+ getAllAsync: {
8
+ <T>(sql: string, params: SQLiteBindParams): Promise<ReadonlyArray<T>>;
9
+ <T>(sql: string): Promise<ReadonlyArray<T>>;
10
+ };
11
+ runAsync: {
12
+ (sql: string, params: SQLiteBindParams): Promise<SQLiteRunResult>;
13
+ (sql: string): Promise<SQLiteRunResult>;
14
+ };
11
15
  };
12
16
  export type ExpoSQLiteTransaction = ExpoSQLiteQueryable;
13
17
  export type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {
14
- withExclusiveTransactionAsync: <T>(task: (transaction: ExpoSQLiteTransaction) => Promise<T>) => Promise<T>;
18
+ withExclusiveTransactionAsync: (task: (transaction: ExpoSQLiteTransaction) => Promise<void>) => Promise<void>;
15
19
  closeAsync?: () => Promise<void>;
16
20
  };
17
21
  type ExpoSQLiteExistingDatabaseOptions = {
@@ -1,17 +1,21 @@
1
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
- };
2
+ import { SQLiteBindParams, SQLiteRunResult } from 'expo-sqlite';
3
+ export type ExpoSQLiteBindParams = SQLiteBindParams;
4
+ export type ExpoSQLiteRunResult = SQLiteRunResult;
7
5
  export type ExpoSQLiteQueryable = {
8
6
  execAsync: (sql: string) => Promise<void>;
9
- getAllAsync: <T>(sql: string, params?: ExpoSQLiteBindParams) => Promise<ReadonlyArray<T>>;
10
- runAsync: (sql: string, params?: ExpoSQLiteBindParams) => Promise<ExpoSQLiteRunResult>;
7
+ getAllAsync: {
8
+ <T>(sql: string, params: SQLiteBindParams): Promise<ReadonlyArray<T>>;
9
+ <T>(sql: string): Promise<ReadonlyArray<T>>;
10
+ };
11
+ runAsync: {
12
+ (sql: string, params: SQLiteBindParams): Promise<SQLiteRunResult>;
13
+ (sql: string): Promise<SQLiteRunResult>;
14
+ };
11
15
  };
12
16
  export type ExpoSQLiteTransaction = ExpoSQLiteQueryable;
13
17
  export type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {
14
- withExclusiveTransactionAsync: <T>(task: (transaction: ExpoSQLiteTransaction) => Promise<T>) => Promise<T>;
18
+ withExclusiveTransactionAsync: (task: (transaction: ExpoSQLiteTransaction) => Promise<void>) => Promise<void>;
15
19
  closeAsync?: () => Promise<void>;
16
20
  };
17
21
  type ExpoSQLiteExistingDatabaseOptions = {
@@ -64,10 +64,12 @@ class ExpoSQLiteDriver {
64
64
  async transactionWithDriver(fn) {
65
65
  return this.enqueue(async () => {
66
66
  const database = await this.getDatabase();
67
- return database.withExclusiveTransactionAsync(async (transaction) => {
67
+ let result;
68
+ await database.withExclusiveTransactionAsync(async (transaction) => {
68
69
  const transactionDriver = this.createTransactionDriver(transaction);
69
- return fn(transactionDriver);
70
+ result = await fn(transactionDriver);
70
71
  });
72
+ return result;
71
73
  });
72
74
  }
73
75
  async close() {
@@ -123,7 +125,14 @@ class ExpoSQLiteDriver {
123
125
  }
124
126
  }
125
127
  function normalizeParams(params) {
126
- return params.length > 0 ? [...params] : void 0;
128
+ return params.map((value) => {
129
+ if (value === null || typeof value === `string` || typeof value === `number` || typeof value === `boolean` || value instanceof Uint8Array) {
130
+ return value;
131
+ }
132
+ throw new TypeError(
133
+ `Expo SQLite bind parameters must be strings, numbers, booleans, null, or Uint8Array values`
134
+ );
135
+ });
127
136
  }
128
137
  export {
129
138
  ExpoSQLiteDriver
@@ -1 +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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"}
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'\nimport type {\n SQLiteBindParams,\n SQLiteBindValue,\n SQLiteRunResult,\n} from 'expo-sqlite'\n\nexport type ExpoSQLiteBindParams = SQLiteBindParams\n\nexport type ExpoSQLiteRunResult = SQLiteRunResult\n\nexport type ExpoSQLiteQueryable = {\n execAsync: (sql: string) => Promise<void>\n getAllAsync: {\n <T>(sql: string, params: SQLiteBindParams): Promise<ReadonlyArray<T>>\n <T>(sql: string): Promise<ReadonlyArray<T>>\n }\n runAsync: {\n (sql: string, params: SQLiteBindParams): Promise<SQLiteRunResult>\n (sql: string): Promise<SQLiteRunResult>\n }\n}\n\nexport type ExpoSQLiteTransaction = ExpoSQLiteQueryable\n\nexport type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {\n withExclusiveTransactionAsync: (\n task: (transaction: ExpoSQLiteTransaction) => Promise<void>,\n ) => Promise<void>\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 let result: T | undefined\n await database.withExclusiveTransactionAsync(async (transaction) => {\n const transactionDriver = this.createTransactionDriver(transaction)\n result = await fn(transactionDriver)\n })\n return result as T\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(params: ReadonlyArray<unknown>): SQLiteBindParams {\n return params.map((value) => {\n if (\n value === null ||\n typeof value === `string` ||\n typeof value === `number` ||\n typeof value === `boolean` ||\n value instanceof Uint8Array\n ) {\n return value satisfies SQLiteBindValue\n }\n\n throw new TypeError(\n `Expo SQLite bind parameters must be strings, numbers, booleans, null, or Uint8Array values`,\n )\n })\n}\n\nexport function createExpoSQLiteDriver(\n options: ExpoSQLiteDriverOptions,\n): ExpoSQLiteDriver {\n return new ExpoSQLiteDriver(options)\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/expo-db-sqlite-persistence",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "Expo SQLite persisted collection adapter for TanStack DB",
5
5
  "author": "TanStack Team",
6
6
  "license": "MIT",
@@ -50,16 +50,17 @@
50
50
  "src"
51
51
  ],
52
52
  "dependencies": {
53
- "@tanstack/db-sqlite-persistence-core": "0.2.23"
53
+ "@tanstack/db-sqlite-persistence-core": "0.4.0"
54
54
  },
55
55
  "peerDependencies": {
56
- "expo-sqlite": "^55.0.10",
56
+ "expo-sqlite": "^55.0.10 || ^57.0.0",
57
57
  "typescript": ">=4.7"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/better-sqlite3": "^7.6.13",
61
61
  "@vitest/coverage-istanbul": "^3.2.4",
62
- "better-sqlite3": "^12.6.2"
62
+ "better-sqlite3": "^12.6.2",
63
+ "expo-sqlite-57": "npm:expo-sqlite@57.0.3"
63
64
  },
64
65
  "scripts": {
65
66
  "build": "vite build",
@@ -1,33 +1,33 @@
1
1
  import { InvalidPersistedCollectionConfigError } from '@tanstack/db-sqlite-persistence-core'
2
2
  import type { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core'
3
+ import type {
4
+ SQLiteBindParams,
5
+ SQLiteBindValue,
6
+ SQLiteRunResult,
7
+ } from 'expo-sqlite'
3
8
 
4
- export type ExpoSQLiteBindParams =
5
- | ReadonlyArray<unknown>
6
- | Record<string, unknown>
9
+ export type ExpoSQLiteBindParams = SQLiteBindParams
7
10
 
8
- export type ExpoSQLiteRunResult = {
9
- changes: number
10
- lastInsertRowId: number
11
- }
11
+ export type ExpoSQLiteRunResult = SQLiteRunResult
12
12
 
13
13
  export type ExpoSQLiteQueryable = {
14
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>
15
+ getAllAsync: {
16
+ <T>(sql: string, params: SQLiteBindParams): Promise<ReadonlyArray<T>>
17
+ <T>(sql: string): Promise<ReadonlyArray<T>>
18
+ }
19
+ runAsync: {
20
+ (sql: string, params: SQLiteBindParams): Promise<SQLiteRunResult>
21
+ (sql: string): Promise<SQLiteRunResult>
22
+ }
23
23
  }
24
24
 
25
25
  export type ExpoSQLiteTransaction = ExpoSQLiteQueryable
26
26
 
27
27
  export type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {
28
- withExclusiveTransactionAsync: <T>(
29
- task: (transaction: ExpoSQLiteTransaction) => Promise<T>,
30
- ) => Promise<T>
28
+ withExclusiveTransactionAsync: (
29
+ task: (transaction: ExpoSQLiteTransaction) => Promise<void>,
30
+ ) => Promise<void>
31
31
  closeAsync?: () => Promise<void>
32
32
  }
33
33
 
@@ -144,10 +144,12 @@ export class ExpoSQLiteDriver implements SQLiteDriver {
144
144
  ): Promise<T> {
145
145
  return this.enqueue(async () => {
146
146
  const database = await this.getDatabase()
147
- return database.withExclusiveTransactionAsync(async (transaction) => {
147
+ let result: T | undefined
148
+ await database.withExclusiveTransactionAsync(async (transaction) => {
148
149
  const transactionDriver = this.createTransactionDriver(transaction)
149
- return fn(transactionDriver)
150
+ result = await fn(transactionDriver)
150
151
  })
152
+ return result as T
151
153
  })
152
154
  }
153
155
 
@@ -225,10 +227,22 @@ export class ExpoSQLiteDriver implements SQLiteDriver {
225
227
  }
226
228
  }
227
229
 
228
- function normalizeParams(
229
- params: ReadonlyArray<unknown>,
230
- ): ExpoSQLiteBindParams | undefined {
231
- return params.length > 0 ? [...params] : undefined
230
+ function normalizeParams(params: ReadonlyArray<unknown>): SQLiteBindParams {
231
+ return params.map((value) => {
232
+ if (
233
+ value === null ||
234
+ typeof value === `string` ||
235
+ typeof value === `number` ||
236
+ typeof value === `boolean` ||
237
+ value instanceof Uint8Array
238
+ ) {
239
+ return value satisfies SQLiteBindValue
240
+ }
241
+
242
+ throw new TypeError(
243
+ `Expo SQLite bind parameters must be strings, numbers, booleans, null, or Uint8Array values`,
244
+ )
245
+ })
232
246
  }
233
247
 
234
248
  export function createExpoSQLiteDriver(