Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/six-ties-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@tanstack/expo-db-sqlite-persistence-e2e-app': patch
'@tanstack/expo-db-sqlite-persistence': patch
---

fixes a TS2322 type error when passing a SQLiteDatabase from expo-sqlite to createExpoSQLitePersistence
53 changes: 14 additions & 39 deletions packages/expo-db-sqlite-persistence/e2e/expo-runtime-app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { ScrollView, Text, View } from 'react-native'
import * as SQLite from 'expo-sqlite'
import { createCollection } from '@tanstack/db'
Expand All @@ -14,20 +14,12 @@ import type {
} from '../runtime-protocol'

type DatabaseHandle = Awaited<ReturnType<typeof SQLite.openDatabaseAsync>>
type TransactionHandleLike = {
execAsync: (sql: string) => Promise<void>
getAllAsync: (
sql: string,
params?: ReadonlyArray<unknown> | Record<string, unknown>,
) => Promise<ReadonlyArray<unknown>>
runAsync: (
sql: string,
params?: ReadonlyArray<unknown> | Record<string, unknown>,
) => Promise<unknown>
}
type TransactionHandle = Parameters<
Parameters<DatabaseHandle['withExclusiveTransactionAsync']>[0]
>[0]

type ActiveTransaction = {
transaction: TransactionHandleLike
transaction: TransactionHandle
complete: {
resolve: () => void
reject: (error?: unknown) => void
Expand Down Expand Up @@ -63,12 +55,6 @@ async function postJson<TBody extends object>(
}
}

function normalizeSqliteParams(
params?: ReadonlyArray<unknown> | Record<string, unknown>,
): ReadonlyArray<unknown> | Record<string, unknown> | undefined {
return params === undefined ? undefined : params
}

async function closeDatabaseHandle(database: DatabaseHandle): Promise<void> {
const closableDatabase = database as DatabaseHandle & {
closeAsync?: () => Promise<void>
Expand Down Expand Up @@ -129,14 +115,7 @@ export default function App() {
): Promise<ExpoRuntimeSmokeTestResult> => {
const database = await SQLite.openDatabaseAsync(databaseName)
const collectionId = `expo-runtime-smoke-${Date.now().toString(36)}`
const persistence = createExpoSQLitePersistence<
{
id: string
title: string
score: number
},
string
>({
const persistence = createExpoSQLitePersistence({
database,
})
const collection = createCollection(
Expand Down Expand Up @@ -198,20 +177,18 @@ export default function App() {
command.databaseId,
command.databaseName,
)
const params = normalizeSqliteParams(command.params)
return params === undefined
return command.params === undefined
? database.getAllAsync(command.sql)
: database.getAllAsync(command.sql, params)
: database.getAllAsync(command.sql, command.params)
}
case `db:run`: {
const database = await getDatabase(
command.databaseId,
command.databaseName,
)
const params = normalizeSqliteParams(command.params)
return params === undefined
return command.params === undefined
? database.runAsync(command.sql)
: database.runAsync(command.sql, params)
: database.runAsync(command.sql, command.params)
}
case `db:close`: {
const database = databaseHandles.get(command.databaseId)
Expand Down Expand Up @@ -270,20 +247,18 @@ export default function App() {
if (!transaction) {
throw new Error(`Unknown transaction id`)
}
const params = normalizeSqliteParams(command.params)
return params === undefined
return command.params === undefined
? transaction.transaction.getAllAsync(command.sql)
: transaction.transaction.getAllAsync(command.sql, params)
: transaction.transaction.getAllAsync(command.sql, command.params)
}
case `tx:run`: {
const transaction = activeTransactions.get(command.transactionId)
if (!transaction) {
throw new Error(`Unknown transaction id`)
}
const params = normalizeSqliteParams(command.params)
return params === undefined
return command.params === undefined
? transaction.transaction.runAsync(command.sql)
: transaction.transaction.runAsync(command.sql, params)
: transaction.transaction.runAsync(command.sql, command.params)
}
case `tx:commit`: {
const transaction = activeTransactions.get(command.transactionId)
Expand Down
13 changes: 9 additions & 4 deletions packages/expo-db-sqlite-persistence/e2e/runtime-protocol.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
export type ExpoRuntimeSQLiteBindValue = string | number | boolean | null
export type ExpoRuntimeSQLiteBindParams =
| Array<ExpoRuntimeSQLiteBindValue>
| Record<string, ExpoRuntimeSQLiteBindValue>

export type ExpoRuntimeCommand =
| {
id: string
Expand All @@ -12,15 +17,15 @@ export type ExpoRuntimeCommand =
databaseId: string
databaseName: string
sql: string
params?: ReadonlyArray<unknown> | Record<string, unknown>
params?: ExpoRuntimeSQLiteBindParams
}
| {
id: string
type: `db:run`
databaseId: string
databaseName: string
sql: string
params?: ReadonlyArray<unknown> | Record<string, unknown>
params?: ExpoRuntimeSQLiteBindParams
}
| {
id: string
Expand All @@ -46,14 +51,14 @@ export type ExpoRuntimeCommand =
type: `tx:getAll`
transactionId: string
sql: string
params?: ReadonlyArray<unknown> | Record<string, unknown>
params?: ExpoRuntimeSQLiteBindParams
}
| {
id: string
type: `tx:run`
transactionId: string
sql: string
params?: ReadonlyArray<unknown> | Record<string, unknown>
params?: ExpoRuntimeSQLiteBindParams
}
| {
id: string
Expand Down
64 changes: 38 additions & 26 deletions packages/expo-db-sqlite-persistence/src/expo-sqlite-driver.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
import { InvalidPersistedCollectionConfigError } from '@tanstack/db-sqlite-persistence-core'
import type { SQLiteDriver } from '@tanstack/db-sqlite-persistence-core'

export type ExpoSQLiteBindParams =
| ReadonlyArray<unknown>
| Record<string, unknown>

export type ExpoSQLiteRunResult = {
changes: number
lastInsertRowId: number
}
import type {
SQLiteBindParams,
SQLiteBindValue,
SQLiteRunResult,
} from 'expo-sqlite'

export type ExpoSQLiteQueryable = {
execAsync: (sql: string) => Promise<void>
getAllAsync: <T>(
sql: string,
params?: ExpoSQLiteBindParams,
) => Promise<ReadonlyArray<T>>
runAsync: (
sql: string,
params?: ExpoSQLiteBindParams,
) => Promise<ExpoSQLiteRunResult>
getAllAsync: {
<T>(sql: string, params: SQLiteBindParams): Promise<ReadonlyArray<T>>
<T>(sql: string): Promise<ReadonlyArray<T>>
}
runAsync: {
(sql: string, params: SQLiteBindParams): Promise<SQLiteRunResult>
(sql: string): Promise<SQLiteRunResult>
}
}

export type ExpoSQLiteTransaction = ExpoSQLiteQueryable

export type ExpoSQLiteDatabaseLike = ExpoSQLiteQueryable & {
withExclusiveTransactionAsync: <T>(
task: (transaction: ExpoSQLiteTransaction) => Promise<T>,
) => Promise<T>
withExclusiveTransactionAsync: (
task: (transaction: ExpoSQLiteTransaction) => Promise<void>,
) => Promise<void>
closeAsync?: () => Promise<void>
}

Expand Down Expand Up @@ -144,10 +140,12 @@ export class ExpoSQLiteDriver implements SQLiteDriver {
): Promise<T> {
return this.enqueue(async () => {
const database = await this.getDatabase()
return database.withExclusiveTransactionAsync(async (transaction) => {
let result: T | undefined
await database.withExclusiveTransactionAsync(async (transaction) => {
const transactionDriver = this.createTransactionDriver(transaction)
return fn(transactionDriver)
result = await fn(transactionDriver)
})
return result as T
})
}

Expand Down Expand Up @@ -225,10 +223,24 @@ export class ExpoSQLiteDriver implements SQLiteDriver {
}
}

function normalizeParams(
params: ReadonlyArray<unknown>,
): ExpoSQLiteBindParams | undefined {
return params.length > 0 ? [...params] : undefined
function normalizeBindValue(value: unknown): SQLiteBindValue {
if (
value === null ||
typeof value === `string` ||
typeof value === `number` ||
typeof value === `boolean` ||
value instanceof Uint8Array
) {
return value
}

throw new TypeError(
`Expo SQLite bind parameters must be strings, numbers, booleans, null, or Uint8Array values`,
)
}

function normalizeParams(params: ReadonlyArray<unknown>): SQLiteBindParams {
return params.map(normalizeBindValue)
}

export function createExpoSQLiteDriver(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import type {
ExpoSQLiteTestDatabase,
ExpoSQLiteTestDatabaseFactory,
} from './expo-sqlite-test-db'
import type {
ExpoSQLiteBindParams,
ExpoSQLiteTransaction,
} from '../../src/expo-sqlite-driver'
import type { SQLiteBindParams } from 'expo-sqlite'
import type { ExpoSQLiteTransaction } from '../../src/expo-sqlite-driver'

function resolvePlatform(): `ios` | `android` {
const platform = process.env.TANSTACK_DB_EXPO_RUNTIME_PLATFORM?.trim()
Expand Down Expand Up @@ -46,13 +44,17 @@ export function createMobileSQLiteTestDatabaseFactory(): ExpoSQLiteTestDatabaseF
execAsync: async (sql: string) => {
await (await getDatabase()).execAsync(sql)
},
getAllAsync: async <T>(sql: string, params?: ExpoSQLiteBindParams) =>
(await getDatabase()).getAllAsync<T>(sql, params),
runAsync: async (sql: string, params?: ExpoSQLiteBindParams) =>
(await getDatabase()).runAsync(sql, params),
withExclusiveTransactionAsync: async <T>(
task: (transaction: ExpoSQLiteTransaction) => Promise<T>,
): Promise<T> =>
getAllAsync: async <T>(sql: string, params?: SQLiteBindParams) =>
params !== undefined
? (await getDatabase()).getAllAsync<T>(sql, params)
: (await getDatabase()).getAllAsync<T>(sql),
runAsync: async (sql: string, params?: SQLiteBindParams) =>
params !== undefined
? (await getDatabase()).runAsync(sql, params)
: (await getDatabase()).runAsync(sql),
withExclusiveTransactionAsync: async (
task: (transaction: ExpoSQLiteTransaction) => Promise<void>,
): Promise<void> =>
(await getDatabase()).withExclusiveTransactionAsync(task),
closeAsync: async () => {
if (!databasePromise) {
Expand Down
Loading