Skip to content

Commit

Permalink
Split libsql (drizzle-team#3170)
Browse files Browse the repository at this point in the history
* Separated different libsql versions into different import paths, adapted tests, added `@libsql/client-wasm` peer and dev dep

* Switched default libsql driver initializer to new initializer type, adapted tests

* Fixed tests

* Excluded ESM-only libsql/wasm package from CJS import tests

* Tests for libsql submodules

* Fixed imports in tests

* Added remote libsql envs

* Modified workflows to include required env params

* Modified tests for `libsql/http`,`libsql/ws`

* Crash prevention on tests after failed view tests in sqlite, increased msDelay for `libsql/ws`/`libsql/http` tests

* Add 0.35.3 version release notes

* Fixed planetscale driver rejecting `Connection` on type level

* No Connection in planetscale, only client

* Remove connection tests

---------

Co-authored-by: AndriiSherman <[email protected]>
  • Loading branch information
Sukairo-02 and AndriiSherman authored Oct 21, 2024
1 parent 6c4c3c5 commit 0fcf975
Show file tree
Hide file tree
Showing 23 changed files with 1,189 additions and 248 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/release-feature-branch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release-latest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
80 changes: 80 additions & 0 deletions changelogs/drizzle-orm/0.35.3.md
Original file line number Diff line number Diff line change
@@ -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
}});
```
4 changes: 3 additions & 1 deletion drizzle-orm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "drizzle-orm",
"version": "0.35.2",
"version": "0.35.3",
"description": "Drizzle ORM package for SQL databases",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -162,6 +163,7 @@
"@cloudflare/workers-types": "^4.20230904.0",
"@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",
Expand Down
64 changes: 64 additions & 0 deletions drizzle-orm/src/libsql/driver-core.ts
Original file line number Diff line number Diff line change
@@ -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<string, unknown> = Record<string, never>,
> extends BaseSQLiteDatabase<'async', ResultSet, TSchema> {
static override readonly [entityKind]: string = 'LibSQLDatabase';

/** @internal */
declare readonly session: LibSQLSession<TSchema, ExtractTablesWithRelations<TSchema>>;

async batch<U extends BatchItem<'sqlite'>, T extends Readonly<[U, ...U[]]>>(
batch: T,
): Promise<BatchResponse<T>> {
return this.session.batch(batch) as Promise<BatchResponse<T>>;
}
}

/** @internal */
export function construct<
TSchema extends Record<string, unknown> = Record<string, never>,
>(client: Client, config: DrizzleConfig<TSchema> = {}): LibSQLDatabase<TSchema> & {
$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<TablesRelationalConfig> | 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<TSchema>;
(<any> db).$client = client;

return db as any;
}
64 changes: 3 additions & 61 deletions drizzle-orm/src/libsql/driver.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,8 @@
import { type Client, type Config, createClient, type 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 Client, type Config, createClient } from '@libsql/client';
import { type DrizzleConfig, type IfNotImported, type ImportTypeError, isConfig } from '~/utils.ts';
import { LibSQLSession } from './session.ts';
import { construct as construct, type LibSQLDatabase } from './driver-core.ts';

export class LibSQLDatabase<
TSchema extends Record<string, unknown> = Record<string, never>,
> extends BaseSQLiteDatabase<'async', ResultSet, TSchema> {
static override readonly [entityKind]: string = 'LibSQLDatabase';

/** @internal */
declare readonly session: LibSQLSession<TSchema, ExtractTablesWithRelations<TSchema>>;

async batch<U extends BatchItem<'sqlite'>, T extends Readonly<[U, ...U[]]>>(
batch: T,
): Promise<BatchResponse<T>> {
return this.session.batch(batch) as Promise<BatchResponse<T>>;
}
}

function construct<
TSchema extends Record<string, unknown> = Record<string, never>,
>(client: Client, config: DrizzleConfig<TSchema> = {}): LibSQLDatabase<TSchema> & {
$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<TablesRelationalConfig> | 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<TSchema>;
(<any> db).$client = client;

return db as any;
}
export { LibSQLDatabase } from './driver-core.ts';

export function drizzle<
TSchema extends Record<string, unknown> = Record<string, never>,
Expand Down
62 changes: 62 additions & 0 deletions drizzle-orm/src/libsql/http/index.ts
Original file line number Diff line number Diff line change
@@ -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<string, unknown> = Record<string, never>,
TClient extends Client = Client,
>(
...params: IfNotImported<
Client,
[ImportTypeError<'@libsql/client'>],
[
TClient | string,
] | [
TClient | string,
DrizzleConfig<TSchema>,
] | [
(
& DrizzleConfig<TSchema>
& ({
connection: string | Config;
} | {
client: TClient;
})
),
]
>
): LibSQLDatabase<TSchema> & {
$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<TSchema>;

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<TSchema> | undefined) as any;
}

export namespace drizzle {
export function mock<TSchema extends Record<string, unknown> = Record<string, never>>(
config?: DrizzleConfig<TSchema>,
): LibSQLDatabase<TSchema> & {
$client: '$client is not available on drizzle.mock()';
} {
return construct({} as any, config) as any;
}
}
62 changes: 62 additions & 0 deletions drizzle-orm/src/libsql/node/index.ts
Original file line number Diff line number Diff line change
@@ -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<string, unknown> = Record<string, never>,
TClient extends Client = Client,
>(
...params: IfNotImported<
Client,
[ImportTypeError<'@libsql/client'>],
[
TClient | string,
] | [
TClient | string,
DrizzleConfig<TSchema>,
] | [
(
& DrizzleConfig<TSchema>
& ({
connection: string | Config;
} | {
client: TClient;
})
),
]
>
): LibSQLDatabase<TSchema> & {
$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<TSchema>;

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<TSchema> | undefined) as any;
}

export namespace drizzle {
export function mock<TSchema extends Record<string, unknown> = Record<string, never>>(
config?: DrizzleConfig<TSchema>,
): LibSQLDatabase<TSchema> & {
$client: '$client is not available on drizzle.mock()';
} {
return construct({} as any, config) as any;
}
}
Loading

0 comments on commit 0fcf975

Please sign in to comment.