prisma-guard 1.23.0 → 1.25.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 +312 -257
- package/dist/generator/index.js.map +1 -1
- package/dist/runtime/index.cjs +766 -403
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +65 -25
- package/dist/runtime/index.d.ts +65 -25
- package/dist/runtime/index.js +766 -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
|
-
if (!value || !isPlainObject(value))
|
|
1239
|
-
continue;
|
|
1240
|
-
if ("equals" in value) {
|
|
1241
|
-
fields.add(key);
|
|
1290
|
+
if (fieldMeta.isRelation) {
|
|
1291
|
+
throw new ShapeError(
|
|
1292
|
+
`Relation field "${model}.${field}" cannot be used in unique where`
|
|
1293
|
+
);
|
|
1242
1294
|
}
|
|
1243
1295
|
}
|
|
1244
|
-
|
|
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
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
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) {
|
|
@@ -1654,7 +1774,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1654
1774
|
);
|
|
1655
1775
|
}
|
|
1656
1776
|
if (modeConfigValue === true) {
|
|
1657
|
-
opSchemas
|
|
1777
|
+
opSchemas.mode = z4.enum(["default", "insensitive"]).optional();
|
|
1658
1778
|
} else {
|
|
1659
1779
|
const actualModeValue = isForcedValue(modeConfigValue) ? modeConfigValue.value : modeConfigValue;
|
|
1660
1780
|
const modeSchema = z4.enum(["default", "insensitive"]);
|
|
@@ -1666,10 +1786,10 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1666
1786
|
`Invalid forced value for "${model}.${fieldName}.mode": ${err.message}`
|
|
1667
1787
|
);
|
|
1668
1788
|
}
|
|
1669
|
-
fieldForced
|
|
1789
|
+
fieldForced.mode = parsed;
|
|
1670
1790
|
}
|
|
1671
1791
|
} else if (hasModeCompatibleOp) {
|
|
1672
|
-
opSchemas
|
|
1792
|
+
opSchemas.mode = z4.enum(["default", "insensitive"]).optional();
|
|
1673
1793
|
}
|
|
1674
1794
|
if (hasClientOps) {
|
|
1675
1795
|
const opObj = z4.object(opSchemas).strict();
|
|
@@ -1697,12 +1817,60 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1697
1817
|
scalarConditions[fieldName] = fieldForced;
|
|
1698
1818
|
}
|
|
1699
1819
|
}
|
|
1820
|
+
function getUniqueConstraint(model, selector) {
|
|
1821
|
+
return (uniqueMap[model] ?? []).find(
|
|
1822
|
+
(constraint) => constraint.selector === selector
|
|
1823
|
+
) ?? null;
|
|
1824
|
+
}
|
|
1825
|
+
function buildDirectUniqueSchema(fieldMeta) {
|
|
1826
|
+
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
1827
|
+
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
1828
|
+
return wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, base);
|
|
1829
|
+
}
|
|
1830
|
+
return base;
|
|
1831
|
+
}
|
|
1832
|
+
function parseForcedUniqueValue(model, fieldName, fieldMeta, value) {
|
|
1833
|
+
const schema = buildDirectUniqueSchema(fieldMeta);
|
|
1834
|
+
const actual = isForcedValue(value) ? value.value : value;
|
|
1835
|
+
try {
|
|
1836
|
+
return schema.parse(actual);
|
|
1837
|
+
} catch (err) {
|
|
1838
|
+
throw new ShapeError(
|
|
1839
|
+
`Invalid forced value for unique where "${model}.${fieldName}": ${err.message}`
|
|
1840
|
+
);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
function assertCompoundSelectorConfig(model, selector, constraint, value) {
|
|
1844
|
+
const actual = isForcedValue(value) ? value.value : value;
|
|
1845
|
+
if (!isPlainObject(actual)) {
|
|
1846
|
+
throw new ShapeError(
|
|
1847
|
+
`Compound unique selector "${model}.${selector}" must be an object with fields: ${constraint.fields.join(", ")}`
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
const allowed = new Set(constraint.fields);
|
|
1851
|
+
const keys = Object.keys(actual);
|
|
1852
|
+
for (const key of keys) {
|
|
1853
|
+
if (!allowed.has(key)) {
|
|
1854
|
+
throw new ShapeError(
|
|
1855
|
+
`Unknown field "${key}" in compound unique selector "${model}.${selector}". Allowed fields: ${constraint.fields.join(", ")}`
|
|
1856
|
+
);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
for (const field of constraint.fields) {
|
|
1860
|
+
if (!(field in actual)) {
|
|
1861
|
+
throw new ShapeError(
|
|
1862
|
+
`Missing field "${field}" in compound unique selector "${model}.${selector}"`
|
|
1863
|
+
);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
return actual;
|
|
1867
|
+
}
|
|
1700
1868
|
function buildUniqueWhereSchema(model, whereConfig) {
|
|
1701
1869
|
const modelFields = typeMap[model];
|
|
1702
1870
|
if (!modelFields)
|
|
1703
1871
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1704
1872
|
const fieldSchemas = {};
|
|
1705
|
-
const
|
|
1873
|
+
const forcedConditions = {};
|
|
1706
1874
|
const forcedOnlyKeys = /* @__PURE__ */ new Set();
|
|
1707
1875
|
for (const [key, value] of Object.entries(whereConfig)) {
|
|
1708
1876
|
if (COMBINATOR_KEYS.has(key)) {
|
|
@@ -1710,65 +1878,97 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1710
1878
|
`Combinator "${key}" is not supported in unique where for model "${model}"`
|
|
1711
1879
|
);
|
|
1712
1880
|
}
|
|
1881
|
+
const constraint = getUniqueConstraint(model, key);
|
|
1882
|
+
if (constraint && constraint.fields.length > 1) {
|
|
1883
|
+
const compoundConfig = assertCompoundSelectorConfig(
|
|
1884
|
+
model,
|
|
1885
|
+
key,
|
|
1886
|
+
constraint,
|
|
1887
|
+
value
|
|
1888
|
+
);
|
|
1889
|
+
const nestedSchemas = {};
|
|
1890
|
+
const forcedCompound = {};
|
|
1891
|
+
for (const fieldName of constraint.fields) {
|
|
1892
|
+
const fieldMeta2 = modelFields[fieldName];
|
|
1893
|
+
if (!fieldMeta2) {
|
|
1894
|
+
throw new ShapeError(
|
|
1895
|
+
`Unknown field "${fieldName}" in compound unique selector "${model}.${key}"`
|
|
1896
|
+
);
|
|
1897
|
+
}
|
|
1898
|
+
if (fieldMeta2.isRelation) {
|
|
1899
|
+
throw new ShapeError(
|
|
1900
|
+
`Relation field "${fieldName}" cannot be used in compound unique selector "${model}.${key}"`
|
|
1901
|
+
);
|
|
1902
|
+
}
|
|
1903
|
+
const fieldValue = compoundConfig[fieldName];
|
|
1904
|
+
const forced2 = isForcedValue(fieldValue);
|
|
1905
|
+
if (!forced2 && isPlainObject(fieldValue)) {
|
|
1906
|
+
const operators = Object.keys(fieldValue);
|
|
1907
|
+
throw new ShapeError(
|
|
1908
|
+
`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.`
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
const directSchema2 = buildDirectUniqueSchema(fieldMeta2);
|
|
1912
|
+
if (fieldValue === true) {
|
|
1913
|
+
nestedSchemas[fieldName] = directSchema2;
|
|
1914
|
+
} else {
|
|
1915
|
+
forcedCompound[fieldName] = parseForcedUniqueValue(
|
|
1916
|
+
model,
|
|
1917
|
+
fieldName,
|
|
1918
|
+
fieldMeta2,
|
|
1919
|
+
fieldValue
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
if (Object.keys(nestedSchemas).length > 0) {
|
|
1924
|
+
fieldSchemas[key] = z4.object(nestedSchemas).strict();
|
|
1925
|
+
}
|
|
1926
|
+
if (Object.keys(forcedCompound).length > 0) {
|
|
1927
|
+
forcedConditions[key] = forcedCompound;
|
|
1928
|
+
}
|
|
1929
|
+
if (Object.keys(nestedSchemas).length === 0) {
|
|
1930
|
+
forcedOnlyKeys.add(key);
|
|
1931
|
+
}
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1713
1934
|
const fieldMeta = modelFields[key];
|
|
1714
|
-
if (!fieldMeta)
|
|
1715
|
-
|
|
1716
|
-
if (fieldMeta.isRelation)
|
|
1935
|
+
if (!fieldMeta) {
|
|
1936
|
+
const selectors = (uniqueMap[model] ?? []).map((constraint2) => constraint2.selector).join(", ");
|
|
1717
1937
|
throw new ShapeError(
|
|
1718
|
-
`
|
|
1719
|
-
);
|
|
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
|
|
1938
|
+
`Unknown unique field or selector "${key}" on model "${model}"${selectors ? `. Unique selectors: ${selectors}` : ""}`
|
|
1727
1939
|
);
|
|
1728
|
-
} else {
|
|
1729
|
-
directSchema = base;
|
|
1730
1940
|
}
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1941
|
+
if (fieldMeta.isRelation) {
|
|
1942
|
+
throw new ShapeError(
|
|
1943
|
+
`Relation field "${key}" cannot be used in unique where for model "${model}"`
|
|
1944
|
+
);
|
|
1735
1945
|
}
|
|
1736
|
-
|
|
1946
|
+
const forced = isForcedValue(value);
|
|
1947
|
+
if (!forced && isPlainObject(value)) {
|
|
1737
1948
|
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;
|
|
1949
|
+
if (keys.includes("equals")) {
|
|
1950
|
+
throw new ShapeError(
|
|
1951
|
+
`Invalid unique where shape for "${model}.${key}". Prisma WhereUniqueInput does not accept filter operator objects. Use { ${key}: true } instead of { ${key}: { equals: true } }.`
|
|
1952
|
+
);
|
|
1754
1953
|
}
|
|
1755
1954
|
throw new ShapeError(
|
|
1756
|
-
`
|
|
1955
|
+
`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
1956
|
);
|
|
1758
1957
|
}
|
|
1759
|
-
const
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
throw new ShapeError(
|
|
1764
|
-
`Invalid forced value for unique where "${model}.${key}": ${err.message}`
|
|
1765
|
-
);
|
|
1958
|
+
const directSchema = buildDirectUniqueSchema(fieldMeta);
|
|
1959
|
+
if (value === true) {
|
|
1960
|
+
fieldSchemas[key] = directSchema;
|
|
1961
|
+
continue;
|
|
1766
1962
|
}
|
|
1963
|
+
forcedConditions[key] = parseForcedUniqueValue(model, key, fieldMeta, value);
|
|
1767
1964
|
forcedOnlyKeys.add(key);
|
|
1768
1965
|
}
|
|
1769
1966
|
return {
|
|
1770
1967
|
schema: Object.keys(fieldSchemas).length > 0 ? z4.object(fieldSchemas).strict() : null,
|
|
1771
|
-
forced
|
|
1968
|
+
forced: {
|
|
1969
|
+
conditions: forcedConditions,
|
|
1970
|
+
relations: {}
|
|
1971
|
+
},
|
|
1772
1972
|
forcedOnlyKeys
|
|
1773
1973
|
};
|
|
1774
1974
|
}
|
|
@@ -1787,6 +1987,15 @@ function requireConfigTrue(config, context) {
|
|
|
1787
1987
|
}
|
|
1788
1988
|
}
|
|
1789
1989
|
}
|
|
1990
|
+
function isPlainRecord(value) {
|
|
1991
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1992
|
+
}
|
|
1993
|
+
function formatUniqueConstraint2(constraint) {
|
|
1994
|
+
return constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`;
|
|
1995
|
+
}
|
|
1996
|
+
function formatUniqueConstraints2(constraints) {
|
|
1997
|
+
return constraints.map(formatUniqueConstraint2).join(" | ");
|
|
1998
|
+
}
|
|
1790
1999
|
function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
1791
2000
|
const sortEnum = z5.enum(["asc", "desc"]);
|
|
1792
2001
|
const nullsEnum = z5.enum(["first", "last"]);
|
|
@@ -1797,11 +2006,17 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
1797
2006
|
if (!fieldMeta)
|
|
1798
2007
|
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
1799
2008
|
if (fieldMeta.isRelation)
|
|
1800
|
-
throw new ShapeError(
|
|
2009
|
+
throw new ShapeError(
|
|
2010
|
+
`Relation field "${fieldName}" in orderBy requires a nested config object, not true`
|
|
2011
|
+
);
|
|
1801
2012
|
if (fieldMeta.type === "Json")
|
|
1802
|
-
throw new ShapeError(
|
|
2013
|
+
throw new ShapeError(
|
|
2014
|
+
`Json field "${fieldName}" cannot be used in orderBy`
|
|
2015
|
+
);
|
|
1803
2016
|
if (fieldMeta.isList)
|
|
1804
|
-
throw new ShapeError(
|
|
2017
|
+
throw new ShapeError(
|
|
2018
|
+
`List field "${fieldName}" cannot be used in orderBy`
|
|
2019
|
+
);
|
|
1805
2020
|
}
|
|
1806
2021
|
function buildOrderBySchema(model, orderByConfig) {
|
|
1807
2022
|
const modelFields = typeMap[model];
|
|
@@ -1811,158 +2026,274 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
1811
2026
|
for (const [fieldName, config] of Object.entries(orderByConfig)) {
|
|
1812
2027
|
const fieldMeta = modelFields[fieldName];
|
|
1813
2028
|
if (!fieldMeta)
|
|
1814
|
-
throw new ShapeError(
|
|
2029
|
+
throw new ShapeError(
|
|
2030
|
+
`Unknown field "${fieldName}" on model "${model}"`
|
|
2031
|
+
);
|
|
1815
2032
|
if (config === true) {
|
|
1816
2033
|
validateScalarOrderByField(fieldName, model, modelFields);
|
|
1817
2034
|
fieldSchemas[fieldName] = scalarOrderSchema.optional();
|
|
1818
2035
|
continue;
|
|
1819
2036
|
}
|
|
1820
|
-
if (
|
|
1821
|
-
throw new ShapeError(
|
|
2037
|
+
if (!isPlainRecord(config)) {
|
|
2038
|
+
throw new ShapeError(
|
|
2039
|
+
`orderBy config for "${fieldName}" on model "${model}" must be true or a relation aggregate object`
|
|
2040
|
+
);
|
|
1822
2041
|
}
|
|
1823
2042
|
if (!fieldMeta.isRelation) {
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
2043
|
+
const allowedOps = getSupportedOperators(
|
|
2044
|
+
fieldMeta.type,
|
|
2045
|
+
fieldMeta.isList
|
|
2046
|
+
);
|
|
2047
|
+
const opSchemas = {};
|
|
2048
|
+
for (const [op, enabled] of Object.entries(config)) {
|
|
2049
|
+
if (enabled !== true) {
|
|
2050
|
+
throw new ShapeError(
|
|
2051
|
+
`orderBy operator config for "${model}.${fieldName}.${op}" must be true`
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
if (!allowedOps.includes(op)) {
|
|
2055
|
+
throw new ShapeError(
|
|
2056
|
+
`Operator "${op}" not supported for orderBy field "${model}.${fieldName}"`
|
|
2057
|
+
);
|
|
2058
|
+
}
|
|
2059
|
+
opSchemas[op] = scalarOrderSchema.optional();
|
|
2060
|
+
}
|
|
2061
|
+
const opKeys = Object.keys(opSchemas);
|
|
2062
|
+
fieldSchemas[fieldName] = z5.object(opSchemas).strict().refine(
|
|
2063
|
+
(v) => opKeys.some(
|
|
2064
|
+
(k) => v[k] !== void 0
|
|
2065
|
+
),
|
|
2066
|
+
{
|
|
2067
|
+
message: `orderBy field "${fieldName}" must specify at least one operator`
|
|
2068
|
+
}
|
|
2069
|
+
).optional();
|
|
2070
|
+
continue;
|
|
1828
2071
|
}
|
|
1829
2072
|
if (fieldMeta.isList) {
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
2073
|
+
if (!("_count" in config)) {
|
|
2074
|
+
throw new ShapeError(
|
|
2075
|
+
`To-many relation orderBy "${fieldName}" only supports _count`
|
|
2076
|
+
);
|
|
1833
2077
|
}
|
|
1834
2078
|
if (config._count !== true) {
|
|
1835
|
-
throw new ShapeError(
|
|
2079
|
+
throw new ShapeError(
|
|
2080
|
+
`orderBy relation aggregate "${fieldName}._count" must be true`
|
|
2081
|
+
);
|
|
1836
2082
|
}
|
|
1837
|
-
fieldSchemas[fieldName] = z5.object({
|
|
2083
|
+
fieldSchemas[fieldName] = z5.object({
|
|
2084
|
+
_count: sortEnum.optional()
|
|
2085
|
+
}).strict().optional();
|
|
1838
2086
|
continue;
|
|
1839
2087
|
}
|
|
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();
|
|
2088
|
+
const nested = buildOrderBySchema(
|
|
2089
|
+
fieldMeta.type,
|
|
2090
|
+
config
|
|
2091
|
+
);
|
|
2092
|
+
fieldSchemas[fieldName] = nested;
|
|
1865
2093
|
}
|
|
1866
2094
|
const fieldKeys = Object.keys(fieldSchemas);
|
|
1867
2095
|
const singleSchema = z5.object(fieldSchemas).strict().refine(
|
|
1868
|
-
(v) => fieldKeys.some(
|
|
2096
|
+
(v) => fieldKeys.some(
|
|
2097
|
+
(k) => v[k] !== void 0
|
|
2098
|
+
),
|
|
1869
2099
|
{ message: "orderBy must specify at least one field" }
|
|
1870
2100
|
);
|
|
1871
|
-
return z5.union([
|
|
2101
|
+
return z5.union([
|
|
2102
|
+
singleSchema,
|
|
2103
|
+
z5.preprocess(coerceToArray, z5.array(singleSchema).min(1))
|
|
2104
|
+
]).optional();
|
|
1872
2105
|
}
|
|
1873
2106
|
function buildTakeSchema(config) {
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
2107
|
+
if (typeof config === "number") {
|
|
2108
|
+
if (!Number.isFinite(config) || !Number.isInteger(config)) {
|
|
2109
|
+
throw new ShapeError(`take must be a finite integer, got ${config}`);
|
|
2110
|
+
}
|
|
2111
|
+
if (config <= 0) {
|
|
2112
|
+
throw new ShapeError("take must be a positive integer");
|
|
2113
|
+
}
|
|
2114
|
+
return z5.literal(config).optional();
|
|
2115
|
+
}
|
|
2116
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
2117
|
+
throw new ShapeError("take config must be a number or { max, default? }");
|
|
2118
|
+
}
|
|
2119
|
+
if (!Number.isFinite(config.max) || !Number.isInteger(config.max)) {
|
|
2120
|
+
throw new ShapeError(
|
|
2121
|
+
`take.max must be a finite integer, got ${config.max}`
|
|
2122
|
+
);
|
|
1877
2123
|
}
|
|
1878
|
-
if (
|
|
1879
|
-
throw new ShapeError(
|
|
2124
|
+
if (config.max <= 0) {
|
|
2125
|
+
throw new ShapeError("take.max must be a positive integer");
|
|
1880
2126
|
}
|
|
1881
|
-
if (
|
|
1882
|
-
if (!Number.isFinite(
|
|
1883
|
-
throw new ShapeError(
|
|
2127
|
+
if (config.default !== void 0) {
|
|
2128
|
+
if (!Number.isFinite(config.default) || !Number.isInteger(config.default)) {
|
|
2129
|
+
throw new ShapeError(
|
|
2130
|
+
`take.default must be a finite integer, got ${config.default}`
|
|
2131
|
+
);
|
|
1884
2132
|
}
|
|
1885
|
-
if (
|
|
1886
|
-
throw new ShapeError(
|
|
2133
|
+
if (config.default <= 0) {
|
|
2134
|
+
throw new ShapeError("take.default must be a positive integer");
|
|
1887
2135
|
}
|
|
1888
|
-
if (
|
|
1889
|
-
throw new ShapeError("take
|
|
2136
|
+
if (config.default > config.max) {
|
|
2137
|
+
throw new ShapeError("take.default cannot exceed take.max");
|
|
1890
2138
|
}
|
|
1891
|
-
return z5.number().int().min(1).max(
|
|
2139
|
+
return z5.number().int().min(1).max(config.max).default(config.default);
|
|
1892
2140
|
}
|
|
1893
|
-
return z5.number().int().min(1).max(
|
|
2141
|
+
return z5.number().int().min(1).max(config.max).optional();
|
|
1894
2142
|
}
|
|
1895
|
-
function
|
|
2143
|
+
function buildCursorFieldSchema(model, fieldName) {
|
|
1896
2144
|
const modelFields = typeMap[model];
|
|
1897
2145
|
if (!modelFields)
|
|
1898
2146
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1899
|
-
|
|
1900
|
-
|
|
2147
|
+
const fieldMeta = modelFields[fieldName];
|
|
2148
|
+
if (!fieldMeta) {
|
|
2149
|
+
throw new ShapeError(
|
|
2150
|
+
`Unknown field "${fieldName}" on model "${model}" in cursor`
|
|
2151
|
+
);
|
|
2152
|
+
}
|
|
2153
|
+
if (fieldMeta.isRelation) {
|
|
2154
|
+
throw new ShapeError(
|
|
2155
|
+
`Relation field "${fieldName}" cannot be used in cursor`
|
|
2156
|
+
);
|
|
2157
|
+
}
|
|
2158
|
+
if (fieldMeta.isList) {
|
|
2159
|
+
throw new ShapeError(
|
|
2160
|
+
`List field "${fieldName}" cannot be used in cursor`
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
2164
|
+
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
2165
|
+
return wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, base);
|
|
2166
|
+
}
|
|
2167
|
+
return base;
|
|
2168
|
+
}
|
|
2169
|
+
function cursorConfigMatchesConstraint(cursorConfig, constraint) {
|
|
2170
|
+
if (!(constraint.selector in cursorConfig))
|
|
2171
|
+
return false;
|
|
2172
|
+
const value = cursorConfig[constraint.selector];
|
|
2173
|
+
if (constraint.fields.length === 1) {
|
|
2174
|
+
return value === true;
|
|
2175
|
+
}
|
|
2176
|
+
if (!isPlainRecord(value))
|
|
2177
|
+
return false;
|
|
2178
|
+
const keys = Object.keys(value);
|
|
2179
|
+
if (keys.length !== constraint.fields.length)
|
|
2180
|
+
return false;
|
|
2181
|
+
return constraint.fields.every((field) => value[field] === true);
|
|
2182
|
+
}
|
|
2183
|
+
function getUniqueConstraints(model) {
|
|
1901
2184
|
const constraints = uniqueMap[model];
|
|
1902
2185
|
if (constraints && constraints.length > 0) {
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
2186
|
+
return constraints;
|
|
2187
|
+
}
|
|
2188
|
+
const modelFields = typeMap[model];
|
|
2189
|
+
if (!modelFields) {
|
|
2190
|
+
throw new ShapeError(`Unknown model: ${model}`);
|
|
2191
|
+
}
|
|
2192
|
+
const inferred = [];
|
|
2193
|
+
for (const [fieldName, fieldMeta] of Object.entries(modelFields)) {
|
|
2194
|
+
if (fieldMeta.isRelation)
|
|
2195
|
+
continue;
|
|
2196
|
+
if (fieldMeta.isId || fieldMeta.isUnique) {
|
|
2197
|
+
inferred.push({
|
|
2198
|
+
selector: fieldName,
|
|
2199
|
+
fields: [fieldName]
|
|
2200
|
+
});
|
|
1911
2201
|
}
|
|
1912
2202
|
}
|
|
2203
|
+
return inferred;
|
|
2204
|
+
}
|
|
2205
|
+
function buildCursorSchema(model, cursorConfig) {
|
|
2206
|
+
const constraints = getUniqueConstraints(model);
|
|
2207
|
+
if (constraints.length === 0) {
|
|
2208
|
+
throw new ShapeError(
|
|
2209
|
+
`cursor on model "${model}" requires at least one unique constraint`
|
|
2210
|
+
);
|
|
2211
|
+
}
|
|
2212
|
+
const matching = constraints.find(
|
|
2213
|
+
(constraint) => cursorConfigMatchesConstraint(cursorConfig, constraint)
|
|
2214
|
+
);
|
|
2215
|
+
if (!matching) {
|
|
2216
|
+
throw new ShapeError(
|
|
2217
|
+
`cursor on model "${model}" must exactly match a unique selector: ${formatUniqueConstraints2(constraints)}`
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
1913
2220
|
const fieldSchemas = {};
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
2221
|
+
if (matching.fields.length === 1) {
|
|
2222
|
+
fieldSchemas[matching.selector] = buildCursorFieldSchema(
|
|
2223
|
+
model,
|
|
2224
|
+
matching.fields[0]
|
|
2225
|
+
).optional();
|
|
2226
|
+
} else {
|
|
2227
|
+
const nestedSchemas = {};
|
|
2228
|
+
for (const field of matching.fields) {
|
|
2229
|
+
nestedSchemas[field] = buildCursorFieldSchema(model, field);
|
|
2230
|
+
}
|
|
2231
|
+
fieldSchemas[matching.selector] = z5.object(nestedSchemas).strict().optional();
|
|
1923
2232
|
}
|
|
1924
|
-
return z5.object(fieldSchemas).strict().
|
|
2233
|
+
return z5.object(fieldSchemas).strict().refine(
|
|
2234
|
+
(v) => v[matching.selector] !== void 0,
|
|
2235
|
+
{ message: `cursor must specify "${matching.selector}"` }
|
|
2236
|
+
).optional();
|
|
1925
2237
|
}
|
|
1926
2238
|
function buildDistinctSchema(model, distinctConfig) {
|
|
1927
|
-
if (distinctConfig.length === 0) {
|
|
1928
|
-
throw new ShapeError("distinct must contain at least one field");
|
|
1929
|
-
}
|
|
1930
2239
|
const modelFields = typeMap[model];
|
|
1931
2240
|
if (!modelFields)
|
|
1932
2241
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2242
|
+
if (!Array.isArray(distinctConfig) || distinctConfig.length === 0) {
|
|
2243
|
+
throw new ShapeError(
|
|
2244
|
+
`distinct on model "${model}" must be a non-empty array of scalar fields`
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
const allowedFields = /* @__PURE__ */ new Set();
|
|
1933
2248
|
for (const fieldName of distinctConfig) {
|
|
1934
2249
|
const fieldMeta = modelFields[fieldName];
|
|
1935
2250
|
if (!fieldMeta)
|
|
1936
|
-
throw new ShapeError(
|
|
2251
|
+
throw new ShapeError(
|
|
2252
|
+
`Unknown field "${fieldName}" on model "${model}" in distinct`
|
|
2253
|
+
);
|
|
1937
2254
|
if (fieldMeta.isRelation)
|
|
1938
|
-
throw new ShapeError(
|
|
1939
|
-
|
|
1940
|
-
|
|
2255
|
+
throw new ShapeError(
|
|
2256
|
+
`Relation field "${fieldName}" cannot be used in distinct`
|
|
2257
|
+
);
|
|
2258
|
+
allowedFields.add(fieldName);
|
|
1941
2259
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
2260
|
+
return z5.union([
|
|
2261
|
+
z5.enum([...allowedFields]),
|
|
2262
|
+
z5.array(z5.enum([...allowedFields])).min(1)
|
|
2263
|
+
]).optional();
|
|
1944
2264
|
}
|
|
1945
2265
|
function buildBySchema(model, byConfig) {
|
|
1946
|
-
if (byConfig.length === 0) {
|
|
1947
|
-
throw new ShapeError('groupBy "by" must contain at least one field');
|
|
1948
|
-
}
|
|
1949
2266
|
const modelFields = typeMap[model];
|
|
1950
2267
|
if (!modelFields)
|
|
1951
2268
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2269
|
+
if (!Array.isArray(byConfig) || byConfig.length === 0) {
|
|
2270
|
+
throw new ShapeError(
|
|
2271
|
+
`groupBy "by" on model "${model}" must be a non-empty array`
|
|
2272
|
+
);
|
|
2273
|
+
}
|
|
2274
|
+
const allowedFields = /* @__PURE__ */ new Set();
|
|
1952
2275
|
for (const fieldName of byConfig) {
|
|
1953
2276
|
const fieldMeta = modelFields[fieldName];
|
|
1954
2277
|
if (!fieldMeta)
|
|
1955
|
-
throw new ShapeError(
|
|
2278
|
+
throw new ShapeError(
|
|
2279
|
+
`Unknown field "${fieldName}" on model "${model}" in groupBy by`
|
|
2280
|
+
);
|
|
1956
2281
|
if (fieldMeta.isRelation)
|
|
1957
|
-
throw new ShapeError(
|
|
2282
|
+
throw new ShapeError(
|
|
2283
|
+
`Relation field "${fieldName}" cannot be used in groupBy by`
|
|
2284
|
+
);
|
|
2285
|
+
if (fieldMeta.isList)
|
|
2286
|
+
throw new ShapeError(
|
|
2287
|
+
`List field "${fieldName}" cannot be used in groupBy by`
|
|
2288
|
+
);
|
|
1958
2289
|
if (UNSUPPORTED_BY_TYPES.has(fieldMeta.type)) {
|
|
1959
|
-
throw new ShapeError(
|
|
2290
|
+
throw new ShapeError(
|
|
2291
|
+
`${fieldMeta.type} field "${fieldName}" cannot be used in groupBy by`
|
|
2292
|
+
);
|
|
1960
2293
|
}
|
|
1961
|
-
|
|
1962
|
-
throw new ShapeError(`List field "${fieldName}" cannot be used in by`);
|
|
2294
|
+
allowedFields.add(fieldName);
|
|
1963
2295
|
}
|
|
1964
|
-
|
|
1965
|
-
return z5.union([enumSchema, z5.preprocess(coerceToArray, z5.array(enumSchema).min(1))]);
|
|
2296
|
+
return z5.array(z5.enum([...allowedFields])).min(1);
|
|
1966
2297
|
}
|
|
1967
2298
|
function buildHavingSchema(model, havingConfig) {
|
|
1968
2299
|
const modelFields = typeMap[model];
|
|
@@ -1973,82 +2304,88 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
1973
2304
|
for (const fieldName of Object.keys(havingConfig)) {
|
|
1974
2305
|
const fieldMeta = modelFields[fieldName];
|
|
1975
2306
|
if (!fieldMeta)
|
|
1976
|
-
throw new ShapeError(
|
|
2307
|
+
throw new ShapeError(
|
|
2308
|
+
`Unknown field "${fieldName}" on model "${model}" in having`
|
|
2309
|
+
);
|
|
1977
2310
|
if (fieldMeta.isRelation)
|
|
1978
|
-
throw new ShapeError(
|
|
2311
|
+
throw new ShapeError(
|
|
2312
|
+
`Relation field "${fieldName}" cannot be used in having`
|
|
2313
|
+
);
|
|
1979
2314
|
if (fieldMeta.isList)
|
|
1980
|
-
throw new ShapeError(
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2315
|
+
throw new ShapeError(
|
|
2316
|
+
`List field "${fieldName}" cannot be used in having`
|
|
2317
|
+
);
|
|
2318
|
+
if (UNSUPPORTED_BY_TYPES.has(fieldMeta.type)) {
|
|
2319
|
+
throw new ShapeError(
|
|
2320
|
+
`${fieldMeta.type} field "${fieldName}" cannot be used in having`
|
|
2321
|
+
);
|
|
1984
2322
|
}
|
|
2323
|
+
const allowedOps = getSupportedOperators(
|
|
2324
|
+
fieldMeta.type,
|
|
2325
|
+
fieldMeta.isList
|
|
2326
|
+
);
|
|
1985
2327
|
const opSchemas = {};
|
|
1986
|
-
const
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2328
|
+
for (const op of allowedOps) {
|
|
2329
|
+
opSchemas[op] = createOperatorSchema(
|
|
2330
|
+
fieldMeta,
|
|
2331
|
+
op,
|
|
2332
|
+
enumMap,
|
|
2333
|
+
scalarBase
|
|
2334
|
+
).optional();
|
|
1990
2335
|
}
|
|
1991
|
-
if (fieldMeta.type === "String"
|
|
1992
|
-
opSchemas
|
|
2336
|
+
if (fieldMeta.type === "String") {
|
|
2337
|
+
opSchemas.mode = z5.enum(["default", "insensitive"]).optional();
|
|
1993
2338
|
}
|
|
2339
|
+
const opKeys = Object.keys(opSchemas).filter((key) => key !== "mode");
|
|
1994
2340
|
fieldSchemas[fieldName] = z5.object(opSchemas).strict().refine(
|
|
1995
2341
|
(v) => opKeys.some((k) => v[k] !== void 0),
|
|
1996
|
-
{
|
|
2342
|
+
{
|
|
2343
|
+
message: `having field "${fieldName}" must specify at least one operator`
|
|
2344
|
+
}
|
|
1997
2345
|
).optional();
|
|
1998
2346
|
}
|
|
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();
|
|
2347
|
+
const fieldKeys = Object.keys(fieldSchemas);
|
|
2348
|
+
return z5.object(fieldSchemas).strict().refine(
|
|
2349
|
+
(v) => fieldKeys.some(
|
|
2350
|
+
(k) => v[k] !== void 0
|
|
2351
|
+
),
|
|
2352
|
+
{ message: "having must specify at least one field" }
|
|
2353
|
+
).optional();
|
|
2015
2354
|
}
|
|
2016
|
-
function buildAggregateFieldSchema(model,
|
|
2355
|
+
function buildAggregateFieldSchema(model, op, config) {
|
|
2017
2356
|
const modelFields = typeMap[model];
|
|
2018
2357
|
if (!modelFields)
|
|
2019
2358
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2020
|
-
requireConfigTrue(
|
|
2021
|
-
const
|
|
2022
|
-
const isComparableOnly = opName === "_min" || opName === "_max";
|
|
2359
|
+
requireConfigTrue(config, `${op} on model "${model}"`);
|
|
2360
|
+
const allowedTypes = op === "_avg" || op === "_sum" ? NUMERIC_TYPES : COMPARABLE_TYPES;
|
|
2023
2361
|
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
|
-
}
|
|
2362
|
+
for (const fieldName of Object.keys(config)) {
|
|
2029
2363
|
const fieldMeta = modelFields[fieldName];
|
|
2030
2364
|
if (!fieldMeta)
|
|
2031
|
-
throw new ShapeError(
|
|
2365
|
+
throw new ShapeError(
|
|
2366
|
+
`Unknown field "${fieldName}" on model "${model}" in ${op}`
|
|
2367
|
+
);
|
|
2032
2368
|
if (fieldMeta.isRelation)
|
|
2033
|
-
throw new ShapeError(
|
|
2369
|
+
throw new ShapeError(
|
|
2370
|
+
`Relation field "${fieldName}" cannot be used in ${op}`
|
|
2371
|
+
);
|
|
2034
2372
|
if (fieldMeta.isList)
|
|
2035
|
-
throw new ShapeError(`List field "${fieldName}" cannot be used in ${opName}`);
|
|
2036
|
-
if (isNumericOnly && !NUMERIC_TYPES.has(fieldMeta.type)) {
|
|
2037
2373
|
throw new ShapeError(
|
|
2038
|
-
`
|
|
2374
|
+
`List field "${fieldName}" cannot be used in ${op}`
|
|
2039
2375
|
);
|
|
2040
|
-
|
|
2041
|
-
if (isComparableOnly && !COMPARABLE_TYPES.has(fieldMeta.type)) {
|
|
2376
|
+
if (!allowedTypes.has(fieldMeta.type)) {
|
|
2042
2377
|
throw new ShapeError(
|
|
2043
|
-
`Field "${fieldName}"
|
|
2378
|
+
`Field "${fieldName}" of type "${fieldMeta.type}" cannot be used in ${op}`
|
|
2044
2379
|
);
|
|
2045
2380
|
}
|
|
2046
2381
|
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
2047
2382
|
}
|
|
2048
|
-
const
|
|
2383
|
+
const aggregateFieldKeys = Object.keys(fieldSchemas);
|
|
2049
2384
|
return z5.object(fieldSchemas).strict().refine(
|
|
2050
|
-
(v) =>
|
|
2051
|
-
|
|
2385
|
+
(v) => aggregateFieldKeys.some(
|
|
2386
|
+
(k) => v[k] !== void 0
|
|
2387
|
+
),
|
|
2388
|
+
{ message: `${op} must specify at least one field` }
|
|
2052
2389
|
).optional();
|
|
2053
2390
|
}
|
|
2054
2391
|
function buildCountFieldSchema(model, config, context) {
|
|
@@ -2064,15 +2401,21 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2064
2401
|
if (fieldName !== "_all") {
|
|
2065
2402
|
const fieldMeta = modelFields[fieldName];
|
|
2066
2403
|
if (!fieldMeta)
|
|
2067
|
-
throw new ShapeError(
|
|
2404
|
+
throw new ShapeError(
|
|
2405
|
+
`Unknown field "${fieldName}" on model "${model}" in ${context}`
|
|
2406
|
+
);
|
|
2068
2407
|
if (fieldMeta.isRelation)
|
|
2069
|
-
throw new ShapeError(
|
|
2408
|
+
throw new ShapeError(
|
|
2409
|
+
`Relation field "${fieldName}" cannot be used in ${context}`
|
|
2410
|
+
);
|
|
2070
2411
|
}
|
|
2071
2412
|
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
2072
2413
|
}
|
|
2073
2414
|
const countFieldKeys = Object.keys(fieldSchemas);
|
|
2074
2415
|
return z5.object(fieldSchemas).strict().refine(
|
|
2075
|
-
(v) => countFieldKeys.some(
|
|
2416
|
+
(v) => countFieldKeys.some(
|
|
2417
|
+
(k) => v[k] !== void 0
|
|
2418
|
+
),
|
|
2076
2419
|
{ message: `${context} must specify at least one field` }
|
|
2077
2420
|
).optional();
|
|
2078
2421
|
}
|
|
@@ -2084,19 +2427,25 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2084
2427
|
const fieldSchemas = {};
|
|
2085
2428
|
for (const fieldName of Object.keys(selectConfig)) {
|
|
2086
2429
|
if (fieldName === "_all") {
|
|
2087
|
-
fieldSchemas
|
|
2430
|
+
fieldSchemas._all = z5.literal(true).optional();
|
|
2088
2431
|
continue;
|
|
2089
2432
|
}
|
|
2090
2433
|
const fieldMeta = modelFields[fieldName];
|
|
2091
2434
|
if (!fieldMeta)
|
|
2092
|
-
throw new ShapeError(
|
|
2435
|
+
throw new ShapeError(
|
|
2436
|
+
`Unknown field "${fieldName}" on model "${model}" in count select`
|
|
2437
|
+
);
|
|
2093
2438
|
if (fieldMeta.isRelation)
|
|
2094
|
-
throw new ShapeError(
|
|
2439
|
+
throw new ShapeError(
|
|
2440
|
+
`Relation field "${fieldName}" cannot be used in count select`
|
|
2441
|
+
);
|
|
2095
2442
|
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
2096
2443
|
}
|
|
2097
2444
|
const countSelectKeys = Object.keys(fieldSchemas);
|
|
2098
2445
|
return z5.object(fieldSchemas).strict().refine(
|
|
2099
|
-
(v) => countSelectKeys.some(
|
|
2446
|
+
(v) => countSelectKeys.some(
|
|
2447
|
+
(k) => v[k] !== void 0
|
|
2448
|
+
),
|
|
2100
2449
|
{ message: "count select must specify at least one field" }
|
|
2101
2450
|
).optional();
|
|
2102
2451
|
}
|
|
@@ -2505,7 +2854,12 @@ var UNIQUE_WHERE_METHODS = /* @__PURE__ */ new Set([
|
|
|
2505
2854
|
"findUniqueOrThrow"
|
|
2506
2855
|
]);
|
|
2507
2856
|
function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
2508
|
-
const whereBuilder = createWhereBuilder(
|
|
2857
|
+
const whereBuilder = createWhereBuilder(
|
|
2858
|
+
typeMap,
|
|
2859
|
+
enumMap,
|
|
2860
|
+
scalarBase,
|
|
2861
|
+
uniqueMap
|
|
2862
|
+
);
|
|
2509
2863
|
const argsBuilder = createArgsBuilder(
|
|
2510
2864
|
typeMap,
|
|
2511
2865
|
enumMap,
|
|
@@ -2527,24 +2881,28 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2527
2881
|
function validateShapeArgs(method, shape) {
|
|
2528
2882
|
const allowed = METHOD_ALLOWED_ARGS[method];
|
|
2529
2883
|
for (const key of Object.keys(shape)) {
|
|
2530
|
-
if (!SHAPE_CONFIG_KEYS.has(key))
|
|
2884
|
+
if (!SHAPE_CONFIG_KEYS.has(key)) {
|
|
2531
2885
|
throw new ShapeError(`Unknown shape config key "${key}"`);
|
|
2532
|
-
|
|
2886
|
+
}
|
|
2887
|
+
if (!allowed.has(key)) {
|
|
2533
2888
|
throw new ShapeError(`Arg "${key}" not allowed for method "${method}"`);
|
|
2889
|
+
}
|
|
2534
2890
|
}
|
|
2535
2891
|
if (UNIQUE_WHERE_METHODS.has(method) && !shape.where) {
|
|
2536
2892
|
throw new ShapeError(`${method} shape must define "where"`);
|
|
2537
2893
|
}
|
|
2538
|
-
if (method === "groupBy" && !shape.by)
|
|
2894
|
+
if (method === "groupBy" && !shape.by) {
|
|
2539
2895
|
throw new ShapeError('groupBy shape must define "by"');
|
|
2896
|
+
}
|
|
2540
2897
|
if (method === "groupBy" && (shape.include || shape.select)) {
|
|
2541
2898
|
throw new ShapeError('groupBy does not support "include" or "select"');
|
|
2542
2899
|
}
|
|
2543
2900
|
if (method === "aggregate" && (shape.include || shape.select)) {
|
|
2544
2901
|
throw new ShapeError('aggregate does not support "include" or "select"');
|
|
2545
2902
|
}
|
|
2546
|
-
if (method === "count" && shape.include)
|
|
2903
|
+
if (method === "count" && shape.include) {
|
|
2547
2904
|
throw new ShapeError('count does not support "include"');
|
|
2905
|
+
}
|
|
2548
2906
|
if (method === "groupBy" && shape.orderBy && shape.orderBy !== true) {
|
|
2549
2907
|
const bySet = new Set(shape.by);
|
|
2550
2908
|
for (const fieldName of Object.keys(shape.orderBy)) {
|
|
@@ -2599,30 +2957,28 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2599
2957
|
let forcedIncludeCountWhere = {};
|
|
2600
2958
|
let forcedSelectCountWhere = {};
|
|
2601
2959
|
if (shape.where) {
|
|
2602
|
-
const
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
forcedWhere = forced;
|
|
2609
|
-
forcedOnlyWhereKeys = forcedOnlyKeys;
|
|
2960
|
+
const builtWhere = UNIQUE_WHERE_METHODS.has(method) ? whereBuilder.buildUniqueWhereSchema(model, shape.where) : whereBuilder.buildWhereSchema(model, shape.where);
|
|
2961
|
+
if (builtWhere.schema) {
|
|
2962
|
+
schemaFields.where = builtWhere.schema;
|
|
2963
|
+
}
|
|
2964
|
+
forcedWhere = builtWhere.forced;
|
|
2965
|
+
forcedOnlyWhereKeys = builtWhere.forcedOnlyKeys;
|
|
2610
2966
|
}
|
|
2611
2967
|
if (shape.include) {
|
|
2612
2968
|
const result = projectionBuilder.buildIncludeSchema(model, shape.include);
|
|
2613
|
-
schemaFields
|
|
2969
|
+
schemaFields.include = result.schema;
|
|
2614
2970
|
forcedIncludeTree = result.forcedTree;
|
|
2615
2971
|
forcedIncludeCountWhere = result.forcedCountWhere;
|
|
2616
2972
|
}
|
|
2617
2973
|
if (shape.select) {
|
|
2618
2974
|
if (method === "count") {
|
|
2619
|
-
schemaFields
|
|
2975
|
+
schemaFields.select = argsBuilder.buildCountSelectSchema(
|
|
2620
2976
|
model,
|
|
2621
2977
|
shape.select
|
|
2622
2978
|
);
|
|
2623
2979
|
} else {
|
|
2624
2980
|
const result = projectionBuilder.buildSelectSchema(model, shape.select);
|
|
2625
|
-
schemaFields
|
|
2981
|
+
schemaFields.select = result.schema;
|
|
2626
2982
|
forcedSelectTree = result.forcedTree;
|
|
2627
2983
|
forcedSelectCountWhere = result.forcedCountWhere;
|
|
2628
2984
|
}
|
|
@@ -2636,7 +2992,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2636
2992
|
for (const field of shape.by) {
|
|
2637
2993
|
groupByOrderFields[field] = sortEnum.optional();
|
|
2638
2994
|
}
|
|
2639
|
-
groupByOrderFields
|
|
2995
|
+
groupByOrderFields._count = sortEnum.optional();
|
|
2640
2996
|
const fieldKeys = Object.keys(groupByOrderFields);
|
|
2641
2997
|
const singleSchema = z7.object(groupByOrderFields).strict().refine(
|
|
2642
2998
|
(v) => fieldKeys.some(
|
|
@@ -2644,13 +3000,16 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2644
3000
|
),
|
|
2645
3001
|
{ message: "orderBy must specify at least one field" }
|
|
2646
3002
|
);
|
|
2647
|
-
schemaFields
|
|
3003
|
+
schemaFields.orderBy = z7.union([
|
|
3004
|
+
singleSchema,
|
|
3005
|
+
z7.preprocess(coerceToArray, z7.array(singleSchema).min(1))
|
|
3006
|
+
]).optional();
|
|
2648
3007
|
} else {
|
|
2649
3008
|
const groupByOrderFields = {};
|
|
2650
3009
|
for (const [fieldName, config] of Object.entries(shape.orderBy)) {
|
|
2651
3010
|
if (fieldName === "_count") {
|
|
2652
3011
|
if (config === true) {
|
|
2653
|
-
groupByOrderFields
|
|
3012
|
+
groupByOrderFields._count = sortEnum.optional();
|
|
2654
3013
|
} else if (typeof config === "object" && config !== null) {
|
|
2655
3014
|
const countFields = {};
|
|
2656
3015
|
for (const countField of Object.keys(config)) {
|
|
@@ -2662,7 +3021,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2662
3021
|
countFields[countField] = sortEnum.optional();
|
|
2663
3022
|
}
|
|
2664
3023
|
const countKeys = Object.keys(countFields);
|
|
2665
|
-
groupByOrderFields
|
|
3024
|
+
groupByOrderFields._count = z7.object(countFields).strict().refine(
|
|
2666
3025
|
(v) => countKeys.some(
|
|
2667
3026
|
(k) => v[k] !== void 0
|
|
2668
3027
|
),
|
|
@@ -2687,70 +3046,83 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2687
3046
|
),
|
|
2688
3047
|
{ message: "orderBy must specify at least one field" }
|
|
2689
3048
|
);
|
|
2690
|
-
schemaFields
|
|
3049
|
+
schemaFields.orderBy = z7.union([
|
|
3050
|
+
singleSchema,
|
|
3051
|
+
z7.preprocess(coerceToArray, z7.array(singleSchema).min(1))
|
|
3052
|
+
]).optional();
|
|
2691
3053
|
}
|
|
2692
3054
|
} else {
|
|
2693
|
-
schemaFields
|
|
3055
|
+
schemaFields.orderBy = argsBuilder.buildOrderBySchema(
|
|
2694
3056
|
model,
|
|
2695
3057
|
shape.orderBy
|
|
2696
3058
|
);
|
|
2697
3059
|
}
|
|
2698
3060
|
}
|
|
2699
|
-
if (shape.cursor)
|
|
2700
|
-
schemaFields
|
|
3061
|
+
if (shape.cursor) {
|
|
3062
|
+
schemaFields.cursor = argsBuilder.buildCursorSchema(
|
|
2701
3063
|
model,
|
|
2702
3064
|
shape.cursor
|
|
2703
3065
|
);
|
|
2704
|
-
|
|
2705
|
-
|
|
3066
|
+
}
|
|
3067
|
+
if (shape.take) {
|
|
3068
|
+
schemaFields.take = argsBuilder.buildTakeSchema(shape.take);
|
|
3069
|
+
}
|
|
2706
3070
|
if (shape.skip !== void 0) {
|
|
2707
3071
|
if (shape.skip !== true) {
|
|
2708
3072
|
throw new ShapeError('Shape config "skip" must be true');
|
|
2709
3073
|
}
|
|
2710
|
-
schemaFields
|
|
3074
|
+
schemaFields.skip = z7.number().int().min(0).optional();
|
|
2711
3075
|
}
|
|
2712
|
-
if (shape.distinct)
|
|
2713
|
-
schemaFields
|
|
3076
|
+
if (shape.distinct) {
|
|
3077
|
+
schemaFields.distinct = argsBuilder.buildDistinctSchema(
|
|
2714
3078
|
model,
|
|
2715
3079
|
shape.distinct
|
|
2716
3080
|
);
|
|
2717
|
-
|
|
2718
|
-
|
|
3081
|
+
}
|
|
3082
|
+
if (shape._count) {
|
|
3083
|
+
schemaFields._count = argsBuilder.buildCountFieldSchema(
|
|
2719
3084
|
model,
|
|
2720
3085
|
shape._count,
|
|
2721
3086
|
"_count"
|
|
2722
3087
|
);
|
|
2723
|
-
|
|
2724
|
-
|
|
3088
|
+
}
|
|
3089
|
+
if (shape._avg) {
|
|
3090
|
+
schemaFields._avg = argsBuilder.buildAggregateFieldSchema(
|
|
2725
3091
|
model,
|
|
2726
3092
|
"_avg",
|
|
2727
3093
|
shape._avg
|
|
2728
3094
|
);
|
|
2729
|
-
|
|
2730
|
-
|
|
3095
|
+
}
|
|
3096
|
+
if (shape._sum) {
|
|
3097
|
+
schemaFields._sum = argsBuilder.buildAggregateFieldSchema(
|
|
2731
3098
|
model,
|
|
2732
3099
|
"_sum",
|
|
2733
3100
|
shape._sum
|
|
2734
3101
|
);
|
|
2735
|
-
|
|
2736
|
-
|
|
3102
|
+
}
|
|
3103
|
+
if (shape._min) {
|
|
3104
|
+
schemaFields._min = argsBuilder.buildAggregateFieldSchema(
|
|
2737
3105
|
model,
|
|
2738
3106
|
"_min",
|
|
2739
3107
|
shape._min
|
|
2740
3108
|
);
|
|
2741
|
-
|
|
2742
|
-
|
|
3109
|
+
}
|
|
3110
|
+
if (shape._max) {
|
|
3111
|
+
schemaFields._max = argsBuilder.buildAggregateFieldSchema(
|
|
2743
3112
|
model,
|
|
2744
3113
|
"_max",
|
|
2745
3114
|
shape._max
|
|
2746
3115
|
);
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
3116
|
+
}
|
|
3117
|
+
if (shape.by) {
|
|
3118
|
+
schemaFields.by = argsBuilder.buildBySchema(model, shape.by);
|
|
3119
|
+
}
|
|
3120
|
+
if (shape.having) {
|
|
3121
|
+
schemaFields.having = argsBuilder.buildHavingSchema(
|
|
2751
3122
|
model,
|
|
2752
3123
|
shape.having
|
|
2753
3124
|
);
|
|
3125
|
+
}
|
|
2754
3126
|
return {
|
|
2755
3127
|
zodSchema: z7.object(schemaFields).strict(),
|
|
2756
3128
|
forcedWhere,
|
|
@@ -2768,7 +3140,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2768
3140
|
return { key: matched, shape: shapes[matched] };
|
|
2769
3141
|
}
|
|
2770
3142
|
function resolveDefaultShape(config, model, method, normalizedBody, opts, builtCache, isUnique) {
|
|
2771
|
-
const shapeOrFn = config
|
|
3143
|
+
const shapeOrFn = config.default;
|
|
2772
3144
|
let built;
|
|
2773
3145
|
if (typeof shapeOrFn === "function") {
|
|
2774
3146
|
const resolved = resolveAndValidateShape(shapeOrFn, opts?.ctx);
|
|
@@ -2822,8 +3194,9 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2822
3194
|
built = builtCache.get("_default");
|
|
2823
3195
|
}
|
|
2824
3196
|
} else {
|
|
2825
|
-
if (!isPlainObject(normalizedBody))
|
|
3197
|
+
if (!isPlainObject(normalizedBody)) {
|
|
2826
3198
|
throw new ShapeError("Request body must be an object");
|
|
3199
|
+
}
|
|
2827
3200
|
if ("caller" in normalizedBody) {
|
|
2828
3201
|
throw new CallerError(
|
|
2829
3202
|
"Pass caller via opts.caller, not in the request body."
|
|
@@ -4153,8 +4526,9 @@ function buildDefaultSelectInput(config) {
|
|
|
4153
4526
|
const nested = {};
|
|
4154
4527
|
if (value.select)
|
|
4155
4528
|
nested.select = buildDefaultSelectInput(value.select);
|
|
4156
|
-
if (value.include)
|
|
4529
|
+
if (value.include) {
|
|
4157
4530
|
nested.include = buildDefaultIncludeInput(value.include);
|
|
4531
|
+
}
|
|
4158
4532
|
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
4159
4533
|
}
|
|
4160
4534
|
}
|
|
@@ -4173,8 +4547,9 @@ function buildDefaultIncludeInput(config) {
|
|
|
4173
4547
|
result[key] = true;
|
|
4174
4548
|
} else {
|
|
4175
4549
|
const nested = {};
|
|
4176
|
-
if (value.include)
|
|
4550
|
+
if (value.include) {
|
|
4177
4551
|
nested.include = buildDefaultIncludeInput(value.include);
|
|
4552
|
+
}
|
|
4178
4553
|
if (value.select)
|
|
4179
4554
|
nested.select = buildDefaultSelectInput(value.select);
|
|
4180
4555
|
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
@@ -4185,8 +4560,9 @@ function buildDefaultIncludeInput(config) {
|
|
|
4185
4560
|
function buildDefaultCountInput(config) {
|
|
4186
4561
|
if (config === true)
|
|
4187
4562
|
return true;
|
|
4188
|
-
if (!isPlainObject(config) || !config.select || !isPlainObject(config.select))
|
|
4563
|
+
if (!isPlainObject(config) || !config.select || !isPlainObject(config.select)) {
|
|
4189
4564
|
return true;
|
|
4565
|
+
}
|
|
4190
4566
|
const selectObj = config.select;
|
|
4191
4567
|
const result = {};
|
|
4192
4568
|
for (const key of Object.keys(selectObj)) {
|
|
@@ -4268,8 +4644,9 @@ function checkIncludeForClientArgs(config) {
|
|
|
4268
4644
|
continue;
|
|
4269
4645
|
if (value.orderBy || value.cursor || value.take || value.skip)
|
|
4270
4646
|
return true;
|
|
4271
|
-
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where))
|
|
4647
|
+
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where)) {
|
|
4272
4648
|
return true;
|
|
4649
|
+
}
|
|
4273
4650
|
if (value.include && checkIncludeForClientArgs(value.include))
|
|
4274
4651
|
return true;
|
|
4275
4652
|
if (value.select && checkSelectForClientArgs(value.select))
|
|
@@ -4296,8 +4673,9 @@ function checkSelectForClientArgs(config) {
|
|
|
4296
4673
|
continue;
|
|
4297
4674
|
if (value.orderBy || value.cursor || value.take || value.skip)
|
|
4298
4675
|
return true;
|
|
4299
|
-
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where))
|
|
4676
|
+
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where)) {
|
|
4300
4677
|
return true;
|
|
4678
|
+
}
|
|
4301
4679
|
if (value.select && checkSelectForClientArgs(value.select))
|
|
4302
4680
|
return true;
|
|
4303
4681
|
if (value.include && checkIncludeForClientArgs(value.include))
|
|
@@ -4358,36 +4736,7 @@ function createModelGuardExtension(config) {
|
|
|
4358
4736
|
validateUniqueEquality(modelName, shape.where, method, uniqueMap, typeMap);
|
|
4359
4737
|
}
|
|
4360
4738
|
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
|
-
}
|
|
4739
|
+
validateUniqueEquality(modelName, where, method, uniqueMap, typeMap);
|
|
4391
4740
|
}
|
|
4392
4741
|
function createGuardedMethods(modelName, modelDelegate, input, explicitCaller) {
|
|
4393
4742
|
function callDelegate(method, args) {
|
|
@@ -4661,10 +5010,10 @@ function createModelGuardExtension(config) {
|
|
|
4661
5010
|
`Invalid "where" on model "${modelName}": unique where must be a plain object`
|
|
4662
5011
|
);
|
|
4663
5012
|
}
|
|
4664
|
-
const sanitized =
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
}
|
|
5013
|
+
const sanitized = hasWhereForced(built.forced) ? stripUniqueWhereForcedInput(
|
|
5014
|
+
bodyWhere,
|
|
5015
|
+
built.forced
|
|
5016
|
+
) : { ...bodyWhere };
|
|
4668
5017
|
try {
|
|
4669
5018
|
result = built.schema.parse(sanitized);
|
|
4670
5019
|
} catch (err) {
|
|
@@ -4677,16 +5026,23 @@ function createModelGuardExtension(config) {
|
|
|
4677
5026
|
}
|
|
4678
5027
|
} else if (bodyWhere !== void 0 && bodyWhere !== null) {
|
|
4679
5028
|
if (isPlainObject(bodyWhere)) {
|
|
4680
|
-
const
|
|
4681
|
-
|
|
5029
|
+
const sanitized = hasWhereForced(built.forced) ? stripUniqueWhereForcedInput(
|
|
5030
|
+
bodyWhere,
|
|
5031
|
+
built.forced
|
|
5032
|
+
) : { ...bodyWhere };
|
|
5033
|
+
if (Object.keys(sanitized).length > 0) {
|
|
4682
5034
|
throw new ShapeError(
|
|
4683
5035
|
`Unique where on model "${modelName}" contains only forced values. Client where input is not accepted.`
|
|
4684
5036
|
);
|
|
4685
5037
|
}
|
|
5038
|
+
} else {
|
|
5039
|
+
throw new ShapeError(
|
|
5040
|
+
`Invalid "where" on model "${modelName}": unique where must be a plain object`
|
|
5041
|
+
);
|
|
4686
5042
|
}
|
|
4687
5043
|
}
|
|
4688
|
-
|
|
4689
|
-
result
|
|
5044
|
+
if (hasWhereForced(built.forced)) {
|
|
5045
|
+
result = mergeUniqueWhereForced(result, built.forced);
|
|
4690
5046
|
}
|
|
4691
5047
|
return result;
|
|
4692
5048
|
}
|
|
@@ -4699,7 +5055,9 @@ function createModelGuardExtension(config) {
|
|
|
4699
5055
|
);
|
|
4700
5056
|
if (Object.keys(where).length === 0) {
|
|
4701
5057
|
const constraints = uniqueMap[modelName];
|
|
4702
|
-
const constraintDesc = constraints ? constraints.map(
|
|
5058
|
+
const constraintDesc = constraints ? constraints.map(
|
|
5059
|
+
(constraint) => constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`
|
|
5060
|
+
).join(" | ") : "unknown";
|
|
4703
5061
|
const expectedFields = shape.where ? Object.keys(shape.where).join(", ") : "none defined";
|
|
4704
5062
|
throw new ShapeError(
|
|
4705
5063
|
`${method} on model "${modelName}" requires a unique where condition. Unique constraints: ${constraintDesc}. Shape allows: ${expectedFields}`
|
|
@@ -4775,8 +5133,9 @@ function createModelGuardExtension(config) {
|
|
|
4775
5133
|
return (body) => {
|
|
4776
5134
|
const caller = resolveCaller();
|
|
4777
5135
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
4778
|
-
if (!resolved.shape.data)
|
|
5136
|
+
if (!resolved.shape.data) {
|
|
4779
5137
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
5138
|
+
}
|
|
4780
5139
|
validateMutationShapeKeys(
|
|
4781
5140
|
resolved.shape,
|
|
4782
5141
|
allowedShapeKeys,
|
|
@@ -4807,10 +5166,12 @@ function createModelGuardExtension(config) {
|
|
|
4807
5166
|
);
|
|
4808
5167
|
args = { data };
|
|
4809
5168
|
} else {
|
|
4810
|
-
if (!Array.isArray(resolved.body.data))
|
|
5169
|
+
if (!Array.isArray(resolved.body.data)) {
|
|
4811
5170
|
throw new ShapeError(`${method} expects data to be an array`);
|
|
4812
|
-
|
|
5171
|
+
}
|
|
5172
|
+
if (resolved.body.data.length === 0) {
|
|
4813
5173
|
throw new ShapeError(`${method} received empty data array`);
|
|
5174
|
+
}
|
|
4814
5175
|
const data = resolved.body.data.map(
|
|
4815
5176
|
(item) => validateAndMergeData(item, dataSchema, method, modelName)
|
|
4816
5177
|
);
|
|
@@ -4844,8 +5205,9 @@ function createModelGuardExtension(config) {
|
|
|
4844
5205
|
return (body) => {
|
|
4845
5206
|
const caller = resolveCaller();
|
|
4846
5207
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
4847
|
-
if (!resolved.shape.data)
|
|
5208
|
+
if (!resolved.shape.data) {
|
|
4848
5209
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
5210
|
+
}
|
|
4849
5211
|
validateMutationShapeKeys(
|
|
4850
5212
|
resolved.shape,
|
|
4851
5213
|
allowedShapeKeys,
|
|
@@ -4924,8 +5286,9 @@ function createModelGuardExtension(config) {
|
|
|
4924
5286
|
return (body) => {
|
|
4925
5287
|
const caller = resolveCaller();
|
|
4926
5288
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
4927
|
-
if (resolved.shape.data)
|
|
5289
|
+
if (resolved.shape.data) {
|
|
4928
5290
|
throw new ShapeError(`Guard shape "data" is not valid for ${method}`);
|
|
5291
|
+
}
|
|
4929
5292
|
validateMutationShapeKeys(
|
|
4930
5293
|
resolved.shape,
|
|
4931
5294
|
allowedShapeKeys,
|