turbine-orm 0.9.1 → 0.10.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 +35 -12
- package/dist/adapters/cockroachdb.d.ts +40 -0
- package/dist/adapters/cockroachdb.js +172 -0
- package/dist/adapters/index.d.ts +107 -0
- package/dist/adapters/index.js +83 -0
- package/dist/adapters/yugabytedb.d.ts +52 -0
- package/dist/adapters/yugabytedb.js +156 -0
- package/dist/cjs/adapters/cockroachdb.js +174 -0
- package/dist/cjs/adapters/index.js +87 -0
- package/dist/cjs/adapters/yugabytedb.js +158 -0
- package/dist/cjs/cli/index.js +2 -1
- package/dist/cjs/cli/migrate.js +18 -12
- package/dist/cjs/cli/studio.js +12 -11
- package/dist/cjs/client.js +3 -3
- package/dist/cjs/generate.js +8 -1
- package/dist/cjs/index.js +10 -3
- package/dist/cjs/introspect.js +46 -18
- package/dist/cjs/query/builder.js +2658 -0
- package/dist/cjs/query/index.js +21 -0
- package/dist/cjs/query/types.js +7 -0
- package/dist/cjs/query/utils.js +140 -0
- package/dist/cjs/schema-sql.js +26 -26
- package/dist/cjs/schema.js +8 -0
- package/dist/cli/config.d.ts +11 -0
- package/dist/cli/index.js +2 -1
- package/dist/cli/migrate.d.ts +3 -0
- package/dist/cli/migrate.js +17 -11
- package/dist/cli/studio.d.ts +4 -0
- package/dist/cli/studio.js +6 -5
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/generate.js +8 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -2
- package/dist/introspect.js +46 -18
- package/dist/pipeline-submittable.d.ts +1 -1
- package/dist/pipeline.d.ts +1 -1
- package/dist/query/builder.d.ts +498 -0
- package/dist/query/builder.js +2655 -0
- package/dist/query/index.d.ts +13 -0
- package/dist/query/index.js +10 -0
- package/dist/query/types.d.ts +365 -0
- package/dist/query/types.js +7 -0
- package/dist/query/utils.d.ts +68 -0
- package/dist/query/utils.js +131 -0
- package/dist/schema-sql.js +1 -1
- package/dist/schema.d.ts +6 -4
- package/dist/schema.js +7 -0
- package/package.json +14 -2
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm — Query builder barrel
|
|
4
|
+
*
|
|
5
|
+
* Re-exports every public symbol from the query submodules so that
|
|
6
|
+
* `import { … } from './query/index.js'` is a drop-in replacement for the
|
|
7
|
+
* former monolithic `import { … } from './query.js'`.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.QueryInterface = exports.sqlToPreparedName = exports.quoteIdent = exports.OPERATOR_KEYS = exports.LRUCache = exports.fnv1a64Hex = exports.escSingleQuote = exports.escapeLike = exports.buildCorrelation = void 0;
|
|
11
|
+
var utils_js_1 = require("./utils.js");
|
|
12
|
+
Object.defineProperty(exports, "buildCorrelation", { enumerable: true, get: function () { return utils_js_1.buildCorrelation; } });
|
|
13
|
+
Object.defineProperty(exports, "escapeLike", { enumerable: true, get: function () { return utils_js_1.escapeLike; } });
|
|
14
|
+
Object.defineProperty(exports, "escSingleQuote", { enumerable: true, get: function () { return utils_js_1.escSingleQuote; } });
|
|
15
|
+
Object.defineProperty(exports, "fnv1a64Hex", { enumerable: true, get: function () { return utils_js_1.fnv1a64Hex; } });
|
|
16
|
+
Object.defineProperty(exports, "LRUCache", { enumerable: true, get: function () { return utils_js_1.LRUCache; } });
|
|
17
|
+
Object.defineProperty(exports, "OPERATOR_KEYS", { enumerable: true, get: function () { return utils_js_1.OPERATOR_KEYS; } });
|
|
18
|
+
Object.defineProperty(exports, "quoteIdent", { enumerable: true, get: function () { return utils_js_1.quoteIdent; } });
|
|
19
|
+
Object.defineProperty(exports, "sqlToPreparedName", { enumerable: true, get: function () { return utils_js_1.sqlToPreparedName; } });
|
|
20
|
+
var builder_js_1 = require("./builder.js");
|
|
21
|
+
Object.defineProperty(exports, "QueryInterface", { enumerable: true, get: function () { return builder_js_1.QueryInterface; } });
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm — Query builder utilities
|
|
4
|
+
*
|
|
5
|
+
* Standalone utility functions and classes used by the query builder.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.OPERATOR_KEYS = exports.LRUCache = void 0;
|
|
9
|
+
exports.quoteIdent = quoteIdent;
|
|
10
|
+
exports.escSingleQuote = escSingleQuote;
|
|
11
|
+
exports.escapeLike = escapeLike;
|
|
12
|
+
exports.fnv1a64Hex = fnv1a64Hex;
|
|
13
|
+
exports.sqlToPreparedName = sqlToPreparedName;
|
|
14
|
+
exports.buildCorrelation = buildCorrelation;
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Identifier quoting — prevents SQL injection via table/column names
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/**
|
|
19
|
+
* Quote a SQL identifier (table name, column name) using Postgres double-quote
|
|
20
|
+
* rules: wrap in double quotes, escape internal double quotes by doubling them.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* quoteIdent('users') → '"users"'
|
|
24
|
+
* quoteIdent('my"table') → '"my""table"'
|
|
25
|
+
* quoteIdent('user name') → '"user name"'
|
|
26
|
+
*/
|
|
27
|
+
function quoteIdent(name) {
|
|
28
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Escape single quotes for use as string keys in json_build_object().
|
|
32
|
+
* Doubles single quotes per SQL quoting rules.
|
|
33
|
+
*/
|
|
34
|
+
function escSingleQuote(s) {
|
|
35
|
+
return s.replace(/'/g, "''");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Escape LIKE pattern metacharacters: %, _, and \.
|
|
39
|
+
* Must be used with `ESCAPE '\'` in the LIKE clause.
|
|
40
|
+
*/
|
|
41
|
+
function escapeLike(value) {
|
|
42
|
+
return value.replace(/\\/g, '\\\\').replace(/%/g, '\\%').replace(/_/g, '\\_');
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// LRU cache — bounded SQL template cache to prevent memory leaks
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
/**
|
|
48
|
+
* Simple LRU (Least Recently Used) cache with a fixed maximum size.
|
|
49
|
+
* When the cache exceeds maxSize, the oldest (least recently used) entry is evicted.
|
|
50
|
+
* Uses Map insertion order for O(1) eviction.
|
|
51
|
+
*/
|
|
52
|
+
class LRUCache {
|
|
53
|
+
maxSize;
|
|
54
|
+
cache = new Map();
|
|
55
|
+
constructor(maxSize) {
|
|
56
|
+
this.maxSize = maxSize;
|
|
57
|
+
}
|
|
58
|
+
get(key) {
|
|
59
|
+
const value = this.cache.get(key);
|
|
60
|
+
if (value !== undefined) {
|
|
61
|
+
// Move to end (most recently used)
|
|
62
|
+
this.cache.delete(key);
|
|
63
|
+
this.cache.set(key, value);
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
set(key, value) {
|
|
68
|
+
if (this.cache.has(key)) {
|
|
69
|
+
this.cache.delete(key);
|
|
70
|
+
}
|
|
71
|
+
else if (this.cache.size >= this.maxSize) {
|
|
72
|
+
// Delete oldest (first) entry
|
|
73
|
+
const firstKey = this.cache.keys().next().value;
|
|
74
|
+
if (firstKey !== undefined)
|
|
75
|
+
this.cache.delete(firstKey);
|
|
76
|
+
}
|
|
77
|
+
this.cache.set(key, value);
|
|
78
|
+
}
|
|
79
|
+
get size() {
|
|
80
|
+
return this.cache.size;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.LRUCache = LRUCache;
|
|
84
|
+
/**
|
|
85
|
+
* FNV-1a 64-bit hash returning 16 lowercase hex chars.
|
|
86
|
+
* Single-loop string iteration. Uses BigInt for 64-bit math.
|
|
87
|
+
*
|
|
88
|
+
* @internal Exported for testing only.
|
|
89
|
+
*/
|
|
90
|
+
function fnv1a64Hex(s) {
|
|
91
|
+
// FNV-1a offset basis and prime for 64-bit
|
|
92
|
+
let hash = 0xcbf29ce484222325n;
|
|
93
|
+
const prime = 0x100000001b3n;
|
|
94
|
+
const mask = 0xffffffffffffffffn; // 64-bit mask
|
|
95
|
+
for (let i = 0; i < s.length; i++) {
|
|
96
|
+
hash ^= BigInt(s.charCodeAt(i));
|
|
97
|
+
hash = (hash * prime) & mask;
|
|
98
|
+
}
|
|
99
|
+
return hash.toString(16).padStart(16, '0');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Derive a prepared-statement name from a SQL string.
|
|
103
|
+
* Format: `t_<16hex>` — always 18 chars, well under NAMEDATALEN (63).
|
|
104
|
+
*
|
|
105
|
+
* @internal Exported for testing only.
|
|
106
|
+
*/
|
|
107
|
+
function sqlToPreparedName(sql) {
|
|
108
|
+
return `t_${fnv1a64Hex(sql)}`;
|
|
109
|
+
}
|
|
110
|
+
/** Known operator keys — used to detect operator objects vs plain values */
|
|
111
|
+
exports.OPERATOR_KEYS = new Set([
|
|
112
|
+
'gt',
|
|
113
|
+
'gte',
|
|
114
|
+
'lt',
|
|
115
|
+
'lte',
|
|
116
|
+
'not',
|
|
117
|
+
'in',
|
|
118
|
+
'notIn',
|
|
119
|
+
'contains',
|
|
120
|
+
'startsWith',
|
|
121
|
+
'endsWith',
|
|
122
|
+
'mode',
|
|
123
|
+
]);
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Composite key correlation helper
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
/**
|
|
128
|
+
* Build a correlation clause joining columns between two table references.
|
|
129
|
+
* Handles both single-column (string) and multi-column (string[]) foreign keys.
|
|
130
|
+
*
|
|
131
|
+
* For single-column: `"alias"."col" = "parent"."col"`
|
|
132
|
+
* For multi-column: `"alias"."col_a" = "parent"."ref_a" AND "alias"."col_b" = "parent"."ref_b"`
|
|
133
|
+
*/
|
|
134
|
+
function buildCorrelation(leftRef, leftColumns, rightRef, rightColumns) {
|
|
135
|
+
const leftCols = Array.isArray(leftColumns) ? leftColumns : [leftColumns];
|
|
136
|
+
const rightCols = Array.isArray(rightColumns) ? rightColumns : [rightColumns];
|
|
137
|
+
return leftCols
|
|
138
|
+
.map((col, i) => `${leftRef}.${quoteIdent(col)} = ${rightRef}.${quoteIdent(rightCols[i])}`)
|
|
139
|
+
.join(' AND ');
|
|
140
|
+
}
|
package/dist/cjs/schema-sql.js
CHANGED
|
@@ -14,7 +14,7 @@ exports.schemaDiff = schemaDiff;
|
|
|
14
14
|
exports.schemaPush = schemaPush;
|
|
15
15
|
exports.schemaToSQLString = schemaToSQLString;
|
|
16
16
|
const pg_1 = __importDefault(require("pg"));
|
|
17
|
-
const
|
|
17
|
+
const index_js_1 = require("./query/index.js");
|
|
18
18
|
const schema_js_1 = require("./schema.js");
|
|
19
19
|
// ---------------------------------------------------------------------------
|
|
20
20
|
// SQL Generation — SchemaDef → CREATE TABLE statements
|
|
@@ -143,18 +143,18 @@ function generateCreateTable(table, resolveRef) {
|
|
|
143
143
|
}
|
|
144
144
|
// Append a table-level PRIMARY KEY constraint when a composite PK is set.
|
|
145
145
|
if (compositePk) {
|
|
146
|
-
const cols = compositePk.map((c) => (0,
|
|
146
|
+
const cols = compositePk.map((c) => (0, index_js_1.quoteIdent)((0, schema_js_1.camelToSnake)(c))).join(', ');
|
|
147
147
|
columnDefs.push(`PRIMARY KEY (${cols})`);
|
|
148
148
|
}
|
|
149
149
|
const body = columnDefs.map((d) => ` ${d}`).join(',\n');
|
|
150
|
-
return `CREATE TABLE ${(0,
|
|
150
|
+
return `CREATE TABLE ${(0, index_js_1.quoteIdent)(tableName)} (\n${body}\n);`;
|
|
151
151
|
}
|
|
152
152
|
/**
|
|
153
153
|
* Generate a single column definition line (e.g. "id BIGSERIAL PRIMARY KEY").
|
|
154
154
|
*/
|
|
155
155
|
function generateColumnDef(fieldName, config, resolveRef) {
|
|
156
156
|
const snakeName = (0, schema_js_1.camelToSnake)(fieldName);
|
|
157
|
-
const parts = [(0,
|
|
157
|
+
const parts = [(0, index_js_1.quoteIdent)(snakeName)];
|
|
158
158
|
// Type
|
|
159
159
|
if (config.type === 'VARCHAR' && config.maxLength != null) {
|
|
160
160
|
parts.push(`VARCHAR(${config.maxLength})`);
|
|
@@ -193,7 +193,7 @@ function generateColumnDef(fieldName, config, resolveRef) {
|
|
|
193
193
|
if (refParts.length === 2) {
|
|
194
194
|
const rawTable = refParts[0];
|
|
195
195
|
const refTable = resolveRef ? resolveRef(rawTable) : rawTable;
|
|
196
|
-
parts.push(`REFERENCES ${(0,
|
|
196
|
+
parts.push(`REFERENCES ${(0, index_js_1.quoteIdent)(refTable)}(${(0, index_js_1.quoteIdent)(refParts[1])})`);
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
return parts.join(' ');
|
|
@@ -242,7 +242,7 @@ function generateForeignKeyIndexes(table) {
|
|
|
242
242
|
if (config.referencesTarget) {
|
|
243
243
|
const snakeName = (0, schema_js_1.camelToSnake)(fieldName);
|
|
244
244
|
const indexName = `idx_${table.name}_${snakeName}`;
|
|
245
|
-
indexes.push(`CREATE INDEX ${(0,
|
|
245
|
+
indexes.push(`CREATE INDEX ${(0, index_js_1.quoteIdent)(indexName)} ON ${(0, index_js_1.quoteIdent)(table.name)}(${(0, index_js_1.quoteIdent)(snakeName)});`);
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
return indexes;
|
|
@@ -317,7 +317,7 @@ async function schemaDiff(schema, connectionString) {
|
|
|
317
317
|
const fkIndexes = generateForeignKeyIndexes(tableDef);
|
|
318
318
|
result.statements.push(...fkIndexes);
|
|
319
319
|
// Reverse: DROP TABLE (with indexes — they drop automatically)
|
|
320
|
-
result.reverseStatements.unshift(`DROP TABLE IF EXISTS ${(0,
|
|
320
|
+
result.reverseStatements.unshift(`DROP TABLE IF EXISTS ${(0, index_js_1.quoteIdent)(ddlName)} CASCADE;`);
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
323
|
// Tables to drop (in DB but not in schema)
|
|
@@ -342,8 +342,8 @@ async function schemaDiff(schema, connectionString) {
|
|
|
342
342
|
if (!dbCol) {
|
|
343
343
|
// Column exists in schema but not in DB — ADD COLUMN
|
|
344
344
|
const colDef = generateColumnDef(fieldName, config, resolveRef);
|
|
345
|
-
const sql = `ALTER TABLE ${(0,
|
|
346
|
-
const reverseSql = `ALTER TABLE ${(0,
|
|
345
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ADD COLUMN ${colDef};`;
|
|
346
|
+
const reverseSql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} DROP COLUMN ${(0, index_js_1.quoteIdent)(snakeName)};`;
|
|
347
347
|
alterDef.columns.push({ column: snakeName, action: 'add', sql, reverseSql });
|
|
348
348
|
result.statements.push(sql);
|
|
349
349
|
result.reverseStatements.unshift(reverseSql);
|
|
@@ -354,8 +354,8 @@ async function schemaDiff(schema, connectionString) {
|
|
|
354
354
|
if (expectedUdt && dbCol.udtName !== expectedUdt) {
|
|
355
355
|
const sqlType = config.type === 'VARCHAR' && config.maxLength ? `VARCHAR(${config.maxLength})` : config.type;
|
|
356
356
|
const oldSqlType = udtToSqlType(dbCol.udtName, dbCol.maxLength);
|
|
357
|
-
const sql = `ALTER TABLE ${(0,
|
|
358
|
-
const reverseSql = `ALTER TABLE ${(0,
|
|
357
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} TYPE ${sqlType} USING ${(0, index_js_1.quoteIdent)(snakeName)}::${sqlType};`;
|
|
358
|
+
const reverseSql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} TYPE ${oldSqlType} USING ${(0, index_js_1.quoteIdent)(snakeName)}::${oldSqlType};`;
|
|
359
359
|
alterDef.columns.push({ column: snakeName, action: 'alter_type', sql, reverseSql });
|
|
360
360
|
result.statements.push(sql);
|
|
361
361
|
result.reverseStatements.unshift(reverseSql);
|
|
@@ -364,15 +364,15 @@ async function schemaDiff(schema, connectionString) {
|
|
|
364
364
|
const shouldBeNotNull = config.isNotNull || config.isPrimaryKey || config.type === 'BIGSERIAL';
|
|
365
365
|
const isCurrentlyNullable = dbCol.isNullable;
|
|
366
366
|
if (shouldBeNotNull && isCurrentlyNullable && !config.isNullable) {
|
|
367
|
-
const sql = `ALTER TABLE ${(0,
|
|
368
|
-
const reverseSql = `ALTER TABLE ${(0,
|
|
367
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} SET NOT NULL;`;
|
|
368
|
+
const reverseSql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} DROP NOT NULL;`;
|
|
369
369
|
alterDef.columns.push({ column: snakeName, action: 'set_not_null', sql, reverseSql });
|
|
370
370
|
result.statements.push(sql);
|
|
371
371
|
result.reverseStatements.unshift(reverseSql);
|
|
372
372
|
}
|
|
373
373
|
else if (!shouldBeNotNull && !isCurrentlyNullable && config.isNullable) {
|
|
374
|
-
const sql = `ALTER TABLE ${(0,
|
|
375
|
-
const reverseSql = `ALTER TABLE ${(0,
|
|
374
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} DROP NOT NULL;`;
|
|
375
|
+
const reverseSql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} SET NOT NULL;`;
|
|
376
376
|
alterDef.columns.push({ column: snakeName, action: 'drop_not_null', sql, reverseSql });
|
|
377
377
|
result.statements.push(sql);
|
|
378
378
|
result.reverseStatements.unshift(reverseSql);
|
|
@@ -384,16 +384,16 @@ async function schemaDiff(schema, connectionString) {
|
|
|
384
384
|
const dbDefault = dbCol.columnDefault;
|
|
385
385
|
if (schemaDefault && !dbDefault) {
|
|
386
386
|
// Schema has default, DB doesn't
|
|
387
|
-
const sql = `ALTER TABLE ${(0,
|
|
388
|
-
const reverseSql = `ALTER TABLE ${(0,
|
|
387
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} SET DEFAULT ${schemaDefault};`;
|
|
388
|
+
const reverseSql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} DROP DEFAULT;`;
|
|
389
389
|
alterDef.columns.push({ column: snakeName, action: 'set_default', sql, reverseSql });
|
|
390
390
|
result.statements.push(sql);
|
|
391
391
|
result.reverseStatements.unshift(reverseSql);
|
|
392
392
|
}
|
|
393
393
|
else if (!schemaDefault && dbDefault && !isSequenceDefault(dbDefault)) {
|
|
394
394
|
// DB has a non-sequence default, schema doesn't
|
|
395
|
-
const sql = `ALTER TABLE ${(0,
|
|
396
|
-
const reverseSql = `ALTER TABLE ${(0,
|
|
395
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} DROP DEFAULT;`;
|
|
396
|
+
const reverseSql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} SET DEFAULT ${dbDefault};`;
|
|
397
397
|
alterDef.columns.push({ column: snakeName, action: 'drop_default', sql, reverseSql });
|
|
398
398
|
result.statements.push(sql);
|
|
399
399
|
result.reverseStatements.unshift(reverseSql);
|
|
@@ -403,8 +403,8 @@ async function schemaDiff(schema, connectionString) {
|
|
|
403
403
|
!isSequenceDefault(dbDefault) &&
|
|
404
404
|
!defaultsMatch(schemaDefault, dbDefault)) {
|
|
405
405
|
// Both have defaults but they differ
|
|
406
|
-
const sql = `ALTER TABLE ${(0,
|
|
407
|
-
const reverseSql = `ALTER TABLE ${(0,
|
|
406
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} SET DEFAULT ${schemaDefault};`;
|
|
407
|
+
const reverseSql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ALTER COLUMN ${(0, index_js_1.quoteIdent)(snakeName)} SET DEFAULT ${dbDefault};`;
|
|
408
408
|
alterDef.columns.push({ column: snakeName, action: 'set_default', sql, reverseSql });
|
|
409
409
|
result.statements.push(sql);
|
|
410
410
|
result.reverseStatements.unshift(reverseSql);
|
|
@@ -416,16 +416,16 @@ async function schemaDiff(schema, connectionString) {
|
|
|
416
416
|
const wantsUnique = config.isUnique === true;
|
|
417
417
|
if (wantsUnique && !hasDbUnique) {
|
|
418
418
|
const constraintName = `${tableName}_${snakeName}_key`;
|
|
419
|
-
const sql = `ALTER TABLE ${(0,
|
|
420
|
-
const reverseSql = `ALTER TABLE ${(0,
|
|
419
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ADD CONSTRAINT ${(0, index_js_1.quoteIdent)(constraintName)} UNIQUE (${(0, index_js_1.quoteIdent)(snakeName)});`;
|
|
420
|
+
const reverseSql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} DROP CONSTRAINT ${(0, index_js_1.quoteIdent)(constraintName)};`;
|
|
421
421
|
alterDef.columns.push({ column: snakeName, action: 'add_unique', sql, reverseSql });
|
|
422
422
|
result.statements.push(sql);
|
|
423
423
|
result.reverseStatements.unshift(reverseSql);
|
|
424
424
|
}
|
|
425
425
|
else if (!wantsUnique && hasDbUnique) {
|
|
426
426
|
const constraintName = tableUniques[snakeName];
|
|
427
|
-
const sql = `ALTER TABLE ${(0,
|
|
428
|
-
const reverseSql = `ALTER TABLE ${(0,
|
|
427
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} DROP CONSTRAINT ${(0, index_js_1.quoteIdent)(constraintName)};`;
|
|
428
|
+
const reverseSql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} ADD CONSTRAINT ${(0, index_js_1.quoteIdent)(constraintName)} UNIQUE (${(0, index_js_1.quoteIdent)(snakeName)});`;
|
|
429
429
|
alterDef.columns.push({ column: snakeName, action: 'drop_unique', sql, reverseSql });
|
|
430
430
|
result.statements.push(sql);
|
|
431
431
|
result.reverseStatements.unshift(reverseSql);
|
|
@@ -436,7 +436,7 @@ async function schemaDiff(schema, connectionString) {
|
|
|
436
436
|
for (const dbColName of Object.keys(dbCols)) {
|
|
437
437
|
const hasField = Object.entries(tableDef.columns).some(([fieldName]) => (0, schema_js_1.camelToSnake)(fieldName) === dbColName);
|
|
438
438
|
if (!hasField) {
|
|
439
|
-
const sql = `ALTER TABLE ${(0,
|
|
439
|
+
const sql = `ALTER TABLE ${(0, index_js_1.quoteIdent)(tableName)} DROP COLUMN ${(0, index_js_1.quoteIdent)(dbColName)};`;
|
|
440
440
|
const reverseSql = `-- Cannot auto-reverse DROP COLUMN for "${dbColName}" — add it back manually`;
|
|
441
441
|
alterDef.columns.push({ column: dbColName, action: 'drop', sql, reverseSql });
|
|
442
442
|
// Don't auto-add drops to statements for safety — user must opt in
|
package/dist/cjs/schema.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* They're used by the query builder, code generator, and CLI.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.normalizeKeyColumns = normalizeKeyColumns;
|
|
9
10
|
exports.pgTypeToTs = pgTypeToTs;
|
|
10
11
|
exports.isDateType = isDateType;
|
|
11
12
|
exports.pgArrayType = pgArrayType;
|
|
@@ -14,6 +15,13 @@ exports.camelToSnake = camelToSnake;
|
|
|
14
15
|
exports.snakeToPascal = snakeToPascal;
|
|
15
16
|
exports.singularize = singularize;
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
18
|
+
// Helpers for composite key handling
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/** Normalize foreignKey/referenceKey to always be an array for uniform processing */
|
|
21
|
+
function normalizeKeyColumns(key) {
|
|
22
|
+
return Array.isArray(key) ? key : [key];
|
|
23
|
+
}
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
17
25
|
// Type mapping: Postgres → TypeScript
|
|
18
26
|
// ---------------------------------------------------------------------------
|
|
19
27
|
const PG_TO_TS = {
|
package/dist/cli/config.d.ts
CHANGED
|
@@ -21,6 +21,17 @@ export interface TurbineCliConfig {
|
|
|
21
21
|
seedFile?: string;
|
|
22
22
|
/** Schema builder file path (for push command) */
|
|
23
23
|
schemaFile?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Database adapter for PostgreSQL-compatible databases that need
|
|
26
|
+
* dialect-specific behavior (e.g. CockroachDB, YugabyteDB).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* import { cockroachdb } from 'turbine-orm/adapters';
|
|
31
|
+
* export default { url: process.env.DATABASE_URL, adapter: cockroachdb };
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
adapter?: import('../adapters/index.js').DatabaseAdapter;
|
|
24
35
|
}
|
|
25
36
|
/**
|
|
26
37
|
* Attempt to load a turbine config file from the current directory.
|
package/dist/cli/index.js
CHANGED
|
@@ -888,7 +888,8 @@ async function cmdStatus(_args, config) {
|
|
|
888
888
|
const isLast = i === rels.length - 1;
|
|
889
889
|
const prefix = isLast ? symbols.teeEnd : symbols.tee;
|
|
890
890
|
const relColor = rel.type === 'hasMany' ? blue : yellow;
|
|
891
|
-
|
|
891
|
+
const fkDisplay = Array.isArray(rel.foreignKey) ? rel.foreignKey.join(', ') : rel.foreignKey;
|
|
892
|
+
console.log(` ${dim(prefix)} ${relColor(relName)} ${dim(symbols.arrow)} ${rel.to} ${dim(`(${rel.type}, FK: ${fkDisplay})`)}`);
|
|
892
893
|
}
|
|
893
894
|
}
|
|
894
895
|
newline();
|
package/dist/cli/migrate.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* -- DOWN
|
|
12
12
|
* DROP TABLE users;
|
|
13
13
|
*/
|
|
14
|
+
import type { DatabaseAdapter } from '../adapters/index.js';
|
|
14
15
|
export interface MigrationFile {
|
|
15
16
|
/** Full filename (e.g. "20260325120000_create_users.sql") */
|
|
16
17
|
filename: string;
|
|
@@ -113,6 +114,7 @@ export declare function migrateUp(connectionString: string, migrationsDir: strin
|
|
|
113
114
|
step?: number;
|
|
114
115
|
allowDrift?: boolean /** @deprecated use allowDrift */;
|
|
115
116
|
force?: boolean;
|
|
117
|
+
adapter?: DatabaseAdapter;
|
|
116
118
|
}): Promise<{
|
|
117
119
|
applied: MigrationFile[];
|
|
118
120
|
errors: Array<{
|
|
@@ -130,6 +132,7 @@ export declare function migrateUp(connectionString: string, migrationsDir: strin
|
|
|
130
132
|
*/
|
|
131
133
|
export declare function migrateDown(connectionString: string, migrationsDir: string, options?: {
|
|
132
134
|
step?: number;
|
|
135
|
+
adapter?: DatabaseAdapter;
|
|
133
136
|
}): Promise<{
|
|
134
137
|
rolledBack: MigrationFile[];
|
|
135
138
|
errors: Array<{
|
package/dist/cli/migrate.js
CHANGED
|
@@ -15,8 +15,9 @@ import { createHash } from 'node:crypto';
|
|
|
15
15
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
16
16
|
import { join } from 'node:path';
|
|
17
17
|
import pg from 'pg';
|
|
18
|
+
import { postgresql } from '../adapters/index.js';
|
|
18
19
|
import { MigrationError } from '../errors.js';
|
|
19
|
-
import { quoteIdent } from '../query.js';
|
|
20
|
+
import { quoteIdent } from '../query/index.js';
|
|
20
21
|
// ---------------------------------------------------------------------------
|
|
21
22
|
// Tracking table management
|
|
22
23
|
// ---------------------------------------------------------------------------
|
|
@@ -234,12 +235,14 @@ async function getCurrentDatabaseName(client) {
|
|
|
234
235
|
const result = await client.query(`SELECT current_database()`);
|
|
235
236
|
return result.rows[0]?.current_database ?? '';
|
|
236
237
|
}
|
|
237
|
-
async function acquireLock(client, lockId) {
|
|
238
|
-
const
|
|
239
|
-
|
|
238
|
+
async function acquireLock(client, lockId, adapter) {
|
|
239
|
+
const a = adapter ?? postgresql;
|
|
240
|
+
// pg.Client satisfies PgCompatPoolClient (query + release)
|
|
241
|
+
return a.acquireLock(client, lockId);
|
|
240
242
|
}
|
|
241
|
-
async function releaseLock(client, lockId) {
|
|
242
|
-
|
|
243
|
+
async function releaseLock(client, lockId, adapter) {
|
|
244
|
+
const a = adapter ?? postgresql;
|
|
245
|
+
await a.releaseLock(client, lockId);
|
|
243
246
|
}
|
|
244
247
|
/**
|
|
245
248
|
* Validate that applied migration files have not been modified or deleted since they were run.
|
|
@@ -306,8 +309,10 @@ export async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
306
309
|
// sibling databases on the same Postgres cluster do not contend.
|
|
307
310
|
const dbName = await getCurrentDatabaseName(client);
|
|
308
311
|
const lockId = deriveLockId(dbName);
|
|
309
|
-
// Acquire
|
|
310
|
-
|
|
312
|
+
// Acquire lock to prevent concurrent migrations.
|
|
313
|
+
// The adapter determines the strategy (advisory lock vs table lock).
|
|
314
|
+
const adapter = options?.adapter;
|
|
315
|
+
const gotLock = await acquireLock(client, lockId, adapter);
|
|
311
316
|
if (!gotLock) {
|
|
312
317
|
throw new MigrationError('[turbine] Could not acquire migration lock — another migration is already running');
|
|
313
318
|
}
|
|
@@ -379,7 +384,7 @@ export async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
379
384
|
return { applied: results, errors };
|
|
380
385
|
}
|
|
381
386
|
finally {
|
|
382
|
-
await releaseLock(client, lockId);
|
|
387
|
+
await releaseLock(client, lockId, adapter);
|
|
383
388
|
}
|
|
384
389
|
}
|
|
385
390
|
finally {
|
|
@@ -402,7 +407,8 @@ export async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
402
407
|
// sibling databases on the same cluster do not contend.
|
|
403
408
|
const dbName = await getCurrentDatabaseName(client);
|
|
404
409
|
const lockId = deriveLockId(dbName);
|
|
405
|
-
const
|
|
410
|
+
const adapter = options?.adapter;
|
|
411
|
+
const gotLock = await acquireLock(client, lockId, adapter);
|
|
406
412
|
if (!gotLock) {
|
|
407
413
|
throw new MigrationError('[turbine] Could not acquire migration lock — another migration is already running');
|
|
408
414
|
}
|
|
@@ -449,7 +455,7 @@ export async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
449
455
|
return { rolledBack: results, errors };
|
|
450
456
|
}
|
|
451
457
|
finally {
|
|
452
|
-
await releaseLock(client, lockId);
|
|
458
|
+
await releaseLock(client, lockId, adapter);
|
|
453
459
|
}
|
|
454
460
|
}
|
|
455
461
|
finally {
|
package/dist/cli/studio.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface StudioOptions {
|
|
|
28
28
|
exclude?: string[];
|
|
29
29
|
/** Directory where studio-queries.json is persisted. Defaults to `.turbine/` in cwd. */
|
|
30
30
|
stateDir?: string;
|
|
31
|
+
/** Database adapter for dialect-specific behavior (e.g. statement timeout syntax). */
|
|
32
|
+
adapter?: import('../adapters/index.js').DatabaseAdapter;
|
|
31
33
|
}
|
|
32
34
|
export interface StudioHandle {
|
|
33
35
|
/** Shut down the server + pool cleanly. */
|
|
@@ -43,6 +45,8 @@ export interface StudioContext {
|
|
|
43
45
|
options: StudioOptions;
|
|
44
46
|
authToken: string;
|
|
45
47
|
stateDir: string;
|
|
48
|
+
/** Resolved statement timeout SQL string (adapter-aware). */
|
|
49
|
+
statementTimeoutSQL: string;
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
48
52
|
* Start the Studio server. Returns a handle with the session token, a pre-built
|
package/dist/cli/studio.js
CHANGED
|
@@ -23,7 +23,7 @@ import { platform } from 'node:os';
|
|
|
23
23
|
import { dirname, resolve as pathResolve } from 'node:path';
|
|
24
24
|
import pg from 'pg';
|
|
25
25
|
import { introspect } from '../introspect.js';
|
|
26
|
-
import { QueryInterface, quoteIdent } from '../query.js';
|
|
26
|
+
import { QueryInterface, quoteIdent } from '../query/index.js';
|
|
27
27
|
import { STUDIO_HTML } from './studio-ui.generated.js';
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
29
29
|
// Main entry point
|
|
@@ -59,7 +59,8 @@ export async function startStudio(options) {
|
|
|
59
59
|
});
|
|
60
60
|
const authToken = randomBytes(24).toString('hex');
|
|
61
61
|
const stateDir = pathResolve(options.stateDir ?? '.turbine');
|
|
62
|
-
const
|
|
62
|
+
const statementTimeoutSQL = options.adapter?.statementTimeout?.(30) ?? `SET LOCAL statement_timeout = '30s'`;
|
|
63
|
+
const ctx = { pool, metadata, options, authToken, stateDir, statementTimeoutSQL };
|
|
63
64
|
const server = createServer((req, res) => {
|
|
64
65
|
handleRequest(req, res, ctx).catch((err) => {
|
|
65
66
|
sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
|
|
@@ -278,7 +279,7 @@ export async function apiTableRows(res, ctx, rawTableName, params) {
|
|
|
278
279
|
const client = await ctx.pool.connect();
|
|
279
280
|
try {
|
|
280
281
|
await client.query('BEGIN READ ONLY');
|
|
281
|
-
await client.query(
|
|
282
|
+
await client.query(ctx.statementTimeoutSQL);
|
|
282
283
|
const result = await client.query(sql, mainValues);
|
|
283
284
|
const countResult = await client.query(countSql, countValues);
|
|
284
285
|
await client.query('COMMIT');
|
|
@@ -344,7 +345,7 @@ async function apiQuery(req, res, ctx) {
|
|
|
344
345
|
const client = await ctx.pool.connect();
|
|
345
346
|
try {
|
|
346
347
|
await client.query('BEGIN READ ONLY');
|
|
347
|
-
await client.query(
|
|
348
|
+
await client.query(ctx.statementTimeoutSQL);
|
|
348
349
|
const started = Date.now();
|
|
349
350
|
const result = await client.query(rawSql);
|
|
350
351
|
const elapsedMs = Date.now() - started;
|
|
@@ -396,7 +397,7 @@ export async function apiBuilder(req, res, ctx) {
|
|
|
396
397
|
const client = await ctx.pool.connect();
|
|
397
398
|
try {
|
|
398
399
|
await client.query('BEGIN READ ONLY');
|
|
399
|
-
await client.query(
|
|
400
|
+
await client.query(ctx.statementTimeoutSQL);
|
|
400
401
|
const started = Date.now();
|
|
401
402
|
const result = await client.query(deferred.sql, deferred.params);
|
|
402
403
|
const elapsedMs = Date.now() - started;
|
package/dist/client.d.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import pg from 'pg';
|
|
25
25
|
import { type ErrorMessageMode } from './errors.js';
|
|
26
26
|
import { type PipelineOptions, type PipelineResults } from './pipeline.js';
|
|
27
|
-
import { type DeferredQuery, QueryInterface, type QueryInterfaceOptions } from './query.js';
|
|
27
|
+
import { type DeferredQuery, QueryInterface, type QueryInterfaceOptions } from './query/index.js';
|
|
28
28
|
import type { SchemaMetadata } from './schema.js';
|
|
29
29
|
/**
|
|
30
30
|
* Minimal pg-compatible query result.
|
package/dist/client.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import pg from 'pg';
|
|
25
25
|
import { setErrorMessageMode, TimeoutError, wrapPgError } from './errors.js';
|
|
26
26
|
import { executePipeline, pipelineSupported } from './pipeline.js';
|
|
27
|
-
import { QueryInterface } from './query.js';
|
|
27
|
+
import { QueryInterface } from './query/index.js';
|
|
28
28
|
/** Maps isolation level names to SQL */
|
|
29
29
|
const ISOLATION_LEVELS = {
|
|
30
30
|
ReadUncommitted: 'READ UNCOMMITTED',
|
package/dist/generate.js
CHANGED
|
@@ -229,7 +229,14 @@ function generateMetadata(schema) {
|
|
|
229
229
|
// relations
|
|
230
230
|
lines.push(' relations: {');
|
|
231
231
|
for (const [relName, rel] of Object.entries(table.relations)) {
|
|
232
|
-
|
|
232
|
+
// Emit foreignKey/referenceKey as string for single-column, array for composite
|
|
233
|
+
const fkLiteral = Array.isArray(rel.foreignKey)
|
|
234
|
+
? `[${rel.foreignKey.map((c) => `'${escSQ(c)}'`).join(', ')}]`
|
|
235
|
+
: `'${escSQ(rel.foreignKey)}'`;
|
|
236
|
+
const refLiteral = Array.isArray(rel.referenceKey)
|
|
237
|
+
? `[${rel.referenceKey.map((c) => `'${escSQ(c)}'`).join(', ')}]`
|
|
238
|
+
: `'${escSQ(rel.referenceKey)}'`;
|
|
239
|
+
lines.push(` ${relName}: { type: '${escSQ(rel.type)}', name: '${escSQ(rel.name)}', from: '${escSQ(rel.from)}', to: '${escSQ(rel.to)}', foreignKey: ${fkLiteral}, referenceKey: ${refLiteral} },`);
|
|
233
240
|
}
|
|
234
241
|
lines.push(' },');
|
|
235
242
|
// indexes
|
package/dist/index.d.ts
CHANGED
|
@@ -32,14 +32,16 @@
|
|
|
32
32
|
* await db.disconnect();
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
|
+
export type { DatabaseAdapter, IntrospectionOverrides } from './adapters/index.js';
|
|
36
|
+
export { alloydb, cockroachdb, postgresql, timescale, yugabytedb } from './adapters/index.js';
|
|
35
37
|
export { type Middleware, type MiddlewareNext, type MiddlewareParams, type PgCompatPool, type PgCompatPoolClient, type PgCompatQueryResult, TransactionClient, type TransactionOptions, TurbineClient, type TurbineConfig, } from './client.js';
|
|
36
38
|
export { CheckConstraintError, CircularRelationError, ConnectionError, DeadlockError, type ErrorMessageMode, ForeignKeyError, getErrorMessageMode, MigrationError, NotFoundError, NotNullViolationError, PipelineError, type PipelineResultSlot, RelationError, SerializationFailureError, setErrorMessageMode, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, ValidationError, wrapPgError, } from './errors.js';
|
|
37
39
|
export { type GenerateOptions, generate } from './generate.js';
|
|
38
40
|
export { type IntrospectOptions, introspect } from './introspect.js';
|
|
39
41
|
export { executePipeline, type PipelineOptions, type PipelineResults, pipelineSupported } from './pipeline.js';
|
|
40
|
-
export { type AggregateArgs, type AggregateResult, type ArrayFilter, type CountArgs, type CreateArgs, type CreateManyArgs, type DeferredQuery, type DeleteArgs, type DeleteManyArgs, type FindManyArgs, type FindManyStreamArgs, type FindUniqueArgs, type GroupByArgs, type JsonFilter, type OrderDirection, QueryInterface, type RelationDescriptor, type RelationFilter, type TypedWithClause, type UpdateArgs, type UpdateInput, type UpdateManyArgs, type UpdateOperatorInput, type UpsertArgs, type WithClause, type WithOptions, type WithResult, } from './query.js';
|
|
42
|
+
export { type AggregateArgs, type AggregateResult, type ArrayFilter, type CountArgs, type CreateArgs, type CreateManyArgs, type DeferredQuery, type DeleteArgs, type DeleteManyArgs, type FindManyArgs, type FindManyStreamArgs, type FindUniqueArgs, type GroupByArgs, type JsonFilter, type OrderDirection, QueryInterface, type RelationDescriptor, type RelationFilter, type TypedWithClause, type UpdateArgs, type UpdateInput, type UpdateManyArgs, type UpdateOperatorInput, type UpsertArgs, type WithClause, type WithOptions, type WithResult, } from './query/index.js';
|
|
41
43
|
export type { ColumnMetadata, IndexMetadata, RelationDef, SchemaMetadata, TableMetadata, } from './schema.js';
|
|
42
|
-
export { camelToSnake, isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
|
|
44
|
+
export { camelToSnake, isDateType, normalizeKeyColumns, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
|
|
43
45
|
export { ColumnBuilder, type ColumnConfig, type ColumnDef, type ColumnType, type ColumnTypeName, column, defineSchema, type SchemaDef, type TableDef, table, } from './schema-builder.js';
|
|
44
46
|
export { type AlterColumnDef, type AlterDef, type DiffResult, type PushResult, schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
|
|
45
47
|
export { type TurbineHttpOptions, turbineHttp } from './serverless.js';
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* await db.disconnect();
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
|
+
export { alloydb, cockroachdb, postgresql, timescale, yugabytedb } from './adapters/index.js';
|
|
35
36
|
// Client
|
|
36
37
|
export { TransactionClient, TurbineClient, } from './client.js';
|
|
37
38
|
// Error types
|
|
@@ -43,9 +44,9 @@ export { introspect } from './introspect.js';
|
|
|
43
44
|
// Pipeline
|
|
44
45
|
export { executePipeline, pipelineSupported } from './pipeline.js';
|
|
45
46
|
// Query builder
|
|
46
|
-
export { QueryInterface, } from './query.js';
|
|
47
|
+
export { QueryInterface, } from './query/index.js';
|
|
47
48
|
// Schema utilities
|
|
48
|
-
export { camelToSnake, isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
|
|
49
|
+
export { camelToSnake, isDateType, normalizeKeyColumns, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
|
|
49
50
|
// Schema builder — define schemas in TypeScript
|
|
50
51
|
export { ColumnBuilder, column, defineSchema,
|
|
51
52
|
// Legacy compat (deprecated — use object format with defineSchema)
|