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/powql.d.ts
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
*
|
|
35
35
|
* @module
|
|
36
36
|
*/
|
|
37
|
-
import {
|
|
37
|
+
import type { PowdbPool } from './powdb.js';
|
|
38
38
|
import type { MiddlewareFn, QueryInterfaceOptions } from './query/index.js';
|
|
39
39
|
import type { AggregateArgs, AggregateResult, CountArgs, CreateArgs, CreateManyArgs, DeleteArgs, DeleteManyArgs, FindManyArgs, FindUniqueArgs, GroupByArgs, UpdateArgs, UpdateManyArgs, UpsertArgs } from './query/types.js';
|
|
40
40
|
import { type SchemaMetadata } from './schema.js';
|
package/dist/powql.js
CHANGED
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
import { randomUUID } from 'node:crypto';
|
|
38
38
|
import { NotFoundError, OptimisticLockError, ReadOnlyError, TimeoutError, UnsupportedFeatureError, ValidationError, } from './errors.js';
|
|
39
39
|
import { executeNestedCreate, executeNestedUpdate, hasRelationFields, } from './nested-write.js';
|
|
40
|
-
import { ALL_POWDB_CAPABILITIES, coerceNativeValue, isJsonColumn, isPowdbDatetimeColumn, isStaleFramePowdbError, PowdbFloatParam, PowdbJsonParam, powqlColumnType, quotePowqlDotted, quotePowqlIdent, requireCapability, rowToEntity, } from './powdb.js';
|
|
40
|
+
import { ALL_POWDB_CAPABILITIES, baseTsType, coerceNativeValue, isJsonColumn, isPowdbDatetimeColumn, isStaleFramePowdbError, PowdbFloatParam, PowdbJsonParam, powqlColumnType, quotePowqlDotted, quotePowqlIdent, requireCapability, rowToEntity, } from './powdb-shared.js';
|
|
41
41
|
import { assertAggregatePiiOptIn } from './query/aggregates.js';
|
|
42
42
|
import { assertWhereIdentifiesOneRow, expandCompoundUniqueWhere } from './query/compound-unique.js';
|
|
43
43
|
import { ARRAY_OPERATOR_KEYS, isJsonFilter, isRelationPickOrderBy, orderByEntries } from './query/filters.js';
|
|
@@ -189,7 +189,7 @@ export class PowqlInterface {
|
|
|
189
189
|
this.options = options;
|
|
190
190
|
const meta = schema.tables[table];
|
|
191
191
|
if (!meta) {
|
|
192
|
-
throw new ValidationError(`
|
|
192
|
+
throw new ValidationError(`Unknown table "${table}". ${availableClause(Object.keys(schema.tables), 'The schema has no tables.')}`);
|
|
193
193
|
}
|
|
194
194
|
this.meta = meta;
|
|
195
195
|
this.defaultLimit = options.defaultLimit;
|
|
@@ -222,7 +222,7 @@ export class PowqlInterface {
|
|
|
222
222
|
const snake = resolved ?? ownLookup(this.meta.columnMap, field) ?? field;
|
|
223
223
|
const col = this.meta.columns.find((c) => c.name === snake || c.field === field);
|
|
224
224
|
if (!col) {
|
|
225
|
-
throw new ValidationError(`
|
|
225
|
+
throw new ValidationError(`Unknown column "${field}" on table "${this.table}". Known: ${this.meta.columns
|
|
226
226
|
.map((c) => c.field)
|
|
227
227
|
.join(', ')}`);
|
|
228
228
|
}
|
|
@@ -326,7 +326,7 @@ export class PowqlInterface {
|
|
|
326
326
|
*/
|
|
327
327
|
writeRef(value, col, params) {
|
|
328
328
|
if (typeof value === 'number' && !Number.isFinite(value) && this.isFloatCol(col)) {
|
|
329
|
-
throw new ValidationError(`
|
|
329
|
+
throw new ValidationError(`Non-finite value for float column "${col.name}" on "${this.table}".`);
|
|
330
330
|
}
|
|
331
331
|
return this.param(value, params, col);
|
|
332
332
|
}
|
|
@@ -452,7 +452,7 @@ export class PowqlInterface {
|
|
|
452
452
|
['offset', offset],
|
|
453
453
|
]) {
|
|
454
454
|
if (value !== undefined && value !== null && value < 0) {
|
|
455
|
-
throw new ValidationError(
|
|
455
|
+
throw new ValidationError(`${context} on "${this.table}": \`${name}\` must not be negative (got ${value}).`);
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
458
|
}
|
|
@@ -502,7 +502,7 @@ export class PowqlInterface {
|
|
|
502
502
|
// Relation filters are pre-resolved to scalar in/notIn by
|
|
503
503
|
// resolveRelationFilters() before buildWhere runs, reaching here means
|
|
504
504
|
// a caller skipped that step (an internal bug, not user error).
|
|
505
|
-
throw new ValidationError(`
|
|
505
|
+
throw new ValidationError(`internal: relation filter "${key}" reached buildWhere unresolved (missing resolveRelationFilters()).`);
|
|
506
506
|
}
|
|
507
507
|
else {
|
|
508
508
|
// A JsonFilter (or a bare `{ path }`) can compile to zero clauses; skip
|
|
@@ -632,7 +632,7 @@ export class PowqlInterface {
|
|
|
632
632
|
conds.push(`${lhs} like ${this.bindLike(`%${escapeLike(String(opVal))}`, params, insensitive)}`);
|
|
633
633
|
break;
|
|
634
634
|
default:
|
|
635
|
-
throw new ValidationError(`
|
|
635
|
+
throw new ValidationError(`Unsupported operator "${opName}" on PowDB.`);
|
|
636
636
|
}
|
|
637
637
|
}
|
|
638
638
|
return conds.length > 1 ? `(${conds.join(' and ')})` : (conds[0] ?? this.alwaysFalse());
|
|
@@ -720,14 +720,14 @@ export class PowqlInterface {
|
|
|
720
720
|
if (v === undefined)
|
|
721
721
|
continue;
|
|
722
722
|
if (filter.path === undefined) {
|
|
723
|
-
throw new ValidationError(`
|
|
723
|
+
throw new ValidationError(`JSON range operator '${op}' on ${col.name} requires a \`path\` ` +
|
|
724
724
|
`(e.g. { path: ['meta', 'score'], ${op}: ${JSON.stringify(v)} }).`);
|
|
725
725
|
}
|
|
726
726
|
if (typeof v !== 'number' && typeof v !== 'string') {
|
|
727
|
-
throw new ValidationError(`
|
|
727
|
+
throw new ValidationError(`JSON range operator '${op}' on ${col.name} requires a number or string, got ${JSON.stringify(v)}.`);
|
|
728
728
|
}
|
|
729
729
|
if (typeof v === 'number' && !Number.isFinite(v)) {
|
|
730
|
-
throw new ValidationError(`
|
|
730
|
+
throw new ValidationError(`JSON range operator '${op}' on ${col.name} requires a finite number.`);
|
|
731
731
|
}
|
|
732
732
|
conds.push(`${pathP()} ${powOp} ${this.param(v, params)}`);
|
|
733
733
|
}
|
|
@@ -743,7 +743,7 @@ export class PowqlInterface {
|
|
|
743
743
|
// generator bug rather than a user error, and must not silently widen the
|
|
744
744
|
// query the way it used to.
|
|
745
745
|
if (!conds.length) {
|
|
746
|
-
throw new ValidationError(`
|
|
746
|
+
throw new ValidationError(`internal: JSON filter on "${col.name}" compiled to no condition. This is a bug in turbine-orm.`);
|
|
747
747
|
}
|
|
748
748
|
return conds.length > 1 ? `(${conds.join(' and ')})` : conds[0];
|
|
749
749
|
}
|
|
@@ -909,7 +909,7 @@ export class PowqlInterface {
|
|
|
909
909
|
}
|
|
910
910
|
const targetMeta = this.schema.tables[rel.to];
|
|
911
911
|
if (!targetMeta)
|
|
912
|
-
throw new ValidationError(`
|
|
912
|
+
throw new ValidationError(`Relation "${rel.name}" targets unknown table "${rel.to}".`);
|
|
913
913
|
// hasMany/hasOne: localKey = our referenceKey, collect the child's foreignKey.
|
|
914
914
|
// belongsTo: localKey = our foreignKey, collect the target's referenceKey.
|
|
915
915
|
const localCol = rel.type === 'belongsTo' ? fk[0] : rk[0];
|
|
@@ -938,13 +938,13 @@ export class PowqlInterface {
|
|
|
938
938
|
async resolveManyToManyCondition(rel, mode, innerWhere, innerEmpty, timeout) {
|
|
939
939
|
const through = rel.through;
|
|
940
940
|
if (!through)
|
|
941
|
-
throw new ValidationError(`
|
|
941
|
+
throw new ValidationError(`manyToMany relation "${rel.name}" is missing its junction (\`through\`).`);
|
|
942
942
|
const sourceJ = normalizeKeyColumns(through.sourceKey);
|
|
943
943
|
const targetJ = normalizeKeyColumns(through.targetKey);
|
|
944
944
|
const sourceRef = normalizeKeyColumns(rel.referenceKey);
|
|
945
945
|
const targetMeta = this.schema.tables[rel.to];
|
|
946
946
|
if (!targetMeta)
|
|
947
|
-
throw new ValidationError(`
|
|
947
|
+
throw new ValidationError(`Relation "${rel.name}" targets unknown table "${rel.to}".`);
|
|
948
948
|
if (sourceJ.length > 1 || targetJ.length > 1 || sourceRef.length > 1 || targetMeta.primaryKey.length > 1) {
|
|
949
949
|
throw new UnsupportedFeatureError('composite-key manyToMany filters', 'PowDB', `relation "${rel.name}", PowQL has no tuple-\`in\` for composite junction/target keys`);
|
|
950
950
|
}
|
|
@@ -1282,9 +1282,9 @@ export class PowqlInterface {
|
|
|
1282
1282
|
}
|
|
1283
1283
|
/** Build the E018 refusal for a write / `begin` on a read-only pool. */
|
|
1284
1284
|
readOnlyError(operation) {
|
|
1285
|
-
// Pass a clean detail: the ReadOnlyError constructor owns
|
|
1286
|
-
//
|
|
1287
|
-
//
|
|
1285
|
+
// Pass a clean detail: the ReadOnlyError constructor owns the "Route writes
|
|
1286
|
+
// to a writable primary." hint, so adding it here would double it. The
|
|
1287
|
+
// `[TURBINE_E018]` tag is added once, later, by formatErrorMessage.
|
|
1288
1288
|
return new ReadOnlyError(`${operation} on "${this.table}" refused: this PowDB connection is read-only.`);
|
|
1289
1289
|
}
|
|
1290
1290
|
/**
|
|
@@ -1651,7 +1651,7 @@ export class PowqlInterface {
|
|
|
1651
1651
|
*/
|
|
1652
1652
|
async loadRelations(parents, withClause, timeout, depth = 0, parent, includePii = false) {
|
|
1653
1653
|
if (depth >= 10) {
|
|
1654
|
-
throw new ValidationError(`
|
|
1654
|
+
throw new ValidationError(`Nested 'with' on PowDB exceeded depth 10 (relation cycle?).`);
|
|
1655
1655
|
}
|
|
1656
1656
|
if (!parents.length)
|
|
1657
1657
|
return;
|
|
@@ -1666,7 +1666,7 @@ export class PowqlInterface {
|
|
|
1666
1666
|
continue;
|
|
1667
1667
|
const rel = this.meta.relations[relName];
|
|
1668
1668
|
if (!rel)
|
|
1669
|
-
throw new ValidationError(`
|
|
1669
|
+
throw new ValidationError(`Unknown relation "${relName}" on "${this.table}".`);
|
|
1670
1670
|
if (strategyIsJoin && parent && this.joinEligible(rel, opt, parent.args, parents.length)) {
|
|
1671
1671
|
if (this.capabilities.serverJoins) {
|
|
1672
1672
|
await this.loadRelationViaJoin(parents, rel, relName, opt, parent, timeout, includePii);
|
|
@@ -1793,13 +1793,13 @@ export class PowqlInterface {
|
|
|
1793
1793
|
async loadManyToMany(parents, rel, relName, opt, timeout, includePii = false) {
|
|
1794
1794
|
const through = rel.through;
|
|
1795
1795
|
if (!through)
|
|
1796
|
-
throw new ValidationError(`
|
|
1796
|
+
throw new ValidationError(`manyToMany relation "${relName}" is missing its junction (\`through\`).`);
|
|
1797
1797
|
const sourceJ = normalizeKeyColumns(through.sourceKey);
|
|
1798
1798
|
const targetJ = normalizeKeyColumns(through.targetKey);
|
|
1799
1799
|
const sourceRef = normalizeKeyColumns(rel.referenceKey);
|
|
1800
1800
|
const targetMeta = this.schema.tables[rel.to];
|
|
1801
1801
|
if (!targetMeta)
|
|
1802
|
-
throw new ValidationError(`
|
|
1802
|
+
throw new ValidationError(`Relation "${relName}" targets unknown table "${rel.to}".`);
|
|
1803
1803
|
if (sourceJ.length > 1 || targetJ.length > 1 || sourceRef.length > 1 || targetMeta.primaryKey.length > 1) {
|
|
1804
1804
|
throw new UnsupportedFeatureError('composite-key manyToMany', 'PowDB', `relation "${relName}", PowQL has no tuple-\`in\`, so composite junction/target keys can't be loaded`);
|
|
1805
1805
|
}
|
|
@@ -2033,7 +2033,7 @@ export class PowqlInterface {
|
|
|
2033
2033
|
const options = (opt === true ? {} : opt);
|
|
2034
2034
|
const targetMeta = this.schema.tables[rel.to];
|
|
2035
2035
|
if (!targetMeta)
|
|
2036
|
-
throw new ValidationError(`
|
|
2036
|
+
throw new ValidationError(`Relation "${relName}" targets unknown table "${rel.to}".`);
|
|
2037
2037
|
const targetQi = new PowqlInterface(this.pool, rel.to, this.schema, [], this.options);
|
|
2038
2038
|
const fk = normalizeKeyColumns(rel.foreignKey);
|
|
2039
2039
|
const rk = normalizeKeyColumns(rel.referenceKey);
|
|
@@ -2073,11 +2073,11 @@ export class PowqlInterface {
|
|
|
2073
2073
|
async loadManyToManyViaJoin(parents, rel, relName, opt, parent, timeout, includePii = false) {
|
|
2074
2074
|
const through = rel.through;
|
|
2075
2075
|
if (!through)
|
|
2076
|
-
throw new ValidationError(`
|
|
2076
|
+
throw new ValidationError(`manyToMany relation "${relName}" is missing its junction (\`through\`).`);
|
|
2077
2077
|
const options = (opt === true ? {} : opt);
|
|
2078
2078
|
const targetMeta = this.schema.tables[rel.to];
|
|
2079
2079
|
if (!targetMeta)
|
|
2080
|
-
throw new ValidationError(`
|
|
2080
|
+
throw new ValidationError(`Relation "${relName}" targets unknown table "${rel.to}".`);
|
|
2081
2081
|
const targetQi = new PowqlInterface(this.pool, rel.to, this.schema, [], this.options);
|
|
2082
2082
|
const sourceJCol = normalizeKeyColumns(through.sourceKey)[0];
|
|
2083
2083
|
const targetJCol = normalizeKeyColumns(through.targetKey)[0];
|
|
@@ -2107,7 +2107,7 @@ export class PowqlInterface {
|
|
|
2107
2107
|
joinChildCols(targetQi, options, includePii = false) {
|
|
2108
2108
|
const plan = targetQi.projectionPlan(options.select, options.omit, includePii);
|
|
2109
2109
|
if (plan.cols.includes('__tpk')) {
|
|
2110
|
-
throw new ValidationError(`
|
|
2110
|
+
throw new ValidationError(`relation target "${targetQi.table}" has a column named "__tpk", which collides with the reserved ` +
|
|
2111
2111
|
`join correlation alias. Rename the column or load this relation with relationLoadStrategy: 'batched'.`);
|
|
2112
2112
|
}
|
|
2113
2113
|
return plan;
|
|
@@ -2242,7 +2242,7 @@ export class PowqlInterface {
|
|
|
2242
2242
|
const { cols, forcedPk } = targetQi.projectionPlan(options.select, options.omit, includePii);
|
|
2243
2243
|
const byName = new Map(targetQi.meta.columns.map((c) => [c.name, c]));
|
|
2244
2244
|
for (const c of cols) {
|
|
2245
|
-
const ts = (byName.get(c)?.tsType ?? '')
|
|
2245
|
+
const ts = baseTsType(byName.get(c)?.tsType ?? '');
|
|
2246
2246
|
if (ts === 'bigint' || ts === 'Uint8Array')
|
|
2247
2247
|
return null;
|
|
2248
2248
|
}
|
|
@@ -2449,7 +2449,7 @@ export class PowqlInterface {
|
|
|
2449
2449
|
// at least one projected child column is bigint/bytes. Otherwise nested
|
|
2450
2450
|
// projections already handle it (and cache), so leave it to them.
|
|
2451
2451
|
const hasCarrierBlockedCol = userCols.some((c) => {
|
|
2452
|
-
const ts = (byName.get(c)?.tsType ?? '')
|
|
2452
|
+
const ts = baseTsType(byName.get(c)?.tsType ?? '');
|
|
2453
2453
|
return ts === 'bigint' || ts === 'Uint8Array';
|
|
2454
2454
|
});
|
|
2455
2455
|
if (!hasCarrierBlockedCol)
|
|
@@ -2633,7 +2633,7 @@ export class PowqlInterface {
|
|
|
2633
2633
|
parts.push(`does not supply ${quoteList(missing)}`);
|
|
2634
2634
|
if (unexpected.length > 0)
|
|
2635
2635
|
parts.push(`supplies ${quoteList(unexpected)}, which the first row does not`);
|
|
2636
|
-
throw new ValidationError(`
|
|
2636
|
+
throw new ValidationError(`createMany on "${this.table}": row ${i} ${parts.join(' and ')}. ` +
|
|
2637
2637
|
'Every row must supply the same fields (a field set to `undefined` counts as omitted, exactly as it does ' +
|
|
2638
2638
|
'in `create`). PowQL itself would insert the ragged rows, but the SQL engines build ONE statement whose ' +
|
|
2639
2639
|
"column list comes from the first row, so there a later row's extra field is dropped and a field it " +
|
|
@@ -2745,14 +2745,14 @@ export class PowqlInterface {
|
|
|
2745
2745
|
else if ('divide' in opObj)
|
|
2746
2746
|
parts.push(`${col} := ${ref} / ${this.writeRef(opObj.divide, colMeta, params)}`);
|
|
2747
2747
|
else
|
|
2748
|
-
throw new ValidationError(`
|
|
2748
|
+
throw new ValidationError(`Unsupported update operator on "${field}".`);
|
|
2749
2749
|
}
|
|
2750
2750
|
else {
|
|
2751
2751
|
parts.push(`${col} := ${this.writeRef(value, colMeta, params)}`);
|
|
2752
2752
|
}
|
|
2753
2753
|
}
|
|
2754
2754
|
if (!parts.length)
|
|
2755
|
-
throw new ValidationError(`
|
|
2755
|
+
throw new ValidationError(`update on "${this.table}" has no fields to set.`);
|
|
2756
2756
|
return parts.join(', ');
|
|
2757
2757
|
}
|
|
2758
2758
|
// -------------------------------------------------------------------------
|
|
@@ -2914,7 +2914,7 @@ export class PowqlInterface {
|
|
|
2914
2914
|
return { field, value: createData[field] ?? createData[pk] };
|
|
2915
2915
|
});
|
|
2916
2916
|
if (pkPairs.some((p) => p.value == null)) {
|
|
2917
|
-
throw new ValidationError(`
|
|
2917
|
+
throw new ValidationError(`upsert on "${this.table}" needs every composite-PK field in \`create\` (${pkPairs
|
|
2918
2918
|
.map((p) => p.field)
|
|
2919
2919
|
.join(', ')}).`);
|
|
2920
2920
|
}
|
|
@@ -3050,7 +3050,7 @@ export class PowqlInterface {
|
|
|
3050
3050
|
const usedKeys = new Set();
|
|
3051
3051
|
const claim = (key, what) => {
|
|
3052
3052
|
if (key === '_count' || usedKeys.has(key)) {
|
|
3053
|
-
throw new ValidationError(`
|
|
3053
|
+
throw new ValidationError(`groupBy output name "${key}" (${what}) collides with another output column on table ` +
|
|
3054
3054
|
`"${this.table}": set an explicit \`alias\` (or rename the aggregate key) to disambiguate.`);
|
|
3055
3055
|
}
|
|
3056
3056
|
usedKeys.add(key);
|
|
@@ -3081,7 +3081,7 @@ export class PowqlInterface {
|
|
|
3081
3081
|
else {
|
|
3082
3082
|
const col = this.column(entry.field);
|
|
3083
3083
|
if (!isJsonColumn(col)) {
|
|
3084
|
-
throw new ValidationError(`
|
|
3084
|
+
throw new ValidationError(`groupBy JSON group key on "${entry.field}" (table "${this.table}") requires a json column.`);
|
|
3085
3085
|
}
|
|
3086
3086
|
assertAggregatePiiOptIn(this.table, this.meta, entry.field, col.name, 'groupBy JSON `by` key', resolveUnsafeFlag(args.includePii, 'includePii'));
|
|
3087
3087
|
this.assertJsonPath('group key', entry.field, entry.path);
|
|
@@ -3187,7 +3187,7 @@ export class PowqlInterface {
|
|
|
3187
3187
|
else {
|
|
3188
3188
|
const col = this.column(target.field);
|
|
3189
3189
|
if (!isJsonColumn(col)) {
|
|
3190
|
-
throw new ValidationError(`
|
|
3190
|
+
throw new ValidationError(`groupBy ${fn} target "${key}" on "${target.field}" (table "${this.table}") requires a json column.`);
|
|
3191
3191
|
}
|
|
3192
3192
|
if (fn === '_min' || fn === '_max') {
|
|
3193
3193
|
assertAggregatePiiOptIn(this.table, this.meta, target.field, col.name, `groupBy ${fn} JSON target`, resolveUnsafeFlag(args.includePii, 'includePii'));
|
|
@@ -3195,7 +3195,7 @@ export class PowqlInterface {
|
|
|
3195
3195
|
this.assertJsonPath(`${fn} target "${key}"`, target.field, target.path);
|
|
3196
3196
|
const alwaysNumeric = fn === '_sum' || fn === '_avg';
|
|
3197
3197
|
if (alwaysNumeric && target.type === 'text') {
|
|
3198
|
-
throw new ValidationError(`
|
|
3198
|
+
throw new ValidationError(`groupBy ${fn} target "${key}" on table "${this.table}": ` +
|
|
3199
3199
|
`${fn} over a JSON path is always numeric: remove \`type: 'text'\`.`);
|
|
3200
3200
|
}
|
|
3201
3201
|
const numeric = alwaysNumeric || target.type === 'numeric';
|
|
@@ -3263,7 +3263,7 @@ export class PowqlInterface {
|
|
|
3263
3263
|
if (!Array.isArray(path) ||
|
|
3264
3264
|
path.length === 0 ||
|
|
3265
3265
|
path.some((el) => typeof el !== 'string' && !(typeof el === 'number' && Number.isFinite(el)))) {
|
|
3266
|
-
throw new ValidationError(`
|
|
3266
|
+
throw new ValidationError(`groupBy ${context} on "${field}" (table "${this.table}") requires a non-empty \`path\` ` +
|
|
3267
3267
|
`array of keys/indexes (e.g. { field: '${field}', path: ['category'] }).`);
|
|
3268
3268
|
}
|
|
3269
3269
|
}
|
|
@@ -3315,7 +3315,7 @@ export class PowqlInterface {
|
|
|
3315
3315
|
// must come from the fixed allowlist, never from the caller's key.
|
|
3316
3316
|
const fn = POWQL_HAVING_AGG_FNS.get(aggKey);
|
|
3317
3317
|
if (!fn) {
|
|
3318
|
-
throw new ValidationError(`
|
|
3318
|
+
throw new ValidationError(`Unknown aggregate "${aggKey}" in having for field "${key}" on table "${this.table}". ` +
|
|
3319
3319
|
`Supported: ${[...POWQL_HAVING_AGG_FNS.keys()].join(', ')}.`);
|
|
3320
3320
|
}
|
|
3321
3321
|
const inner = this.lookupGroupKey(aggInner, key) ?? this.ref(key);
|
|
@@ -3353,14 +3353,14 @@ export class PowqlInterface {
|
|
|
3353
3353
|
if (key === '_count') {
|
|
3354
3354
|
const expr = aggOrderExprs.get('_count');
|
|
3355
3355
|
if (!expr) {
|
|
3356
|
-
throw new ValidationError(`
|
|
3356
|
+
throw new ValidationError(`Cannot order groupBy by "_count" on table "${this.table}": _count is not selected. ` +
|
|
3357
3357
|
`Orderable keys: ${validKeys()}.`);
|
|
3358
3358
|
}
|
|
3359
3359
|
parts.push(`${expr} ${this.groupOrderDir(value, '_count')}`);
|
|
3360
3360
|
continue;
|
|
3361
3361
|
}
|
|
3362
3362
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
3363
|
-
throw new ValidationError(`
|
|
3363
|
+
throw new ValidationError(`Invalid groupBy orderBy for "${key}" on table "${this.table}": ` +
|
|
3364
3364
|
`expected a field map like { ${key}: { amount: 'desc' } }.`);
|
|
3365
3365
|
}
|
|
3366
3366
|
for (const [field, dirSpec] of Object.entries(value)) {
|
|
@@ -3370,7 +3370,7 @@ export class PowqlInterface {
|
|
|
3370
3370
|
// the registry is keyed by the canonical one.
|
|
3371
3371
|
const expr = aggOrderExprs.get(`${key}:${field}`) ?? this.aggOrderExpr(aggOrderExprs, key, field);
|
|
3372
3372
|
if (!expr) {
|
|
3373
|
-
throw new ValidationError(`
|
|
3373
|
+
throw new ValidationError(`Cannot order groupBy by "${key}.${field}" on table "${this.table}": ` +
|
|
3374
3374
|
`that aggregate is not requested in this call. Orderable keys: ${validKeys()}.`);
|
|
3375
3375
|
}
|
|
3376
3376
|
parts.push(`${expr} ${this.groupOrderDir(dirSpec, `${key}.${field}`)}`);
|
|
@@ -3379,7 +3379,7 @@ export class PowqlInterface {
|
|
|
3379
3379
|
}
|
|
3380
3380
|
const expr = this.lookupGroupKey(byOrderExprs, key);
|
|
3381
3381
|
if (!expr) {
|
|
3382
|
-
throw new ValidationError(`
|
|
3382
|
+
throw new ValidationError(`Unknown field "${key}" in groupBy orderBy on table "${this.table}". Orderable keys: ${validKeys()}.`);
|
|
3383
3383
|
}
|
|
3384
3384
|
parts.push(`${expr} ${this.groupOrderDir(value, key)}`);
|
|
3385
3385
|
}
|
|
@@ -3469,14 +3469,14 @@ export class PowqlInterface {
|
|
|
3469
3469
|
return;
|
|
3470
3470
|
if (compiledWhere.length > 0)
|
|
3471
3471
|
return;
|
|
3472
|
-
throw new ValidationError(
|
|
3472
|
+
throw new ValidationError(`${action} on "${this.table}" refused: the \`where\` clause is empty. ` +
|
|
3473
3473
|
"Pass `allowFullTableScan: UNSAFE` to opt in (import { UNSAFE } from 'turbine-orm'), " +
|
|
3474
3474
|
'or check that your filter values are defined.');
|
|
3475
3475
|
}
|
|
3476
3476
|
}
|
|
3477
3477
|
/** Coerce a group-key scalar string by the column's TS type (numbers/bools). */
|
|
3478
3478
|
function coerceScalar(raw, tsType) {
|
|
3479
|
-
const ts = tsType
|
|
3479
|
+
const ts = baseTsType(tsType);
|
|
3480
3480
|
if (raw === 'null')
|
|
3481
3481
|
return null;
|
|
3482
3482
|
if (ts === 'number' || ts === 'bigint') {
|
package/dist/prisma-compat.js
CHANGED
|
@@ -324,7 +324,7 @@ function assertTranslateDepth(depth, clause) {
|
|
|
324
324
|
// The two clauses core also walks raise core's own error, verbatim.
|
|
325
325
|
if (clause === 'where' || clause === 'having')
|
|
326
326
|
assertWhereDepth(depth, clause);
|
|
327
|
-
throw new ValidationError(
|
|
327
|
+
throw new ValidationError(`\`${clause}\` nests more than ${MAX_WHERE_DEPTH} levels deep. That is far past anything a real ` +
|
|
328
328
|
`query needs, and an unbounded walk over caller-supplied nesting is a stack-overflow surface, so it is ` +
|
|
329
329
|
`refused. If this is a generated argument, flatten it: a single array of N entries is one level, not N.`);
|
|
330
330
|
}
|
|
@@ -715,7 +715,7 @@ function translateOmit(ctx, mm, omit) {
|
|
|
715
715
|
if (val === false || val == null)
|
|
716
716
|
continue;
|
|
717
717
|
if (mm.relations[key]) {
|
|
718
|
-
throw new ValidationError(`
|
|
718
|
+
throw new ValidationError(`prisma-compat: \`omit\` on model "${modelName(ctx, mm)}" takes scalar fields, but "${key}" ` +
|
|
719
719
|
'is a relation. Leave the relation out of `include` instead.');
|
|
720
720
|
}
|
|
721
721
|
out[renameField(mm, key)] = true;
|
|
@@ -741,10 +741,10 @@ function translateProjection(ctx, mm, args, depth = 0) {
|
|
|
741
741
|
const select = args.select;
|
|
742
742
|
const omitArg = args.omit;
|
|
743
743
|
if (include && select) {
|
|
744
|
-
throw new ValidationError('
|
|
744
|
+
throw new ValidationError('prisma-compat: `include` and `select` are mutually exclusive.');
|
|
745
745
|
}
|
|
746
746
|
if (select && omitArg) {
|
|
747
|
-
throw new ValidationError('
|
|
747
|
+
throw new ValidationError('prisma-compat: `select` and `omit` are mutually exclusive. ' +
|
|
748
748
|
'A `select` already lists exactly the fields you want.');
|
|
749
749
|
}
|
|
750
750
|
const omit = omitArg ? translateOmit(ctx, mm, omitArg) : undefined;
|
|
@@ -761,7 +761,7 @@ function translateProjection(ctx, mm, args, depth = 0) {
|
|
|
761
761
|
}
|
|
762
762
|
const rel = mm.relations[key];
|
|
763
763
|
if (!rel) {
|
|
764
|
-
throw new ValidationError(`
|
|
764
|
+
throw new ValidationError(`prisma-compat: unknown relation "${key}" in include on model "${modelName(ctx, mm)}".`);
|
|
765
765
|
}
|
|
766
766
|
withClause[rel.name] = translateWithOption(ctx, mm, rel.name, val, depth + 1);
|
|
767
767
|
hasWith = true;
|
|
@@ -816,7 +816,7 @@ function assertWriteProjectionField(ctx, mm, op, clause, key) {
|
|
|
816
816
|
return;
|
|
817
817
|
const known = Object.keys(mm.fields);
|
|
818
818
|
const suggestion = suggestKey(key, new Set(known));
|
|
819
|
-
throw new ValidationError(`
|
|
819
|
+
throw new ValidationError(`prisma-compat: unknown field "${key}" in \`${clause}\` on ${modelName(ctx, mm)}.${op}().` +
|
|
820
820
|
(suggestion ? ` Did you mean "${suggestion}"?` : '') +
|
|
821
821
|
` Known fields: ${known.join(', ') || '(none)'}.`);
|
|
822
822
|
}
|
|
@@ -848,10 +848,10 @@ function resolveWriteProjection(ctx, mm, op, args) {
|
|
|
848
848
|
const select = args.select;
|
|
849
849
|
const omitArg = args.omit;
|
|
850
850
|
if (include && select) {
|
|
851
|
-
throw new ValidationError('
|
|
851
|
+
throw new ValidationError('prisma-compat: `include` and `select` are mutually exclusive.');
|
|
852
852
|
}
|
|
853
853
|
if (select && omitArg) {
|
|
854
|
-
throw new ValidationError('
|
|
854
|
+
throw new ValidationError('prisma-compat: `select` and `omit` are mutually exclusive. ' +
|
|
855
855
|
'A `select` already lists exactly the fields you want.');
|
|
856
856
|
}
|
|
857
857
|
if (include) {
|
|
@@ -885,7 +885,7 @@ function resolveWriteProjection(ctx, mm, op, args) {
|
|
|
885
885
|
if (val === false || val == null)
|
|
886
886
|
continue;
|
|
887
887
|
if (mm.relations[key]) {
|
|
888
|
-
throw new ValidationError(`
|
|
888
|
+
throw new ValidationError(`prisma-compat: \`omit\` on model "${modelName(ctx, mm)}" takes scalar fields, but "${key}" ` +
|
|
889
889
|
'is a relation.');
|
|
890
890
|
}
|
|
891
891
|
assertWriteProjectionField(ctx, mm, op, 'omit', key);
|
|
@@ -1768,7 +1768,7 @@ function makeDelegate(ctx, mm, getQI, runInTx) {
|
|
|
1768
1768
|
};
|
|
1769
1769
|
const requireWhere = (args, op) => {
|
|
1770
1770
|
if (!args || args.where === undefined) {
|
|
1771
|
-
throw new ValidationError(`
|
|
1771
|
+
throw new ValidationError(`prisma-compat: ${op} on "${modelName(ctx, mm)}" requires a \`where\`.`);
|
|
1772
1772
|
}
|
|
1773
1773
|
return args;
|
|
1774
1774
|
};
|
|
@@ -1934,7 +1934,7 @@ function makeDelegate(ctx, mm, getQI, runInTx) {
|
|
|
1934
1934
|
function poolOf(db) {
|
|
1935
1935
|
const pool = db.pool;
|
|
1936
1936
|
if (!pool)
|
|
1937
|
-
throw new ValidationError('
|
|
1937
|
+
throw new ValidationError('prisma-compat: raw SQL needs a TurbineClient with an active pool.');
|
|
1938
1938
|
return pool;
|
|
1939
1939
|
}
|
|
1940
1940
|
function placeholderOf(db) {
|
|
@@ -2127,7 +2127,7 @@ const EMPTY_EXTENSIONS = { client: {}, model: new Map(), allModels: {} };
|
|
|
2127
2127
|
*/
|
|
2128
2128
|
function applyExtension(prev, ext, modelKeys, clientKeys) {
|
|
2129
2129
|
if (typeof ext !== 'object' || ext === null) {
|
|
2130
|
-
throw new ValidationError('
|
|
2130
|
+
throw new ValidationError('prisma-compat: $extends expects an extension object or a function ' +
|
|
2131
2131
|
`(Prisma.defineExtension callback form), received ${ext === null ? 'null' : typeof ext}.`);
|
|
2132
2132
|
}
|
|
2133
2133
|
for (const [component, value] of Object.entries(ext)) {
|
|
@@ -2145,7 +2145,7 @@ function applyExtension(prev, ext, modelKeys, clientKeys) {
|
|
|
2145
2145
|
const client = { ...prev.client };
|
|
2146
2146
|
for (const [name, member] of Object.entries(ext.client ?? {})) {
|
|
2147
2147
|
if (clientKeys.has(name)) {
|
|
2148
|
-
throw new ValidationError(`
|
|
2148
|
+
throw new ValidationError(`prisma-compat: $extends \`client\` member "${name}" would shadow an existing client ` +
|
|
2149
2149
|
'member (a model delegate or a client-level method). Rename it.');
|
|
2150
2150
|
}
|
|
2151
2151
|
client[name] = member;
|
|
@@ -2159,7 +2159,7 @@ function applyExtension(prev, ext, modelKeys, clientKeys) {
|
|
|
2159
2159
|
}
|
|
2160
2160
|
const canonical = modelKeys.get(key);
|
|
2161
2161
|
if (!canonical) {
|
|
2162
|
-
throw new ValidationError(`
|
|
2162
|
+
throw new ValidationError(`prisma-compat: $extends \`model\` key "${key}" is not a model on this client. ` +
|
|
2163
2163
|
`Known models: ${[...new Set(modelKeys.values())].sort().join(', ') || '(none)'}.`);
|
|
2164
2164
|
}
|
|
2165
2165
|
model.set(canonical, { ...(model.get(canonical) ?? {}), ...members });
|
|
@@ -2265,7 +2265,7 @@ export function createPrismaCompatClient(client, map, options = {}) {
|
|
|
2265
2265
|
*/
|
|
2266
2266
|
const txRunRaw = (tx) => async (text, params) => {
|
|
2267
2267
|
if (typeof tx.rawQuery !== 'function') {
|
|
2268
|
-
throw decorate(new ValidationError('
|
|
2268
|
+
throw decorate(new ValidationError('prisma-compat: raw SQL inside $transaction needs a transaction client that can execute it ' +
|
|
2269
2269
|
'(core TransactionClient.rawQuery). Refusing to run the statement on a pool connection, which would ' +
|
|
2270
2270
|
'silently place it outside the transaction.'), ctx.options.prismaErrorCodes);
|
|
2271
2271
|
}
|
|
@@ -2310,7 +2310,7 @@ export function createPrismaCompatClient(client, map, options = {}) {
|
|
|
2310
2310
|
const batchables = arg.map((p, i) => {
|
|
2311
2311
|
const b = batchableOf(p);
|
|
2312
2312
|
if (!b) {
|
|
2313
|
-
throw new ValidationError(`
|
|
2313
|
+
throw new ValidationError(`prisma-compat: $transaction([...]) item ${i} is not a lazy model call. Pass un-awaited delegate calls (e.g. prisma.User.create(...)).`);
|
|
2314
2314
|
}
|
|
2315
2315
|
return b;
|
|
2316
2316
|
});
|