@molecule/api-database-duckdb 0.0.0-stage → 0.0.1-stage
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/LICENSE +202 -0
- package/README.md +772 -2
- package/dist/browser-guard.d.ts +2 -0
- package/dist/browser-guard.d.ts.map +1 -0
- package/dist/browser-guard.js +19 -0
- package/dist/browser-guard.js.map +1 -0
- package/dist/capabilities.d.ts +30 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +48 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/index.d.ts +115 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +169 -0
- package/dist/index.js.map +1 -0
- package/dist/instances.d.ts +73 -0
- package/dist/instances.d.ts.map +1 -0
- package/dist/instances.js +117 -0
- package/dist/instances.js.map +1 -0
- package/dist/lifecycle.d.ts +22 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +45 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/migrator.d.ts +74 -0
- package/dist/migrator.d.ts.map +1 -0
- package/dist/migrator.js +172 -0
- package/dist/migrator.js.map +1 -0
- package/dist/pool.d.ts +40 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +273 -0
- package/dist/pool.js.map +1 -0
- package/dist/queue.d.ts +48 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +98 -0
- package/dist/queue.js.map +1 -0
- package/dist/secrets.d.ts +16 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +25 -0
- package/dist/secrets.js.map +1 -0
- package/dist/sql-dialect.d.ts +92 -0
- package/dist/sql-dialect.d.ts.map +1 -0
- package/dist/sql-dialect.js +309 -0
- package/dist/sql-dialect.js.map +1 -0
- package/dist/store.d.ts +18 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +335 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utilities.d.ts +119 -0
- package/dist/utilities.d.ts.map +1 -0
- package/dist/utilities.js +246 -0
- package/dist/utilities.js.map +1 -0
- package/package.json +87 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-guard.d.ts","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser guard — `@molecule/api-database-duckdb` is SERVER-ONLY.
|
|
3
|
+
*
|
|
4
|
+
* Generated by scripts/gen-browser-guards.mjs (workspace root) — edit THAT, not this.
|
|
5
|
+
* Evaluating a server package in a browser bundle is always an import-graph mistake
|
|
6
|
+
* (node APIs, secrets); without this guard it surfaces as a cryptic downstream crash
|
|
7
|
+
* ("Buffer is not defined") far from the culprit. Throwing here names the package and
|
|
8
|
+
* the fix at the exact moment the client bundle evaluates it. jsdom tests and SSR are
|
|
9
|
+
* unaffected: the throw requires browser globals AND the absence of a node runtime.
|
|
10
|
+
*/
|
|
11
|
+
const g = globalThis;
|
|
12
|
+
if (g.window !== undefined && g.document !== undefined && !g.process?.versions?.node) {
|
|
13
|
+
throw new Error('@molecule/api-database-duckdb is SERVER-ONLY: it was bundled into browser/client code. Import it only ' +
|
|
14
|
+
'from server code (a server route/function or your API), or dynamic-import it inside ' +
|
|
15
|
+
'the server handler — never from components or shared client modules, and never ' +
|
|
16
|
+
'polyfill Buffer/process to silence this.');
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=browser-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-guard.js","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,GAAG,UAIT,CAAA;AACD,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrF,MAAM,IAAI,KAAK,CACb,wGAAwG;QACtG,sFAAsF;QACtF,iFAAiF;QACjF,0CAA0C,CAC7C,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuckDB's SQL capability declaration — what the bond can and cannot reproduce
|
|
3
|
+
* from the PostgreSQL-dialect setup library.
|
|
4
|
+
*
|
|
5
|
+
* `@molecule/api-database` defines the contract (`SqlCapabilities`); the same
|
|
6
|
+
* data is mirrored into this package's `mlcl/registry.json` entry so the CLI can
|
|
7
|
+
* run the preflight without installing the bond. Verified against
|
|
8
|
+
* `@duckdb/node-api` 2.0.0-r.1 (the real engine, not a doc page):
|
|
9
|
+
*
|
|
10
|
+
* - a foreign key with `ON DELETE CASCADE` / `SET NULL` / `SET DEFAULT` fails at
|
|
11
|
+
* parse time with `FOREIGN KEY constraints cannot use CASCADE, SET NULL or SET
|
|
12
|
+
* DEFAULT`, and DuckDB has no triggers, so the action cannot be reproduced;
|
|
13
|
+
* - `CREATE INDEX ... WHERE ...` fails with `Creating partial indexes is not
|
|
14
|
+
* supported currently`, while a plain (non-unique) index covers every row
|
|
15
|
+
* without changing results and a UNIQUE partial index is reproducible with a
|
|
16
|
+
* `CASE WHEN` expression index.
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
import type { SqlCapabilities } from '@molecule/api-database';
|
|
21
|
+
/**
|
|
22
|
+
* What the DuckDB bond supports from PostgreSQL-dialect migrations.
|
|
23
|
+
*
|
|
24
|
+
* `fk-referential-action` is deliberately `unsupported`: applying the setup
|
|
25
|
+
* library with the action dropped would make a parent delete fail while child
|
|
26
|
+
* rows exist, where PostgreSQL cascades — a silently changed delete, which this
|
|
27
|
+
* bond refuses rather than reproduces inexactly.
|
|
28
|
+
*/
|
|
29
|
+
export declare const sqlCapabilities: SqlCapabilities;
|
|
30
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAE7D;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,eAsB7B,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuckDB's SQL capability declaration — what the bond can and cannot reproduce
|
|
3
|
+
* from the PostgreSQL-dialect setup library.
|
|
4
|
+
*
|
|
5
|
+
* `@molecule/api-database` defines the contract (`SqlCapabilities`); the same
|
|
6
|
+
* data is mirrored into this package's `mlcl/registry.json` entry so the CLI can
|
|
7
|
+
* run the preflight without installing the bond. Verified against
|
|
8
|
+
* `@duckdb/node-api` 2.0.0-r.1 (the real engine, not a doc page):
|
|
9
|
+
*
|
|
10
|
+
* - a foreign key with `ON DELETE CASCADE` / `SET NULL` / `SET DEFAULT` fails at
|
|
11
|
+
* parse time with `FOREIGN KEY constraints cannot use CASCADE, SET NULL or SET
|
|
12
|
+
* DEFAULT`, and DuckDB has no triggers, so the action cannot be reproduced;
|
|
13
|
+
* - `CREATE INDEX ... WHERE ...` fails with `Creating partial indexes is not
|
|
14
|
+
* supported currently`, while a plain (non-unique) index covers every row
|
|
15
|
+
* without changing results and a UNIQUE partial index is reproducible with a
|
|
16
|
+
* `CASE WHEN` expression index.
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* What the DuckDB bond supports from PostgreSQL-dialect migrations.
|
|
22
|
+
*
|
|
23
|
+
* `fk-referential-action` is deliberately `unsupported`: applying the setup
|
|
24
|
+
* library with the action dropped would make a parent delete fail while child
|
|
25
|
+
* rows exist, where PostgreSQL cascades — a silently changed delete, which this
|
|
26
|
+
* bond refuses rather than reproduces inexactly.
|
|
27
|
+
*/
|
|
28
|
+
export const sqlCapabilities = {
|
|
29
|
+
'fk-referential-action': {
|
|
30
|
+
support: 'unsupported',
|
|
31
|
+
note: 'DuckDB rejects ON DELETE and ON UPDATE CASCADE, SET NULL and SET DEFAULT at ' +
|
|
32
|
+
'parse time and has no triggers, so a cascading or nulling delete cannot be ' +
|
|
33
|
+
'reproduced. Select a database provider that enforces referential actions, or ' +
|
|
34
|
+
'remove the resource whose schema needs them.',
|
|
35
|
+
},
|
|
36
|
+
'partial-index': {
|
|
37
|
+
support: 'translated',
|
|
38
|
+
note: 'Applied as a full index on the same columns. The WHERE clause only narrows ' +
|
|
39
|
+
'which rows the index covers, so query results are identical without it.',
|
|
40
|
+
},
|
|
41
|
+
'unique-partial-index': {
|
|
42
|
+
support: 'translated',
|
|
43
|
+
note: 'Applied as a UNIQUE index over CASE WHEN <predicate> THEN <column> END for each ' +
|
|
44
|
+
'indexed column. Rows outside the predicate get NULL keys, which DuckDB keeps ' +
|
|
45
|
+
'distinct, so exactly the rows PostgreSQL would constrain are constrained.',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C,uBAAuB,EAAE;QACvB,OAAO,EAAE,aAAa;QACtB,IAAI,EACF,8EAA8E;YAC9E,6EAA6E;YAC7E,+EAA+E;YAC/E,8CAA8C;KACjD;IACD,eAAe,EAAE;QACf,OAAO,EAAE,YAAY;QACrB,IAAI,EACF,6EAA6E;YAC7E,yEAAyE;KAC5E;IACD,sBAAsB,EAAE;QACtB,OAAO,EAAE,YAAY;QACrB,IAAI,EACF,kFAAkF;YAClF,+EAA+E;YAC/E,2EAA2E;KAC9E;CACF,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuckDB database provider for molecule.dev — an embedded analytical SQL
|
|
3
|
+
* engine that queries Parquet, CSV and JSON files in-process.
|
|
4
|
+
*
|
|
5
|
+
* Provides DatabasePool and DataStore implementations backed by
|
|
6
|
+
* `@duckdb/node-api` (DuckDB 2.0). DuckDB is the SQLite shape, but
|
|
7
|
+
* columnar/OLAP: it is the right bond for analytics over files
|
|
8
|
+
* (`SELECT * FROM read_parquet('events.parquet')`), aggregations and ad-hoc
|
|
9
|
+
* slices — not for a high-write-rate OLTP app (that is the postgresql bond's
|
|
10
|
+
* job).
|
|
11
|
+
*
|
|
12
|
+
* DuckDB cannot reproduce a foreign key's `ON DELETE`/`ON UPDATE` `CASCADE`,
|
|
13
|
+
* `SET NULL` or `SET DEFAULT` action, and this bond refuses to silently drop
|
|
14
|
+
* it: a resource whose setup SQL needs one is refused by the generator's
|
|
15
|
+
* compatibility preflight ({@link sqlCapabilities}) before an app is built.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { setPool, setStore } from '@molecule/api-database'
|
|
20
|
+
* import { pool, store } from '@molecule/api-database-duckdb'
|
|
21
|
+
*
|
|
22
|
+
* setPool(pool)
|
|
23
|
+
* setStore(store)
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* Configure via the `DUCKDB_PATH` environment variable (default:
|
|
28
|
+
* `./data/app.duckdb`; `:memory:` for a database that lives only for the
|
|
29
|
+
* process's lifetime).
|
|
30
|
+
* - **`pool.query(text, values)` takes Postgres-style `$1, $2` placeholders —
|
|
31
|
+
* or `?` — natively, with no translation.** With VALUES a string must be
|
|
32
|
+
* ONE statement (the client prepares it); without values a multi-statement
|
|
33
|
+
* string runs in full and the LAST statement's result is returned.
|
|
34
|
+
* `rowCount` follows node-postgres: rows returned for a SELECT or
|
|
35
|
+
* `…RETURNING` statement, rows changed for plain INSERT/UPDATE/DELETE,
|
|
36
|
+
* `null` for DDL.
|
|
37
|
+
* - **One process per file, in read-write mode.** DuckDB holds a file lock:
|
|
38
|
+
* the dev server and a CLI must never open the same `.duckdb` file
|
|
39
|
+
* read-write at the same time (`Could not set lock on file`). This pool
|
|
40
|
+
* shares one instance per file inside the process (`fromCache`) and
|
|
41
|
+
* serializes every statement on ONE connection behind a FIFO queue, the
|
|
42
|
+
* sqlite bond's model — a `transaction()` holds the queue for its whole
|
|
43
|
+
* `BEGIN` → … → `COMMIT`/`ROLLBACK` lifetime. A second PROCESS wanting
|
|
44
|
+
* concurrent reads should open `readOnly: true`.
|
|
45
|
+
* - **Resource defaults differ from DuckDB's own.** DuckDB defaults to 80% of
|
|
46
|
+
* RAM for `memory_limit` and one thread per core — enough to eat a shared
|
|
47
|
+
* host. This bond defaults to `memory_limit: '1GB'` and `threads: 4`,
|
|
48
|
+
* overridable via `DUCKDB_MEMORY_LIMIT` / `DUCKDB_THREADS` or the
|
|
49
|
+
* `memoryLimit`/`threads` config fields.
|
|
50
|
+
* - **Files are the point:** `read_parquet`, `read_csv`, `read_json` and the
|
|
51
|
+
* `'file.parquet'` shorthand all work through `pool.query()` as plain SQL.
|
|
52
|
+
* They read relative to the API process's working directory.
|
|
53
|
+
* - **JSON columns read as JSON TEXT through raw `query()`** — DuckDB's
|
|
54
|
+
* `JSON` type is an alias over VARCHAR, so the type is invisible in result
|
|
55
|
+
* metadata. The DataStore CRUD helpers (`create`, `findMany`, …) parse
|
|
56
|
+
* JSON-typed columns for you; in raw SQL use `data -> 'key'` /
|
|
57
|
+
* `json_extract` or accept the string.
|
|
58
|
+
* - **`COUNT(*)` and BIGINT come back as JS numbers** when they fit
|
|
59
|
+
* `Number.MAX_SAFE_INTEGER` (raw DuckDB returns `bigint`, which
|
|
60
|
+
* `JSON.stringify` throws on); a larger value becomes its decimal string,
|
|
61
|
+
* matching node-postgres.
|
|
62
|
+
* - **Schema changes are `*.sql` files in the app's `api/migrations/`**,
|
|
63
|
+
* applied in lexical order on startup by `createMigrator`. Postgres-dialect
|
|
64
|
+
* DDL applies after a translation that keeps behaviour: `jsonb` → `json`,
|
|
65
|
+
* index `USING <method>` dropped, and a partial index applied as a full index
|
|
66
|
+
* or (UNIQUE) as a `CASE WHEN` expression index. `gen_random_uuid()`,
|
|
67
|
+
* `now()`, `ADD/DROP COLUMN IF [NOT] EXISTS`, `::` casts and `uuid`/
|
|
68
|
+
* `timestamptz`/`bytea`/`text[]` types are native. An `ON DELETE`/
|
|
69
|
+
* `ON UPDATE` referential action throws `UnsupportedSqlFeatureError` —
|
|
70
|
+
* DuckDB cannot reproduce it; each file runs once (recorded in
|
|
71
|
+
* `_molecule_migrations`) and in one transaction.
|
|
72
|
+
* - **2.0 file format is a one-way break:** versions before DuckDB 2.0 cannot
|
|
73
|
+
* read files this bond creates (2.0 still reads 1.x files). The engine is
|
|
74
|
+
* OLAP — bulk scans and aggregations are its home; an app doing many small
|
|
75
|
+
* row transactions belongs on the postgresql bond.
|
|
76
|
+
* - `pool.end()` closes the pool's connection; a FILE-backed instance is
|
|
77
|
+
* process-lifetime by design (the client's instance cache has no eviction).
|
|
78
|
+
*
|
|
79
|
+
* @module
|
|
80
|
+
*/
|
|
81
|
+
export * from './browser-guard.js';
|
|
82
|
+
export * from './capabilities.js';
|
|
83
|
+
export * from './instances.js';
|
|
84
|
+
export * from './lifecycle.js';
|
|
85
|
+
export * from './migrator.js';
|
|
86
|
+
export * from './pool.js';
|
|
87
|
+
export * from './queue.js';
|
|
88
|
+
export * from './secrets.js';
|
|
89
|
+
export * from './store.js';
|
|
90
|
+
export * from './types.js';
|
|
91
|
+
export * from './utilities.js';
|
|
92
|
+
import type { DatabasePool } from '@molecule/api-database';
|
|
93
|
+
/**
|
|
94
|
+
* The DuckDB connection pool instance.
|
|
95
|
+
*/
|
|
96
|
+
export declare const pool: DatabasePool;
|
|
97
|
+
/**
|
|
98
|
+
* Whether the lazy default pool has been created.
|
|
99
|
+
*
|
|
100
|
+
* Readiness and shutdown wiring must not FORCE a connection.
|
|
101
|
+
*
|
|
102
|
+
* @returns `true` once the default pool exists.
|
|
103
|
+
*/
|
|
104
|
+
export declare const defaultPoolCreated: () => boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Close the lazy default pool, if it was created.
|
|
107
|
+
*
|
|
108
|
+
* @returns Resolves once the pool has ended.
|
|
109
|
+
*/
|
|
110
|
+
export declare const closeDefaultPool: () => Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Default DataStore proxy backed by the lazily-initialized default pool.
|
|
113
|
+
*/
|
|
114
|
+
export declare const store: import("@molecule/api-database").DataStore;
|
|
115
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAE9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAsB1D;;GAEG;AACH,eAAO,MAAM,IAAI,EAAE,YAQjB,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,QAAO,OAAyB,CAAA;AAE/D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAa,OAAO,CAAC,IAAI,CAIrD,CAAA;AAOD;;GAEG;AACH,eAAO,MAAM,KAAK,4CAUhB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuckDB database provider for molecule.dev — an embedded analytical SQL
|
|
3
|
+
* engine that queries Parquet, CSV and JSON files in-process.
|
|
4
|
+
*
|
|
5
|
+
* Provides DatabasePool and DataStore implementations backed by
|
|
6
|
+
* `@duckdb/node-api` (DuckDB 2.0). DuckDB is the SQLite shape, but
|
|
7
|
+
* columnar/OLAP: it is the right bond for analytics over files
|
|
8
|
+
* (`SELECT * FROM read_parquet('events.parquet')`), aggregations and ad-hoc
|
|
9
|
+
* slices — not for a high-write-rate OLTP app (that is the postgresql bond's
|
|
10
|
+
* job).
|
|
11
|
+
*
|
|
12
|
+
* DuckDB cannot reproduce a foreign key's `ON DELETE`/`ON UPDATE` `CASCADE`,
|
|
13
|
+
* `SET NULL` or `SET DEFAULT` action, and this bond refuses to silently drop
|
|
14
|
+
* it: a resource whose setup SQL needs one is refused by the generator's
|
|
15
|
+
* compatibility preflight ({@link sqlCapabilities}) before an app is built.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { setPool, setStore } from '@molecule/api-database'
|
|
20
|
+
* import { pool, store } from '@molecule/api-database-duckdb'
|
|
21
|
+
*
|
|
22
|
+
* setPool(pool)
|
|
23
|
+
* setStore(store)
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* Configure via the `DUCKDB_PATH` environment variable (default:
|
|
28
|
+
* `./data/app.duckdb`; `:memory:` for a database that lives only for the
|
|
29
|
+
* process's lifetime).
|
|
30
|
+
* - **`pool.query(text, values)` takes Postgres-style `$1, $2` placeholders —
|
|
31
|
+
* or `?` — natively, with no translation.** With VALUES a string must be
|
|
32
|
+
* ONE statement (the client prepares it); without values a multi-statement
|
|
33
|
+
* string runs in full and the LAST statement's result is returned.
|
|
34
|
+
* `rowCount` follows node-postgres: rows returned for a SELECT or
|
|
35
|
+
* `…RETURNING` statement, rows changed for plain INSERT/UPDATE/DELETE,
|
|
36
|
+
* `null` for DDL.
|
|
37
|
+
* - **One process per file, in read-write mode.** DuckDB holds a file lock:
|
|
38
|
+
* the dev server and a CLI must never open the same `.duckdb` file
|
|
39
|
+
* read-write at the same time (`Could not set lock on file`). This pool
|
|
40
|
+
* shares one instance per file inside the process (`fromCache`) and
|
|
41
|
+
* serializes every statement on ONE connection behind a FIFO queue, the
|
|
42
|
+
* sqlite bond's model — a `transaction()` holds the queue for its whole
|
|
43
|
+
* `BEGIN` → … → `COMMIT`/`ROLLBACK` lifetime. A second PROCESS wanting
|
|
44
|
+
* concurrent reads should open `readOnly: true`.
|
|
45
|
+
* - **Resource defaults differ from DuckDB's own.** DuckDB defaults to 80% of
|
|
46
|
+
* RAM for `memory_limit` and one thread per core — enough to eat a shared
|
|
47
|
+
* host. This bond defaults to `memory_limit: '1GB'` and `threads: 4`,
|
|
48
|
+
* overridable via `DUCKDB_MEMORY_LIMIT` / `DUCKDB_THREADS` or the
|
|
49
|
+
* `memoryLimit`/`threads` config fields.
|
|
50
|
+
* - **Files are the point:** `read_parquet`, `read_csv`, `read_json` and the
|
|
51
|
+
* `'file.parquet'` shorthand all work through `pool.query()` as plain SQL.
|
|
52
|
+
* They read relative to the API process's working directory.
|
|
53
|
+
* - **JSON columns read as JSON TEXT through raw `query()`** — DuckDB's
|
|
54
|
+
* `JSON` type is an alias over VARCHAR, so the type is invisible in result
|
|
55
|
+
* metadata. The DataStore CRUD helpers (`create`, `findMany`, …) parse
|
|
56
|
+
* JSON-typed columns for you; in raw SQL use `data -> 'key'` /
|
|
57
|
+
* `json_extract` or accept the string.
|
|
58
|
+
* - **`COUNT(*)` and BIGINT come back as JS numbers** when they fit
|
|
59
|
+
* `Number.MAX_SAFE_INTEGER` (raw DuckDB returns `bigint`, which
|
|
60
|
+
* `JSON.stringify` throws on); a larger value becomes its decimal string,
|
|
61
|
+
* matching node-postgres.
|
|
62
|
+
* - **Schema changes are `*.sql` files in the app's `api/migrations/`**,
|
|
63
|
+
* applied in lexical order on startup by `createMigrator`. Postgres-dialect
|
|
64
|
+
* DDL applies after a translation that keeps behaviour: `jsonb` → `json`,
|
|
65
|
+
* index `USING <method>` dropped, and a partial index applied as a full index
|
|
66
|
+
* or (UNIQUE) as a `CASE WHEN` expression index. `gen_random_uuid()`,
|
|
67
|
+
* `now()`, `ADD/DROP COLUMN IF [NOT] EXISTS`, `::` casts and `uuid`/
|
|
68
|
+
* `timestamptz`/`bytea`/`text[]` types are native. An `ON DELETE`/
|
|
69
|
+
* `ON UPDATE` referential action throws `UnsupportedSqlFeatureError` —
|
|
70
|
+
* DuckDB cannot reproduce it; each file runs once (recorded in
|
|
71
|
+
* `_molecule_migrations`) and in one transaction.
|
|
72
|
+
* - **2.0 file format is a one-way break:** versions before DuckDB 2.0 cannot
|
|
73
|
+
* read files this bond creates (2.0 still reads 1.x files). The engine is
|
|
74
|
+
* OLAP — bulk scans and aggregations are its home; an app doing many small
|
|
75
|
+
* row transactions belongs on the postgresql bond.
|
|
76
|
+
* - `pool.end()` closes the pool's connection; a FILE-backed instance is
|
|
77
|
+
* process-lifetime by design (the client's instance cache has no eviction).
|
|
78
|
+
*
|
|
79
|
+
* @module
|
|
80
|
+
*/
|
|
81
|
+
export * from './browser-guard.js';
|
|
82
|
+
export * from './capabilities.js';
|
|
83
|
+
export * from './instances.js';
|
|
84
|
+
export * from './lifecycle.js';
|
|
85
|
+
export * from './migrator.js';
|
|
86
|
+
export * from './pool.js';
|
|
87
|
+
export * from './queue.js';
|
|
88
|
+
export * from './secrets.js';
|
|
89
|
+
export * from './store.js';
|
|
90
|
+
export * from './types.js';
|
|
91
|
+
export * from './utilities.js';
|
|
92
|
+
import { registerLifecycleIntegration } from './lifecycle.js';
|
|
93
|
+
import { createPool } from './pool.js';
|
|
94
|
+
import { createStore } from './store.js';
|
|
95
|
+
/**
|
|
96
|
+
* Lazily-initialized default pool.
|
|
97
|
+
*/
|
|
98
|
+
let _pool = null;
|
|
99
|
+
/**
|
|
100
|
+
* Returns the lazily-initialized default pool, creating it on first call.
|
|
101
|
+
* @returns The singleton DatabasePool instance.
|
|
102
|
+
*/
|
|
103
|
+
function getPoolInstance() {
|
|
104
|
+
if (!_pool) {
|
|
105
|
+
_pool = createPool();
|
|
106
|
+
}
|
|
107
|
+
return _pool;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The DuckDB connection pool instance.
|
|
111
|
+
*/
|
|
112
|
+
export const pool = new Proxy({}, {
|
|
113
|
+
get(_, prop, receiver) {
|
|
114
|
+
return Reflect.get(getPoolInstance(), prop, receiver);
|
|
115
|
+
},
|
|
116
|
+
// set trap: methods run with `this` bound to the proxy — without it, instance-state writes land on the dummy target and are lost (see api-push-notifications-web-push)
|
|
117
|
+
set(_, prop, value) {
|
|
118
|
+
return Reflect.set(getPoolInstance(), prop, value);
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
/**
|
|
122
|
+
* Whether the lazy default pool has been created.
|
|
123
|
+
*
|
|
124
|
+
* Readiness and shutdown wiring must not FORCE a connection.
|
|
125
|
+
*
|
|
126
|
+
* @returns `true` once the default pool exists.
|
|
127
|
+
*/
|
|
128
|
+
export const defaultPoolCreated = () => _pool !== null;
|
|
129
|
+
/**
|
|
130
|
+
* Close the lazy default pool, if it was created.
|
|
131
|
+
*
|
|
132
|
+
* @returns Resolves once the pool has ended.
|
|
133
|
+
*/
|
|
134
|
+
export const closeDefaultPool = async () => {
|
|
135
|
+
const current = _pool;
|
|
136
|
+
_pool = null;
|
|
137
|
+
if (current)
|
|
138
|
+
await current.end();
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Default DataStore instance backed by the default pool.
|
|
142
|
+
*/
|
|
143
|
+
let _store = null;
|
|
144
|
+
/**
|
|
145
|
+
* Default DataStore proxy backed by the lazily-initialized default pool.
|
|
146
|
+
*/
|
|
147
|
+
export const store = new Proxy({}, {
|
|
148
|
+
get(_, prop, receiver) {
|
|
149
|
+
if (!_store)
|
|
150
|
+
_store = createStore(getPoolInstance());
|
|
151
|
+
return Reflect.get(_store, prop, receiver);
|
|
152
|
+
},
|
|
153
|
+
// set trap: methods run with `this` bound to the proxy — without it, instance-state writes land on the dummy target and are lost (see api-push-notifications-web-push)
|
|
154
|
+
set(_, prop, value) {
|
|
155
|
+
if (!_store)
|
|
156
|
+
_store = createStore(getPoolInstance());
|
|
157
|
+
return Reflect.set(_store, prop, value);
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
// Register the readiness probe and shutdown hook once, when this bond is
|
|
161
|
+
// imported; the core queues them until a lifecycle provider is bonded.
|
|
162
|
+
registerLifecycleIntegration({
|
|
163
|
+
ping: async () => {
|
|
164
|
+
await pool.query('SELECT 1');
|
|
165
|
+
},
|
|
166
|
+
close: closeDefaultPool,
|
|
167
|
+
created: defaultPoolCreated,
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAI9B,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAExC;;GAEG;AACH,IAAI,KAAK,GAAwB,IAAI,CAAA;AAErC;;;GAGG;AACH,SAAS,eAAe;IACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,UAAU,EAAE,CAAA;IACtB,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,IAAI,GAAiB,IAAI,KAAK,CAAC,EAAkB,EAAE;IAC9D,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ;QACnB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;IACvD,CAAC;IACD,uKAAuK;IACvK,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK;QAChB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IACpD,CAAC;CACF,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAY,EAAE,CAAC,KAAK,KAAK,IAAI,CAAA;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,IAAmB,EAAE;IACxD,MAAM,OAAO,GAAG,KAAK,CAAA;IACrB,KAAK,GAAG,IAAI,CAAA;IACZ,IAAI,OAAO;QAAE,MAAM,OAAO,CAAC,GAAG,EAAE,CAAA;AAClC,CAAC,CAAA;AAED;;GAEG;AACH,IAAI,MAAM,GAA0C,IAAI,CAAA;AAExD;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAoC,EAAE;IACnE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ;QACnB,IAAI,CAAC,MAAM;YAAE,MAAM,GAAG,WAAW,CAAC,eAAe,EAAE,CAAC,CAAA;QACpD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;IAC5C,CAAC;IACD,uKAAuK;IACvK,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK;QAChB,IAAI,CAAC,MAAM;YAAE,MAAM,GAAG,WAAW,CAAC,eAAe,EAAE,CAAC,CAAA;QACpD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IACzC,CAAC;CACF,CAAC,CAAA;AAEF,yEAAyE;AACzE,uEAAuE;AACvE,4BAA4B,CAAC;IAC3B,IAAI,EAAE,KAAK,IAAI,EAAE;QACf,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IAC9B,CAAC;IACD,KAAK,EAAE,gBAAgB;IACvB,OAAO,EAAE,kBAAkB;CAC5B,CAAC,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opens DuckDB instances and shares one instance per database file.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { DuckDBInstance } from '@duckdb/node-api';
|
|
7
|
+
import { ConnectionLock } from './queue.js';
|
|
8
|
+
import type { DuckdbConfig } from './types.js';
|
|
9
|
+
/** The in-memory database path. */
|
|
10
|
+
export declare const MEMORY_PATH = ":memory:";
|
|
11
|
+
/** Default on-disk database path. */
|
|
12
|
+
export declare const DEFAULT_DUCKDB_PATH = "./data/app.duckdb";
|
|
13
|
+
/** Default `memory_limit`: DuckDB's own default is 80% of RAM — far too much on a shared host. */
|
|
14
|
+
export declare const DEFAULT_MEMORY_LIMIT = "1GB";
|
|
15
|
+
/** Default `threads`: DuckDB's own default is every core — oversubscribes a shared host. */
|
|
16
|
+
export declare const DEFAULT_THREADS = 4;
|
|
17
|
+
/**
|
|
18
|
+
* One opened database: the DuckDB instance, the lock over its single pooled
|
|
19
|
+
* connection, and how many pools currently use it.
|
|
20
|
+
*/
|
|
21
|
+
export interface DuckdbInstanceHandle {
|
|
22
|
+
/** The DuckDB instance. */
|
|
23
|
+
instance: DuckDBInstance;
|
|
24
|
+
/** FIFO lock over the pool's single connection. */
|
|
25
|
+
lock: ConnectionLock;
|
|
26
|
+
/** The resolved path (`':memory:'` for in-memory). */
|
|
27
|
+
path: string;
|
|
28
|
+
/** Pools currently using this instance. */
|
|
29
|
+
refs: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the effective instance options for a config: sane `memory_limit` and
|
|
33
|
+
* `threads` defaults (DuckDB's own defaults are 80% of RAM and every core,
|
|
34
|
+
* which let one in-process engine eat a shared VM whole), `access_mode` for
|
|
35
|
+
* `readOnly`, env overrides, then the caller's raw `config` map on top.
|
|
36
|
+
*
|
|
37
|
+
* @param config - Pool configuration.
|
|
38
|
+
* @returns The options for `DuckDBInstance.fromCache(path, options)`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function instanceOptionsFor(config?: DuckdbConfig): Record<string, string>;
|
|
41
|
+
/**
|
|
42
|
+
* Pick the database path from config, then `DUCKDB_PATH`, then the default.
|
|
43
|
+
*
|
|
44
|
+
* @param config - Pool configuration.
|
|
45
|
+
* @returns The path as given (not yet resolved).
|
|
46
|
+
*/
|
|
47
|
+
export declare function choosePath(config?: DuckdbConfig): string;
|
|
48
|
+
/**
|
|
49
|
+
* Open (or join) the instance for a pool.
|
|
50
|
+
*
|
|
51
|
+
* A FILE path goes through `DuckDBInstance.fromCache`: the official answer to
|
|
52
|
+
* "two instances must never open the same file" — every pool in this process
|
|
53
|
+
* on one file shares one instance (and one write lock). In-memory databases
|
|
54
|
+
* are always PRIVATE (a shared `:memory:` cache entry would hand two test
|
|
55
|
+
* pools one database), so they use a fresh `create` and are closed when their
|
|
56
|
+
* last pool ends. File-backed cached instances are NEVER closed by this
|
|
57
|
+
* module: the client's cache has no eviction, and closing a cached instance
|
|
58
|
+
* would hand the next `fromCache` caller a dead one — they live for the
|
|
59
|
+
* process, which is also why a file path must not be reused across processes
|
|
60
|
+
* in read-write mode.
|
|
61
|
+
*
|
|
62
|
+
* @param config - Pool configuration.
|
|
63
|
+
* @returns The instance, with a reference taken for the caller.
|
|
64
|
+
*/
|
|
65
|
+
export declare function acquireInstance(config?: DuckdbConfig): Promise<DuckdbInstanceHandle>;
|
|
66
|
+
/**
|
|
67
|
+
* Drop a pool's reference; close the instance when no pool uses it and it is
|
|
68
|
+
* one of this module's private in-memory instances.
|
|
69
|
+
*
|
|
70
|
+
* @param handle - The instance to release.
|
|
71
|
+
*/
|
|
72
|
+
export declare function releaseInstance(handle: DuckdbInstanceHandle): void;
|
|
73
|
+
//# sourceMappingURL=instances.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../src/instances.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,mCAAmC;AACnC,eAAO,MAAM,WAAW,aAAa,CAAA;AAErC,qCAAqC;AACrC,eAAO,MAAM,mBAAmB,sBAAsB,CAAA;AAEtD,kGAAkG;AAClG,eAAO,MAAM,oBAAoB,QAAQ,CAAA;AAEzC,4FAA4F;AAC5F,eAAO,MAAM,eAAe,IAAI,CAAA;AAEhC;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,2BAA2B;IAC3B,QAAQ,EAAE,cAAc,CAAA;IACxB,mDAAmD;IACnD,IAAI,EAAE,cAAc,CAAA;IACpB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAA;IACZ,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAA;CACb;AAaD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAchF;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAKxD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAuB1F;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAMlE"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opens DuckDB instances and shares one instance per database file.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
7
|
+
import { dirname, resolve } from 'node:path';
|
|
8
|
+
import { DuckDBInstance } from '@duckdb/node-api';
|
|
9
|
+
import { ConnectionLock } from './queue.js';
|
|
10
|
+
/** The in-memory database path. */
|
|
11
|
+
export const MEMORY_PATH = ':memory:';
|
|
12
|
+
/** Default on-disk database path. */
|
|
13
|
+
export const DEFAULT_DUCKDB_PATH = './data/app.duckdb';
|
|
14
|
+
/** Default `memory_limit`: DuckDB's own default is 80% of RAM — far too much on a shared host. */
|
|
15
|
+
export const DEFAULT_MEMORY_LIMIT = '1GB';
|
|
16
|
+
/** Default `threads`: DuckDB's own default is every core — oversubscribes a shared host. */
|
|
17
|
+
export const DEFAULT_THREADS = 4;
|
|
18
|
+
/** In-memory instances, each private to the pool that opened it. */
|
|
19
|
+
const memoryHandles = new Set();
|
|
20
|
+
/**
|
|
21
|
+
* One lock per shared FILE instance: `fromCache` hands every pool on a path
|
|
22
|
+
* the SAME instance object, so the lock is keyed off that object — two pools
|
|
23
|
+
* on one file then share one queue (and cannot race each other's writes),
|
|
24
|
+
* while each `:memory:` pool owns its own lock via its private handle.
|
|
25
|
+
*/
|
|
26
|
+
const sharedLocks = new WeakMap();
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the effective instance options for a config: sane `memory_limit` and
|
|
29
|
+
* `threads` defaults (DuckDB's own defaults are 80% of RAM and every core,
|
|
30
|
+
* which let one in-process engine eat a shared VM whole), `access_mode` for
|
|
31
|
+
* `readOnly`, env overrides, then the caller's raw `config` map on top.
|
|
32
|
+
*
|
|
33
|
+
* @param config - Pool configuration.
|
|
34
|
+
* @returns The options for `DuckDBInstance.fromCache(path, options)`.
|
|
35
|
+
*/
|
|
36
|
+
export function instanceOptionsFor(config) {
|
|
37
|
+
const memoryLimit = config?.memoryLimit ?? process.env.DUCKDB_MEMORY_LIMIT ?? DEFAULT_MEMORY_LIMIT;
|
|
38
|
+
const threads = String(config?.threads ??
|
|
39
|
+
(process.env.DUCKDB_THREADS != null && process.env.DUCKDB_THREADS !== ''
|
|
40
|
+
? process.env.DUCKDB_THREADS
|
|
41
|
+
: DEFAULT_THREADS));
|
|
42
|
+
return {
|
|
43
|
+
memory_limit: memoryLimit,
|
|
44
|
+
threads,
|
|
45
|
+
...(config?.readOnly ? { access_mode: 'READ_ONLY' } : {}),
|
|
46
|
+
...(config?.config ?? {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Pick the database path from config, then `DUCKDB_PATH`, then the default.
|
|
51
|
+
*
|
|
52
|
+
* @param config - Pool configuration.
|
|
53
|
+
* @returns The path as given (not yet resolved).
|
|
54
|
+
*/
|
|
55
|
+
export function choosePath(config) {
|
|
56
|
+
if (config?.path)
|
|
57
|
+
return config.path;
|
|
58
|
+
const fromEnv = process.env.DUCKDB_PATH;
|
|
59
|
+
if (fromEnv)
|
|
60
|
+
return fromEnv;
|
|
61
|
+
return DEFAULT_DUCKDB_PATH;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Open (or join) the instance for a pool.
|
|
65
|
+
*
|
|
66
|
+
* A FILE path goes through `DuckDBInstance.fromCache`: the official answer to
|
|
67
|
+
* "two instances must never open the same file" — every pool in this process
|
|
68
|
+
* on one file shares one instance (and one write lock). In-memory databases
|
|
69
|
+
* are always PRIVATE (a shared `:memory:` cache entry would hand two test
|
|
70
|
+
* pools one database), so they use a fresh `create` and are closed when their
|
|
71
|
+
* last pool ends. File-backed cached instances are NEVER closed by this
|
|
72
|
+
* module: the client's cache has no eviction, and closing a cached instance
|
|
73
|
+
* would hand the next `fromCache` caller a dead one — they live for the
|
|
74
|
+
* process, which is also why a file path must not be reused across processes
|
|
75
|
+
* in read-write mode.
|
|
76
|
+
*
|
|
77
|
+
* @param config - Pool configuration.
|
|
78
|
+
* @returns The instance, with a reference taken for the caller.
|
|
79
|
+
*/
|
|
80
|
+
export async function acquireInstance(config) {
|
|
81
|
+
const path = choosePath(config);
|
|
82
|
+
const options = instanceOptionsFor(config);
|
|
83
|
+
if (path === MEMORY_PATH) {
|
|
84
|
+
const instance = await DuckDBInstance.create(path, options);
|
|
85
|
+
const handle = { instance, lock: new ConnectionLock(), path, refs: 1 };
|
|
86
|
+
memoryHandles.add(handle);
|
|
87
|
+
return handle;
|
|
88
|
+
}
|
|
89
|
+
const dir = dirname(path);
|
|
90
|
+
if (dir && !existsSync(dir)) {
|
|
91
|
+
mkdirSync(dir, { recursive: true });
|
|
92
|
+
}
|
|
93
|
+
const resolved = resolve(path);
|
|
94
|
+
const instance = await DuckDBInstance.fromCache(resolved, options);
|
|
95
|
+
let lock = sharedLocks.get(instance);
|
|
96
|
+
if (!lock) {
|
|
97
|
+
lock = new ConnectionLock();
|
|
98
|
+
sharedLocks.set(instance, lock);
|
|
99
|
+
}
|
|
100
|
+
return { instance, lock, path: resolved, refs: 1 };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Drop a pool's reference; close the instance when no pool uses it and it is
|
|
104
|
+
* one of this module's private in-memory instances.
|
|
105
|
+
*
|
|
106
|
+
* @param handle - The instance to release.
|
|
107
|
+
*/
|
|
108
|
+
export function releaseInstance(handle) {
|
|
109
|
+
handle.refs--;
|
|
110
|
+
if (handle.refs > 0)
|
|
111
|
+
return;
|
|
112
|
+
if (!memoryHandles.has(handle))
|
|
113
|
+
return;
|
|
114
|
+
memoryHandles.delete(handle);
|
|
115
|
+
handle.instance.closeSync();
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=instances.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instances.js","sourceRoot":"","sources":["../src/instances.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-lifecycle integration for the DuckDB bond.
|
|
3
|
+
*
|
|
4
|
+
* Optional and dynamic — a project without `@molecule/api-process-lifecycle`
|
|
5
|
+
* imports this bond unchanged. With the package present, `/health/ready` runs
|
|
6
|
+
* a CRITICAL `SELECT 1` (a DuckDB file can be locked, removed, or its disk
|
|
7
|
+
* full after boot) and the ordered shutdown closes the pool.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Register the database readiness check and shutdown hook with the bonded
|
|
13
|
+
* process-lifecycle provider.
|
|
14
|
+
*
|
|
15
|
+
* @param resources - The ping and close functions this bond owns.
|
|
16
|
+
*/
|
|
17
|
+
export declare function registerLifecycleIntegration(resources: {
|
|
18
|
+
ping: () => Promise<void>;
|
|
19
|
+
close: () => Promise<void>;
|
|
20
|
+
created: () => boolean;
|
|
21
|
+
}): void;
|
|
22
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsBH;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE;IACtD,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,OAAO,EAAE,MAAM,OAAO,CAAA;CACvB,GAAG,IAAI,CAwBP"}
|