@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
package/dist/store.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuckDB DataStore implementation.
|
|
3
|
+
*
|
|
4
|
+
* Translates abstract DataStore CRUD methods to DuckDB SQL. DuckDB binds `?`
|
|
5
|
+
* and `$1` placeholders natively positionally, so the SQL built here needs no
|
|
6
|
+
* conversion on its way to the engine.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { getLogger } from '@molecule/api-bond';
|
|
12
|
+
import { assertFindManyOptions, assertOptionalWhereConditions, assertWhereConditions, } from '@molecule/api-database/query-guards.js';
|
|
13
|
+
import { parseJsonColumns } from './utilities.js';
|
|
14
|
+
/**
|
|
15
|
+
* Validates that an identifier (table/column name) is safe for SQL interpolation.
|
|
16
|
+
* Hyphens are allowed: every interpolation site double-quotes the identifier, and
|
|
17
|
+
* inside a quoted identifier only `"` itself is dangerous (still rejected). Real
|
|
18
|
+
* consumers use hyphenated tables (e.g. `@molecule/api-resource-share`'s
|
|
19
|
+
* `"resource-share-links"`) — parity with the postgresql/sqlite bonds.
|
|
20
|
+
*/
|
|
21
|
+
const SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
|
|
22
|
+
/**
|
|
23
|
+
* Validates that a SQL identifier (table or column name) is safe for interpolation.
|
|
24
|
+
*
|
|
25
|
+
* Table/column names cannot be bound as parameters, so they are interpolated
|
|
26
|
+
* directly into the SQL string. Any value that reaches an interpolation point MUST be
|
|
27
|
+
* validated here — otherwise a caller passing a user-supplied identifier (e.g. a
|
|
28
|
+
* `sort_by` column from a query string) opens a SQL-injection hole. The whitelist of
|
|
29
|
+
* plain `[A-Za-z_][A-Za-z0-9_-]*` identifiers makes the `"..."` quoting unbreakable.
|
|
30
|
+
*
|
|
31
|
+
* @param name - The identifier string to validate.
|
|
32
|
+
*/
|
|
33
|
+
function assertSafeIdentifier(name) {
|
|
34
|
+
if (!SAFE_IDENTIFIER.test(name)) {
|
|
35
|
+
throw new Error(`Invalid SQL identifier: ${name}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Builds a SQL WHERE clause from WhereCondition[] using ? placeholders.
|
|
40
|
+
*
|
|
41
|
+
* @param conditions - Array of WhereCondition objects to convert to SQL.
|
|
42
|
+
* @returns An object containing the SQL clause string and bound values array.
|
|
43
|
+
*/
|
|
44
|
+
function buildWhere(conditions) {
|
|
45
|
+
if (conditions.length === 0) {
|
|
46
|
+
return { clause: '', values: [] };
|
|
47
|
+
}
|
|
48
|
+
const parts = [];
|
|
49
|
+
const values = [];
|
|
50
|
+
for (const cond of conditions) {
|
|
51
|
+
assertSafeIdentifier(cond.field);
|
|
52
|
+
switch (cond.operator) {
|
|
53
|
+
case '=':
|
|
54
|
+
case '!=':
|
|
55
|
+
case '>':
|
|
56
|
+
case '<':
|
|
57
|
+
case '>=':
|
|
58
|
+
case '<=':
|
|
59
|
+
// A list given to a one-value operator matches nothing on every store: say so, the way
|
|
60
|
+
// 'in' given a scalar does.
|
|
61
|
+
if (Array.isArray(cond.value)) {
|
|
62
|
+
throw new Error(`'${cond.operator}' operator takes one value, not a list (field "${cond.field}"); use 'in' for a list`);
|
|
63
|
+
}
|
|
64
|
+
parts.push(`"${cond.field}" ${cond.operator} ?`);
|
|
65
|
+
values.push(cond.value);
|
|
66
|
+
break;
|
|
67
|
+
case 'in': {
|
|
68
|
+
const arr = cond.value;
|
|
69
|
+
if (!Array.isArray(arr)) {
|
|
70
|
+
throw new Error(`'${cond.operator}' operator requires an array value (field "${cond.field}")`);
|
|
71
|
+
}
|
|
72
|
+
if (arr.length === 0) {
|
|
73
|
+
// `IN ()` is a syntax error; match Postgres `= ANY('{}')` semantics: no match.
|
|
74
|
+
parts.push('1 = 0');
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
const placeholders = arr.map(() => '?').join(', ');
|
|
78
|
+
parts.push(`"${cond.field}" IN (${placeholders})`);
|
|
79
|
+
values.push(...arr);
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case 'not_in': {
|
|
83
|
+
const arr = cond.value;
|
|
84
|
+
if (!Array.isArray(arr)) {
|
|
85
|
+
throw new Error(`'${cond.operator}' operator requires an array value (field "${cond.field}")`);
|
|
86
|
+
}
|
|
87
|
+
if (arr.length === 0) {
|
|
88
|
+
// `NOT IN ()` is a syntax error; match Postgres `!= ALL('{}')`: everything matches.
|
|
89
|
+
parts.push('1 = 1');
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
const placeholders = arr.map(() => '?').join(', ');
|
|
93
|
+
parts.push(`"${cond.field}" NOT IN (${placeholders})`);
|
|
94
|
+
values.push(...arr);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case 'like':
|
|
98
|
+
// Case-insensitive RAW pattern match — the caller supplies the full SQL
|
|
99
|
+
// pattern (their own %/_ wildcards are honored, NOT escaped). LOWER()
|
|
100
|
+
// both sides for the documented core contract: `like` must be
|
|
101
|
+
// case-insensitive and IDENTICAL across every bond (DuckDB's own LIKE
|
|
102
|
+
// is case-sensitive; ILIKE exists but LIKE + LOWER() keeps the exact
|
|
103
|
+
// sqlite/mysql spelling of the contract). ESCAPE '\' pins backslash as
|
|
104
|
+
// THE escape character in the caller's pattern.
|
|
105
|
+
parts.push(`LOWER("${cond.field}") LIKE LOWER(?) ESCAPE '\\'`);
|
|
106
|
+
values.push(cond.value);
|
|
107
|
+
break;
|
|
108
|
+
case 'ilike':
|
|
109
|
+
// Case-insensitive CONTAINS on a literal substring — the documented core
|
|
110
|
+
// contract (`@molecule/api-database` WhereCondition): escape the value's
|
|
111
|
+
// LIKE metacharacters, wrap with `%…%`.
|
|
112
|
+
parts.push(`LOWER("${cond.field}") LIKE LOWER(?) ESCAPE '\\'`);
|
|
113
|
+
values.push(`%${String(cond.value).replace(/[%_\\]/g, '\\$&')}%`);
|
|
114
|
+
break;
|
|
115
|
+
case 'is_null':
|
|
116
|
+
parts.push(`"${cond.field}" IS NULL`);
|
|
117
|
+
break;
|
|
118
|
+
case 'is_not_null':
|
|
119
|
+
parts.push(`"${cond.field}" IS NOT NULL`);
|
|
120
|
+
break;
|
|
121
|
+
default:
|
|
122
|
+
// Parity with the postgres/sqlite bonds: an unknown operator must FAIL
|
|
123
|
+
// LOUDLY, not silently drop the condition (which would return unfiltered rows).
|
|
124
|
+
throw new Error(`Invalid SQL operator: ${String(cond.operator)}. The operators are lowercase: =, !=, >, <, >=, <=, in, not_in, like, ilike, is_null, is_not_null.`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return { clause: `WHERE ${parts.join(' AND ')}`, values };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Builds an ORDER BY clause.
|
|
131
|
+
*
|
|
132
|
+
* @param orderBy - Array of field/direction pairs to sort by.
|
|
133
|
+
* @returns A SQL ORDER BY clause string, or an empty string if none provided.
|
|
134
|
+
*/
|
|
135
|
+
function buildOrderBy(orderBy) {
|
|
136
|
+
if (!orderBy || orderBy.length === 0)
|
|
137
|
+
return '';
|
|
138
|
+
return `ORDER BY ${orderBy
|
|
139
|
+
.map((o) => {
|
|
140
|
+
assertSafeIdentifier(o.field);
|
|
141
|
+
// Whitelist the direction to exactly ASC/DESC — never interpolate an
|
|
142
|
+
// arbitrary `toUpperCase()` of caller input into the SQL string.
|
|
143
|
+
const dir = o.direction.toLowerCase() === 'desc' ? 'DESC' : 'ASC';
|
|
144
|
+
return `"${o.field}" ${dir}`;
|
|
145
|
+
})
|
|
146
|
+
.join(', ')}`;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Creates a DataStore backed by a DuckDB DatabasePool.
|
|
150
|
+
*
|
|
151
|
+
* @param pool - The DuckDB DatabasePool to use for queries.
|
|
152
|
+
* @returns A DataStore that translates CRUD operations to DuckDB-compatible SQL.
|
|
153
|
+
*/
|
|
154
|
+
export function createStore(pool) {
|
|
155
|
+
// Cache of "this table's JSON-typed columns" so rows read back through the
|
|
156
|
+
// CRUD helpers carry parsed values, not JSON text.
|
|
157
|
+
const jsonColumnsCache = new Map();
|
|
158
|
+
/**
|
|
159
|
+
* The JSON-typed columns of a table, lower-cased (cached). DuckDB's `JSON`
|
|
160
|
+
* type is an alias over VARCHAR, so a JSON column reads as its JSON text and
|
|
161
|
+
* the column's type is visible ONLY in `information_schema.columns` — never
|
|
162
|
+
* in the result metadata. Without this, a `meta` JSON column round-trips as
|
|
163
|
+
* a string through `create()`/`findMany()` on this bond while the same row
|
|
164
|
+
* parses on the postgresql/sqlite bonds.
|
|
165
|
+
*
|
|
166
|
+
* @param table - The table name to introspect.
|
|
167
|
+
* @returns Its JSON-typed column names, lower-cased.
|
|
168
|
+
*/
|
|
169
|
+
function jsonColumnsFor(table) {
|
|
170
|
+
const cached = jsonColumnsCache.get(table);
|
|
171
|
+
if (cached)
|
|
172
|
+
return cached;
|
|
173
|
+
const query = pool
|
|
174
|
+
.query("SELECT column_name FROM information_schema.columns WHERE table_name = ? AND data_type = 'JSON'", [table])
|
|
175
|
+
.then((result) => new Set(result.rows.map((row) => String(row.column_name).toLowerCase())))
|
|
176
|
+
.catch((error) => {
|
|
177
|
+
// A missing table has no JSON columns either; the caller's own query
|
|
178
|
+
// against it will name the real problem, so only log here.
|
|
179
|
+
getLogger().warn('DuckDB: could not list JSON columns', {
|
|
180
|
+
error,
|
|
181
|
+
table,
|
|
182
|
+
});
|
|
183
|
+
return new Set();
|
|
184
|
+
});
|
|
185
|
+
jsonColumnsCache.set(table, query);
|
|
186
|
+
return query;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Run a query for `table`, parsing its JSON columns back into values and
|
|
190
|
+
* naming the table in a column/table error (DuckDB's "does not exist" errors
|
|
191
|
+
* name neither — the same gap the sqlite store patches with `queryOn`).
|
|
192
|
+
*
|
|
193
|
+
* @param table - The table the statement is for.
|
|
194
|
+
* @param sql - The statement.
|
|
195
|
+
* @param values - Its parameters.
|
|
196
|
+
* @returns The query result with JSON columns parsed.
|
|
197
|
+
*/
|
|
198
|
+
async function queryOn(table, sql, values) {
|
|
199
|
+
let result;
|
|
200
|
+
try {
|
|
201
|
+
result = await pool.query(sql, values);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
if (error instanceof Error &&
|
|
205
|
+
/does not exist|referenced table .* not found|no matching entry/i.test(error.message) &&
|
|
206
|
+
!error.message.includes(`(table "${table}")`)) {
|
|
207
|
+
error.message = `${error.message} (table "${table}")`;
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
rows: parseJsonColumns(result.rows, await jsonColumnsFor(table)),
|
|
213
|
+
rowCount: result.rowCount,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
async findById(table, id) {
|
|
218
|
+
assertSafeIdentifier(table);
|
|
219
|
+
const result = await queryOn(table, `SELECT * FROM "${table}" WHERE "id" = ? LIMIT 1`, [
|
|
220
|
+
id,
|
|
221
|
+
]);
|
|
222
|
+
return result.rows[0] ?? null;
|
|
223
|
+
},
|
|
224
|
+
async findOne(table, where) {
|
|
225
|
+
assertSafeIdentifier(table);
|
|
226
|
+
assertWhereConditions(where, 'findOne');
|
|
227
|
+
const { clause, values } = buildWhere(where);
|
|
228
|
+
const result = await queryOn(table, `SELECT * FROM "${table}" ${clause} LIMIT 1`, values);
|
|
229
|
+
return result.rows[0] ?? null;
|
|
230
|
+
},
|
|
231
|
+
async findMany(table, options) {
|
|
232
|
+
assertSafeIdentifier(table);
|
|
233
|
+
assertFindManyOptions(options);
|
|
234
|
+
const { clause, values } = buildWhere(options?.where ?? []);
|
|
235
|
+
const selectFields = options?.select
|
|
236
|
+
?.map((s) => {
|
|
237
|
+
assertSafeIdentifier(s);
|
|
238
|
+
return `"${s}"`;
|
|
239
|
+
})
|
|
240
|
+
.join(', ') ?? '*';
|
|
241
|
+
const orderBy = buildOrderBy(options?.orderBy);
|
|
242
|
+
// Safety cap: if caller doesn't specify a limit, apply a default to prevent
|
|
243
|
+
// unbounded queries from returning millions of rows and exhausting memory.
|
|
244
|
+
const MAX_DEFAULT_LIMIT = 10_000;
|
|
245
|
+
const effectiveLimit = Math.max(0, Math.min(Number(options?.limit ?? MAX_DEFAULT_LIMIT), MAX_DEFAULT_LIMIT));
|
|
246
|
+
const limit = `LIMIT ${effectiveLimit}`;
|
|
247
|
+
// Validate offset as a non-negative integer to prevent SQL injection.
|
|
248
|
+
const safeOffset = options?.offset != null ? Math.max(0, Math.floor(Number(options.offset))) : 0;
|
|
249
|
+
const offset = safeOffset > 0 ? `OFFSET ${safeOffset}` : '';
|
|
250
|
+
const sql = `SELECT ${selectFields} FROM "${table}" ${clause} ${orderBy} ${limit} ${offset}`.trim();
|
|
251
|
+
const result = await queryOn(table, sql, values);
|
|
252
|
+
return result.rows;
|
|
253
|
+
},
|
|
254
|
+
async count(table, where) {
|
|
255
|
+
assertSafeIdentifier(table);
|
|
256
|
+
assertOptionalWhereConditions(where);
|
|
257
|
+
const { clause, values } = buildWhere(where ?? []);
|
|
258
|
+
const result = await queryOn(table, `SELECT COUNT(*) AS "count" FROM "${table}" ${clause}`, values);
|
|
259
|
+
return Number(result.rows[0]?.count ?? 0);
|
|
260
|
+
},
|
|
261
|
+
async create(table, data) {
|
|
262
|
+
assertSafeIdentifier(table);
|
|
263
|
+
// The documented bare-create pattern — create('table', { ...fields }) with
|
|
264
|
+
// no explicit id — relies on the row getting an id. A hand-written
|
|
265
|
+
// migration may declare `id uuid NOT NULL` with no DEFAULT, so generate
|
|
266
|
+
// one when the caller omits it (no-op when supplied; composite-PK join
|
|
267
|
+
// tables without an id column are handled by the engine's own error).
|
|
268
|
+
if (data.id == null) {
|
|
269
|
+
data = { id: randomUUID(), ...data };
|
|
270
|
+
}
|
|
271
|
+
const keys = Object.keys(data);
|
|
272
|
+
for (const k of keys)
|
|
273
|
+
assertSafeIdentifier(k);
|
|
274
|
+
const columns = keys.map((k) => `"${k}"`).join(', ');
|
|
275
|
+
const placeholders = keys.map(() => '?').join(', ');
|
|
276
|
+
const values = keys.map((k) => data[k]);
|
|
277
|
+
const result = await queryOn(table, `INSERT INTO "${table}" (${columns}) VALUES (${placeholders}) RETURNING *`, values);
|
|
278
|
+
return { data: result.rows[0] ?? null, affected: result.rowCount ?? 0 };
|
|
279
|
+
},
|
|
280
|
+
async updateById(table, id, data) {
|
|
281
|
+
assertSafeIdentifier(table);
|
|
282
|
+
const keys = Object.keys(data);
|
|
283
|
+
// No-op when there are no fields to update — return the existing row
|
|
284
|
+
// so callers see `affected: 1` for known-id reads (parity with the
|
|
285
|
+
// postgres/sqlite bonds). Issuing an UPDATE with an empty SET clause is
|
|
286
|
+
// a SQL syntax error.
|
|
287
|
+
if (keys.length === 0) {
|
|
288
|
+
const existing = await queryOn(table, `SELECT * FROM "${table}" WHERE "id" = ? LIMIT 1`, [id]);
|
|
289
|
+
return { data: existing.rows[0] ?? null, affected: existing.rows.length };
|
|
290
|
+
}
|
|
291
|
+
for (const k of keys)
|
|
292
|
+
assertSafeIdentifier(k);
|
|
293
|
+
const setClauses = keys.map((k) => `"${k}" = ?`).join(', ');
|
|
294
|
+
const values = [...keys.map((k) => data[k]), id];
|
|
295
|
+
const result = await queryOn(table, `UPDATE "${table}" SET ${setClauses} WHERE "id" = ? RETURNING *`, values);
|
|
296
|
+
return { data: result.rows[0] ?? null, affected: result.rowCount ?? 0 };
|
|
297
|
+
},
|
|
298
|
+
async updateMany(table, where, data) {
|
|
299
|
+
assertWhereConditions(where, 'updateMany');
|
|
300
|
+
// Refuse an empty WHERE — parity with the postgres/sqlite bonds; an empty
|
|
301
|
+
// `where` would become an UNSCOPED full-table UPDATE.
|
|
302
|
+
if (where.length === 0) {
|
|
303
|
+
throw new Error('updateMany requires at least one WHERE condition to prevent accidental full-table updates');
|
|
304
|
+
}
|
|
305
|
+
assertSafeIdentifier(table);
|
|
306
|
+
const keys = Object.keys(data);
|
|
307
|
+
for (const k of keys)
|
|
308
|
+
assertSafeIdentifier(k);
|
|
309
|
+
const setClauses = keys.map((k) => `"${k}" = ?`).join(', ');
|
|
310
|
+
const setValues = keys.map((k) => data[k]);
|
|
311
|
+
const { clause, values: whereValues } = buildWhere(where);
|
|
312
|
+
const values = [...setValues, ...whereValues];
|
|
313
|
+
const result = await queryOn(table, `UPDATE "${table}" SET ${setClauses} ${clause}`, values);
|
|
314
|
+
return { data: null, affected: result.rowCount ?? 0 };
|
|
315
|
+
},
|
|
316
|
+
async deleteById(table, id) {
|
|
317
|
+
assertSafeIdentifier(table);
|
|
318
|
+
const result = await queryOn(table, `DELETE FROM "${table}" WHERE "id" = ?`, [id]);
|
|
319
|
+
return { data: null, affected: result.rowCount ?? 0 };
|
|
320
|
+
},
|
|
321
|
+
async deleteMany(table, where) {
|
|
322
|
+
assertWhereConditions(where, 'deleteMany');
|
|
323
|
+
// Refuse an empty WHERE — parity with the postgres/sqlite bonds (else an
|
|
324
|
+
// empty `where` becomes an UNSCOPED full-table DELETE).
|
|
325
|
+
if (where.length === 0) {
|
|
326
|
+
throw new Error('deleteMany requires at least one WHERE condition to prevent accidental full-table deletes');
|
|
327
|
+
}
|
|
328
|
+
assertSafeIdentifier(table);
|
|
329
|
+
const { clause, values } = buildWhere(where);
|
|
330
|
+
const result = await queryOn(table, `DELETE FROM "${table}" ${clause}`, values);
|
|
331
|
+
return { data: null, affected: result.rowCount ?? 0 };
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the DuckDB database provider.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for the DuckDB database provider.
|
|
8
|
+
*/
|
|
9
|
+
export interface DuckdbConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Path of the DuckDB database file — created on first open — or `':memory:'`
|
|
12
|
+
* for a database that lives only for the process's lifetime. Defaults to the
|
|
13
|
+
* `DUCKDB_PATH` env var, then `'./data/app.duckdb'`.
|
|
14
|
+
*/
|
|
15
|
+
path?: string;
|
|
16
|
+
/**
|
|
17
|
+
* DuckDB `memory_limit` for the instance. DuckDB's own default is 80% of
|
|
18
|
+
* system RAM, which on a shared VM lets one query evict everything else, so
|
|
19
|
+
* this bond caps it at `'1GB'` by default. Accepts DuckDB size strings
|
|
20
|
+
* (`'512MB'`, `'2GB'`) or a plain byte count as a string. Also settable via
|
|
21
|
+
* the `DUCKDB_MEMORY_LIMIT` env var.
|
|
22
|
+
*/
|
|
23
|
+
memoryLimit?: string;
|
|
24
|
+
/**
|
|
25
|
+
* DuckDB `threads` for the instance. DuckDB's own default is every CPU core,
|
|
26
|
+
* which oversubscribes a shared host, so this bond caps it at `4` by default.
|
|
27
|
+
* Also settable via the `DUCKDB_THREADS` env var.
|
|
28
|
+
*/
|
|
29
|
+
threads?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Open the database read-only (`access_mode = 'READ_ONLY'`). Multiple
|
|
32
|
+
* processes may read one file concurrently; only read-write mode is
|
|
33
|
+
* exclusive, so a read-only pool never fights an app holding the write lock.
|
|
34
|
+
*/
|
|
35
|
+
readOnly?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Extra DuckDB settings handed to `DuckDBInstance.fromCache(path, options)`
|
|
38
|
+
* as-is (a flat `Record<string, string>` of setting name → value). Entries
|
|
39
|
+
* here win over the `memoryLimit`/`threads`/`readOnly` fields.
|
|
40
|
+
*/
|
|
41
|
+
config?: Record<string, string>;
|
|
42
|
+
/**
|
|
43
|
+
* How long (ms) a query, `connect()` or `transaction()` waits for the single
|
|
44
|
+
* connection before failing with an error that says who is holding it.
|
|
45
|
+
* Defaults to 30 000.
|
|
46
|
+
*/
|
|
47
|
+
acquireTimeoutMillis?: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Environment variables consumed by the DuckDB database provider.
|
|
51
|
+
*/
|
|
52
|
+
export interface ProcessEnv {
|
|
53
|
+
DUCKDB_PATH?: string;
|
|
54
|
+
DUCKDB_MEMORY_LIMIT?: string;
|
|
55
|
+
DUCKDB_THREADS?: string;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for DuckDB operations.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { type DuckDBValue } from '@duckdb/node-api';
|
|
7
|
+
import type { SqlCompatibilityIssue, SqlFeature } from '@molecule/api-database/sql-compatibility.js';
|
|
8
|
+
/**
|
|
9
|
+
* Options for {@link translateDdlToDuckdb}.
|
|
10
|
+
*/
|
|
11
|
+
export interface TranslateDdlOptions {
|
|
12
|
+
/** File name the SQL came from; copied into an unsupported-construct error. */
|
|
13
|
+
source?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The error this bond's translator throws when a migration needs a construct
|
|
17
|
+
* DuckDB cannot honour. Named and typed so a caller can tell a genuine dialect
|
|
18
|
+
* limit apart from malformed SQL, and report it without a migration run.
|
|
19
|
+
*
|
|
20
|
+
* The diagnostic itself is described by the core TYPE contract
|
|
21
|
+
* ({@link SqlCompatibilityIssue}); this class is the bond's own error
|
|
22
|
+
* construction, next to the translation that raises it.
|
|
23
|
+
*/
|
|
24
|
+
export declare class UnsupportedSqlFeatureError extends Error {
|
|
25
|
+
/** The construct's feature. */
|
|
26
|
+
readonly feature: SqlFeature;
|
|
27
|
+
/** The exact construct found in the migration. */
|
|
28
|
+
readonly detail: string;
|
|
29
|
+
/** Where the statement came from, when known. */
|
|
30
|
+
readonly source?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Build the error from a compatibility diagnostic, copying its message and fields.
|
|
33
|
+
*
|
|
34
|
+
* @param issue - The diagnostic being refused.
|
|
35
|
+
*/
|
|
36
|
+
constructor(issue: SqlCompatibilityIssue);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Translate PostgreSQL-dialect DDL to DuckDB-compatible DDL at migration time.
|
|
40
|
+
*
|
|
41
|
+
* Resource/template setup migrations are authored in PostgreSQL dialect. DuckDB
|
|
42
|
+
* 2.0 already speaks most of it natively — `uuid`/`timestamptz`/`bytea`/`text[]`
|
|
43
|
+
* type names, `gen_random_uuid()`, `now()`, `CURRENT_TIMESTAMP`, `::` casts,
|
|
44
|
+
* `ADD COLUMN IF NOT EXISTS`, `DROP COLUMN IF EXISTS`, `ON CONFLICT`, `ILIKE` —
|
|
45
|
+
* all verified against 2.0.0-r.1. The constructs it rejects are rewritten here,
|
|
46
|
+
* and only where the rewrite preserves behaviour:
|
|
47
|
+
*
|
|
48
|
+
* - `jsonb` → `json`, in both the column-type position and the `::jsonb` cast
|
|
49
|
+
* (DuckDB has a `JSON` type, stored as VARCHAR; there is no `jsonb`).
|
|
50
|
+
* - `USING <method>` on a `CREATE INDEX` (`gin`/`btree`/`hash`/`gist`) →
|
|
51
|
+
* removed: DuckDB indexes have no access method.
|
|
52
|
+
* - A partial index gets no `WHERE` support in DuckDB. A non-unique partial
|
|
53
|
+
* index becomes a full index on the same columns — the predicate only narrows
|
|
54
|
+
* which rows the index covers, never a query's result. A UNIQUE partial index
|
|
55
|
+
* becomes a UNIQUE index over `CASE WHEN <predicate> THEN <column> END` per
|
|
56
|
+
* column: rows outside the predicate get NULL keys (distinct in DuckDB), so
|
|
57
|
+
* uniqueness applies to exactly the rows PostgreSQL would constrain.
|
|
58
|
+
*
|
|
59
|
+
* What it does NOT rewrite is an `ON DELETE`/`ON UPDATE` referential action
|
|
60
|
+
* (`CASCADE`, `SET NULL`, `SET DEFAULT`): DuckDB rejects the clause at parse
|
|
61
|
+
* time and has no triggers, so dropping it would turn a cascading delete into a
|
|
62
|
+
* refusing one. Such SQL throws {@link UnsupportedSqlFeatureError}, naming the
|
|
63
|
+
* construct — see {@link sqlCapabilities}. The compatibility preflight in the
|
|
64
|
+
* generator rejects those resource/provider combinations before an app exists;
|
|
65
|
+
* this throw is the last line of defence for SQL that reaches the runner another
|
|
66
|
+
* way.
|
|
67
|
+
*
|
|
68
|
+
* Everything else passes through untouched — unlike the sqlite bond, no
|
|
69
|
+
* placeholder conversion or timestamp-default rewriting happens here.
|
|
70
|
+
*
|
|
71
|
+
* @param sql - Raw migration SQL, possibly in Postgres dialect.
|
|
72
|
+
* @param options - The source file name used in an unsupported-construct error.
|
|
73
|
+
* @returns DuckDB-compatible SQL.
|
|
74
|
+
* @throws {UnsupportedSqlFeatureError} When the SQL needs a construct DuckDB
|
|
75
|
+
* cannot reproduce without changing its behaviour.
|
|
76
|
+
*/
|
|
77
|
+
export declare const translateDdlToDuckdb: (sql: string, options?: TranslateDdlOptions) => string;
|
|
78
|
+
/**
|
|
79
|
+
* Coerce a JS value into something `@duckdb/node-api` can bind. The client
|
|
80
|
+
* infers a parameter's type from the VALUE and only understands `null`,
|
|
81
|
+
* booleans, numbers, bigints and strings — anything else fails at bind time
|
|
82
|
+
* with `Cannot create values of type ANY. Specify a specific type.`, which
|
|
83
|
+
* would crash every `create()`/`update()` that passes a `Date`, a boolean-ish
|
|
84
|
+
* object or a jsonb value. Strings cast on the way in: DuckDB implicitly casts
|
|
85
|
+
* a bound VARCHAR to UUID/TIMESTAMPTZ/DECIMAL/JSON/LIST parameters, verified
|
|
86
|
+
* against 2.0.0-r.1.
|
|
87
|
+
*
|
|
88
|
+
* @param value - The value to bind.
|
|
89
|
+
* @returns A `@duckdb/node-api`-bindable value.
|
|
90
|
+
*/
|
|
91
|
+
export declare const coerceDuckdbParam: (value: unknown) => DuckDBValue;
|
|
92
|
+
/**
|
|
93
|
+
* Normalize DuckDB result rows to the JS shapes the other database bonds hand
|
|
94
|
+
* back. DuckDB's JS conversion returns a `bigint` for every 64+-bit integer —
|
|
95
|
+
* `COUNT(*)`, `BIGINT` and `HUGEINT` columns — and `JSON.stringify` THROWS on a
|
|
96
|
+
* bigint, so a handler that serializes a row with `COUNT(*)` in it would 500.
|
|
97
|
+
* A bigint within `Number.MAX_SAFE_INTEGER` becomes a JS number (matching the
|
|
98
|
+
* sqlite bond); a larger one becomes its decimal string (matching node-postgres
|
|
99
|
+
* `int8AsString`). Mutates rows in place for speed.
|
|
100
|
+
*
|
|
101
|
+
* @param rows - Row objects from `result.getRowObjectsJS()`.
|
|
102
|
+
* @returns The normalized rows.
|
|
103
|
+
*/
|
|
104
|
+
export declare const normalizeDuckdbRows: <T>(rows: Record<string, unknown>[]) => T[];
|
|
105
|
+
/**
|
|
106
|
+
* Parse the JSON-typed columns of a table's rows into values, matching what
|
|
107
|
+
* the postgresql/sqlite bonds hand back from a `json`/`jsonb` column. DuckDB's
|
|
108
|
+
* `JSON` type is an alias over VARCHAR, so a JSON column always READS as its
|
|
109
|
+
* JSON text — the column's type is only visible in
|
|
110
|
+
* `information_schema.columns.data_type`, never in the result metadata, which
|
|
111
|
+
* is why this runs in the DataStore (it knows the table) and not in the pool.
|
|
112
|
+
* A value that is not valid JSON text is left as the raw string.
|
|
113
|
+
*
|
|
114
|
+
* @param rows - Row objects.
|
|
115
|
+
* @param jsonColumns - The table's JSON-typed column names, lower-cased.
|
|
116
|
+
* @returns The rows with JSON columns parsed.
|
|
117
|
+
*/
|
|
118
|
+
export declare const parseJsonColumns: <T>(rows: Record<string, unknown>[], jsonColumns: Set<string>) => T[];
|
|
119
|
+
//# sourceMappingURL=utilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,6CAA6C,CAAA;AAUpG;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;GAQG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,+BAA+B;IAC/B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;IAC5B,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IAExB;;;;OAIG;gBACS,KAAK,EAAE,qBAAqB;CAOzC;AAqCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,EAAE,UAAS,mBAAwB,KAAG,MAarF,CAAA;AAqCD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,WAiBlD,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAG,CAAC,EAazE,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAChC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,aAAa,GAAG,CAAC,MAAM,CAAC,KACvB,CAAC,EAgBH,CAAA"}
|