diff --git a/.github/workflows/release-feature-branch.yaml b/.github/workflows/release-feature-branch.yaml index 5c2d76fb7..eb360da2d 100644 --- a/.github/workflows/release-feature-branch.yaml +++ b/.github/workflows/release-feature-branch.yaml @@ -151,6 +151,8 @@ jobs: XATA_API_KEY: ${{ secrets.XATA_API_KEY }} XATA_BRANCH: ${{ secrets.XATA_BRANCH }} LIBSQL_URL: file:local.db + LIBSQL_REMOTE_URL: ${{ secrets.LIBSQL_REMOTE_URL }} + LIBSQL_REMOTE_TOKEN: ${{ secrets.LIBSQL_REMOTE_TOKEN }} run: | if [[ ${{ github.event_name }} != "push" && "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]]; then export SKIP_EXTERNAL_DB_TESTS=1 diff --git a/.github/workflows/release-latest.yaml b/.github/workflows/release-latest.yaml index d81a0bcab..f9292b2e0 100644 --- a/.github/workflows/release-latest.yaml +++ b/.github/workflows/release-latest.yaml @@ -154,6 +154,8 @@ jobs: XATA_API_KEY: ${{ secrets.XATA_API_KEY }} XATA_BRANCH: ${{ secrets.XATA_BRANCH }} LIBSQL_URL: file:local.db + LIBSQL_REMOTE_URL: ${{ secrets.LIBSQL_REMOTE_URL }} + LIBSQL_REMOTE_TOKEN: ${{ secrets.LIBSQL_REMOTE_TOKEN }} run: | if [[ "${{ matrix.package }}" == "drizzle-orm" ]]; then pnpm test --filter ${{ matrix.package }} --filter integration-tests diff --git a/changelogs/drizzle-kit/0.26.1.md b/changelogs/drizzle-kit/0.26.1.md new file mode 100644 index 000000000..da488ede8 --- /dev/null +++ b/changelogs/drizzle-kit/0.26.1.md @@ -0,0 +1 @@ +- Fix `data is malformed` for views \ No newline at end of file diff --git a/changelogs/drizzle-kit/0.26.2.md b/changelogs/drizzle-kit/0.26.2.md new file mode 100644 index 000000000..59e29de14 --- /dev/null +++ b/changelogs/drizzle-kit/0.26.2.md @@ -0,0 +1 @@ +- Updated internal versions for the drizzle-kit and drizzle-orm packages. Changes were introduced in the last minor release, and you are required to upgrade both packages to ensure they work as expected \ No newline at end of file diff --git a/changelogs/drizzle-orm/0.35.1.md b/changelogs/drizzle-orm/0.35.1.md new file mode 100644 index 000000000..59e29de14 --- /dev/null +++ b/changelogs/drizzle-orm/0.35.1.md @@ -0,0 +1 @@ +- Updated internal versions for the drizzle-kit and drizzle-orm packages. Changes were introduced in the last minor release, and you are required to upgrade both packages to ensure they work as expected \ No newline at end of file diff --git a/changelogs/drizzle-orm/0.35.2.md b/changelogs/drizzle-orm/0.35.2.md new file mode 100644 index 000000000..1df586618 --- /dev/null +++ b/changelogs/drizzle-orm/0.35.2.md @@ -0,0 +1,6 @@ +- Fix issues with importing in several environments after updating the Drizzle driver implementation + +We've added approximately 240 tests to check the ESM and CJS builds for all the drivers we have. You can check them [here](https://github.com/drizzle-team/drizzle-orm/tree/main/integration-tests/js-tests/driver-init) + +- Fixed [[BUG]: Type Error in PgTransaction Missing $client Property After Upgrading to drizzle-orm@0.35.1](https://github.com/drizzle-team/drizzle-orm/issues/3140) +- Fixed [[BUG]: New critical Build error drizzle 0.35.0 deploying on Cloudflare ](https://github.com/drizzle-team/drizzle-orm/issues/3137) \ No newline at end of file diff --git a/changelogs/drizzle-orm/0.35.3.md b/changelogs/drizzle-orm/0.35.3.md new file mode 100644 index 000000000..b25658e93 --- /dev/null +++ b/changelogs/drizzle-orm/0.35.3.md @@ -0,0 +1,80 @@ +# New LibSQL driver modules + +Drizzle now has native support for all `@libsql/client` driver variations: + +1. `@libsql/client` - defaults to node import, automatically changes to web if target or platform is set for bundler, e.g. `esbuild --platform=browser` + +```ts +import { drizzle } from 'drizzle-orm/libsql'; + +const db = drizzle({ connection: { + url: process.env.DATABASE_URL, + authToken: process.env.DATABASE_AUTH_TOKEN +}}); +``` + +2. `@libsql/client/node` node compatible module, supports :memory:, file, wss, http and turso connection protocols + +```ts +import { drizzle } from 'drizzle-orm/libsql/node'; + +const db = drizzle({ connection: { + url: process.env.DATABASE_URL, + authToken: process.env.DATABASE_AUTH_TOKEN +}}); +``` + +3. `@libsql/client/web` module for fullstack web frameworks like next, nuxt, astro, etc. + +```ts +import { drizzle } from 'drizzle-orm/libsql/web'; + +const db = drizzle({ connection: { + url: process.env.DATABASE_URL, + authToken: process.env.DATABASE_AUTH_TOKEN +}}); +``` + +4. `@libsql/client/http` module for http and https connection protocols + +```ts +import { drizzle } from 'drizzle-orm/libsql/http'; + +const db = drizzle({ connection: { + url: process.env.DATABASE_URL, + authToken: process.env.DATABASE_AUTH_TOKEN +}}); +``` + +5. `@libsql/client/ws` module for ws and wss connection protocols + +```ts +import { drizzle } from 'drizzle-orm/libsql/ws'; + +const db = drizzle({ connection: { + url: process.env.DATABASE_URL, + authToken: process.env.DATABASE_AUTH_TOKEN +}}); +``` + +6. `@libsql/client/sqlite3` module for :memory: and file connection protocols + +```ts +import { drizzle } from 'drizzle-orm/libsql/wasm'; + +const db = drizzle({ connection: { + url: process.env.DATABASE_URL, + authToken: process.env.DATABASE_AUTH_TOKEN +}}); +``` + +7. `@libsql/client-wasm` Separate experimental package for WASM + +```ts +import { drizzle } from 'drizzle-orm/libsql'; + +const db = drizzle({ connection: { + url: process.env.DATABASE_URL, + authToken: process.env.DATABASE_AUTH_TOKEN +}}); +``` diff --git a/drizzle-kit/package.json b/drizzle-kit/package.json index ce2de1468..7499a7ec1 100644 --- a/drizzle-kit/package.json +++ b/drizzle-kit/package.json @@ -1,6 +1,6 @@ { "name": "drizzle-kit", - "version": "0.26.0", + "version": "0.26.2", "homepage": "https://orm.drizzle.team", "keywords": [ "drizzle", @@ -51,7 +51,7 @@ "@arethetypeswrong/cli": "^0.15.3", "@aws-sdk/client-rds-data": "^3.556.0", "@cloudflare/workers-types": "^4.20230518.0", - "@electric-sql/pglite": "^0.1.5", + "@electric-sql/pglite": "^0.2.12", "@hono/node-server": "^1.9.0", "@hono/zod-validator": "^0.2.1", "@libsql/client": "^0.10.0", diff --git a/drizzle-kit/src/cli/utils.ts b/drizzle-kit/src/cli/utils.ts index 0a5d7862e..4f665ec05 100644 --- a/drizzle-kit/src/cli/utils.ts +++ b/drizzle-kit/src/cli/utils.ts @@ -74,7 +74,7 @@ export const assertEitherPackage = async ( process.exit(1); }; -const requiredApiVersion = 8; +const requiredApiVersion = 9; export const assertOrmCoreVersion = async () => { try { const { compatibilityVersion } = await import('drizzle-orm/version'); diff --git a/drizzle-kit/src/serializer/mysqlSchema.ts b/drizzle-kit/src/serializer/mysqlSchema.ts index 0255afc10..3a6fb9179 100644 --- a/drizzle-kit/src/serializer/mysqlSchema.ts +++ b/drizzle-kit/src/serializer/mysqlSchema.ts @@ -148,7 +148,7 @@ export const schemaInternal = object({ version: literal('5'), dialect: dialect, tables: record(string(), table), - views: record(string(), view), + views: record(string(), view).default({}), _meta: object({ tables: record(string(), string()), columns: record(string(), string()), diff --git a/drizzle-kit/src/serializer/sqliteSchema.ts b/drizzle-kit/src/serializer/sqliteSchema.ts index 54587c3e0..8fd98d99d 100644 --- a/drizzle-kit/src/serializer/sqliteSchema.ts +++ b/drizzle-kit/src/serializer/sqliteSchema.ts @@ -90,7 +90,7 @@ export const schemaInternalV4 = object({ version: literal('4'), dialect: dialect, tables: record(string(), table), - views: record(string(), view), + views: record(string(), view).default({}), enums: object({}), }).strict(); @@ -122,7 +122,7 @@ export const schemaInternal = object({ version: latestVersion, dialect: dialect, tables: record(string(), table), - views: record(string(), view), + views: record(string(), view).default({}), enums: object({}), _meta: object({ tables: record(string(), string()), diff --git a/drizzle-orm/package.json b/drizzle-orm/package.json index bd3221754..8622fea11 100644 --- a/drizzle-orm/package.json +++ b/drizzle-orm/package.json @@ -1,6 +1,6 @@ { "name": "drizzle-orm", - "version": "0.35.0", + "version": "0.35.3", "description": "Drizzle ORM package for SQL databases", "type": "module", "scripts": { @@ -47,6 +47,7 @@ "@cloudflare/workers-types": ">=3", "@electric-sql/pglite": ">=0.1.1", "@libsql/client": ">=0.10.0", + "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.1", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", @@ -160,8 +161,9 @@ "devDependencies": { "@aws-sdk/client-rds-data": "^3.549.0", "@cloudflare/workers-types": "^4.20230904.0", - "@electric-sql/pglite": "^0.1.1", + "@electric-sql/pglite": "^0.2.12", "@libsql/client": "^0.10.0", + "@libsql/client-wasm": "^0.10.0", "@miniflare/d1": "^2.14.4", "@neondatabase/serverless": "^0.9.0", "@op-engineering/op-sqlite": "^2.0.16", diff --git a/drizzle-orm/src/better-sqlite3/driver.ts b/drizzle-orm/src/better-sqlite3/driver.ts index 14e6644bc..0fe7364bd 100644 --- a/drizzle-orm/src/better-sqlite3/driver.ts +++ b/drizzle-orm/src/better-sqlite3/driver.ts @@ -9,7 +9,7 @@ import { } from '~/relations.ts'; import { BaseSQLiteDatabase } from '~/sqlite-core/db.ts'; import { SQLiteSyncDialect } from '~/sqlite-core/dialect.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import { BetterSQLiteSession } from './session.ts'; export type DrizzleBetterSQLite3DatabaseConfig = @@ -89,12 +89,13 @@ export function drizzle< ): BetterSQLite3Database & { $client: Database; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (params[0] instanceof Client) { - return construct(params[0] as Database, params[1] as DrizzleConfig | undefined) as any; + if (params[0] === undefined || typeof params[0] === 'string') { + const instance = params[0] === undefined ? new Client() : new Client(params[0]); + + return construct(instance, params[1]) as any; } - if (typeof params[0] === 'object') { + if (isConfig(params[0])) { const { connection, client, ...drizzleConfig } = params[0] as & { connection?: DrizzleBetterSQLite3DatabaseConfig; @@ -117,9 +118,7 @@ export function drizzle< return construct(instance, drizzleConfig) as any; } - const instance = new Client(params[0]); - - return construct(instance, params[1]) as any; + return construct(params[0] as Database, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/bun-sqlite/driver.ts b/drizzle-orm/src/bun-sqlite/driver.ts index 91a2e370b..7510fe58e 100644 --- a/drizzle-orm/src/bun-sqlite/driver.ts +++ b/drizzle-orm/src/bun-sqlite/driver.ts @@ -11,7 +11,7 @@ import { } from '~/relations.ts'; import { BaseSQLiteDatabase } from '~/sqlite-core/db.ts'; import { SQLiteSyncDialect } from '~/sqlite-core/dialect.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import { SQLiteBunSession } from './session.ts'; export class BunSQLiteDatabase< @@ -111,12 +111,13 @@ export function drizzle< ): BunSQLiteDatabase & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (params[0] instanceof Database) { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; + if (params[0] === undefined || typeof params[0] === 'string') { + const instance = params[0] === undefined ? new Database() : new Database(params[0]); + + return construct(instance, params[1]) as any; } - if (typeof params[0] === 'object') { + if (isConfig(params[0])) { const { connection, client, ...drizzleConfig } = params[0] as & ({ connection?: DrizzleBunSqliteDatabaseConfig | string; @@ -141,9 +142,7 @@ export function drizzle< return construct(instance, drizzleConfig) as any; } - const instance = new Database(params[0]); - - return construct(instance, params[1]) as any; + return construct(params[0] as Database, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/libsql/driver-core.ts b/drizzle-orm/src/libsql/driver-core.ts new file mode 100644 index 000000000..1bee47d7b --- /dev/null +++ b/drizzle-orm/src/libsql/driver-core.ts @@ -0,0 +1,64 @@ +import type { Client, ResultSet } from '@libsql/client'; +import type { BatchItem, BatchResponse } from '~/batch.ts'; +import { entityKind } from '~/entity.ts'; +import { DefaultLogger } from '~/logger.ts'; +import { + createTableRelationsHelpers, + extractTablesRelationalConfig, + type ExtractTablesWithRelations, + type RelationalSchemaConfig, + type TablesRelationalConfig, +} from '~/relations.ts'; +import { BaseSQLiteDatabase } from '~/sqlite-core/db.ts'; +import { SQLiteAsyncDialect } from '~/sqlite-core/dialect.ts'; +import type { DrizzleConfig } from '~/utils.ts'; +import { LibSQLSession } from './session.ts'; + +export class LibSQLDatabase< + TSchema extends Record = Record, +> extends BaseSQLiteDatabase<'async', ResultSet, TSchema> { + static override readonly [entityKind]: string = 'LibSQLDatabase'; + + /** @internal */ + declare readonly session: LibSQLSession>; + + async batch, T extends Readonly<[U, ...U[]]>>( + batch: T, + ): Promise> { + return this.session.batch(batch) as Promise>; + } +} + +/** @internal */ +export function construct< + TSchema extends Record = Record, +>(client: Client, config: DrizzleConfig = {}): LibSQLDatabase & { + $client: Client; +} { + const dialect = new SQLiteAsyncDialect({ casing: config.casing }); + let logger; + if (config.logger === true) { + logger = new DefaultLogger(); + } else if (config.logger !== false) { + 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 LibSQLSession(client, dialect, schema, { logger }, undefined); + const db = new LibSQLDatabase('async', dialect, session, schema) as LibSQLDatabase; + ( db).$client = client; + + return db as any; +} diff --git a/drizzle-orm/src/libsql/driver.ts b/drizzle-orm/src/libsql/driver.ts index c5e3957d2..bf0ef6662 100644 --- a/drizzle-orm/src/libsql/driver.ts +++ b/drizzle-orm/src/libsql/driver.ts @@ -1,69 +1,8 @@ -import { type Client, type Config, createClient, type ResultSet } from '@libsql/client'; -import { HttpClient } from '@libsql/client/http'; -import { Sqlite3Client } from '@libsql/client/sqlite3'; -import { WsClient } from '@libsql/client/ws'; -import type { BatchItem, BatchResponse } from '~/batch.ts'; -import { entityKind } from '~/entity.ts'; -import { DefaultLogger } from '~/logger.ts'; -import { - createTableRelationsHelpers, - extractTablesRelationalConfig, - type ExtractTablesWithRelations, - type RelationalSchemaConfig, - type TablesRelationalConfig, -} from '~/relations.ts'; -import { BaseSQLiteDatabase } from '~/sqlite-core/db.ts'; -import { SQLiteAsyncDialect } from '~/sqlite-core/dialect.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; -import { LibSQLSession } from './session.ts'; +import { type Client, type Config, createClient } from '@libsql/client'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; +import { construct as construct, type LibSQLDatabase } from './driver-core.ts'; -export class LibSQLDatabase< - TSchema extends Record = Record, -> extends BaseSQLiteDatabase<'async', ResultSet, TSchema> { - static override readonly [entityKind]: string = 'LibSQLDatabase'; - - /** @internal */ - declare readonly session: LibSQLSession>; - - async batch, T extends Readonly<[U, ...U[]]>>( - batch: T, - ): Promise> { - return this.session.batch(batch) as Promise>; - } -} - -function construct< - TSchema extends Record = Record, ->(client: Client, config: DrizzleConfig = {}): LibSQLDatabase & { - $client: Client; -} { - const dialect = new SQLiteAsyncDialect({ casing: config.casing }); - let logger; - if (config.logger === true) { - logger = new DefaultLogger(); - } else if (config.logger !== false) { - 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 LibSQLSession(client, dialect, schema, { logger }, undefined); - const db = new LibSQLDatabase('async', dialect, session, schema) as LibSQLDatabase; - ( db).$client = client; - - return db as any; -} +export { LibSQLDatabase } from './driver-core.ts'; export function drizzle< TSchema extends Record = Record, @@ -91,11 +30,6 @@ export function drizzle< ): LibSQLDatabase & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (params[0] instanceof WsClient || params[0] instanceof HttpClient || params[0] instanceof Sqlite3Client) { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; - } - if (typeof params[0] === 'string') { const instance = createClient({ url: params[0], @@ -104,15 +38,19 @@ export function drizzle< return construct(instance, params[1]) as any; } - const { connection, client, ...drizzleConfig } = params[0] as - & { connection?: Config; client?: TClient } - & DrizzleConfig; + if (isConfig(params[0])) { + const { connection, client, ...drizzleConfig } = params[0] as + & { connection?: Config; client?: TClient } + & DrizzleConfig; - if (client) return construct(client, drizzleConfig) as any; + if (client) return construct(client, drizzleConfig) as any; - const instance = typeof connection === 'string' ? createClient({ url: connection }) : createClient(connection!); + const instance = typeof connection === 'string' ? createClient({ url: connection }) : createClient(connection!); + + return construct(instance, drizzleConfig) as any; + } - return construct(instance, drizzleConfig) as any; + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/libsql/http/index.ts b/drizzle-orm/src/libsql/http/index.ts new file mode 100644 index 000000000..fddf910f5 --- /dev/null +++ b/drizzle-orm/src/libsql/http/index.ts @@ -0,0 +1,62 @@ +import { type Client, type Config, createClient } from '@libsql/client/http'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; +import { construct, type LibSQLDatabase } from '../driver-core.ts'; + +export function drizzle< + TSchema extends Record = Record, + TClient extends Client = Client, +>( + ...params: IfNotImported< + Client, + [ImportTypeError<'@libsql/client'>], + [ + TClient | string, + ] | [ + TClient | string, + DrizzleConfig, + ] | [ + ( + & DrizzleConfig + & ({ + connection: string | Config; + } | { + client: TClient; + }) + ), + ] + > +): LibSQLDatabase & { + $client: TClient; +} { + if (typeof params[0] === 'string') { + const instance = createClient({ + url: params[0], + }); + + return construct(instance, params[1]) as any; + } + + if (isConfig(params[0])) { + const { connection, client, ...drizzleConfig } = params[0] as + & { connection?: Config; client?: TClient } + & DrizzleConfig; + + if (client) return construct(client, drizzleConfig) as any; + + const instance = typeof connection === 'string' ? createClient({ url: connection }) : createClient(connection!); + + return construct(instance, drizzleConfig) as any; + } + + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; +} + +export namespace drizzle { + export function mock = Record>( + config?: DrizzleConfig, + ): LibSQLDatabase & { + $client: '$client is not available on drizzle.mock()'; + } { + return construct({} as any, config) as any; + } +} diff --git a/drizzle-orm/src/libsql/node/index.ts b/drizzle-orm/src/libsql/node/index.ts new file mode 100644 index 000000000..003a5c899 --- /dev/null +++ b/drizzle-orm/src/libsql/node/index.ts @@ -0,0 +1,62 @@ +import { type Client, type Config, createClient } from '@libsql/client/node'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; +import { construct, type LibSQLDatabase } from '../driver-core.ts'; + +export function drizzle< + TSchema extends Record = Record, + TClient extends Client = Client, +>( + ...params: IfNotImported< + Client, + [ImportTypeError<'@libsql/client'>], + [ + TClient | string, + ] | [ + TClient | string, + DrizzleConfig, + ] | [ + ( + & DrizzleConfig + & ({ + connection: string | Config; + } | { + client: TClient; + }) + ), + ] + > +): LibSQLDatabase & { + $client: TClient; +} { + if (typeof params[0] === 'string') { + const instance = createClient({ + url: params[0], + }); + + return construct(instance, params[1]) as any; + } + + if (isConfig(params[0])) { + const { connection, client, ...drizzleConfig } = params[0] as + & { connection?: Config; client?: TClient } + & DrizzleConfig; + + if (client) return construct(client, drizzleConfig) as any; + + const instance = typeof connection === 'string' ? createClient({ url: connection }) : createClient(connection!); + + return construct(instance, drizzleConfig) as any; + } + + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; +} + +export namespace drizzle { + export function mock = Record>( + config?: DrizzleConfig, + ): LibSQLDatabase & { + $client: '$client is not available on drizzle.mock()'; + } { + return construct({} as any, config) as any; + } +} diff --git a/drizzle-orm/src/libsql/sqlite3/index.ts b/drizzle-orm/src/libsql/sqlite3/index.ts new file mode 100644 index 000000000..0f17ef935 --- /dev/null +++ b/drizzle-orm/src/libsql/sqlite3/index.ts @@ -0,0 +1,62 @@ +import { type Client, type Config, createClient } from '@libsql/client/sqlite3'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; +import { construct, type LibSQLDatabase } from '../driver-core.ts'; + +export function drizzle< + TSchema extends Record = Record, + TClient extends Client = Client, +>( + ...params: IfNotImported< + Client, + [ImportTypeError<'@libsql/client'>], + [ + TClient | string, + ] | [ + TClient | string, + DrizzleConfig, + ] | [ + ( + & DrizzleConfig + & ({ + connection: string | Config; + } | { + client: TClient; + }) + ), + ] + > +): LibSQLDatabase & { + $client: TClient; +} { + if (typeof params[0] === 'string') { + const instance = createClient({ + url: params[0], + }); + + return construct(instance, params[1]) as any; + } + + if (isConfig(params[0])) { + const { connection, client, ...drizzleConfig } = params[0] as + & { connection?: Config; client?: TClient } + & DrizzleConfig; + + if (client) return construct(client, drizzleConfig) as any; + + const instance = typeof connection === 'string' ? createClient({ url: connection }) : createClient(connection!); + + return construct(instance, drizzleConfig) as any; + } + + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; +} + +export namespace drizzle { + export function mock = Record>( + config?: DrizzleConfig, + ): LibSQLDatabase & { + $client: '$client is not available on drizzle.mock()'; + } { + return construct({} as any, config) as any; + } +} diff --git a/drizzle-orm/src/libsql/wasm/index.ts b/drizzle-orm/src/libsql/wasm/index.ts new file mode 100644 index 000000000..23c4f4dcc --- /dev/null +++ b/drizzle-orm/src/libsql/wasm/index.ts @@ -0,0 +1,62 @@ +import { type Client, type Config, createClient } from '@libsql/client-wasm'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; +import { construct, type LibSQLDatabase } from '../driver-core.ts'; + +export function drizzle< + TSchema extends Record = Record, + TClient extends Client = Client, +>( + ...params: IfNotImported< + Client, + [ImportTypeError<'@libsql/client-wasm'>], + [ + TClient | string, + ] | [ + TClient | string, + DrizzleConfig, + ] | [ + ( + & DrizzleConfig + & ({ + connection: string | Config; + } | { + client: TClient; + }) + ), + ] + > +): LibSQLDatabase & { + $client: TClient; +} { + if (typeof params[0] === 'string') { + const instance = createClient({ + url: params[0], + }); + + return construct(instance, params[1]) as any; + } + + if (isConfig(params[0])) { + const { connection, client, ...drizzleConfig } = params[0] as + & { connection?: Config; client?: TClient } + & DrizzleConfig; + + if (client) return construct(client, drizzleConfig) as any; + + const instance = typeof connection === 'string' ? createClient({ url: connection }) : createClient(connection!); + + return construct(instance, drizzleConfig) as any; + } + + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; +} + +export namespace drizzle { + export function mock = Record>( + config?: DrizzleConfig, + ): LibSQLDatabase & { + $client: '$client is not available on drizzle.mock()'; + } { + return construct({} as any, config) as any; + } +} diff --git a/drizzle-orm/src/libsql/web/index.ts b/drizzle-orm/src/libsql/web/index.ts new file mode 100644 index 000000000..e7c31c9bb --- /dev/null +++ b/drizzle-orm/src/libsql/web/index.ts @@ -0,0 +1,62 @@ +import { type Client, type Config, createClient } from '@libsql/client/web'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; +import { construct, type LibSQLDatabase } from '../driver-core.ts'; + +export function drizzle< + TSchema extends Record = Record, + TClient extends Client = Client, +>( + ...params: IfNotImported< + Client, + [ImportTypeError<'@libsql/client'>], + [ + TClient | string, + ] | [ + TClient | string, + DrizzleConfig, + ] | [ + ( + & DrizzleConfig + & ({ + connection: string | Config; + } | { + client: TClient; + }) + ), + ] + > +): LibSQLDatabase & { + $client: TClient; +} { + if (typeof params[0] === 'string') { + const instance = createClient({ + url: params[0], + }); + + return construct(instance, params[1]) as any; + } + + if (isConfig(params[0])) { + const { connection, client, ...drizzleConfig } = params[0] as + & { connection?: Config; client?: TClient } + & DrizzleConfig; + + if (client) return construct(client, drizzleConfig) as any; + + const instance = typeof connection === 'string' ? createClient({ url: connection }) : createClient(connection!); + + return construct(instance, drizzleConfig) as any; + } + + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; +} + +export namespace drizzle { + export function mock = Record>( + config?: DrizzleConfig, + ): LibSQLDatabase & { + $client: '$client is not available on drizzle.mock()'; + } { + return construct({} as any, config) as any; + } +} diff --git a/drizzle-orm/src/libsql/ws/index.ts b/drizzle-orm/src/libsql/ws/index.ts new file mode 100644 index 000000000..115ec9b24 --- /dev/null +++ b/drizzle-orm/src/libsql/ws/index.ts @@ -0,0 +1,62 @@ +import { type Client, type Config, createClient } from '@libsql/client/ws'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; +import { construct, type LibSQLDatabase } from '../driver-core.ts'; + +export function drizzle< + TSchema extends Record = Record, + TClient extends Client = Client, +>( + ...params: IfNotImported< + Client, + [ImportTypeError<'@libsql/client'>], + [ + TClient | string, + ] | [ + TClient | string, + DrizzleConfig, + ] | [ + ( + & DrizzleConfig + & ({ + connection: string | Config; + } | { + client: TClient; + }) + ), + ] + > +): LibSQLDatabase & { + $client: TClient; +} { + if (typeof params[0] === 'string') { + const instance = createClient({ + url: params[0], + }); + + return construct(instance, params[1]) as any; + } + + if (isConfig(params[0])) { + const { connection, client, ...drizzleConfig } = params[0] as + & { connection?: Config; client?: TClient } + & DrizzleConfig; + + if (client) return construct(client, drizzleConfig) as any; + + const instance = typeof connection === 'string' ? createClient({ url: connection }) : createClient(connection!); + + return construct(instance, drizzleConfig) as any; + } + + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; +} + +export namespace drizzle { + export function mock = Record>( + config?: DrizzleConfig, + ): LibSQLDatabase & { + $client: '$client is not available on drizzle.mock()'; + } { + return construct({} as any, config) as any; + } +} diff --git a/drizzle-orm/src/mysql2/driver.ts b/drizzle-orm/src/mysql2/driver.ts index ef34604e3..ec791e571 100644 --- a/drizzle-orm/src/mysql2/driver.ts +++ b/drizzle-orm/src/mysql2/driver.ts @@ -1,4 +1,3 @@ -import { EventEmitter } from 'events'; import { type Connection as CallbackConnection, createPool, type Pool as CallbackPool, type PoolOptions } from 'mysql2'; import type { Connection, Pool } from 'mysql2/promise'; import { entityKind } from '~/entity.ts'; @@ -13,7 +12,7 @@ import { type RelationalSchemaConfig, type TablesRelationalConfig, } from '~/relations.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import { DrizzleError } from '../errors.ts'; import type { MySql2Client, MySql2PreparedQueryHKT, MySql2QueryResultHKT } from './session.ts'; import { MySql2Session } from './session.ts'; @@ -137,12 +136,16 @@ export function drizzle< ): MySql2Database & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (params[0] instanceof EventEmitter) { - return construct(params[0] as TClient, params[1] as MySql2DrizzleConfig | undefined) as any; + if (typeof params[0] === 'string') { + const connectionString = params[0]!; + const instance = createPool({ + uri: connectionString, + }); + + return construct(instance, params[1]) as any; } - if (typeof params[0] === 'object') { + if (isConfig(params[0])) { const { connection, client, ...drizzleConfig } = params[0] as & { connection?: PoolOptions | string; client?: TClient } & MySql2DrizzleConfig; @@ -159,12 +162,7 @@ export function drizzle< return db as any; } - const connectionString = params[0]!; - const instance = createPool({ - uri: connectionString, - }); - - return construct(instance, params[1]) as any; + return construct(params[0] as TClient, params[1] as MySql2DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/neon-http/driver.ts b/drizzle-orm/src/neon-http/driver.ts index f79fd9de3..4ef1dc36a 100644 --- a/drizzle-orm/src/neon-http/driver.ts +++ b/drizzle-orm/src/neon-http/driver.ts @@ -8,7 +8,7 @@ import { PgDatabase } from '~/pg-core/db.ts'; import { PgDialect } from '~/pg-core/dialect.ts'; import { createTableRelationsHelpers, extractTablesRelationalConfig } from '~/relations.ts'; import type { ExtractTablesWithRelations, RelationalSchemaConfig, TablesRelationalConfig } from '~/relations.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import { type NeonHttpClient, type NeonHttpQueryResultHKT, NeonHttpSession } from './session.ts'; export interface NeonDriverOptions { @@ -124,12 +124,12 @@ export function drizzle< ): NeonHttpDatabase & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (typeof params[0] === 'function') { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; + if (typeof params[0] === 'string') { + const instance = neon(params[0] as string); + return construct(instance, params[1]) as any; } - if (typeof params[0] === 'object') { + if (isConfig(params[0])) { const { connection, client, ...drizzleConfig } = params[0] as & { connection?: @@ -156,8 +156,7 @@ export function drizzle< return construct(instance, drizzleConfig) as any; } - const instance = neon(params[0] as string); - return construct(instance, params[1]) as any; + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/neon-serverless/driver.ts b/drizzle-orm/src/neon-serverless/driver.ts index c0f962e96..a1975c78b 100644 --- a/drizzle-orm/src/neon-serverless/driver.ts +++ b/drizzle-orm/src/neon-serverless/driver.ts @@ -10,7 +10,7 @@ import { type RelationalSchemaConfig, type TablesRelationalConfig, } from '~/relations.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import type { NeonClient, NeonQueryResultHKT } from './session.ts'; import { NeonSession } from './session.ts'; @@ -108,12 +108,15 @@ export function drizzle< ): NeonDatabase & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (params[0] instanceof Pool) { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; + if (typeof params[0] === 'string') { + const instance = new Pool({ + connectionString: params[0], + }); + + return construct(instance, params[1]) as any; } - if (typeof params[0] === 'object') { + if (isConfig(params[0])) { const { connection, client, ws, ...drizzleConfig } = params[0] as { connection?: PoolConfig | string; ws?: any; @@ -135,11 +138,7 @@ export function drizzle< return construct(instance, drizzleConfig) as any; } - const instance = new Pool({ - connectionString: params[0], - }); - - return construct(instance, params[1]) as any; + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/node-postgres/driver.ts b/drizzle-orm/src/node-postgres/driver.ts index b9bb063d8..4b5b5eaab 100644 --- a/drizzle-orm/src/node-postgres/driver.ts +++ b/drizzle-orm/src/node-postgres/driver.ts @@ -1,4 +1,3 @@ -import { EventEmitter } from 'events'; import pg, { type Pool, type PoolConfig } from 'pg'; import { entityKind } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; @@ -11,7 +10,7 @@ import { type RelationalSchemaConfig, type TablesRelationalConfig, } from '~/relations.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import type { NodePgClient, NodePgQueryResultHKT } from './session.ts'; import { NodePgSession } from './session.ts'; @@ -86,7 +85,7 @@ export function drizzle< >( ...params: IfNotImported< Pool, - [ImportTypeError<'pg'>], + [ImportTypeError<'@types/pg` `pg'>], | [ TClient | string, ] @@ -108,12 +107,15 @@ export function drizzle< ): NodePgDatabase & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (params[0] instanceof EventEmitter) { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; + if (typeof params[0] === 'string') { + const instance = new pg.Pool({ + connectionString: params[0], + }); + + return construct(instance, params[1] as DrizzleConfig | undefined) as any; } - if (typeof params[0] === 'object') { + if (isConfig(params[0])) { const { connection, client, ...drizzleConfig } = params[0] as ( & ({ connection?: PoolConfig | string; client?: TClient }) & DrizzleConfig @@ -130,11 +132,7 @@ export function drizzle< return construct(instance, drizzleConfig) as any; } - const instance = new pg.Pool({ - connectionString: params[0], - }); - - return construct(instance, params[1] as DrizzleConfig | undefined) as any; + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/pglite/driver.ts b/drizzle-orm/src/pglite/driver.ts index 89d37d1f9..ee6aaa252 100644 --- a/drizzle-orm/src/pglite/driver.ts +++ b/drizzle-orm/src/pglite/driver.ts @@ -10,7 +10,7 @@ import { type RelationalSchemaConfig, type TablesRelationalConfig, } from '~/relations.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import type { PgliteClient, PgliteQueryResultHKT } from './session.ts'; import { PgliteSession } from './session.ts'; @@ -105,12 +105,12 @@ export function drizzle< ): PgliteDatabase & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (params[0] instanceof PGlite) { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; + if (params[0] === undefined || typeof params[0] === 'string') { + const instance = new PGlite(params[0]); + return construct(instance, params[1]) as any; } - if (typeof params[0] === 'object') { + if (isConfig(params[0])) { const { connection, client, ...drizzleConfig } = params[0] as { connection?: PGliteOptions & { dataDir: string }; client?: TClient; @@ -131,8 +131,7 @@ export function drizzle< return construct(instance, drizzleConfig) as any; } - const instance = new PGlite(params[0]); - return construct(instance, params[1]) as any; + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/planetscale-serverless/driver.ts b/drizzle-orm/src/planetscale-serverless/driver.ts index 1865673bf..e8309e10a 100644 --- a/drizzle-orm/src/planetscale-serverless/driver.ts +++ b/drizzle-orm/src/planetscale-serverless/driver.ts @@ -1,4 +1,4 @@ -import type { Config, Connection } from '@planetscale/database'; +import type { Config } from '@planetscale/database'; import { Client } from '@planetscale/database'; import { entityKind } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; @@ -11,7 +11,7 @@ import { type RelationalSchemaConfig, type TablesRelationalConfig, } from '~/relations.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from './session.ts'; import { PlanetscaleSession } from './session.ts'; @@ -27,7 +27,7 @@ export class PlanetScaleDatabase< function construct< TSchema extends Record = Record, - TClient extends Client | Connection = Client | Connection, + TClient extends Client = Client, >( client: TClient, config: DrizzleConfig = {}, @@ -37,22 +37,7 @@ function construct< // Client is not Drizzle Object, so we can ignore this rule here // eslint-disable-next-line no-instanceof/no-instanceof if (!(client instanceof Client)) { - // Should use error on 0.30.0 release - // throw new DrizzleError({ - // message: `You need to pass an instance of Client: - - // import { Client } from "@planetscale/database"; - - // const client = new Client({ - // host: process.env["DATABASE_HOST"], - // username: process.env["DATABASE_USERNAME"], - // password: process.env["DATABASE_PASSWORD"], - // }); - - // const db = drizzle(client); - // `, - // }); - console.log(`Warning: You need to pass an instance of Client: + throw new Error(`Warning: You need to pass an instance of Client: import { Client } from "@planetscale/database"; @@ -63,8 +48,6 @@ const client = new Client({ }); const db = drizzle(client); - -Starting from version 0.30.0, you will encounter an error if you attempt to use anything other than a Client instance.\nPlease make the necessary changes now to prevent any runtime errors in the future `); } @@ -122,12 +105,15 @@ export function drizzle< ): PlanetScaleDatabase & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (params[0] instanceof Client) { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; + if (typeof params[0] === 'string') { + const instance = new Client({ + url: params[0], + }); + + return construct(instance, params[1]) as any; } - if (typeof params[0] === 'object') { + if (isConfig(params[0])) { const { connection, client, ...drizzleConfig } = params[0] as & { connection?: Config | string; client?: TClient } & DrizzleConfig; @@ -145,11 +131,7 @@ export function drizzle< return construct(instance, drizzleConfig) as any; } - const instance = new Client({ - url: params[0], - }); - - return construct(instance, params[1]) as any; + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/postgres-js/driver.ts b/drizzle-orm/src/postgres-js/driver.ts index 5c2979c84..5d1ff6755 100644 --- a/drizzle-orm/src/postgres-js/driver.ts +++ b/drizzle-orm/src/postgres-js/driver.ts @@ -9,7 +9,7 @@ import { type RelationalSchemaConfig, type TablesRelationalConfig, } from '~/relations.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import type { PostgresJsQueryResultHKT } from './session.ts'; import { PostgresJsSession } from './session.ts'; @@ -89,11 +89,13 @@ export function drizzle< ): PostgresJsDatabase & { $client: TClient; } { - if (typeof params[0] === 'function') { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; + if (typeof params[0] === 'string') { + const instance = pgClient(params[0] as string); + + return construct(instance, params[1]) as any; } - if (typeof params[0] === 'object') { + if (isConfig(params[0])) { const { connection, client, ...drizzleConfig } = params[0] as { connection?: { url?: string } & Options>; client?: TClient; @@ -112,9 +114,7 @@ export function drizzle< return construct(instance, drizzleConfig) as any; } - const instance = pgClient(params[0] as string); - - return construct(instance, params[1]) as any; + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/tidb-serverless/driver.ts b/drizzle-orm/src/tidb-serverless/driver.ts index 01f54af6e..62fbfb9ab 100644 --- a/drizzle-orm/src/tidb-serverless/driver.ts +++ b/drizzle-orm/src/tidb-serverless/driver.ts @@ -1,4 +1,4 @@ -import { type Config, connect, Connection } from '@tidbcloud/serverless'; +import { type Config, connect, type Connection } from '@tidbcloud/serverless'; import { entityKind } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; import { DefaultLogger } from '~/logger.ts'; @@ -10,7 +10,7 @@ import { type RelationalSchemaConfig, type TablesRelationalConfig, } from '~/relations.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import type { TiDBServerlessPreparedQueryHKT, TiDBServerlessQueryResultHKT } from './session.ts'; import { TiDBServerlessSession } from './session.ts'; @@ -82,11 +82,6 @@ export function drizzle< ): TiDBServerlessDatabase & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (params[0] instanceof Connection) { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; - } - if (typeof params[0] === 'string') { const instance = connect({ url: params[0], @@ -95,19 +90,23 @@ export function drizzle< return construct(instance, params[1]) as any; } - const { connection, client, ...drizzleConfig } = params[0] as - & { connection?: Config | string; client?: TClient } - & DrizzleConfig; + if (isConfig(params[0])) { + const { connection, client, ...drizzleConfig } = params[0] as + & { connection?: Config | string; client?: TClient } + & DrizzleConfig; - if (client) return construct(client, drizzleConfig) as any; + if (client) return construct(client, drizzleConfig) as any; - const instance = typeof connection === 'string' - ? connect({ - url: connection, - }) - : connect(connection!); + const instance = typeof connection === 'string' + ? connect({ + url: connection, + }) + : connect(connection!); + + return construct(instance, drizzleConfig) as any; + } - return construct(instance, drizzleConfig) as any; + return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/utils.ts b/drizzle-orm/src/utils.ts index 20abd0a5a..8d563d0da 100644 --- a/drizzle-orm/src/utils.ts +++ b/drizzle-orm/src/utils.ts @@ -240,8 +240,74 @@ export function getColumnNameAndConfig< export type IfNotImported = unknown extends T ? Y : N; export type ImportTypeError = - `Please install \`${TPackageName}\`to allow Drizzle ORM to connect to the database`; + `Please install \`${TPackageName}\` to allow Drizzle ORM to connect to the database`; export type RequireAtLeastOne = Keys extends any ? Required> & Partial> : never; + +type ExpectedConfigShape = { + logger?: boolean | { + logQuery(query: string, params: unknown[]): void; + }; + schema?: Record; + casing?: 'snake_case' | 'camelCase'; +}; + +// If this errors, you must update config shape checker function with new config specs +const _: DrizzleConfig = {} as ExpectedConfigShape; +const __: ExpectedConfigShape = {} as DrizzleConfig; + +export function isConfig(data: any): boolean { + if (typeof data !== 'object' || data === null) return false; + + if (data.constructor.name !== 'Object') return false; + + if ('logger' in data) { + const type = typeof data['logger']; + if ( + type !== 'boolean' && (type !== 'object' || typeof data['logger']['logQuery'] !== 'function') + && type !== 'undefined' + ) return false; + + return true; + } + + if ('schema' in data) { + const type = typeof data['logger']; + if (type !== 'object' && type !== 'undefined') return false; + + return true; + } + + if ('casing' in data) { + const type = typeof data['logger']; + if (type !== 'string' && type !== 'undefined') return false; + + return true; + } + + if ('mode' in data) { + if (data['mode'] !== 'default' || data['mode'] !== 'planetscale' || data['mode'] !== undefined) return false; + + return true; + } + + if ('connection' in data) { + const type = typeof data['connection']; + if (type !== 'string' && type !== 'object' && type !== 'undefined') return false; + + return true; + } + + if ('client' in data) { + const type = typeof data['client']; + if (type !== 'object' && type !== 'undefined') return false; + + return true; + } + + if (Object.keys(data).length === 0) return true; + + return false; +} diff --git a/drizzle-orm/src/vercel-postgres/driver.ts b/drizzle-orm/src/vercel-postgres/driver.ts index 5e6c44c27..44606a079 100644 --- a/drizzle-orm/src/vercel-postgres/driver.ts +++ b/drizzle-orm/src/vercel-postgres/driver.ts @@ -1,4 +1,4 @@ -import { type QueryResult, type QueryResultRow, sql, type VercelPool } from '@vercel/postgres'; +import { sql, type VercelPool } from '@vercel/postgres'; import { entityKind } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; import { DefaultLogger } from '~/logger.ts'; @@ -10,7 +10,7 @@ import { type RelationalSchemaConfig, type TablesRelationalConfig, } from '~/relations.ts'; -import type { DrizzleConfig, IfNotImported, ImportTypeError } from '~/utils.ts'; +import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts'; import { type VercelPgClient, type VercelPgQueryResultHKT, VercelPgSession } from './session.ts'; export interface VercelPgDriverOptions { @@ -75,13 +75,9 @@ function construct = Record = Record, - TClient extends VercelPgClient = - & VercelPool - & ((strings: TemplateStringsArray, ...values: Primitive[]) => Promise>), + TClient extends VercelPgClient = typeof sql, >( ...params: IfNotImported< VercelPool, @@ -103,17 +99,12 @@ export function drizzle< ): VercelPgDatabase & { $client: TClient; } { - // eslint-disable-next-line no-instanceof/no-instanceof - if (typeof params[0] === 'function') { - return construct(params[0] as TClient, params[1] as DrizzleConfig | undefined) as any; - } - - if (!params[0] || !(params[0] as { client?: TClient }).client) { - return construct(sql, params[0] as DrizzleConfig | undefined) as any; + if (isConfig(params[0])) { + const { client, ...drizzleConfig } = params[0] as ({ client?: TClient } & DrizzleConfig); + return construct(client ?? sql, drizzleConfig) as any; } - const { client, ...drizzleConfig } = params[0] as ({ client?: TClient } & DrizzleConfig); - return construct(client ?? sql, drizzleConfig) as any; + return construct((params[0] ?? sql) as TClient, params[1] as DrizzleConfig | undefined) as any; } export namespace drizzle { diff --git a/drizzle-orm/src/version.ts b/drizzle-orm/src/version.ts index d670a0575..87f7e2ae9 100644 --- a/drizzle-orm/src/version.ts +++ b/drizzle-orm/src/version.ts @@ -1,4 +1,4 @@ // @ts-ignore - imported using Rollup json plugin export { version as npmVersion } from '../package.json'; // In version 7, we changed the PostgreSQL indexes API -export const compatibilityVersion = 8; +export const compatibilityVersion = 9; diff --git a/integration-tests/.env.example b/integration-tests/.env.example index 50b3c95f2..ceff7d132 100644 --- a/integration-tests/.env.example +++ b/integration-tests/.env.example @@ -5,7 +5,10 @@ TIDB_CONNECTION_STRING= NEON_CONNECTION_STRING= LIBSQL_URL="file:local.db" LIBSQL_AUTH_TOKEN="ey..." # For Turso only +LIBSQL_REMOTE_URL="libsql://..." +LIBSQL_REMOTE_TOKEN="ey..." AWS_DATA_API_DB= AWS_DATA_API_SECRET_ARN= AWS_DATA_API_RESOURCE_ARN= AWS_TEST_PROFILE= +VERCEL_CONNECTION_STRING= # For driver-init, utils/is-config tests. Must not be local DB - breaks with Vercel drivers \ No newline at end of file diff --git a/integration-tests/js-tests/driver-init/commonjs/better-sqlite3.test.cjs b/integration-tests/js-tests/driver-init/commonjs/better-sqlite3.test.cjs new file mode 100644 index 000000000..1a9941e30 --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/better-sqlite3.test.cjs @@ -0,0 +1,124 @@ +require('dotenv/config'); +const Database = require('better-sqlite3'); +const { drizzle } = require('drizzle-orm/better-sqlite3'); +const { sqlite: schema } = require('./schema.cjs'); +import { describe, expect } from 'vitest'; + +describe('better-sqlite3', async (it) => { + it('drizzle()', async () => { + const db = drizzle(); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(string)', async () => { + const db = drizzle(':memory:'); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle(':memory:', { + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: ':memory:', + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + source: ':memory:', + }, + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: {}, ...config})', async () => { + const db = drizzle({ + connection: {}, + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({...config})', async () => { + const db = drizzle({ + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = new Database(':memory:'); + const db = drizzle(client); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(client, config)', async () => { + const client = new Database(':memory:'); + const db = drizzle(client, { + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Database(':memory:'); + const db = drizzle({ + client, + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/libsql.test.cjs b/integration-tests/js-tests/driver-init/commonjs/libsql.test.cjs new file mode 100644 index 000000000..e070af1bc --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/libsql.test.cjs @@ -0,0 +1,97 @@ +require('dotenv/config'); +const { createClient } = require('@libsql/client'); +const { drizzle } = require('drizzle-orm/libsql'); +const { sqlite: schema } = require('./schema.cjs'); +import { describe, expect } from 'vitest'; + +describe('libsql', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle(':memory:'); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle(':memory:', { + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: ':memory:', + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + url: ':memory:', + }, + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = createClient({ + url: ':memory:', + }); + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(client, config)', async () => { + const client = createClient({ + url: ':memory:', + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createClient({ + url: ':memory:', + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/mysql2.test.cjs b/integration-tests/js-tests/driver-init/commonjs/mysql2.test.cjs new file mode 100644 index 000000000..44a0987d1 --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/mysql2.test.cjs @@ -0,0 +1,153 @@ +require('dotenv/config'); +const { drizzle } = require('drizzle-orm/mysql2'); +const { createPool, createConnection, Connection } = require('mysql2'); +const { mysql: schema } = require('./schema.cjs'); +import { describe, expect } from 'vitest'; + +if (!process.env['MYSQL_CONNECTION_STRING']) { + throw new Error('MYSQL_CONNECTION_STRING is not defined'); +} + +describe('mysql2', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['MYSQL_CONNECTION_STRING'], + ); + + await db.$client.execute(`SELECT 1`); + + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['MYSQL_CONNECTION_STRING'], + { + schema, + mode: 'default', + }, + ); + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['MYSQL_CONNECTION_STRING'], + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + uri: process.env['MYSQL_CONNECTION_STRING'], + }, + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = createPool({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle(client, config)', async () => { + const client = createPool({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createPool({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const db = drizzle({ + client, + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); +}); + +describe('mysql2:connection', async (it) => { + it('drizzle(client)', async () => { + const client = createConnection({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client.getConnection).toStrictEqual(undefined); + }); + + it('drizzle(client, config)', async () => { + const client = createConnection({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createConnection({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const db = drizzle({ + client, + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/neon-http.test.cjs b/integration-tests/js-tests/driver-init/commonjs/neon-http.test.cjs new file mode 100644 index 000000000..ad68b96a3 --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/neon-http.test.cjs @@ -0,0 +1,92 @@ +require('dotenv/config'); +const { neon: pg } = require('@neondatabase/serverless'); +const { drizzle } = require('drizzle-orm/neon-http'); +const { pg: schema } = require('./schema.cjs'); +import { describe, expect } from 'vitest'; + +if (!process.env['NEON_CONNECTION_STRING']) { + throw new Error('NEON_CONNECTION_STRING is not defined'); +} + +describe('neon-http', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['NEON_CONNECTION_STRING'], + ); + + await db.$client('SELECT 1;'); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['NEON_CONNECTION_STRING'], + { + schema, + }, + ); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['NEON_CONNECTION_STRING'], + schema, + }); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + connectionString: process.env['NEON_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = pg( + process.env['NEON_CONNECTION_STRING'], + ); + const db = drizzle(client); + + await db.$client('SELECT 1;'); + }); + + it('drizzle(client, config)', async () => { + const client = pg( + process.env['NEON_CONNECTION_STRING'], + ); + const db = drizzle(client, { + schema, + }); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = pg( + process.env['NEON_CONNECTION_STRING'], + ); + const db = drizzle({ + client, + schema, + }); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/neon-ws.test.cjs b/integration-tests/js-tests/driver-init/commonjs/neon-ws.test.cjs new file mode 100644 index 000000000..28b5026c6 --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/neon-ws.test.cjs @@ -0,0 +1,211 @@ +require('dotenv/config'); +const { neonConfig, Pool, Client } = require('@neondatabase/serverless'); +const { drizzle } = require('drizzle-orm/neon-serverless'); +const { pg: schema } = require('./schema.cjs'); +const ws = require('ws'); +import { describe, expect } from 'vitest'; + +neonConfig.webSocketConstructor = ws; + +if (!process.env['NEON_CONNECTION_STRING']) { + throw new Error('NEON_CONNECTION_STRING is not defined'); +} + +describe('neon-ws', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['NEON_CONNECTION_STRING'], + ); + + await db.$client.query('SELECT 1;'); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['NEON_CONNECTION_STRING'], + { + schema, + }, + ); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['NEON_CONNECTION_STRING'], + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + connectionString: process.env['NEON_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(client)', async () => { + const client = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const client = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); +}); + +describe('neon-ws:Client', async (it) => { + it('drizzle(client)', async () => { + const client = new Client({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + + await client.connect(); + + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const client = new Client({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Client({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); +}); + +describe('neon-ws:PoolClient', async (it) => { + it('drizzle(client)', async () => { + const pool = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + client.release(); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const pool = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + client.release(); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); + + it('drizzle({client, ...config})', async () => { + const pool = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle({ + client, + schema, + }); + + await db.$client.query('SELECT 1;'); + + client.release(); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/node-pg.test.cjs b/integration-tests/js-tests/driver-init/commonjs/node-pg.test.cjs new file mode 100644 index 000000000..53338d061 --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/node-pg.test.cjs @@ -0,0 +1,201 @@ +require('dotenv/config'); +const { drizzle } = require('drizzle-orm/node-postgres'); +const pg = require('pg'); +const { pg: schema } = require('./schema.cjs'); +import { describe, expect } from 'vitest'; + +const Pool = pg.Pool; +const Client = pg.Client; + +if (!process.env['PG_CONNECTION_STRING']) { + throw new Error('PG_CONNECTION_STRING is not defined'); +} + +describe('node-pg', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle(process.env['PG_CONNECTION_STRING']); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle(process.env['PG_CONNECTION_STRING'], { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['PG_CONNECTION_STRING'], + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + connectionString: process.env['PG_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const client = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('node-pg:Client', async (it) => { + it('drizzle(client)', async () => { + const client = new Client({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const client = new Client({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Client({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('node-pg:PoolClient', async (it) => { + it('drizzle(client)', async () => { + const pool = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const client = await pool.connect(); + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const pool = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const client = await pool.connect(); + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const pool = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const client = await pool.connect(); + const db = drizzle({ + client, + schema, + }); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/pglite.test.cjs b/integration-tests/js-tests/driver-init/commonjs/pglite.test.cjs new file mode 100644 index 000000000..a00afb041 --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/pglite.test.cjs @@ -0,0 +1,88 @@ +require('dotenv/config'); +const { drizzle } = require('drizzle-orm/pglite'); +const { pg: schema } = require('./schema.cjs'); +const { PGlite: Database } = require('@electric-sql/pglite'); +import { describe, expect } from 'vitest'; + +describe('pglite', async (it) => { + it('drizzle()', async () => { + const db = drizzle(); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + }); + + it('drizzle(string)', async () => { + const db = drizzle('memory://'); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle('memory://', { + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: {}, ...config})', async () => { + const db = drizzle({ + connection: {}, + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({...config})', async () => { + const db = drizzle({ + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = new Database('memory://'); + const db = drizzle(client); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + }); + + it('drizzle(client, config)', async () => { + const client = new Database('memory://'); + const db = drizzle(client, { + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Database('memory://'); + const db = drizzle({ + client, + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/planetscale.test.cjs b/integration-tests/js-tests/driver-init/commonjs/planetscale.test.cjs new file mode 100644 index 000000000..0894d30c2 --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/planetscale.test.cjs @@ -0,0 +1,101 @@ +require('dotenv/config'); +const { Client } = require('@planetscale/database'); +const { drizzle } = require('drizzle-orm/planetscale-serverless'); +const { mysql: schema } = require('./schema.cjs'); +import { describe, expect } from 'vitest'; + +if (!process.env['PLANETSCALE_CONNECTION_STRING']) { + throw new Error('PLANETSCALE_CONNECTION_STRING is not defined'); +} + +describe('planetscale', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['PLANETSCALE_CONNECTION_STRING'], + ); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['PLANETSCALE_CONNECTION_STRING'], + { + schema, + }, + ); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['PLANETSCALE_CONNECTION_STRING'], + schema, + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + url: process.env['PLANETSCALE_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = new Client({ + url: process.env['PLANETSCALE_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const client = new Client({ + url: process.env['PLANETSCALE_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Client({ + url: process.env['PLANETSCALE_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/postgres-js.test.cjs b/integration-tests/js-tests/driver-init/commonjs/postgres-js.test.cjs new file mode 100644 index 000000000..da79953f3 --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/postgres-js.test.cjs @@ -0,0 +1,81 @@ +require('dotenv/config'); +const { drizzle } = require('drizzle-orm/postgres-js'); +const pg = require('postgres'); +const { pg: schema } = require('./schema.cjs'); +import { describe, expect } from 'vitest'; + +if (!process.env['PG_CONNECTION_STRING']) { + throw new Error('PG_CONNECTION_STRING is not defined'); +} + +describe('postgres-js', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle(process.env['PG_CONNECTION_STRING']); + + await db.$client.unsafe('SELECT 1;'); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle(process.env['PG_CONNECTION_STRING'], { + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['PG_CONNECTION_STRING'], + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + url: process.env['PG_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = pg(process.env['PG_CONNECTION_STRING']); + const db = drizzle(client); + + await db.$client.unsafe('SELECT 1;'); + }); + + it('drizzle(client, config)', async () => { + const client = pg(process.env['PG_CONNECTION_STRING']); + const db = drizzle(client, { + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = pg(process.env['PG_CONNECTION_STRING']); + const db = drizzle({ + client, + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/schema.cjs b/integration-tests/js-tests/driver-init/commonjs/schema.cjs new file mode 100644 index 000000000..7015a068d --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/schema.cjs @@ -0,0 +1,21 @@ +const { int: mysqlInt, mysqlTable } = require('drizzle-orm/mysql-core'); +const { integer: pgInt, pgTable } = require('drizzle-orm/pg-core'); +const { integer: sqliteInt, sqliteTable } = require('drizzle-orm/sqlite-core'); + +module.exports.sqlite = { + User: sqliteTable('test', { + id: sqliteInt('id').primaryKey().notNull(), + }), +}; + +module.exports.pg = { + User: pgTable('test', { + id: pgInt('id').primaryKey().notNull(), + }), +}; + +module.exports.mysql = { + User: mysqlTable('test', { + id: mysqlInt('id').primaryKey().notNull(), + }), +}; diff --git a/integration-tests/js-tests/driver-init/commonjs/tidb.test.cjs b/integration-tests/js-tests/driver-init/commonjs/tidb.test.cjs new file mode 100644 index 000000000..a64588b8a --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/tidb.test.cjs @@ -0,0 +1,88 @@ +require('dotenv/config'); +const { connect } = require('@tidbcloud/serverless'); +const { drizzle } = require('drizzle-orm/tidb-serverless'); +const { mysql: schema } = require('./schema.cjs'); +import { describe, expect } from 'vitest'; + +if (!process.env['TIDB_CONNECTION_STRING']) { + throw new Error('TIDB_CONNECTION_STRING is not defined'); +} + +describe('tidb', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['TIDB_CONNECTION_STRING'], + ); + + await db.$client.execute(`SELECT 1`); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['TIDB_CONNECTION_STRING'], + { + schema, + }, + ); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['TIDB_CONNECTION_STRING'], + schema, + }); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + url: process.env['TIDB_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = connect({ + url: process.env['TIDB_CONNECTION_STRING'], + }); + + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + }); + + it('drizzle(client, config)', async () => { + const client = connect({ + url: process.env['TIDB_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = connect({ + url: process.env['TIDB_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/commonjs/vercel.test.cjs b/integration-tests/js-tests/driver-init/commonjs/vercel.test.cjs new file mode 100644 index 000000000..ad418a26e --- /dev/null +++ b/integration-tests/js-tests/driver-init/commonjs/vercel.test.cjs @@ -0,0 +1,229 @@ +require('dotenv/config'); +const vc = require('@vercel/postgres'); +const { drizzle } = require('drizzle-orm/vercel-postgres'); +const { pg: schema } = require('./schema.cjs'); +import { describe, expect } from 'vitest'; +const { sql, createClient, createPool } = vc; + +const Pool = vc.VercelPool; +const Client = vc.VercelClient; + +if (!process.env['VERCEL_CONNECTION_STRING']) { + throw new Error('VERCEL_CONNECTION_STRING is not defined'); +} + +// Used for non-pooled connection +if (!process.env['NEON_CONNECTION_STRING']) { + throw new Error('NEON_CONNECTION_STRING is not defined'); +} +process.env['POSTGRES_URL'] = process.env['VERCEL_CONNECTION_STRING']; + +describe('vercel:sql', async (it) => { + it('drizzle()', async () => { + const db = drizzle(); + + await sql.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + }); + + it('drizzle(client)', async () => { + const db = drizzle(sql); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + }); + + it('drizzle(client, config)', async () => { + const db = drizzle(sql, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const db = drizzle({ + client: sql, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({...config})', async () => { + const db = drizzle({ + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('vercel:Pool', async (it) => { + it('drizzle(client)', async () => { + const client = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const client = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const db = drizzle({ + client: client, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('vercel:Client', async (it) => { + it('drizzle(client)', async () => { + const client = createClient({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const client = createClient({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createClient({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle({ + client: client, + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('vercel:PoolClient', async (it) => { + it('drizzle(client)', async () => { + const pool = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const pool = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const pool = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle({ + client: client, + schema, + }); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/better-sqlite3.test.mjs b/integration-tests/js-tests/driver-init/module/better-sqlite3.test.mjs new file mode 100644 index 000000000..063eb958f --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/better-sqlite3.test.mjs @@ -0,0 +1,124 @@ +import 'dotenv/config'; +import Database from 'better-sqlite3'; +import { drizzle } from 'drizzle-orm/better-sqlite3'; +import { describe, expect } from 'vitest'; +import { sqlite as schema } from './schema.mjs'; + +describe('better-sqlite3', async (it) => { + it('drizzle()', async () => { + const db = drizzle(); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(string)', async () => { + const db = drizzle(':memory:'); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle(':memory:', { + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: ':memory:', + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + source: ':memory:', + }, + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: {}, ...config})', async () => { + const db = drizzle({ + connection: {}, + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({...config})', async () => { + const db = drizzle({ + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = new Database(':memory:'); + const db = drizzle(client); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(client, config)', async () => { + const client = new Database(':memory:'); + const db = drizzle(client, { + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Database(':memory:'); + const db = drizzle({ + client, + schema, + }); + + await db.$client.exec('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/libsql.test.mjs b/integration-tests/js-tests/driver-init/module/libsql.test.mjs new file mode 100644 index 000000000..816d0eb0e --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/libsql.test.mjs @@ -0,0 +1,97 @@ +import 'dotenv/config'; +import { createClient } from '@libsql/client'; +import { drizzle } from 'drizzle-orm/libsql'; +import { describe, expect } from 'vitest'; +import { sqlite as schema } from './schema.mjs'; + +describe('libsql', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle(':memory:'); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle(':memory:', { + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: ':memory:', + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + url: ':memory:', + }, + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = createClient({ + url: ':memory:', + }); + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + }); + + it('drizzle(client, config)', async () => { + const client = createClient({ + url: ':memory:', + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createClient({ + url: ':memory:', + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.execute('SELECT 1;'); + + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/mysql2.test.mjs b/integration-tests/js-tests/driver-init/module/mysql2.test.mjs new file mode 100644 index 000000000..1a1565c67 --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/mysql2.test.mjs @@ -0,0 +1,153 @@ +import 'dotenv/config'; +import { drizzle } from 'drizzle-orm/mysql2'; +import { Connection, createConnection, createPool } from 'mysql2'; +import { describe, expect } from 'vitest'; +import { mysql as schema } from './schema.mjs'; + +if (!process.env['MYSQL_CONNECTION_STRING']) { + throw new Error('MYSQL_CONNECTION_STRING is not defined'); +} + +describe('mysql2', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['MYSQL_CONNECTION_STRING'], + ); + + await db.$client.execute(`SELECT 1`); + + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['MYSQL_CONNECTION_STRING'], + { + schema, + mode: 'default', + }, + ); + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['MYSQL_CONNECTION_STRING'], + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + uri: process.env['MYSQL_CONNECTION_STRING'], + }, + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = createPool({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle(client, config)', async () => { + const client = createPool({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createPool({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const db = drizzle({ + client, + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).not.toStrictEqual(undefined); + }); +}); + +describe('mysql2:connection', async (it) => { + it('drizzle(client)', async () => { + const client = createConnection({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client.getConnection).toStrictEqual(undefined); + }); + + it('drizzle(client, config)', async () => { + const client = createConnection({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createConnection({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const db = drizzle({ + client, + schema, + mode: 'default', + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client.getConnection).toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/neon-http.test.mjs b/integration-tests/js-tests/driver-init/module/neon-http.test.mjs new file mode 100644 index 000000000..b9c07f4d9 --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/neon-http.test.mjs @@ -0,0 +1,92 @@ +import 'dotenv/config'; +import { neon as pg } from '@neondatabase/serverless'; +import { drizzle } from 'drizzle-orm/neon-http'; +import { describe, expect } from 'vitest'; +import { pg as schema } from './schema.mjs'; + +if (!process.env['NEON_CONNECTION_STRING']) { + throw new Error('NEON_CONNECTION_STRING is not defined'); +} + +describe('neon-http', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['NEON_CONNECTION_STRING'], + ); + + await db.$client('SELECT 1;'); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['NEON_CONNECTION_STRING'], + { + schema, + }, + ); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['NEON_CONNECTION_STRING'], + schema, + }); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + connectionString: process.env['NEON_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = pg( + process.env['NEON_CONNECTION_STRING'], + ); + const db = drizzle(client); + + await db.$client('SELECT 1;'); + }); + + it('drizzle(client, config)', async () => { + const client = pg( + process.env['NEON_CONNECTION_STRING'], + ); + const db = drizzle(client, { + schema, + }); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = pg( + process.env['NEON_CONNECTION_STRING'], + ); + const db = drizzle({ + client, + schema, + }); + + await db.$client('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/neon-ws.test.mjs b/integration-tests/js-tests/driver-init/module/neon-ws.test.mjs new file mode 100644 index 000000000..5a045b76a --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/neon-ws.test.mjs @@ -0,0 +1,211 @@ +import 'dotenv/config'; +import { Client, neonConfig, Pool } from '@neondatabase/serverless'; +import { drizzle } from 'drizzle-orm/neon-serverless'; +import { describe, expect } from 'vitest'; +import ws from 'ws'; +import { pg as schema } from './schema.mjs'; + +neonConfig.webSocketConstructor = ws; + +if (!process.env['NEON_CONNECTION_STRING']) { + throw new Error('NEON_CONNECTION_STRING is not defined'); +} + +describe('neon-ws', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['NEON_CONNECTION_STRING'], + ); + + await db.$client.query('SELECT 1;'); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['NEON_CONNECTION_STRING'], + { + schema, + }, + ); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['NEON_CONNECTION_STRING'], + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + connectionString: process.env['NEON_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(client)', async () => { + const client = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const client = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Pool); + }); +}); + +describe('neon-ws:Client', async (it) => { + it('drizzle(client)', async () => { + const client = new Client({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + + await client.connect(); + + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const client = new Client({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Client({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); +}); + +describe('neon-ws:PoolClient', async (it) => { + it('drizzle(client)', async () => { + const pool = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + client.release(); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const pool = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + client.release(); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); + + it('drizzle({client, ...config})', async () => { + const pool = new Pool({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle({ + client, + schema, + }); + + await db.$client.query('SELECT 1;'); + + client.release(); + + expect(db.query.User).not.toStrictEqual(undefined); + expect(db.$client).toBeInstanceOf(Client); + expect(db.$client).not.toBeInstanceOf(Pool); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/node-pg.test.mjs b/integration-tests/js-tests/driver-init/module/node-pg.test.mjs new file mode 100644 index 000000000..75dd391f1 --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/node-pg.test.mjs @@ -0,0 +1,201 @@ +import 'dotenv/config'; +import { drizzle } from 'drizzle-orm/node-postgres'; +import pg from 'pg'; +import { describe, expect } from 'vitest'; +import { pg as schema } from './schema.mjs'; + +const Pool = pg.Pool; +const Client = pg.Client; + +if (!process.env['PG_CONNECTION_STRING']) { + throw new Error('PG_CONNECTION_STRING is not defined'); +} + +describe('node-pg', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle(process.env['PG_CONNECTION_STRING']); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle(process.env['PG_CONNECTION_STRING'], { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['PG_CONNECTION_STRING'], + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + connectionString: process.env['PG_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const client = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('node-pg:Client', async (it) => { + it('drizzle(client)', async () => { + const client = new Client({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const client = new Client({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Client({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('node-pg:PoolClient', async (it) => { + it('drizzle(client)', async () => { + const pool = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const client = await pool.connect(); + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const pool = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const client = await pool.connect(); + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const pool = new Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + const client = await pool.connect(); + const db = drizzle({ + client, + schema, + }); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/pglite.test.mjs b/integration-tests/js-tests/driver-init/module/pglite.test.mjs new file mode 100644 index 000000000..bb67f383b --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/pglite.test.mjs @@ -0,0 +1,88 @@ +import 'dotenv/config'; +import { PGlite as Database } from '@electric-sql/pglite'; +import { drizzle } from 'drizzle-orm/pglite'; +import { describe, expect } from 'vitest'; +import { pg as schema } from './schema.mjs'; + +describe('pglite', async (it) => { + it('drizzle()', async () => { + const db = drizzle(); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + }); + + it('drizzle(string)', async () => { + const db = drizzle('memory://'); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle('memory://', { + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: {}, ...config})', async () => { + const db = drizzle({ + connection: {}, + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({...config})', async () => { + const db = drizzle({ + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = new Database('memory://'); + const db = drizzle(client); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + }); + + it('drizzle(client, config)', async () => { + const client = new Database('memory://'); + const db = drizzle(client, { + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Database('memory://'); + const db = drizzle({ + client, + schema, + }); + + await db.$client.exec('SELECT 1;'); + await db.$client.close(); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/planetscale.test.mjs b/integration-tests/js-tests/driver-init/module/planetscale.test.mjs new file mode 100644 index 000000000..0a68b7e9f --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/planetscale.test.mjs @@ -0,0 +1,101 @@ +import 'dotenv/config'; +import { Client } from '@planetscale/database'; +import { drizzle } from 'drizzle-orm/planetscale-serverless'; +import { describe, expect } from 'vitest'; +import { mysql as schema } from './schema.mjs'; + +if (!process.env['PLANETSCALE_CONNECTION_STRING']) { + throw new Error('PLANETSCALE_CONNECTION_STRING is not defined'); +} + +describe('planetscale', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['PLANETSCALE_CONNECTION_STRING'], + ); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['PLANETSCALE_CONNECTION_STRING'], + { + schema, + }, + ); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['PLANETSCALE_CONNECTION_STRING'], + schema, + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + url: process.env['PLANETSCALE_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = new Client({ + url: process.env['PLANETSCALE_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const client = new Client({ + url: process.env['PLANETSCALE_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = new Client({ + url: process.env['PLANETSCALE_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.execute('SELECT 1;'); + + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/postgres-js.test.mjs b/integration-tests/js-tests/driver-init/module/postgres-js.test.mjs new file mode 100644 index 000000000..40925aceb --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/postgres-js.test.mjs @@ -0,0 +1,81 @@ +import 'dotenv/config'; +import { drizzle } from 'drizzle-orm/postgres-js'; +import pg from 'postgres'; +import { describe, expect } from 'vitest'; +import { pg as schema } from './schema.mjs'; + +if (!process.env['PG_CONNECTION_STRING']) { + throw new Error('PG_CONNECTION_STRING is not defined'); +} + +describe('postgres-js', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle(process.env['PG_CONNECTION_STRING']); + + await db.$client.unsafe('SELECT 1;'); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle(process.env['PG_CONNECTION_STRING'], { + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['PG_CONNECTION_STRING'], + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + url: process.env['PG_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = pg(process.env['PG_CONNECTION_STRING']); + const db = drizzle(client); + + await db.$client.unsafe('SELECT 1;'); + }); + + it('drizzle(client, config)', async () => { + const client = pg(process.env['PG_CONNECTION_STRING']); + const db = drizzle(client, { + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = pg(process.env['PG_CONNECTION_STRING']); + const db = drizzle({ + client, + schema, + }); + + await db.$client.unsafe('SELECT 1;'); + + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/schema.mjs b/integration-tests/js-tests/driver-init/module/schema.mjs new file mode 100644 index 000000000..9c0d4d689 --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/schema.mjs @@ -0,0 +1,21 @@ +import { int as mysqlInt, mysqlTable } from 'drizzle-orm/mysql-core'; +import { integer as pgInt, pgTable } from 'drizzle-orm/pg-core'; +import { integer as sqliteInt, sqliteTable } from 'drizzle-orm/sqlite-core'; + +export const sqlite = { + User: sqliteTable('test', { + id: sqliteInt('id').primaryKey().notNull(), + }), +}; + +export const pg = { + User: pgTable('test', { + id: pgInt('id').primaryKey().notNull(), + }), +}; + +export const mysql = { + User: mysqlTable('test', { + id: mysqlInt('id').primaryKey().notNull(), + }), +}; diff --git a/integration-tests/js-tests/driver-init/module/tidb.test.mjs b/integration-tests/js-tests/driver-init/module/tidb.test.mjs new file mode 100644 index 000000000..0cd62f00a --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/tidb.test.mjs @@ -0,0 +1,88 @@ +import 'dotenv/config'; +import { connect } from '@tidbcloud/serverless'; +import { drizzle } from 'drizzle-orm/tidb-serverless'; +import { describe, expect } from 'vitest'; +import { mysql as schema } from './schema.mjs'; + +if (!process.env['TIDB_CONNECTION_STRING']) { + throw new Error('TIDB_CONNECTION_STRING is not defined'); +} + +describe('tidb', async (it) => { + it('drizzle(string)', async () => { + const db = drizzle( + process.env['TIDB_CONNECTION_STRING'], + ); + + await db.$client.execute(`SELECT 1`); + }); + + it('drizzle(string, config)', async () => { + const db = drizzle( + process.env['TIDB_CONNECTION_STRING'], + { + schema, + }, + ); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: string, ...config})', async () => { + const db = drizzle({ + connection: process.env['TIDB_CONNECTION_STRING'], + schema, + }); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({connection: params, ...config})', async () => { + const db = drizzle({ + connection: { + url: process.env['TIDB_CONNECTION_STRING'], + }, + schema, + }); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle(client)', async () => { + const client = connect({ + url: process.env['TIDB_CONNECTION_STRING'], + }); + + const db = drizzle(client); + + await db.$client.execute('SELECT 1;'); + }); + + it('drizzle(client, config)', async () => { + const client = connect({ + url: process.env['TIDB_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = connect({ + url: process.env['TIDB_CONNECTION_STRING'], + }); + const db = drizzle({ + client, + schema, + }); + + await db.$client.execute('SELECT 1;'); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/js-tests/driver-init/module/vercel.test.mjs b/integration-tests/js-tests/driver-init/module/vercel.test.mjs new file mode 100644 index 000000000..0ef7c460e --- /dev/null +++ b/integration-tests/js-tests/driver-init/module/vercel.test.mjs @@ -0,0 +1,229 @@ +import 'dotenv/config'; +import { createClient, createPool, sql, VercelClient, VercelPool } from '@vercel/postgres'; +import { drizzle } from 'drizzle-orm/vercel-postgres'; +import { describe, expect } from 'vitest'; +import { pg as schema } from './schema.mjs'; + +const Pool = VercelPool; +const Client = VercelClient; + +if (!process.env['VERCEL_CONNECTION_STRING']) { + throw new Error('VERCEL_CONNECTION_STRING is not defined'); +} + +// Used for non-pooled connection +if (!process.env['NEON_CONNECTION_STRING']) { + throw new Error('NEON_CONNECTION_STRING is not defined'); +} + +process.env['POSTGRES_URL'] = process.env['VERCEL_CONNECTION_STRING']; + +describe('vercel:sql', async (it) => { + it('drizzle()', async () => { + const db = drizzle(); + + await sql.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + }); + + it('drizzle(client)', async () => { + const db = drizzle(sql); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + }); + + it('drizzle(client, config)', async () => { + const db = drizzle(sql, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const db = drizzle({ + client: sql, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({...config})', async () => { + const db = drizzle({ + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).toBeTypeOf('function'); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('vercel:Pool', async (it) => { + it('drizzle(client)', async () => { + const client = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).toBeInstanceOf(Pool); + }); + + it('drizzle(client, config)', async () => { + const client = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const db = drizzle({ + client: client, + schema, + }); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).toBeInstanceOf(Pool); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('vercel:Client', async (it) => { + it('drizzle(client)', async () => { + const client = createClient({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const client = createClient({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle(client, { + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const client = createClient({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + const db = drizzle({ + client: client, + schema, + }); + + await client.connect(); + + await db.$client.query('SELECT 1;'); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); + +describe('vercel:PoolClient', async (it) => { + it('drizzle(client)', async () => { + const pool = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle(client); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + }); + + it('drizzle(client, config)', async () => { + const pool = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle(client, { + schema, + }); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); + + it('drizzle({client, ...config})', async () => { + const pool = createPool({ + connectionString: process.env['VERCEL_CONNECTION_STRING'], + }); + const client = await pool.connect(); + + const db = drizzle({ + client: client, + schema, + }); + + await db.$client.query('SELECT 1;'); + client.release(); + + expect(db.$client).not.toBeTypeOf('function'); + expect(db.$client).not.toBeInstanceOf(Pool); + expect(db.$client).toBeInstanceOf(Client); + expect(db.query.User).not.toStrictEqual(undefined); + }); +}); diff --git a/integration-tests/package.json b/integration-tests/package.json index 2b26ec374..775103111 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -43,7 +43,7 @@ "dependencies": { "@aws-sdk/client-rds-data": "^3.549.0", "@aws-sdk/credential-providers": "^3.549.0", - "@electric-sql/pglite": "^0.1.1", + "@electric-sql/pglite": "^0.2.12", "@miniflare/d1": "^2.14.4", "@miniflare/shared": "^2.14.4", "@planetscale/database": "^1.16.0", diff --git a/integration-tests/tests/imports/index.test.ts b/integration-tests/tests/imports/index.test.ts index 875d1d24c..7a44942fa 100644 --- a/integration-tests/tests/imports/index.test.ts +++ b/integration-tests/tests/imports/index.test.ts @@ -20,7 +20,7 @@ it('dynamic imports check for CommonJS', async () => { const o1 = path.join('drizzle-orm', key); if ( o1.startsWith('drizzle-orm/bun-sqlite') || o1.startsWith('drizzle-orm/pglite') - || o1.startsWith('drizzle-orm/expo-sqlite') + || o1.startsWith('drizzle-orm/expo-sqlite') || o1.startsWith('drizzle-orm/libsql/wasm') ) { continue; } diff --git a/integration-tests/tests/pg/pglite.test.ts b/integration-tests/tests/pg/pglite.test.ts index 37cd3fe62..2a3d6a2ff 100644 --- a/integration-tests/tests/pg/pglite.test.ts +++ b/integration-tests/tests/pg/pglite.test.ts @@ -86,6 +86,10 @@ skipTests([ 'subquery with view', 'mySchema :: materialized view', 'select count()', + // not working in 0.2.12 + 'select with group by as sql + column', + 'select with group by as column + sql', + 'mySchema :: select with group by as column + sql', ]); tests(); diff --git a/integration-tests/tests/sqlite/libsql-http.test.ts b/integration-tests/tests/sqlite/libsql-http.test.ts new file mode 100644 index 000000000..576d8d48a --- /dev/null +++ b/integration-tests/tests/sqlite/libsql-http.test.ts @@ -0,0 +1,186 @@ +import { type Client, createClient } from '@libsql/client/http'; +import retry from 'async-retry'; +import { asc, eq, getTableColumns, sql } from 'drizzle-orm'; +import type { LibSQLDatabase } from 'drizzle-orm/libsql'; +import { drizzle } from 'drizzle-orm/libsql/http'; +import { migrate } from 'drizzle-orm/libsql/migrator'; +import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; +import { skipTests } from '~/common'; +import { randomString } from '~/utils'; +import { anotherUsersMigratorTable, tests, usersMigratorTable, usersOnUpdate } from './sqlite-common'; + +const ENABLE_LOGGING = false; + +let db: LibSQLDatabase; +let client: Client; + +beforeAll(async () => { + const url = process.env['LIBSQL_REMOTE_URL']; + const authToken = process.env['LIBSQL_REMOTE_TOKEN']; + if (!url) { + throw new Error('LIBSQL_REMOTE_URL is not set'); + } + client = await retry(async () => { + client = createClient({ url, authToken }); + return client; + }, { + retries: 20, + factor: 1, + minTimeout: 250, + maxTimeout: 250, + randomize: false, + onRetry() { + client?.close(); + }, + }); + db = drizzle(client, { logger: ENABLE_LOGGING }); +}); + +afterAll(async () => { + client?.close(); +}); + +beforeEach((ctx) => { + ctx.sqlite = { + db, + }; +}); + +test('migrator', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }).run(); + const result = await db.select().from(usersMigratorTable).all(); + + await db.insert(anotherUsersMigratorTable).values({ name: 'John', email: 'email' }).run(); + const result2 = await db.select().from(anotherUsersMigratorTable).all(); + + expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); + expect(result2).toEqual([{ id: 1, name: 'John', email: 'email' }]); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + +test('migrator : migrate with custom table', async () => { + const customTable = randomString(); + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists ${sql.identifier(customTable)}`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite', migrationsTable: customTable }); + + // test if the custom migrations table was created + const res = await db.all(sql`select * from ${sql.identifier(customTable)};`); + expect(res.length > 0).toBeTruthy(); + + // test if the migrated table are working as expected + await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); + const result = await db.select().from(usersMigratorTable); + expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table ${sql.identifier(customTable)}`); +}); + +test('test $onUpdateFn and $onUpdate works as $default', async (ctx) => { + const { db } = ctx.sqlite; + + await db.run(sql`drop table if exists ${usersOnUpdate}`); + + await db.run( + sql` + create table ${usersOnUpdate} ( + id integer primary key autoincrement, + name text not null, + update_counter integer default 1 not null, + updated_at integer, + always_null text + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + expect(response).toEqual([ + { name: 'John', id: 1, updateCounter: 1, alwaysNull: null }, + { name: 'Jane', id: 2, updateCounter: 1, alwaysNull: null }, + { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, + { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, + ]); + const msDelay = 1750; + + for (const eachUser of justDates) { + expect(eachUser.updatedAt!.valueOf()).toBeGreaterThan(Date.now() - msDelay); + } +}); + +test('test $onUpdateFn and $onUpdate works updating', async (ctx) => { + const { db } = ctx.sqlite; + + await db.run(sql`drop table if exists ${usersOnUpdate}`); + + await db.run( + sql` + create table ${usersOnUpdate} ( + id integer primary key autoincrement, + name text not null, + update_counter integer default 1, + updated_at integer, + always_null text + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John', alwaysNull: 'this will be null after updating' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + + await db.update(usersOnUpdate).set({ name: 'Angel' }).where(eq(usersOnUpdate.id, 1)); + await db.update(usersOnUpdate).set({ updateCounter: null }).where(eq(usersOnUpdate.id, 2)); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + expect(response).toEqual([ + { name: 'Angel', id: 1, updateCounter: 2, alwaysNull: null }, + { name: 'Jane', id: 2, updateCounter: null, alwaysNull: null }, + { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, + { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, + ]); + const msDelay = 1750; + + for (const eachUser of justDates) { + expect(eachUser.updatedAt!.valueOf()).toBeGreaterThan(Date.now() - msDelay); + } +}); + +skipTests([ + 'delete with limit and order by', + 'update with limit and order by', + 'test $onUpdateFn and $onUpdate works as $default', + 'test $onUpdateFn and $onUpdate works updating', +]); + +tests(); diff --git a/integration-tests/tests/sqlite/libsql-node.test.ts b/integration-tests/tests/sqlite/libsql-node.test.ts new file mode 100644 index 000000000..bfe2c1574 --- /dev/null +++ b/integration-tests/tests/sqlite/libsql-node.test.ts @@ -0,0 +1,97 @@ +import { type Client, createClient } from '@libsql/client/node'; +import retry from 'async-retry'; +import { sql } from 'drizzle-orm'; +import type { LibSQLDatabase } from 'drizzle-orm/libsql'; +import { migrate } from 'drizzle-orm/libsql/migrator'; +import { drizzle } from 'drizzle-orm/libsql/node'; +import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; +import { skipTests } from '~/common'; +import { randomString } from '~/utils'; +import { anotherUsersMigratorTable, tests, usersMigratorTable } from './sqlite-common'; + +const ENABLE_LOGGING = false; + +let db: LibSQLDatabase; +let client: Client; + +beforeAll(async () => { + const url = process.env['LIBSQL_URL']; + const authToken = process.env['LIBSQL_AUTH_TOKEN']; + if (!url) { + throw new Error('LIBSQL_URL is not set'); + } + client = await retry(async () => { + client = createClient({ url, authToken }); + return client; + }, { + retries: 20, + factor: 1, + minTimeout: 250, + maxTimeout: 250, + randomize: false, + onRetry() { + client?.close(); + }, + }); + db = drizzle(client, { logger: ENABLE_LOGGING }); +}); + +afterAll(async () => { + client?.close(); +}); + +beforeEach((ctx) => { + ctx.sqlite = { + db, + }; +}); + +test('migrator', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }).run(); + const result = await db.select().from(usersMigratorTable).all(); + + await db.insert(anotherUsersMigratorTable).values({ name: 'John', email: 'email' }).run(); + const result2 = await db.select().from(anotherUsersMigratorTable).all(); + + expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); + expect(result2).toEqual([{ id: 1, name: 'John', email: 'email' }]); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + +test('migrator : migrate with custom table', async () => { + const customTable = randomString(); + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists ${sql.identifier(customTable)}`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite', migrationsTable: customTable }); + + // test if the custom migrations table was created + const res = await db.all(sql`select * from ${sql.identifier(customTable)};`); + expect(res.length > 0).toBeTruthy(); + + // test if the migrated table are working as expected + await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); + const result = await db.select().from(usersMigratorTable); + expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table ${sql.identifier(customTable)}`); +}); + +skipTests([ + 'delete with limit and order by', + 'update with limit and order by', +]); + +tests(); diff --git a/integration-tests/tests/sqlite/libsql-sqlite3.test.ts b/integration-tests/tests/sqlite/libsql-sqlite3.test.ts new file mode 100644 index 000000000..8d8419f17 --- /dev/null +++ b/integration-tests/tests/sqlite/libsql-sqlite3.test.ts @@ -0,0 +1,97 @@ +import { type Client, createClient } from '@libsql/client/sqlite3'; +import retry from 'async-retry'; +import { sql } from 'drizzle-orm'; +import type { LibSQLDatabase } from 'drizzle-orm/libsql'; +import { migrate } from 'drizzle-orm/libsql/migrator'; +import { drizzle } from 'drizzle-orm/libsql/sqlite3'; +import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; +import { skipTests } from '~/common'; +import { randomString } from '~/utils'; +import { anotherUsersMigratorTable, tests, usersMigratorTable } from './sqlite-common'; + +const ENABLE_LOGGING = false; + +let db: LibSQLDatabase; +let client: Client; + +beforeAll(async () => { + const url = ':memory:'; + client = await retry(async () => { + client = createClient({ url }); + return client; + }, { + retries: 20, + factor: 1, + minTimeout: 250, + maxTimeout: 250, + randomize: false, + onRetry() { + client?.close(); + }, + }); + db = drizzle(client, { logger: ENABLE_LOGGING }); +}); + +afterAll(async () => { + client?.close(); +}); + +beforeEach((ctx) => { + ctx.sqlite = { + db, + }; +}); + +test('migrator', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }).run(); + const result = await db.select().from(usersMigratorTable).all(); + + await db.insert(anotherUsersMigratorTable).values({ name: 'John', email: 'email' }).run(); + const result2 = await db.select().from(anotherUsersMigratorTable).all(); + + expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); + expect(result2).toEqual([{ id: 1, name: 'John', email: 'email' }]); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + +test('migrator : migrate with custom table', async () => { + const customTable = randomString(); + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists ${sql.identifier(customTable)}`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite', migrationsTable: customTable }); + + // test if the custom migrations table was created + const res = await db.all(sql`select * from ${sql.identifier(customTable)};`); + expect(res.length > 0).toBeTruthy(); + + // test if the migrated table are working as expected + await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); + const result = await db.select().from(usersMigratorTable); + expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table ${sql.identifier(customTable)}`); +}); + +skipTests([ + 'delete with limit and order by', + 'update with limit and order by', + 'transaction', + 'transaction rollback', + 'nested transaction', + 'nested transaction rollback', +]); + +tests(); diff --git a/integration-tests/tests/sqlite/libsql-ws.test.ts b/integration-tests/tests/sqlite/libsql-ws.test.ts new file mode 100644 index 000000000..86810a36f --- /dev/null +++ b/integration-tests/tests/sqlite/libsql-ws.test.ts @@ -0,0 +1,188 @@ +import { type Client, createClient } from '@libsql/client/ws'; +import retry from 'async-retry'; +import { asc, eq, getTableColumns, sql } from 'drizzle-orm'; +import type { LibSQLDatabase } from 'drizzle-orm/libsql'; +import { migrate } from 'drizzle-orm/libsql/migrator'; +import { drizzle } from 'drizzle-orm/libsql/ws'; +import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; +import { skipTests } from '~/common'; +import { randomString } from '~/utils'; +import { anotherUsersMigratorTable, tests, usersMigratorTable, usersOnUpdate } from './sqlite-common'; + +const ENABLE_LOGGING = false; + +let db: LibSQLDatabase; +let client: Client; + +beforeAll(async () => { + const url = process.env['LIBSQL_REMOTE_URL']; + const authToken = process.env['LIBSQL_REMOTE_TOKEN']; + if (!url) { + throw new Error('LIBSQL_REMOTE_URL is not set'); + } + client = await retry(async () => { + client = createClient({ url, authToken }); + return client; + }, { + retries: 20, + factor: 1, + minTimeout: 250, + maxTimeout: 250, + randomize: false, + onRetry() { + client?.close(); + }, + }); + db = drizzle(client, { logger: ENABLE_LOGGING }); +}); + +afterAll(async () => { + client?.close(); +}); + +beforeEach((ctx) => { + ctx.sqlite = { + db, + }; +}); + +test('migrator', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }).run(); + const result = await db.select().from(usersMigratorTable).all(); + + await db.insert(anotherUsersMigratorTable).values({ name: 'John', email: 'email' }).run(); + const result2 = await db.select().from(anotherUsersMigratorTable).all(); + + expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); + expect(result2).toEqual([{ id: 1, name: 'John', email: 'email' }]); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + +test('migrator : migrate with custom table', async () => { + const customTable = randomString(); + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists ${sql.identifier(customTable)}`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite', migrationsTable: customTable }); + + // test if the custom migrations table was created + const res = await db.all(sql`select * from ${sql.identifier(customTable)};`); + expect(res.length > 0).toBeTruthy(); + + // test if the migrated table are working as expected + await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); + const result = await db.select().from(usersMigratorTable); + expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table ${sql.identifier(customTable)}`); +}); + +test('test $onUpdateFn and $onUpdate works as $default', async (ctx) => { + const { db } = ctx.sqlite; + + await db.run(sql`drop table if exists ${usersOnUpdate}`); + + await db.run( + sql` + create table ${usersOnUpdate} ( + id integer primary key autoincrement, + name text not null, + update_counter integer default 1 not null, + updated_at integer, + always_null text + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + expect(response).toEqual([ + { name: 'John', id: 1, updateCounter: 1, alwaysNull: null }, + { name: 'Jane', id: 2, updateCounter: 1, alwaysNull: null }, + { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, + { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, + ]); + const msDelay = 1250; + + for (const eachUser of justDates) { + expect(eachUser.updatedAt!.valueOf()).toBeGreaterThan(Date.now() - msDelay); + } +}); + +test('test $onUpdateFn and $onUpdate works updating', async (ctx) => { + const { db } = ctx.sqlite; + + await db.run(sql`drop table if exists ${usersOnUpdate}`); + + await db.run( + sql` + create table ${usersOnUpdate} ( + id integer primary key autoincrement, + name text not null, + update_counter integer default 1, + updated_at integer, + always_null text + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John', alwaysNull: 'this will be null after updating' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + + await db.update(usersOnUpdate).set({ name: 'Angel' }).where(eq(usersOnUpdate.id, 1)); + await db.update(usersOnUpdate).set({ updateCounter: null }).where(eq(usersOnUpdate.id, 2)); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + expect(response).toEqual([ + { name: 'Angel', id: 1, updateCounter: 2, alwaysNull: null }, + { name: 'Jane', id: 2, updateCounter: null, alwaysNull: null }, + { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, + { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, + ]); + const msDelay = 1250; + + for (const eachUser of justDates) { + expect(eachUser.updatedAt!.valueOf()).toBeGreaterThan(Date.now() - msDelay); + } +}); + +skipTests([ + 'delete with limit and order by', + 'update with limit and order by', + 'join view as subquery', + 'test $onUpdateFn and $onUpdate works as $default', + 'test $onUpdateFn and $onUpdate works updating', + 'prepared statement reuse', +]); + +tests(); diff --git a/integration-tests/tests/sqlite/sqlite-common.ts b/integration-tests/tests/sqlite/sqlite-common.ts index f31bdbbd2..83beff74d 100644 --- a/integration-tests/tests/sqlite/sqlite-common.ts +++ b/integration-tests/tests/sqlite/sqlite-common.ts @@ -63,7 +63,7 @@ export const usersTable = sqliteTable('users', { createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`strftime('%s', 'now')`), }); -const usersOnUpdate = sqliteTable('users_on_update', { +export const usersOnUpdate = sqliteTable('users_on_update', { id: integer('id').primaryKey({ autoIncrement: true }), name: text('name').notNull(), updateCounter: integer('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`), @@ -1388,7 +1388,7 @@ export function tests() { cityId: integer('city_id').notNull(), }).existing(); - await db.run(sql`create view new_yorkers as ${getViewConfig(newYorkers1).query}`); + await db.run(sql`create view if not exists new_yorkers as ${getViewConfig(newYorkers1).query}`); await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]).run(); @@ -1782,7 +1782,7 @@ export function tests() { await db.run( sql`create table ${users} (id integer not null primary key, name text not null, city_id integer not null)`, ); - await db.run(sql`create view ${newYorkers} as ${getViewConfig(newYorkers).query}`); + await db.run(sql`create view if not exists ${newYorkers} as ${getViewConfig(newYorkers).query}`); db.insert(users).values([ { name: 'John', cityId: 1 }, diff --git a/integration-tests/tests/utils/is-config.test.ts b/integration-tests/tests/utils/is-config.test.ts new file mode 100644 index 000000000..e3d8d95e8 --- /dev/null +++ b/integration-tests/tests/utils/is-config.test.ts @@ -0,0 +1,300 @@ +import 'dotenv/config'; +import { PGlite as pglite } from '@electric-sql/pglite'; +import { createClient as libsql } from '@libsql/client'; +import { Client as neonClient, neon, neonConfig, Pool as neonPool } from '@neondatabase/serverless'; +import { connect as planetscale } from '@planetscale/database'; +import { connect as tidb } from '@tidbcloud/serverless'; +import { createClient as vcClient, sql as vcSql } from '@vercel/postgres'; +import betterSqlite3 from 'better-sqlite3'; +import { type DrizzleConfig, isConfig } from 'drizzle-orm'; +import { createConnection as ms2Connection, createPool as ms2Pool } from 'mysql2'; +import { createConnection as ms2pConnection, createPool as ms2pPool } from 'mysql2/promise'; +import pg from 'pg'; +import postgres from 'postgres'; +import { describe, expect } from 'vitest'; +import ws from 'ws'; + +neonConfig.webSocketConstructor = ws; + +if ( + !process.env['PG_CONNECTION_STRING'] || !process.env['MYSQL_CONNECTION_STRING'] + || !process.env['PLANETSCALE_CONNECTION_STRING'] || !process.env['TIDB_CONNECTION_STRING'] + || !process.env['NEON_CONNECTION_STRING'] + // todo get back after we will have a pool for vercel + // || !process.env['VERCEL_CONNECTION_STRING'] +) { + throw new Error('process.env is missing some connection strings!'); +} + +// process.env['POSTGRES_URL'] = process.env['VERCEL_CONNECTION_STRING']; + +describe('Objects', (it) => { + it('Passes configs', () => { + expect(isConfig({} as DrizzleConfig)).toEqual(true); + + expect( + isConfig({ + casing: 'camelCase', + } as DrizzleConfig), + ).toEqual(true); + + expect( + isConfig({ + logger: true, + } as DrizzleConfig), + ).toEqual(true); + + expect( + isConfig({ + logger: { + logQuery: () => {}, + }, + } as DrizzleConfig), + ).toEqual(true); + + expect( + isConfig({ + schema: { + any: true, + }, + } as DrizzleConfig), + ).toEqual(true); + + expect( + isConfig({ + casing: 'camelCase', + logger: true, + schema: { + any: true, + }, + } as DrizzleConfig), + ).toEqual(true); + + expect( + isConfig({ + casing: 'camelCase', + trash: true, + } as DrizzleConfig), + ).toEqual(true); + }); + + it('Rejects non-configs', () => { + expect(isConfig('')).toEqual(false); + + expect(isConfig('data')).toEqual(false); + + expect(isConfig(true)).toEqual(false); + + expect(isConfig(false)).toEqual(false); + + expect(isConfig(null)).toEqual(false); + + expect(isConfig(undefined)).toEqual(false); + + expect(isConfig(5)).toEqual(false); + + expect(isConfig(BigInt(5))).toEqual(false); + + expect(isConfig(new Date())).toEqual(false); + + expect( + isConfig({ + trash: true, + } as DrizzleConfig), + ).toEqual(false); + }); +}); + +describe('Rejects drivers', (it) => { + it('libsql', () => { + const cl = libsql({ + url: ':memory:', + }); + + expect(isConfig(cl)).toEqual(false); + }); + + it('better-sqlite3', () => { + const cl = new betterSqlite3(':memory:'); + + expect(isConfig(cl)).toEqual(false); + }); + + it('pglite', () => { + const cl = new pglite('memory://'); + + expect(isConfig(cl)).toEqual(false); + }); + + it('node-postgres:Pool', () => { + const cl = new pg.Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + + expect(isConfig(cl)).toEqual(false); + }); + + it('node-postgres:Client', async () => { + const cl = new pg.Client({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + + const res = isConfig(cl); + + await cl.end(); + + expect(res).toEqual(false); + }); + + it('node-postgres:PoolClient', async () => { + const cl = new pg.Pool({ + connectionString: process.env['PG_CONNECTION_STRING'], + }); + + const con = await cl.connect(); + + const res = isConfig(con); + + con.release(); + + expect(res).toEqual(false); + }); + + it('postgres-js', () => { + const cl = postgres(process.env['PG_CONNECTION_STRING']!); + + expect(isConfig(cl)).toEqual(false); + }); + + it('vercel:sql', () => { + expect(isConfig(vcSql)).toEqual(false); + }); + + // it('vercel:Pool', () => { + // const cl = vcPool({ + // connectionString: process.env['VERCEL_CONNECTION_STRING'], + // }); + + // expect(isConfig(cl)).toEqual(false); + // }); + + it('vercel:Client', async () => { + const cl = vcClient({ + connectionString: process.env['NEON_CONNECTION_STRING'], + }); + + const res = isConfig(cl); + + expect(res).toEqual(false); + }); + + // it('vercel:PoolClient', async () => { + // const cl = vcPool({ + // connectionString: process.env['VERCEL_CONNECTION_STRING'], + // }); + + // const con = await cl.connect(); + + // const res = isConfig(con); + + // con.release(); + + // expect(res).toEqual(false); + // }); + + it('neon-serverless:Pool', async () => { + const cl = new neonPool({ + connectionString: process.env['NEON_CONNECTION_STRING']!, + }); + + expect(isConfig(cl)).toEqual(false); + }); + + it('neon-serverless:Client', async () => { + const cl = new neonClient({ + connectionString: process.env['NEON_CONNECTION_STRING']!, + }); + + const res = isConfig(cl); + + await cl.end(); + + expect(res).toEqual(false); + }); + + it('neon-serverless:PoolClient', async () => { + const cl = new neonPool({ + connectionString: process.env['NEON_CONNECTION_STRING']!, + }); + + const con = await cl.connect(); + + const res = isConfig(con); + + con.release(); + + expect(res).toEqual(false); + }); + + it('neon-http', async () => { + const cl = neon(process.env['NEON_CONNECTION_STRING']!); + + expect(isConfig(cl)).toEqual(false); + }); + + it('planetscale', async () => { + const cl = planetscale({ + url: process.env['PLANETSCALE_CONNECTION_STRING'], + }); + + expect(isConfig(cl)).toEqual(false); + }); + + it('mysql2:Pool', async () => { + const cl = ms2Pool({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + expect(isConfig(cl)).toEqual(false); + }); + + it('mysql2:Connection', async () => { + const cl = ms2Connection({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + expect(isConfig(cl)).toEqual(false); + }); + + it('mysql2/promise:Pool', async () => { + const cl = await ms2pPool({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const res = isConfig(cl); + + await cl.end(); + + expect(res).toEqual(false); + }); + + it('mysql2/promise:Connection', async () => { + const cl = await ms2pConnection({ + uri: process.env['MYSQL_CONNECTION_STRING'], + }); + + const res = isConfig(cl); + + await cl.end(); + + expect(res).toEqual(false); + }); + + it('tidb', async () => { + const cl = tidb({ + url: process.env['TIDB_CONNECTION_STRING'], + }); + + expect(isConfig(cl)).toEqual(false); + }); +}); diff --git a/integration-tests/vitest.config.ts b/integration-tests/vitest.config.ts index 3952eca49..84ea9b1c8 100644 --- a/integration-tests/vitest.config.ts +++ b/integration-tests/vitest.config.ts @@ -15,6 +15,9 @@ export default defineConfig({ 'tests/extensions/vectors/**/*', 'tests/version.test.ts', 'tests/pg/node-postgres.test.ts', + 'tests/utils/is-config.test.ts', + 'js-tests/driver-init/commonjs/*.test.cjs', + 'js-tests/driver-init/module/*.test.mjs', ], exclude: [ ...(process.env.SKIP_EXTERNAL_DB_TESTS @@ -28,6 +31,17 @@ export default defineConfig({ 'tests/sqlite/libsql-batch.test.ts', 'tests/pg/neon-http.test.ts', 'tests/pg/neon-http-batch.test.ts', + 'tests/utils/is-config.test.ts', // Uses external DBs in some cases + 'js-tests/driver-init/commonjs/neon-http.test.cjs', + 'js-tests/driver-init/commonjs/neon-ws.test.cjs', + 'js-tests/driver-init/commonjs/planetscale.test.cjs', + 'js-tests/driver-init/commonjs/tidb.test.cjs', + 'js-tests/driver-init/commonjs/vercel.test.cjs', + 'js-tests/driver-init/module/neon-http.test.mjs', + 'js-tests/driver-init/module/neon-ws.test.mjs', + 'js-tests/driver-init/module/planetscale.test.mjs', + 'js-tests/driver-init/module/tidb.test.mjs', + 'js-tests/driver-init/module/vercel.test.mjs', ] : []), 'tests/pg/awsdatapi.test.ts', @@ -37,6 +51,12 @@ export default defineConfig({ // Have a strange "invalid SQL: ERROR: must be owner of schema public" error. Will need to check with xata team 'tests/pg/xata-http.test.ts', 'tests/pg/neon-http-batch.ts', + // todo: remove + 'js-tests/driver-init/module/vercel.test.mjs', + 'js-tests/driver-init/commonjs/vercel.test.cjs', + // move back after decide on speed + 'tests/sqlite/libsql-ws.test.ts', + 'tests/sqlite/libsql-http.test.ts', ], typecheck: { tsconfig: 'tsconfig.json', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2400e16d..20ff6ca0f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,8 +109,8 @@ importers: specifier: ^4.20230518.0 version: 4.20240524.0 '@electric-sql/pglite': - specifier: ^0.1.5 - version: 0.1.5 + specifier: ^0.2.12 + version: 0.2.12 '@hono/node-server': specifier: ^1.9.0 version: 1.12.0 @@ -298,11 +298,14 @@ importers: specifier: ^4.20230904.0 version: 4.20240512.0 '@electric-sql/pglite': - specifier: ^0.1.1 - version: 0.1.5 + specifier: ^0.2.12 + version: 0.2.12 '@libsql/client': specifier: ^0.10.0 version: 0.10.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + '@libsql/client-wasm': + specifier: ^0.10.0 + version: 0.10.0 '@miniflare/d1': specifier: ^2.14.4 version: 2.14.4 @@ -311,7 +314,7 @@ importers: version: 0.9.0 '@op-engineering/op-sqlite': specifier: ^2.0.16 - version: 2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + version: 2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1) '@opentelemetry/api': specifier: ^1.4.1 version: 1.8.0 @@ -359,7 +362,7 @@ importers: version: 10.1.0 expo-sqlite: specifier: ^13.2.0 - version: 13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + version: 13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) knex: specifier: ^2.4.2 version: 2.5.1(better-sqlite3@8.7.0)(mysql2@3.3.3)(pg@8.11.5)(sqlite3@5.1.7) @@ -548,10 +551,10 @@ importers: version: 3.583.0 '@aws-sdk/credential-providers': specifier: ^3.549.0 - version: 3.569.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)) + version: 3.569.0(@aws-sdk/client-sso-oidc@3.583.0) '@electric-sql/pglite': - specifier: ^0.1.1 - version: 0.1.5 + specifier: ^0.2.12 + version: 0.2.12 '@miniflare/d1': specifier: ^2.14.4 version: 2.14.4 @@ -1993,8 +1996,8 @@ packages: '@drizzle-team/studio@0.0.5': resolution: {integrity: sha512-ps5qF0tMxWRVu+V5gvCRrQNqlY92aTnIKdq27gm9LZMSdaKYZt6AVvSK1dlUMzs6Rt0Jm80b+eWct6xShBKhIw==} - '@electric-sql/pglite@0.1.5': - resolution: {integrity: sha512-eymv4ONNvoPZQTvOQIi5dbpR+J5HzEv0qQH9o/y3gvNheJV/P/NFcrbsfJZYTsDKoq7DKrTiFNexsRkJKy8x9Q==} + '@electric-sql/pglite@0.2.12': + resolution: {integrity: sha512-J/X42ujcoFEbOkgRyoNqZB5qcqrnJRWVlwpH3fKYoJkTz49N91uAK/rDSSG/85WRas9nC9mdV4FnMTxnQWE/rw==} '@esbuild-kit/core-utils@3.1.0': resolution: {integrity: sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==} @@ -3094,6 +3097,11 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@libsql/client-wasm@0.10.0': + resolution: {integrity: sha512-xSlpGdBGEr4mRtjCnDejTqtDpct2ng8cqHUQs+S4xG1yv0h+hLdzOtQJSY9JV9T/2MWWDfdCiEntPs2SdErSJA==} + bundledDependencies: + - '@libsql/libsql-wasm-experimental' + '@libsql/client@0.10.0': resolution: {integrity: sha512-2ERn08T4XOVx34yBtUPq0RDjAdd9TJ5qNH/izugr208ml2F94mk92qC64kXyDVQINodWJvp3kAdq6P4zTtCZ7g==} @@ -10911,12 +10919,12 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-ini@3.568.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0))': + '@aws-sdk/credential-provider-ini@3.568.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0))': dependencies: '@aws-sdk/client-sts': 3.569.0(@aws-sdk/client-sso-oidc@3.569.0) '@aws-sdk/credential-provider-env': 3.568.0 '@aws-sdk/credential-provider-process': 3.568.0 - '@aws-sdk/credential-provider-sso': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)) + '@aws-sdk/credential-provider-sso': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/credential-provider-web-identity': 3.568.0(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0)) '@aws-sdk/types': 3.567.0 '@smithy/credential-provider-imds': 2.3.0 @@ -10999,13 +11007,13 @@ snapshots: - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-node@3.569.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0))': + '@aws-sdk/credential-provider-node@3.569.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0))': dependencies: '@aws-sdk/credential-provider-env': 3.568.0 '@aws-sdk/credential-provider-http': 3.568.0 - '@aws-sdk/credential-provider-ini': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0)) + '@aws-sdk/credential-provider-ini': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0)) '@aws-sdk/credential-provider-process': 3.568.0 - '@aws-sdk/credential-provider-sso': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)) + '@aws-sdk/credential-provider-sso': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/credential-provider-web-identity': 3.568.0(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0)) '@aws-sdk/types': 3.567.0 '@smithy/credential-provider-imds': 2.3.0 @@ -11086,10 +11094,10 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-sso@3.568.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))': + '@aws-sdk/credential-provider-sso@3.568.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: '@aws-sdk/client-sso': 3.568.0 - '@aws-sdk/token-providers': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)) + '@aws-sdk/token-providers': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/types': 3.567.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 @@ -11143,7 +11151,7 @@ snapshots: '@smithy/types': 3.0.0 tslib: 2.6.2 - '@aws-sdk/credential-providers@3.569.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))': + '@aws-sdk/credential-providers@3.569.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: '@aws-sdk/client-cognito-identity': 3.569.0 '@aws-sdk/client-sso': 3.568.0 @@ -11151,10 +11159,10 @@ snapshots: '@aws-sdk/credential-provider-cognito-identity': 3.569.0 '@aws-sdk/credential-provider-env': 3.568.0 '@aws-sdk/credential-provider-http': 3.568.0 - '@aws-sdk/credential-provider-ini': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0)) - '@aws-sdk/credential-provider-node': 3.569.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0)) + '@aws-sdk/credential-provider-ini': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0)) + '@aws-sdk/credential-provider-node': 3.569.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0)) '@aws-sdk/credential-provider-process': 3.568.0 - '@aws-sdk/credential-provider-sso': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)) + '@aws-sdk/credential-provider-sso': 3.568.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/credential-provider-web-identity': 3.568.0(@aws-sdk/client-sts@3.569.0(@aws-sdk/client-sso-oidc@3.569.0)) '@aws-sdk/types': 3.567.0 '@smithy/credential-provider-imds': 2.3.0 @@ -11336,7 +11344,7 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.6.2 - '@aws-sdk/token-providers@3.568.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))': + '@aws-sdk/token-providers@3.568.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/types': 3.567.0 @@ -12493,7 +12501,7 @@ snapshots: '@drizzle-team/studio@0.0.5': {} - '@electric-sql/pglite@0.1.5': {} + '@electric-sql/pglite@0.2.12': {} '@esbuild-kit/core-utils@3.1.0': dependencies: @@ -13021,7 +13029,7 @@ snapshots: mv: 2.1.1 safe-json-stringify: 1.2.0 - '@expo/cli@0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)': + '@expo/cli@0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)(utf-8-validate@6.0.3)': dependencies: '@babel/runtime': 7.24.6 '@expo/code-signing-certificates': 0.0.5 @@ -13039,7 +13047,7 @@ snapshots: '@expo/rudder-sdk-node': 1.1.1(encoding@0.1.13) '@expo/spawn-async': 1.7.2 '@expo/xcpretty': 4.3.1 - '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@urql/core': 2.3.6(graphql@15.8.0) '@urql/exchange-retry': 0.3.0(graphql@15.8.0) accepts: 1.3.8 @@ -13461,6 +13469,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@libsql/client-wasm@0.10.0': + dependencies: + '@libsql/core': 0.10.0 + js-base64: 3.7.7 + '@libsql/client@0.10.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)': dependencies: '@libsql/core': 0.10.0 @@ -13615,10 +13628,10 @@ snapshots: rimraf: 3.0.2 optional: true - '@op-engineering/op-sqlite@2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': + '@op-engineering/op-sqlite@2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1)': dependencies: react: 18.3.1 - react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1) + react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3) '@opentelemetry/api@1.8.0': {} @@ -13755,7 +13768,7 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-server-api@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native-community/cli-server-api@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@react-native-community/cli-debugger-ui': 13.6.6 '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) @@ -13765,7 +13778,7 @@ snapshots: nocache: 3.0.4 pretty-format: 26.6.2 serve-static: 1.15.0 - ws: 6.2.2(bufferutil@4.0.8) + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding @@ -13792,14 +13805,14 @@ snapshots: dependencies: joi: 17.13.1 - '@react-native-community/cli@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native-community/cli@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@react-native-community/cli-clean': 13.6.6(encoding@0.1.13) '@react-native-community/cli-config': 13.6.6(encoding@0.1.13) '@react-native-community/cli-debugger-ui': 13.6.6 '@react-native-community/cli-doctor': 13.6.6(encoding@0.1.13) '@react-native-community/cli-hermes': 13.6.6(encoding@0.1.13) - '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) '@react-native-community/cli-types': 13.6.6 chalk: 4.1.2 @@ -13888,16 +13901,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native/community-cli-plugin@0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: - '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) - '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native/metro-babel-transformer': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6)) chalk: 4.1.2 execa: 5.1.1 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) - metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-core: 0.80.9 node-fetch: 2.7.0(encoding@0.1.13) querystring: 0.2.1 @@ -13912,7 +13925,7 @@ snapshots: '@react-native/debugger-frontend@0.74.83': {} - '@react-native/dev-middleware@0.74.83(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native/dev-middleware@0.74.83(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@isaacs/ttlcache': 1.4.1 '@react-native/debugger-frontend': 0.74.83 @@ -13926,7 +13939,7 @@ snapshots: selfsigned: 2.4.1 serve-static: 1.15.0 temp-dir: 2.0.0 - ws: 6.2.2(bufferutil@4.0.8) + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding @@ -13949,12 +13962,12 @@ snapshots: '@react-native/normalize-colors@0.74.83': {} - '@react-native/virtualized-lists@0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': + '@react-native/virtualized-lists@0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 18.3.1 - react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1) + react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3) optionalDependencies: '@types/react': 18.3.1 @@ -15264,7 +15277,7 @@ snapshots: pathe: 1.1.2 picocolors: 1.0.1 sirv: 2.0.4 - vitest: 1.6.0(@types/node@18.19.33)(@vitest/ui@1.6.0)(lightningcss@1.25.1)(terser@5.31.0) + vitest: 1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(lightningcss@1.25.1)(terser@5.31.0) optional: true '@vitest/ui@1.6.0(vitest@2.1.2)': @@ -17325,35 +17338,35 @@ snapshots: expand-template@2.0.3: {} - expo-asset@10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-asset@10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@react-native/assets-registry': 0.74.83 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) - expo-constants: 16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo-constants: 16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) invariant: 2.2.4 md5-file: 3.2.3 transitivePeerDependencies: - supports-color - expo-constants@16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-constants@16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@expo/config': 9.0.2 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) transitivePeerDependencies: - supports-color - expo-file-system@17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-file-system@17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - expo-font@12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-font@12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) fontfaceobserver: 2.3.0 - expo-keep-awake@13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-keep-awake@13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) expo-modules-autolinking@1.11.1: dependencies: @@ -17367,24 +17380,24 @@ snapshots: dependencies: invariant: 2.2.4 - expo-sqlite@13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-sqlite@13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@expo/websql': 1.0.1 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13): + expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/runtime': 7.24.6 - '@expo/cli': 0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1) + '@expo/cli': 0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)(utf-8-validate@6.0.3) '@expo/config': 9.0.2 '@expo/config-plugins': 8.0.4 '@expo/metro-config': 0.18.4 '@expo/vector-icons': 14.0.2 babel-preset-expo: 11.0.6(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6)) - expo-asset: 10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) - expo-file-system: 17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) - expo-font: 12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) - expo-keep-awake: 13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + expo-asset: 10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-file-system: 17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-font: 12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-keep-awake: 13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) expo-modules-autolinking: 1.11.1 expo-modules-core: 1.12.11 fbemitter: 3.0.0(encoding@0.1.13) @@ -18866,12 +18879,12 @@ snapshots: metro-core: 0.80.9 rimraf: 3.0.2 - metro-config@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): + metro-config@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: connect: 3.7.0 cosmiconfig: 5.2.1 jest-validate: 29.7.0 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-cache: 0.80.9 metro-core: 0.80.9 metro-runtime: 0.80.9 @@ -18947,13 +18960,13 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): + metro-transform-worker@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/core': 7.24.6 '@babel/generator': 7.24.6 '@babel/parser': 7.24.6 '@babel/types': 7.24.6 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-babel-transformer: 0.80.9 metro-cache: 0.80.9 metro-cache-key: 0.80.9 @@ -18967,7 +18980,7 @@ snapshots: - supports-color - utf-8-validate - metro@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): + metro@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/code-frame': 7.24.6 '@babel/core': 7.24.6 @@ -18993,7 +19006,7 @@ snapshots: metro-babel-transformer: 0.80.9 metro-cache: 0.80.9 metro-cache-key: 0.80.9 - metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-core: 0.80.9 metro-file-map: 0.80.9 metro-resolver: 0.80.9 @@ -19001,7 +19014,7 @@ snapshots: metro-source-map: 0.80.9 metro-symbolicate: 0.80.9 metro-transform-plugins: 0.80.9 - metro-transform-worker: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro-transform-worker: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) mime-types: 2.1.35 node-fetch: 2.7.0(encoding@0.1.13) nullthrows: 1.1.1 @@ -19010,7 +19023,7 @@ snapshots: source-map: 0.5.7 strip-ansi: 6.0.1 throat: 5.0.0 - ws: 7.5.9(bufferutil@4.0.8) + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -19898,10 +19911,10 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-devtools-core@5.2.0(bufferutil@4.0.8): + react-devtools-core@5.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): dependencies: shell-quote: 1.8.1 - ws: 7.5.9(bufferutil@4.0.8) + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -19914,19 +19927,19 @@ snapshots: react-is@18.3.1: {} - react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1): + react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native-community/cli': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native-community/cli': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native-community/cli-platform-android': 13.6.6(encoding@0.1.13) '@react-native-community/cli-platform-ios': 13.6.6(encoding@0.1.13) '@react-native/assets-registry': 0.74.83 '@react-native/codegen': 0.74.83(@babel/preset-env@7.24.6(@babel/core@7.24.6)) - '@react-native/community-cli-plugin': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native/community-cli-plugin': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native/gradle-plugin': 0.74.83 '@react-native/js-polyfills': 0.74.83 '@react-native/normalize-colors': 0.74.83 - '@react-native/virtualized-lists': 0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + '@react-native/virtualized-lists': 0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -19945,14 +19958,14 @@ snapshots: pretty-format: 26.6.2 promise: 8.3.0 react: 18.3.1 - react-devtools-core: 5.2.0(bufferutil@4.0.8) + react-devtools-core: 5.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) react-refresh: 0.14.2 react-shallow-renderer: 16.15.0(react@18.3.1) regenerator-runtime: 0.13.11 scheduler: 0.24.0-canary-efb381bbf-20230505 stacktrace-parser: 0.1.10 whatwg-fetch: 3.6.20 - ws: 6.2.2(bufferutil@4.0.8) + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) yargs: 17.7.2 optionalDependencies: '@types/react': 18.3.1 @@ -21872,15 +21885,17 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.0.2 - ws@6.2.2(bufferutil@4.0.8): + ws@6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3): dependencies: async-limiter: 1.0.1 optionalDependencies: bufferutil: 4.0.8 + utf-8-validate: 6.0.3 - ws@7.5.9(bufferutil@4.0.8): + ws@7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3): optionalDependencies: bufferutil: 4.0.8 + utf-8-validate: 6.0.3 ws@8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3): optionalDependencies: