prisma-guard 1.24.0 → 1.26.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 +126 -39
- package/dist/generator/index.js +46 -16
- package/dist/generator/index.js.map +1 -1
- package/dist/runtime/index.cjs +783 -403
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +60 -21
- package/dist/runtime/index.d.ts +60 -21
- package/dist/runtime/index.js +783 -403
- package/dist/runtime/index.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/index.js
CHANGED
|
@@ -342,12 +342,24 @@ var JSON_ARRAY_OPERATORS = /* @__PURE__ */ new Set([
|
|
|
342
342
|
"array_starts_with",
|
|
343
343
|
"array_ends_with"
|
|
344
344
|
]);
|
|
345
|
-
function getSupportedOperators(
|
|
346
|
-
|
|
345
|
+
function getSupportedOperators(input, isList = false, isEnum = false) {
|
|
346
|
+
let fieldType;
|
|
347
|
+
let list;
|
|
348
|
+
let enumField;
|
|
349
|
+
if (typeof input === "string") {
|
|
350
|
+
fieldType = input;
|
|
351
|
+
list = isList;
|
|
352
|
+
enumField = isEnum;
|
|
353
|
+
} else {
|
|
354
|
+
fieldType = input.type;
|
|
355
|
+
list = input.isList;
|
|
356
|
+
enumField = input.isEnum === true;
|
|
357
|
+
}
|
|
358
|
+
if (list)
|
|
347
359
|
return [...SCALAR_LIST_OPERATORS];
|
|
348
|
-
if (
|
|
360
|
+
if (enumField)
|
|
349
361
|
return [...ENUM_OPERATORS];
|
|
350
|
-
const ops = SCALAR_OPERATORS[
|
|
362
|
+
const ops = SCALAR_OPERATORS[fieldType];
|
|
351
363
|
if (!ops)
|
|
352
364
|
return [];
|
|
353
365
|
return [...ops];
|
|
@@ -408,15 +420,14 @@ function createJsonOperatorSchema(fieldMeta, operator) {
|
|
|
408
420
|
if (JSON_ARRAY_OPERATORS.has(operator)) {
|
|
409
421
|
return jsonValue;
|
|
410
422
|
}
|
|
411
|
-
throw new ShapeError(
|
|
412
|
-
`Operator "${operator}" not supported for Json fields`
|
|
413
|
-
);
|
|
423
|
+
throw new ShapeError(`Operator "${operator}" not supported for Json fields`);
|
|
414
424
|
}
|
|
415
425
|
function buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase) {
|
|
416
426
|
const allOps = getSupportedOperators(fieldMeta);
|
|
417
|
-
const nestedOps = allOps.filter((
|
|
418
|
-
if (nestedOps.length === 0)
|
|
427
|
+
const nestedOps = allOps.filter((op) => op !== "not");
|
|
428
|
+
if (nestedOps.length === 0) {
|
|
419
429
|
return scalarSchema;
|
|
430
|
+
}
|
|
420
431
|
const nestedSchemas = {};
|
|
421
432
|
for (const op of nestedOps) {
|
|
422
433
|
nestedSchemas[op] = createOperatorSchema(
|
|
@@ -428,14 +439,35 @@ function buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase) {
|
|
|
428
439
|
}
|
|
429
440
|
const nestedKeys = Object.keys(nestedSchemas);
|
|
430
441
|
const nestedObj = z2.object(nestedSchemas).strict().refine(
|
|
431
|
-
(
|
|
432
|
-
(
|
|
442
|
+
(value) => nestedKeys.some(
|
|
443
|
+
(key) => value[key] !== void 0
|
|
433
444
|
),
|
|
434
445
|
{ message: "not filter must specify at least one operator" }
|
|
435
446
|
);
|
|
436
447
|
return z2.union([scalarSchema, nestedObj]);
|
|
437
448
|
}
|
|
438
449
|
function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
|
|
450
|
+
const allowedOps = getSupportedOperators(fieldMeta);
|
|
451
|
+
if (!allowedOps.includes(operator)) {
|
|
452
|
+
if (fieldMeta.isList) {
|
|
453
|
+
throw new ShapeError(
|
|
454
|
+
`Operator "${operator}" not supported for scalar list fields`
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
if (fieldMeta.isEnum) {
|
|
458
|
+
throw new ShapeError(
|
|
459
|
+
`Operator "${operator}" not supported for enum fields`
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
if (fieldMeta.type === "Bytes" && allowedOps.length === 0) {
|
|
463
|
+
throw new ShapeError(
|
|
464
|
+
`Type "${fieldMeta.type}" does not support filter operators`
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
throw new ShapeError(
|
|
468
|
+
`Operator "${operator}" not supported for type "${fieldMeta.type}"`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
439
471
|
if (fieldMeta.isList) {
|
|
440
472
|
return createScalarListOperatorSchema(
|
|
441
473
|
fieldMeta,
|
|
@@ -449,50 +481,35 @@ function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
|
|
|
449
481
|
if (!values || values.length === 0) {
|
|
450
482
|
throw new ShapeError(`Unknown enum: ${fieldMeta.type}`);
|
|
451
483
|
}
|
|
452
|
-
if (!ENUM_OPERATORS.has(operator)) {
|
|
453
|
-
throw new ShapeError(
|
|
454
|
-
`Operator "${operator}" not supported for enum fields`
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
484
|
const enumSchema = z2.enum(values);
|
|
458
485
|
if (operator === "equals") {
|
|
459
486
|
return !fieldMeta.isRequired ? z2.union([enumSchema, z2.null()]) : enumSchema;
|
|
460
487
|
}
|
|
461
488
|
if (operator === "not") {
|
|
462
489
|
const scalarSchema = !fieldMeta.isRequired ? z2.union([enumSchema, z2.null()]) : enumSchema;
|
|
463
|
-
return buildNotFilterSchema(
|
|
490
|
+
return buildNotFilterSchema(
|
|
491
|
+
fieldMeta,
|
|
492
|
+
scalarSchema,
|
|
493
|
+
enumMap,
|
|
494
|
+
scalarBase
|
|
495
|
+
);
|
|
464
496
|
}
|
|
465
497
|
const itemSchema = !fieldMeta.isRequired ? z2.union([enumSchema, z2.null()]) : enumSchema;
|
|
466
498
|
return z2.preprocess(coerceToArray, z2.array(itemSchema));
|
|
467
499
|
}
|
|
468
500
|
if (fieldMeta.type === "Json") {
|
|
469
|
-
const supportedOps2 = SCALAR_OPERATORS["Json"];
|
|
470
|
-
if (!supportedOps2 || !supportedOps2.has(operator)) {
|
|
471
|
-
throw new ShapeError(
|
|
472
|
-
`Operator "${operator}" not supported for type "Json"`
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
501
|
if (operator === "not") {
|
|
476
502
|
const jsonValue = z2.unknown();
|
|
477
503
|
const scalarSchema = !fieldMeta.isRequired ? z2.union([jsonValue, z2.null()]) : jsonValue;
|
|
478
|
-
return buildNotFilterSchema(
|
|
504
|
+
return buildNotFilterSchema(
|
|
505
|
+
fieldMeta,
|
|
506
|
+
scalarSchema,
|
|
507
|
+
enumMap,
|
|
508
|
+
scalarBase
|
|
509
|
+
);
|
|
479
510
|
}
|
|
480
511
|
return createJsonOperatorSchema(fieldMeta, operator);
|
|
481
512
|
}
|
|
482
|
-
const supportedOps = SCALAR_OPERATORS[fieldMeta.type];
|
|
483
|
-
if (!supportedOps) {
|
|
484
|
-
throw new ShapeError(`Unknown scalar type for operator: ${fieldMeta.type}`);
|
|
485
|
-
}
|
|
486
|
-
if (supportedOps.size === 0) {
|
|
487
|
-
throw new ShapeError(
|
|
488
|
-
`Type "${fieldMeta.type}" does not support filter operators`
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
if (!supportedOps.has(operator)) {
|
|
492
|
-
throw new ShapeError(
|
|
493
|
-
`Operator "${operator}" not supported for type "${fieldMeta.type}"`
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
513
|
const factory = scalarBase[fieldMeta.type];
|
|
497
514
|
if (!factory) {
|
|
498
515
|
throw new ShapeError(`Unknown scalar type: ${fieldMeta.type}`);
|
|
@@ -968,33 +985,67 @@ function mergeWhereForced(where, forced) {
|
|
|
968
985
|
}
|
|
969
986
|
return result;
|
|
970
987
|
}
|
|
988
|
+
function uniqueValuesEqual(a, b) {
|
|
989
|
+
if (a === b)
|
|
990
|
+
return true;
|
|
991
|
+
if (a === null || b === null)
|
|
992
|
+
return false;
|
|
993
|
+
if (typeof a !== typeof b)
|
|
994
|
+
return false;
|
|
995
|
+
if (a instanceof Date && b instanceof Date) {
|
|
996
|
+
return a.getTime() === b.getTime();
|
|
997
|
+
}
|
|
998
|
+
if (Array.isArray(a)) {
|
|
999
|
+
if (!Array.isArray(b))
|
|
1000
|
+
return false;
|
|
1001
|
+
if (a.length !== b.length)
|
|
1002
|
+
return false;
|
|
1003
|
+
return a.every((v, i) => uniqueValuesEqual(v, b[i]));
|
|
1004
|
+
}
|
|
1005
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
1006
|
+
const aKeys = Object.keys(a);
|
|
1007
|
+
const bKeys = Object.keys(b);
|
|
1008
|
+
if (aKeys.length !== bKeys.length)
|
|
1009
|
+
return false;
|
|
1010
|
+
return aKeys.every((key) => key in b && uniqueValuesEqual(a[key], b[key]));
|
|
1011
|
+
}
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1014
|
+
function mergeUniqueValue(target, key, value) {
|
|
1015
|
+
const cloned = deepClone(value);
|
|
1016
|
+
if (!(key in target)) {
|
|
1017
|
+
target[key] = cloned;
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const existing = target[key];
|
|
1021
|
+
if (isPlainObject(existing) && isPlainObject(cloned)) {
|
|
1022
|
+
const merged = { ...existing };
|
|
1023
|
+
for (const [nestedKey, nestedValue] of Object.entries(cloned)) {
|
|
1024
|
+
if (nestedKey in merged && !uniqueValuesEqual(merged[nestedKey], nestedValue)) {
|
|
1025
|
+
throw new ShapeError(
|
|
1026
|
+
`Conflicting unique where value for "${key}.${nestedKey}"`
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
merged[nestedKey] = nestedValue;
|
|
1030
|
+
}
|
|
1031
|
+
target[key] = merged;
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
if (!uniqueValuesEqual(existing, cloned)) {
|
|
1035
|
+
throw new ShapeError(`Conflicting unique where value for "${key}"`);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
971
1038
|
function mergeUniqueWhereForced(where, forced) {
|
|
972
1039
|
if (!hasWhereForced(forced))
|
|
973
1040
|
return where ?? {};
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
}
|
|
979
|
-
const relObj = result[relName];
|
|
980
|
-
for (const [op, nestedForced] of Object.entries(opMap)) {
|
|
981
|
-
relObj[op] = mergeWhereForced(
|
|
982
|
-
relObj[op],
|
|
983
|
-
nestedForced
|
|
984
|
-
);
|
|
985
|
-
}
|
|
1041
|
+
if (Object.keys(forced.relations).length > 0) {
|
|
1042
|
+
throw new ShapeError(
|
|
1043
|
+
"Unique where forced conditions cannot contain relation filters"
|
|
1044
|
+
);
|
|
986
1045
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
if (existing) {
|
|
991
|
-
if (Array.isArray(existing)) {
|
|
992
|
-
conditions.unshift(...existing);
|
|
993
|
-
} else {
|
|
994
|
-
conditions.unshift(existing);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
result.AND = conditions;
|
|
1046
|
+
const result = where ? deepClone(where) : {};
|
|
1047
|
+
for (const [key, value] of Object.entries(forced.conditions)) {
|
|
1048
|
+
mergeUniqueValue(result, key, value);
|
|
998
1049
|
}
|
|
999
1050
|
return result;
|
|
1000
1051
|
}
|
|
@@ -1010,6 +1061,17 @@ function applyBuiltShape(built, body, isUniqueMethod, modelName) {
|
|
|
1010
1061
|
if (!hasWhereInSchema) {
|
|
1011
1062
|
const { where: _, ...rest } = bodyObj;
|
|
1012
1063
|
parseable = rest;
|
|
1064
|
+
} else if (isUniqueMethod && hasWhereForced(built.forcedWhere) && isPlainObject(bodyObj.where)) {
|
|
1065
|
+
const where = stripUniqueWhereForcedInput(
|
|
1066
|
+
bodyObj.where,
|
|
1067
|
+
built.forcedWhere
|
|
1068
|
+
);
|
|
1069
|
+
if (Object.keys(where).length === 0) {
|
|
1070
|
+
const { where: _, ...rest } = bodyObj;
|
|
1071
|
+
parseable = rest;
|
|
1072
|
+
} else {
|
|
1073
|
+
parseable = { ...bodyObj, where };
|
|
1074
|
+
}
|
|
1013
1075
|
} else if (built.forcedOnlyWhereKeys.size > 0 && isPlainObject(bodyObj.where)) {
|
|
1014
1076
|
const where = { ...bodyObj.where };
|
|
1015
1077
|
for (const key of built.forcedOnlyWhereKeys) {
|
|
@@ -1117,13 +1179,15 @@ function applyForcedTree(validated, key, tree) {
|
|
|
1117
1179
|
);
|
|
1118
1180
|
}
|
|
1119
1181
|
if (forced.include) {
|
|
1120
|
-
if (!relObj.include)
|
|
1182
|
+
if (!relObj.include) {
|
|
1121
1183
|
relObj.include = buildForcedOnlyContainer(forced.include);
|
|
1184
|
+
}
|
|
1122
1185
|
applyForcedTree(relObj, "include", forced.include);
|
|
1123
1186
|
}
|
|
1124
1187
|
if (forced.select) {
|
|
1125
|
-
if (!relObj.select)
|
|
1188
|
+
if (!relObj.select) {
|
|
1126
1189
|
relObj.select = buildForcedOnlyContainer(forced.select);
|
|
1190
|
+
}
|
|
1127
1191
|
applyForcedTree(relObj, "select", forced.select);
|
|
1128
1192
|
}
|
|
1129
1193
|
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
@@ -1148,10 +1212,12 @@ function buildForcedOnlyContainer(tree) {
|
|
|
1148
1212
|
if (forced.where && hasWhereForced(forced.where)) {
|
|
1149
1213
|
nested.where = mergeWhereForced(void 0, forced.where);
|
|
1150
1214
|
}
|
|
1151
|
-
if (forced.include)
|
|
1215
|
+
if (forced.include) {
|
|
1152
1216
|
nested.include = buildForcedOnlyContainer(forced.include);
|
|
1153
|
-
|
|
1217
|
+
}
|
|
1218
|
+
if (forced.select) {
|
|
1154
1219
|
nested.select = buildForcedOnlyContainer(forced.select);
|
|
1220
|
+
}
|
|
1155
1221
|
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
1156
1222
|
const placement = forced._countWherePlacement ?? "include";
|
|
1157
1223
|
if (!nested[placement])
|
|
@@ -1187,77 +1253,131 @@ function applyForcedCountWhere(container, forcedCountWhere) {
|
|
|
1187
1253
|
}
|
|
1188
1254
|
}
|
|
1189
1255
|
}
|
|
1190
|
-
function
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
} else if (key !== "OR" && key !== "NOT") {
|
|
1202
|
-
keys.add(key);
|
|
1203
|
-
}
|
|
1256
|
+
function formatUniqueConstraint(constraint) {
|
|
1257
|
+
return constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`;
|
|
1258
|
+
}
|
|
1259
|
+
function formatUniqueConstraints(constraints) {
|
|
1260
|
+
return constraints.map(formatUniqueConstraint).join(" | ");
|
|
1261
|
+
}
|
|
1262
|
+
function resolvedWhereCoversConstraint(where, constraint) {
|
|
1263
|
+
if (constraint.fields.length === 1) {
|
|
1264
|
+
return constraint.fields[0] in where;
|
|
1204
1265
|
}
|
|
1205
|
-
|
|
1266
|
+
const value = where[constraint.selector];
|
|
1267
|
+
if (!isPlainObject(value))
|
|
1268
|
+
return false;
|
|
1269
|
+
return constraint.fields.every((field) => field in value);
|
|
1206
1270
|
}
|
|
1207
1271
|
function validateResolvedUniqueWhere(model, where, method, uniqueMap) {
|
|
1208
1272
|
const constraints = uniqueMap[model];
|
|
1209
1273
|
if (!constraints || constraints.length === 0)
|
|
1210
1274
|
return;
|
|
1211
|
-
const fieldKeys = collectWhereFieldKeys(where);
|
|
1212
1275
|
const covered = constraints.some(
|
|
1213
|
-
(constraint) =>
|
|
1276
|
+
(constraint) => resolvedWhereCoversConstraint(where, constraint)
|
|
1214
1277
|
);
|
|
1215
1278
|
if (!covered) {
|
|
1216
|
-
const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
|
|
1217
1279
|
throw new ShapeError(
|
|
1218
|
-
`${method} on model "${model}" requires resolved where to cover a unique constraint: ${
|
|
1280
|
+
`${method} on model "${model}" requires resolved where to cover a unique constraint: ${formatUniqueConstraints(constraints)}`
|
|
1219
1281
|
);
|
|
1220
1282
|
}
|
|
1221
1283
|
}
|
|
1222
|
-
function
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
if (isPlainObject(value)) {
|
|
1228
|
-
for (const f of collectEqualityFields(value, model, typeMap)) {
|
|
1229
|
-
fields.add(f);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
continue;
|
|
1284
|
+
function assertDirectUniqueShapeValue(model, field, value, typeMap) {
|
|
1285
|
+
if (typeMap && model) {
|
|
1286
|
+
const fieldMeta = typeMap[model]?.[field];
|
|
1287
|
+
if (!fieldMeta) {
|
|
1288
|
+
throw new ShapeError(`Unknown unique where field "${model}.${field}"`);
|
|
1233
1289
|
}
|
|
1234
|
-
if (
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1290
|
+
if (fieldMeta.isRelation) {
|
|
1291
|
+
throw new ShapeError(
|
|
1292
|
+
`Relation field "${model}.${field}" cannot be used in unique where`
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
if (isForcedValue(value))
|
|
1297
|
+
return;
|
|
1298
|
+
if (isPlainObject(value)) {
|
|
1299
|
+
const keys = Object.keys(value);
|
|
1300
|
+
throw new ShapeError(
|
|
1301
|
+
`Invalid unique where shape for "${model ?? "unknown"}.${field}". Prisma WhereUniqueInput does not accept filter operator objects${keys.length ? `: ${keys.join(", ")}` : ""}. Use { ${field}: true } in guard shape and { ${field}: value } in request args.`
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
if (value === null || value === void 0) {
|
|
1305
|
+
throw new ShapeError(
|
|
1306
|
+
`Invalid unique where shape for "${model ?? "unknown"}.${field}". Unique fields must use true or a forced value.`
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
function shapeCoversConstraint(where, constraint, model, typeMap) {
|
|
1311
|
+
if (constraint.fields.length === 1) {
|
|
1312
|
+
const field = constraint.fields[0];
|
|
1313
|
+
if (!(field in where))
|
|
1314
|
+
return false;
|
|
1315
|
+
assertDirectUniqueShapeValue(model, field, where[field], typeMap);
|
|
1316
|
+
return true;
|
|
1317
|
+
}
|
|
1318
|
+
if (!(constraint.selector in where))
|
|
1319
|
+
return false;
|
|
1320
|
+
const selectorValue = where[constraint.selector];
|
|
1321
|
+
if (isForcedValue(selectorValue))
|
|
1322
|
+
return true;
|
|
1323
|
+
if (!isPlainObject(selectorValue)) {
|
|
1324
|
+
throw new ShapeError(
|
|
1325
|
+
`Compound unique selector "${model}.${constraint.selector}" must be an object with fields: ${constraint.fields.join(", ")}`
|
|
1326
|
+
);
|
|
1327
|
+
}
|
|
1328
|
+
const allowed = new Set(constraint.fields);
|
|
1329
|
+
for (const key of Object.keys(selectorValue)) {
|
|
1330
|
+
if (!allowed.has(key)) {
|
|
1331
|
+
throw new ShapeError(
|
|
1332
|
+
`Unknown field "${key}" in compound unique selector "${model}.${constraint.selector}". Allowed fields: ${constraint.fields.join(", ")}`
|
|
1333
|
+
);
|
|
1242
1334
|
}
|
|
1243
1335
|
}
|
|
1244
|
-
|
|
1336
|
+
for (const field of constraint.fields) {
|
|
1337
|
+
if (!(field in selectorValue)) {
|
|
1338
|
+
throw new ShapeError(
|
|
1339
|
+
`Missing field "${field}" in compound unique selector "${model}.${constraint.selector}"`
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
assertDirectUniqueShapeValue(model, field, selectorValue[field], typeMap);
|
|
1343
|
+
}
|
|
1344
|
+
return true;
|
|
1245
1345
|
}
|
|
1246
1346
|
function validateUniqueEquality(model, where, method, uniqueMap, typeMap) {
|
|
1247
1347
|
const constraints = uniqueMap[model];
|
|
1248
1348
|
if (!constraints || constraints.length === 0)
|
|
1249
1349
|
return;
|
|
1250
|
-
const equalityFields = collectEqualityFields(where, model, typeMap);
|
|
1251
1350
|
const valid = constraints.some(
|
|
1252
|
-
(constraint) =>
|
|
1351
|
+
(constraint) => shapeCoversConstraint(where, constraint, model, typeMap)
|
|
1253
1352
|
);
|
|
1254
1353
|
if (!valid) {
|
|
1255
|
-
const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
|
|
1256
1354
|
throw new ShapeError(
|
|
1257
|
-
`${method} on model "${model}" requires where to cover a unique constraint
|
|
1355
|
+
`${method} on model "${model}" requires unique where shape to cover a unique constraint using Prisma unique selector syntax: ${formatUniqueConstraints(constraints)}`
|
|
1258
1356
|
);
|
|
1259
1357
|
}
|
|
1260
1358
|
}
|
|
1359
|
+
function stripUniqueWhereForcedInput(where, forced) {
|
|
1360
|
+
const result = deepClone(where);
|
|
1361
|
+
for (const [key, forcedValue] of Object.entries(forced.conditions)) {
|
|
1362
|
+
if (!(key in result))
|
|
1363
|
+
continue;
|
|
1364
|
+
const currentValue = result[key];
|
|
1365
|
+
if (isPlainObject(currentValue) && isPlainObject(forcedValue)) {
|
|
1366
|
+
const nested = { ...currentValue };
|
|
1367
|
+
for (const nestedKey of Object.keys(forcedValue)) {
|
|
1368
|
+
delete nested[nestedKey];
|
|
1369
|
+
}
|
|
1370
|
+
if (Object.keys(nested).length === 0) {
|
|
1371
|
+
delete result[key];
|
|
1372
|
+
} else {
|
|
1373
|
+
result[key] = nested;
|
|
1374
|
+
}
|
|
1375
|
+
continue;
|
|
1376
|
+
}
|
|
1377
|
+
delete result[key];
|
|
1378
|
+
}
|
|
1379
|
+
return result;
|
|
1380
|
+
}
|
|
1261
1381
|
|
|
1262
1382
|
// src/runtime/query-builder-where.ts
|
|
1263
1383
|
var UNSUPPORTED_WHERE_TYPES = /* @__PURE__ */ new Set(["Bytes"]);
|
|
@@ -1374,7 +1494,7 @@ function isModeCompatibleOp(fieldType, op) {
|
|
|
1374
1494
|
return JSON_STRING_MODE_OPS.has(op);
|
|
1375
1495
|
return false;
|
|
1376
1496
|
}
|
|
1377
|
-
function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
1497
|
+
function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
1378
1498
|
function buildWhereSchema(model, whereConfig, depth) {
|
|
1379
1499
|
const currentDepth = depth ?? 0;
|
|
1380
1500
|
if (currentDepth > MAX_WHERE_DEPTH) {
|
|
@@ -1431,6 +1551,18 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1431
1551
|
scalarConditions
|
|
1432
1552
|
);
|
|
1433
1553
|
}
|
|
1554
|
+
for (const key of Object.keys(scalarConditions)) {
|
|
1555
|
+
if (COMBINATOR_KEYS.has(key))
|
|
1556
|
+
continue;
|
|
1557
|
+
if (!(key in fieldSchemas)) {
|
|
1558
|
+
fieldSchemas[key] = z4.object({}).strict().optional();
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
for (const key of Object.keys(relationForced)) {
|
|
1562
|
+
if (!(key in fieldSchemas)) {
|
|
1563
|
+
fieldSchemas[key] = z4.object({}).strict().optional();
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1434
1566
|
const schema = Object.keys(fieldSchemas).length > 0 ? z4.object(fieldSchemas).strict().optional() : null;
|
|
1435
1567
|
const forcedOnlyKeys = /* @__PURE__ */ new Set();
|
|
1436
1568
|
for (const key of Object.keys(whereConfig)) {
|
|
@@ -1654,7 +1786,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1654
1786
|
);
|
|
1655
1787
|
}
|
|
1656
1788
|
if (modeConfigValue === true) {
|
|
1657
|
-
opSchemas
|
|
1789
|
+
opSchemas.mode = z4.enum(["default", "insensitive"]).optional();
|
|
1658
1790
|
} else {
|
|
1659
1791
|
const actualModeValue = isForcedValue(modeConfigValue) ? modeConfigValue.value : modeConfigValue;
|
|
1660
1792
|
const modeSchema = z4.enum(["default", "insensitive"]);
|
|
@@ -1666,10 +1798,10 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1666
1798
|
`Invalid forced value for "${model}.${fieldName}.mode": ${err.message}`
|
|
1667
1799
|
);
|
|
1668
1800
|
}
|
|
1669
|
-
fieldForced
|
|
1801
|
+
fieldForced.mode = parsed;
|
|
1670
1802
|
}
|
|
1671
1803
|
} else if (hasModeCompatibleOp) {
|
|
1672
|
-
opSchemas
|
|
1804
|
+
opSchemas.mode = z4.enum(["default", "insensitive"]).optional();
|
|
1673
1805
|
}
|
|
1674
1806
|
if (hasClientOps) {
|
|
1675
1807
|
const opObj = z4.object(opSchemas).strict();
|
|
@@ -1697,12 +1829,60 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1697
1829
|
scalarConditions[fieldName] = fieldForced;
|
|
1698
1830
|
}
|
|
1699
1831
|
}
|
|
1832
|
+
function getUniqueConstraint(model, selector) {
|
|
1833
|
+
return (uniqueMap[model] ?? []).find(
|
|
1834
|
+
(constraint) => constraint.selector === selector
|
|
1835
|
+
) ?? null;
|
|
1836
|
+
}
|
|
1837
|
+
function buildDirectUniqueSchema(fieldMeta) {
|
|
1838
|
+
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
1839
|
+
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
1840
|
+
return wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, base);
|
|
1841
|
+
}
|
|
1842
|
+
return base;
|
|
1843
|
+
}
|
|
1844
|
+
function parseForcedUniqueValue(model, fieldName, fieldMeta, value) {
|
|
1845
|
+
const schema = buildDirectUniqueSchema(fieldMeta);
|
|
1846
|
+
const actual = isForcedValue(value) ? value.value : value;
|
|
1847
|
+
try {
|
|
1848
|
+
return schema.parse(actual);
|
|
1849
|
+
} catch (err) {
|
|
1850
|
+
throw new ShapeError(
|
|
1851
|
+
`Invalid forced value for unique where "${model}.${fieldName}": ${err.message}`
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
function assertCompoundSelectorConfig(model, selector, constraint, value) {
|
|
1856
|
+
const actual = isForcedValue(value) ? value.value : value;
|
|
1857
|
+
if (!isPlainObject(actual)) {
|
|
1858
|
+
throw new ShapeError(
|
|
1859
|
+
`Compound unique selector "${model}.${selector}" must be an object with fields: ${constraint.fields.join(", ")}`
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
const allowed = new Set(constraint.fields);
|
|
1863
|
+
const keys = Object.keys(actual);
|
|
1864
|
+
for (const key of keys) {
|
|
1865
|
+
if (!allowed.has(key)) {
|
|
1866
|
+
throw new ShapeError(
|
|
1867
|
+
`Unknown field "${key}" in compound unique selector "${model}.${selector}". Allowed fields: ${constraint.fields.join(", ")}`
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
for (const field of constraint.fields) {
|
|
1872
|
+
if (!(field in actual)) {
|
|
1873
|
+
throw new ShapeError(
|
|
1874
|
+
`Missing field "${field}" in compound unique selector "${model}.${selector}"`
|
|
1875
|
+
);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
return actual;
|
|
1879
|
+
}
|
|
1700
1880
|
function buildUniqueWhereSchema(model, whereConfig) {
|
|
1701
1881
|
const modelFields = typeMap[model];
|
|
1702
1882
|
if (!modelFields)
|
|
1703
1883
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1704
1884
|
const fieldSchemas = {};
|
|
1705
|
-
const
|
|
1885
|
+
const forcedConditions = {};
|
|
1706
1886
|
const forcedOnlyKeys = /* @__PURE__ */ new Set();
|
|
1707
1887
|
for (const [key, value] of Object.entries(whereConfig)) {
|
|
1708
1888
|
if (COMBINATOR_KEYS.has(key)) {
|
|
@@ -1710,65 +1890,102 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1710
1890
|
`Combinator "${key}" is not supported in unique where for model "${model}"`
|
|
1711
1891
|
);
|
|
1712
1892
|
}
|
|
1893
|
+
const constraint = getUniqueConstraint(model, key);
|
|
1894
|
+
if (constraint && constraint.fields.length > 1) {
|
|
1895
|
+
const compoundConfig = assertCompoundSelectorConfig(
|
|
1896
|
+
model,
|
|
1897
|
+
key,
|
|
1898
|
+
constraint,
|
|
1899
|
+
value
|
|
1900
|
+
);
|
|
1901
|
+
const nestedSchemas = {};
|
|
1902
|
+
const forcedCompound = {};
|
|
1903
|
+
for (const fieldName of constraint.fields) {
|
|
1904
|
+
const fieldMeta2 = modelFields[fieldName];
|
|
1905
|
+
if (!fieldMeta2) {
|
|
1906
|
+
throw new ShapeError(
|
|
1907
|
+
`Unknown field "${fieldName}" in compound unique selector "${model}.${key}"`
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
if (fieldMeta2.isRelation) {
|
|
1911
|
+
throw new ShapeError(
|
|
1912
|
+
`Relation field "${fieldName}" cannot be used in compound unique selector "${model}.${key}"`
|
|
1913
|
+
);
|
|
1914
|
+
}
|
|
1915
|
+
const fieldValue = compoundConfig[fieldName];
|
|
1916
|
+
const forced2 = isForcedValue(fieldValue);
|
|
1917
|
+
if (!forced2 && isPlainObject(fieldValue)) {
|
|
1918
|
+
const operators = Object.keys(fieldValue);
|
|
1919
|
+
throw new ShapeError(
|
|
1920
|
+
`Invalid compound unique where shape for "${model}.${key}.${fieldName}". Prisma compound unique selectors do not accept filter operator objects${operators.length ? `: ${operators.join(", ")}` : ""}. Use direct values only.`
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1923
|
+
const directSchema2 = buildDirectUniqueSchema(fieldMeta2);
|
|
1924
|
+
if (fieldValue === true) {
|
|
1925
|
+
nestedSchemas[fieldName] = directSchema2;
|
|
1926
|
+
} else {
|
|
1927
|
+
forcedCompound[fieldName] = parseForcedUniqueValue(
|
|
1928
|
+
model,
|
|
1929
|
+
fieldName,
|
|
1930
|
+
fieldMeta2,
|
|
1931
|
+
fieldValue
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
if (Object.keys(nestedSchemas).length > 0) {
|
|
1936
|
+
fieldSchemas[key] = z4.object(nestedSchemas).strict();
|
|
1937
|
+
}
|
|
1938
|
+
if (Object.keys(forcedCompound).length > 0) {
|
|
1939
|
+
forcedConditions[key] = forcedCompound;
|
|
1940
|
+
}
|
|
1941
|
+
if (Object.keys(nestedSchemas).length === 0) {
|
|
1942
|
+
forcedOnlyKeys.add(key);
|
|
1943
|
+
}
|
|
1944
|
+
continue;
|
|
1945
|
+
}
|
|
1713
1946
|
const fieldMeta = modelFields[key];
|
|
1714
|
-
if (!fieldMeta)
|
|
1715
|
-
|
|
1716
|
-
if (fieldMeta.isRelation)
|
|
1947
|
+
if (!fieldMeta) {
|
|
1948
|
+
const selectors = (uniqueMap[model] ?? []).map((constraint2) => constraint2.selector).join(", ");
|
|
1717
1949
|
throw new ShapeError(
|
|
1718
|
-
`
|
|
1950
|
+
`Unknown unique field or selector "${key}" on model "${model}"${selectors ? `. Unique selectors: ${selectors}` : ""}`
|
|
1719
1951
|
);
|
|
1720
|
-
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
1721
|
-
let directSchema;
|
|
1722
|
-
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
1723
|
-
directSchema = wrapWithInputCoercion(
|
|
1724
|
-
fieldMeta.type,
|
|
1725
|
-
fieldMeta.isList,
|
|
1726
|
-
base
|
|
1727
|
-
);
|
|
1728
|
-
} else {
|
|
1729
|
-
directSchema = base;
|
|
1730
1952
|
}
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1953
|
+
if (fieldMeta.isRelation) {
|
|
1954
|
+
throw new ShapeError(
|
|
1955
|
+
`Relation field "${key}" cannot be used in unique where for model "${model}"`
|
|
1956
|
+
);
|
|
1735
1957
|
}
|
|
1736
|
-
|
|
1958
|
+
const forced = isForcedValue(value);
|
|
1959
|
+
if (!forced && isPlainObject(value)) {
|
|
1737
1960
|
const keys = Object.keys(value);
|
|
1738
|
-
if (keys.
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
} else {
|
|
1743
|
-
const actual2 = isForcedValue(equalsVal) ? equalsVal.value : equalsVal;
|
|
1744
|
-
try {
|
|
1745
|
-
forced[key] = directSchema.parse(actual2);
|
|
1746
|
-
} catch (err) {
|
|
1747
|
-
throw new ShapeError(
|
|
1748
|
-
`Invalid forced value for unique where "${model}.${key}": ${err.message}`
|
|
1749
|
-
);
|
|
1750
|
-
}
|
|
1751
|
-
forcedOnlyKeys.add(key);
|
|
1752
|
-
}
|
|
1753
|
-
continue;
|
|
1961
|
+
if (keys.includes("equals")) {
|
|
1962
|
+
throw new ShapeError(
|
|
1963
|
+
`Invalid unique where shape for "${model}.${key}". Prisma WhereUniqueInput does not accept filter operator objects. Use { ${key}: true } instead of { ${key}: { equals: true } }.`
|
|
1964
|
+
);
|
|
1754
1965
|
}
|
|
1755
1966
|
throw new ShapeError(
|
|
1756
|
-
`
|
|
1967
|
+
`Invalid unique where shape for "${model}.${key}". Prisma WhereUniqueInput does not accept operators: ${keys.join(", ")}. Use a direct unique value shape, for example { ${key}: true }.`
|
|
1757
1968
|
);
|
|
1758
1969
|
}
|
|
1759
|
-
const
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
throw new ShapeError(
|
|
1764
|
-
`Invalid forced value for unique where "${model}.${key}": ${err.message}`
|
|
1765
|
-
);
|
|
1970
|
+
const directSchema = buildDirectUniqueSchema(fieldMeta);
|
|
1971
|
+
if (value === true) {
|
|
1972
|
+
fieldSchemas[key] = directSchema;
|
|
1973
|
+
continue;
|
|
1766
1974
|
}
|
|
1975
|
+
forcedConditions[key] = parseForcedUniqueValue(
|
|
1976
|
+
model,
|
|
1977
|
+
key,
|
|
1978
|
+
fieldMeta,
|
|
1979
|
+
value
|
|
1980
|
+
);
|
|
1767
1981
|
forcedOnlyKeys.add(key);
|
|
1768
1982
|
}
|
|
1769
1983
|
return {
|
|
1770
1984
|
schema: Object.keys(fieldSchemas).length > 0 ? z4.object(fieldSchemas).strict() : null,
|
|
1771
|
-
forced
|
|
1985
|
+
forced: {
|
|
1986
|
+
conditions: forcedConditions,
|
|
1987
|
+
relations: {}
|
|
1988
|
+
},
|
|
1772
1989
|
forcedOnlyKeys
|
|
1773
1990
|
};
|
|
1774
1991
|
}
|
|
@@ -1787,6 +2004,15 @@ function requireConfigTrue(config, context) {
|
|
|
1787
2004
|
}
|
|
1788
2005
|
}
|
|
1789
2006
|
}
|
|
2007
|
+
function isPlainRecord(value) {
|
|
2008
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2009
|
+
}
|
|
2010
|
+
function formatUniqueConstraint2(constraint) {
|
|
2011
|
+
return constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`;
|
|
2012
|
+
}
|
|
2013
|
+
function formatUniqueConstraints2(constraints) {
|
|
2014
|
+
return constraints.map(formatUniqueConstraint2).join(" | ");
|
|
2015
|
+
}
|
|
1790
2016
|
function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
1791
2017
|
const sortEnum = z5.enum(["asc", "desc"]);
|
|
1792
2018
|
const nullsEnum = z5.enum(["first", "last"]);
|
|
@@ -1797,11 +2023,17 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
1797
2023
|
if (!fieldMeta)
|
|
1798
2024
|
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
1799
2025
|
if (fieldMeta.isRelation)
|
|
1800
|
-
throw new ShapeError(
|
|
2026
|
+
throw new ShapeError(
|
|
2027
|
+
`Relation field "${fieldName}" in orderBy requires a nested config object, not true`
|
|
2028
|
+
);
|
|
1801
2029
|
if (fieldMeta.type === "Json")
|
|
1802
|
-
throw new ShapeError(
|
|
2030
|
+
throw new ShapeError(
|
|
2031
|
+
`Json field "${fieldName}" cannot be used in orderBy`
|
|
2032
|
+
);
|
|
1803
2033
|
if (fieldMeta.isList)
|
|
1804
|
-
throw new ShapeError(
|
|
2034
|
+
throw new ShapeError(
|
|
2035
|
+
`List field "${fieldName}" cannot be used in orderBy`
|
|
2036
|
+
);
|
|
1805
2037
|
}
|
|
1806
2038
|
function buildOrderBySchema(model, orderByConfig) {
|
|
1807
2039
|
const modelFields = typeMap[model];
|
|
@@ -1811,158 +2043,274 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
1811
2043
|
for (const [fieldName, config] of Object.entries(orderByConfig)) {
|
|
1812
2044
|
const fieldMeta = modelFields[fieldName];
|
|
1813
2045
|
if (!fieldMeta)
|
|
1814
|
-
throw new ShapeError(
|
|
2046
|
+
throw new ShapeError(
|
|
2047
|
+
`Unknown field "${fieldName}" on model "${model}"`
|
|
2048
|
+
);
|
|
1815
2049
|
if (config === true) {
|
|
1816
2050
|
validateScalarOrderByField(fieldName, model, modelFields);
|
|
1817
2051
|
fieldSchemas[fieldName] = scalarOrderSchema.optional();
|
|
1818
2052
|
continue;
|
|
1819
2053
|
}
|
|
1820
|
-
if (
|
|
1821
|
-
throw new ShapeError(
|
|
2054
|
+
if (!isPlainRecord(config)) {
|
|
2055
|
+
throw new ShapeError(
|
|
2056
|
+
`orderBy config for "${fieldName}" on model "${model}" must be true or a relation aggregate object`
|
|
2057
|
+
);
|
|
1822
2058
|
}
|
|
1823
2059
|
if (!fieldMeta.isRelation) {
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
2060
|
+
const allowedOps = getSupportedOperators(
|
|
2061
|
+
fieldMeta.type,
|
|
2062
|
+
fieldMeta.isList
|
|
2063
|
+
);
|
|
2064
|
+
const opSchemas = {};
|
|
2065
|
+
for (const [op, enabled] of Object.entries(config)) {
|
|
2066
|
+
if (enabled !== true) {
|
|
2067
|
+
throw new ShapeError(
|
|
2068
|
+
`orderBy operator config for "${model}.${fieldName}.${op}" must be true`
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
2071
|
+
if (!allowedOps.includes(op)) {
|
|
2072
|
+
throw new ShapeError(
|
|
2073
|
+
`Operator "${op}" not supported for orderBy field "${model}.${fieldName}"`
|
|
2074
|
+
);
|
|
2075
|
+
}
|
|
2076
|
+
opSchemas[op] = scalarOrderSchema.optional();
|
|
2077
|
+
}
|
|
2078
|
+
const opKeys = Object.keys(opSchemas);
|
|
2079
|
+
fieldSchemas[fieldName] = z5.object(opSchemas).strict().refine(
|
|
2080
|
+
(v) => opKeys.some(
|
|
2081
|
+
(k) => v[k] !== void 0
|
|
2082
|
+
),
|
|
2083
|
+
{
|
|
2084
|
+
message: `orderBy field "${fieldName}" must specify at least one operator`
|
|
2085
|
+
}
|
|
2086
|
+
).optional();
|
|
2087
|
+
continue;
|
|
1828
2088
|
}
|
|
1829
2089
|
if (fieldMeta.isList) {
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
2090
|
+
if (!("_count" in config)) {
|
|
2091
|
+
throw new ShapeError(
|
|
2092
|
+
`To-many relation orderBy "${fieldName}" only supports _count`
|
|
2093
|
+
);
|
|
1833
2094
|
}
|
|
1834
2095
|
if (config._count !== true) {
|
|
1835
|
-
throw new ShapeError(
|
|
2096
|
+
throw new ShapeError(
|
|
2097
|
+
`orderBy relation aggregate "${fieldName}._count" must be true`
|
|
2098
|
+
);
|
|
1836
2099
|
}
|
|
1837
|
-
fieldSchemas[fieldName] = z5.object({
|
|
2100
|
+
fieldSchemas[fieldName] = z5.object({
|
|
2101
|
+
_count: sortEnum.optional()
|
|
2102
|
+
}).strict().optional();
|
|
1838
2103
|
continue;
|
|
1839
2104
|
}
|
|
1840
|
-
const
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
for (const [nestedField, nestedVal] of Object.entries(config)) {
|
|
1846
|
-
if (nestedVal !== true) {
|
|
1847
|
-
throw new ShapeError(`Nested orderBy field "${nestedField}" on relation "${fieldName}" must be true`);
|
|
1848
|
-
}
|
|
1849
|
-
const nestedMeta = relatedFields[nestedField];
|
|
1850
|
-
if (!nestedMeta)
|
|
1851
|
-
throw new ShapeError(`Unknown field "${nestedField}" on model "${relatedModel}" in orderBy`);
|
|
1852
|
-
if (nestedMeta.isRelation)
|
|
1853
|
-
throw new ShapeError(`Nested relation "${nestedField}" in orderBy on "${fieldName}" is not supported`);
|
|
1854
|
-
if (nestedMeta.type === "Json")
|
|
1855
|
-
throw new ShapeError(`Json field "${nestedField}" cannot be used in orderBy`);
|
|
1856
|
-
if (nestedMeta.isList)
|
|
1857
|
-
throw new ShapeError(`List field "${nestedField}" cannot be used in orderBy`);
|
|
1858
|
-
nestedSchemas[nestedField] = scalarOrderSchema.optional();
|
|
1859
|
-
}
|
|
1860
|
-
const nestedKeys = Object.keys(nestedSchemas);
|
|
1861
|
-
fieldSchemas[fieldName] = z5.object(nestedSchemas).strict().refine(
|
|
1862
|
-
(v) => nestedKeys.some((k) => v[k] !== void 0),
|
|
1863
|
-
{ message: `orderBy for relation "${fieldName}" must specify at least one field` }
|
|
1864
|
-
).optional();
|
|
2105
|
+
const nested = buildOrderBySchema(
|
|
2106
|
+
fieldMeta.type,
|
|
2107
|
+
config
|
|
2108
|
+
);
|
|
2109
|
+
fieldSchemas[fieldName] = nested;
|
|
1865
2110
|
}
|
|
1866
2111
|
const fieldKeys = Object.keys(fieldSchemas);
|
|
1867
2112
|
const singleSchema = z5.object(fieldSchemas).strict().refine(
|
|
1868
|
-
(v) => fieldKeys.some(
|
|
2113
|
+
(v) => fieldKeys.some(
|
|
2114
|
+
(k) => v[k] !== void 0
|
|
2115
|
+
),
|
|
1869
2116
|
{ message: "orderBy must specify at least one field" }
|
|
1870
2117
|
);
|
|
1871
|
-
return z5.union([
|
|
2118
|
+
return z5.union([
|
|
2119
|
+
singleSchema,
|
|
2120
|
+
z5.preprocess(coerceToArray, z5.array(singleSchema).min(1))
|
|
2121
|
+
]).optional();
|
|
1872
2122
|
}
|
|
1873
2123
|
function buildTakeSchema(config) {
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
2124
|
+
if (typeof config === "number") {
|
|
2125
|
+
if (!Number.isFinite(config) || !Number.isInteger(config)) {
|
|
2126
|
+
throw new ShapeError(`take must be a finite integer, got ${config}`);
|
|
2127
|
+
}
|
|
2128
|
+
if (config <= 0) {
|
|
2129
|
+
throw new ShapeError("take must be a positive integer");
|
|
2130
|
+
}
|
|
2131
|
+
return z5.literal(config).optional();
|
|
2132
|
+
}
|
|
2133
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
2134
|
+
throw new ShapeError("take config must be a number or { max, default? }");
|
|
2135
|
+
}
|
|
2136
|
+
if (!Number.isFinite(config.max) || !Number.isInteger(config.max)) {
|
|
2137
|
+
throw new ShapeError(
|
|
2138
|
+
`take.max must be a finite integer, got ${config.max}`
|
|
2139
|
+
);
|
|
1877
2140
|
}
|
|
1878
|
-
if (
|
|
1879
|
-
throw new ShapeError(
|
|
2141
|
+
if (config.max <= 0) {
|
|
2142
|
+
throw new ShapeError("take.max must be a positive integer");
|
|
1880
2143
|
}
|
|
1881
|
-
if (
|
|
1882
|
-
if (!Number.isFinite(
|
|
1883
|
-
throw new ShapeError(
|
|
2144
|
+
if (config.default !== void 0) {
|
|
2145
|
+
if (!Number.isFinite(config.default) || !Number.isInteger(config.default)) {
|
|
2146
|
+
throw new ShapeError(
|
|
2147
|
+
`take.default must be a finite integer, got ${config.default}`
|
|
2148
|
+
);
|
|
1884
2149
|
}
|
|
1885
|
-
if (
|
|
1886
|
-
throw new ShapeError(
|
|
2150
|
+
if (config.default <= 0) {
|
|
2151
|
+
throw new ShapeError("take.default must be a positive integer");
|
|
1887
2152
|
}
|
|
1888
|
-
if (
|
|
1889
|
-
throw new ShapeError("take
|
|
2153
|
+
if (config.default > config.max) {
|
|
2154
|
+
throw new ShapeError("take.default cannot exceed take.max");
|
|
1890
2155
|
}
|
|
1891
|
-
return z5.number().int().min(1).max(
|
|
2156
|
+
return z5.number().int().min(1).max(config.max).default(config.default);
|
|
1892
2157
|
}
|
|
1893
|
-
return z5.number().int().min(1).max(
|
|
2158
|
+
return z5.number().int().min(1).max(config.max).optional();
|
|
1894
2159
|
}
|
|
1895
|
-
function
|
|
2160
|
+
function buildCursorFieldSchema(model, fieldName) {
|
|
1896
2161
|
const modelFields = typeMap[model];
|
|
1897
2162
|
if (!modelFields)
|
|
1898
2163
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1899
|
-
|
|
1900
|
-
|
|
2164
|
+
const fieldMeta = modelFields[fieldName];
|
|
2165
|
+
if (!fieldMeta) {
|
|
2166
|
+
throw new ShapeError(
|
|
2167
|
+
`Unknown field "${fieldName}" on model "${model}" in cursor`
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
if (fieldMeta.isRelation) {
|
|
2171
|
+
throw new ShapeError(
|
|
2172
|
+
`Relation field "${fieldName}" cannot be used in cursor`
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
if (fieldMeta.isList) {
|
|
2176
|
+
throw new ShapeError(
|
|
2177
|
+
`List field "${fieldName}" cannot be used in cursor`
|
|
2178
|
+
);
|
|
2179
|
+
}
|
|
2180
|
+
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
2181
|
+
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
2182
|
+
return wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, base);
|
|
2183
|
+
}
|
|
2184
|
+
return base;
|
|
2185
|
+
}
|
|
2186
|
+
function cursorConfigMatchesConstraint(cursorConfig, constraint) {
|
|
2187
|
+
if (!(constraint.selector in cursorConfig))
|
|
2188
|
+
return false;
|
|
2189
|
+
const value = cursorConfig[constraint.selector];
|
|
2190
|
+
if (constraint.fields.length === 1) {
|
|
2191
|
+
return value === true;
|
|
2192
|
+
}
|
|
2193
|
+
if (!isPlainRecord(value))
|
|
2194
|
+
return false;
|
|
2195
|
+
const keys = Object.keys(value);
|
|
2196
|
+
if (keys.length !== constraint.fields.length)
|
|
2197
|
+
return false;
|
|
2198
|
+
return constraint.fields.every((field) => value[field] === true);
|
|
2199
|
+
}
|
|
2200
|
+
function getUniqueConstraints(model) {
|
|
1901
2201
|
const constraints = uniqueMap[model];
|
|
1902
2202
|
if (constraints && constraints.length > 0) {
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
2203
|
+
return constraints;
|
|
2204
|
+
}
|
|
2205
|
+
const modelFields = typeMap[model];
|
|
2206
|
+
if (!modelFields) {
|
|
2207
|
+
throw new ShapeError(`Unknown model: ${model}`);
|
|
2208
|
+
}
|
|
2209
|
+
const inferred = [];
|
|
2210
|
+
for (const [fieldName, fieldMeta] of Object.entries(modelFields)) {
|
|
2211
|
+
if (fieldMeta.isRelation)
|
|
2212
|
+
continue;
|
|
2213
|
+
if (fieldMeta.isId || fieldMeta.isUnique) {
|
|
2214
|
+
inferred.push({
|
|
2215
|
+
selector: fieldName,
|
|
2216
|
+
fields: [fieldName]
|
|
2217
|
+
});
|
|
1911
2218
|
}
|
|
1912
2219
|
}
|
|
2220
|
+
return inferred;
|
|
2221
|
+
}
|
|
2222
|
+
function buildCursorSchema(model, cursorConfig) {
|
|
2223
|
+
const constraints = getUniqueConstraints(model);
|
|
2224
|
+
if (constraints.length === 0) {
|
|
2225
|
+
throw new ShapeError(
|
|
2226
|
+
`cursor on model "${model}" requires at least one unique constraint`
|
|
2227
|
+
);
|
|
2228
|
+
}
|
|
2229
|
+
const matching = constraints.find(
|
|
2230
|
+
(constraint) => cursorConfigMatchesConstraint(cursorConfig, constraint)
|
|
2231
|
+
);
|
|
2232
|
+
if (!matching) {
|
|
2233
|
+
throw new ShapeError(
|
|
2234
|
+
`cursor on model "${model}" must exactly match a unique selector: ${formatUniqueConstraints2(constraints)}`
|
|
2235
|
+
);
|
|
2236
|
+
}
|
|
1913
2237
|
const fieldSchemas = {};
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
2238
|
+
if (matching.fields.length === 1) {
|
|
2239
|
+
fieldSchemas[matching.selector] = buildCursorFieldSchema(
|
|
2240
|
+
model,
|
|
2241
|
+
matching.fields[0]
|
|
2242
|
+
).optional();
|
|
2243
|
+
} else {
|
|
2244
|
+
const nestedSchemas = {};
|
|
2245
|
+
for (const field of matching.fields) {
|
|
2246
|
+
nestedSchemas[field] = buildCursorFieldSchema(model, field);
|
|
2247
|
+
}
|
|
2248
|
+
fieldSchemas[matching.selector] = z5.object(nestedSchemas).strict().optional();
|
|
1923
2249
|
}
|
|
1924
|
-
return z5.object(fieldSchemas).strict().
|
|
2250
|
+
return z5.object(fieldSchemas).strict().refine(
|
|
2251
|
+
(v) => v[matching.selector] !== void 0,
|
|
2252
|
+
{ message: `cursor must specify "${matching.selector}"` }
|
|
2253
|
+
).optional();
|
|
1925
2254
|
}
|
|
1926
2255
|
function buildDistinctSchema(model, distinctConfig) {
|
|
1927
|
-
if (distinctConfig.length === 0) {
|
|
1928
|
-
throw new ShapeError("distinct must contain at least one field");
|
|
1929
|
-
}
|
|
1930
2256
|
const modelFields = typeMap[model];
|
|
1931
2257
|
if (!modelFields)
|
|
1932
2258
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2259
|
+
if (!Array.isArray(distinctConfig) || distinctConfig.length === 0) {
|
|
2260
|
+
throw new ShapeError(
|
|
2261
|
+
`distinct on model "${model}" must be a non-empty array of scalar fields`
|
|
2262
|
+
);
|
|
2263
|
+
}
|
|
2264
|
+
const allowedFields = /* @__PURE__ */ new Set();
|
|
1933
2265
|
for (const fieldName of distinctConfig) {
|
|
1934
2266
|
const fieldMeta = modelFields[fieldName];
|
|
1935
2267
|
if (!fieldMeta)
|
|
1936
|
-
throw new ShapeError(
|
|
2268
|
+
throw new ShapeError(
|
|
2269
|
+
`Unknown field "${fieldName}" on model "${model}" in distinct`
|
|
2270
|
+
);
|
|
1937
2271
|
if (fieldMeta.isRelation)
|
|
1938
|
-
throw new ShapeError(
|
|
1939
|
-
|
|
1940
|
-
|
|
2272
|
+
throw new ShapeError(
|
|
2273
|
+
`Relation field "${fieldName}" cannot be used in distinct`
|
|
2274
|
+
);
|
|
2275
|
+
allowedFields.add(fieldName);
|
|
1941
2276
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
2277
|
+
return z5.union([
|
|
2278
|
+
z5.enum([...allowedFields]),
|
|
2279
|
+
z5.array(z5.enum([...allowedFields])).min(1)
|
|
2280
|
+
]).optional();
|
|
1944
2281
|
}
|
|
1945
2282
|
function buildBySchema(model, byConfig) {
|
|
1946
|
-
if (byConfig.length === 0) {
|
|
1947
|
-
throw new ShapeError('groupBy "by" must contain at least one field');
|
|
1948
|
-
}
|
|
1949
2283
|
const modelFields = typeMap[model];
|
|
1950
2284
|
if (!modelFields)
|
|
1951
2285
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2286
|
+
if (!Array.isArray(byConfig) || byConfig.length === 0) {
|
|
2287
|
+
throw new ShapeError(
|
|
2288
|
+
`groupBy "by" on model "${model}" must be a non-empty array`
|
|
2289
|
+
);
|
|
2290
|
+
}
|
|
2291
|
+
const allowedFields = /* @__PURE__ */ new Set();
|
|
1952
2292
|
for (const fieldName of byConfig) {
|
|
1953
2293
|
const fieldMeta = modelFields[fieldName];
|
|
1954
2294
|
if (!fieldMeta)
|
|
1955
|
-
throw new ShapeError(
|
|
2295
|
+
throw new ShapeError(
|
|
2296
|
+
`Unknown field "${fieldName}" on model "${model}" in groupBy by`
|
|
2297
|
+
);
|
|
1956
2298
|
if (fieldMeta.isRelation)
|
|
1957
|
-
throw new ShapeError(
|
|
2299
|
+
throw new ShapeError(
|
|
2300
|
+
`Relation field "${fieldName}" cannot be used in groupBy by`
|
|
2301
|
+
);
|
|
2302
|
+
if (fieldMeta.isList)
|
|
2303
|
+
throw new ShapeError(
|
|
2304
|
+
`List field "${fieldName}" cannot be used in groupBy by`
|
|
2305
|
+
);
|
|
1958
2306
|
if (UNSUPPORTED_BY_TYPES.has(fieldMeta.type)) {
|
|
1959
|
-
throw new ShapeError(
|
|
2307
|
+
throw new ShapeError(
|
|
2308
|
+
`${fieldMeta.type} field "${fieldName}" cannot be used in groupBy by`
|
|
2309
|
+
);
|
|
1960
2310
|
}
|
|
1961
|
-
|
|
1962
|
-
throw new ShapeError(`List field "${fieldName}" cannot be used in by`);
|
|
2311
|
+
allowedFields.add(fieldName);
|
|
1963
2312
|
}
|
|
1964
|
-
|
|
1965
|
-
return z5.union([enumSchema, z5.preprocess(coerceToArray, z5.array(enumSchema).min(1))]);
|
|
2313
|
+
return z5.array(z5.enum([...allowedFields])).min(1);
|
|
1966
2314
|
}
|
|
1967
2315
|
function buildHavingSchema(model, havingConfig) {
|
|
1968
2316
|
const modelFields = typeMap[model];
|
|
@@ -1973,82 +2321,88 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
1973
2321
|
for (const fieldName of Object.keys(havingConfig)) {
|
|
1974
2322
|
const fieldMeta = modelFields[fieldName];
|
|
1975
2323
|
if (!fieldMeta)
|
|
1976
|
-
throw new ShapeError(
|
|
2324
|
+
throw new ShapeError(
|
|
2325
|
+
`Unknown field "${fieldName}" on model "${model}" in having`
|
|
2326
|
+
);
|
|
1977
2327
|
if (fieldMeta.isRelation)
|
|
1978
|
-
throw new ShapeError(
|
|
2328
|
+
throw new ShapeError(
|
|
2329
|
+
`Relation field "${fieldName}" cannot be used in having`
|
|
2330
|
+
);
|
|
1979
2331
|
if (fieldMeta.isList)
|
|
1980
|
-
throw new ShapeError(
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2332
|
+
throw new ShapeError(
|
|
2333
|
+
`List field "${fieldName}" cannot be used in having`
|
|
2334
|
+
);
|
|
2335
|
+
if (UNSUPPORTED_BY_TYPES.has(fieldMeta.type)) {
|
|
2336
|
+
throw new ShapeError(
|
|
2337
|
+
`${fieldMeta.type} field "${fieldName}" cannot be used in having`
|
|
2338
|
+
);
|
|
1984
2339
|
}
|
|
2340
|
+
const allowedOps = getSupportedOperators(
|
|
2341
|
+
fieldMeta.type,
|
|
2342
|
+
fieldMeta.isList
|
|
2343
|
+
);
|
|
1985
2344
|
const opSchemas = {};
|
|
1986
|
-
const
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2345
|
+
for (const op of allowedOps) {
|
|
2346
|
+
opSchemas[op] = createOperatorSchema(
|
|
2347
|
+
fieldMeta,
|
|
2348
|
+
op,
|
|
2349
|
+
enumMap,
|
|
2350
|
+
scalarBase
|
|
2351
|
+
).optional();
|
|
1990
2352
|
}
|
|
1991
|
-
if (fieldMeta.type === "String"
|
|
1992
|
-
opSchemas
|
|
2353
|
+
if (fieldMeta.type === "String") {
|
|
2354
|
+
opSchemas.mode = z5.enum(["default", "insensitive"]).optional();
|
|
1993
2355
|
}
|
|
2356
|
+
const opKeys = Object.keys(opSchemas).filter((key) => key !== "mode");
|
|
1994
2357
|
fieldSchemas[fieldName] = z5.object(opSchemas).strict().refine(
|
|
1995
2358
|
(v) => opKeys.some((k) => v[k] !== void 0),
|
|
1996
|
-
{
|
|
2359
|
+
{
|
|
2360
|
+
message: `having field "${fieldName}" must specify at least one operator`
|
|
2361
|
+
}
|
|
1997
2362
|
).optional();
|
|
1998
2363
|
}
|
|
1999
|
-
const
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
z5.preprocess(coerceToArray, z5.array(havingSchema).min(1))
|
|
2007
|
-
]).optional();
|
|
2008
|
-
const allKeys = [...havingFieldKeys, "AND", "OR", "NOT"];
|
|
2009
|
-
return z5.object(allSchemas).strict().refine(
|
|
2010
|
-
(v) => allKeys.some((k) => v[k] !== void 0),
|
|
2011
|
-
{ message: "having must specify at least one field or combinator" }
|
|
2012
|
-
);
|
|
2013
|
-
});
|
|
2014
|
-
return havingSchema.optional();
|
|
2364
|
+
const fieldKeys = Object.keys(fieldSchemas);
|
|
2365
|
+
return z5.object(fieldSchemas).strict().refine(
|
|
2366
|
+
(v) => fieldKeys.some(
|
|
2367
|
+
(k) => v[k] !== void 0
|
|
2368
|
+
),
|
|
2369
|
+
{ message: "having must specify at least one field" }
|
|
2370
|
+
).optional();
|
|
2015
2371
|
}
|
|
2016
|
-
function buildAggregateFieldSchema(model,
|
|
2372
|
+
function buildAggregateFieldSchema(model, op, config) {
|
|
2017
2373
|
const modelFields = typeMap[model];
|
|
2018
2374
|
if (!modelFields)
|
|
2019
2375
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2020
|
-
requireConfigTrue(
|
|
2021
|
-
const
|
|
2022
|
-
const isComparableOnly = opName === "_min" || opName === "_max";
|
|
2376
|
+
requireConfigTrue(config, `${op} on model "${model}"`);
|
|
2377
|
+
const allowedTypes = op === "_avg" || op === "_sum" ? NUMERIC_TYPES : COMPARABLE_TYPES;
|
|
2023
2378
|
const fieldSchemas = {};
|
|
2024
|
-
for (const fieldName of Object.keys(
|
|
2025
|
-
if (fieldName === "_all" && opName === "_count") {
|
|
2026
|
-
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
2027
|
-
continue;
|
|
2028
|
-
}
|
|
2379
|
+
for (const fieldName of Object.keys(config)) {
|
|
2029
2380
|
const fieldMeta = modelFields[fieldName];
|
|
2030
2381
|
if (!fieldMeta)
|
|
2031
|
-
throw new ShapeError(
|
|
2382
|
+
throw new ShapeError(
|
|
2383
|
+
`Unknown field "${fieldName}" on model "${model}" in ${op}`
|
|
2384
|
+
);
|
|
2032
2385
|
if (fieldMeta.isRelation)
|
|
2033
|
-
throw new ShapeError(
|
|
2386
|
+
throw new ShapeError(
|
|
2387
|
+
`Relation field "${fieldName}" cannot be used in ${op}`
|
|
2388
|
+
);
|
|
2034
2389
|
if (fieldMeta.isList)
|
|
2035
|
-
throw new ShapeError(`List field "${fieldName}" cannot be used in ${opName}`);
|
|
2036
|
-
if (isNumericOnly && !NUMERIC_TYPES.has(fieldMeta.type)) {
|
|
2037
2390
|
throw new ShapeError(
|
|
2038
|
-
`
|
|
2391
|
+
`List field "${fieldName}" cannot be used in ${op}`
|
|
2039
2392
|
);
|
|
2040
|
-
|
|
2041
|
-
if (isComparableOnly && !COMPARABLE_TYPES.has(fieldMeta.type)) {
|
|
2393
|
+
if (!allowedTypes.has(fieldMeta.type)) {
|
|
2042
2394
|
throw new ShapeError(
|
|
2043
|
-
`Field "${fieldName}"
|
|
2395
|
+
`Field "${fieldName}" of type "${fieldMeta.type}" cannot be used in ${op}`
|
|
2044
2396
|
);
|
|
2045
2397
|
}
|
|
2046
2398
|
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
2047
2399
|
}
|
|
2048
|
-
const
|
|
2400
|
+
const aggregateFieldKeys = Object.keys(fieldSchemas);
|
|
2049
2401
|
return z5.object(fieldSchemas).strict().refine(
|
|
2050
|
-
(v) =>
|
|
2051
|
-
|
|
2402
|
+
(v) => aggregateFieldKeys.some(
|
|
2403
|
+
(k) => v[k] !== void 0
|
|
2404
|
+
),
|
|
2405
|
+
{ message: `${op} must specify at least one field` }
|
|
2052
2406
|
).optional();
|
|
2053
2407
|
}
|
|
2054
2408
|
function buildCountFieldSchema(model, config, context) {
|
|
@@ -2064,15 +2418,21 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2064
2418
|
if (fieldName !== "_all") {
|
|
2065
2419
|
const fieldMeta = modelFields[fieldName];
|
|
2066
2420
|
if (!fieldMeta)
|
|
2067
|
-
throw new ShapeError(
|
|
2421
|
+
throw new ShapeError(
|
|
2422
|
+
`Unknown field "${fieldName}" on model "${model}" in ${context}`
|
|
2423
|
+
);
|
|
2068
2424
|
if (fieldMeta.isRelation)
|
|
2069
|
-
throw new ShapeError(
|
|
2425
|
+
throw new ShapeError(
|
|
2426
|
+
`Relation field "${fieldName}" cannot be used in ${context}`
|
|
2427
|
+
);
|
|
2070
2428
|
}
|
|
2071
2429
|
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
2072
2430
|
}
|
|
2073
2431
|
const countFieldKeys = Object.keys(fieldSchemas);
|
|
2074
2432
|
return z5.object(fieldSchemas).strict().refine(
|
|
2075
|
-
(v) => countFieldKeys.some(
|
|
2433
|
+
(v) => countFieldKeys.some(
|
|
2434
|
+
(k) => v[k] !== void 0
|
|
2435
|
+
),
|
|
2076
2436
|
{ message: `${context} must specify at least one field` }
|
|
2077
2437
|
).optional();
|
|
2078
2438
|
}
|
|
@@ -2084,19 +2444,25 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2084
2444
|
const fieldSchemas = {};
|
|
2085
2445
|
for (const fieldName of Object.keys(selectConfig)) {
|
|
2086
2446
|
if (fieldName === "_all") {
|
|
2087
|
-
fieldSchemas
|
|
2447
|
+
fieldSchemas._all = z5.literal(true).optional();
|
|
2088
2448
|
continue;
|
|
2089
2449
|
}
|
|
2090
2450
|
const fieldMeta = modelFields[fieldName];
|
|
2091
2451
|
if (!fieldMeta)
|
|
2092
|
-
throw new ShapeError(
|
|
2452
|
+
throw new ShapeError(
|
|
2453
|
+
`Unknown field "${fieldName}" on model "${model}" in count select`
|
|
2454
|
+
);
|
|
2093
2455
|
if (fieldMeta.isRelation)
|
|
2094
|
-
throw new ShapeError(
|
|
2456
|
+
throw new ShapeError(
|
|
2457
|
+
`Relation field "${fieldName}" cannot be used in count select`
|
|
2458
|
+
);
|
|
2095
2459
|
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
2096
2460
|
}
|
|
2097
2461
|
const countSelectKeys = Object.keys(fieldSchemas);
|
|
2098
2462
|
return z5.object(fieldSchemas).strict().refine(
|
|
2099
|
-
(v) => countSelectKeys.some(
|
|
2463
|
+
(v) => countSelectKeys.some(
|
|
2464
|
+
(k) => v[k] !== void 0
|
|
2465
|
+
),
|
|
2100
2466
|
{ message: "count select must specify at least one field" }
|
|
2101
2467
|
).optional();
|
|
2102
2468
|
}
|
|
@@ -2505,7 +2871,12 @@ var UNIQUE_WHERE_METHODS = /* @__PURE__ */ new Set([
|
|
|
2505
2871
|
"findUniqueOrThrow"
|
|
2506
2872
|
]);
|
|
2507
2873
|
function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
2508
|
-
const whereBuilder = createWhereBuilder(
|
|
2874
|
+
const whereBuilder = createWhereBuilder(
|
|
2875
|
+
typeMap,
|
|
2876
|
+
enumMap,
|
|
2877
|
+
scalarBase,
|
|
2878
|
+
uniqueMap
|
|
2879
|
+
);
|
|
2509
2880
|
const argsBuilder = createArgsBuilder(
|
|
2510
2881
|
typeMap,
|
|
2511
2882
|
enumMap,
|
|
@@ -2527,24 +2898,28 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2527
2898
|
function validateShapeArgs(method, shape) {
|
|
2528
2899
|
const allowed = METHOD_ALLOWED_ARGS[method];
|
|
2529
2900
|
for (const key of Object.keys(shape)) {
|
|
2530
|
-
if (!SHAPE_CONFIG_KEYS.has(key))
|
|
2901
|
+
if (!SHAPE_CONFIG_KEYS.has(key)) {
|
|
2531
2902
|
throw new ShapeError(`Unknown shape config key "${key}"`);
|
|
2532
|
-
|
|
2903
|
+
}
|
|
2904
|
+
if (!allowed.has(key)) {
|
|
2533
2905
|
throw new ShapeError(`Arg "${key}" not allowed for method "${method}"`);
|
|
2906
|
+
}
|
|
2534
2907
|
}
|
|
2535
2908
|
if (UNIQUE_WHERE_METHODS.has(method) && !shape.where) {
|
|
2536
2909
|
throw new ShapeError(`${method} shape must define "where"`);
|
|
2537
2910
|
}
|
|
2538
|
-
if (method === "groupBy" && !shape.by)
|
|
2911
|
+
if (method === "groupBy" && !shape.by) {
|
|
2539
2912
|
throw new ShapeError('groupBy shape must define "by"');
|
|
2913
|
+
}
|
|
2540
2914
|
if (method === "groupBy" && (shape.include || shape.select)) {
|
|
2541
2915
|
throw new ShapeError('groupBy does not support "include" or "select"');
|
|
2542
2916
|
}
|
|
2543
2917
|
if (method === "aggregate" && (shape.include || shape.select)) {
|
|
2544
2918
|
throw new ShapeError('aggregate does not support "include" or "select"');
|
|
2545
2919
|
}
|
|
2546
|
-
if (method === "count" && shape.include)
|
|
2920
|
+
if (method === "count" && shape.include) {
|
|
2547
2921
|
throw new ShapeError('count does not support "include"');
|
|
2922
|
+
}
|
|
2548
2923
|
if (method === "groupBy" && shape.orderBy && shape.orderBy !== true) {
|
|
2549
2924
|
const bySet = new Set(shape.by);
|
|
2550
2925
|
for (const fieldName of Object.keys(shape.orderBy)) {
|
|
@@ -2599,30 +2974,28 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2599
2974
|
let forcedIncludeCountWhere = {};
|
|
2600
2975
|
let forcedSelectCountWhere = {};
|
|
2601
2976
|
if (shape.where) {
|
|
2602
|
-
const
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
forcedWhere = forced;
|
|
2609
|
-
forcedOnlyWhereKeys = forcedOnlyKeys;
|
|
2977
|
+
const builtWhere = UNIQUE_WHERE_METHODS.has(method) ? whereBuilder.buildUniqueWhereSchema(model, shape.where) : whereBuilder.buildWhereSchema(model, shape.where);
|
|
2978
|
+
if (builtWhere.schema) {
|
|
2979
|
+
schemaFields.where = builtWhere.schema;
|
|
2980
|
+
}
|
|
2981
|
+
forcedWhere = builtWhere.forced;
|
|
2982
|
+
forcedOnlyWhereKeys = builtWhere.forcedOnlyKeys;
|
|
2610
2983
|
}
|
|
2611
2984
|
if (shape.include) {
|
|
2612
2985
|
const result = projectionBuilder.buildIncludeSchema(model, shape.include);
|
|
2613
|
-
schemaFields
|
|
2986
|
+
schemaFields.include = result.schema;
|
|
2614
2987
|
forcedIncludeTree = result.forcedTree;
|
|
2615
2988
|
forcedIncludeCountWhere = result.forcedCountWhere;
|
|
2616
2989
|
}
|
|
2617
2990
|
if (shape.select) {
|
|
2618
2991
|
if (method === "count") {
|
|
2619
|
-
schemaFields
|
|
2992
|
+
schemaFields.select = argsBuilder.buildCountSelectSchema(
|
|
2620
2993
|
model,
|
|
2621
2994
|
shape.select
|
|
2622
2995
|
);
|
|
2623
2996
|
} else {
|
|
2624
2997
|
const result = projectionBuilder.buildSelectSchema(model, shape.select);
|
|
2625
|
-
schemaFields
|
|
2998
|
+
schemaFields.select = result.schema;
|
|
2626
2999
|
forcedSelectTree = result.forcedTree;
|
|
2627
3000
|
forcedSelectCountWhere = result.forcedCountWhere;
|
|
2628
3001
|
}
|
|
@@ -2636,7 +3009,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2636
3009
|
for (const field of shape.by) {
|
|
2637
3010
|
groupByOrderFields[field] = sortEnum.optional();
|
|
2638
3011
|
}
|
|
2639
|
-
groupByOrderFields
|
|
3012
|
+
groupByOrderFields._count = sortEnum.optional();
|
|
2640
3013
|
const fieldKeys = Object.keys(groupByOrderFields);
|
|
2641
3014
|
const singleSchema = z7.object(groupByOrderFields).strict().refine(
|
|
2642
3015
|
(v) => fieldKeys.some(
|
|
@@ -2644,13 +3017,16 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2644
3017
|
),
|
|
2645
3018
|
{ message: "orderBy must specify at least one field" }
|
|
2646
3019
|
);
|
|
2647
|
-
schemaFields
|
|
3020
|
+
schemaFields.orderBy = z7.union([
|
|
3021
|
+
singleSchema,
|
|
3022
|
+
z7.preprocess(coerceToArray, z7.array(singleSchema).min(1))
|
|
3023
|
+
]).optional();
|
|
2648
3024
|
} else {
|
|
2649
3025
|
const groupByOrderFields = {};
|
|
2650
3026
|
for (const [fieldName, config] of Object.entries(shape.orderBy)) {
|
|
2651
3027
|
if (fieldName === "_count") {
|
|
2652
3028
|
if (config === true) {
|
|
2653
|
-
groupByOrderFields
|
|
3029
|
+
groupByOrderFields._count = sortEnum.optional();
|
|
2654
3030
|
} else if (typeof config === "object" && config !== null) {
|
|
2655
3031
|
const countFields = {};
|
|
2656
3032
|
for (const countField of Object.keys(config)) {
|
|
@@ -2662,7 +3038,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2662
3038
|
countFields[countField] = sortEnum.optional();
|
|
2663
3039
|
}
|
|
2664
3040
|
const countKeys = Object.keys(countFields);
|
|
2665
|
-
groupByOrderFields
|
|
3041
|
+
groupByOrderFields._count = z7.object(countFields).strict().refine(
|
|
2666
3042
|
(v) => countKeys.some(
|
|
2667
3043
|
(k) => v[k] !== void 0
|
|
2668
3044
|
),
|
|
@@ -2687,70 +3063,83 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2687
3063
|
),
|
|
2688
3064
|
{ message: "orderBy must specify at least one field" }
|
|
2689
3065
|
);
|
|
2690
|
-
schemaFields
|
|
3066
|
+
schemaFields.orderBy = z7.union([
|
|
3067
|
+
singleSchema,
|
|
3068
|
+
z7.preprocess(coerceToArray, z7.array(singleSchema).min(1))
|
|
3069
|
+
]).optional();
|
|
2691
3070
|
}
|
|
2692
3071
|
} else {
|
|
2693
|
-
schemaFields
|
|
3072
|
+
schemaFields.orderBy = argsBuilder.buildOrderBySchema(
|
|
2694
3073
|
model,
|
|
2695
3074
|
shape.orderBy
|
|
2696
3075
|
);
|
|
2697
3076
|
}
|
|
2698
3077
|
}
|
|
2699
|
-
if (shape.cursor)
|
|
2700
|
-
schemaFields
|
|
3078
|
+
if (shape.cursor) {
|
|
3079
|
+
schemaFields.cursor = argsBuilder.buildCursorSchema(
|
|
2701
3080
|
model,
|
|
2702
3081
|
shape.cursor
|
|
2703
3082
|
);
|
|
2704
|
-
|
|
2705
|
-
|
|
3083
|
+
}
|
|
3084
|
+
if (shape.take) {
|
|
3085
|
+
schemaFields.take = argsBuilder.buildTakeSchema(shape.take);
|
|
3086
|
+
}
|
|
2706
3087
|
if (shape.skip !== void 0) {
|
|
2707
3088
|
if (shape.skip !== true) {
|
|
2708
3089
|
throw new ShapeError('Shape config "skip" must be true');
|
|
2709
3090
|
}
|
|
2710
|
-
schemaFields
|
|
3091
|
+
schemaFields.skip = z7.number().int().min(0).optional();
|
|
2711
3092
|
}
|
|
2712
|
-
if (shape.distinct)
|
|
2713
|
-
schemaFields
|
|
3093
|
+
if (shape.distinct) {
|
|
3094
|
+
schemaFields.distinct = argsBuilder.buildDistinctSchema(
|
|
2714
3095
|
model,
|
|
2715
3096
|
shape.distinct
|
|
2716
3097
|
);
|
|
2717
|
-
|
|
2718
|
-
|
|
3098
|
+
}
|
|
3099
|
+
if (shape._count) {
|
|
3100
|
+
schemaFields._count = argsBuilder.buildCountFieldSchema(
|
|
2719
3101
|
model,
|
|
2720
3102
|
shape._count,
|
|
2721
3103
|
"_count"
|
|
2722
3104
|
);
|
|
2723
|
-
|
|
2724
|
-
|
|
3105
|
+
}
|
|
3106
|
+
if (shape._avg) {
|
|
3107
|
+
schemaFields._avg = argsBuilder.buildAggregateFieldSchema(
|
|
2725
3108
|
model,
|
|
2726
3109
|
"_avg",
|
|
2727
3110
|
shape._avg
|
|
2728
3111
|
);
|
|
2729
|
-
|
|
2730
|
-
|
|
3112
|
+
}
|
|
3113
|
+
if (shape._sum) {
|
|
3114
|
+
schemaFields._sum = argsBuilder.buildAggregateFieldSchema(
|
|
2731
3115
|
model,
|
|
2732
3116
|
"_sum",
|
|
2733
3117
|
shape._sum
|
|
2734
3118
|
);
|
|
2735
|
-
|
|
2736
|
-
|
|
3119
|
+
}
|
|
3120
|
+
if (shape._min) {
|
|
3121
|
+
schemaFields._min = argsBuilder.buildAggregateFieldSchema(
|
|
2737
3122
|
model,
|
|
2738
3123
|
"_min",
|
|
2739
3124
|
shape._min
|
|
2740
3125
|
);
|
|
2741
|
-
|
|
2742
|
-
|
|
3126
|
+
}
|
|
3127
|
+
if (shape._max) {
|
|
3128
|
+
schemaFields._max = argsBuilder.buildAggregateFieldSchema(
|
|
2743
3129
|
model,
|
|
2744
3130
|
"_max",
|
|
2745
3131
|
shape._max
|
|
2746
3132
|
);
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
3133
|
+
}
|
|
3134
|
+
if (shape.by) {
|
|
3135
|
+
schemaFields.by = argsBuilder.buildBySchema(model, shape.by);
|
|
3136
|
+
}
|
|
3137
|
+
if (shape.having) {
|
|
3138
|
+
schemaFields.having = argsBuilder.buildHavingSchema(
|
|
2751
3139
|
model,
|
|
2752
3140
|
shape.having
|
|
2753
3141
|
);
|
|
3142
|
+
}
|
|
2754
3143
|
return {
|
|
2755
3144
|
zodSchema: z7.object(schemaFields).strict(),
|
|
2756
3145
|
forcedWhere,
|
|
@@ -2768,7 +3157,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2768
3157
|
return { key: matched, shape: shapes[matched] };
|
|
2769
3158
|
}
|
|
2770
3159
|
function resolveDefaultShape(config, model, method, normalizedBody, opts, builtCache, isUnique) {
|
|
2771
|
-
const shapeOrFn = config
|
|
3160
|
+
const shapeOrFn = config.default;
|
|
2772
3161
|
let built;
|
|
2773
3162
|
if (typeof shapeOrFn === "function") {
|
|
2774
3163
|
const resolved = resolveAndValidateShape(shapeOrFn, opts?.ctx);
|
|
@@ -2822,8 +3211,9 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2822
3211
|
built = builtCache.get("_default");
|
|
2823
3212
|
}
|
|
2824
3213
|
} else {
|
|
2825
|
-
if (!isPlainObject(normalizedBody))
|
|
3214
|
+
if (!isPlainObject(normalizedBody)) {
|
|
2826
3215
|
throw new ShapeError("Request body must be an object");
|
|
3216
|
+
}
|
|
2827
3217
|
if ("caller" in normalizedBody) {
|
|
2828
3218
|
throw new CallerError(
|
|
2829
3219
|
"Pass caller via opts.caller, not in the request body."
|
|
@@ -4153,8 +4543,9 @@ function buildDefaultSelectInput(config) {
|
|
|
4153
4543
|
const nested = {};
|
|
4154
4544
|
if (value.select)
|
|
4155
4545
|
nested.select = buildDefaultSelectInput(value.select);
|
|
4156
|
-
if (value.include)
|
|
4546
|
+
if (value.include) {
|
|
4157
4547
|
nested.include = buildDefaultIncludeInput(value.include);
|
|
4548
|
+
}
|
|
4158
4549
|
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
4159
4550
|
}
|
|
4160
4551
|
}
|
|
@@ -4173,8 +4564,9 @@ function buildDefaultIncludeInput(config) {
|
|
|
4173
4564
|
result[key] = true;
|
|
4174
4565
|
} else {
|
|
4175
4566
|
const nested = {};
|
|
4176
|
-
if (value.include)
|
|
4567
|
+
if (value.include) {
|
|
4177
4568
|
nested.include = buildDefaultIncludeInput(value.include);
|
|
4569
|
+
}
|
|
4178
4570
|
if (value.select)
|
|
4179
4571
|
nested.select = buildDefaultSelectInput(value.select);
|
|
4180
4572
|
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
@@ -4185,8 +4577,9 @@ function buildDefaultIncludeInput(config) {
|
|
|
4185
4577
|
function buildDefaultCountInput(config) {
|
|
4186
4578
|
if (config === true)
|
|
4187
4579
|
return true;
|
|
4188
|
-
if (!isPlainObject(config) || !config.select || !isPlainObject(config.select))
|
|
4580
|
+
if (!isPlainObject(config) || !config.select || !isPlainObject(config.select)) {
|
|
4189
4581
|
return true;
|
|
4582
|
+
}
|
|
4190
4583
|
const selectObj = config.select;
|
|
4191
4584
|
const result = {};
|
|
4192
4585
|
for (const key of Object.keys(selectObj)) {
|
|
@@ -4268,8 +4661,9 @@ function checkIncludeForClientArgs(config) {
|
|
|
4268
4661
|
continue;
|
|
4269
4662
|
if (value.orderBy || value.cursor || value.take || value.skip)
|
|
4270
4663
|
return true;
|
|
4271
|
-
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where))
|
|
4664
|
+
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where)) {
|
|
4272
4665
|
return true;
|
|
4666
|
+
}
|
|
4273
4667
|
if (value.include && checkIncludeForClientArgs(value.include))
|
|
4274
4668
|
return true;
|
|
4275
4669
|
if (value.select && checkSelectForClientArgs(value.select))
|
|
@@ -4296,8 +4690,9 @@ function checkSelectForClientArgs(config) {
|
|
|
4296
4690
|
continue;
|
|
4297
4691
|
if (value.orderBy || value.cursor || value.take || value.skip)
|
|
4298
4692
|
return true;
|
|
4299
|
-
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where))
|
|
4693
|
+
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where)) {
|
|
4300
4694
|
return true;
|
|
4695
|
+
}
|
|
4301
4696
|
if (value.select && checkSelectForClientArgs(value.select))
|
|
4302
4697
|
return true;
|
|
4303
4698
|
if (value.include && checkIncludeForClientArgs(value.include))
|
|
@@ -4358,36 +4753,7 @@ function createModelGuardExtension(config) {
|
|
|
4358
4753
|
validateUniqueEquality(modelName, shape.where, method, uniqueMap, typeMap);
|
|
4359
4754
|
}
|
|
4360
4755
|
function validateUniqueWhereShapeConfig(modelName, where, method) {
|
|
4361
|
-
|
|
4362
|
-
if (!constraints || constraints.length === 0)
|
|
4363
|
-
return;
|
|
4364
|
-
const equalityFields = /* @__PURE__ */ new Set();
|
|
4365
|
-
for (const [key, value] of Object.entries(where)) {
|
|
4366
|
-
if (key === "AND" || key === "OR" || key === "NOT")
|
|
4367
|
-
continue;
|
|
4368
|
-
if (value === true) {
|
|
4369
|
-
equalityFields.add(key);
|
|
4370
|
-
continue;
|
|
4371
|
-
}
|
|
4372
|
-
if (isPlainObject(value)) {
|
|
4373
|
-
if ("equals" in value) {
|
|
4374
|
-
equalityFields.add(key);
|
|
4375
|
-
}
|
|
4376
|
-
continue;
|
|
4377
|
-
}
|
|
4378
|
-
if (value !== null && value !== void 0) {
|
|
4379
|
-
equalityFields.add(key);
|
|
4380
|
-
}
|
|
4381
|
-
}
|
|
4382
|
-
const valid = constraints.some(
|
|
4383
|
-
(constraint) => constraint.every((field) => equalityFields.has(field))
|
|
4384
|
-
);
|
|
4385
|
-
if (!valid) {
|
|
4386
|
-
const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
|
|
4387
|
-
throw new ShapeError(
|
|
4388
|
-
`${method} on model "${modelName}" requires where to cover a unique constraint with equality operators only: ${constraintDesc}`
|
|
4389
|
-
);
|
|
4390
|
-
}
|
|
4756
|
+
validateUniqueEquality(modelName, where, method, uniqueMap, typeMap);
|
|
4391
4757
|
}
|
|
4392
4758
|
function createGuardedMethods(modelName, modelDelegate, input, explicitCaller) {
|
|
4393
4759
|
function callDelegate(method, args) {
|
|
@@ -4661,10 +5027,10 @@ function createModelGuardExtension(config) {
|
|
|
4661
5027
|
`Invalid "where" on model "${modelName}": unique where must be a plain object`
|
|
4662
5028
|
);
|
|
4663
5029
|
}
|
|
4664
|
-
const sanitized =
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
}
|
|
5030
|
+
const sanitized = hasWhereForced(built.forced) ? stripUniqueWhereForcedInput(
|
|
5031
|
+
bodyWhere,
|
|
5032
|
+
built.forced
|
|
5033
|
+
) : { ...bodyWhere };
|
|
4668
5034
|
try {
|
|
4669
5035
|
result = built.schema.parse(sanitized);
|
|
4670
5036
|
} catch (err) {
|
|
@@ -4677,16 +5043,23 @@ function createModelGuardExtension(config) {
|
|
|
4677
5043
|
}
|
|
4678
5044
|
} else if (bodyWhere !== void 0 && bodyWhere !== null) {
|
|
4679
5045
|
if (isPlainObject(bodyWhere)) {
|
|
4680
|
-
const
|
|
4681
|
-
|
|
5046
|
+
const sanitized = hasWhereForced(built.forced) ? stripUniqueWhereForcedInput(
|
|
5047
|
+
bodyWhere,
|
|
5048
|
+
built.forced
|
|
5049
|
+
) : { ...bodyWhere };
|
|
5050
|
+
if (Object.keys(sanitized).length > 0) {
|
|
4682
5051
|
throw new ShapeError(
|
|
4683
5052
|
`Unique where on model "${modelName}" contains only forced values. Client where input is not accepted.`
|
|
4684
5053
|
);
|
|
4685
5054
|
}
|
|
5055
|
+
} else {
|
|
5056
|
+
throw new ShapeError(
|
|
5057
|
+
`Invalid "where" on model "${modelName}": unique where must be a plain object`
|
|
5058
|
+
);
|
|
4686
5059
|
}
|
|
4687
5060
|
}
|
|
4688
|
-
|
|
4689
|
-
result
|
|
5061
|
+
if (hasWhereForced(built.forced)) {
|
|
5062
|
+
result = mergeUniqueWhereForced(result, built.forced);
|
|
4690
5063
|
}
|
|
4691
5064
|
return result;
|
|
4692
5065
|
}
|
|
@@ -4699,7 +5072,9 @@ function createModelGuardExtension(config) {
|
|
|
4699
5072
|
);
|
|
4700
5073
|
if (Object.keys(where).length === 0) {
|
|
4701
5074
|
const constraints = uniqueMap[modelName];
|
|
4702
|
-
const constraintDesc = constraints ? constraints.map(
|
|
5075
|
+
const constraintDesc = constraints ? constraints.map(
|
|
5076
|
+
(constraint) => constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`
|
|
5077
|
+
).join(" | ") : "unknown";
|
|
4703
5078
|
const expectedFields = shape.where ? Object.keys(shape.where).join(", ") : "none defined";
|
|
4704
5079
|
throw new ShapeError(
|
|
4705
5080
|
`${method} on model "${modelName}" requires a unique where condition. Unique constraints: ${constraintDesc}. Shape allows: ${expectedFields}`
|
|
@@ -4775,8 +5150,9 @@ function createModelGuardExtension(config) {
|
|
|
4775
5150
|
return (body) => {
|
|
4776
5151
|
const caller = resolveCaller();
|
|
4777
5152
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
4778
|
-
if (!resolved.shape.data)
|
|
5153
|
+
if (!resolved.shape.data) {
|
|
4779
5154
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
5155
|
+
}
|
|
4780
5156
|
validateMutationShapeKeys(
|
|
4781
5157
|
resolved.shape,
|
|
4782
5158
|
allowedShapeKeys,
|
|
@@ -4807,10 +5183,12 @@ function createModelGuardExtension(config) {
|
|
|
4807
5183
|
);
|
|
4808
5184
|
args = { data };
|
|
4809
5185
|
} else {
|
|
4810
|
-
if (!Array.isArray(resolved.body.data))
|
|
5186
|
+
if (!Array.isArray(resolved.body.data)) {
|
|
4811
5187
|
throw new ShapeError(`${method} expects data to be an array`);
|
|
4812
|
-
|
|
5188
|
+
}
|
|
5189
|
+
if (resolved.body.data.length === 0) {
|
|
4813
5190
|
throw new ShapeError(`${method} received empty data array`);
|
|
5191
|
+
}
|
|
4814
5192
|
const data = resolved.body.data.map(
|
|
4815
5193
|
(item) => validateAndMergeData(item, dataSchema, method, modelName)
|
|
4816
5194
|
);
|
|
@@ -4844,8 +5222,9 @@ function createModelGuardExtension(config) {
|
|
|
4844
5222
|
return (body) => {
|
|
4845
5223
|
const caller = resolveCaller();
|
|
4846
5224
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
4847
|
-
if (!resolved.shape.data)
|
|
5225
|
+
if (!resolved.shape.data) {
|
|
4848
5226
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
5227
|
+
}
|
|
4849
5228
|
validateMutationShapeKeys(
|
|
4850
5229
|
resolved.shape,
|
|
4851
5230
|
allowedShapeKeys,
|
|
@@ -4924,8 +5303,9 @@ function createModelGuardExtension(config) {
|
|
|
4924
5303
|
return (body) => {
|
|
4925
5304
|
const caller = resolveCaller();
|
|
4926
5305
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
4927
|
-
if (resolved.shape.data)
|
|
5306
|
+
if (resolved.shape.data) {
|
|
4928
5307
|
throw new ShapeError(`Guard shape "data" is not valid for ${method}`);
|
|
5308
|
+
}
|
|
4929
5309
|
validateMutationShapeKeys(
|
|
4930
5310
|
resolved.shape,
|
|
4931
5311
|
allowedShapeKeys,
|