@leonardovida-md/drizzle-neo-duckdb 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![npm version](https://img.shields.io/npm/v/@leonardovida-md/drizzle-neo-duckdb)](https://www.npmjs.com/package/@leonardovida-md/drizzle-neo-duckdb)
8
8
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
9
9
 
10
- [Documentation](./docs) • [Examples](./example) • [Contributing](#contributing)
10
+ [Documentation](https://leonardovida.github.io/drizzle-neo-duckdb/) • [LLM Context](https://leonardovida.github.io/drizzle-neo-duckdb/llms.txt) • [Examples](./example) • [Contributing](#contributing)
11
11
 
12
12
  </div>
13
13
 
@@ -19,6 +19,8 @@ Works with local DuckDB files, in-memory databases, and [MotherDuck](https://mot
19
19
 
20
20
  > **Status:** Experimental. Core query building, migrations, and type inference work well. Some DuckDB-specific types and edge cases are still being refined.
21
21
 
22
+ Docs tip: every docs page has a **Markdown (raw)** button for LLM-friendly source.
23
+
22
24
  ## Installation
23
25
 
24
26
  ```bash
@@ -119,7 +121,14 @@ Drizzle DuckDB uses `drizzle-orm/pg-core` for schema definitions since DuckDB's
119
121
 
120
122
  ```typescript
121
123
  import { sql } from 'drizzle-orm';
122
- import { integer, text, boolean, timestamp, pgTable, pgSchema } from 'drizzle-orm/pg-core';
124
+ import {
125
+ integer,
126
+ text,
127
+ boolean,
128
+ timestamp,
129
+ pgTable,
130
+ pgSchema,
131
+ } from 'drizzle-orm/pg-core';
123
132
 
124
133
  // Tables in default schema
125
134
  const posts = pgTable('posts', {
@@ -181,7 +190,20 @@ const products = pgTable('products', {
181
190
  });
182
191
  ```
183
192
 
184
- See [Column Types Documentation](./docs/columns.md) for complete reference.
193
+ See [Column Types Documentation](https://leonardovida.github.io/drizzle-neo-duckdb/api/columns) for complete reference.
194
+
195
+ ### Client-Safe Imports (schemas, drizzle-zod, trpc)
196
+
197
+ When generated schemas are consumed in client bundles, import the helpers from the browser-safe subpath to avoid pulling the native DuckDB bindings:
198
+
199
+ ```typescript
200
+ import {
201
+ duckDbJson,
202
+ duckDbList,
203
+ } from '@leonardovida-md/drizzle-neo-duckdb/helpers';
204
+ ```
205
+
206
+ The introspection CLI now emits this import path by default. You can still override `importBasePath` if you need the old root import.
185
207
 
186
208
  ## Querying
187
209
 
@@ -189,10 +211,15 @@ All standard Drizzle query methods work:
189
211
 
190
212
  ```typescript
191
213
  // Select
192
- const users = await db.select().from(usersTable).where(eq(usersTable.active, true));
214
+ const users = await db
215
+ .select()
216
+ .from(usersTable)
217
+ .where(eq(usersTable.active, true));
193
218
 
194
219
  // Insert
195
- await db.insert(usersTable).values({ name: 'Alice', email: 'alice@example.com' });
220
+ await db
221
+ .insert(usersTable)
222
+ .values({ name: 'Alice', email: 'alice@example.com' });
196
223
 
197
224
  // Insert with returning
198
225
  const inserted = await db
@@ -201,7 +228,10 @@ const inserted = await db
201
228
  .returning({ id: usersTable.id });
202
229
 
203
230
  // Update
204
- await db.update(usersTable).set({ name: 'Updated' }).where(eq(usersTable.id, 1));
231
+ await db
232
+ .update(usersTable)
233
+ .set({ name: 'Updated' })
234
+ .where(eq(usersTable.id, 1));
205
235
 
206
236
  // Delete
207
237
  await db.delete(usersTable).where(eq(usersTable.id, 1));
@@ -228,7 +258,9 @@ const results = await db
228
258
  const results = await db
229
259
  .select()
230
260
  .from(products)
231
- .where(duckDbArrayContained(products.tags, ['electronics', 'sale', 'featured']));
261
+ .where(
262
+ duckDbArrayContained(products.tags, ['electronics', 'sale', 'featured'])
263
+ );
232
264
 
233
265
  // Check if arrays overlap
234
266
  const results = await db
@@ -258,7 +290,7 @@ import { migrate } from '@leonardovida-md/drizzle-neo-duckdb';
258
290
  await migrate(db, { migrationsFolder: './drizzle' });
259
291
  ```
260
292
 
261
- Migration metadata is stored in `drizzle.__drizzle_migrations` by default. See [Migrations Documentation](./docs/migrations.md) for configuration options.
293
+ Migration metadata is stored in `drizzle.__drizzle_migrations` by default. See [Migrations Documentation](https://leonardovida.github.io/drizzle-neo-duckdb/guide/migrations) for configuration options.
262
294
 
263
295
  ## Schema Introspection
264
296
 
@@ -283,7 +315,7 @@ const result = await introspect(db, {
283
315
  console.log(result.files.schemaTs);
284
316
  ```
285
317
 
286
- See [Introspection Documentation](./docs/introspection.md) for all options.
318
+ See [Introspection Documentation](https://leonardovida.github.io/drizzle-neo-duckdb/guide/introspection) for all options.
287
319
 
288
320
  ## Configuration Options
289
321
 
@@ -307,22 +339,23 @@ const db = drizzle(connection, {
307
339
 
308
340
  This connector aims for compatibility with Drizzle's Postgres driver but has some differences:
309
341
 
310
- | Feature | Status |
311
- |---------|--------|
312
- | Basic CRUD operations | Full support |
313
- | Joins and subqueries | Full support |
314
- | Transactions | No savepoints (nested transactions reuse outer) |
315
- | JSON/JSONB columns | Use `duckDbJson()` instead |
316
- | Prepared statements | No statement caching |
317
- | Streaming results | Results are materialized |
342
+ | Feature | Status |
343
+ | --------------------- | ---------------------------------------------------------- |
344
+ | Basic CRUD operations | Full support |
345
+ | Joins and subqueries | Full support |
346
+ | Transactions | No savepoints (nested transactions reuse outer) |
347
+ | JSON/JSONB columns | Use `duckDbJson()` instead |
348
+ | Prepared statements | No statement caching |
349
+ | Streaming results | Materialized by default; use `executeBatches()` for chunks |
318
350
 
319
- See [Limitations Documentation](./docs/limitations.md) for details.
351
+ See [Limitations Documentation](https://leonardovida.github.io/drizzle-neo-duckdb/guide/limitations) for details.
320
352
 
321
353
  ## Examples
322
354
 
323
355
  - **[MotherDuck NYC Taxi](./example/motherduck-nyc.ts)** — Query the built-in NYC taxi dataset from MotherDuck cloud
324
356
 
325
357
  Run examples:
358
+
326
359
  ```bash
327
360
  MOTHERDUCK_TOKEN=your_token bun example/motherduck-nyc.ts
328
361
  ```
package/dist/client.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import { type DuckDBConnection } from '@duckdb/node-api';
2
- export type DuckDBClientLike = DuckDBConnection;
2
+ export type DuckDBClientLike = DuckDBConnection | DuckDBConnectionPool;
3
3
  export type RowData = Record<string, unknown>;
4
+ export interface DuckDBConnectionPool {
5
+ acquire(): Promise<DuckDBConnection>;
6
+ release(connection: DuckDBConnection): void | Promise<void>;
7
+ close?(): Promise<void> | void;
8
+ }
9
+ export declare function isPool(client: DuckDBClientLike): client is DuckDBConnectionPool;
4
10
  export interface PrepareParamsOptions {
5
11
  rejectStringArrayLiterals?: boolean;
6
12
  warnOnStringArrayLiteral?: () => void;
@@ -8,3 +14,15 @@ export interface PrepareParamsOptions {
8
14
  export declare function prepareParams(params: unknown[], options?: PrepareParamsOptions): unknown[];
9
15
  export declare function closeClientConnection(connection: DuckDBConnection): Promise<void>;
10
16
  export declare function executeOnClient(client: DuckDBClientLike, query: string, params: unknown[]): Promise<RowData[]>;
17
+ export interface ExecuteInBatchesOptions {
18
+ rowsPerChunk?: number;
19
+ }
20
+ /**
21
+ * Stream results from DuckDB in batches to avoid fully materializing rows in JS.
22
+ */
23
+ export declare function executeInBatches(client: DuckDBClientLike, query: string, params: unknown[], options?: ExecuteInBatchesOptions): AsyncGenerator<RowData[], void, void>;
24
+ /**
25
+ * Return columnar results when the underlying node-api exposes an Arrow/columnar API.
26
+ * Falls back to column-major JS arrays when Arrow is unavailable.
27
+ */
28
+ export declare function executeArrowOnClient(client: DuckDBClientLike, query: string, params: unknown[]): Promise<unknown>;
package/dist/columns.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type SQL } from 'drizzle-orm';
2
2
  import type { SQLWrapper } from 'drizzle-orm/sql/sql';
3
+ import { type ListValueWrapper, type ArrayValueWrapper, type MapValueWrapper, type BlobValueWrapper, type JsonValueWrapper } from './value-wrappers-core.ts';
3
4
  type IntColType = 'SMALLINT' | 'INTEGER' | 'BIGINT' | 'HUGEINT' | 'USMALLINT' | 'UINTEGER' | 'UBIGINT' | 'UHUGEINT' | 'INT' | 'INT16' | 'INT32' | 'INT64' | 'INT128' | 'LONG' | 'VARINT';
4
5
  type FloatColType = 'FLOAT' | 'DOUBLE';
5
6
  type StringColType = 'STRING' | 'VARCHAR' | 'TEXT';
@@ -16,7 +17,7 @@ export declare const duckDbList: <TData = unknown>(name: string, elementType: An
16
17
  dataType: "custom";
17
18
  columnType: "PgCustomColumn";
18
19
  data: TData[];
19
- driverParam: string | unknown[] | SQL<unknown>;
20
+ driverParam: string | ListValueWrapper | unknown[];
20
21
  enumValues: undefined;
21
22
  }>;
22
23
  export declare const duckDbArray: <TData = unknown>(name: string, elementType: AnyColType, fixedLength?: number) => import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
@@ -24,7 +25,7 @@ export declare const duckDbArray: <TData = unknown>(name: string, elementType: A
24
25
  dataType: "custom";
25
26
  columnType: "PgCustomColumn";
26
27
  data: TData[];
27
- driverParam: string | unknown[] | SQL<unknown>;
28
+ driverParam: string | unknown[] | ArrayValueWrapper;
28
29
  enumValues: undefined;
29
30
  }>;
30
31
  export declare const duckDbMap: <TData extends Record<string, any>>(name: string, valueType: AnyColType | ListColType | ArrayColType) => import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
@@ -32,7 +33,7 @@ export declare const duckDbMap: <TData extends Record<string, any>>(name: string
32
33
  dataType: "custom";
33
34
  columnType: "PgCustomColumn";
34
35
  data: TData;
35
- driverParam: TData;
36
+ driverParam: MapValueWrapper | TData;
36
37
  enumValues: undefined;
37
38
  }>;
38
39
  export declare const duckDbStruct: <TData extends Record<string, any>>(name: string, schema: Record<string, Primitive>) => import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
@@ -43,12 +44,19 @@ export declare const duckDbStruct: <TData extends Record<string, any>>(name: str
43
44
  driverParam: TData;
44
45
  enumValues: undefined;
45
46
  }>;
47
+ /**
48
+ * JSON column type that wraps values and delays JSON.stringify() to binding time.
49
+ * This ensures consistent handling with other wrapped types.
50
+ *
51
+ * Note: DuckDB stores JSON as VARCHAR internally, so the final binding
52
+ * is always a stringified JSON value.
53
+ */
46
54
  export declare const duckDbJson: <TData = unknown>(name: string) => import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
47
55
  name: string;
48
56
  dataType: "custom";
49
57
  columnType: "PgCustomColumn";
50
58
  data: TData;
51
- driverParam: string | SQL<unknown>;
59
+ driverParam: string | JsonValueWrapper | SQL<unknown>;
52
60
  enumValues: undefined;
53
61
  }>;
54
62
  export declare const duckDbBlob: {
@@ -56,24 +64,24 @@ export declare const duckDbBlob: {
56
64
  name: "";
57
65
  dataType: "custom";
58
66
  columnType: "PgCustomColumn";
59
- data: Buffer;
60
- driverParam: unknown;
67
+ data: Buffer<ArrayBufferLike>;
68
+ driverParam: BlobValueWrapper;
61
69
  enumValues: undefined;
62
70
  }>;
63
71
  <TConfig extends Record<string, any>>(fieldConfig?: TConfig | undefined): import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
64
72
  name: "";
65
73
  dataType: "custom";
66
74
  columnType: "PgCustomColumn";
67
- data: Buffer;
68
- driverParam: unknown;
75
+ data: Buffer<ArrayBufferLike>;
76
+ driverParam: BlobValueWrapper;
69
77
  enumValues: undefined;
70
78
  }>;
71
79
  <TName extends string>(dbName: TName, fieldConfig?: unknown): import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
72
80
  name: TName;
73
81
  dataType: "custom";
74
82
  columnType: "PgCustomColumn";
75
- data: Buffer;
76
- driverParam: unknown;
83
+ data: Buffer<ArrayBufferLike>;
84
+ driverParam: BlobValueWrapper;
77
85
  enumValues: undefined;
78
86
  }>;
79
87
  };
package/dist/driver.d.ts CHANGED
@@ -1,13 +1,17 @@
1
+ import { DuckDBInstance } from '@duckdb/node-api';
1
2
  import { entityKind } from 'drizzle-orm/entity';
2
3
  import type { Logger } from 'drizzle-orm/logger';
3
4
  import { PgDatabase } from 'drizzle-orm/pg-core/db';
4
5
  import type { SelectedFields } from 'drizzle-orm/pg-core/query-builders';
5
6
  import { type ExtractTablesWithRelations, type RelationalSchemaConfig, type TablesRelationalConfig } from 'drizzle-orm/relations';
6
7
  import { type DrizzleConfig } from 'drizzle-orm/utils';
8
+ import type { SQL } from 'drizzle-orm/sql/sql';
7
9
  import type { DuckDBClientLike, DuckDBQueryResultHKT, DuckDBTransaction } from './session.ts';
8
10
  import { DuckDBSession } from './session.ts';
9
11
  import { DuckDBDialect } from './dialect.ts';
10
12
  import { DuckDBSelectBuilder } from './select-builder.ts';
13
+ import type { ExecuteInBatchesOptions, RowData } from './client.ts';
14
+ import { type DuckDBPoolConfig, type PoolPreset } from './pool.ts';
11
15
  export interface PgDriverOptions {
12
16
  logger?: Logger;
13
17
  rewriteArrays?: boolean;
@@ -21,17 +25,49 @@ export declare class DuckDBDriver {
21
25
  constructor(client: DuckDBClientLike, dialect: DuckDBDialect, options?: PgDriverOptions);
22
26
  createSession(schema: RelationalSchemaConfig<TablesRelationalConfig> | undefined): DuckDBSession<Record<string, unknown>, TablesRelationalConfig>;
23
27
  }
28
+ /** Connection configuration when using path-based connection */
29
+ export interface DuckDBConnectionConfig {
30
+ /** Database path: ':memory:', './file.duckdb', 'md:', 'md:database' */
31
+ path: string;
32
+ /** DuckDB instance options (e.g., motherduck_token) */
33
+ options?: Record<string, string>;
34
+ }
24
35
  export interface DuckDBDrizzleConfig<TSchema extends Record<string, unknown> = Record<string, never>> extends DrizzleConfig<TSchema> {
25
36
  rewriteArrays?: boolean;
26
37
  rejectStringArrayLiterals?: boolean;
38
+ /** Pool configuration. Use preset name, size config, or false to disable. */
39
+ pool?: DuckDBPoolConfig | PoolPreset | false;
40
+ }
41
+ export interface DuckDBDrizzleConfigWithConnection<TSchema extends Record<string, unknown> = Record<string, never>> extends DuckDBDrizzleConfig<TSchema> {
42
+ /** Connection string or config object */
43
+ connection: string | DuckDBConnectionConfig;
44
+ }
45
+ export interface DuckDBDrizzleConfigWithClient<TSchema extends Record<string, unknown> = Record<string, never>> extends DuckDBDrizzleConfig<TSchema> {
46
+ /** Explicit client (connection or pool) */
47
+ client: DuckDBClientLike;
27
48
  }
49
+ export declare function drizzle<TSchema extends Record<string, unknown> = Record<string, never>>(connectionString: string): Promise<DuckDBDatabase<TSchema, ExtractTablesWithRelations<TSchema>>>;
50
+ export declare function drizzle<TSchema extends Record<string, unknown> = Record<string, never>>(connectionString: string, config: DuckDBDrizzleConfig<TSchema>): Promise<DuckDBDatabase<TSchema, ExtractTablesWithRelations<TSchema>>>;
51
+ export declare function drizzle<TSchema extends Record<string, unknown> = Record<string, never>>(config: DuckDBDrizzleConfigWithConnection<TSchema>): Promise<DuckDBDatabase<TSchema, ExtractTablesWithRelations<TSchema>>>;
52
+ export declare function drizzle<TSchema extends Record<string, unknown> = Record<string, never>>(config: DuckDBDrizzleConfigWithClient<TSchema>): DuckDBDatabase<TSchema, ExtractTablesWithRelations<TSchema>>;
28
53
  export declare function drizzle<TSchema extends Record<string, unknown> = Record<string, never>>(client: DuckDBClientLike, config?: DuckDBDrizzleConfig<TSchema>): DuckDBDatabase<TSchema, ExtractTablesWithRelations<TSchema>>;
29
54
  export declare class DuckDBDatabase<TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>> extends PgDatabase<DuckDBQueryResultHKT, TFullSchema, TSchema> {
30
55
  readonly dialect: DuckDBDialect;
31
56
  readonly session: DuckDBSession<TFullSchema, TSchema>;
32
57
  static readonly [entityKind]: string;
33
- constructor(dialect: DuckDBDialect, session: DuckDBSession<TFullSchema, TSchema>, schema: RelationalSchemaConfig<TSchema> | undefined);
58
+ /** The underlying connection or pool */
59
+ readonly $client: DuckDBClientLike;
60
+ /** The DuckDB instance (when created from connection string) */
61
+ readonly $instance?: DuckDBInstance;
62
+ constructor(dialect: DuckDBDialect, session: DuckDBSession<TFullSchema, TSchema>, schema: RelationalSchemaConfig<TSchema> | undefined, client: DuckDBClientLike, instance?: DuckDBInstance);
63
+ /**
64
+ * Close the database connection pool and instance.
65
+ * Should be called when shutting down the application.
66
+ */
67
+ close(): Promise<void>;
34
68
  select(): DuckDBSelectBuilder<undefined>;
35
69
  select<TSelection extends SelectedFields>(fields: TSelection): DuckDBSelectBuilder<TSelection>;
70
+ executeBatches<T extends RowData = RowData>(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<T[], void, void>;
71
+ executeArrow(query: SQL): Promise<unknown>;
36
72
  transaction<T>(transaction: (tx: DuckDBTransaction<TFullSchema, TSchema>) => Promise<T>): Promise<T>;
37
73
  }