diff --git a/.changeset/calm-rivers-appear.md b/.changeset/calm-rivers-appear.md new file mode 100644 index 000000000..66d730df5 --- /dev/null +++ b/.changeset/calm-rivers-appear.md @@ -0,0 +1,5 @@ +--- +'@powersync/drizzle-driver': minor +--- + +Rewrite the Drizzle driver against the Drizzle v1 beta `relations` + `db.query` API and drop `0.x` compatibility from this release line. diff --git a/demos/react-neon-tanstack-query-notes/package.json b/demos/react-neon-tanstack-query-notes/package.json index a59428796..8160e49f5 100644 --- a/demos/react-neon-tanstack-query-notes/package.json +++ b/demos/react-neon-tanstack-query-notes/package.json @@ -28,7 +28,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "drizzle-kit": "^0.31.7", - "drizzle-orm": "^0.44.7", + "drizzle-orm": "1.0.0-rc.3", "lucide-react": "^0.503.0", "moment": "^2.30.1", "react": "^19.2.0", diff --git a/demos/react-neon-tanstack-query-notes/src/components/app/notes-list.tsx b/demos/react-neon-tanstack-query-notes/src/components/app/notes-list.tsx index 284a332f8..bedf74c7b 100644 --- a/demos/react-neon-tanstack-query-notes/src/components/app/notes-list.tsx +++ b/demos/react-neon-tanstack-query-notes/src/components/app/notes-list.tsx @@ -1,8 +1,11 @@ import NoteCard from '@/components/app/note-card'; -import type { Note } from '@/lib/api'; +import { notes as notesTable } from '@/lib/powersync-schema'; import { useRouter } from '@tanstack/react-router'; +import type { InferSelectModel } from 'drizzle-orm'; import { PlusCircleIcon } from 'lucide-react'; +type Note = InferSelectModel; + export default function NotesList({ notes }: { notes: Note[] }) { const router = useRouter(); @@ -27,7 +30,9 @@ export default function NotesList({ notes }: { notes: Note[] }) {
- {notes?.map((note) => )} + {notes?.map((note) => ( + + ))} {notes.length === 0 &&
No notes yet
}
diff --git a/demos/react-neon-tanstack-query-notes/src/lib/powersync-schema.ts b/demos/react-neon-tanstack-query-notes/src/lib/powersync-schema.ts index 287a80b0f..cb88afb5e 100644 --- a/demos/react-neon-tanstack-query-notes/src/lib/powersync-schema.ts +++ b/demos/react-neon-tanstack-query-notes/src/lib/powersync-schema.ts @@ -1,5 +1,5 @@ +import { defineRelations } from 'drizzle-orm'; import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'; -import { relations } from 'drizzle-orm'; export const notes = sqliteTable('notes', { id: text().primaryKey(), @@ -17,10 +17,23 @@ export const paragraphs = sqliteTable('paragraphs', { created_at: text().notNull(), }); -export const notesRelations = relations(notes, ({ many }) => ({ - paragraphs: many(paragraphs), -})); - export const drizzleSchema = { - notes, paragraphs, notesRelations -}; \ No newline at end of file + notes, + paragraphs +}; + +export const drizzleRelations = defineRelations(drizzleSchema, (r) => ({ + notes: { + paragraphs: r.many.paragraphs({ + from: r.notes.id, + to: r.paragraphs.note_id + }) + }, + paragraphs: { + note: r.one.notes({ + from: r.paragraphs.note_id, + to: r.notes.id, + optional: false + }) + } +})); diff --git a/demos/react-neon-tanstack-query-notes/src/lib/powersync.ts b/demos/react-neon-tanstack-query-notes/src/lib/powersync.ts index 1ea638f3b..e8d8b6a51 100644 --- a/demos/react-neon-tanstack-query-notes/src/lib/powersync.ts +++ b/demos/react-neon-tanstack-query-notes/src/lib/powersync.ts @@ -15,7 +15,7 @@ import { DrizzleAppSchema, } from "@powersync/drizzle-driver"; -import { drizzleSchema } from "./powersync-schema"; +import { drizzleRelations, drizzleSchema } from "./powersync-schema"; /// Postgres Response codes that we cannot recover from by retrying. const FATAL_RESPONSE_CODES = [ @@ -205,7 +205,9 @@ export const powersync = new PowerSyncDatabase({ }, }); -export const powersyncDrizzle = wrapPowerSyncWithDrizzle(powersync); +export const powersyncDrizzle = wrapPowerSyncWithDrizzle(powersync, { + relations: drizzleRelations, +}); let isInitialized = false; diff --git a/packages/capacitor/example-app/android/build.gradle b/packages/capacitor/example-app/android/build.gradle index f8f0e43b6..b648f20e0 100644 --- a/packages/capacitor/example-app/android/build.gradle +++ b/packages/capacitor/example-app/android/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - + repositories { google() mavenCentral() diff --git a/packages/drizzle-driver/README.md b/packages/drizzle-driver/README.md index 7ee8c29da..b3462070f 100644 --- a/packages/drizzle-driver/README.md +++ b/packages/drizzle-driver/README.md @@ -4,7 +4,7 @@ This package (`@powersync/drizzle-driver`) brings the benefits of an ORM through ## Beta Release -The `drizzle-driver` package is currently in an Beta release. +This release line targets the Drizzle v1 RC line (`drizzle-orm@1.0.0-rc.3` and newer pre-1.0 RCs) and the `relations` + `db.query` API. ## Getting Started @@ -13,7 +13,7 @@ Set up the PowerSync Database and wrap it with Drizzle. ```js import { wrapPowerSyncWithDrizzle } from '@powersync/drizzle-driver'; import { PowerSyncDatabase } from '@powersync/web'; -import { relations } from 'drizzle-orm'; +import { defineRelations } from 'drizzle-orm'; import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { AppSchema } from './schema'; @@ -29,27 +29,25 @@ export const todos = sqliteTable('todos', { created_at: text('created_at') }); -export const listsRelations = relations(lists, ({ one, many }) => ({ - todos: many(todos) -})); - -export const todosRelations = relations(todos, ({ one, many }) => ({ - list: one(lists, { - fields: [todos.list_id], - references: [lists.id] - }) +export const relations = defineRelations({ lists, todos }, (r) => ({ + lists: { + todos: r.many.todos({ + from: r.lists.id, + to: r.todos.list_id + }) + }, + todos: { + list: r.one.lists({ + from: r.todos.list_id, + to: r.lists.id, + optional: false + }) + } })); -export const drizzleSchema = { - lists, - todos, - listsRelations, - todosRelations -}; - // As an alternative to manually defining a PowerSync schema, generate the local PowerSync schema from the Drizzle schema with the `DrizzleAppSchema` constructor: // import { DrizzleAppSchema } from '@powersync/drizzle-driver'; -// export const AppSchema = new DrizzleAppSchema(drizzleSchema); +// export const AppSchema = new DrizzleAppSchema({ lists, todos }); // // This is optional, but recommended, since you will only need to maintain one schema on the client-side // Read on to learn more. @@ -63,7 +61,17 @@ export const powerSyncDb = new PowerSyncDatabase({ // This is the DB you will use in queries export const db = wrapPowerSyncWithDrizzle(powerSyncDb, { - schema: drizzleSchema + relations +}); +``` + +To make relational queries, use `db.query`: + +```js +const listsWithTodos = await db.query.lists.findMany({ + with: { + todos: true + } }); ``` diff --git a/packages/drizzle-driver/package.json b/packages/drizzle-driver/package.json index fcf4f3bfd..917dc0808 100644 --- a/packages/drizzle-driver/package.json +++ b/packages/drizzle-driver/package.json @@ -47,7 +47,7 @@ }, "peerDependencies": { "@powersync/common": "workspace:^1.54.0", - "drizzle-orm": "<1.0.0" + "drizzle-orm": ">=1.0.0-rc.3 <1.0.0" }, "devDependencies": { "@journeyapps/wa-sqlite": "catalog:", @@ -56,7 +56,7 @@ "@rollup/plugin-node-resolve": "catalog:", "@rollup/plugin-typescript": "catalog:", "@types/node": "catalog:", - "drizzle-orm": "catalog:", + "drizzle-orm": "1.0.0-rc.3", "rollup": "catalog:", "rollup-plugin-dts": "catalog:", "vite": "catalog:" diff --git a/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteBaseSession.ts b/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteBaseSession.ts index 9698c35e1..ec5922dd6 100644 --- a/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteBaseSession.ts +++ b/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteBaseSession.ts @@ -1,9 +1,9 @@ -import type { QueryResult } from '@powersync/common'; +import type { AbstractPowerSyncDatabase, QueryResult } from '@powersync/common'; import type { WithCacheConfig } from 'drizzle-orm/cache/core/types'; import { entityKind } from 'drizzle-orm/entity'; import type { Logger } from 'drizzle-orm/logger'; import { NoopLogger } from 'drizzle-orm/logger'; -import type { RelationalSchemaConfig, TablesRelationalConfig } from 'drizzle-orm/relations'; +import type { AnyRelations, EmptyRelations, RelationalQueryMapperConfig } from 'drizzle-orm/relations'; import { type Query } from 'drizzle-orm/sql/sql'; import type { SQLiteAsyncDialect } from 'drizzle-orm/sqlite-core/dialect'; import type { SelectedFieldsOrdered } from 'drizzle-orm/sqlite-core/query-builders/select.types'; @@ -16,25 +16,36 @@ import { } from 'drizzle-orm/sqlite-core/session'; import { PowerSyncSQLitePreparedQuery, type ContextProvider } from './PowerSyncSQLitePreparedQuery.js'; +type ResultMapper = (rows: unknown[][], mapColumnValue?: (value: unknown) => unknown) => unknown; +type RelationalResultMapper = ( + rows: Record[], + mapColumnValue?: (value: unknown) => unknown +) => unknown; + export interface PowerSyncSQLiteSessionOptions { logger?: Logger; + db: AbstractPowerSyncDatabase; } export type PowerSyncSQLiteTransactionConfig = SQLiteTransactionConfig & { accessMode?: 'read only' | 'read write'; }; -export class PowerSyncSQLiteTransaction< - TFullSchema extends Record, - TSchema extends TablesRelationalConfig -> extends SQLiteTransaction<'async', QueryResult, TFullSchema, TSchema> { +export class PowerSyncSQLiteTransaction extends SQLiteTransaction< + 'async', + QueryResult, + Record, + TRelations +> { static readonly [entityKind]: string = 'PowerSyncSQLiteTransaction'; } -export class PowerSyncSQLiteBaseSession< - TFullSchema extends Record, - TSchema extends TablesRelationalConfig -> extends SQLiteSession<'async', QueryResult, TFullSchema, TSchema> { +export class PowerSyncSQLiteBaseSession extends SQLiteSession< + 'async', + QueryResult, + Record, + TRelations +> { static readonly [entityKind]: string = 'PowerSyncSQLiteBaseSession'; protected logger: Logger; @@ -42,8 +53,8 @@ export class PowerSyncSQLiteBaseSession< constructor( protected contextProvider: ContextProvider, protected dialect: SQLiteAsyncDialect, - protected schema: RelationalSchemaConfig | undefined, - protected options: PowerSyncSQLiteSessionOptions = {} + protected relations: TRelations, + protected options: PowerSyncSQLiteSessionOptions ) { super(dialect); this.logger = options.logger ?? new NoopLogger(); @@ -53,8 +64,7 @@ export class PowerSyncSQLiteBaseSession< query: Query, fields: SelectedFieldsOrdered | undefined, executeMethod: SQLiteExecuteMethod, - isResponseInArrayMode: boolean, - customResultMapper?: (rows: unknown[][], mapColumnValue?: (value: unknown) => unknown) => unknown, + customResultMapper?: ResultMapper, queryMetadata?: { type: 'select' | 'update' | 'delete' | 'insert'; tables: string[]; @@ -67,16 +77,38 @@ export class PowerSyncSQLiteBaseSession< this.logger, fields, executeMethod, - isResponseInArrayMode, + false, customResultMapper, - undefined, // cache not supported yet + undefined, queryMetadata, cacheConfig ); } + prepareRelationalQuery( + query: Query, + fields: SelectedFieldsOrdered | undefined, + executeMethod: SQLiteExecuteMethod, + customResultMapper: RelationalResultMapper, + _config: RelationalQueryMapperConfig + ): PowerSyncSQLitePreparedQuery { + return new PowerSyncSQLitePreparedQuery( + this.contextProvider, + query, + this.logger, + fields, + executeMethod, + false, + customResultMapper, + undefined, + { type: 'select', tables: [] }, + undefined, + true + ); + } + transaction( - _transaction: (tx: PowerSyncSQLiteTransaction) => T, + _transaction: (tx: PowerSyncSQLiteTransaction) => T, _config: PowerSyncSQLiteTransactionConfig = {} ): T { throw new Error('Nested transactions are not supported'); diff --git a/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteDatabase.ts b/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteDatabase.ts index c203ab7c2..1b01593b0 100644 --- a/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteDatabase.ts +++ b/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteDatabase.ts @@ -7,32 +7,34 @@ import { } from '@powersync/common'; import { Query } from 'drizzle-orm'; import { DefaultLogger } from 'drizzle-orm/logger'; -import { - createTableRelationsHelpers, - extractTablesRelationalConfig, - ExtractTablesWithRelations, - TableRelationalConfig, - type RelationalSchemaConfig, - type TablesRelationalConfig -} from 'drizzle-orm/relations'; -import { SQLiteSession, SQLiteTable, SQLiteTransaction } from 'drizzle-orm/sqlite-core'; +import type { AnyRelations, EmptyRelations } from 'drizzle-orm/relations'; +import { SQLiteTransaction } from 'drizzle-orm/sqlite-core'; import { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core/db'; import { SQLiteAsyncDialect } from 'drizzle-orm/sqlite-core/dialect'; -import { RelationalQueryBuilder } from 'drizzle-orm/sqlite-core/query-builders/query'; import type { DrizzleConfig } from 'drizzle-orm/utils'; import { toCompilableQuery } from './../utils/compilableQuery.js'; -import { PowerSyncSQLiteBaseSession, PowerSyncSQLiteTransactionConfig } from './PowerSyncSQLiteBaseSession.js'; +import { PowerSyncSQLiteTransactionConfig } from './PowerSyncSQLiteBaseSession.js'; import { PowerSyncSQLiteSession } from './PowerSyncSQLiteSession.js'; export type DrizzleQuery = { toSQL(): Query; execute(): Promise }; -export class PowerSyncSQLiteDatabase< - TSchema extends Record = Record -> extends BaseSQLiteDatabase<'async', QueryResult, TSchema> { +export type PowerSyncDrizzleConfig = Omit< + DrizzleConfig, TRelations>, + 'schema' | 'relations' +> & { + relations: TRelations; +}; + +export class PowerSyncSQLiteDatabase extends BaseSQLiteDatabase< + 'async', + QueryResult, + Record, + TRelations +> { private db: AbstractPowerSyncDatabase; - constructor(db: AbstractPowerSyncDatabase, config: DrizzleConfig = {}) { - const dialect = new SQLiteAsyncDialect({ casing: config.casing }); + constructor(db: AbstractPowerSyncDatabase, config: PowerSyncDrizzleConfig) { + const dialect = new SQLiteAsyncDialect(); let logger; if (config.logger === true) { logger = new DefaultLogger(); @@ -40,63 +42,17 @@ export class PowerSyncSQLiteDatabase< logger = config.logger; } - let schema: RelationalSchemaConfig | undefined; - if (config.schema) { - const tablesConfig = extractTablesRelationalConfig(config.schema, createTableRelationsHelpers); - schema = { - fullSchema: config.schema, - schema: tablesConfig.tables, - tableNamesMap: tablesConfig.tableNamesMap - }; - } - - const session = new PowerSyncSQLiteSession(db, dialect, schema, { - logger + const session = new PowerSyncSQLiteSession(dialect, config.relations, { + logger, + db }); - super('async', dialect, session as any, schema as any); + super('async', dialect, session, config.relations, undefined, undefined, true); this.db = db; - - /** - * A hack in order to use read locks for `db.query.users.findMany()` etc queries. - * We don't currently get queryMetadata for these queries, so we can't use the regular session. - * This session always uses read locks. - */ - const querySession = new PowerSyncSQLiteBaseSession( - { - useReadContext: (callback) => db.readLock(callback), - useWriteContext: (callback) => db.readLock(callback) - }, - dialect, - schema, - { - logger - } - ); - if (this._.schema) { - // https://github.com/drizzle-team/drizzle-orm/blob/ad4ddd444d066b339ffd5765cb6ec3bf49380189/drizzle-orm/src/sqlite-core/db.ts#L72 - const query = this.query as { - [K in keyof TSchema]: RelationalQueryBuilder<'async', any, any, any>; - }; - for (const [tableName, columns] of Object.entries(this._.schema)) { - query[tableName as keyof TSchema] = new RelationalQueryBuilder( - 'async', - schema!.fullSchema, - this._.schema, - this._.tableNamesMap, - schema!.fullSchema[tableName] as SQLiteTable, - columns as TableRelationalConfig, - dialect, - querySession as SQLiteSession<'async', any, any, any> - ); - } - } } transaction( - transaction: ( - tx: SQLiteTransaction<'async', QueryResult, TSchema, ExtractTablesWithRelations> - ) => Promise, + transaction: (tx: SQLiteTransaction<'async', QueryResult, Record, TRelations>) => Promise, config?: PowerSyncSQLiteTransactionConfig ): Promise { return super.transaction(transaction, config); @@ -107,9 +63,9 @@ export class PowerSyncSQLiteDatabase< } } -export function wrapPowerSyncWithDrizzle = Record>( +export function wrapPowerSyncWithDrizzle( db: AbstractPowerSyncDatabase, - config: DrizzleConfig = {} -): PowerSyncSQLiteDatabase { - return new PowerSyncSQLiteDatabase(db, config); + config: PowerSyncDrizzleConfig +): PowerSyncSQLiteDatabase { + return new PowerSyncSQLiteDatabase(db, config); } diff --git a/packages/drizzle-driver/src/sqlite/PowerSyncSQLitePreparedQuery.ts b/packages/drizzle-driver/src/sqlite/PowerSyncSQLitePreparedQuery.ts index bb8161eae..2c73b7e33 100644 --- a/packages/drizzle-driver/src/sqlite/PowerSyncSQLitePreparedQuery.ts +++ b/packages/drizzle-driver/src/sqlite/PowerSyncSQLitePreparedQuery.ts @@ -32,6 +32,12 @@ export type ContextProvider = { useWriteContext: (fn: LockCallback) => Promise; }; +type ResultMapper = (rows: unknown[][], mapColumnValue?: (value: unknown) => unknown) => unknown; +type RelationalResultMapper = ( + rows: Record[], + mapColumnValue?: (value: unknown) => unknown +) => unknown; + export class PowerSyncSQLitePreparedQuery< T extends PreparedQueryConfig = PreparedQueryConfig > extends SQLitePreparedQuery<{ @@ -46,6 +52,42 @@ export class PowerSyncSQLitePreparedQuery< private readOnly = false; + constructor( + contextProvider: ContextProvider, + query: Query, + logger: Logger, + fields: SelectedFieldsOrdered | undefined, + executeMethod: SQLiteExecuteMethod, + isResponseInArrayMode: boolean, + customResultMapper?: ResultMapper, + cache?: Cache | undefined, + queryMetadata?: + | { + type: 'select' | 'update' | 'delete' | 'insert'; + tables: string[]; + } + | undefined, + cacheConfig?: WithCacheConfig | undefined, + relationalQueryMode?: false + ); + constructor( + contextProvider: ContextProvider, + query: Query, + logger: Logger, + fields: SelectedFieldsOrdered | undefined, + executeMethod: SQLiteExecuteMethod, + isResponseInArrayMode: boolean, + customResultMapper: RelationalResultMapper, + cache: Cache | undefined, + queryMetadata: + | { + type: 'select' | 'update' | 'delete' | 'insert'; + tables: string[]; + } + | undefined, + cacheConfig: WithCacheConfig | undefined, + relationalQueryMode: true + ); constructor( private contextProvider: ContextProvider, query: Query, @@ -53,7 +95,7 @@ export class PowerSyncSQLitePreparedQuery< private fields: SelectedFieldsOrdered | undefined, executeMethod: SQLiteExecuteMethod, private _isResponseInArrayMode: boolean, - private customResultMapper?: (rows: unknown[][]) => unknown, + private customResultMapper?: ResultMapper | RelationalResultMapper, cache?: Cache | undefined, queryMetadata?: | { @@ -61,10 +103,11 @@ export class PowerSyncSQLitePreparedQuery< tables: string[]; } | undefined, - cacheConfig?: WithCacheConfig | undefined + cacheConfig?: WithCacheConfig | undefined, + private relationalQueryMode = false ) { super('async', executeMethod, query, cache, queryMetadata, cacheConfig); - this.readOnly = queryMetadata?.type == 'select'; + this.readOnly = queryMetadata?.type == 'select' || relationalQueryMode; } async run(placeholderValues?: Record): Promise { @@ -85,10 +128,20 @@ export class PowerSyncSQLitePreparedQuery< }); } + if (customResultMapper && this.relationalQueryMode) { + const params = fillPlaceholders(query.params, placeholderValues ?? {}); + logger.logQuery(query.sql, params); + const relationalResultMapper = customResultMapper as RelationalResultMapper; + return await this.useContext(async (ctx) => { + const rows = (await ctx.getAll(this.query.sql, params)) as Record[]; + return relationalResultMapper(rows) as T['all']; + }); + } + const rows = (await this.values(placeholderValues)) as unknown[][]; if (customResultMapper) { - const mapped = customResultMapper(rows) as T['all']; - return mapped; + const resultMapper = customResultMapper as ResultMapper; + return resultMapper(rows) as T['all']; } return rows.map((row) => mapResultRow(fields!, row, (this as any).joinsNotNullableMap)); } @@ -105,6 +158,17 @@ export class PowerSyncSQLitePreparedQuery< }); } + if (customResultMapper && this.relationalQueryMode) { + const relationalResultMapper = customResultMapper as RelationalResultMapper; + return this.useContext(async (ctx) => { + const row = (await ctx.get(this.query.sql, params)) as Record | undefined; + if (!row) { + return undefined as T['get']; + } + return relationalResultMapper([row]) as T['get']; + }); + } + const rows = (await this.values(placeholderValues)) as unknown[][]; const row = rows[0]; @@ -113,7 +177,8 @@ export class PowerSyncSQLitePreparedQuery< } if (customResultMapper) { - return customResultMapper(rows) as T['get']; + const resultMapper = customResultMapper as ResultMapper; + return resultMapper(rows) as T['get']; } return mapResultRow(fields!, row, joinsNotNullableMap); @@ -181,7 +246,7 @@ export function mapResultRow( /** * Determines the appropriate decoder for a given field. */ -function getDecoder(field: SQLiteColumn | SQL | SQL.Aliased): DriverValueDecoder { +function getDecoder(field: any): DriverValueDecoder { if (is(field, Column)) { return field; } else if (is(field, SQL)) { @@ -204,8 +269,11 @@ function updateNullifyMap( const objectName = path[0]!; if (!(objectName in nullifyMap)) { - nullifyMap[objectName] = value === null ? getTableName(field.table) : false; - } else if (typeof nullifyMap[objectName] === 'string' && nullifyMap[objectName] !== getTableName(field.table)) { + nullifyMap[objectName] = value === null ? getTableName((field as any).table) : false; + } else if ( + typeof nullifyMap[objectName] === 'string' && + nullifyMap[objectName] !== getTableName((field as any).table) + ) { nullifyMap[objectName] = false; } } diff --git a/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteSession.ts b/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteSession.ts index d86cdd7d7..ca03e03fe 100644 --- a/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteSession.ts +++ b/packages/drizzle-driver/src/sqlite/PowerSyncSQLiteSession.ts @@ -1,6 +1,6 @@ -import { AbstractPowerSyncDatabase, LockContext } from '@powersync/common'; +import { LockContext } from '@powersync/common'; import { entityKind } from 'drizzle-orm/entity'; -import type { RelationalSchemaConfig, TablesRelationalConfig } from 'drizzle-orm/relations'; +import type { AnyRelations, EmptyRelations } from 'drizzle-orm/relations'; import type { SQLiteAsyncDialect } from 'drizzle-orm/sqlite-core/dialect'; import { PowerSyncSQLiteBaseSession, @@ -9,63 +9,58 @@ import { PowerSyncSQLiteTransactionConfig } from './PowerSyncSQLiteBaseSession.js'; -export class PowerSyncSQLiteSession< - TFullSchema extends Record, - TSchema extends TablesRelationalConfig -> extends PowerSyncSQLiteBaseSession { +export class PowerSyncSQLiteSession extends PowerSyncSQLiteBaseSession { static readonly [entityKind]: string = 'PowerSyncSQLiteSession'; - protected client: AbstractPowerSyncDatabase; - constructor( - db: AbstractPowerSyncDatabase, - dialect: SQLiteAsyncDialect, - schema: RelationalSchemaConfig | undefined, - options: PowerSyncSQLiteSessionOptions = {} - ) { + + constructor(dialect: SQLiteAsyncDialect, relations: TRelations, options: PowerSyncSQLiteSessionOptions) { super( - // Top level operations use the respective locks. { - useReadContext: (callback) => db.readLock(callback), - useWriteContext: (callback) => db.writeLock(callback) + useReadContext: (callback) => options.db.readLock(callback), + useWriteContext: (callback) => options.db.writeLock(callback) }, dialect, - schema, + relations, options ); - this.client = db; } transaction( - transaction: (tx: PowerSyncSQLiteTransaction) => T, + transaction: (tx: PowerSyncSQLiteTransaction) => T, config: PowerSyncSQLiteTransactionConfig = {} ): T { const { accessMode = 'read write' } = config; if (accessMode === 'read only') { - return this.client.readLock(async (ctx) => this.internalTransaction(ctx, transaction, config)) as T; + return this.options.db.readLock(async (ctx) => this.internalTransaction(ctx, transaction, config)) as T; } - return this.client.writeLock(async (ctx) => this.internalTransaction(ctx, transaction, config)) as T; + return this.options.db.writeLock(async (ctx) => this.internalTransaction(ctx, transaction, config)) as T; } protected async internalTransaction( connection: LockContext, - fn: (tx: PowerSyncSQLiteTransaction) => T, + fn: (tx: PowerSyncSQLiteTransaction) => T, config: PowerSyncSQLiteTransactionConfig = {} ): Promise { - const tx = new PowerSyncSQLiteTransaction( + const transactionSession = new PowerSyncSQLiteBaseSession( + { + useReadContext: (callback) => callback(connection), + useWriteContext: (callback) => callback(connection) + }, + this.dialect, + this.relations, + this.options + ); + + const tx = new PowerSyncSQLiteTransaction( 'async', - (this as any).dialect, - new PowerSyncSQLiteBaseSession( - { - // We already have a fixed context here. We need to use it for both "read" and "write" operations. - useReadContext: (callback) => callback(connection), - useWriteContext: (callback) => callback(connection) - }, - this.dialect, - this.schema, - this.options - ), - this.schema + this.dialect, + transactionSession, + this.relations, + undefined, + undefined, + undefined, + true ); await connection.execute(`begin${config?.behavior ? ' ' + config.behavior : ''}`); diff --git a/packages/drizzle-driver/src/utils/schema.ts b/packages/drizzle-driver/src/utils/schema.ts index 7a61dcefc..a20b1c85f 100644 --- a/packages/drizzle-driver/src/utils/schema.ts +++ b/packages/drizzle-driver/src/utils/schema.ts @@ -7,8 +7,8 @@ import { type BaseColumnType, type TableV2Options } from '@powersync/common'; -import { entityKind, InferSelectModel, isTable, Relations, type Casing } from 'drizzle-orm'; -import { CasingCache } from 'drizzle-orm/casing'; +import { entityKind, isTable } from 'drizzle-orm'; +import { getCasingFn, type Casing } from 'drizzle-orm/casing'; import { getTableConfig, SQLiteBoolean, @@ -23,17 +23,46 @@ import { type TableConfig } from 'drizzle-orm/sqlite-core'; +type PowerSyncColumnValue = null extends T + ? PowerSyncNonNullColumnValue> | null + : PowerSyncNonNullColumnValue; + +type PowerSyncNonNullColumnValue = T extends number | string ? T : T extends boolean | Date ? number : string; + +type DrizzleTableColumns> = + T extends SQLiteTableWithColumns ? TConfig['columns'] : never; + +type DrizzleColumnData = T extends { _: { data: infer TData } } ? TData : never; + +type ColumnCasingCache = { + getColumnCasing(column: SQLiteColumn): string; +}; + +function createColumnCasingCache(casing: Casing): ColumnCasingCache { + const casingFn = getCasingFn(casing); + + return { + getColumnCasing(column: SQLiteColumn): string { + return column.keyAsName ? casingFn(column.name) : column.name; + } + }; +} + export type ExtractPowerSyncColumns> = { - [K in keyof InferSelectModel as K extends 'id' ? never : K]: BaseColumnType[K]>; + [K in keyof DrizzleTableColumns as K extends 'id' ? never : K]: BaseColumnType< + PowerSyncColumnValue[K]>> + >; }; export type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; export function toPowerSyncTable>( table: T, - options?: Omit & { casingCache?: CasingCache } + options?: Omit & { casingCache?: ColumnCasingCache } ): Table>> { - const { columns: drizzleColumns, indexes: drizzleIndexes } = getTableConfig(table); + const { columns: drizzleColumns, indexes: drizzleIndexes } = getTableConfig( + table as SQLiteTableWithColumns + ); const { casingCache } = options ?? {}; const columns: { [key: string]: BaseColumnType } = {}; @@ -101,33 +130,33 @@ export type DrizzleTableWithPowerSyncOptions = { options?: DrizzleTablePowerSyncOptions; }; -export type TableName = - T extends SQLiteTableWithColumns - ? T['_']['name'] - : T extends DrizzleTableWithPowerSyncOptions - ? T['tableDefinition']['_']['name'] - : never; +type DrizzleSchemaEntry = SQLiteTableWithColumns | DrizzleTableWithPowerSyncOptions | Record; + +export type TableName = T extends { _: { name: infer TName extends string } } + ? TName + : T extends { tableDefinition: { _: { name: infer TName extends string } } } + ? TName + : never; export type TablesFromSchemaEntries = { - [K in keyof T as T[K] extends Relations - ? never - : T[K] extends SQLiteTableWithColumns | DrizzleTableWithPowerSyncOptions - ? TableName - : never]: T[K] extends SQLiteTableWithColumns + [K in keyof T as T[K] extends SQLiteTableWithColumns | DrizzleTableWithPowerSyncOptions + ? TableName + : never]: T[K] extends SQLiteTableWithColumns ? Table>> : T[K] extends DrizzleTableWithPowerSyncOptions ? Table>> : never; }; -function toPowerSyncTables< - T extends Record | Relations | DrizzleTableWithPowerSyncOptions> ->(schemaEntries: T, options?: DrizzleAppSchemaOptions) { - const casingCache = options?.casing ? new CasingCache(options?.casing) : undefined; +function toPowerSyncTables>( + schemaEntries: T, + options?: DrizzleAppSchemaOptions +) { + const casingCache = options?.casing ? createColumnCasingCache(options.casing) : undefined; const tables: Record = {}; for (const schemaEntry of Object.values(schemaEntries)) { - let maybeTable: SQLiteTableWithColumns | Relations | undefined = undefined; + let maybeTable: SQLiteTableWithColumns | undefined = undefined; let maybeOptions: DrizzleTablePowerSyncOptions | undefined = undefined; if (typeof schemaEntry === 'object' && 'tableDefinition' in schemaEntry) { @@ -135,7 +164,7 @@ function toPowerSyncTables< maybeTable = tableWithOptions.tableDefinition; maybeOptions = tableWithOptions.options; } else { - maybeTable = schemaEntry; + maybeTable = schemaEntry as SQLiteTableWithColumns | undefined; } if (isTable(maybeTable)) { @@ -153,9 +182,7 @@ function toPowerSyncTables< export type DrizzleAppSchemaOptions = { casing?: Casing; }; -export class DrizzleAppSchema< - T extends Record | Relations | DrizzleTableWithPowerSyncOptions> -> extends Schema { +export class DrizzleAppSchema> extends Schema { constructor(drizzleSchema: T, options?: DrizzleAppSchemaOptions) { super(toPowerSyncTables(drizzleSchema, options)); // This is just used for typing diff --git a/packages/drizzle-driver/tests/setup/db.ts b/packages/drizzle-driver/tests/setup/db.ts index 88b650787..48c130565 100644 --- a/packages/drizzle-driver/tests/setup/db.ts +++ b/packages/drizzle-driver/tests/setup/db.ts @@ -1,4 +1,5 @@ import { AbstractPowerSyncDatabase, column, PowerSyncDatabase, Schema, Table } from '@powersync/web'; +import { defineRelations } from 'drizzle-orm'; import { sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { wrapPowerSyncWithDrizzle } from '../../src/sqlite/PowerSyncSQLiteDatabase.js'; @@ -12,7 +13,7 @@ export const drizzleUsers = sqliteTable('users', { }); export const TestSchema = new Schema({ users }); -export const DrizzleSchema = { users: drizzleUsers }; +export const DrizzleRelations = defineRelations({ users: drizzleUsers }); export const getPowerSyncDb = () => { const database = new PowerSyncDatabase({ @@ -26,7 +27,7 @@ export const getPowerSyncDb = () => { }; export const getDrizzleDb = (db: AbstractPowerSyncDatabase) => { - const database = wrapPowerSyncWithDrizzle(db, { schema: DrizzleSchema, logger: { logQuery: () => {} } }); + const database = wrapPowerSyncWithDrizzle(db, { relations: DrizzleRelations, logger: { logQuery: () => {} } }); return database; }; diff --git a/packages/drizzle-driver/tests/sqlite/db.test.ts b/packages/drizzle-driver/tests/sqlite/db.test.ts index ef22871fa..38594b4af 100644 --- a/packages/drizzle-driver/tests/sqlite/db.test.ts +++ b/packages/drizzle-driver/tests/sqlite/db.test.ts @@ -2,11 +2,11 @@ import { AbstractPowerSyncDatabase } from '@powersync/common'; import { eq, sql } from 'drizzle-orm'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import * as SUT from '../../src/sqlite/PowerSyncSQLiteDatabase.js'; -import { DrizzleSchema, drizzleUsers, getDrizzleDb, getPowerSyncDb } from '../setup/db.js'; +import { DrizzleRelations, drizzleUsers, getDrizzleDb, getPowerSyncDb } from '../setup/db.js'; describe('Database operations', () => { let powerSyncDb: AbstractPowerSyncDatabase; - let db: SUT.PowerSyncSQLiteDatabase; + let db: SUT.PowerSyncSQLiteDatabase; beforeEach(() => { powerSyncDb = getPowerSyncDb(); diff --git a/packages/drizzle-driver/tests/sqlite/query.test.ts b/packages/drizzle-driver/tests/sqlite/query.test.ts index e34075de6..559604143 100644 --- a/packages/drizzle-driver/tests/sqlite/query.test.ts +++ b/packages/drizzle-driver/tests/sqlite/query.test.ts @@ -3,7 +3,7 @@ import { Query } from 'drizzle-orm/sql/sql'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { PowerSyncSQLiteDatabase } from '../../src/sqlite/PowerSyncSQLiteDatabase.js'; import { ContextProvider, PowerSyncSQLitePreparedQuery } from '../../src/sqlite/PowerSyncSQLitePreparedQuery.js'; -import { DrizzleSchema, drizzleUsers, getDrizzleDb, getPowerSyncDb } from '../setup/db.js'; +import { DrizzleRelations, drizzleUsers, getDrizzleDb, getPowerSyncDb } from '../setup/db.js'; function toContextProvider(db: AbstractPowerSyncDatabase): ContextProvider { return { @@ -13,7 +13,7 @@ function toContextProvider(db: AbstractPowerSyncDatabase): ContextProvider { } describe('PowerSyncSQLitePreparedQuery', () => { let powerSyncDb: AbstractPowerSyncDatabase; - let db: PowerSyncSQLiteDatabase; + let db: PowerSyncSQLiteDatabase; const loggerMock = { logQuery: () => {} }; beforeEach(async () => { diff --git a/packages/drizzle-driver/tests/sqlite/relationship.test.ts b/packages/drizzle-driver/tests/sqlite/relationship.test.ts index 47cb8ed5f..04320d1b9 100644 --- a/packages/drizzle-driver/tests/sqlite/relationship.test.ts +++ b/packages/drizzle-driver/tests/sqlite/relationship.test.ts @@ -1,6 +1,6 @@ import { AbstractPowerSyncDatabase, column, Schema, Table } from '@powersync/common'; import { PowerSyncDatabase } from '@powersync/web'; -import { eq, relations } from 'drizzle-orm'; +import { defineRelations, eq } from 'drizzle-orm'; import { sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import * as SUT from '../../src/sqlite/PowerSyncSQLiteDatabase.js'; @@ -29,23 +29,26 @@ const drizzlePosts = sqliteTable('posts', { .references(() => drizzleUsers.id) }); -const usersRelations = relations(drizzleUsers, ({ one, many }) => ({ - posts: many(drizzlePosts) -})); - -const postsRelations = relations(drizzlePosts, ({ one }) => ({ - user: one(drizzleUsers, { - fields: [drizzlePosts.user_id], - references: [drizzleUsers.id] - }) -})); - const PsSchema = new Schema({ users, posts }); -const DrizzleSchema = { users: drizzleUsers, posts: drizzlePosts, usersRelations, postsRelations }; +const DrizzleRelations = defineRelations({ users: drizzleUsers, posts: drizzlePosts }, (r) => ({ + users: { + posts: r.many.posts({ + from: r.users.id, + to: r.posts.user_id + }) + }, + posts: { + user: r.one.users({ + from: r.posts.user_id, + to: r.users.id, + optional: false + }) + } +})); describe('Relationship tests', () => { let powerSyncDb: AbstractPowerSyncDatabase; - let db: SUT.PowerSyncSQLiteDatabase; + let db: SUT.PowerSyncSQLiteDatabase; beforeEach(async () => { powerSyncDb = new PowerSyncDatabase({ @@ -54,7 +57,7 @@ describe('Relationship tests', () => { }, schema: PsSchema }); - db = SUT.wrapPowerSyncWithDrizzle(powerSyncDb, { schema: DrizzleSchema, logger: { logQuery: () => {} } }); + db = SUT.wrapPowerSyncWithDrizzle(powerSyncDb, { relations: DrizzleRelations, logger: { logQuery: () => {} } }); await powerSyncDb.init(); diff --git a/packages/drizzle-driver/tests/sqlite/schema.test.ts b/packages/drizzle-driver/tests/sqlite/schema.test.ts index 6ffbb3d10..1468efab9 100644 --- a/packages/drizzle-driver/tests/sqlite/schema.test.ts +++ b/packages/drizzle-driver/tests/sqlite/schema.test.ts @@ -1,5 +1,4 @@ import { column, Schema, Table } from '@powersync/common'; -import { CasingCache } from 'drizzle-orm/casing'; import { customType, index, integer, real, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { describe, expect, it } from 'vitest'; import { DrizzleAppSchema, DrizzleTableWithPowerSyncOptions, toPowerSyncTable } from '../../src/utils/schema.js'; @@ -93,15 +92,17 @@ describe('toPowerSyncTable', () => { }) ); - const convertedList = toPowerSyncTable(lists, { casingCache: new CasingCache('snake_case') }); + const convertedList = new DrizzleAppSchema({ lists }, { casing: 'snake_case' }).props.lists; - const expectedLists = new Table( - { - my_name: column.text, - yourName: column.text - }, - { indexes: { names: ['my_name', 'yourName'] } } - ); + const expectedLists = new Schema({ + lists: new Table( + { + my_name: column.text, + yourName: column.text + }, + { indexes: { names: ['my_name', 'yourName'] } } + ) + }).props.lists; expect(convertedList).toEqual(expectedLists); }); diff --git a/packages/drizzle-driver/tests/sqlite/watch.test.ts b/packages/drizzle-driver/tests/sqlite/watch.test.ts index 7fffa8a2d..d393e0cee 100644 --- a/packages/drizzle-driver/tests/sqlite/watch.test.ts +++ b/packages/drizzle-driver/tests/sqlite/watch.test.ts @@ -1,6 +1,6 @@ import { AbstractPowerSyncDatabase, column, Schema, Table } from '@powersync/common'; import { PowerSyncDatabase } from '@powersync/web'; -import { count, eq, relations, sql } from 'drizzle-orm'; +import { count, defineRelations, eq, sql } from 'drizzle-orm'; import { integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import * as SUT from '../../src/sqlite/PowerSyncSQLiteDatabase.js'; @@ -52,19 +52,21 @@ const customers = sqliteTable('customers', { email: text('email') }); -export const customersRelations = relations(customers, ({ many }) => ({ - assets: many(assets) -})); - -export const assetsRelations = relations(assets, ({ one }) => ({ - customer: one(customers, { - fields: [assets.customer_id], - references: [customers.id] - }) +const DrizzleRelations = defineRelations({ assets, customers }, (r) => ({ + customers: { + assets: r.many.assets({ + from: r.customers.id, + to: r.assets.customer_id + }) + }, + assets: { + customer: r.one.customers({ + from: r.assets.customer_id, + to: r.customers.id + }) + } })); -const DrizzleSchema = { assets, customers, assetsRelations, customersRelations }; - /** * There seems to be an issue with Vitest browser mode's setTimeout and * fake timer functionality. @@ -77,7 +79,7 @@ const throttleDuration = 1000; describe('Watch Tests', () => { let powerSyncDb: AbstractPowerSyncDatabase; - let db: SUT.PowerSyncSQLiteDatabase; + let db: SUT.PowerSyncSQLiteDatabase; beforeEach(async () => { powerSyncDb = new PowerSyncDatabase({ @@ -86,7 +88,7 @@ describe('Watch Tests', () => { }, schema: PsSchema }); - db = SUT.wrapPowerSyncWithDrizzle(powerSyncDb, { schema: DrizzleSchema, logger: { logQuery: () => {} } }); + db = SUT.wrapPowerSyncWithDrizzle(powerSyncDb, { relations: DrizzleRelations, logger: { logQuery: () => {} } }); await powerSyncDb.init(); }); diff --git a/packages/drizzle-driver/vitest.config.ts b/packages/drizzle-driver/vitest.config.ts index fabff67a8..8baf49585 100644 --- a/packages/drizzle-driver/vitest.config.ts +++ b/packages/drizzle-driver/vitest.config.ts @@ -6,6 +6,22 @@ const config: ViteUserConfig = { format: 'es' }, optimizeDeps: { + include: [ + 'drizzle-orm', + 'drizzle-orm/casing', + 'drizzle-orm/column', + 'drizzle-orm/entity', + 'drizzle-orm/logger', + 'drizzle-orm/pg-core/primary-keys', + 'drizzle-orm/query-promise', + 'drizzle-orm/relations', + 'drizzle-orm/sql/sql', + 'drizzle-orm/sqlite-core', + 'drizzle-orm/sqlite-core/db', + 'drizzle-orm/sqlite-core/dialect', + 'drizzle-orm/sqlite-core/query-builders/query', + 'drizzle-orm/sqlite-core/session' + ], exclude: ['@journeyapps/wa-sqlite'] }, test: { @@ -15,7 +31,11 @@ const config: ViteUserConfig = { browser: { enabled: true, headless: true, - provider: playwright(), + provider: playwright( + process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH + ? { launchOptions: { executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH } } + : {} + ), instances: [ { browser: 'chromium' diff --git a/packages/node/package.json b/packages/node/package.json index 5be2ab794..e87cf0923 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -66,7 +66,7 @@ "better-sqlite3": "^12.10.0", "@types/better-sqlite3": "^7.6.13", "bson": "catalog:", - "drizzle-orm": "catalog:", + "drizzle-orm": "1.0.0-rc.3", "js-logger": "catalog:", "typescript": "catalog:", "vitest": "catalog:" @@ -78,4 +78,4 @@ "real-time data stream", "live data" ] -} \ No newline at end of file +} diff --git a/packages/node/tests/DrizzleNode.test.ts b/packages/node/tests/DrizzleNode.test.ts index 10bb19546..b626892f5 100644 --- a/packages/node/tests/DrizzleNode.test.ts +++ b/packages/node/tests/DrizzleNode.test.ts @@ -1,5 +1,5 @@ import { sqliteTable, text } from 'drizzle-orm/sqlite-core'; -import { eq, relations } from 'drizzle-orm'; +import { defineRelations, eq } from 'drizzle-orm'; import { customDatabaseTest, databaseTest } from './utils.js'; import { wrapPowerSyncWithDrizzle } from '@powersync/drizzle-driver'; @@ -17,27 +17,30 @@ export const drizzleTodos = sqliteTable('todos', { list_id: text('list_id') }); -export const listsRelations = relations(drizzleLists, ({ one, many }) => ({ - todos: many(drizzleTodos) -})); - -export const todosRelations = relations(drizzleTodos, ({ one, many }) => ({ - list: one(drizzleLists, { - fields: [drizzleTodos.list_id], - references: [drizzleLists.id] - }) -})); - export const drizzleSchema = { lists: drizzleLists, - todos: drizzleTodos, - listsRelations, - todosRelations + todos: drizzleTodos }; +export const drizzleRelations = defineRelations(drizzleSchema, (r) => ({ + lists: { + todos: r.many.todos({ + from: r.lists.id, + to: r.todos.list_id + }) + }, + todos: { + list: r.one.lists({ + from: r.todos.list_id, + to: r.lists.id, + optional: false + }) + } +})); + const setupDrizzle = async (database: PowerSyncDatabase) => { const db = wrapPowerSyncWithDrizzle(database, { - schema: drizzleSchema + relations: drizzleRelations }); await db.insert(drizzleLists).values({ id: '1', name: 'list 1' }); @@ -199,10 +202,8 @@ customDatabaseTest({ database: { readWorkerCount: 2 } as any })( expect(result2[0].lists).toEqual({ id: '1', name: 'list 1' }); expect(result2[0].todos).toEqual({ id: '33', content: 'Post content', list_id: '1' }); - // Note: This case is not supported yet (drizzle 0.44.7), since it doesn't set - // queryMetadata for these queries - // const result3 = await db.query.lists.findMany(); - // expect(result3).toEqual([{ id: '1', name: 'list 1' }]); + const result3 = await db.query.lists.findMany(); + expect(result3).toEqual([{ id: '1', name: 'list 1' }]); completedRead.resolve(); diff --git a/packages/react/package.json b/packages/react/package.json index 74546b0dd..5f2ad9a4c 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -41,7 +41,7 @@ "@powersync/drizzle-driver": "workspace:*", "@powersync/web": "workspace:*", "@testing-library/react": "^16.0.0", - "drizzle-orm": "catalog:", + "drizzle-orm": "1.0.0-rc.3", "p-defer": "catalog:", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", diff --git a/packages/react/tests/useQuery.test.tsx b/packages/react/tests/useQuery.test.tsx index 273b5730e..8230b80ba 100644 --- a/packages/react/tests/useQuery.test.tsx +++ b/packages/react/tests/useQuery.test.tsx @@ -1,7 +1,7 @@ import * as commonSdk from '@powersync/common'; import { toCompilableQuery, wrapPowerSyncWithDrizzle } from '@powersync/drizzle-driver'; import { act, cleanup, renderHook, waitFor } from '@testing-library/react'; -import { eq } from 'drizzle-orm'; +import { defineRelations, eq } from 'drizzle-orm'; import { sqliteTable, text } from 'drizzle-orm/sqlite-core'; import pDefer from 'p-defer'; import React, { useEffect } from 'react'; @@ -469,10 +469,10 @@ describe('useQuery', () => { name: text('name') }); + const relations = defineRelations({ lists }); + const drizzleDb = wrapPowerSyncWithDrizzle(db, { - schema: { - lists - } + relations }); let updateParameters = (params: string): void => {}; diff --git a/packages/react/vitest.config.ts b/packages/react/vitest.config.ts index d12cf8875..0c4f9c690 100644 --- a/packages/react/vitest.config.ts +++ b/packages/react/vitest.config.ts @@ -25,7 +25,11 @@ const config: ViteUserConfig = { isolate: true, browser: { enabled: true, - provider: playwright(), + provider: playwright( + process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH + ? { launchOptions: { executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH } } + : {} + ), headless: true, instances: [ { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c21f5ac36..477010f4c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -72,9 +72,6 @@ catalogs: comlink: specifier: ^4.4.2 version: 4.4.2 - drizzle-orm: - specifier: ^0.44.7 - version: 0.44.7 eslint: specifier: ^8.57.1 version: 8.57.1 @@ -488,8 +485,8 @@ importers: specifier: 'catalog:' version: 24.10.13 drizzle-orm: - specifier: 'catalog:' - version: 0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0) + specifier: 1.0.0-rc.3 + version: 1.0.0-rc.3(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(arktype@2.1.29)(better-sqlite3@12.10.0)(sql.js@1.14.0)(zod@4.3.6) rollup: specifier: 'catalog:' version: 4.59.0 @@ -565,8 +562,8 @@ importers: specifier: 'catalog:' version: 6.10.4 drizzle-orm: - specifier: 'catalog:' - version: 0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0) + specifier: 1.0.0-rc.3 + version: 1.0.0-rc.3(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(arktype@2.1.29)(better-sqlite3@12.10.0)(sql.js@1.14.0)(zod@4.3.6) js-logger: specifier: 'catalog:' version: 1.6.1 @@ -587,7 +584,7 @@ importers: version: 3.2.3(magicast@0.5.2)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)) '@nuxt/devtools-ui-kit': specifier: 'catalog:' - version: 3.2.3(@nuxt/devtools@3.2.3(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3)))(@unocss/webpack@66.6.6(webpack@5.105.2(esbuild@0.27.3)))(@vue/compiler-core@3.5.35)(fuse.js@7.1.0)(magicast@0.5.2)(nprogress@0.2.0)(nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928))(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3))(webpack@5.105.2(esbuild@0.27.3)) + version: 3.2.3(@nuxt/devtools@3.2.3(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3)))(@unocss/webpack@66.6.6(webpack@5.105.2(esbuild@0.27.3)))(@vue/compiler-core@3.5.35)(fuse.js@7.1.0)(magicast@0.5.2)(nprogress@0.2.0)(nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0))(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3))(webpack@5.105.2(esbuild@0.27.3)) '@nuxt/kit': specifier: ^4.3.1 version: 4.3.1(magicast@0.5.2) @@ -599,7 +596,7 @@ importers: version: 14.2.1(vue@3.5.30(typescript@6.0.3)) '@vueuse/nuxt': specifier: ^14.2.1 - version: 14.2.1(magicast@0.5.2)(nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928))(vue@3.5.30(typescript@6.0.3)) + version: 14.2.1(magicast@0.5.2)(nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3)) bson: specifier: 'catalog:' version: 6.10.4 @@ -623,7 +620,7 @@ importers: version: 66.6.6(@unocss/webpack@66.6.6(webpack@5.105.2(esbuild@0.27.3)))(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)) unstorage: specifier: ^1.17.4 - version: 1.17.4(db0@0.3.4(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0)))(ioredis@5.10.0) + version: 1.17.4(db0@0.3.4(better-sqlite3@12.10.0))(ioredis@5.10.0) devDependencies: '@journeyapps/wa-sqlite': specifier: ^1.7.0 @@ -654,7 +651,7 @@ importers: version: 4.4.2 nuxt: specifier: ^4.3.1 - version: 4.3.1(b42e0915a804f8048a0c960d367b4928) + version: 4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0) vitest: specifier: 'catalog:' version: 4.1.8(@types/node@25.9.1)(@vitest/browser-playwright@4.1.8)(@vitest/browser-preview@4.1.8)(jsdom@24.1.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)) @@ -732,8 +729,8 @@ importers: specifier: ^4.5.0 version: 4.5.1 drizzle-orm: - specifier: 'catalog:' - version: 0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0) + specifier: 1.0.0-rc.3 + version: 1.0.0-rc.3(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(arktype@2.1.29)(better-sqlite3@12.10.0)(sql.js@1.14.0)(zod@4.3.6) jsdom: specifier: 'catalog:' version: 24.1.3 @@ -10275,11 +10272,12 @@ packages: resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} engines: {node: '>=12'} - drizzle-orm@0.44.7: - resolution: {integrity: sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ==} + drizzle-orm@1.0.0-rc.3: + resolution: {integrity: sha512-akZOa5UxapFbdBG8IDkfBRpSZJpMHaOJtGgp7oi1oHaiU8S3KN92waHo2l5aRuv1D9tMGYpv3BQFOsiGcNjLTQ==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=4' + '@effect/sql-pg': '>=4.0.0-beta.58 || >=4.0.0' '@electric-sql/pglite': '>=0.2.0' '@libsql/client': '>=0.10.0' '@libsql/client-wasm': '>=0.10.0' @@ -10287,31 +10285,40 @@ packages: '@op-engineering/op-sqlite': '>=2' '@opentelemetry/api': ^1.4.1 '@planetscale/database': '>=1.13' - '@prisma/client': '*' + '@sinclair/typebox': '>=0.34.8' + '@sqlitecloud/drivers': '>=1.0.653' '@tidbcloud/serverless': '*' + '@tursodatabase/database': '>=0.2.1' + '@tursodatabase/database-common': '>=0.2.1' + '@tursodatabase/database-wasm': '>=0.2.1' '@types/better-sqlite3': '*' + '@types/mssql': ^9.1.4 '@types/pg': '*' '@types/sql.js': '*' '@upstash/redis': '>=1.34.7' '@vercel/postgres': '>=0.8.0' '@xata.io/client': '*' - better-sqlite3: '>=7' + arktype: '>=2.0.0' + better-sqlite3: '>=9.3.0' bun-types: '*' + effect: '>=4.0.0-beta.58 || >=4.0.0' expo-sqlite: '>=14.0.0' - gel: '>=2' - knex: '*' - kysely: '*' + mssql: ^11.0.1 mysql2: '>=2' pg: '>=8' postgres: '>=3' - prisma: '*' sql.js: '>=1' sqlite3: '>=5' + typebox: '>=1.0.0' + valibot: '>=1.0.0-beta.7' + zod: ^3.25.0 || ^4.0.0 peerDependenciesMeta: '@aws-sdk/client-rds-data': optional: true '@cloudflare/workers-types': optional: true + '@effect/sql-pg': + optional: true '@electric-sql/pglite': optional: true '@libsql/client': @@ -10326,12 +10333,22 @@ packages: optional: true '@planetscale/database': optional: true - '@prisma/client': + '@sinclair/typebox': + optional: true + '@sqlitecloud/drivers': optional: true '@tidbcloud/serverless': optional: true + '@tursodatabase/database': + optional: true + '@tursodatabase/database-common': + optional: true + '@tursodatabase/database-wasm': + optional: true '@types/better-sqlite3': optional: true + '@types/mssql': + optional: true '@types/pg': optional: true '@types/sql.js': @@ -10342,17 +10359,17 @@ packages: optional: true '@xata.io/client': optional: true + arktype: + optional: true better-sqlite3: optional: true bun-types: optional: true - expo-sqlite: - optional: true - gel: + effect: optional: true - knex: + expo-sqlite: optional: true - kysely: + mssql: optional: true mysql2: optional: true @@ -10360,12 +10377,16 @@ packages: optional: true postgres: optional: true - prisma: - optional: true sql.js: optional: true sqlite3: optional: true + typebox: + optional: true + valibot: + optional: true + zod: + optional: true dtrace-provider@0.8.8: resolution: {integrity: sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==} @@ -23268,7 +23289,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/devtools-ui-kit@3.2.3(@nuxt/devtools@3.2.3(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3)))(@unocss/webpack@66.6.6(webpack@5.105.2(esbuild@0.27.3)))(@vue/compiler-core@3.5.35)(fuse.js@7.1.0)(magicast@0.5.2)(nprogress@0.2.0)(nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928))(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3))(webpack@5.105.2(esbuild@0.27.3))': + '@nuxt/devtools-ui-kit@3.2.3(@nuxt/devtools@3.2.3(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3)))(@unocss/webpack@66.6.6(webpack@5.105.2(esbuild@0.27.3)))(@vue/compiler-core@3.5.35)(fuse.js@7.1.0)(magicast@0.5.2)(nprogress@0.2.0)(nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0))(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3))(webpack@5.105.2(esbuild@0.27.3))': dependencies: '@iconify-json/carbon': 1.2.19 '@iconify-json/logos': 1.2.10 @@ -23285,7 +23306,7 @@ snapshots: '@unocss/reset': 66.6.6 '@vueuse/core': 14.2.1(vue@3.5.30(typescript@6.0.3)) '@vueuse/integrations': 14.2.1(focus-trap@8.0.0)(fuse.js@7.1.0)(nprogress@0.2.0)(vue@3.5.30(typescript@6.0.3)) - '@vueuse/nuxt': 14.2.1(magicast@0.5.2)(nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928))(vue@3.5.30(typescript@6.0.3)) + '@vueuse/nuxt': 14.2.1(magicast@0.5.2)(nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3)) defu: 6.1.7 focus-trap: 8.0.0 splitpanes: 4.0.4(vue@3.5.30(typescript@6.0.3)) @@ -23439,7 +23460,7 @@ snapshots: - vue - vue-tsc - '@nuxt/nitro-server@4.3.1(better-sqlite3@12.10.0)(db0@0.3.4(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0)))(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0))(encoding@0.1.13)(ioredis@5.10.0)(magicast@0.5.2)(nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928))(typescript@6.0.3)': + '@nuxt/nitro-server@4.3.1(better-sqlite3@12.10.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(magicast@0.5.2)(nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0))(typescript@6.0.3)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 4.3.1(magicast@0.5.2) @@ -23456,8 +23477,8 @@ snapshots: impound: 1.1.5 klona: 2.0.6 mocked-exports: 0.1.1 - nitropack: 2.13.1(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0))(encoding@0.1.13) - nuxt: 4.3.1(b42e0915a804f8048a0c960d367b4928) + nitropack: 2.13.1(better-sqlite3@12.10.0)(encoding@0.1.13) + nuxt: 4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0) ohash: 2.0.11 pathe: 2.0.3 pkg-types: 2.3.0 @@ -23465,7 +23486,7 @@ snapshots: std-env: 3.10.0 ufo: 1.6.3 unctx: 2.5.0 - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0)))(ioredis@5.10.0) + unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.10.0))(ioredis@5.10.0) vue: 3.5.30(typescript@6.0.3) vue-bundle-renderer: 2.2.0 vue-devtools-stub: 0.1.0 @@ -23563,7 +23584,7 @@ snapshots: - typescript - vite - '@nuxt/vite-builder@4.3.1(@types/node@25.9.1)(lightningcss@1.32.0)(magicast@0.5.2)(nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928))(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vue-tsc@3.2.5(typescript@6.0.3))(vue@3.5.30(typescript@6.0.3))(yaml@2.9.0)': + '@nuxt/vite-builder@4.3.1(@types/node@25.9.1)(lightningcss@1.32.0)(magicast@0.5.2)(nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0))(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vue-tsc@3.2.5(typescript@6.0.3))(vue@3.5.30(typescript@6.0.3))(yaml@2.9.0)': dependencies: '@nuxt/kit': 4.3.1(magicast@0.5.2) '@rollup/plugin-replace': 6.0.3(rollup@4.59.0) @@ -23582,7 +23603,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.1 mocked-exports: 0.1.1 - nuxt: 4.3.1(b42e0915a804f8048a0c960d367b4928) + nuxt: 4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0) pathe: 2.0.3 pkg-types: 2.3.0 postcss: 8.5.15 @@ -28245,13 +28266,13 @@ snapshots: '@vueuse/metadata@14.2.1': {} - '@vueuse/nuxt@14.2.1(magicast@0.5.2)(nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928))(vue@3.5.30(typescript@6.0.3))': + '@vueuse/nuxt@14.2.1(magicast@0.5.2)(nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3))': dependencies: '@nuxt/kit': 4.3.1(magicast@0.5.2) '@vueuse/core': 14.2.1(vue@3.5.30(typescript@6.0.3)) '@vueuse/metadata': 14.2.1 local-pkg: 1.1.2 - nuxt: 4.3.1(b42e0915a804f8048a0c960d367b4928) + nuxt: 4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0) vue: 3.5.30(typescript@6.0.3) transitivePeerDependencies: - magicast @@ -30302,10 +30323,9 @@ snapshots: dayjs@1.11.20: {} - db0@0.3.4(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0)): + db0@0.3.4(better-sqlite3@12.10.0): optionalDependencies: better-sqlite3: 12.10.0 - drizzle-orm: 0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0) debounce@1.2.1: {} @@ -30610,33 +30630,25 @@ snapshots: dotenv@17.3.1: {} - drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0): - optionalDependencies: - '@op-engineering/op-sqlite': 15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@types/better-sqlite3': 7.6.13 - '@types/sql.js': 1.4.9 - better-sqlite3: 12.10.0 - kysely: 0.28.11 - sql.js: 1.14.0 - optional: true - - drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0): + drizzle-orm@1.0.0-rc.3(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(arktype@2.1.29)(better-sqlite3@12.10.0)(sql.js@1.14.0)(zod@4.3.6): optionalDependencies: '@op-engineering/op-sqlite': 15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1) '@types/better-sqlite3': 7.6.13 '@types/sql.js': 1.4.9 + arktype: 2.1.29 better-sqlite3: 12.10.0 - kysely: 0.28.11 sql.js: 1.14.0 + zod: 4.3.6 - drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0): + drizzle-orm@1.0.0-rc.3(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(arktype@2.1.29)(better-sqlite3@12.10.0)(sql.js@1.14.0)(zod@4.3.6): optionalDependencies: '@op-engineering/op-sqlite': 15.2.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.7))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) '@types/better-sqlite3': 7.6.13 '@types/sql.js': 1.4.9 + arktype: 2.1.29 better-sqlite3: 12.10.0 - kysely: 0.28.11 sql.js: 1.14.0 + zod: 4.3.6 dtrace-provider@0.8.8: dependencies: @@ -32222,7 +32234,7 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.3 @@ -35186,7 +35198,7 @@ snapshots: next-path@1.0.0: {} - nitropack@2.13.1(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0))(encoding@0.1.13): + nitropack@2.13.1(better-sqlite3@12.10.0)(encoding@0.1.13): dependencies: '@cloudflare/kv-asset-handler': 0.4.2 '@rollup/plugin-alias': 6.0.0(rollup@4.59.0) @@ -35207,7 +35219,7 @@ snapshots: cookie-es: 2.0.0 croner: 9.1.0 crossws: 0.3.5 - db0: 0.3.4(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0)) + db0: 0.3.4(better-sqlite3@12.10.0) defu: 6.1.7 destr: 2.0.5 dot-prop: 10.1.0 @@ -35253,7 +35265,7 @@ snapshots: unenv: 2.0.0-rc.24 unimport: 5.7.0 unplugin-utils: 0.3.1 - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0)))(ioredis@5.10.0) + unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.10.0))(ioredis@5.10.0) untyped: 2.0.0 unwasm: 0.5.3 youch: 4.1.0 @@ -35441,16 +35453,16 @@ snapshots: nullthrows@1.1.1: {} - nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928): + nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0): dependencies: '@dxup/nuxt': 0.3.2(magicast@0.5.2) '@nuxt/cli': 3.34.0(@nuxt/schema@4.3.1)(cac@6.7.14)(commander@13.1.0)(magicast@0.5.2) '@nuxt/devtools': 3.2.3(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3)) '@nuxt/kit': 4.3.1(magicast@0.5.2) - '@nuxt/nitro-server': 4.3.1(better-sqlite3@12.10.0)(db0@0.3.4(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0)))(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0))(encoding@0.1.13)(ioredis@5.10.0)(magicast@0.5.2)(nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928))(typescript@6.0.3) + '@nuxt/nitro-server': 4.3.1(better-sqlite3@12.10.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(magicast@0.5.2)(nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0))(typescript@6.0.3) '@nuxt/schema': 4.3.1 '@nuxt/telemetry': 2.7.0(@nuxt/kit@4.3.1(magicast@0.5.2)) - '@nuxt/vite-builder': 4.3.1(@types/node@25.9.1)(lightningcss@1.32.0)(magicast@0.5.2)(nuxt@4.3.1(b42e0915a804f8048a0c960d367b4928))(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vue-tsc@3.2.5(typescript@6.0.3))(vue@3.5.30(typescript@6.0.3))(yaml@2.9.0) + '@nuxt/vite-builder': 4.3.1(@types/node@25.9.1)(lightningcss@1.32.0)(magicast@0.5.2)(nuxt@4.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.1)(@vue/compiler-sfc@3.5.35)(better-sqlite3@12.10.0)(cac@6.7.14)(commander@13.1.0)(db0@0.3.4(better-sqlite3@12.10.0))(encoding@0.1.13)(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(vue-tsc@3.2.5(typescript@6.0.3))(yaml@2.9.0))(optionator@0.9.4)(rollup@4.59.0)(terser@5.48.0)(tsx@4.21.0)(typescript@6.0.3)(vue-tsc@3.2.5(typescript@6.0.3))(vue@3.5.30(typescript@6.0.3))(yaml@2.9.0) '@unhead/vue': 2.1.12(vue@3.5.30(typescript@6.0.3)) '@vue/shared': 3.5.30 c12: 3.3.3(magicast@0.5.2) @@ -39811,7 +39823,7 @@ snapshots: picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 - unstorage@1.17.4(db0@0.3.4(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0)))(ioredis@5.10.0): + unstorage@1.17.4(db0@0.3.4(better-sqlite3@12.10.0))(ioredis@5.10.0): dependencies: anymatch: 3.1.3 chokidar: 5.0.0 @@ -39822,7 +39834,7 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.3 optionalDependencies: - db0: 0.3.4(better-sqlite3@12.10.0)(drizzle-orm@0.44.7(@op-engineering/op-sqlite@15.2.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.0.0(typescript@6.0.3))(@react-native/metro-config@0.83.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-sqlite3@12.10.0)(kysely@0.28.11)(sql.js@1.14.0)) + db0: 0.3.4(better-sqlite3@12.10.0) ioredis: 5.10.0 untildify@4.0.0: {}