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
package/dist/cjs/cli/studio.js
CHANGED
|
@@ -38,7 +38,7 @@ const node_os_1 = require("node:os");
|
|
|
38
38
|
const node_path_1 = require("node:path");
|
|
39
39
|
const pg_1 = __importDefault(require("pg"));
|
|
40
40
|
const introspect_js_1 = require("../introspect.js");
|
|
41
|
-
const
|
|
41
|
+
const index_js_1 = require("../query/index.js");
|
|
42
42
|
const studio_ui_generated_js_1 = require("./studio-ui.generated.js");
|
|
43
43
|
// ---------------------------------------------------------------------------
|
|
44
44
|
// Main entry point
|
|
@@ -74,7 +74,8 @@ async function startStudio(options) {
|
|
|
74
74
|
});
|
|
75
75
|
const authToken = (0, node_crypto_1.randomBytes)(24).toString('hex');
|
|
76
76
|
const stateDir = (0, node_path_1.resolve)(options.stateDir ?? '.turbine');
|
|
77
|
-
const
|
|
77
|
+
const statementTimeoutSQL = options.adapter?.statementTimeout?.(30) ?? `SET LOCAL statement_timeout = '30s'`;
|
|
78
|
+
const ctx = { pool, metadata, options, authToken, stateDir, statementTimeoutSQL };
|
|
78
79
|
const server = (0, node_http_1.createServer)((req, res) => {
|
|
79
80
|
handleRequest(req, res, ctx).catch((err) => {
|
|
80
81
|
sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
|
|
@@ -259,10 +260,10 @@ async function apiTableRows(res, ctx, rawTableName, params) {
|
|
|
259
260
|
if (orderByRaw) {
|
|
260
261
|
const col = resolveColumnName(table, orderByRaw);
|
|
261
262
|
if (col)
|
|
262
|
-
orderByClause = `ORDER BY ${(0,
|
|
263
|
+
orderByClause = `ORDER BY ${(0, index_js_1.quoteIdent)(col)} ${dir}`;
|
|
263
264
|
}
|
|
264
265
|
if (!orderByClause && table.primaryKey.length > 0 && table.primaryKey[0]) {
|
|
265
|
-
orderByClause = `ORDER BY ${(0,
|
|
266
|
+
orderByClause = `ORDER BY ${(0, index_js_1.quoteIdent)(table.primaryKey[0])} ${dir}`;
|
|
266
267
|
}
|
|
267
268
|
// Full-text-ish search: ILIKE across text/varchar columns. The value is
|
|
268
269
|
// parameterized so injection is impossible. Each query gets its own
|
|
@@ -276,7 +277,7 @@ async function apiTableRows(res, ctx, rawTableName, params) {
|
|
|
276
277
|
let mainWhere = '';
|
|
277
278
|
if (hasSearch && pattern !== null) {
|
|
278
279
|
mainValues.push(pattern);
|
|
279
|
-
const conds = textColumns.map((c) => `${(0,
|
|
280
|
+
const conds = textColumns.map((c) => `${(0, index_js_1.quoteIdent)(c)} ILIKE $3`);
|
|
280
281
|
mainWhere = `WHERE (${conds.join(' OR ')})`;
|
|
281
282
|
}
|
|
282
283
|
// Count query: $1 = pattern (if search)
|
|
@@ -284,16 +285,16 @@ async function apiTableRows(res, ctx, rawTableName, params) {
|
|
|
284
285
|
let countWhere = '';
|
|
285
286
|
if (hasSearch && pattern !== null) {
|
|
286
287
|
countValues.push(pattern);
|
|
287
|
-
const conds = textColumns.map((c) => `${(0,
|
|
288
|
+
const conds = textColumns.map((c) => `${(0, index_js_1.quoteIdent)(c)} ILIKE $1`);
|
|
288
289
|
countWhere = `WHERE (${conds.join(' OR ')})`;
|
|
289
290
|
}
|
|
290
|
-
const qualifiedTable = `${(0,
|
|
291
|
+
const qualifiedTable = `${(0, index_js_1.quoteIdent)(ctx.options.schema)}.${(0, index_js_1.quoteIdent)(table.name)}`;
|
|
291
292
|
const sql = `SELECT * FROM ${qualifiedTable} ${mainWhere} ${orderByClause} LIMIT $1 OFFSET $2`;
|
|
292
293
|
const countSql = `SELECT COUNT(*)::text AS count FROM ${qualifiedTable} ${countWhere}`;
|
|
293
294
|
const client = await ctx.pool.connect();
|
|
294
295
|
try {
|
|
295
296
|
await client.query('BEGIN READ ONLY');
|
|
296
|
-
await client.query(
|
|
297
|
+
await client.query(ctx.statementTimeoutSQL);
|
|
297
298
|
const result = await client.query(sql, mainValues);
|
|
298
299
|
const countResult = await client.query(countSql, countValues);
|
|
299
300
|
await client.query('COMMIT');
|
|
@@ -359,7 +360,7 @@ async function apiQuery(req, res, ctx) {
|
|
|
359
360
|
const client = await ctx.pool.connect();
|
|
360
361
|
try {
|
|
361
362
|
await client.query('BEGIN READ ONLY');
|
|
362
|
-
await client.query(
|
|
363
|
+
await client.query(ctx.statementTimeoutSQL);
|
|
363
364
|
const started = Date.now();
|
|
364
365
|
const result = await client.query(rawSql);
|
|
365
366
|
const elapsedMs = Date.now() - started;
|
|
@@ -397,7 +398,7 @@ async function apiBuilder(req, res, ctx) {
|
|
|
397
398
|
}
|
|
398
399
|
let deferred;
|
|
399
400
|
try {
|
|
400
|
-
const qi = new
|
|
401
|
+
const qi = new index_js_1.QueryInterface(ctx.pool, tableName, ctx.metadata, [], {
|
|
401
402
|
warnOnUnlimited: false,
|
|
402
403
|
sqlCache: false,
|
|
403
404
|
preparedStatements: false,
|
|
@@ -411,7 +412,7 @@ async function apiBuilder(req, res, ctx) {
|
|
|
411
412
|
const client = await ctx.pool.connect();
|
|
412
413
|
try {
|
|
413
414
|
await client.query('BEGIN READ ONLY');
|
|
414
|
-
await client.query(
|
|
415
|
+
await client.query(ctx.statementTimeoutSQL);
|
|
415
416
|
const started = Date.now();
|
|
416
417
|
const result = await client.query(deferred.sql, deferred.params);
|
|
417
418
|
const elapsedMs = Date.now() - started;
|
package/dist/cjs/client.js
CHANGED
|
@@ -30,7 +30,7 @@ exports.TurbineClient = exports.TransactionClient = void 0;
|
|
|
30
30
|
const pg_1 = __importDefault(require("pg"));
|
|
31
31
|
const errors_js_1 = require("./errors.js");
|
|
32
32
|
const pipeline_js_1 = require("./pipeline.js");
|
|
33
|
-
const
|
|
33
|
+
const index_js_1 = require("./query/index.js");
|
|
34
34
|
/** Maps isolation level names to SQL */
|
|
35
35
|
const ISOLATION_LEVELS = {
|
|
36
36
|
ReadUncommitted: 'READ UNCOMMITTED',
|
|
@@ -79,7 +79,7 @@ class TransactionClient {
|
|
|
79
79
|
// Create a QueryInterface that uses the transaction client as its "pool"
|
|
80
80
|
// We use a proxy pool that routes queries through the transaction client
|
|
81
81
|
const txPool = this.createTxPool();
|
|
82
|
-
qi = new
|
|
82
|
+
qi = new index_js_1.QueryInterface(txPool, name, this.schema, this.middlewares, this.queryOptions);
|
|
83
83
|
this.tableCache.set(name, qi);
|
|
84
84
|
}
|
|
85
85
|
return qi;
|
|
@@ -306,7 +306,7 @@ class TurbineClient {
|
|
|
306
306
|
table(name) {
|
|
307
307
|
let qi = this.tableCache.get(name);
|
|
308
308
|
if (!qi) {
|
|
309
|
-
qi = new
|
|
309
|
+
qi = new index_js_1.QueryInterface(this.pool, name, this.schema, this.middlewares, this.queryOptions);
|
|
310
310
|
this.tableCache.set(name, qi);
|
|
311
311
|
}
|
|
312
312
|
return qi;
|
package/dist/cjs/generate.js
CHANGED
|
@@ -233,7 +233,14 @@ function generateMetadata(schema) {
|
|
|
233
233
|
// relations
|
|
234
234
|
lines.push(' relations: {');
|
|
235
235
|
for (const [relName, rel] of Object.entries(table.relations)) {
|
|
236
|
-
|
|
236
|
+
// Emit foreignKey/referenceKey as string for single-column, array for composite
|
|
237
|
+
const fkLiteral = Array.isArray(rel.foreignKey)
|
|
238
|
+
? `[${rel.foreignKey.map((c) => `'${escSQ(c)}'`).join(', ')}]`
|
|
239
|
+
: `'${escSQ(rel.foreignKey)}'`;
|
|
240
|
+
const refLiteral = Array.isArray(rel.referenceKey)
|
|
241
|
+
? `[${rel.referenceKey.map((c) => `'${escSQ(c)}'`).join(', ')}]`
|
|
242
|
+
: `'${escSQ(rel.referenceKey)}'`;
|
|
243
|
+
lines.push(` ${relName}: { type: '${escSQ(rel.type)}', name: '${escSQ(rel.name)}', from: '${escSQ(rel.from)}', to: '${escSQ(rel.to)}', foreignKey: ${fkLiteral}, referenceKey: ${refLiteral} },`);
|
|
237
244
|
}
|
|
238
245
|
lines.push(' },');
|
|
239
246
|
// indexes
|
package/dist/cjs/index.js
CHANGED
|
@@ -34,7 +34,13 @@
|
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.turbineHttp = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.table = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.isDateType = exports.camelToSnake = exports.QueryInterface = exports.pipelineSupported = exports.executePipeline = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.PipelineError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.TurbineClient = exports.TransactionClient = void 0;
|
|
37
|
+
exports.turbineHttp = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.table = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = exports.camelToSnake = exports.QueryInterface = exports.pipelineSupported = exports.executePipeline = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.PipelineError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
|
|
38
|
+
var index_js_1 = require("./adapters/index.js");
|
|
39
|
+
Object.defineProperty(exports, "alloydb", { enumerable: true, get: function () { return index_js_1.alloydb; } });
|
|
40
|
+
Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return index_js_1.cockroachdb; } });
|
|
41
|
+
Object.defineProperty(exports, "postgresql", { enumerable: true, get: function () { return index_js_1.postgresql; } });
|
|
42
|
+
Object.defineProperty(exports, "timescale", { enumerable: true, get: function () { return index_js_1.timescale; } });
|
|
43
|
+
Object.defineProperty(exports, "yugabytedb", { enumerable: true, get: function () { return index_js_1.yugabytedb; } });
|
|
38
44
|
// Client
|
|
39
45
|
var client_js_1 = require("./client.js");
|
|
40
46
|
Object.defineProperty(exports, "TransactionClient", { enumerable: true, get: function () { return client_js_1.TransactionClient; } });
|
|
@@ -71,12 +77,13 @@ var pipeline_js_1 = require("./pipeline.js");
|
|
|
71
77
|
Object.defineProperty(exports, "executePipeline", { enumerable: true, get: function () { return pipeline_js_1.executePipeline; } });
|
|
72
78
|
Object.defineProperty(exports, "pipelineSupported", { enumerable: true, get: function () { return pipeline_js_1.pipelineSupported; } });
|
|
73
79
|
// Query builder
|
|
74
|
-
var
|
|
75
|
-
Object.defineProperty(exports, "QueryInterface", { enumerable: true, get: function () { return
|
|
80
|
+
var index_js_2 = require("./query/index.js");
|
|
81
|
+
Object.defineProperty(exports, "QueryInterface", { enumerable: true, get: function () { return index_js_2.QueryInterface; } });
|
|
76
82
|
// Schema utilities
|
|
77
83
|
var schema_js_1 = require("./schema.js");
|
|
78
84
|
Object.defineProperty(exports, "camelToSnake", { enumerable: true, get: function () { return schema_js_1.camelToSnake; } });
|
|
79
85
|
Object.defineProperty(exports, "isDateType", { enumerable: true, get: function () { return schema_js_1.isDateType; } });
|
|
86
|
+
Object.defineProperty(exports, "normalizeKeyColumns", { enumerable: true, get: function () { return schema_js_1.normalizeKeyColumns; } });
|
|
80
87
|
Object.defineProperty(exports, "pgArrayType", { enumerable: true, get: function () { return schema_js_1.pgArrayType; } });
|
|
81
88
|
Object.defineProperty(exports, "pgTypeToTs", { enumerable: true, get: function () { return schema_js_1.pgTypeToTs; } });
|
|
82
89
|
Object.defineProperty(exports, "singularize", { enumerable: true, get: function () { return schema_js_1.singularize; } });
|
package/dist/cjs/introspect.js
CHANGED
|
@@ -72,13 +72,16 @@ const SQL_FOREIGN_KEYS = `
|
|
|
72
72
|
const SQL_UNIQUE_CONSTRAINTS = `
|
|
73
73
|
SELECT
|
|
74
74
|
tc.table_name,
|
|
75
|
-
|
|
75
|
+
tc.constraint_name,
|
|
76
|
+
kcu.column_name,
|
|
77
|
+
kcu.ordinal_position
|
|
76
78
|
FROM information_schema.table_constraints tc
|
|
77
79
|
JOIN information_schema.key_column_usage kcu
|
|
78
80
|
ON tc.constraint_name = kcu.constraint_name
|
|
79
81
|
AND tc.table_schema = kcu.table_schema
|
|
80
82
|
WHERE tc.constraint_type = 'UNIQUE'
|
|
81
83
|
AND tc.table_schema = $1
|
|
84
|
+
ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position
|
|
82
85
|
`;
|
|
83
86
|
const SQL_INDEXES = `
|
|
84
87
|
SELECT tablename, indexname, indexdef
|
|
@@ -159,14 +162,22 @@ async function introspect(options) {
|
|
|
159
162
|
pkByTable.get(row.table_name).push(row.column_name);
|
|
160
163
|
}
|
|
161
164
|
// ----- Group unique constraints by table -----
|
|
165
|
+
// Group rows by (table_name, constraint_name) to correctly handle multi-column unique constraints
|
|
162
166
|
const uniqueByTable = new Map();
|
|
167
|
+
const uniqueConstraintGroups = new Map();
|
|
163
168
|
for (const row of uniqueResult.rows) {
|
|
164
169
|
if (!tableSet.has(row.table_name))
|
|
165
170
|
continue;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
171
|
+
const key = `${row.table_name}::${row.constraint_name}`;
|
|
172
|
+
if (!uniqueConstraintGroups.has(key)) {
|
|
173
|
+
uniqueConstraintGroups.set(key, { table: row.table_name, columns: [] });
|
|
174
|
+
}
|
|
175
|
+
uniqueConstraintGroups.get(key).columns.push(row.column_name);
|
|
176
|
+
}
|
|
177
|
+
for (const { table, columns } of uniqueConstraintGroups.values()) {
|
|
178
|
+
if (!uniqueByTable.has(table))
|
|
179
|
+
uniqueByTable.set(table, []);
|
|
180
|
+
uniqueByTable.get(table).push(columns);
|
|
170
181
|
}
|
|
171
182
|
// ----- Group indexes by table -----
|
|
172
183
|
const indexesByTable = new Map();
|
|
@@ -193,17 +204,25 @@ async function introspect(options) {
|
|
|
193
204
|
enums[row.typname] = [];
|
|
194
205
|
enums[row.typname].push(row.enumlabel);
|
|
195
206
|
}
|
|
196
|
-
const
|
|
207
|
+
const fkGroups = new Map();
|
|
197
208
|
for (const row of fkResult.rows) {
|
|
198
209
|
if (!tableSet.has(row.source_table) || !tableSet.has(row.target_table))
|
|
199
210
|
continue;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
211
|
+
const key = row.constraint_name;
|
|
212
|
+
if (!fkGroups.has(key)) {
|
|
213
|
+
fkGroups.set(key, {
|
|
214
|
+
sourceTable: row.source_table,
|
|
215
|
+
sourceColumns: [],
|
|
216
|
+
targetTable: row.target_table,
|
|
217
|
+
targetColumns: [],
|
|
218
|
+
constraintName: key,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
const entry = fkGroups.get(key);
|
|
222
|
+
entry.sourceColumns.push(row.source_column);
|
|
223
|
+
entry.targetColumns.push(row.target_column);
|
|
206
224
|
}
|
|
225
|
+
const foreignKeys = Array.from(fkGroups.values());
|
|
207
226
|
// ----- Build relations from foreign keys -----
|
|
208
227
|
// Count FKs per (source, target) pair for disambiguation
|
|
209
228
|
const fkCounts = new Map();
|
|
@@ -215,10 +234,17 @@ async function introspect(options) {
|
|
|
215
234
|
for (const fk of foreignKeys) {
|
|
216
235
|
const pairKey = `${fk.sourceTable}→${fk.targetTable}`;
|
|
217
236
|
const needsDisambiguation = (fkCounts.get(pairKey) ?? 0) > 1;
|
|
237
|
+
// For single-column FKs, keep string form for backwards compatibility.
|
|
238
|
+
// For multi-column (composite) FKs, use array form.
|
|
239
|
+
const foreignKey = fk.sourceColumns.length === 1 ? fk.sourceColumns[0] : fk.sourceColumns;
|
|
240
|
+
const referenceKey = fk.targetColumns.length === 1 ? fk.targetColumns[0] : fk.targetColumns;
|
|
218
241
|
// --- belongsTo on the source (child) table ---
|
|
219
242
|
// e.g. posts.user_id → users.id creates posts.user (belongsTo)
|
|
243
|
+
// For composite FKs with disambiguation, use the constraint name
|
|
220
244
|
const belongsToName = needsDisambiguation
|
|
221
|
-
?
|
|
245
|
+
? fk.sourceColumns.length === 1
|
|
246
|
+
? (0, schema_js_1.snakeToCamel)(fk.sourceColumns[0].replace(/_id$/, ''))
|
|
247
|
+
: (0, schema_js_1.snakeToCamel)(fk.constraintName.replace(/^fk_/, '').replace(/_fkey$/, ''))
|
|
222
248
|
: (0, schema_js_1.singularize)((0, schema_js_1.snakeToCamel)(fk.targetTable));
|
|
223
249
|
if (!relationsByTable.has(fk.sourceTable))
|
|
224
250
|
relationsByTable.set(fk.sourceTable, {});
|
|
@@ -227,13 +253,15 @@ async function introspect(options) {
|
|
|
227
253
|
name: belongsToName,
|
|
228
254
|
from: fk.sourceTable,
|
|
229
255
|
to: fk.targetTable,
|
|
230
|
-
foreignKey
|
|
231
|
-
referenceKey
|
|
256
|
+
foreignKey,
|
|
257
|
+
referenceKey,
|
|
232
258
|
};
|
|
233
259
|
// --- hasMany on the target (parent) table ---
|
|
234
260
|
// e.g. posts.user_id → users.id creates users.posts (hasMany)
|
|
235
261
|
const hasManyName = needsDisambiguation
|
|
236
|
-
?
|
|
262
|
+
? fk.sourceColumns.length === 1
|
|
263
|
+
? (0, schema_js_1.snakeToCamel)(`${fk.sourceTable}_by_${fk.sourceColumns[0].replace(/_id$/, '')}`)
|
|
264
|
+
: (0, schema_js_1.snakeToCamel)(`${fk.sourceTable}_by_${fk.constraintName.replace(/^fk_/, '').replace(/_fkey$/, '')}`)
|
|
237
265
|
: (0, schema_js_1.snakeToCamel)(fk.sourceTable);
|
|
238
266
|
if (!relationsByTable.has(fk.targetTable))
|
|
239
267
|
relationsByTable.set(fk.targetTable, {});
|
|
@@ -242,8 +270,8 @@ async function introspect(options) {
|
|
|
242
270
|
name: hasManyName,
|
|
243
271
|
from: fk.targetTable,
|
|
244
272
|
to: fk.sourceTable,
|
|
245
|
-
foreignKey
|
|
246
|
-
referenceKey
|
|
273
|
+
foreignKey,
|
|
274
|
+
referenceKey,
|
|
247
275
|
};
|
|
248
276
|
}
|
|
249
277
|
// ----- Assemble TableMetadata for each table -----
|