{"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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"}