From 9ee30731786a2662a0b8a177f76406dc695dbf68 Mon Sep 17 00:00:00 2001 From: Kevin De Porre Date: Wed, 1 Apr 2026 15:24:20 +0200 Subject: [PATCH 1/3] Remove unnecessary type parameters from persistence interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PersistedCollectionPersistence, PersistenceAdapter, and SQLiteCorePersistenceAdapter were generic over but all implementations erased these to Record / string | number internally. The type params were threaded through the entire persistence hierarchy without providing real type safety at the implementation level. Collection-level typing (T, TKey) is fully preserved — it flows from getKey, schema, and other CollectionConfig fields, not from persistence. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/db/persisted-todos.ts | 2 +- ...ingle-tab-persisted-collection.e2e.test.ts | 2 +- .../src/browser-coordinator.ts | 5 +- .../src/browser-persistence.ts | 26 +-- .../tests/browser-coordinator.test.ts | 5 +- .../tests/browser-persistence.test.ts | 14 +- .../tests/browser-single-tab.test.ts | 6 +- ...-persisted-collection-conformance-suite.ts | 4 +- .../capacitor-persisted-collection-harness.ts | 6 +- .../src/capacitor-persistence.ts | 26 +-- .../src/capacitor.ts | 9 +- .../tests/capacitor-persistence.test.ts | 10 +- ...citor-runtime-persistence-contract.test.ts | 2 +- ...citor-sqlite-core-adapter-contract.test.ts | 10 +- .../src/do-persistence.ts | 26 +-- .../tests/do-persistence.test.ts | 20 +-- .../do-sqlite-core-adapter-contract.test.ts | 10 +- .../src/persisted.ts | 148 ++++++++---------- .../src/sqlite-core-adapter.ts | 75 +++++---- .../contracts/runtime-persistence-contract.ts | 6 +- .../contracts/sqlite-core-adapter-contract.ts | 1 - .../tests/persisted.test-d.ts | 2 +- .../tests/persisted.test.ts | 23 +-- .../tests/sqlite-core-adapter.test.ts | 46 ++---- .../src/electron-coordinator.ts | 5 +- .../src/main.ts | 17 +- .../src/renderer.ts | 49 ++---- .../tests/electron-ipc.test.ts | 40 ++--- .../electron-persisted-collection.e2e.test.ts | 7 +- ...ctron-sqlite-core-adapter-contract.test.ts | 15 +- ...-persisted-collection-conformance-suite.ts | 6 +- .../expo-db-sqlite-persistence/src/expo.ts | 26 +-- .../tests/expo-persistence.test.ts | 8 +- .../expo-runtime-persistence-contract.test.ts | 12 +- .../expo-sqlite-core-adapter-contract.test.ts | 10 +- .../e2e/node-persisted-collection.e2e.test.ts | 2 +- .../src/node-persistence.ts | 26 +-- .../tests/node-persistence.test.ts | 19 +-- .../node-sqlite-core-adapter-contract.test.ts | 10 +- ...-persisted-collection-conformance-suite.ts | 6 +- .../src/mobile-persistence.ts | 26 +-- .../src/react-native.ts | 9 +- .../expo-sqlite-core-adapter-contract.test.ts | 10 +- ...obile-runtime-persistence-contract.test.ts | 2 +- .../tests/react-native-persistence.test.ts | 27 ++-- ...ative-sqlite-core-adapter-contract.test.ts | 10 +- .../e2e/app/src/register-tauri-e2e-suite.ts | 9 +- .../tauri-persisted-collection-harness.ts | 6 +- .../src/tauri-persistence.ts | 26 +-- .../tauri-db-sqlite-persistence/src/tauri.ts | 9 +- ...tauri-runtime-persistence-contract.test.ts | 19 +-- ...tauri-sqlite-core-adapter-contract.test.ts | 10 +- 52 files changed, 302 insertions(+), 603 deletions(-) diff --git a/examples/react/offline-transactions/src/db/persisted-todos.ts b/examples/react/offline-transactions/src/db/persisted-todos.ts index 18537b98d..4eb068977 100644 --- a/examples/react/offline-transactions/src/db/persisted-todos.ts +++ b/examples/react/offline-transactions/src/db/persisted-todos.ts @@ -29,7 +29,7 @@ export async function createPersistedTodoCollection(): Promise({ + const persistence = createBrowserWASQLitePersistence({ database, coordinator, }) diff --git a/packages/browser-db-sqlite-persistence/e2e/browser-single-tab-persisted-collection.e2e.test.ts b/packages/browser-db-sqlite-persistence/e2e/browser-single-tab-persisted-collection.e2e.test.ts index ae3dfe6e9..e48a582cc 100644 --- a/packages/browser-db-sqlite-persistence/e2e/browser-single-tab-persisted-collection.e2e.test.ts +++ b/packages/browser-db-sqlite-persistence/e2e/browser-single-tab-persisted-collection.e2e.test.ts @@ -36,7 +36,7 @@ function createPersistedCollection( id: string, syncMode: `eager` | `on-demand`, ): PersistedCollectionHarness { - const persistence = createBrowserWASQLitePersistence({ + const persistence = createBrowserWASQLitePersistence({ database, }) let seedTxSequence = 0 diff --git a/packages/browser-db-sqlite-persistence/src/browser-coordinator.ts b/packages/browser-db-sqlite-persistence/src/browser-coordinator.ts index 272cf56f5..5b518ea38 100644 --- a/packages/browser-db-sqlite-persistence/src/browser-coordinator.ts +++ b/packages/browser-db-sqlite-persistence/src/browser-coordinator.ts @@ -81,10 +81,7 @@ type CollectionState = { } // Adapter with pullSince support -type AdapterWithPullSince = PersistenceAdapter< - Record, - string | number -> & { +type AdapterWithPullSince = PersistenceAdapter & { pullSince?: ( collectionId: string, fromRowVersion: number, diff --git a/packages/browser-db-sqlite-persistence/src/browser-persistence.ts b/packages/browser-db-sqlite-persistence/src/browser-persistence.ts index 2d69bc160..bfa049bf6 100644 --- a/packages/browser-db-sqlite-persistence/src/browser-persistence.ts +++ b/packages/browser-db-sqlite-persistence/src/browser-persistence.ts @@ -89,24 +89,16 @@ function resolveAdapterBaseOptions( * many collections on the same database. This is single-tab wiring using * SingleProcessCoordinator semantics (no election required). */ -export function createBrowserWASQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createBrowserWASQLitePersistence( options: BrowserWASQLitePersistenceOptions, -): PersistedCollectionPersistence { +): PersistedCollectionPersistence { const { coordinator, schemaMismatchPolicy } = options const driver = createInternalSQLiteDriver(options) const adapterBaseOptions = resolveAdapterBaseOptions(options) const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator() const adapterCache = new Map< string, - ReturnType< - typeof createSQLiteCorePersistenceAdapter< - Record, - string | number - > - > + ReturnType >() const getAdapterForCollection = ( @@ -126,10 +118,7 @@ export function createBrowserWASQLitePersistence< return cachedAdapter } - const adapter = createSQLiteCorePersistenceAdapter< - Record, - string | number - >({ + const adapter = createSQLiteCorePersistenceAdapter({ ...adapterBaseOptions, driver, schemaMismatchPolicy: resolvedSchemaMismatchPolicy, @@ -149,11 +138,8 @@ export function createBrowserWASQLitePersistence< const createCollectionPersistence = ( mode: PersistedCollectionMode, schemaVersion: number | undefined, - ): PersistedCollectionPersistence => ({ - adapter: getAdapterForCollection( - mode, - schemaVersion, - ) as unknown as PersistedCollectionPersistence[`adapter`], + ): PersistedCollectionPersistence => ({ + adapter: getAdapterForCollection(mode, schemaVersion), coordinator: resolvedCoordinator, }) diff --git a/packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts b/packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts index f7a8f78f4..5f554006e 100644 --- a/packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts +++ b/packages/browser-db-sqlite-persistence/tests/browser-coordinator.test.ts @@ -171,10 +171,7 @@ function cleanupGlobals(): void { // Adapter stub // --------------------------------------------------------------------------- -function createStubAdapter(): PersistenceAdapter< - Record, - string | number -> & { +function createStubAdapter(): PersistenceAdapter & { pullSince: ( collectionId: string, fromRowVersion: number, diff --git a/packages/browser-db-sqlite-persistence/tests/browser-persistence.test.ts b/packages/browser-db-sqlite-persistence/tests/browser-persistence.test.ts index 1e1db719c..bf068b70f 100644 --- a/packages/browser-db-sqlite-persistence/tests/browser-persistence.test.ts +++ b/packages/browser-db-sqlite-persistence/tests/browser-persistence.test.ts @@ -48,11 +48,11 @@ runRuntimePersistenceContractSuite( { createDatabaseHarness: createRuntimeDatabaseHarness, createAdapter: (driver) => - createBrowserWASQLitePersistence({ + createBrowserWASQLitePersistence({ database: (driver as BrowserWASQLiteDriver).getDatabase(), }).adapter, createPersistence: (driver, coordinator) => - createBrowserWASQLitePersistence({ + createBrowserWASQLitePersistence({ database: (driver as BrowserWASQLiteDriver).getDatabase(), coordinator, }), @@ -98,10 +98,7 @@ describe(`browser wa-sqlite persistence helpers`, () => { const firstDatabase = createWASQLiteTestDatabase({ filename: dbPath }) try { - const firstPersistence = createBrowserWASQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const firstPersistence = createBrowserWASQLitePersistence({ database: firstDatabase, }) @@ -141,10 +138,7 @@ describe(`browser wa-sqlite persistence helpers`, () => { const secondDatabase = createWASQLiteTestDatabase({ filename: dbPath }) try { - const secondPersistence = createBrowserWASQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const secondPersistence = createBrowserWASQLitePersistence({ database: secondDatabase, }) diff --git a/packages/browser-db-sqlite-persistence/tests/browser-single-tab.test.ts b/packages/browser-db-sqlite-persistence/tests/browser-single-tab.test.ts index 73443876c..773ac90b3 100644 --- a/packages/browser-db-sqlite-persistence/tests/browser-single-tab.test.ts +++ b/packages/browser-db-sqlite-persistence/tests/browser-single-tab.test.ts @@ -93,11 +93,7 @@ it(`works without browser election primitives`, async () => { } } - const persistence = createBrowserWASQLitePersistence<{ - id: string - title: string - score: number - }>({ + const persistence = createBrowserWASQLitePersistence({ database, }) diff --git a/packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite.ts b/packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite.ts index 031c4bc51..a39ba23f6 100644 --- a/packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite.ts +++ b/packages/capacitor-db-sqlite-persistence/e2e/capacitor-persisted-collection-conformance-suite.ts @@ -6,9 +6,9 @@ import { createCapacitorPersistedCollectionHarnessConfig } from './shared/capaci import { registerPersistedCollectionConformanceSuite } from './shared/register-persisted-collection-conformance-suite' import type { PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core' -type CapacitorPersistenceFactory = ( +type CapacitorPersistenceFactory = ( database: ReturnType, -) => PersistedCollectionPersistence +) => PersistedCollectionPersistence export function runCapacitorPersistedCollectionConformanceSuite( suiteName: string, diff --git a/packages/capacitor-db-sqlite-persistence/e2e/shared/capacitor-persisted-collection-harness.ts b/packages/capacitor-db-sqlite-persistence/e2e/shared/capacitor-persisted-collection-harness.ts index 054fee4e0..193a27357 100644 --- a/packages/capacitor-db-sqlite-persistence/e2e/shared/capacitor-persisted-collection-harness.ts +++ b/packages/capacitor-db-sqlite-persistence/e2e/shared/capacitor-persisted-collection-harness.ts @@ -25,9 +25,9 @@ type PersistedTransactionHandle = { } } -type PersistenceFactory = ( +type PersistenceFactory = ( database: TDatabase, -) => PersistedCollectionPersistence +) => PersistedCollectionPersistence type DatabaseLike = { close: () => Promise @@ -41,7 +41,7 @@ function createPersistedCollection( syncMode: `eager` | `on-demand`, createPersistence: PersistenceFactory, ): PersistedCollectionHarness { - const persistence = createPersistence(database) + const persistence = createPersistence(database) let seedTxSequence = 0 const seedPersisted = async (rows: Array): Promise => { diff --git a/packages/capacitor-db-sqlite-persistence/src/capacitor-persistence.ts b/packages/capacitor-db-sqlite-persistence/src/capacitor-persistence.ts index 785f07a50..9eea35b9b 100644 --- a/packages/capacitor-db-sqlite-persistence/src/capacitor-persistence.ts +++ b/packages/capacitor-db-sqlite-persistence/src/capacitor-persistence.ts @@ -87,24 +87,16 @@ function resolveAdapterBaseOptions( } } -export function createCapacitorSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createCapacitorSQLitePersistence( options: CapacitorSQLitePersistenceOptions, -): PersistedCollectionPersistence { +): PersistedCollectionPersistence { const { coordinator, schemaMismatchPolicy } = options const driver = createInternalSQLiteDriver(options) const adapterBaseOptions = resolveAdapterBaseOptions(options) const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator() const adapterCache = new Map< string, - ReturnType< - typeof createSQLiteCorePersistenceAdapter< - Record, - string | number - > - > + ReturnType >() const getAdapterForCollection = ( @@ -124,10 +116,7 @@ export function createCapacitorSQLitePersistence< return cachedAdapter } - const adapter = createSQLiteCorePersistenceAdapter< - Record, - string | number - >({ + const adapter = createSQLiteCorePersistenceAdapter({ ...adapterBaseOptions, driver, schemaMismatchPolicy: resolvedSchemaMismatchPolicy, @@ -140,11 +129,8 @@ export function createCapacitorSQLitePersistence< const createCollectionPersistence = ( mode: PersistedCollectionMode, schemaVersion: number | undefined, - ): PersistedCollectionPersistence => ({ - adapter: getAdapterForCollection( - mode, - schemaVersion, - ) as unknown as PersistedCollectionPersistence[`adapter`], + ): PersistedCollectionPersistence => ({ + adapter: getAdapterForCollection(mode, schemaVersion), coordinator: resolvedCoordinator, }) diff --git a/packages/capacitor-db-sqlite-persistence/src/capacitor.ts b/packages/capacitor-db-sqlite-persistence/src/capacitor.ts index fcf7d8eef..304f45df8 100644 --- a/packages/capacitor-db-sqlite-persistence/src/capacitor.ts +++ b/packages/capacitor-db-sqlite-persistence/src/capacitor.ts @@ -12,11 +12,8 @@ export type CapacitorSQLiteSchemaMismatchPolicy = export type { CapacitorSQLiteDatabaseLike } from './capacitor-persistence' export type { SQLiteDBConnection } from './capacitor-persistence' -export function createCapacitorSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createCapacitorSQLitePersistence( options: CapacitorSQLitePersistenceOptions, -): PersistedCollectionPersistence { - return createPersistence(options) +): PersistedCollectionPersistence { + return createPersistence(options) } diff --git a/packages/capacitor-db-sqlite-persistence/tests/capacitor-persistence.test.ts b/packages/capacitor-db-sqlite-persistence/tests/capacitor-persistence.test.ts index 66b22d8b5..69490a9d3 100644 --- a/packages/capacitor-db-sqlite-persistence/tests/capacitor-persistence.test.ts +++ b/packages/capacitor-db-sqlite-persistence/tests/capacitor-persistence.test.ts @@ -40,7 +40,7 @@ it(`persists data across app restart (close and reopen)`, async () => { const collectionId = `todos-restart` const firstDatabase = createCapacitorSQLiteTestDatabase({ filename: dbPath }) - const firstPersistence = createCapacitorSQLitePersistence({ + const firstPersistence = createCapacitorSQLitePersistence({ database: firstDatabase, }) const firstAdapter = firstPersistence.adapter @@ -66,7 +66,7 @@ it(`persists data across app restart (close and reopen)`, async () => { const secondDatabase = createCapacitorSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => secondDatabase.close()) - const secondPersistence = createCapacitorSQLitePersistence({ + const secondPersistence = createCapacitorSQLitePersistence({ database: secondDatabase, }) const secondAdapter = secondPersistence.adapter @@ -90,7 +90,7 @@ it(`keeps all committed rows under rapid mutation bursts`, async () => { const database = createCapacitorSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => database.close()) - const persistence = createCapacitorSQLitePersistence({ + const persistence = createCapacitorSQLitePersistence({ database, }) const adapter = persistence.adapter @@ -126,7 +126,7 @@ it(`shares persistence across multiple collections on one database`, async () => const database = createCapacitorSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => database.close()) - const persistence = createCapacitorSQLitePersistence({ + const persistence = createCapacitorSQLitePersistence({ database, }) @@ -179,7 +179,7 @@ it(`resumes persisted sync after simulated app lifecycle transitions`, async () const database = createCapacitorSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => database.close()) - const persistence = createCapacitorSQLitePersistence({ + const persistence = createCapacitorSQLitePersistence({ database, }) const collection = createCollection( diff --git a/packages/capacitor-db-sqlite-persistence/tests/capacitor-runtime-persistence-contract.test.ts b/packages/capacitor-db-sqlite-persistence/tests/capacitor-runtime-persistence-contract.test.ts index 82da3c0ad..511a06503 100644 --- a/packages/capacitor-db-sqlite-persistence/tests/capacitor-runtime-persistence-contract.test.ts +++ b/packages/capacitor-db-sqlite-persistence/tests/capacitor-runtime-persistence-contract.test.ts @@ -23,7 +23,7 @@ import type { type RuntimePersistenceFactory = (options: { database: CapacitorSQLiteDatabaseLike coordinator?: PersistedCollectionCoordinator -}) => PersistedCollectionPersistence +}) => PersistedCollectionPersistence function createRuntimeDatabaseHarness(): RuntimePersistenceDatabaseHarness { const tempDirectory = mkdtempSync(join(tmpdir(), `db-capacitor-persistence-`)) diff --git a/packages/capacitor-db-sqlite-persistence/tests/capacitor-sqlite-core-adapter-contract.test.ts b/packages/capacitor-db-sqlite-persistence/tests/capacitor-sqlite-core-adapter-contract.test.ts index 8ebc45566..46e12fe86 100644 --- a/packages/capacitor-db-sqlite-persistence/tests/capacitor-sqlite-core-adapter-contract.test.ts +++ b/packages/capacitor-db-sqlite-persistence/tests/capacitor-sqlite-core-adapter-contract.test.ts @@ -5,10 +5,7 @@ import { runSQLiteCoreAdapterContractSuite } from '../../db-sqlite-persistence-c import { CapacitorSQLiteDriver } from '../src/capacitor-sqlite-driver' import { SQLiteCorePersistenceAdapter } from '../../db-sqlite-persistence-core/src' import { createCapacitorSQLiteTestDatabase } from './helpers/capacitor-sqlite-test-db' -import type { - SQLiteCoreAdapterContractTodo, - SQLiteCoreAdapterHarnessFactory, -} from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' +import type { SQLiteCoreAdapterHarnessFactory } from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { const tempDirectory = mkdtempSync(join(tmpdir(), `db-capacitor-core-`)) @@ -18,10 +15,7 @@ const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { }) const driver = new CapacitorSQLiteDriver({ database }) - const adapter = new SQLiteCorePersistenceAdapter< - SQLiteCoreAdapterContractTodo, - string - >({ + const adapter = new SQLiteCorePersistenceAdapter({ driver, ...options, }) diff --git a/packages/cloudflare-durable-objects-db-sqlite-persistence/src/do-persistence.ts b/packages/cloudflare-durable-objects-db-sqlite-persistence/src/do-persistence.ts index c9043c5d4..bf6c7667f 100644 --- a/packages/cloudflare-durable-objects-db-sqlite-persistence/src/do-persistence.ts +++ b/packages/cloudflare-durable-objects-db-sqlite-persistence/src/do-persistence.ts @@ -89,24 +89,16 @@ function resolveAdapterBaseOptions( * Creates a shared Durable Object SQLite persistence instance that can be reused * by many collections in a single Durable Object storage. */ -export function createCloudflareDOSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createCloudflareDOSQLitePersistence( options: CloudflareDOSQLitePersistenceOptions, -): PersistedCollectionPersistence { +): PersistedCollectionPersistence { const { coordinator, schemaMismatchPolicy } = options const driver = resolveSQLiteDriver(options) const adapterBaseOptions = resolveAdapterBaseOptions(options) const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator() const adapterCache = new Map< string, - ReturnType< - typeof createSQLiteCorePersistenceAdapter< - Record, - string | number - > - > + ReturnType >() const getAdapterForCollection = ( @@ -126,10 +118,7 @@ export function createCloudflareDOSQLitePersistence< return cachedAdapter } - const adapter = createSQLiteCorePersistenceAdapter< - Record, - string | number - >({ + const adapter = createSQLiteCorePersistenceAdapter({ ...adapterBaseOptions, driver, schemaMismatchPolicy: resolvedSchemaMismatchPolicy, @@ -142,11 +131,8 @@ export function createCloudflareDOSQLitePersistence< const createCollectionPersistence = ( mode: PersistedCollectionMode, schemaVersion: number | undefined, - ): PersistedCollectionPersistence => ({ - adapter: getAdapterForCollection( - mode, - schemaVersion, - ) as unknown as PersistedCollectionPersistence[`adapter`], + ): PersistedCollectionPersistence => ({ + adapter: getAdapterForCollection(mode, schemaVersion), coordinator: resolvedCoordinator, }) diff --git a/packages/cloudflare-durable-objects-db-sqlite-persistence/tests/do-persistence.test.ts b/packages/cloudflare-durable-objects-db-sqlite-persistence/tests/do-persistence.test.ts index 805c6dff4..b33b72278 100644 --- a/packages/cloudflare-durable-objects-db-sqlite-persistence/tests/do-persistence.test.ts +++ b/packages/cloudflare-durable-objects-db-sqlite-persistence/tests/do-persistence.test.ts @@ -51,17 +51,11 @@ runRuntimePersistenceContractSuite( { createDatabaseHarness: createRuntimeDatabaseHarness, createAdapter: (driver) => - createCloudflareDOSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + createCloudflareDOSQLitePersistence({ storage: (driver as CloudflareDOSQLiteDriver).getStorage(), }).adapter, createPersistence: (driver, coordinator) => - createCloudflareDOSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + createCloudflareDOSQLitePersistence({ storage: (driver as CloudflareDOSQLiteDriver).getStorage(), coordinator, }), @@ -91,10 +85,7 @@ describe(`cloudflare durable object persistence helpers`, () => { const firstStorageHarness = createBetterSqliteDoStorageHarness({ filename: dbPath, }) - const firstPersistence = createCloudflareDOSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const firstPersistence = createCloudflareDOSQLitePersistence({ storage: firstStorageHarness.storage, }) @@ -135,10 +126,7 @@ describe(`cloudflare durable object persistence helpers`, () => { const secondStorageHarness = createBetterSqliteDoStorageHarness({ filename: dbPath, }) - const secondPersistence = createCloudflareDOSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const secondPersistence = createCloudflareDOSQLitePersistence({ storage: secondStorageHarness.storage, }) try { diff --git a/packages/cloudflare-durable-objects-db-sqlite-persistence/tests/do-sqlite-core-adapter-contract.test.ts b/packages/cloudflare-durable-objects-db-sqlite-persistence/tests/do-sqlite-core-adapter-contract.test.ts index 7a056deae..a339817aa 100644 --- a/packages/cloudflare-durable-objects-db-sqlite-persistence/tests/do-sqlite-core-adapter-contract.test.ts +++ b/packages/cloudflare-durable-objects-db-sqlite-persistence/tests/do-sqlite-core-adapter-contract.test.ts @@ -5,10 +5,7 @@ import { runSQLiteCoreAdapterContractSuite } from '../../db-sqlite-persistence-c import { CloudflareDOSQLiteDriver } from '../src/do-driver' import { SQLiteCorePersistenceAdapter } from '../../db-sqlite-persistence-core/src' import { createBetterSqliteDoStorageHarness } from './helpers/better-sqlite-do-storage' -import type { - SQLiteCoreAdapterContractTodo, - SQLiteCoreAdapterHarnessFactory, -} from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' +import type { SQLiteCoreAdapterHarnessFactory } from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { const tempDirectory = mkdtempSync(join(tmpdir(), `db-cf-do-sql-core-`)) @@ -19,10 +16,7 @@ const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { const driver = new CloudflareDOSQLiteDriver({ storage: storageHarness.storage, }) - const adapter = new SQLiteCorePersistenceAdapter< - SQLiteCoreAdapterContractTodo, - string - >({ + const adapter = new SQLiteCorePersistenceAdapter({ driver, ...options, }) diff --git a/packages/db-sqlite-persistence-core/src/persisted.ts b/packages/db-sqlite-persistence-core/src/persisted.ts index 236eddb9c..fa40d2d4b 100644 --- a/packages/db-sqlite-persistence-core/src/persisted.ts +++ b/packages/db-sqlite-persistence-core/src/persisted.ts @@ -199,7 +199,7 @@ export type ReplayableTxDelta< } export type PersistedScannedRow< - T extends object, + T extends object = Record, TKey extends string | number = string | number, > = { key: TKey @@ -212,7 +212,7 @@ export type PersistedRowScanOptions = { } export type PersistedTx< - T extends object, + T extends object = Record, TKey extends string | number = string | number, > = { txId: string @@ -241,18 +241,17 @@ export type PersistedTx< collectionMetadataMutations?: Array } -export interface PersistenceAdapter< - T extends object, - TKey extends string | number = string | number, -> { +export interface PersistenceAdapter { loadSubset: ( collectionId: string, options: LoadSubsetOptions, ctx?: { requiredIndexSignatures?: ReadonlyArray }, - ) => Promise> + ) => Promise< + Array<{ key: string | number; value: Record; metadata?: unknown }> + > applyCommittedTx: ( collectionId: string, - tx: PersistedTx, + tx: PersistedTx, ) => Promise loadCollectionMetadata?: ( collectionId: string, @@ -260,7 +259,7 @@ export interface PersistenceAdapter< scanRows?: ( collectionId: string, options?: PersistedRowScanOptions, - ) => Promise>> + ) => Promise> ensureIndex: ( collectionId: string, signature: string, @@ -317,26 +316,20 @@ export interface PersistedCollectionCoordinator { ) => Promise } -export interface PersistedCollectionPersistence< - T extends object, - TKey extends string | number = string | number, -> { - adapter: PersistenceAdapter +export interface PersistedCollectionPersistence { + adapter: PersistenceAdapter coordinator?: PersistedCollectionCoordinator resolvePersistenceForCollection?: (options: { collectionId: string mode: PersistedCollectionMode schemaVersion?: number - }) => PersistedCollectionPersistence + }) => PersistedCollectionPersistence resolvePersistenceForMode?: ( mode: PersistedCollectionMode, - ) => PersistedCollectionPersistence + ) => PersistedCollectionPersistence } -type PersistedResolvedPersistence< - T extends object, - TKey extends string | number, -> = PersistedCollectionPersistence & { +type PersistedResolvedPersistence = PersistedCollectionPersistence & { coordinator: PersistedCollectionCoordinator } @@ -360,7 +353,7 @@ export type PersistedSyncWrappedOptions< TUtils extends UtilsRecord = UtilsRecord, > = CollectionConfig & { sync: SyncConfig - persistence: PersistedCollectionPersistence + persistence: PersistedCollectionPersistence schemaVersion?: number } @@ -370,7 +363,7 @@ export type PersistedLocalOnlyOptions< TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord, > = Omit, `sync`> & { - persistence: PersistedCollectionPersistence + persistence: PersistedCollectionPersistence schemaVersion?: number } @@ -380,7 +373,7 @@ type PersistedSyncOptionsResult< TSchema extends StandardSchemaV1, TUtils extends UtilsRecord, > = CollectionConfig & { - persistence: PersistedResolvedPersistence + persistence: PersistedResolvedPersistence } type PersistedLocalOnlyOptionsResult< @@ -390,7 +383,7 @@ type PersistedLocalOnlyOptionsResult< TUtils extends UtilsRecord, > = CollectionConfig & { id: string - persistence: PersistedResolvedPersistence + persistence: PersistedResolvedPersistence utils: TUtils & PersistedCollectionUtils } @@ -414,10 +407,7 @@ const REQUIRED_COORDINATOR_METHODS: ReadonlyArray< ] const REQUIRED_ADAPTER_METHODS: ReadonlyArray< - keyof Pick< - PersistenceAdapter, - `loadSubset` | `applyCommittedTx` | `ensureIndex` - > + keyof Pick > = [`loadSubset`, `applyCommittedTx`, `ensureIndex`] const TARGETED_INVALIDATION_KEY_LIMIT = 128 @@ -496,10 +486,7 @@ export function validatePersistedCollectionCoordinator( } } -function validatePersistenceAdapter< - T extends object, - TKey extends string | number, ->(adapter: PersistenceAdapter): void { +function validatePersistenceAdapter(adapter: PersistenceAdapter): void { for (const method of REQUIRED_ADAPTER_METHODS) { if (typeof adapter[method] !== `function`) { throw new InvalidPersistenceAdapterError(method) @@ -507,9 +494,9 @@ function validatePersistenceAdapter< } } -function resolvePersistence( - persistence: PersistedCollectionPersistence, -): PersistedResolvedPersistence { +function resolvePersistence( + persistence: PersistedCollectionPersistence, +): PersistedResolvedPersistence { validatePersistenceAdapter(persistence.adapter) const coordinator = persistence.coordinator ?? new SingleProcessCoordinator() @@ -521,28 +508,22 @@ function resolvePersistence( } } -function resolvePersistenceForMode< - T extends object, - TKey extends string | number, ->( - persistence: PersistedCollectionPersistence, +function resolvePersistenceForMode( + persistence: PersistedCollectionPersistence, mode: PersistedCollectionMode, -): PersistedResolvedPersistence { +): PersistedResolvedPersistence { const modeSpecificPersistence = persistence.resolvePersistenceForMode?.(mode) return resolvePersistence(modeSpecificPersistence ?? persistence) } -function resolvePersistenceForCollection< - T extends object, - TKey extends string | number, ->( - persistence: PersistedCollectionPersistence, +function resolvePersistenceForCollection( + persistence: PersistedCollectionPersistence, options: { collectionId: string mode: PersistedCollectionMode schemaVersion?: number }, -): PersistedResolvedPersistence { +): PersistedResolvedPersistence { const collectionSpecificPersistence = persistence.resolvePersistenceForCollection?.(options) if (collectionSpecificPersistence) { @@ -838,7 +819,7 @@ class PersistedCollectionRuntime< constructor( private readonly mode: PersistedMode, private readonly collectionId: string, - private readonly persistence: PersistedResolvedPersistence, + private readonly persistence: PersistedResolvedPersistence, private readonly syncMode: `eager` | `on-demand`, private readonly dbName: string, ) {} @@ -1195,7 +1176,7 @@ class PersistedCollectionRuntime< ): Promise> { return this.persistence.adapter.loadSubset(this.collectionId, options, { requiredIndexSignatures: this.getRequiredIndexSignatures(), - }) + }) as Promise> } private async scanPersistedRowsUnsafe( @@ -1205,7 +1186,10 @@ class PersistedCollectionRuntime< return [] } - return this.persistence.adapter.scanRows(this.collectionId, options) + return this.persistence.adapter.scanRows( + this.collectionId, + options, + ) as Promise>> } async scanPersistedRows( @@ -1421,7 +1405,10 @@ class PersistedCollectionRuntime< requiresFullReload: transaction.truncate, changedRows: transaction.operations .filter((operation) => operation.type === `update`) - .map((operation) => ({ key: operation.key, value: operation.value })), + .map((operation) => ({ + key: operation.key, + value: operation.value as Record, + })), deletedKeys: transaction.operations .filter((operation) => operation.type === `delete`) .map((operation) => operation.key), @@ -1434,7 +1421,7 @@ class PersistedCollectionRuntime< private createPersistedTxFromOperations( transaction: BufferedSyncTransaction, streamPosition: { term: number; seq: number; rowVersion: number }, - ): PersistedTx { + ): PersistedTx { return { txId: crypto.randomUUID(), term: streamPosition.term, @@ -1444,22 +1431,26 @@ class PersistedCollectionRuntime< mutations: transaction.operations.map((operation) => operation.type === `update` ? { - type: `update`, + type: `update` as const, key: operation.key, - value: operation.value, + value: operation.value as Record, } : { - type: `delete`, + type: `delete` as const, key: operation.key, - value: operation.value, + value: operation.value as Record, }, ), rowMetadataMutations: Array.from( transaction.rowMetadataWrites.entries(), ).map(([key, metadataWrite]) => metadataWrite.type === `delete` - ? { type: `delete`, key } - : { type: `set`, key, value: metadataWrite.value }, + ? { type: `delete` as const, key: key } + : { + type: `set` as const, + key: key, + value: metadataWrite.value, + }, ), collectionMetadataMutations: Array.from( transaction.collectionMetadataWrites.entries(), @@ -1474,7 +1465,7 @@ class PersistedCollectionRuntime< private createPersistedTxFromMutations( mutations: Array>, streamPosition: { term: number; seq: number; rowVersion: number }, - ): PersistedTx { + ): PersistedTx { return { txId: crypto.randomUUID(), term: streamPosition.term, @@ -1483,24 +1474,24 @@ class PersistedCollectionRuntime< mutations: mutations.map((mutation) => { if (mutation.type === `delete`) { return { - type: `delete`, - key: mutation.key as TKey, - value: mutation.original as T, + type: `delete` as const, + key: mutation.key as string | number, + value: mutation.original as Record, } } if (mutation.type === `insert`) { return { - type: `insert`, - key: mutation.key as TKey, - value: mutation.modified, + type: `insert` as const, + key: mutation.key as string | number, + value: mutation.modified as Record, } } return { - type: `update`, - key: mutation.key as TKey, - value: mutation.modified, + type: `update` as const, + key: mutation.key as string | number, + value: mutation.modified as Record, } }), } @@ -1618,12 +1609,12 @@ class PersistedCollectionRuntime< changedRows: mutations .filter((mutation) => mutation.type !== `delete`) .map((mutation) => ({ - key: mutation.key as TKey, - value: mutation.modified, + key: mutation.key as string | number, + value: mutation.modified as Record, })), deletedKeys: mutations .filter((mutation) => mutation.type === `delete`) - .map((mutation) => mutation.key as TKey), + .map((mutation) => mutation.key as string | number), rowMetadataMutations: tx.rowMetadataMutations, collectionMetadataMutations: tx.collectionMetadataMutations, }), @@ -1637,9 +1628,9 @@ class PersistedCollectionRuntime< seq: number txId: string latestRowVersion: number - changedRows: Array<{ key: TKey; value: T }> - deletedKeys: Array - rowMetadataMutations?: Array> + changedRows: Array<{ key: string | number; value: Record }> + deletedKeys: Array + rowMetadataMutations?: Array collectionMetadataMutations?: Array hasMetadataChanges?: boolean requiresFullReload?: boolean @@ -1672,14 +1663,9 @@ class PersistedCollectionRuntime< txId: args.txId, latestRowVersion: args.latestRowVersion, requiresFullReload: false, - changedRows: args.changedRows as Array<{ - key: string | number - value: Record - }>, + changedRows: args.changedRows, deletedKeys: args.deletedKeys, - rowMetadataMutations: rowMetadataMutations as Array< - PersistedRowMetadataMutation - >, + rowMetadataMutations, collectionMetadataMutations, } } diff --git a/packages/db-sqlite-persistence-core/src/sqlite-core-adapter.ts b/packages/db-sqlite-persistence-core/src/sqlite-core-adapter.ts index 0c3a030c2..1bb27684d 100644 --- a/packages/db-sqlite-persistence-core/src/sqlite-core-adapter.ts +++ b/packages/db-sqlite-persistence-core/src/sqlite-core-adapter.ts @@ -608,14 +608,14 @@ function sanitizeExpressionSqlFragment(fragment: string): string { return fragment } -type InMemoryRow = { +type InMemoryRow> = { key: TKey value: T metadata?: unknown rowVersion: number } -function decodeStoredSqliteRows( +function decodeStoredSqliteRows>( storedRows: ReadonlyArray, ): Array> { return storedRows.map((row) => { @@ -1002,10 +1002,7 @@ function buildIndexName(collectionId: string, signature: string): string { return `idx_${hashedPart}_${suffix}` } -export class SQLiteCorePersistenceAdapter< - T extends object, - TKey extends string | number = string | number, -> implements PersistenceAdapter { +export class SQLiteCorePersistenceAdapter implements PersistenceAdapter { private readonly driver: SQLiteDriver private readonly schemaVersion: number private readonly schemaMismatchPolicy: SQLiteCoreAdapterSchemaMismatchPolicy @@ -1085,7 +1082,9 @@ export class SQLiteCorePersistenceAdapter< collectionId: string, options: LoadSubsetOptions, ctx?: { requiredIndexSignatures?: ReadonlyArray }, - ): Promise> { + ): Promise< + Array<{ key: string | number; value: Record; metadata?: unknown }> + > { const tableMapping = await this.ensureCollectionReady(collectionId) await this.touchRequiredIndexes(collectionId, ctx?.requiredIndexSignatures) @@ -1109,7 +1108,10 @@ export class SQLiteCorePersistenceAdapter< this.loadSubsetInternal(tableMapping, whereFromOptions), ]) - const mergedRows = new Map>() + const mergedRows = new Map< + string, + InMemoryRow> + >() for (const row of [...whereCurrentRows, ...whereFromRows]) { mergedRows.set(encodePersistedStorageKey(row.key), row) } @@ -1136,7 +1138,7 @@ export class SQLiteCorePersistenceAdapter< async applyCommittedTx( collectionId: string, - tx: PersistedTx, + tx: PersistedTx, ): Promise { const tableMapping = await this.ensureCollectionReady(collectionId) const collectionTableSql = quoteIdentifier(tableMapping.tableName) @@ -1166,10 +1168,7 @@ export class SQLiteCorePersistenceAdapter< ) const currentRowVersion = versionRows[0]?.latest_row_version ?? 0 const nextRowVersion = Math.max(currentRowVersion + 1, tx.rowVersion) - const replayDelta: ReplayableTxDelta< - Record, - TKey - > | null = tx.truncate + const replayDelta: ReplayableTxDelta | null = tx.truncate ? null : { txId: tx.txId, @@ -1178,7 +1177,7 @@ export class SQLiteCorePersistenceAdapter< .filter((mutation) => mutation.type !== `delete`) .map((mutation) => ({ key: mutation.key, - value: mutation.value as Record, + value: mutation.value, })), deletedKeys: tx.mutations .filter((mutation) => mutation.type === `delete`) @@ -1379,7 +1378,7 @@ export class SQLiteCorePersistenceAdapter< async scanRows( collectionId: string, options?: PersistedRowScanOptions, - ): Promise>> { + ): Promise> { const tableMapping = await this.ensureCollectionReady(collectionId) const collectionTableSql = quoteIdentifier(tableMapping.tableName) @@ -1392,7 +1391,7 @@ export class SQLiteCorePersistenceAdapter< FROM ${collectionTableSql}`, ) - return decodeStoredSqliteRows(storedRows).map((row) => ({ + return decodeStoredSqliteRows(storedRows).map((row) => ({ key: row.key, value: row.value, metadata: row.metadata, @@ -1531,7 +1530,7 @@ export class SQLiteCorePersistenceAdapter< async pullSince( collectionId: string, fromRowVersion: number, - ): Promise> { + ): Promise> { const tableMapping = await this.ensureCollectionReady(collectionId) const collectionTableSql = quoteIdentifier(tableMapping.tableName) const tombstoneTableSql = quoteIdentifier(tableMapping.tombstoneTableName) @@ -1593,9 +1592,9 @@ export class SQLiteCorePersistenceAdapter< } } - const decodeKey = (encodedKey: string): TKey => { + const decodeKey = (encodedKey: string): string | number => { try { - return decodePersistedStorageKey(encodedKey) as TKey + return decodePersistedStorageKey(encodedKey) } catch (error) { throw new InvalidPersistedStorageKeyEncodingError( `${encodedKey}: ${(error as Error).message}`, @@ -1604,10 +1603,9 @@ export class SQLiteCorePersistenceAdapter< } const deltas = replayRows.map((row) => { - const parsed = deserializePersistedRowValue, - TKey - > | null>(row.replay_json ?? `null`) + const parsed = deserializePersistedRowValue( + row.replay_json ?? `null`, + ) if (!parsed) { throw new InvalidPersistedCollectionConfigError( `missing replay payload for applied_tx row`, @@ -1645,7 +1643,7 @@ export class SQLiteCorePersistenceAdapter< private async loadSubsetInternal( tableMapping: CollectionTableMapping, options: LoadSubsetOptions, - ): Promise>> { + ): Promise>>> { const collectionTableSql = quoteIdentifier(tableMapping.tableName) const whereCompiled = options.where ? compileSqlExpression(options.where) @@ -1669,7 +1667,7 @@ export class SQLiteCorePersistenceAdapter< sql, queryParams, ) - const parsedRows = decodeStoredSqliteRows(storedRows) + const parsedRows = decodeStoredSqliteRows(storedRows) const filteredRows = this.applyInMemoryWhere(parsedRows, options.where) const orderedRows = this.applyInMemoryOrderBy(filteredRows, options.orderBy) @@ -1681,9 +1679,9 @@ export class SQLiteCorePersistenceAdapter< } private applyInMemoryWhere( - rows: Array>, + rows: Array>>, where: IR.BasicExpression | undefined, - ): Array> { + ): Array>> { if (!where) { return rows } @@ -1691,15 +1689,15 @@ export class SQLiteCorePersistenceAdapter< const evaluator = compileRowExpressionEvaluator(where) return rows.filter((row) => toBooleanPredicate( - evaluator(row.value as Record) as boolean | null, + evaluator(row.value) as boolean | null, ), ) } private applyInMemoryOrderBy( - rows: Array>, + rows: Array>>, orderBy: IR.OrderBy | undefined, - ): Array> { + ): Array>> { if (!orderBy || orderBy.length === 0) { return rows } @@ -1713,10 +1711,10 @@ export class SQLiteCorePersistenceAdapter< ordered.sort((left, right) => { for (const clause of compiledClauses) { const leftValue = clause.evaluator( - left.value as Record, + left.value, ) const rightValue = clause.evaluator( - right.value as Record, + right.value, ) const comparison = compareOrderByValues( @@ -1747,10 +1745,10 @@ export class SQLiteCorePersistenceAdapter< } private applyInMemoryPagination( - rows: Array>, + rows: Array>>, limit: number | undefined, offset: number | undefined, - ): Array> { + ): Array>> { const start = offset ?? 0 if (limit === undefined) { return rows.slice(start) @@ -2121,9 +2119,8 @@ export class SQLiteCorePersistenceAdapter< } } -export function createSQLiteCorePersistenceAdapter< - T extends object, - TKey extends string | number = string | number, ->(options: SQLiteCoreAdapterOptions): PersistenceAdapter { - return new SQLiteCorePersistenceAdapter(options) +export function createSQLiteCorePersistenceAdapter( + options: SQLiteCoreAdapterOptions, +): PersistenceAdapter { + return new SQLiteCorePersistenceAdapter(options) } diff --git a/packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract.ts b/packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract.ts index 66ef276fe..bfca7987d 100644 --- a/packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract.ts +++ b/packages/db-sqlite-persistence-core/tests/contracts/runtime-persistence-contract.ts @@ -21,13 +21,11 @@ export type RuntimePersistenceDatabaseHarness = { export type RuntimePersistenceContractFactory = { createDatabaseHarness: () => RuntimePersistenceDatabaseHarness - createAdapter: ( - driver: SQLiteDriver, - ) => PersistenceAdapter + createAdapter: (driver: SQLiteDriver) => PersistenceAdapter createPersistence: ( driver: SQLiteDriver, coordinator?: PersistedCollectionCoordinator, - ) => PersistedCollectionPersistence + ) => PersistedCollectionPersistence createCoordinator: () => PersistedCollectionCoordinator } diff --git a/packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract.ts b/packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract.ts index b5ef33e5c..1aae48715 100644 --- a/packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract.ts +++ b/packages/db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract.ts @@ -1,5 +1,4 @@ export { runSQLiteCoreAdapterContractSuite, type SQLiteCoreAdapterHarnessFactory, - type SQLiteCoreAdapterContractTodo, } from '../sqlite-core-adapter.test' diff --git a/packages/db-sqlite-persistence-core/tests/persisted.test-d.ts b/packages/db-sqlite-persistence-core/tests/persisted.test-d.ts index e363a4954..b07f9f787 100644 --- a/packages/db-sqlite-persistence-core/tests/persisted.test-d.ts +++ b/packages/db-sqlite-persistence-core/tests/persisted.test-d.ts @@ -17,7 +17,7 @@ interface SyncExtraUtils extends UtilsRecord { refetch: () => Promise } -const adapter: PersistenceAdapter = { +const adapter: PersistenceAdapter = { loadSubset: () => Promise.resolve([]), applyCommittedTx: () => Promise.resolve(), ensureIndex: () => Promise.resolve(), diff --git a/packages/db-sqlite-persistence-core/tests/persisted.test.ts b/packages/db-sqlite-persistence-core/tests/persisted.test.ts index 63af1f5d4..6f0c35249 100644 --- a/packages/db-sqlite-persistence-core/tests/persisted.test.ts +++ b/packages/db-sqlite-persistence-core/tests/persisted.test.ts @@ -32,7 +32,7 @@ type Todo = { title: string } -type RecordingAdapter = PersistenceAdapter & { +type RecordingAdapter = PersistenceAdapter & { applyCommittedTxCalls: Array<{ collectionId: string tx: { @@ -40,7 +40,10 @@ type RecordingAdapter = PersistenceAdapter & { seq: number rowVersion: number truncate?: boolean - mutations: Array<{ type: `insert` | `update` | `delete`; key: string }> + mutations: Array<{ + type: `insert` | `update` | `delete` + key: string | number + }> } }> ensureIndexCalls: Array<{ collectionId: string; signature: string }> @@ -126,20 +129,20 @@ function createRecordingAdapter( for (const mutation of tx.mutations) { if (mutation.type === `delete`) { - rows.delete(mutation.key) - rowMetadata.delete(mutation.key) + rows.delete(mutation.key as string) + rowMetadata.delete(mutation.key as string) } else { - rows.set(mutation.key, mutation.value) + rows.set(mutation.key as string, mutation.value as Todo) if (mutation.metadataChanged) { - rowMetadata.set(mutation.key, mutation.metadata) + rowMetadata.set(mutation.key as string, mutation.metadata) } } } for (const rowMetadataMutation of tx.rowMetadataMutations ?? []) { if (rowMetadataMutation.type === `delete`) { - rowMetadata.delete(rowMetadataMutation.key) + rowMetadata.delete(rowMetadataMutation.key as string) } else { - rowMetadata.set(rowMetadataMutation.key, rowMetadataMutation.value) + rowMetadata.set(rowMetadataMutation.key as string, rowMetadataMutation.value) } } for (const metadataMutation of tx.collectionMetadataMutations ?? []) { @@ -167,7 +170,7 @@ function createRecordingAdapter( return adapter } -function createNoopAdapter(): PersistenceAdapter { +function createNoopAdapter(): PersistenceAdapter { return { loadSubset: () => Promise.resolve([]), applyCommittedTx: () => Promise.resolve(), @@ -703,7 +706,7 @@ describe(`persistedCollectionOptions`, () => { schemaVersion?: number }> = [] - const persistence: PersistedCollectionPersistence = { + const persistence: PersistedCollectionPersistence = { adapter: baseAdapter, resolvePersistenceForCollection: (options) => { resolverCalls.push(options) diff --git a/packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts b/packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts index 35e28eeba..3be794194 100644 --- a/packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts +++ b/packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts @@ -20,21 +20,7 @@ type Todo = { score: number } -export type SQLiteCoreAdapterContractTodo = Todo -type MixedKeyTodo = { - id: string | number - title: string - createdAt: string - score: number -} - -type FlexibleTodoRow = Record & { - id: string - title: string - createdAt: string - score: number -} const execFileAsync = promisify(execFile) @@ -185,18 +171,18 @@ class SqliteCliDriver implements SQLiteDriver { } type AdapterHarness = { - adapter: SQLiteCorePersistenceAdapter + adapter: SQLiteCorePersistenceAdapter driver: SqliteCliDriver dbPath: string cleanup: () => void | Promise } export type SQLiteCoreAdapterContractHarness = { - adapter: PersistenceAdapter & { + adapter: PersistenceAdapter & { pullSince: ( collectionId: string, fromRowVersion: number, - ) => Promise> + ) => Promise> } driver: SQLiteDriver cleanup: () => void | Promise @@ -204,14 +190,14 @@ export type SQLiteCoreAdapterContractHarness = { function createHarness( options?: Omit< - ConstructorParameters>[0], + ConstructorParameters[0], `driver` >, ): AdapterHarness { const tempDirectory = mkdtempSync(join(tmpdir(), `db-sqlite-core-`)) const dbPath = join(tempDirectory, `state.sqlite`) const driver = new SqliteCliDriver(dbPath) - const adapter = new SQLiteCorePersistenceAdapter({ + const adapter = new SQLiteCorePersistenceAdapter({ driver, ...options, }) @@ -237,7 +223,7 @@ afterEach(async () => { function registerHarness( options?: Omit< - ConstructorParameters>[0], + ConstructorParameters[0], `driver` >, ): AdapterHarness { @@ -248,7 +234,7 @@ function registerHarness( export type SQLiteCoreAdapterHarnessFactory = ( options?: Omit< - ConstructorParameters>[0], + ConstructorParameters[0], `driver` >, ) => SQLiteCoreAdapterContractHarness @@ -787,10 +773,7 @@ export function runSQLiteCoreAdapterContractSuite( it(`persists bigint/date values and evaluates typed predicates`, async () => { const { adapter } = registerContractHarness() - const typedAdapter = adapter as unknown as PersistenceAdapter< - Record, - string - > + const typedAdapter = adapter as unknown as PersistenceAdapter const collectionId = `typed-values` const firstBigInt = BigInt(`9007199254740992`) const secondBigInt = BigInt(`9007199254740997`) @@ -1008,7 +991,7 @@ export function runSQLiteCoreAdapterContractSuite( ], }) - const strictAdapter = new SQLiteCorePersistenceAdapter({ + const strictAdapter = new SQLiteCorePersistenceAdapter({ driver: baseHarness.driver, schemaVersion: 2, schemaMismatchPolicy: `sync-absent-error`, @@ -1017,7 +1000,7 @@ export function runSQLiteCoreAdapterContractSuite( /Schema version mismatch/, ) - const resetAdapter = new SQLiteCorePersistenceAdapter({ + const resetAdapter = new SQLiteCorePersistenceAdapter({ driver: baseHarness.driver, schemaVersion: 2, schemaMismatchPolicy: `sync-present-reset`, @@ -1224,10 +1207,7 @@ export function runSQLiteCoreAdapterContractSuite( it(`keeps numeric and string keys distinct in storage`, async () => { const { driver } = registerContractHarness() - const adapter = new SQLiteCorePersistenceAdapter< - MixedKeyTodo, - string | number - >({ + const adapter = new SQLiteCorePersistenceAdapter({ driver, }) const collectionId = `mixed-keys` @@ -1499,7 +1479,7 @@ export function runSQLiteCoreAdapterContractSuite( it(`falls back to in-memory filtering when SQL json path pushdown is unsupported`, async () => { const { driver } = registerContractHarness() - const adapter = new SQLiteCorePersistenceAdapter( + const adapter = new SQLiteCorePersistenceAdapter( { driver, }, @@ -1549,7 +1529,7 @@ export function runSQLiteCoreAdapterContractSuite( it(`supports alias-qualified refs during in-memory fallback filtering`, async () => { const { driver } = registerContractHarness() - const adapter = new SQLiteCorePersistenceAdapter( + const adapter = new SQLiteCorePersistenceAdapter( { driver, }, diff --git a/packages/electron-db-sqlite-persistence/src/electron-coordinator.ts b/packages/electron-db-sqlite-persistence/src/electron-coordinator.ts index c7d48d17a..ea9735e70 100644 --- a/packages/electron-db-sqlite-persistence/src/electron-coordinator.ts +++ b/packages/electron-db-sqlite-persistence/src/electron-coordinator.ts @@ -81,10 +81,7 @@ type CollectionState = { } // Adapter with pullSince support -type AdapterWithPullSince = PersistenceAdapter< - Record, - string | number -> & { +type AdapterWithPullSince = PersistenceAdapter & { pullSince?: ( collectionId: string, fromRowVersion: number, diff --git a/packages/electron-db-sqlite-persistence/src/main.ts b/packages/electron-db-sqlite-persistence/src/main.ts index 609a6c1c7..e412ebd4f 100644 --- a/packages/electron-db-sqlite-persistence/src/main.ts +++ b/packages/electron-db-sqlite-persistence/src/main.ts @@ -16,10 +16,7 @@ import type { ElectronSerializedError, } from './protocol' -type ElectronMainPersistenceAdapter = PersistenceAdapter< - ElectronPersistedRow, - ElectronPersistedKey -> & { +type ElectronMainPersistenceAdapter = PersistenceAdapter & { loadCollectionMetadata?: ( collectionId: string, ) => Promise> @@ -241,12 +238,9 @@ async function executeRequestAgainstAdapter( } function resolveModeAwarePersistence( - persistence: PersistedCollectionPersistence< - ElectronPersistedRow, - ElectronPersistedKey - >, + persistence: PersistedCollectionPersistence, request: ElectronPersistenceRequestEnvelope, -): PersistedCollectionPersistence { +): PersistedCollectionPersistence { const mode = request.resolution?.mode ?? `sync-absent` const schemaVersion = request.resolution?.schemaVersion const collectionAwarePersistence = @@ -275,10 +269,7 @@ export type ElectronIpcMainLike = { } export type ElectronSQLiteMainProcessOptions = { - persistence: PersistedCollectionPersistence< - ElectronPersistedRow, - ElectronPersistedKey - > + persistence: PersistedCollectionPersistence ipcMain: ElectronIpcMainLike channel?: string } diff --git a/packages/electron-db-sqlite-persistence/src/renderer.ts b/packages/electron-db-sqlite-persistence/src/renderer.ts index fe0f97aed..d454e69ed 100644 --- a/packages/electron-db-sqlite-persistence/src/renderer.ts +++ b/packages/electron-db-sqlite-persistence/src/renderer.ts @@ -149,21 +149,18 @@ function createRendererRequestExecutor(options: { } } -type ElectronRendererResolvedAdapter< - T extends object, - TKey extends string | number = string | number, -> = PersistedCollectionPersistence[`adapter`] & { +type ElectronRendererResolvedAdapter = PersistedCollectionPersistence[`adapter`] & { loadCollectionMetadata: ( collectionId: string, ) => Promise> scanRows: ( collectionId: string, options?: { metadataOnly?: boolean }, - ) => Promise> + ) => Promise; metadata?: unknown }>> pullSince: ( collectionId: string, fromRowVersion: number, - ) => Promise> + ) => Promise> getStreamPosition: (collectionId: string) => Promise<{ latestTerm: number latestSeq: number @@ -171,13 +168,10 @@ type ElectronRendererResolvedAdapter< }> } -function createResolvedRendererAdapter< - T extends object, - TKey extends string | number = string | number, ->( +function createResolvedRendererAdapter( executeRequest: RendererRequestExecutor, resolution?: ElectronPersistenceResolution, -): ElectronRendererResolvedAdapter { +): ElectronRendererResolvedAdapter { return { loadSubset: async ( collectionId: string, @@ -194,11 +188,11 @@ function createResolvedRendererAdapter< resolution, ) - return result as Array<{ key: TKey; value: T }> + return result as Array<{ key: string | number; value: Record }> }, applyCommittedTx: async ( collectionId: string, - tx: PersistedTx, + tx: PersistedTx, string | number>, ): Promise => { await executeRequest( `applyCommittedTx`, @@ -222,14 +216,14 @@ function createResolvedRendererAdapter< scanRows: async ( collectionId: string, options?: { metadataOnly?: boolean }, - ): Promise> => { + ): Promise; metadata?: unknown }>> => { const result = await executeRequest( `scanRows`, collectionId, { options }, resolution, ) - return result as Array<{ key: TKey; value: T; metadata?: unknown }> + return result as Array<{ key: string | number; value: Record; metadata?: unknown }> }, ensureIndex: async ( collectionId: string, @@ -262,7 +256,7 @@ function createResolvedRendererAdapter< pullSince: async ( collectionId: string, fromRowVersion: number, - ): Promise> => { + ): Promise> => { const result = await executeRequest( `pullSince`, collectionId, @@ -271,7 +265,7 @@ function createResolvedRendererAdapter< }, resolution, ) - return result as SQLitePullSinceResult + return result as SQLitePullSinceResult }, getStreamPosition: async ( collectionId: string, @@ -316,12 +310,9 @@ function resolveInvoke( ) } -export function createElectronSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createElectronSQLitePersistence( options: ElectronSQLitePersistenceOptions, -): PersistedCollectionPersistence { +): PersistedCollectionPersistence { const invoke = resolveInvoke(options) const coordinator = options.coordinator ?? new SingleProcessCoordinator() const executeRequest = createRendererRequestExecutor({ @@ -331,7 +322,7 @@ export function createElectronSQLitePersistence< }) const adapterCache = new Map< string, - ElectronRendererResolvedAdapter, string | number> + ElectronRendererResolvedAdapter >() const getAdapterForCollection = ( @@ -346,10 +337,7 @@ export function createElectronSQLitePersistence< return cachedAdapter } - const adapter = createResolvedRendererAdapter< - Record, - string | number - >(executeRequest, { + const adapter = createResolvedRendererAdapter(executeRequest, { mode, schemaVersion, }) @@ -367,11 +355,8 @@ export function createElectronSQLitePersistence< const createCollectionPersistence = ( mode: PersistedCollectionMode, schemaVersion: number | undefined, - ): PersistedCollectionPersistence => ({ - adapter: getAdapterForCollection( - mode, - schemaVersion, - ) as unknown as PersistedCollectionPersistence[`adapter`], + ): PersistedCollectionPersistence => ({ + adapter: getAdapterForCollection(mode, schemaVersion), coordinator, }) diff --git a/packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts b/packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts index 6754539f3..3d1f06aa8 100644 --- a/packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts +++ b/packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts @@ -24,21 +24,13 @@ import type { ElectronPersistenceResponseEnvelope, } from '../src/protocol' -type Todo = { - id: string - title: string - score: number -} type InvokeHarness = { invoke: ElectronPersistenceInvoke close: () => void } -type ElectronMainPersistence = PersistedCollectionPersistence< - Record, - string | number -> +type ElectronMainPersistence = PersistedCollectionPersistence const electronRuntimeBridgeTimeoutMs = isElectronFullE2EEnabled() ? 45_000 @@ -111,10 +103,7 @@ function createInvokeHarness( } const driver = new BetterSqlite3SQLiteDriver({ filename: dbPath }) - const persistence = createNodeSQLitePersistence< - Record, - string | number - >({ + const persistence = createNodeSQLitePersistence({ database: driver.getDatabase(), }) const filteredPersistence = createFilteredPersistence( @@ -185,7 +174,7 @@ describe(`electron sqlite persistence bridge`, () => { const invokeHarness = createInvokeHarness(dbPath, `todos`) activeCleanupFns.push(() => invokeHarness.close()) - const rendererPersistence = createElectronSQLitePersistence({ + const rendererPersistence = createElectronSQLitePersistence({ invoke: async (channel, request) => { expect(channel).toBe(DEFAULT_ELECTRON_PERSISTENCE_CHANNEL) return invokeHarness.invoke(channel, request) @@ -233,12 +222,10 @@ describe(`electron sqlite persistence bridge`, () => { collectionId: `todos`, timeoutMs: electronRuntimeBridgeTimeoutMs, }) - const rendererPersistence = createElectronSQLitePersistence( - { - invoke, - timeoutMs: electronRuntimeBridgeTimeoutMs, - }, - ) + const rendererPersistence = createElectronSQLitePersistence({ + invoke, + timeoutMs: electronRuntimeBridgeTimeoutMs, + }) await rendererPersistence.adapter.applyCommittedTx(`todos`, { txId: `tx-restart-1`, @@ -264,7 +251,7 @@ describe(`electron sqlite persistence bridge`, () => { } const invokeHarnessA = createInvokeHarness(dbPath, `todos`) - const rendererPersistenceA = createElectronSQLitePersistence({ + const rendererPersistenceA = createElectronSQLitePersistence({ invoke: invokeHarnessA.invoke, timeoutMs: electronRuntimeBridgeTimeoutMs, }) @@ -289,7 +276,7 @@ describe(`electron sqlite persistence bridge`, () => { const invokeHarnessB = createInvokeHarness(dbPath, `todos`) activeCleanupFns.push(() => invokeHarnessB.close()) - const rendererPersistenceB = createElectronSQLitePersistence({ + const rendererPersistenceB = createElectronSQLitePersistence({ invoke: invokeHarnessB.invoke, timeoutMs: electronRuntimeBridgeTimeoutMs, }) @@ -301,7 +288,7 @@ describe(`electron sqlite persistence bridge`, () => { const neverInvoke: ElectronPersistenceInvoke = async () => await new Promise(() => {}) - const rendererPersistence = createElectronSQLitePersistence({ + const rendererPersistence = createElectronSQLitePersistence({ invoke: neverInvoke, timeoutMs: 5, }) @@ -315,7 +302,7 @@ describe(`electron sqlite persistence bridge`, () => { const dbPath = createTempDbPath() const invokeHarness = createInvokeHarness(dbPath, `known`, false) activeCleanupFns.push(() => invokeHarness.close()) - const rendererPersistence = createElectronSQLitePersistence({ + const rendererPersistence = createElectronSQLitePersistence({ invoke: invokeHarness.invoke, timeoutMs: electronRuntimeBridgeTimeoutMs, }) @@ -355,10 +342,7 @@ describe(`electron sqlite persistence bridge`, () => { filename: createTempDbPath(), }) activeCleanupFns.push(() => driver.close()) - const persistence = createNodeSQLitePersistence< - Record, - string | number - >({ + const persistence = createNodeSQLitePersistence({ database: driver.getDatabase(), }) diff --git a/packages/electron-db-sqlite-persistence/tests/electron-persisted-collection.e2e.test.ts b/packages/electron-db-sqlite-persistence/tests/electron-persisted-collection.e2e.test.ts index 23976a234..bca27cd59 100644 --- a/packages/electron-db-sqlite-persistence/tests/electron-persisted-collection.e2e.test.ts +++ b/packages/electron-db-sqlite-persistence/tests/electron-persisted-collection.e2e.test.ts @@ -61,10 +61,7 @@ function createInvokeHarness(dbPath: string): InvokeHarness { } const driver = new BetterSqlite3SQLiteDriver({ filename: dbPath }) - const persistence = createNodeSQLitePersistence< - Record, - string | number - >({ + const persistence = createNodeSQLitePersistence({ database: driver.getDatabase(), }) let handler: @@ -120,7 +117,7 @@ function createPersistedCollection( id: string, syncMode: `eager` | `on-demand`, ): PersistedCollectionHarness { - const persistence = createElectronSQLitePersistence({ + const persistence = createElectronSQLitePersistence({ invoke, timeoutMs: isElectronFullE2EEnabled() ? 90_000 : undefined, }) diff --git a/packages/electron-db-sqlite-persistence/tests/electron-sqlite-core-adapter-contract.test.ts b/packages/electron-db-sqlite-persistence/tests/electron-sqlite-core-adapter-contract.test.ts index 663bae336..c27e439b5 100644 --- a/packages/electron-db-sqlite-persistence/tests/electron-sqlite-core-adapter-contract.test.ts +++ b/packages/electron-db-sqlite-persistence/tests/electron-sqlite-core-adapter-contract.test.ts @@ -12,10 +12,7 @@ import { createElectronRuntimeBridgeInvoke, isElectronFullE2EEnabled, } from './e2e/electron-process-client' -import type { - SQLiteCoreAdapterContractTodo, - SQLiteCoreAdapterHarnessFactory, -} from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' +import type { SQLiteCoreAdapterHarnessFactory } from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' import type { ElectronPersistenceInvoke, ElectronPersistenceResponseEnvelope, @@ -44,10 +41,7 @@ const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { adapterOptions: options, }) } else { - const mainAdapter = createSQLiteCorePersistenceAdapter< - Record, - string | number - >({ + const mainAdapter = createSQLiteCorePersistenceAdapter({ driver, ...options, }) @@ -80,10 +74,7 @@ const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { cleanupInvoke = () => dispose() } - const rendererAdapter = createElectronSQLitePersistence< - SQLiteCoreAdapterContractTodo, - string - >({ + const rendererAdapter = createElectronSQLitePersistence({ invoke, timeoutMs: requestTimeoutMs, }).adapter as ReturnType[`adapter`] diff --git a/packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts b/packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts index fd5e1ff39..ed62a8d44 100644 --- a/packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts +++ b/packages/expo-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts @@ -26,9 +26,9 @@ type PersistedCollectionHarness = { seedPersisted: (rows: Array) => Promise } -type MobilePersistenceFactory = ( +type MobilePersistenceFactory = ( database: ReturnType, -) => PersistedCollectionPersistence +) => PersistedCollectionPersistence function createPersistedCollection( database: ReturnType, @@ -36,7 +36,7 @@ function createPersistedCollection( syncMode: `eager` | `on-demand`, createPersistence: MobilePersistenceFactory, ): PersistedCollectionHarness { - const persistence = createPersistence(database) + const persistence = createPersistence(database) let seedTxSequence = 0 const seedPersisted = async (rows: Array): Promise => { if (rows.length === 0) { diff --git a/packages/expo-db-sqlite-persistence/src/expo.ts b/packages/expo-db-sqlite-persistence/src/expo.ts index fda560d97..53cf16e0e 100644 --- a/packages/expo-db-sqlite-persistence/src/expo.ts +++ b/packages/expo-db-sqlite-persistence/src/expo.ts @@ -83,24 +83,16 @@ function resolveAdapterBaseOptions( } } -export function createExpoSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createExpoSQLitePersistence( options: ExpoSQLitePersistenceOptions, -): PersistedCollectionPersistence { +): PersistedCollectionPersistence { const { coordinator, schemaMismatchPolicy } = options const driver = createInternalSQLiteDriver(options) const adapterBaseOptions = resolveAdapterBaseOptions(options) const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator() const adapterCache = new Map< string, - ReturnType< - typeof createSQLiteCorePersistenceAdapter< - Record, - string | number - > - > + ReturnType >() const getAdapterForCollection = ( @@ -120,10 +112,7 @@ export function createExpoSQLitePersistence< return cachedAdapter } - const adapter = createSQLiteCorePersistenceAdapter< - Record, - string | number - >({ + const adapter = createSQLiteCorePersistenceAdapter({ ...adapterBaseOptions, driver, schemaMismatchPolicy: resolvedSchemaMismatchPolicy, @@ -136,11 +125,8 @@ export function createExpoSQLitePersistence< const createCollectionPersistence = ( mode: PersistedCollectionMode, schemaVersion: number | undefined, - ): PersistedCollectionPersistence => ({ - adapter: getAdapterForCollection( - mode, - schemaVersion, - ) as unknown as PersistedCollectionPersistence[`adapter`], + ): PersistedCollectionPersistence => ({ + adapter: getAdapterForCollection(mode, schemaVersion), coordinator: resolvedCoordinator, }) diff --git a/packages/expo-db-sqlite-persistence/tests/expo-persistence.test.ts b/packages/expo-db-sqlite-persistence/tests/expo-persistence.test.ts index b22938157..a7a4cb1f1 100644 --- a/packages/expo-db-sqlite-persistence/tests/expo-persistence.test.ts +++ b/packages/expo-db-sqlite-persistence/tests/expo-persistence.test.ts @@ -35,7 +35,7 @@ it(`persists data across app restart (close and reopen)`, async () => { const collectionId = `todos-restart` const firstDatabase = createExpoSQLiteTestDatabase({ filename: dbPath }) - const firstPersistence = createExpoSQLitePersistence({ + const firstPersistence = createExpoSQLitePersistence({ database: firstDatabase, }) const firstAdapter = firstPersistence.adapter @@ -61,7 +61,7 @@ it(`persists data across app restart (close and reopen)`, async () => { const secondDatabase = createExpoSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => secondDatabase.closeAsync()) - const secondPersistence = createExpoSQLitePersistence({ + const secondPersistence = createExpoSQLitePersistence({ database: secondDatabase, }) const secondAdapter = secondPersistence.adapter @@ -85,7 +85,7 @@ it(`keeps all committed rows under rapid mutation bursts`, async () => { const database = createExpoSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => database.closeAsync()) - const persistence = createExpoSQLitePersistence({ + const persistence = createExpoSQLitePersistence({ database, }) const adapter = persistence.adapter @@ -122,7 +122,7 @@ it(`resumes persisted sync after simulated background/foreground transitions`, a const database = createExpoSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => database.closeAsync()) - const persistence = createExpoSQLitePersistence({ + const persistence = createExpoSQLitePersistence({ database, }) const collection = createCollection( diff --git a/packages/expo-db-sqlite-persistence/tests/expo-runtime-persistence-contract.test.ts b/packages/expo-db-sqlite-persistence/tests/expo-runtime-persistence-contract.test.ts index 8e88a3f1d..ffe68c7f0 100644 --- a/packages/expo-db-sqlite-persistence/tests/expo-runtime-persistence-contract.test.ts +++ b/packages/expo-db-sqlite-persistence/tests/expo-runtime-persistence-contract.test.ts @@ -20,7 +20,7 @@ import type { type RuntimePersistenceFactory = (options: { database: ExpoSQLiteDatabaseLike coordinator?: PersistedCollectionCoordinator -}) => PersistedCollectionPersistence +}) => PersistedCollectionPersistence type SQLiteDriverWithDatabase = ReturnType & { __tanstackDbDatabase: ExpoSQLiteDatabaseLike @@ -118,10 +118,7 @@ describe(`expo runtime persistence helper parity`, () => { }) try { - const firstPersistence = createExpoSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const firstPersistence = createExpoSQLitePersistence({ database: firstDatabase, }) const firstCollectionOptions = persistedCollectionOptions< @@ -161,10 +158,7 @@ describe(`expo runtime persistence helper parity`, () => { filename: dbPath, }) try { - const secondPersistence = createExpoSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const secondPersistence = createExpoSQLitePersistence({ database: secondDatabase, }) diff --git a/packages/expo-db-sqlite-persistence/tests/expo-sqlite-core-adapter-contract.test.ts b/packages/expo-db-sqlite-persistence/tests/expo-sqlite-core-adapter-contract.test.ts index c6cfdf816..a526b2d05 100644 --- a/packages/expo-db-sqlite-persistence/tests/expo-sqlite-core-adapter-contract.test.ts +++ b/packages/expo-db-sqlite-persistence/tests/expo-sqlite-core-adapter-contract.test.ts @@ -5,10 +5,7 @@ import { runSQLiteCoreAdapterContractSuite } from '../../db-sqlite-persistence-c import { ExpoSQLiteDriver } from '../src/expo-sqlite-driver' import { SQLiteCorePersistenceAdapter } from '../../db-sqlite-persistence-core/src' import { createExpoSQLiteTestDatabase } from './helpers/expo-sqlite-test-db' -import type { - SQLiteCoreAdapterContractTodo, - SQLiteCoreAdapterHarnessFactory, -} from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' +import type { SQLiteCoreAdapterHarnessFactory } from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { const tempDirectory = mkdtempSync(join(tmpdir(), `db-expo-core-`)) @@ -18,10 +15,7 @@ const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { }) const driver = new ExpoSQLiteDriver({ database }) - const adapter = new SQLiteCorePersistenceAdapter< - SQLiteCoreAdapterContractTodo, - string - >({ + const adapter = new SQLiteCorePersistenceAdapter({ driver, ...options, }) diff --git a/packages/node-db-sqlite-persistence/e2e/node-persisted-collection.e2e.test.ts b/packages/node-db-sqlite-persistence/e2e/node-persisted-collection.e2e.test.ts index 221fdd950..0fcc91367 100644 --- a/packages/node-db-sqlite-persistence/e2e/node-persisted-collection.e2e.test.ts +++ b/packages/node-db-sqlite-persistence/e2e/node-persisted-collection.e2e.test.ts @@ -32,7 +32,7 @@ function createPersistedCollection( id: string, syncMode: `eager` | `on-demand`, ): PersistedCollectionHarness { - const persistence = createNodeSQLitePersistence({ + const persistence = createNodeSQLitePersistence({ database, }) let seedTxSequence = 0 diff --git a/packages/node-db-sqlite-persistence/src/node-persistence.ts b/packages/node-db-sqlite-persistence/src/node-persistence.ts index f63031e81..2dbd718c3 100644 --- a/packages/node-db-sqlite-persistence/src/node-persistence.ts +++ b/packages/node-db-sqlite-persistence/src/node-persistence.ts @@ -92,24 +92,16 @@ function resolveAdapterBaseOptions( * collections on the same database. Collection-specific schema versions are * resolved by `persistedCollectionOptions` via `resolvePersistenceForCollection`. */ -export function createNodeSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createNodeSQLitePersistence( options: NodeSQLitePersistenceOptions, -): PersistedCollectionPersistence { +): PersistedCollectionPersistence { const { coordinator, schemaMismatchPolicy } = options const driver = createInternalSQLiteDriver(options) const adapterBaseOptions = resolveAdapterBaseOptions(options) const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator() const adapterCache = new Map< string, - ReturnType< - typeof createSQLiteCorePersistenceAdapter< - Record, - string | number - > - > + ReturnType >() const getAdapterForCollection = ( @@ -129,10 +121,7 @@ export function createNodeSQLitePersistence< return cachedAdapter } - const adapter = createSQLiteCorePersistenceAdapter< - Record, - string | number - >({ + const adapter = createSQLiteCorePersistenceAdapter({ ...adapterBaseOptions, driver, schemaMismatchPolicy: resolvedSchemaMismatchPolicy, @@ -145,11 +134,8 @@ export function createNodeSQLitePersistence< const createCollectionPersistence = ( mode: PersistedCollectionMode, schemaVersion: number | undefined, - ): PersistedCollectionPersistence => ({ - adapter: getAdapterForCollection( - mode, - schemaVersion, - ) as unknown as PersistedCollectionPersistence[`adapter`], + ): PersistedCollectionPersistence => ({ + adapter: getAdapterForCollection(mode, schemaVersion), coordinator: resolvedCoordinator, }) diff --git a/packages/node-db-sqlite-persistence/tests/node-persistence.test.ts b/packages/node-db-sqlite-persistence/tests/node-persistence.test.ts index 4cf036c13..12fc53f93 100644 --- a/packages/node-db-sqlite-persistence/tests/node-persistence.test.ts +++ b/packages/node-db-sqlite-persistence/tests/node-persistence.test.ts @@ -40,11 +40,11 @@ function createRuntimeDatabaseHarness(): RuntimePersistenceDatabaseHarness { runRuntimePersistenceContractSuite(`node runtime persistence helpers`, { createDatabaseHarness: createRuntimeDatabaseHarness, createAdapter: (driver) => - createNodeSQLitePersistence({ + createNodeSQLitePersistence({ database: (driver as BetterSqlite3SQLiteDriver).getDatabase(), }).adapter, createPersistence: (driver, coordinator) => - createNodeSQLitePersistence({ + createNodeSQLitePersistence({ database: (driver as BetterSqlite3SQLiteDriver).getDatabase(), coordinator, }), @@ -88,10 +88,7 @@ describe(`node persistence helpers`, () => { const database = new BetterSqlite3(dbPath) try { - const persistence = createNodeSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const persistence = createNodeSQLitePersistence({ database, }) @@ -137,10 +134,7 @@ describe(`node persistence helpers`, () => { const firstDatabase = new BetterSqlite3(dbPath) try { - const firstPersistence = createNodeSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const firstPersistence = createNodeSQLitePersistence({ database: firstDatabase, }) const firstCollectionOptions = persistedCollectionOptions< @@ -179,10 +173,7 @@ describe(`node persistence helpers`, () => { const secondDatabase = new BetterSqlite3(dbPath) try { - const secondPersistence = createNodeSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const secondPersistence = createNodeSQLitePersistence({ database: secondDatabase, }) const syncAbsentOptions = persistedCollectionOptions< diff --git a/packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract.test.ts b/packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract.test.ts index 77035b6df..55b41e20e 100644 --- a/packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract.test.ts +++ b/packages/node-db-sqlite-persistence/tests/node-sqlite-core-adapter-contract.test.ts @@ -4,19 +4,13 @@ import { join } from 'node:path' import { runSQLiteCoreAdapterContractSuite } from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' import { BetterSqlite3SQLiteDriver } from '../src/node-driver' import { SQLiteCorePersistenceAdapter } from '../../db-sqlite-persistence-core/src' -import type { - SQLiteCoreAdapterContractTodo, - SQLiteCoreAdapterHarnessFactory, -} from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' +import type { SQLiteCoreAdapterHarnessFactory } from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { const tempDirectory = mkdtempSync(join(tmpdir(), `db-node-sqlite-core-`)) const dbPath = join(tempDirectory, `state.sqlite`) const driver = new BetterSqlite3SQLiteDriver({ filename: dbPath }) - const adapter = new SQLiteCorePersistenceAdapter< - SQLiteCoreAdapterContractTodo, - string - >({ + const adapter = new SQLiteCorePersistenceAdapter({ driver, ...options, }) diff --git a/packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts b/packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts index a85b9cd41..1291551a6 100644 --- a/packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts +++ b/packages/react-native-db-sqlite-persistence/e2e/mobile-persisted-collection-conformance-suite.ts @@ -26,9 +26,9 @@ type PersistedCollectionHarness = { seedPersisted: (rows: Array) => Promise } -type MobilePersistenceFactory = ( +type MobilePersistenceFactory = ( database: ReturnType, -) => PersistedCollectionPersistence +) => PersistedCollectionPersistence function createPersistedCollection( database: ReturnType, @@ -36,7 +36,7 @@ function createPersistedCollection( syncMode: `eager` | `on-demand`, createPersistence: MobilePersistenceFactory, ): PersistedCollectionHarness { - const persistence = createPersistence(database) + const persistence = createPersistence(database) let seedTxSequence = 0 const seedPersisted = async (rows: Array): Promise => { if (rows.length === 0) { diff --git a/packages/react-native-db-sqlite-persistence/src/mobile-persistence.ts b/packages/react-native-db-sqlite-persistence/src/mobile-persistence.ts index 099a51c17..073994f38 100644 --- a/packages/react-native-db-sqlite-persistence/src/mobile-persistence.ts +++ b/packages/react-native-db-sqlite-persistence/src/mobile-persistence.ts @@ -85,24 +85,16 @@ function resolveAdapterBaseOptions( } } -export function createMobileSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createMobileSQLitePersistence( options: MobileSQLitePersistenceOptions, -): PersistedCollectionPersistence { +): PersistedCollectionPersistence { const { coordinator, schemaMismatchPolicy } = options const driver = createInternalSQLiteDriver(options) const adapterBaseOptions = resolveAdapterBaseOptions(options) const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator() const adapterCache = new Map< string, - ReturnType< - typeof createSQLiteCorePersistenceAdapter< - Record, - string | number - > - > + ReturnType >() const getAdapterForCollection = ( @@ -122,10 +114,7 @@ export function createMobileSQLitePersistence< return cachedAdapter } - const adapter = createSQLiteCorePersistenceAdapter< - Record, - string | number - >({ + const adapter = createSQLiteCorePersistenceAdapter({ ...adapterBaseOptions, driver, schemaMismatchPolicy: resolvedSchemaMismatchPolicy, @@ -138,11 +127,8 @@ export function createMobileSQLitePersistence< const createCollectionPersistence = ( mode: PersistedCollectionMode, schemaVersion: number | undefined, - ): PersistedCollectionPersistence => ({ - adapter: getAdapterForCollection( - mode, - schemaVersion, - ) as unknown as PersistedCollectionPersistence[`adapter`], + ): PersistedCollectionPersistence => ({ + adapter: getAdapterForCollection(mode, schemaVersion), coordinator: resolvedCoordinator, }) diff --git a/packages/react-native-db-sqlite-persistence/src/react-native.ts b/packages/react-native-db-sqlite-persistence/src/react-native.ts index 354855d30..7a274aeee 100644 --- a/packages/react-native-db-sqlite-persistence/src/react-native.ts +++ b/packages/react-native-db-sqlite-persistence/src/react-native.ts @@ -10,11 +10,8 @@ export type ReactNativeSQLiteSchemaMismatchPolicy = MobileSQLiteSchemaMismatchPolicy export type { OpSQLiteDatabaseLike } from './mobile-persistence' -export function createReactNativeSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createReactNativeSQLitePersistence( options: ReactNativeSQLitePersistenceOptions, -): PersistedCollectionPersistence { - return createMobileSQLitePersistence(options) +): PersistedCollectionPersistence { + return createMobileSQLitePersistence(options) } diff --git a/packages/react-native-db-sqlite-persistence/tests/expo-sqlite-core-adapter-contract.test.ts b/packages/react-native-db-sqlite-persistence/tests/expo-sqlite-core-adapter-contract.test.ts index d2f741a6e..613d16828 100644 --- a/packages/react-native-db-sqlite-persistence/tests/expo-sqlite-core-adapter-contract.test.ts +++ b/packages/react-native-db-sqlite-persistence/tests/expo-sqlite-core-adapter-contract.test.ts @@ -5,10 +5,7 @@ import { runSQLiteCoreAdapterContractSuite } from '../../db-sqlite-persistence-c import { OpSQLiteDriver } from '../src/op-sqlite-driver' import { SQLiteCorePersistenceAdapter } from '../../db-sqlite-persistence-core/src' import { createOpSQLiteTestDatabase } from './helpers/op-sqlite-test-db' -import type { - SQLiteCoreAdapterContractTodo, - SQLiteCoreAdapterHarnessFactory, -} from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' +import type { SQLiteCoreAdapterHarnessFactory } from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { const tempDirectory = mkdtempSync(join(tmpdir(), `db-expo-sqlite-core-`)) @@ -19,10 +16,7 @@ const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { }) const driver = new OpSQLiteDriver({ database }) - const adapter = new SQLiteCorePersistenceAdapter< - SQLiteCoreAdapterContractTodo, - string - >({ + const adapter = new SQLiteCorePersistenceAdapter({ driver, ...options, }) diff --git a/packages/react-native-db-sqlite-persistence/tests/mobile-runtime-persistence-contract.test.ts b/packages/react-native-db-sqlite-persistence/tests/mobile-runtime-persistence-contract.test.ts index 94194a538..ebfedef63 100644 --- a/packages/react-native-db-sqlite-persistence/tests/mobile-runtime-persistence-contract.test.ts +++ b/packages/react-native-db-sqlite-persistence/tests/mobile-runtime-persistence-contract.test.ts @@ -23,7 +23,7 @@ import type { type RuntimePersistenceFactory = (options: { database: OpSQLiteDatabaseLike coordinator?: PersistedCollectionCoordinator -}) => PersistedCollectionPersistence +}) => PersistedCollectionPersistence function createRuntimeDatabaseHarness(): RuntimePersistenceDatabaseHarness { const tempDirectory = mkdtempSync(join(tmpdir(), `db-mobile-persistence-`)) diff --git a/packages/react-native-db-sqlite-persistence/tests/react-native-persistence.test.ts b/packages/react-native-db-sqlite-persistence/tests/react-native-persistence.test.ts index 179e9fc66..65269b814 100644 --- a/packages/react-native-db-sqlite-persistence/tests/react-native-persistence.test.ts +++ b/packages/react-native-db-sqlite-persistence/tests/react-native-persistence.test.ts @@ -38,7 +38,7 @@ it(`persists data across app restart (close and reopen)`, async () => { const collectionId = `todos-restart` const firstDatabase = createOpSQLiteTestDatabase({ filename: dbPath }) - const firstPersistence = createReactNativeSQLitePersistence({ + const firstPersistence = createReactNativeSQLitePersistence({ database: firstDatabase, }) const firstAdapter = firstPersistence.adapter @@ -64,7 +64,7 @@ it(`persists data across app restart (close and reopen)`, async () => { const secondDatabase = createOpSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => Promise.resolve(secondDatabase.close())) - const secondPersistence = createReactNativeSQLitePersistence({ + const secondPersistence = createReactNativeSQLitePersistence({ database: secondDatabase, }) const secondAdapter = secondPersistence.adapter @@ -87,7 +87,7 @@ it(`shared react-native api persists across expo-style restart`, async () => { const collectionId = `todos-restart-expo` const firstDatabase = createOpSQLiteTestDatabase({ filename: dbPath }) - const firstPersistence = createReactNativeSQLitePersistence({ + const firstPersistence = createReactNativeSQLitePersistence({ database: firstDatabase, }) const firstAdapter = firstPersistence.adapter @@ -113,7 +113,7 @@ it(`shared react-native api persists across expo-style restart`, async () => { const secondDatabase = createOpSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => Promise.resolve(secondDatabase.close())) - const secondPersistence = createReactNativeSQLitePersistence({ + const secondPersistence = createReactNativeSQLitePersistence({ database: secondDatabase, }) const secondAdapter = secondPersistence.adapter @@ -137,7 +137,7 @@ it(`keeps all committed rows under rapid mutation bursts`, async () => { const database = createOpSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => Promise.resolve(database.close())) - const persistence = createReactNativeSQLitePersistence({ + const persistence = createReactNativeSQLitePersistence({ database, }) const adapter = persistence.adapter @@ -174,17 +174,12 @@ it(`uses a single react-native api across runtime aliases`, async () => { const database = createOpSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => Promise.resolve(database.close())) - const reactNativePersistence = createReactNativeSQLitePersistence< - Todo, - string - >({ + const reactNativePersistence = createReactNativeSQLitePersistence({ + database, + }) + const sharedApiPersistence = createReactNativeSQLitePersistence({ database, }) - const sharedApiPersistence = createReactNativeSQLitePersistence( - { - database, - }, - ) await reactNativePersistence.adapter.applyCommittedTx(collectionId, { txId: `tx-entrypoint-1`, @@ -214,7 +209,7 @@ it(`resumes persisted sync after simulated background/foreground transitions`, a const database = createOpSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => Promise.resolve(database.close())) - const persistence = createReactNativeSQLitePersistence({ + const persistence = createReactNativeSQLitePersistence({ database, }) const collection = createCollection( @@ -268,7 +263,7 @@ it(`shared api resumes persisted sync in expo-style lifecycle`, async () => { const database = createOpSQLiteTestDatabase({ filename: dbPath }) activeCleanupFns.push(() => Promise.resolve(database.close())) - const persistence = createReactNativeSQLitePersistence({ + const persistence = createReactNativeSQLitePersistence({ database, }) const collection = createCollection( diff --git a/packages/react-native-db-sqlite-persistence/tests/react-native-sqlite-core-adapter-contract.test.ts b/packages/react-native-db-sqlite-persistence/tests/react-native-sqlite-core-adapter-contract.test.ts index 03e34dcf0..cb2734946 100644 --- a/packages/react-native-db-sqlite-persistence/tests/react-native-sqlite-core-adapter-contract.test.ts +++ b/packages/react-native-db-sqlite-persistence/tests/react-native-sqlite-core-adapter-contract.test.ts @@ -5,10 +5,7 @@ import { runSQLiteCoreAdapterContractSuite } from '../../db-sqlite-persistence-c import { OpSQLiteDriver } from '../src/op-sqlite-driver' import { SQLiteCorePersistenceAdapter } from '../../db-sqlite-persistence-core/src' import { createOpSQLiteTestDatabase } from './helpers/op-sqlite-test-db' -import type { - SQLiteCoreAdapterContractTodo, - SQLiteCoreAdapterHarnessFactory, -} from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' +import type { SQLiteCoreAdapterHarnessFactory } from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { const tempDirectory = mkdtempSync(join(tmpdir(), `db-rn-sqlite-core-`)) @@ -19,10 +16,7 @@ const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { }) const driver = new OpSQLiteDriver({ database }) - const adapter = new SQLiteCorePersistenceAdapter< - SQLiteCoreAdapterContractTodo, - string - >({ + const adapter = new SQLiteCorePersistenceAdapter({ driver, ...options, }) diff --git a/packages/tauri-db-sqlite-persistence/e2e/app/src/register-tauri-e2e-suite.ts b/packages/tauri-db-sqlite-persistence/e2e/app/src/register-tauri-e2e-suite.ts index 594c7abd3..336e7fb4e 100644 --- a/packages/tauri-db-sqlite-persistence/e2e/app/src/register-tauri-e2e-suite.ts +++ b/packages/tauri-db-sqlite-persistence/e2e/app/src/register-tauri-e2e-suite.ts @@ -4,9 +4,6 @@ import { registerPersistedCollectionConformanceSuite } from '../../shared/regist import type { PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core' import type { TauriSQLiteDatabaseLike } from '../../../src' -type PersistableRow = { - id: string -} export function registerTauriNativeE2ESuite(options: { suiteName: string @@ -19,10 +16,10 @@ export function registerTauriNativeE2ESuite(options: { createTauriPersistedCollectionHarnessConfig({ database: options.database, suiteId: options.runId, - createPersistence: ( + createPersistence: ( database: TauriSQLiteDatabaseLike, - ): PersistedCollectionPersistence => - createTauriSQLitePersistence({ + ): PersistedCollectionPersistence => + createTauriSQLitePersistence({ database, }), }), diff --git a/packages/tauri-db-sqlite-persistence/e2e/shared/tauri-persisted-collection-harness.ts b/packages/tauri-db-sqlite-persistence/e2e/shared/tauri-persisted-collection-harness.ts index ca64f141e..a3d046ddc 100644 --- a/packages/tauri-db-sqlite-persistence/e2e/shared/tauri-persisted-collection-harness.ts +++ b/packages/tauri-db-sqlite-persistence/e2e/shared/tauri-persisted-collection-harness.ts @@ -25,9 +25,9 @@ type PersistedTransactionHandle = { } } -type PersistenceFactory = ( +type PersistenceFactory = ( database: TDatabase, -) => PersistedCollectionPersistence +) => PersistedCollectionPersistence type DatabaseLike = { close?: () => Promise | unknown @@ -41,7 +41,7 @@ function createPersistedCollection( syncMode: `eager` | `on-demand`, createPersistence: PersistenceFactory, ): PersistedCollectionHarness { - const persistence = createPersistence(database) + const persistence = createPersistence(database) let seedTxSequence = 0 const seedPersisted = async (rows: Array): Promise => { diff --git a/packages/tauri-db-sqlite-persistence/src/tauri-persistence.ts b/packages/tauri-db-sqlite-persistence/src/tauri-persistence.ts index e21f1718d..956f64dfd 100644 --- a/packages/tauri-db-sqlite-persistence/src/tauri-persistence.ts +++ b/packages/tauri-db-sqlite-persistence/src/tauri-persistence.ts @@ -97,24 +97,16 @@ function resolveAdapterBaseOptions( } } -export function createTauriSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createTauriSQLitePersistence( options: TauriSQLitePersistenceOptions, -): PersistedCollectionPersistence { +): PersistedCollectionPersistence { const { coordinator, schemaMismatchPolicy } = options const driver = createInternalSQLiteDriver(options) const adapterBaseOptions = resolveAdapterBaseOptions(options) const resolvedCoordinator = coordinator ?? new SingleProcessCoordinator() const adapterCache = new Map< string, - ReturnType< - typeof createSQLiteCorePersistenceAdapter< - Record, - string | number - > - > + ReturnType >() const getAdapterForCollection = ( @@ -134,10 +126,7 @@ export function createTauriSQLitePersistence< return cachedAdapter } - const adapter = createSQLiteCorePersistenceAdapter< - Record, - string | number - >({ + const adapter = createSQLiteCorePersistenceAdapter({ ...adapterBaseOptions, driver, schemaMismatchPolicy: resolvedSchemaMismatchPolicy, @@ -150,11 +139,8 @@ export function createTauriSQLitePersistence< const createCollectionPersistence = ( mode: PersistedCollectionMode, schemaVersion: number | undefined, - ): PersistedCollectionPersistence => ({ - adapter: getAdapterForCollection( - mode, - schemaVersion, - ) as unknown as PersistedCollectionPersistence[`adapter`], + ): PersistedCollectionPersistence => ({ + adapter: getAdapterForCollection(mode, schemaVersion), coordinator: resolvedCoordinator, }) diff --git a/packages/tauri-db-sqlite-persistence/src/tauri.ts b/packages/tauri-db-sqlite-persistence/src/tauri.ts index 73d954962..7d7508dc3 100644 --- a/packages/tauri-db-sqlite-persistence/src/tauri.ts +++ b/packages/tauri-db-sqlite-persistence/src/tauri.ts @@ -10,11 +10,8 @@ export type TauriSQLiteSchemaMismatchPolicy = TauriSQLiteSchemaMismatchPolicyBase export type { TauriSQLiteDatabaseLike } from './tauri-persistence' -export function createTauriSQLitePersistence< - T extends object, - TKey extends string | number = string | number, ->( +export function createTauriSQLitePersistence( options: TauriSQLitePersistenceOptions, -): PersistedCollectionPersistence { - return createPersistence(options) +): PersistedCollectionPersistence { + return createPersistence(options) } diff --git a/packages/tauri-db-sqlite-persistence/tests/tauri-runtime-persistence-contract.test.ts b/packages/tauri-db-sqlite-persistence/tests/tauri-runtime-persistence-contract.test.ts index c77486d53..193eb2e83 100644 --- a/packages/tauri-db-sqlite-persistence/tests/tauri-runtime-persistence-contract.test.ts +++ b/packages/tauri-db-sqlite-persistence/tests/tauri-runtime-persistence-contract.test.ts @@ -47,11 +47,11 @@ function createRuntimeDatabaseHarness(): RuntimePersistenceDatabaseHarness { runRuntimePersistenceContractSuite(`tauri runtime persistence helpers`, { createDatabaseHarness: createRuntimeDatabaseHarness, createAdapter: (driver) => - createTauriSQLitePersistence({ + createTauriSQLitePersistence({ database: (driver as TauriSQLiteDriver).getDatabase(), }).adapter, createPersistence: (driver, coordinator) => - createTauriSQLitePersistence({ + createTauriSQLitePersistence({ database: (driver as TauriSQLiteDriver).getDatabase(), coordinator, }), @@ -63,10 +63,7 @@ describe(`tauri runtime persistence helpers`, () => { const runtimeHarness = createRuntimeDatabaseHarness() const driver = runtimeHarness.createDriver() try { - const persistence = createTauriSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const persistence = createTauriSQLitePersistence({ database: (driver as TauriSQLiteDriver).getDatabase(), }) const resolvePersistenceForCollection = @@ -150,10 +147,7 @@ describe(`tauri runtime persistence helpers`, () => { const firstDatabase = createTauriSQLiteTestDatabase({ filename: dbPath }) try { - const firstPersistence = createTauriSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const firstPersistence = createTauriSQLitePersistence({ database: firstDatabase, }) const firstCollectionOptions = persistedCollectionOptions< @@ -192,10 +186,7 @@ describe(`tauri runtime persistence helpers`, () => { const secondDatabase = createTauriSQLiteTestDatabase({ filename: dbPath }) try { - const secondPersistence = createTauriSQLitePersistence< - RuntimePersistenceContractTodo, - string - >({ + const secondPersistence = createTauriSQLitePersistence({ database: secondDatabase, }) const syncAbsentOptions = persistedCollectionOptions< diff --git a/packages/tauri-db-sqlite-persistence/tests/tauri-sqlite-core-adapter-contract.test.ts b/packages/tauri-db-sqlite-persistence/tests/tauri-sqlite-core-adapter-contract.test.ts index ea1561984..c0edc64d7 100644 --- a/packages/tauri-db-sqlite-persistence/tests/tauri-sqlite-core-adapter-contract.test.ts +++ b/packages/tauri-db-sqlite-persistence/tests/tauri-sqlite-core-adapter-contract.test.ts @@ -5,10 +5,7 @@ import { runSQLiteCoreAdapterContractSuite } from '../../db-sqlite-persistence-c import { TauriSQLiteDriver } from '../src/tauri-sql-driver' import { SQLiteCorePersistenceAdapter } from '../../db-sqlite-persistence-core/src' import { createTauriSQLiteTestDatabase } from './helpers/tauri-sql-test-db' -import type { - SQLiteCoreAdapterContractTodo, - SQLiteCoreAdapterHarnessFactory, -} from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' +import type { SQLiteCoreAdapterHarnessFactory } from '../../db-sqlite-persistence-core/tests/contracts/sqlite-core-adapter-contract' const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { const tempDirectory = mkdtempSync(join(tmpdir(), `db-tauri-core-`)) @@ -16,10 +13,7 @@ const createHarness: SQLiteCoreAdapterHarnessFactory = (options) => { const database = createTauriSQLiteTestDatabase({ filename: dbPath }) const driver = new TauriSQLiteDriver({ database }) - const adapter = new SQLiteCorePersistenceAdapter< - SQLiteCoreAdapterContractTodo, - string - >({ + const adapter = new SQLiteCorePersistenceAdapter({ driver, ...options, }) From cd599a1b36ad8261babfcb6cb0ff79e1eb1262d2 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:33:49 +0000 Subject: [PATCH 2/3] ci: apply automated fixes --- .../src/persisted.ts | 16 +++-- .../src/sqlite-core-adapter.ts | 35 +++++----- .../tests/persisted.test.ts | 5 +- .../tests/sqlite-core-adapter.test.ts | 18 ++--- .../src/renderer.ts | 67 ++++++++++++------- .../tests/electron-ipc.test.ts | 1 - .../e2e/app/src/register-tauri-e2e-suite.ts | 1 - 7 files changed, 79 insertions(+), 64 deletions(-) diff --git a/packages/db-sqlite-persistence-core/src/persisted.ts b/packages/db-sqlite-persistence-core/src/persisted.ts index fa40d2d4b..7c20e9688 100644 --- a/packages/db-sqlite-persistence-core/src/persisted.ts +++ b/packages/db-sqlite-persistence-core/src/persisted.ts @@ -247,12 +247,13 @@ export interface PersistenceAdapter { options: LoadSubsetOptions, ctx?: { requiredIndexSignatures?: ReadonlyArray }, ) => Promise< - Array<{ key: string | number; value: Record; metadata?: unknown }> + Array<{ + key: string | number + value: Record + metadata?: unknown + }> > - applyCommittedTx: ( - collectionId: string, - tx: PersistedTx, - ) => Promise + applyCommittedTx: (collectionId: string, tx: PersistedTx) => Promise loadCollectionMetadata?: ( collectionId: string, ) => Promise> @@ -407,7 +408,10 @@ const REQUIRED_COORDINATOR_METHODS: ReadonlyArray< ] const REQUIRED_ADAPTER_METHODS: ReadonlyArray< - keyof Pick + keyof Pick< + PersistenceAdapter, + `loadSubset` | `applyCommittedTx` | `ensureIndex` + > > = [`loadSubset`, `applyCommittedTx`, `ensureIndex`] const TARGETED_INVALIDATION_KEY_LIMIT = 128 diff --git a/packages/db-sqlite-persistence-core/src/sqlite-core-adapter.ts b/packages/db-sqlite-persistence-core/src/sqlite-core-adapter.ts index 1bb27684d..6bbf06c4d 100644 --- a/packages/db-sqlite-persistence-core/src/sqlite-core-adapter.ts +++ b/packages/db-sqlite-persistence-core/src/sqlite-core-adapter.ts @@ -608,16 +608,20 @@ function sanitizeExpressionSqlFragment(fragment: string): string { return fragment } -type InMemoryRow> = { +type InMemoryRow< + TKey extends string | number = string | number, + T extends object = Record, +> = { key: TKey value: T metadata?: unknown rowVersion: number } -function decodeStoredSqliteRows>( - storedRows: ReadonlyArray, -): Array> { +function decodeStoredSqliteRows< + TKey extends string | number = string | number, + T extends object = Record, +>(storedRows: ReadonlyArray): Array> { return storedRows.map((row) => { const key = decodePersistedStorageKey(row.key) as TKey const value = deserializePersistedRowValue(row.value) @@ -1083,7 +1087,11 @@ export class SQLiteCorePersistenceAdapter implements PersistenceAdapter { options: LoadSubsetOptions, ctx?: { requiredIndexSignatures?: ReadonlyArray }, ): Promise< - Array<{ key: string | number; value: Record; metadata?: unknown }> + Array<{ + key: string | number + value: Record + metadata?: unknown + }> > { const tableMapping = await this.ensureCollectionReady(collectionId) await this.touchRequiredIndexes(collectionId, ctx?.requiredIndexSignatures) @@ -1136,10 +1144,7 @@ export class SQLiteCorePersistenceAdapter implements PersistenceAdapter { })) } - async applyCommittedTx( - collectionId: string, - tx: PersistedTx, - ): Promise { + async applyCommittedTx(collectionId: string, tx: PersistedTx): Promise { const tableMapping = await this.ensureCollectionReady(collectionId) const collectionTableSql = quoteIdentifier(tableMapping.tableName) const tombstoneTableSql = quoteIdentifier(tableMapping.tombstoneTableName) @@ -1688,9 +1693,7 @@ export class SQLiteCorePersistenceAdapter implements PersistenceAdapter { const evaluator = compileRowExpressionEvaluator(where) return rows.filter((row) => - toBooleanPredicate( - evaluator(row.value) as boolean | null, - ), + toBooleanPredicate(evaluator(row.value) as boolean | null), ) } @@ -1710,12 +1713,8 @@ export class SQLiteCorePersistenceAdapter implements PersistenceAdapter { const ordered = [...rows] ordered.sort((left, right) => { for (const clause of compiledClauses) { - const leftValue = clause.evaluator( - left.value, - ) - const rightValue = clause.evaluator( - right.value, - ) + const leftValue = clause.evaluator(left.value) + const rightValue = clause.evaluator(right.value) const comparison = compareOrderByValues( leftValue, diff --git a/packages/db-sqlite-persistence-core/tests/persisted.test.ts b/packages/db-sqlite-persistence-core/tests/persisted.test.ts index 6f0c35249..57c11d844 100644 --- a/packages/db-sqlite-persistence-core/tests/persisted.test.ts +++ b/packages/db-sqlite-persistence-core/tests/persisted.test.ts @@ -142,7 +142,10 @@ function createRecordingAdapter( if (rowMetadataMutation.type === `delete`) { rowMetadata.delete(rowMetadataMutation.key as string) } else { - rowMetadata.set(rowMetadataMutation.key as string, rowMetadataMutation.value) + rowMetadata.set( + rowMetadataMutation.key as string, + rowMetadataMutation.value, + ) } } for (const metadataMutation of tx.collectionMetadataMutations ?? []) { diff --git a/packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts b/packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts index 3be794194..d02f2a635 100644 --- a/packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts +++ b/packages/db-sqlite-persistence-core/tests/sqlite-core-adapter.test.ts @@ -20,8 +20,6 @@ type Todo = { score: number } - - const execFileAsync = promisify(execFile) function toSqlLiteral(value: unknown): string { @@ -1479,11 +1477,9 @@ export function runSQLiteCoreAdapterContractSuite( it(`falls back to in-memory filtering when SQL json path pushdown is unsupported`, async () => { const { driver } = registerContractHarness() - const adapter = new SQLiteCorePersistenceAdapter( - { - driver, - }, - ) + const adapter = new SQLiteCorePersistenceAdapter({ + driver, + }) const collectionId = `fallback-where` await adapter.applyCommittedTx(collectionId, { @@ -1529,11 +1525,9 @@ export function runSQLiteCoreAdapterContractSuite( it(`supports alias-qualified refs during in-memory fallback filtering`, async () => { const { driver } = registerContractHarness() - const adapter = new SQLiteCorePersistenceAdapter( - { - driver, - }, - ) + const adapter = new SQLiteCorePersistenceAdapter({ + driver, + }) const collectionId = `fallback-alias-qualified-ref` await adapter.applyCommittedTx(collectionId, { diff --git a/packages/electron-db-sqlite-persistence/src/renderer.ts b/packages/electron-db-sqlite-persistence/src/renderer.ts index d454e69ed..2ac720300 100644 --- a/packages/electron-db-sqlite-persistence/src/renderer.ts +++ b/packages/electron-db-sqlite-persistence/src/renderer.ts @@ -149,24 +149,31 @@ function createRendererRequestExecutor(options: { } } -type ElectronRendererResolvedAdapter = PersistedCollectionPersistence[`adapter`] & { - loadCollectionMetadata: ( - collectionId: string, - ) => Promise> - scanRows: ( - collectionId: string, - options?: { metadataOnly?: boolean }, - ) => Promise; metadata?: unknown }>> - pullSince: ( - collectionId: string, - fromRowVersion: number, - ) => Promise> - getStreamPosition: (collectionId: string) => Promise<{ - latestTerm: number - latestSeq: number - latestRowVersion: number - }> -} +type ElectronRendererResolvedAdapter = + PersistedCollectionPersistence[`adapter`] & { + loadCollectionMetadata: ( + collectionId: string, + ) => Promise> + scanRows: ( + collectionId: string, + options?: { metadataOnly?: boolean }, + ) => Promise< + Array<{ + key: string | number + value: Record + metadata?: unknown + }> + > + pullSince: ( + collectionId: string, + fromRowVersion: number, + ) => Promise> + getStreamPosition: (collectionId: string) => Promise<{ + latestTerm: number + latestSeq: number + latestRowVersion: number + }> + } function createResolvedRendererAdapter( executeRequest: RendererRequestExecutor, @@ -188,7 +195,10 @@ function createResolvedRendererAdapter( resolution, ) - return result as Array<{ key: string | number; value: Record }> + return result as Array<{ + key: string | number + value: Record + }> }, applyCommittedTx: async ( collectionId: string, @@ -216,14 +226,24 @@ function createResolvedRendererAdapter( scanRows: async ( collectionId: string, options?: { metadataOnly?: boolean }, - ): Promise; metadata?: unknown }>> => { + ): Promise< + Array<{ + key: string | number + value: Record + metadata?: unknown + }> + > => { const result = await executeRequest( `scanRows`, collectionId, { options }, resolution, ) - return result as Array<{ key: string | number; value: Record; metadata?: unknown }> + return result as Array<{ + key: string | number + value: Record + metadata?: unknown + }> }, ensureIndex: async ( collectionId: string, @@ -320,10 +340,7 @@ export function createElectronSQLitePersistence( channel: options.channel, timeoutMs: options.timeoutMs, }) - const adapterCache = new Map< - string, - ElectronRendererResolvedAdapter - >() + const adapterCache = new Map() const getAdapterForCollection = ( mode: PersistedCollectionMode, diff --git a/packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts b/packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts index 3d1f06aa8..d1c330fb6 100644 --- a/packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts +++ b/packages/electron-db-sqlite-persistence/tests/electron-ipc.test.ts @@ -24,7 +24,6 @@ import type { ElectronPersistenceResponseEnvelope, } from '../src/protocol' - type InvokeHarness = { invoke: ElectronPersistenceInvoke close: () => void diff --git a/packages/tauri-db-sqlite-persistence/e2e/app/src/register-tauri-e2e-suite.ts b/packages/tauri-db-sqlite-persistence/e2e/app/src/register-tauri-e2e-suite.ts index 336e7fb4e..4c4c0c263 100644 --- a/packages/tauri-db-sqlite-persistence/e2e/app/src/register-tauri-e2e-suite.ts +++ b/packages/tauri-db-sqlite-persistence/e2e/app/src/register-tauri-e2e-suite.ts @@ -4,7 +4,6 @@ import { registerPersistedCollectionConformanceSuite } from '../../shared/regist import type { PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core' import type { TauriSQLiteDatabaseLike } from '../../../src' - export function registerTauriNativeE2ESuite(options: { suiteName: string database: TauriSQLiteDatabaseLike From 4637e2f6f8af1631e3424e725ae431394b70fa93 Mon Sep 17 00:00:00 2001 From: Kevin De Porre Date: Wed, 1 Apr 2026 15:49:29 +0200 Subject: [PATCH 3/3] Fix missed type arguments in tauri persistence test Co-Authored-By: Claude Opus 4.6 (1M context) --- .../tests/tauri-persistence.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/tauri-db-sqlite-persistence/tests/tauri-persistence.test.ts b/packages/tauri-db-sqlite-persistence/tests/tauri-persistence.test.ts index c3486f873..da5abe8e9 100644 --- a/packages/tauri-db-sqlite-persistence/tests/tauri-persistence.test.ts +++ b/packages/tauri-db-sqlite-persistence/tests/tauri-persistence.test.ts @@ -41,7 +41,7 @@ it(`persists data across app restart (close and reopen)`, async () => { const collectionId = `todos-restart` const firstDatabase = createTauriSQLiteTestDatabase({ filename: dbPath }) - const firstPersistence = createTauriPersistence({ + const firstPersistence = createTauriPersistence({ database: firstDatabase, }) const firstAdapter = firstPersistence.adapter @@ -69,7 +69,7 @@ it(`persists data across app restart (close and reopen)`, async () => { activeCleanupFns.push(async () => { await Promise.resolve(secondDatabase.close()) }) - const secondPersistence = createTauriPersistence({ + const secondPersistence = createTauriPersistence({ database: secondDatabase, }) const secondAdapter = secondPersistence.adapter @@ -95,8 +95,8 @@ it(`shares the same runtime behavior through index and tauri entrypoints`, async await Promise.resolve(database.close()) }) - const indexPersistence = createIndexPersistence({ database }) - const tauriPersistence = createTauriPersistence({ database }) + const indexPersistence = createIndexPersistence({ database }) + const tauriPersistence = createTauriPersistence({ database }) await indexPersistence.adapter.applyCommittedTx(collectionId, { txId: `tx-entrypoint-1`, @@ -128,7 +128,7 @@ it(`resumes persisted sync after cleanup and restart`, async () => { await Promise.resolve(database.close()) }) - const persistence = createTauriPersistence({ + const persistence = createTauriPersistence({ database, }) const collection = createCollection(