Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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 =
| ReadonlyArray<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
55 changes: 32 additions & 23 deletions packages/expo-db-sqlite-persistence/src/expo-sqlite-driver.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
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,
params?: SQLiteBindParams,
) => Promise<ReadonlyArray<T>>
runAsync: (
sql: string,
params?: ExpoSQLiteBindParams,
) => Promise<ExpoSQLiteRunResult>
runAsync: (sql: string, params?: SQLiteBindParams) => 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 +137,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 +220,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,13 @@ export function createMobileSQLiteTestDatabaseFactory(): ExpoSQLiteTestDatabaseF
execAsync: async (sql: string) => {
await (await getDatabase()).execAsync(sql)
},
getAllAsync: async <T>(sql: string, params?: ExpoSQLiteBindParams) =>
getAllAsync: async <T>(sql: string, params?: SQLiteBindParams) =>
(await getDatabase()).getAllAsync<T>(sql, params),
runAsync: async (sql: string, params?: ExpoSQLiteBindParams) =>
runAsync: async (sql: string, params?: SQLiteBindParams) =>
(await getDatabase()).runAsync(sql, params),
withExclusiveTransactionAsync: async <T>(
task: (transaction: ExpoSQLiteTransaction) => Promise<T>,
): Promise<T> =>
withExclusiveTransactionAsync: async (
task: (transaction: ExpoSQLiteTransaction) => Promise<void>,
): Promise<void> =>
(await getDatabase()).withExclusiveTransactionAsync(task),
closeAsync: async () => {
if (!databasePromise) {
Expand Down
Loading