turbine-orm 0.76.0 → 0.77.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/dist/cjs/cli/index.js +2 -2
- package/dist/cjs/cli/migrate.js +8 -8
- package/dist/cjs/cli/studio.js +6 -3
- package/dist/cjs/client.js +10 -10
- package/dist/cjs/dialect.js +1 -1
- package/dist/cjs/errors.d.ts +2 -2
- package/dist/cjs/errors.js +21 -25
- package/dist/cjs/generate.js +1 -1
- package/dist/cjs/introspect.js +5 -5
- package/dist/cjs/mssql.js +12 -12
- package/dist/cjs/mysql.js +5 -5
- package/dist/cjs/nested-write.js +31 -31
- package/dist/cjs/observe.js +1 -1
- package/dist/cjs/pipeline-submittable.js +5 -1
- package/dist/cjs/pipeline.js +8 -0
- package/dist/cjs/powdb-introspect.d.ts +1 -1
- package/dist/cjs/powdb-introspect.js +4 -4
- package/dist/cjs/powdb-shared.d.ts +348 -0
- package/dist/cjs/powdb-shared.js +587 -0
- package/dist/cjs/powdb.d.ts +12 -299
- package/dist/cjs/powdb.js +106 -567
- package/dist/cjs/powql.d.ts +1 -1
- package/dist/cjs/powql.js +89 -89
- package/dist/cjs/prisma-compat.js +16 -16
- package/dist/cjs/query/aggregates.js +19 -20
- package/dist/cjs/query/batched-loader.js +8 -8
- package/dist/cjs/query/builder.js +8 -8
- package/dist/cjs/query/compound-unique.js +2 -2
- package/dist/cjs/query/filters.js +1 -1
- package/dist/cjs/query/relations.js +60 -48
- package/dist/cjs/query/types.js +4 -4
- package/dist/cjs/query/utils.js +5 -5
- package/dist/cjs/query/warn-registry.d.ts +7 -0
- package/dist/cjs/query/warn-registry.js +7 -0
- package/dist/cjs/query/where-compile.js +1 -1
- package/dist/cjs/query/where.js +30 -31
- package/dist/cjs/query/writes.js +7 -7
- package/dist/cjs/realtime.js +4 -4
- package/dist/cjs/schema-metadata.js +1 -1
- package/dist/cjs/schema-sql.js +3 -3
- package/dist/cjs/seed.js +1 -1
- package/dist/cjs/sqlite.js +7 -7
- package/dist/cjs/typed-sql.js +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/migrate.js +8 -8
- package/dist/cli/studio.js +6 -3
- package/dist/client.js +10 -10
- package/dist/dialect.js +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +21 -25
- package/dist/generate.js +1 -1
- package/dist/introspect.js +5 -5
- package/dist/mssql.js +12 -12
- package/dist/mysql.js +5 -5
- package/dist/nested-write.js +31 -31
- package/dist/observe.js +1 -1
- package/dist/pipeline-submittable.js +6 -2
- package/dist/pipeline.js +9 -1
- package/dist/powdb-introspect.d.ts +1 -1
- package/dist/powdb-introspect.js +2 -2
- package/dist/powdb-shared.d.ts +348 -0
- package/dist/powdb-shared.js +570 -0
- package/dist/powdb.d.ts +12 -299
- package/dist/powdb.js +71 -534
- package/dist/powql.d.ts +1 -1
- package/dist/powql.js +43 -43
- package/dist/prisma-compat.js +16 -16
- package/dist/query/aggregates.js +19 -20
- package/dist/query/batched-loader.js +8 -8
- package/dist/query/builder.js +8 -8
- package/dist/query/compound-unique.js +2 -2
- package/dist/query/filters.js +1 -1
- package/dist/query/relations.js +60 -48
- package/dist/query/types.js +4 -4
- package/dist/query/utils.js +5 -5
- package/dist/query/warn-registry.d.ts +7 -0
- package/dist/query/warn-registry.js +7 -0
- package/dist/query/where-compile.js +1 -1
- package/dist/query/where.js +30 -31
- package/dist/query/writes.js +7 -7
- package/dist/realtime.js +4 -4
- package/dist/schema-metadata.js +1 -1
- package/dist/schema-sql.js +3 -3
- package/dist/seed.js +1 -1
- package/dist/sqlite.js +7 -7
- package/dist/typed-sql.js +1 -1
- package/package.json +15 -7
package/dist/cjs/nested-write.js
CHANGED
|
@@ -222,11 +222,11 @@ function keyOf(value) {
|
|
|
222
222
|
function validateOps(relationName, ops, isUpdate) {
|
|
223
223
|
for (const opName of Object.keys(ops)) {
|
|
224
224
|
if (!CREATE_ONLY_OPS.has(opName) && !UPDATE_ONLY_OPS.has(opName)) {
|
|
225
|
-
throw new errors_js_1.ValidationError(`
|
|
225
|
+
throw new errors_js_1.ValidationError(`Unknown nested write operation "${opName}" on relation "${relationName}". ` +
|
|
226
226
|
`Valid operations: create, connect, connectOrCreate${isUpdate ? ', disconnect, set, delete, update, upsert' : ''}.`);
|
|
227
227
|
}
|
|
228
228
|
if (!isUpdate && UPDATE_ONLY_OPS.has(opName)) {
|
|
229
|
-
throw new errors_js_1.ValidationError(`
|
|
229
|
+
throw new errors_js_1.ValidationError(`Operation "${opName}" on relation "${relationName}" is only valid inside update(), not create().`);
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
}
|
|
@@ -245,7 +245,7 @@ function pkWhere(tableMeta, row) {
|
|
|
245
245
|
// be one this engine just read, so a missing member is an internal fault,
|
|
246
246
|
// and the only safe response is to refuse rather than to run.
|
|
247
247
|
if (row[field] === undefined) {
|
|
248
|
-
throw new errors_js_1.ValidationError(`
|
|
248
|
+
throw new errors_js_1.ValidationError(`Cannot address a row of "${tableMeta.name}" by primary key: "${field}" is missing from the ` +
|
|
249
249
|
'row this nested write is operating on, so the generated predicate would match more rows than intended. ' +
|
|
250
250
|
'This is a bug in turbine, please report it.');
|
|
251
251
|
}
|
|
@@ -347,7 +347,7 @@ function assertTargetSelectsSomething(target, op, relName, rel) {
|
|
|
347
347
|
if (target === true) {
|
|
348
348
|
if (toOne)
|
|
349
349
|
return;
|
|
350
|
-
throw new errors_js_1.ValidationError(`
|
|
350
|
+
throw new errors_js_1.ValidationError(`Nested ${op} on to-many relation "${relName}" needs a "where" selector: ` +
|
|
351
351
|
`"${op}: true" would ${op} every related "${rel.to}" row.`);
|
|
352
352
|
}
|
|
353
353
|
if (target && typeof target === 'object' && !Array.isArray(target)) {
|
|
@@ -355,7 +355,7 @@ function assertTargetSelectsSomething(target, op, relName, rel) {
|
|
|
355
355
|
if (bound)
|
|
356
356
|
return;
|
|
357
357
|
}
|
|
358
|
-
throw new errors_js_1.ValidationError(`
|
|
358
|
+
throw new errors_js_1.ValidationError(`Nested ${op} on relation "${relName}" requires a selector with at least one defined value. ` +
|
|
359
359
|
`An empty or all-undefined "where" would ${op} every related "${rel.to}" row of this parent.`);
|
|
360
360
|
}
|
|
361
361
|
/**
|
|
@@ -394,7 +394,7 @@ function junctionAccessorHint(junction) {
|
|
|
394
394
|
*/
|
|
395
395
|
function manyToManyOpUnsupported(op, relName, rel) {
|
|
396
396
|
const junction = rel.through?.table ?? 'junction';
|
|
397
|
-
return new errors_js_1.ValidationError(`
|
|
397
|
+
return new errors_js_1.ValidationError(`Nested "${op}" is not supported on the many-to-many relation "${relName}" ` +
|
|
398
398
|
`(via the "${junction}" junction table). The supported many-to-many nested operations are ` +
|
|
399
399
|
`connect, disconnect and set. To ${op} a "${rel.to}" row itself, write it directly on the ` +
|
|
400
400
|
`"${rel.to}" table inside the same $transaction and link it with a nested connect; to write ` +
|
|
@@ -432,24 +432,24 @@ function junctionPairIsUnique(junctionMeta, sourceCol, targetCol) {
|
|
|
432
432
|
function junctionPlan(ctx, rel, relName, parentRow) {
|
|
433
433
|
const through = rel.through;
|
|
434
434
|
if (!through) {
|
|
435
|
-
throw new errors_js_1.ValidationError(`
|
|
435
|
+
throw new errors_js_1.ValidationError(`manyToMany relation "${relName}" is missing a \`through\` junction descriptor.`);
|
|
436
436
|
}
|
|
437
437
|
const junctionMeta = ctx.schema.tables[through.table];
|
|
438
438
|
if (!junctionMeta) {
|
|
439
|
-
throw new errors_js_1.ValidationError(`
|
|
439
|
+
throw new errors_js_1.ValidationError(`Nested write on the many-to-many relation "${relName}": junction table ` +
|
|
440
440
|
`"${through.table}" is not present in the schema metadata, so its rows cannot be written. ` +
|
|
441
441
|
`Regenerate the schema (turbine generate) so the junction table is included.`);
|
|
442
442
|
}
|
|
443
443
|
const targetMeta = ctx.schema.tables[rel.to];
|
|
444
444
|
if (!targetMeta) {
|
|
445
|
-
throw new errors_js_1.ValidationError(`
|
|
445
|
+
throw new errors_js_1.ValidationError(`Nested write on the many-to-many relation "${relName}": unknown target table "${rel.to}".`);
|
|
446
446
|
}
|
|
447
447
|
const sourceKeys = (0, schema_js_1.normalizeKeyColumns)(through.sourceKey);
|
|
448
448
|
const targetKeys = (0, schema_js_1.normalizeKeyColumns)(through.targetKey);
|
|
449
449
|
const refKeys = (0, schema_js_1.normalizeKeyColumns)(rel.referenceKey);
|
|
450
450
|
const targetPk = targetMeta.primaryKey;
|
|
451
451
|
if (sourceKeys.length !== 1 || targetKeys.length !== 1 || refKeys.length !== 1 || targetPk.length !== 1) {
|
|
452
|
-
throw new errors_js_1.ValidationError(`
|
|
452
|
+
throw new errors_js_1.ValidationError(`Nested writes on the many-to-many relation "${relName}" (via "${through.table}") support ` +
|
|
453
453
|
`single-column junction keys only; this relation links on composite keys ` +
|
|
454
454
|
`(source ${sourceKeys.length}, target ${targetKeys.length}, target primary key ${targetPk.length} column(s)). ` +
|
|
455
455
|
`Write the junction rows directly inside the same $transaction: ${junctionAccessorHint(through.table)}.`);
|
|
@@ -462,7 +462,7 @@ function junctionPlan(ctx, rel, relName, parentRow) {
|
|
|
462
462
|
// key at all. Introspection can never produce this (it requires two distinct
|
|
463
463
|
// FK columns), but a hand-declared `defineSchema` manyToMany can.
|
|
464
464
|
if (sourceKeys[0] === targetKeys[0]) {
|
|
465
|
-
throw new errors_js_1.ValidationError(`
|
|
465
|
+
throw new errors_js_1.ValidationError(`Nested write on the many-to-many relation "${relName}" cannot run: the junction ` +
|
|
466
466
|
`"${through.table}" names the same column "${sourceKeys[0]}" as BOTH its sourceKey and its ` +
|
|
467
467
|
`targetKey, so a link row cannot hold the parent key and the target key at once. Fix the ` +
|
|
468
468
|
`relation's \`through\` descriptor to name the two distinct junction columns.`);
|
|
@@ -470,7 +470,7 @@ function junctionPlan(ctx, rel, relName, parentRow) {
|
|
|
470
470
|
const parentField = ctx.schema.tables[rel.from]?.reverseColumnMap[refKeys[0]] ?? refKeys[0];
|
|
471
471
|
const parentValue = parentRow[parentField];
|
|
472
472
|
if (parentValue === null || parentValue === undefined) {
|
|
473
|
-
throw new errors_js_1.ValidationError(`
|
|
473
|
+
throw new errors_js_1.ValidationError(`Nested write on the many-to-many relation "${relName}" cannot run: the parent's reference ` +
|
|
474
474
|
`key "${parentField}" is ${parentValue === null ? 'null' : 'missing from the loaded row'}, so no ` +
|
|
475
475
|
`junction row can be correlated to this parent.`);
|
|
476
476
|
}
|
|
@@ -485,7 +485,7 @@ function junctionPlan(ctx, rel, relName, parentRow) {
|
|
|
485
485
|
}
|
|
486
486
|
/** The E003 for a target selector that matched no row. */
|
|
487
487
|
function noTargetRow(op, relName, rel, target) {
|
|
488
|
-
return new errors_js_1.ValidationError(`
|
|
488
|
+
return new errors_js_1.ValidationError(`Nested ${op} on the many-to-many relation "${relName}": no "${rel.to}" row found ` +
|
|
489
489
|
`matching ${(0, errors_js_1.describeTargetForMessage)(target)}.`);
|
|
490
490
|
}
|
|
491
491
|
/**
|
|
@@ -544,7 +544,7 @@ async function resolveJunctionTargets(ctx, rel, relName, plan, op, items) {
|
|
|
544
544
|
throw noTargetRow(op, relName, rel, target);
|
|
545
545
|
const value = row[plan.targetKeyField];
|
|
546
546
|
if (value === null || value === undefined) {
|
|
547
|
-
throw new errors_js_1.ValidationError(`
|
|
547
|
+
throw new errors_js_1.ValidationError(`Nested ${op} on the many-to-many relation "${relName}": the "${rel.to}" row matching ` +
|
|
548
548
|
`${(0, errors_js_1.describeTargetForMessage)(target)} has no "${plan.targetKeyField}" value to link.`);
|
|
549
549
|
}
|
|
550
550
|
const key = keyOf(value);
|
|
@@ -743,7 +743,7 @@ function notRelatedToParent(op, relName, rel, target) {
|
|
|
743
743
|
table: rel.to,
|
|
744
744
|
where: target,
|
|
745
745
|
operation: `nested ${op}`,
|
|
746
|
-
message: `
|
|
746
|
+
message: `Nested ${op} on relation "${relName}": no "${rel.to}" record matching ` +
|
|
747
747
|
`${(0, errors_js_1.describeTargetForMessage)(target)} is related to this parent. Either it does not exist, ` +
|
|
748
748
|
`or it belongs to a different parent (a nested ${op} can only touch this parent's rows).`,
|
|
749
749
|
});
|
|
@@ -777,14 +777,14 @@ async function executeNestedCreate(ctx, tableName, data, depth = 0, path = []) {
|
|
|
777
777
|
}
|
|
778
778
|
const tableMeta = ctx.schema.tables[tableName];
|
|
779
779
|
if (!tableMeta) {
|
|
780
|
-
throw new errors_js_1.ValidationError(`
|
|
780
|
+
throw new errors_js_1.ValidationError(`Unknown table "${tableName}".`);
|
|
781
781
|
}
|
|
782
782
|
const { scalars, relations } = extractRelationFields(data, tableMeta);
|
|
783
783
|
// Validate all relation operations
|
|
784
784
|
for (const [relName, ops] of Object.entries(relations)) {
|
|
785
785
|
const rel = tableMeta.relations[relName];
|
|
786
786
|
if (!rel) {
|
|
787
|
-
throw new errors_js_1.RelationError(`
|
|
787
|
+
throw new errors_js_1.RelationError(`Unknown relation "${relName}" on table "${tableName}". ` +
|
|
788
788
|
`Available relations: ${Object.keys(tableMeta.relations).join(', ') || '(none)'}.`);
|
|
789
789
|
}
|
|
790
790
|
validateOps(relName, ops, false);
|
|
@@ -856,14 +856,14 @@ async function executeNestedUpdate(ctx, tableName, where, data, depth = 0, path
|
|
|
856
856
|
}
|
|
857
857
|
const tableMeta = ctx.schema.tables[tableName];
|
|
858
858
|
if (!tableMeta) {
|
|
859
|
-
throw new errors_js_1.ValidationError(`
|
|
859
|
+
throw new errors_js_1.ValidationError(`Unknown table "${tableName}".`);
|
|
860
860
|
}
|
|
861
861
|
const { scalars, relations } = extractRelationFields(data, tableMeta);
|
|
862
862
|
// Validate all relation operations
|
|
863
863
|
for (const [relName, ops] of Object.entries(relations)) {
|
|
864
864
|
const rel = tableMeta.relations[relName];
|
|
865
865
|
if (!rel) {
|
|
866
|
-
throw new errors_js_1.RelationError(`
|
|
866
|
+
throw new errors_js_1.RelationError(`Unknown relation "${relName}" on table "${tableName}". ` +
|
|
867
867
|
`Available relations: ${Object.keys(tableMeta.relations).join(', ') || '(none)'}.`);
|
|
868
868
|
}
|
|
869
869
|
validateOps(relName, ops, true);
|
|
@@ -876,7 +876,7 @@ async function executeNestedUpdate(ctx, tableName, where, data, depth = 0, path
|
|
|
876
876
|
else {
|
|
877
877
|
parentRow = (await ctx.tx.table(tableName).findUnique({ where }));
|
|
878
878
|
if (!parentRow) {
|
|
879
|
-
throw new errors_js_1.ValidationError(`
|
|
879
|
+
throw new errors_js_1.ValidationError(`update: no ${tableName} row found matching ${(0, errors_js_1.describeTargetForMessage)(where)}.`);
|
|
880
880
|
}
|
|
881
881
|
}
|
|
882
882
|
// Process each relation
|
|
@@ -924,7 +924,7 @@ async function executeNestedUpdate(ctx, tableName, where, data, depth = 0, path
|
|
|
924
924
|
return col?.nullable ?? false;
|
|
925
925
|
});
|
|
926
926
|
if (!nullable) {
|
|
927
|
-
throw new errors_js_1.ValidationError(`
|
|
927
|
+
throw new errors_js_1.ValidationError(`Cannot disconnect "${relName}": foreign key column(s) ${fks.join(', ')} are NOT NULL. Use delete instead.`);
|
|
928
928
|
}
|
|
929
929
|
const updateData = {};
|
|
930
930
|
for (const fk of fks) {
|
|
@@ -1033,7 +1033,7 @@ async function resolveBelongsToForCreate(ctx, rel, ops, parentTable, depth, path
|
|
|
1033
1033
|
const target = items[0];
|
|
1034
1034
|
relatedRow = (await ctx.tx.table(rel.to).findUnique({ where: target }));
|
|
1035
1035
|
if (!relatedRow) {
|
|
1036
|
-
throw new errors_js_1.ValidationError(`
|
|
1036
|
+
throw new errors_js_1.ValidationError(`connect on "${relName}": no ${rel.to} row found matching ${(0, errors_js_1.describeTargetForMessage)(target)}.`);
|
|
1037
1037
|
}
|
|
1038
1038
|
}
|
|
1039
1039
|
}
|
|
@@ -1088,7 +1088,7 @@ async function processBelongsToCreate(ctx, rel, ops, parentRow, parentTable, dep
|
|
|
1088
1088
|
const target = items[0];
|
|
1089
1089
|
const existing = await ctx.tx.table(rel.to).findUnique({ where: target });
|
|
1090
1090
|
if (!existing) {
|
|
1091
|
-
throw new errors_js_1.ValidationError(`
|
|
1091
|
+
throw new errors_js_1.ValidationError(`connect on "${relName}": no ${rel.to} row found matching ${(0, errors_js_1.describeTargetForMessage)(target)}.`);
|
|
1092
1092
|
}
|
|
1093
1093
|
const updateData = {};
|
|
1094
1094
|
const relatedTable = ctx.schema.tables[rel.to];
|
|
@@ -1138,7 +1138,7 @@ function assertConnectInScope(ctx, rel, child, parentRow, target) {
|
|
|
1138
1138
|
continue;
|
|
1139
1139
|
if (current === key(parentRow[refField]))
|
|
1140
1140
|
continue;
|
|
1141
|
-
throw new errors_js_1.ValidationError(`
|
|
1141
|
+
throw new errors_js_1.ValidationError(`connect refused: ${rel.to} row ${(0, errors_js_1.describeTargetForMessage)(target)} is already owned by ` +
|
|
1142
1142
|
`another "${rel.from}" (its ${fks[i]} is ${current}). \`scopedConnect\` only allows connecting a row ` +
|
|
1143
1143
|
`that is unowned or already owned by this parent; re-parenting must be an explicit update.`);
|
|
1144
1144
|
}
|
|
@@ -1153,7 +1153,7 @@ async function batchConnect(ctx, rel, items, parentRow) {
|
|
|
1153
1153
|
for (const target of items) {
|
|
1154
1154
|
const existing = await ctx.tx.table(rel.to).findUnique({ where: target });
|
|
1155
1155
|
if (!existing) {
|
|
1156
|
-
throw new errors_js_1.ValidationError(`
|
|
1156
|
+
throw new errors_js_1.ValidationError(`connect: no ${rel.to} row found matching ${(0, errors_js_1.describeTargetForMessage)(target)}.`);
|
|
1157
1157
|
}
|
|
1158
1158
|
assertConnectInScope(ctx, rel, existing, parentRow, target);
|
|
1159
1159
|
}
|
|
@@ -1205,7 +1205,7 @@ async function processDisconnect(ctx, rel, disconnectArg, relName, parentRow) {
|
|
|
1205
1205
|
return col?.nullable ?? false;
|
|
1206
1206
|
});
|
|
1207
1207
|
if (!nullable) {
|
|
1208
|
-
throw new errors_js_1.ValidationError(`
|
|
1208
|
+
throw new errors_js_1.ValidationError(`Cannot disconnect "${relName}": foreign key column(s) ${fks.join(', ')} on "${rel.to}" are NOT NULL. Use delete instead.`);
|
|
1209
1209
|
}
|
|
1210
1210
|
const items = toArray(disconnectArg);
|
|
1211
1211
|
if (items.length === 0)
|
|
@@ -1248,7 +1248,7 @@ async function processSet(ctx, rel, setItems, parentRow) {
|
|
|
1248
1248
|
const refField = ctx.schema.tables[rel.from]?.reverseColumnMap[refs[i]] ?? refs[i];
|
|
1249
1249
|
const value = parentRow[refField];
|
|
1250
1250
|
if (value === null || value === undefined) {
|
|
1251
|
-
throw new errors_js_1.ValidationError(`
|
|
1251
|
+
throw new errors_js_1.ValidationError(`Nested set on relation "${rel.name}" cannot run: the parent's reference key ` +
|
|
1252
1252
|
`"${refField}" is ${value === null ? 'null' : 'missing from the loaded row'}, so no child rows ` +
|
|
1253
1253
|
`can be correlated to this parent.`);
|
|
1254
1254
|
}
|
|
@@ -1286,7 +1286,7 @@ async function processNestedUpdate(ctx, rel, updateArg, relName, parentRow) {
|
|
|
1286
1286
|
const correlation = parentCorrelationWhere(ctx, rel, parentRow);
|
|
1287
1287
|
for (const item of items) {
|
|
1288
1288
|
if (!item.where || !item.data) {
|
|
1289
|
-
throw new errors_js_1.ValidationError(`
|
|
1289
|
+
throw new errors_js_1.ValidationError(`Nested update on "${rel.name}" requires both "where" and "data" fields.`);
|
|
1290
1290
|
}
|
|
1291
1291
|
assertTargetSelectsSomething(item.where, 'update', relName, rel);
|
|
1292
1292
|
if (!correlation)
|
|
@@ -1310,7 +1310,7 @@ async function processNestedUpsert(ctx, rel, upsertArg, parentRow, relName) {
|
|
|
1310
1310
|
const correlation = parentCorrelationWhere(ctx, rel, parentRow);
|
|
1311
1311
|
for (const item of items) {
|
|
1312
1312
|
if (!item.where || !item.create || !item.update) {
|
|
1313
|
-
throw new errors_js_1.ValidationError(`
|
|
1313
|
+
throw new errors_js_1.ValidationError(`Nested upsert on "${rel.name}" requires "where", "create", and "update" fields.`);
|
|
1314
1314
|
}
|
|
1315
1315
|
assertTargetSelectsSomething(item.where, 'upsert', relName, rel);
|
|
1316
1316
|
const scoped = correlation ? scopeWhereToParent(item.where, correlation) : null;
|
|
@@ -1332,7 +1332,7 @@ async function processNestedUpsert(ctx, rel, upsertArg, parentRow, relName) {
|
|
|
1332
1332
|
async function processBelongsToUpdate(ctx, rel, updateArg, parentRow, parentTable) {
|
|
1333
1333
|
const item = updateArg;
|
|
1334
1334
|
if (!item.data) {
|
|
1335
|
-
throw new errors_js_1.ValidationError(`
|
|
1335
|
+
throw new errors_js_1.ValidationError(`Nested update on belongsTo "${rel.name}" requires a "data" field.`);
|
|
1336
1336
|
}
|
|
1337
1337
|
// The related row is the one this parent's FK points at. Route through the
|
|
1338
1338
|
// shared correlation helper (like every sibling operation) so a NULL parent
|
|
@@ -1354,7 +1354,7 @@ async function processBelongsToUpdate(ctx, rel, updateArg, parentRow, parentTabl
|
|
|
1354
1354
|
async function processBelongsToUpsert(ctx, rel, upsertArg, parentRow, parentTable) {
|
|
1355
1355
|
const item = upsertArg;
|
|
1356
1356
|
if (!item.where || !item.create || !item.update) {
|
|
1357
|
-
throw new errors_js_1.ValidationError(`
|
|
1357
|
+
throw new errors_js_1.ValidationError(`Nested upsert on belongsTo "${rel.name}" requires "where", "create", and "update" fields.`);
|
|
1358
1358
|
}
|
|
1359
1359
|
// Scope the lookup to the row this parent actually points at. Without the
|
|
1360
1360
|
// correlation, a `where` naming any other row would update a record with no
|
package/dist/cjs/observe.js
CHANGED
|
@@ -164,7 +164,7 @@ class ObserveEngine {
|
|
|
164
164
|
stopped = false;
|
|
165
165
|
constructor(config) {
|
|
166
166
|
if (!config.sink && !config.connectionString) {
|
|
167
|
-
throw new errors_js_1.ValidationError('
|
|
167
|
+
throw new errors_js_1.ValidationError('ObserveEngine: neither `connectionString` nor `sink` was provided, so there is nowhere to ' +
|
|
168
168
|
'flush metrics. Pass `connectionString` (a separate metrics database URL, often TURBINE_OBSERVE_URL) ' +
|
|
169
169
|
'or a custom `sink` implementing ObserveSink.');
|
|
170
170
|
}
|
|
@@ -318,7 +318,11 @@ async function runPipelined(client, queries, options = {}) {
|
|
|
318
318
|
// -----------------------------------------------------------------------
|
|
319
319
|
if (timeout && timeout > 0) {
|
|
320
320
|
timeoutHandle = setTimeout(() => {
|
|
321
|
-
|
|
321
|
+
// TimeoutError (E002), matching $transaction({ timeout }). A bare Error
|
|
322
|
+
// here meant `pipeline({ timeout })` rejected with no .code, no
|
|
323
|
+
// .docsUrl, and failing `instanceof TurbineError`, so the two timeout
|
|
324
|
+
// paths in the same client disagreed about their own contract.
|
|
325
|
+
pipelineError = new errors_js_1.TimeoutError(timeout, 'Pipeline');
|
|
322
326
|
if (connection.stream.destroy) {
|
|
323
327
|
connection.stream.destroy(pipelineError);
|
|
324
328
|
}
|
package/dist/cjs/pipeline.js
CHANGED
|
@@ -222,6 +222,14 @@ async function executePipeline(pool, queries, options) {
|
|
|
222
222
|
// keywords rather than Postgres's.
|
|
223
223
|
return await runSequential(client, queries, poolDialect(pool), options);
|
|
224
224
|
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
// Already-typed Turbine errors pass through untouched; anything else is a
|
|
227
|
+
// driver error that must not escape with a SQLSTATE sitting in the same
|
|
228
|
+
// `.code` slot Turbine puts TURBINE_E0NN in.
|
|
229
|
+
if (err instanceof errors_js_1.TurbineError)
|
|
230
|
+
throw err;
|
|
231
|
+
throw (0, errors_js_1.wrapPgError)(err);
|
|
232
|
+
}
|
|
225
233
|
finally {
|
|
226
234
|
client.release();
|
|
227
235
|
}
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
* wired to a networked `exec`, and `cli/config.ts` teaching the generate
|
|
63
63
|
* funnel to construct a PowDB client instead of a `pg` client for `powdb://`.
|
|
64
64
|
*/
|
|
65
|
-
import { type PowdbCapabilities } from './powdb.js';
|
|
65
|
+
import { type PowdbCapabilities } from './powdb-shared.js';
|
|
66
66
|
import type { SchemaMetadata } from './schema.js';
|
|
67
67
|
/** A minimal rows-returning executor over a PowDB connection (embedded or networked). */
|
|
68
68
|
export type PowdbExec = (powql: string) => Promise<{
|
|
@@ -67,7 +67,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
67
67
|
exports.introspectPowdbDatabase = introspectPowdbDatabase;
|
|
68
68
|
const errors_js_1 = require("./errors.js");
|
|
69
69
|
const introspect_js_1 = require("./introspect.js");
|
|
70
|
-
const
|
|
70
|
+
const powdb_shared_js_1 = require("./powdb-shared.js");
|
|
71
71
|
const schema_js_1 = require("./schema.js");
|
|
72
72
|
/** Coerce a wire cell to string (legacy wire cells are strings; native cells may be typed). */
|
|
73
73
|
function asString(v) {
|
|
@@ -116,7 +116,7 @@ async function introspectPowdbDatabase(exec, options = {}) {
|
|
|
116
116
|
// knows the version, so a pre-0.10 engine gets a typed E017 hint instead of
|
|
117
117
|
// an opaque `unexpected token schema` parse error.
|
|
118
118
|
if (options.capabilities) {
|
|
119
|
-
(0,
|
|
119
|
+
(0, powdb_shared_js_1.requireCapability)(options.capabilities, 'introspection', 'PowDB `describe` introspection');
|
|
120
120
|
}
|
|
121
121
|
// ----- Types (one row per table, columns `name`, `columns`) -----
|
|
122
122
|
const schemaRows = (await exec('schema')).rows;
|
|
@@ -126,7 +126,7 @@ async function introspectPowdbDatabase(exec, options = {}) {
|
|
|
126
126
|
// every table filters out and the schema comes back silently empty. Refuse
|
|
127
127
|
// that instead of losing data: real rows must carry a `name`.
|
|
128
128
|
if (schemaRows.length > 0 && candidateTables.length === 0) {
|
|
129
|
-
throw new errors_js_1.ValidationError(`
|
|
129
|
+
throw new errors_js_1.ValidationError(`PowDB introspection: the \`schema\` statement returned ${schemaRows.length} row(s) but none carried a ` +
|
|
130
130
|
'`name` cell. The `exec` you supplied likely returns POSITIONAL rows (string[][]) rather than records keyed by ' +
|
|
131
131
|
'column name; zip `columns` with each row (see introspectPowdbDatabase docs).');
|
|
132
132
|
}
|
|
@@ -137,7 +137,7 @@ async function introspectPowdbDatabase(exec, options = {}) {
|
|
|
137
137
|
// `describe` needs the table name in bare-identifier position → quote it so
|
|
138
138
|
// a reserved-word / non-bare table name (`order`) does not become a parse
|
|
139
139
|
// error.
|
|
140
|
-
const describeRows = (await exec(`describe ${(0,
|
|
140
|
+
const describeRows = (await exec(`describe ${(0, powdb_shared_js_1.quotePowqlIdent)(tableName)}`)).rows
|
|
141
141
|
// PowDB >= 0.19.1 appends entity-LINK rows after the 4 column rows in
|
|
142
142
|
// `describe <T>` (a `type` cell of literally `"link"`, an Empty nullable
|
|
143
143
|
// slot, and an arrow description in the index slot). They are NOT columns,
|