prisma-guard 1.27.1 → 1.28.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 +279 -33
- package/dist/generator/index.js +244 -209
- package/dist/generator/index.js.map +1 -1
- package/dist/runtime/index.cjs +1655 -1660
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +68 -35
- package/dist/runtime/index.d.ts +68 -35
- package/dist/runtime/index.js +1655 -1660
- package/dist/runtime/index.js.map +1 -1
- package/package.json +3 -2
package/dist/runtime/index.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// src/runtime/guard.ts
|
|
2
|
-
import { ZodError } from "zod";
|
|
3
|
-
|
|
4
1
|
// src/shared/errors.ts
|
|
5
2
|
var PolicyError = class extends Error {
|
|
6
3
|
status = 403;
|
|
@@ -30,10 +27,16 @@ function formatIssue(issue) {
|
|
|
30
27
|
const path = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
|
|
31
28
|
const code = issue.code;
|
|
32
29
|
if (code === "invalid_union") {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
const raw = issue;
|
|
31
|
+
let branchIssues = null;
|
|
32
|
+
if (Array.isArray(raw.errors) && raw.errors.length > 0) {
|
|
33
|
+
branchIssues = raw.errors;
|
|
34
|
+
} else if (Array.isArray(raw.unionErrors) && raw.unionErrors.length > 0) {
|
|
35
|
+
branchIssues = raw.unionErrors.map((ue) => ue.issues);
|
|
36
|
+
}
|
|
37
|
+
if (branchIssues) {
|
|
38
|
+
const branches = branchIssues.map((issues, i) => {
|
|
39
|
+
const nested = issues.map(formatIssue).join(", ");
|
|
37
40
|
return `branch ${i + 1}: [${nested}]`;
|
|
38
41
|
}).join(" | ");
|
|
39
42
|
return `${path}No matching variant (${branches})`;
|
|
@@ -52,6 +55,9 @@ function formatIssue(issue) {
|
|
|
52
55
|
if (expected && received) {
|
|
53
56
|
return `${path}Expected ${expected}, received ${received}`;
|
|
54
57
|
}
|
|
58
|
+
if (expected) {
|
|
59
|
+
return `${path}Expected ${expected}`;
|
|
60
|
+
}
|
|
55
61
|
}
|
|
56
62
|
if (code === "invalid_enum_value") {
|
|
57
63
|
const options = issue.options;
|
|
@@ -61,28 +67,28 @@ function formatIssue(issue) {
|
|
|
61
67
|
}
|
|
62
68
|
if (code === "too_small") {
|
|
63
69
|
const minimum = issue.minimum;
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
70
|
+
const origin = issue.origin ?? issue.type;
|
|
71
|
+
if (origin === "string" && minimum !== void 0) {
|
|
66
72
|
return `${path}String must contain at least ${minimum} character(s)`;
|
|
67
73
|
}
|
|
68
|
-
if (
|
|
74
|
+
if (origin === "array" && minimum !== void 0) {
|
|
69
75
|
return `${path}Array must contain at least ${minimum} element(s)`;
|
|
70
76
|
}
|
|
71
|
-
if (
|
|
77
|
+
if (origin === "number" && minimum !== void 0) {
|
|
72
78
|
return `${path}Number must be >= ${minimum}`;
|
|
73
79
|
}
|
|
74
80
|
return `${path}${issue.message}`;
|
|
75
81
|
}
|
|
76
82
|
if (code === "too_big") {
|
|
77
83
|
const maximum = issue.maximum;
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
84
|
+
const origin = issue.origin ?? issue.type;
|
|
85
|
+
if (origin === "string" && maximum !== void 0) {
|
|
80
86
|
return `${path}String must contain at most ${maximum} character(s)`;
|
|
81
87
|
}
|
|
82
|
-
if (
|
|
88
|
+
if (origin === "array" && maximum !== void 0) {
|
|
83
89
|
return `${path}Array must contain at most ${maximum} element(s)`;
|
|
84
90
|
}
|
|
85
|
-
if (
|
|
91
|
+
if (origin === "number" && maximum !== void 0) {
|
|
86
92
|
return `${path}Number must be <= ${maximum}`;
|
|
87
93
|
}
|
|
88
94
|
return `${path}${issue.message}`;
|
|
@@ -104,18 +110,42 @@ function formatIssue(issue) {
|
|
|
104
110
|
function formatZodError(err) {
|
|
105
111
|
return err.issues.map(formatIssue).join("; ");
|
|
106
112
|
}
|
|
107
|
-
function
|
|
113
|
+
function isZodErrorLike(err) {
|
|
114
|
+
return !!err && typeof err === "object" && "issues" in err;
|
|
115
|
+
}
|
|
116
|
+
function wrapZod(err, context) {
|
|
108
117
|
if (err instanceof ShapeError) {
|
|
109
118
|
throw new ShapeError(`${context}: ${err.message}`, { cause: err });
|
|
110
119
|
}
|
|
111
|
-
if (err
|
|
120
|
+
if (isZodErrorLike(err)) {
|
|
112
121
|
throw new ShapeError(`${context}: ${formatZodError(err)}`, { cause: err });
|
|
113
122
|
}
|
|
114
123
|
throw err;
|
|
115
124
|
}
|
|
125
|
+
function wrapParseError(err, context) {
|
|
126
|
+
return wrapZod(err, context);
|
|
127
|
+
}
|
|
128
|
+
function toShapeError(err, prefix = "Validation failed") {
|
|
129
|
+
if (isZodErrorLike(err)) {
|
|
130
|
+
return new ShapeError(`${prefix}: ${formatZodError(err)}`, { cause: err });
|
|
131
|
+
}
|
|
132
|
+
return err;
|
|
133
|
+
}
|
|
116
134
|
|
|
117
135
|
// src/shared/scalar-base.ts
|
|
118
136
|
import { z } from "zod";
|
|
137
|
+
function isPrismaNullSentinel(value) {
|
|
138
|
+
if (value === null || typeof value !== "object")
|
|
139
|
+
return false;
|
|
140
|
+
const tag = value._tag;
|
|
141
|
+
if (tag === "DbNull" || tag === "JsonNull" || tag === "AnyNull")
|
|
142
|
+
return true;
|
|
143
|
+
const constructorName = value.constructor?.name;
|
|
144
|
+
if (constructorName === "DbNull" || constructorName === "JsonNull" || constructorName === "AnyNull") {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
119
149
|
function isJsonSafe(value) {
|
|
120
150
|
const stack = [{ tag: "visit", value }];
|
|
121
151
|
const ancestors = /* @__PURE__ */ new Set();
|
|
@@ -130,6 +160,8 @@ function isJsonSafe(value) {
|
|
|
130
160
|
return false;
|
|
131
161
|
if (current === null)
|
|
132
162
|
continue;
|
|
163
|
+
if (isPrismaNullSentinel(current))
|
|
164
|
+
continue;
|
|
133
165
|
switch (typeof current) {
|
|
134
166
|
case "string":
|
|
135
167
|
case "boolean":
|
|
@@ -214,8 +246,8 @@ function wrapWithInputCoercion(fieldType, isList, schema) {
|
|
|
214
246
|
break;
|
|
215
247
|
case "Int":
|
|
216
248
|
itemCoercion = z.union([
|
|
217
|
-
z.number().
|
|
218
|
-
z.string().regex(/^-?\d
|
|
249
|
+
z.number().int(),
|
|
250
|
+
z.string().regex(/^-?\d+$/).transform((v) => Number(v))
|
|
219
251
|
]);
|
|
220
252
|
break;
|
|
221
253
|
case "Float":
|
|
@@ -232,10 +264,10 @@ function wrapWithInputCoercion(fieldType, isList, schema) {
|
|
|
232
264
|
}
|
|
233
265
|
|
|
234
266
|
// src/runtime/schema-builder.ts
|
|
235
|
-
import { z as
|
|
267
|
+
import { z as z4 } from "zod";
|
|
236
268
|
|
|
237
269
|
// src/runtime/zod-type-map.ts
|
|
238
|
-
import { z as
|
|
270
|
+
import { z as z3 } from "zod";
|
|
239
271
|
|
|
240
272
|
// src/shared/utils.ts
|
|
241
273
|
function isPlainObject(v) {
|
|
@@ -264,7 +296,7 @@ function coerceToArray(value) {
|
|
|
264
296
|
const keys = Object.keys(value);
|
|
265
297
|
if (keys.length === 0)
|
|
266
298
|
return value;
|
|
267
|
-
const allNumeric = keys.every((k) => /^\d+$/.test(k));
|
|
299
|
+
const allNumeric = keys.every((k) => /^\d+$/.test(k) && String(Number(k)) === k);
|
|
268
300
|
if (!allNumeric)
|
|
269
301
|
return value;
|
|
270
302
|
const indices = keys.map(Number).sort((a, b) => a - b);
|
|
@@ -280,6 +312,48 @@ function coerceToArray(value) {
|
|
|
280
312
|
return result;
|
|
281
313
|
}
|
|
282
314
|
|
|
315
|
+
// src/shared/zod-helpers.ts
|
|
316
|
+
import { z as z2 } from "zod";
|
|
317
|
+
function strictObjectRequiringOne(shape, message) {
|
|
318
|
+
const keys = Object.keys(shape);
|
|
319
|
+
return z2.object(shape).strict().refine(
|
|
320
|
+
(v) => keys.some((k) => v[k] !== void 0),
|
|
321
|
+
{ message }
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
function singleOrArraySchema(single) {
|
|
325
|
+
return z2.union([
|
|
326
|
+
single,
|
|
327
|
+
z2.preprocess(coerceToArray, z2.array(single).min(1))
|
|
328
|
+
]);
|
|
329
|
+
}
|
|
330
|
+
function optionalOneOrMany(single) {
|
|
331
|
+
return z2.union([single, z2.preprocess(coerceToArray, z2.array(single))]).optional();
|
|
332
|
+
}
|
|
333
|
+
function wrapRelationOp(isList, single) {
|
|
334
|
+
if (!isList)
|
|
335
|
+
return single.optional();
|
|
336
|
+
return optionalOneOrMany(single);
|
|
337
|
+
}
|
|
338
|
+
function buildLiteralTrueSchema(fieldNames, message, validate) {
|
|
339
|
+
const fieldSchemas = {};
|
|
340
|
+
for (const fieldName of fieldNames) {
|
|
341
|
+
if (validate)
|
|
342
|
+
validate(fieldName);
|
|
343
|
+
fieldSchemas[fieldName] = z2.literal(true).optional();
|
|
344
|
+
}
|
|
345
|
+
return strictObjectRequiringOne(fieldSchemas, message).optional();
|
|
346
|
+
}
|
|
347
|
+
function requireConfigTrue(config, context) {
|
|
348
|
+
for (const [key, value] of Object.entries(config)) {
|
|
349
|
+
if (value !== true) {
|
|
350
|
+
throw new ShapeError(
|
|
351
|
+
`Config value for "${key}" in ${context} must be true, got ${typeof value}`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
283
357
|
// src/runtime/zod-type-map.ts
|
|
284
358
|
var SCALAR_OPERATORS = {
|
|
285
359
|
String: /* @__PURE__ */ new Set([
|
|
@@ -367,13 +441,13 @@ function getSupportedOperators(input, isList = false, isEnum = false) {
|
|
|
367
441
|
function createBaseType(fieldMeta, enumMap, scalarBase) {
|
|
368
442
|
let base;
|
|
369
443
|
if (fieldMeta.isUnsupported) {
|
|
370
|
-
base =
|
|
444
|
+
base = z3.unknown();
|
|
371
445
|
} else if (fieldMeta.isEnum) {
|
|
372
446
|
const values = enumMap[fieldMeta.type];
|
|
373
447
|
if (!values || values.length === 0) {
|
|
374
448
|
throw new ShapeError(`Unknown enum: ${fieldMeta.type}`);
|
|
375
449
|
}
|
|
376
|
-
base =
|
|
450
|
+
base = z3.enum(values);
|
|
377
451
|
} else {
|
|
378
452
|
const factory = scalarBase[fieldMeta.type];
|
|
379
453
|
if (!factory) {
|
|
@@ -382,7 +456,7 @@ function createBaseType(fieldMeta, enumMap, scalarBase) {
|
|
|
382
456
|
base = factory();
|
|
383
457
|
}
|
|
384
458
|
if (fieldMeta.isList) {
|
|
385
|
-
base =
|
|
459
|
+
base = z3.array(base);
|
|
386
460
|
}
|
|
387
461
|
return base;
|
|
388
462
|
}
|
|
@@ -393,35 +467,38 @@ function createScalarListOperatorSchema(fieldMeta, operator, enumMap, scalarBase
|
|
|
393
467
|
);
|
|
394
468
|
}
|
|
395
469
|
if (operator === "isEmpty") {
|
|
396
|
-
return
|
|
470
|
+
return z3.boolean();
|
|
397
471
|
}
|
|
398
472
|
const itemMeta = { ...fieldMeta, isList: false };
|
|
399
473
|
const itemBase = createBaseType(itemMeta, enumMap, scalarBase);
|
|
400
474
|
if (operator === "has") {
|
|
401
|
-
return !fieldMeta.isRequired ?
|
|
475
|
+
return !fieldMeta.isRequired ? z3.union([itemBase, z3.null()]) : itemBase;
|
|
402
476
|
}
|
|
403
477
|
if (operator === "equals") {
|
|
404
|
-
const arrSchema =
|
|
405
|
-
return fieldMeta.isRequired ?
|
|
478
|
+
const arrSchema = z3.array(itemBase);
|
|
479
|
+
return fieldMeta.isRequired ? z3.preprocess(coerceToArray, arrSchema) : z3.union([z3.preprocess(coerceToArray, arrSchema), z3.null()]);
|
|
406
480
|
}
|
|
407
|
-
return
|
|
481
|
+
return z3.preprocess(coerceToArray, z3.array(itemBase));
|
|
408
482
|
}
|
|
409
483
|
function createJsonOperatorSchema(fieldMeta, operator) {
|
|
410
|
-
const jsonValue =
|
|
484
|
+
const jsonValue = z3.unknown();
|
|
411
485
|
if (operator === "equals") {
|
|
412
|
-
return !fieldMeta.isRequired ?
|
|
486
|
+
return !fieldMeta.isRequired ? z3.union([jsonValue, z3.null()]) : jsonValue;
|
|
413
487
|
}
|
|
414
488
|
if (operator === "path") {
|
|
415
|
-
return
|
|
489
|
+
return z3.preprocess(coerceToArray, z3.array(z3.string()).min(1));
|
|
416
490
|
}
|
|
417
491
|
if (JSON_STRING_OPERATORS.has(operator)) {
|
|
418
|
-
return
|
|
492
|
+
return z3.string();
|
|
419
493
|
}
|
|
420
494
|
if (JSON_ARRAY_OPERATORS.has(operator)) {
|
|
421
495
|
return jsonValue;
|
|
422
496
|
}
|
|
423
497
|
throw new ShapeError(`Operator "${operator}" not supported for Json fields`);
|
|
424
498
|
}
|
|
499
|
+
function nullableIfOptional(fieldMeta, base) {
|
|
500
|
+
return !fieldMeta.isRequired ? z3.union([base, z3.null()]) : base;
|
|
501
|
+
}
|
|
425
502
|
function buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase) {
|
|
426
503
|
const allOps = getSupportedOperators(fieldMeta);
|
|
427
504
|
const nestedOps = allOps.filter((op) => op !== "not");
|
|
@@ -437,14 +514,15 @@ function buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase) {
|
|
|
437
514
|
scalarBase
|
|
438
515
|
).optional();
|
|
439
516
|
}
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
(key) => value[key] !== void 0
|
|
444
|
-
),
|
|
445
|
-
{ message: "not filter must specify at least one operator" }
|
|
517
|
+
const nestedObj = strictObjectRequiringOne(
|
|
518
|
+
nestedSchemas,
|
|
519
|
+
"not filter must specify at least one operator"
|
|
446
520
|
);
|
|
447
|
-
return
|
|
521
|
+
return z3.union([scalarSchema, nestedObj]);
|
|
522
|
+
}
|
|
523
|
+
function buildNotOperator(fieldMeta, scalarBaseSchema, enumMap, scalarBase) {
|
|
524
|
+
const scalarSchema = nullableIfOptional(fieldMeta, scalarBaseSchema);
|
|
525
|
+
return buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase);
|
|
448
526
|
}
|
|
449
527
|
function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
|
|
450
528
|
const allowedOps = getSupportedOperators(fieldMeta);
|
|
@@ -481,32 +559,19 @@ function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
|
|
|
481
559
|
if (!values || values.length === 0) {
|
|
482
560
|
throw new ShapeError(`Unknown enum: ${fieldMeta.type}`);
|
|
483
561
|
}
|
|
484
|
-
const enumSchema =
|
|
562
|
+
const enumSchema = z3.enum(values);
|
|
485
563
|
if (operator === "equals") {
|
|
486
|
-
return
|
|
564
|
+
return nullableIfOptional(fieldMeta, enumSchema);
|
|
487
565
|
}
|
|
488
566
|
if (operator === "not") {
|
|
489
|
-
|
|
490
|
-
return buildNotFilterSchema(
|
|
491
|
-
fieldMeta,
|
|
492
|
-
scalarSchema,
|
|
493
|
-
enumMap,
|
|
494
|
-
scalarBase
|
|
495
|
-
);
|
|
567
|
+
return buildNotOperator(fieldMeta, enumSchema, enumMap, scalarBase);
|
|
496
568
|
}
|
|
497
|
-
const itemSchema =
|
|
498
|
-
return
|
|
569
|
+
const itemSchema = nullableIfOptional(fieldMeta, enumSchema);
|
|
570
|
+
return z3.preprocess(coerceToArray, z3.array(itemSchema));
|
|
499
571
|
}
|
|
500
572
|
if (fieldMeta.type === "Json") {
|
|
501
573
|
if (operator === "not") {
|
|
502
|
-
|
|
503
|
-
const scalarSchema = !fieldMeta.isRequired ? z2.union([jsonValue, z2.null()]) : jsonValue;
|
|
504
|
-
return buildNotFilterSchema(
|
|
505
|
-
fieldMeta,
|
|
506
|
-
scalarSchema,
|
|
507
|
-
enumMap,
|
|
508
|
-
scalarBase
|
|
509
|
-
);
|
|
574
|
+
return buildNotOperator(fieldMeta, z3.unknown(), enumMap, scalarBase);
|
|
510
575
|
}
|
|
511
576
|
return createJsonOperatorSchema(fieldMeta, operator);
|
|
512
577
|
}
|
|
@@ -517,15 +582,14 @@ function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
|
|
|
517
582
|
const scalar = factory();
|
|
518
583
|
const coerced = wrapWithInputCoercion(fieldMeta.type, false, scalar);
|
|
519
584
|
if (operator === "equals") {
|
|
520
|
-
return
|
|
585
|
+
return nullableIfOptional(fieldMeta, coerced);
|
|
521
586
|
}
|
|
522
587
|
if (operator === "not") {
|
|
523
|
-
|
|
524
|
-
return buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase);
|
|
588
|
+
return buildNotOperator(fieldMeta, coerced, enumMap, scalarBase);
|
|
525
589
|
}
|
|
526
590
|
if (operator === "in" || operator === "notIn") {
|
|
527
|
-
const itemSchema =
|
|
528
|
-
return
|
|
591
|
+
const itemSchema = nullableIfOptional(fieldMeta, coerced);
|
|
592
|
+
return z3.preprocess(coerceToArray, z3.array(itemSchema));
|
|
529
593
|
}
|
|
530
594
|
return coerced;
|
|
531
595
|
}
|
|
@@ -551,6 +615,26 @@ function lruSet(cache, key, value, maxSize) {
|
|
|
551
615
|
cache.delete(oldest);
|
|
552
616
|
}
|
|
553
617
|
}
|
|
618
|
+
function applyCreateUpdateNullability(fieldMeta, fieldSchema, options) {
|
|
619
|
+
const { mode, handlesUndefined } = options;
|
|
620
|
+
const allowNull = options.allowNull ?? true;
|
|
621
|
+
if (mode === "create") {
|
|
622
|
+
if (!fieldMeta.isRequired) {
|
|
623
|
+
if (handlesUndefined) {
|
|
624
|
+
return allowNull ? fieldSchema.nullable() : fieldSchema;
|
|
625
|
+
}
|
|
626
|
+
return allowNull ? fieldSchema.nullable().optional() : fieldSchema.optional();
|
|
627
|
+
}
|
|
628
|
+
if (fieldMeta.hasDefault) {
|
|
629
|
+
return handlesUndefined ? fieldSchema : fieldSchema.optional();
|
|
630
|
+
}
|
|
631
|
+
return fieldSchema;
|
|
632
|
+
}
|
|
633
|
+
if (!fieldMeta.isRequired && allowNull) {
|
|
634
|
+
return fieldSchema.nullable().optional();
|
|
635
|
+
}
|
|
636
|
+
return fieldSchema.optional();
|
|
637
|
+
}
|
|
554
638
|
function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefaults) {
|
|
555
639
|
const chainCache = /* @__PURE__ */ new Map();
|
|
556
640
|
function buildFieldSchema(model, field) {
|
|
@@ -605,20 +689,25 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
|
|
|
605
689
|
const zodDefaultSet = zodDefaultFields ? new Set(zodDefaultFields) : void 0;
|
|
606
690
|
let fieldNames = Object.keys(modelFields).filter((name) => {
|
|
607
691
|
const meta = modelFields[name];
|
|
608
|
-
return !meta.isRelation && !meta.isUpdatedAt;
|
|
692
|
+
return !meta.isRelation && !meta.isUpdatedAt && !meta.isUnsupported;
|
|
609
693
|
});
|
|
610
694
|
if (opts.pick) {
|
|
611
695
|
for (const name of opts.pick) {
|
|
612
|
-
|
|
696
|
+
const meta = modelFields[name];
|
|
697
|
+
if (!meta)
|
|
613
698
|
throw new ShapeError(`Unknown field "${name}" on model "${model}"`);
|
|
614
|
-
if (
|
|
699
|
+
if (meta.isRelation)
|
|
615
700
|
throw new ShapeError(
|
|
616
701
|
`Field "${name}" cannot be used in input schema (relation field)`
|
|
617
702
|
);
|
|
618
|
-
if (
|
|
703
|
+
if (meta.isUpdatedAt)
|
|
619
704
|
throw new ShapeError(
|
|
620
705
|
`Field "${name}" cannot be used in input schema (updatedAt field)`
|
|
621
706
|
);
|
|
707
|
+
if (meta.isUnsupported)
|
|
708
|
+
throw new ShapeError(
|
|
709
|
+
`Field "${name}" on model "${model}" has an Unsupported type and cannot be used in input schema`
|
|
710
|
+
);
|
|
622
711
|
}
|
|
623
712
|
fieldNames = fieldNames.filter((n) => opts.pick.includes(n));
|
|
624
713
|
} else if (opts.omit) {
|
|
@@ -631,7 +720,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
|
|
|
631
720
|
const schemaMap = {};
|
|
632
721
|
for (const name of fieldNames) {
|
|
633
722
|
const fieldMeta = modelFields[name];
|
|
634
|
-
let
|
|
723
|
+
let baseSchema;
|
|
635
724
|
let handlesUndefined;
|
|
636
725
|
if (opts.refine?.[name]) {
|
|
637
726
|
let refined;
|
|
@@ -648,34 +737,19 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
|
|
|
648
737
|
`Refine function for "${model}.${name}" must return a Zod schema`
|
|
649
738
|
);
|
|
650
739
|
}
|
|
651
|
-
|
|
652
|
-
handlesUndefined = schemaProducesValueForUndefined(
|
|
740
|
+
baseSchema = refined;
|
|
741
|
+
handlesUndefined = schemaProducesValueForUndefined(baseSchema);
|
|
653
742
|
} else {
|
|
654
|
-
|
|
743
|
+
baseSchema = buildFieldSchema(model, name);
|
|
655
744
|
handlesUndefined = zodDefaultSet !== void 0 && zodDefaultSet.has(name);
|
|
656
745
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
fieldSchema = allowNull ? fieldSchema.nullable().optional() : fieldSchema.optional();
|
|
663
|
-
}
|
|
664
|
-
} else if (fieldMeta.hasDefault) {
|
|
665
|
-
if (!handlesUndefined) {
|
|
666
|
-
fieldSchema = fieldSchema.optional();
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
} else {
|
|
670
|
-
if (!fieldMeta.isRequired && allowNull) {
|
|
671
|
-
fieldSchema = fieldSchema.nullable().optional();
|
|
672
|
-
} else {
|
|
673
|
-
fieldSchema = fieldSchema.optional();
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
schemaMap[name] = fieldSchema;
|
|
746
|
+
schemaMap[name] = applyCreateUpdateNullability(fieldMeta, baseSchema, {
|
|
747
|
+
mode,
|
|
748
|
+
handlesUndefined,
|
|
749
|
+
allowNull
|
|
750
|
+
});
|
|
677
751
|
}
|
|
678
|
-
let schema =
|
|
752
|
+
let schema = z4.object(schemaMap).strict();
|
|
679
753
|
if (opts.partial) {
|
|
680
754
|
schema = schema.partial();
|
|
681
755
|
}
|
|
@@ -702,13 +776,18 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
|
|
|
702
776
|
const includeKeys = new Set(Object.keys(opts.include ?? {}));
|
|
703
777
|
if (opts.pick) {
|
|
704
778
|
for (const name of opts.pick) {
|
|
705
|
-
|
|
779
|
+
const meta = modelFields[name];
|
|
780
|
+
if (!meta)
|
|
706
781
|
throw new ShapeError(`Unknown field "${name}" on model "${model}"`);
|
|
707
|
-
if (
|
|
782
|
+
if (meta.isRelation && !includeKeys.has(name)) {
|
|
708
783
|
throw new ShapeError(
|
|
709
784
|
`Field "${name}" is a relation on model "${model}". Use include: { ${name}: ... } instead of pick.`
|
|
710
785
|
);
|
|
711
786
|
}
|
|
787
|
+
if (meta.isUnsupported)
|
|
788
|
+
throw new ShapeError(
|
|
789
|
+
`Field "${name}" on model "${model}" has an Unsupported type and cannot be used in output schema`
|
|
790
|
+
);
|
|
712
791
|
}
|
|
713
792
|
}
|
|
714
793
|
if (opts.omit) {
|
|
@@ -719,7 +798,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
|
|
|
719
798
|
}
|
|
720
799
|
let scalarNames = Object.keys(modelFields).filter((name) => {
|
|
721
800
|
const meta = modelFields[name];
|
|
722
|
-
return !meta.isRelation;
|
|
801
|
+
return !meta.isRelation && !meta.isUnsupported;
|
|
723
802
|
});
|
|
724
803
|
if (opts.pick) {
|
|
725
804
|
scalarNames = scalarNames.filter((n) => opts.pick.includes(n));
|
|
@@ -756,7 +835,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
|
|
|
756
835
|
effectiveMaxDepth
|
|
757
836
|
);
|
|
758
837
|
if (fieldMeta.isList) {
|
|
759
|
-
relSchema =
|
|
838
|
+
relSchema = z4.array(relSchema);
|
|
760
839
|
} else if (!fieldMeta.isRequired) {
|
|
761
840
|
relSchema = relSchema.nullable();
|
|
762
841
|
}
|
|
@@ -769,9 +848,9 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
|
|
|
769
848
|
if (opts._count === true) {
|
|
770
849
|
const countFields = {};
|
|
771
850
|
for (const relName of listRelationNames) {
|
|
772
|
-
countFields[relName] =
|
|
851
|
+
countFields[relName] = z4.number().int().min(0);
|
|
773
852
|
}
|
|
774
|
-
schemaMap["_count"] =
|
|
853
|
+
schemaMap["_count"] = z4.object(countFields);
|
|
775
854
|
} else {
|
|
776
855
|
const countFields = {};
|
|
777
856
|
for (const relName of Object.keys(opts._count)) {
|
|
@@ -787,12 +866,12 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
|
|
|
787
866
|
throw new ShapeError(
|
|
788
867
|
`Field "${relName}" is a to-one relation on model "${model}" in _count. Only to-many relations support _count.`
|
|
789
868
|
);
|
|
790
|
-
countFields[relName] =
|
|
869
|
+
countFields[relName] = z4.number().int().min(0);
|
|
791
870
|
}
|
|
792
|
-
schemaMap["_count"] =
|
|
871
|
+
schemaMap["_count"] = z4.object(countFields);
|
|
793
872
|
}
|
|
794
873
|
}
|
|
795
|
-
let schema =
|
|
874
|
+
let schema = z4.object(schemaMap);
|
|
796
875
|
if (opts.strict) {
|
|
797
876
|
schema = schema.strict();
|
|
798
877
|
}
|
|
@@ -807,7 +886,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
|
|
|
807
886
|
}
|
|
808
887
|
|
|
809
888
|
// src/runtime/query-builder.ts
|
|
810
|
-
import { z as
|
|
889
|
+
import { z as z8 } from "zod";
|
|
811
890
|
|
|
812
891
|
// src/shared/constants.ts
|
|
813
892
|
var SHAPE_CONFIG_KEY_LIST = [
|
|
@@ -863,6 +942,8 @@ function unsupported() {
|
|
|
863
942
|
|
|
864
943
|
// src/shared/match-caller.ts
|
|
865
944
|
function matchCallerPattern(patterns, caller) {
|
|
945
|
+
if (typeof caller !== "string" || caller.trim().length === 0)
|
|
946
|
+
return null;
|
|
866
947
|
if (patterns.includes(caller))
|
|
867
948
|
return caller;
|
|
868
949
|
const matches = [];
|
|
@@ -916,7 +997,7 @@ function validateContext(ctx) {
|
|
|
916
997
|
}
|
|
917
998
|
|
|
918
999
|
// src/runtime/query-builder-where.ts
|
|
919
|
-
import { z as
|
|
1000
|
+
import { z as z5 } from "zod";
|
|
920
1001
|
|
|
921
1002
|
// src/shared/deep-clone.ts
|
|
922
1003
|
function deepClone(value) {
|
|
@@ -951,44 +1032,75 @@ function deepClone(value) {
|
|
|
951
1032
|
}
|
|
952
1033
|
}
|
|
953
1034
|
|
|
954
|
-
// src/
|
|
955
|
-
|
|
956
|
-
conditions: {},
|
|
957
|
-
relations: {}
|
|
958
|
-
};
|
|
959
|
-
function hasWhereForced(f) {
|
|
960
|
-
return Object.keys(f.conditions).length > 0 || Object.keys(f.relations).length > 0;
|
|
961
|
-
}
|
|
962
|
-
function forcedScalarsEqual(a, b) {
|
|
1035
|
+
// src/shared/deep-equal.ts
|
|
1036
|
+
function deepEqual(a, b) {
|
|
963
1037
|
if (a === b)
|
|
964
1038
|
return true;
|
|
965
1039
|
if (a === null || b === null)
|
|
966
1040
|
return false;
|
|
967
|
-
if (
|
|
1041
|
+
if (a === void 0 || b === void 0)
|
|
1042
|
+
return false;
|
|
1043
|
+
const ta = typeof a;
|
|
1044
|
+
const tb = typeof b;
|
|
1045
|
+
if (ta !== tb)
|
|
1046
|
+
return false;
|
|
1047
|
+
if (ta === "bigint")
|
|
1048
|
+
return a === b;
|
|
1049
|
+
if (ta !== "object")
|
|
968
1050
|
return false;
|
|
969
1051
|
if (a instanceof Date && b instanceof Date) {
|
|
970
1052
|
return a.getTime() === b.getTime();
|
|
971
1053
|
}
|
|
1054
|
+
if (a instanceof Date || b instanceof Date)
|
|
1055
|
+
return false;
|
|
972
1056
|
if (a instanceof RegExp && b instanceof RegExp) {
|
|
973
1057
|
return a.source === b.source && a.flags === b.flags;
|
|
974
1058
|
}
|
|
1059
|
+
if (a instanceof RegExp || b instanceof RegExp)
|
|
1060
|
+
return false;
|
|
975
1061
|
if (Array.isArray(a)) {
|
|
976
1062
|
if (!Array.isArray(b))
|
|
977
1063
|
return false;
|
|
978
1064
|
if (a.length !== b.length)
|
|
979
1065
|
return false;
|
|
980
|
-
|
|
1066
|
+
for (let i = 0; i < a.length; i++) {
|
|
1067
|
+
if (!deepEqual(a[i], b[i]))
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1070
|
+
return true;
|
|
981
1071
|
}
|
|
982
|
-
if (
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1072
|
+
if (Array.isArray(b))
|
|
1073
|
+
return false;
|
|
1074
|
+
if (!isPlainObject(a) || !isPlainObject(b))
|
|
1075
|
+
return false;
|
|
1076
|
+
const aKeys = Object.keys(a);
|
|
1077
|
+
const bKeys = Object.keys(b);
|
|
1078
|
+
if (aKeys.length !== bKeys.length)
|
|
1079
|
+
return false;
|
|
1080
|
+
for (const key of aKeys) {
|
|
1081
|
+
if (!(key in b))
|
|
1082
|
+
return false;
|
|
1083
|
+
if (!deepEqual(a[key], b[key]))
|
|
986
1084
|
return false;
|
|
987
|
-
return aKeys.every(
|
|
988
|
-
(k) => k in b && forcedScalarsEqual(a[k], b[k])
|
|
989
|
-
);
|
|
990
1085
|
}
|
|
991
|
-
return
|
|
1086
|
+
return true;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// src/shared/unique-constraints.ts
|
|
1090
|
+
function formatUniqueConstraint(constraint) {
|
|
1091
|
+
return constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`;
|
|
1092
|
+
}
|
|
1093
|
+
function formatUniqueConstraints(constraints) {
|
|
1094
|
+
return constraints.map(formatUniqueConstraint).join(" | ");
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// src/runtime/query-builder-forced.ts
|
|
1098
|
+
var EMPTY_WHERE_FORCED = {
|
|
1099
|
+
conditions: {},
|
|
1100
|
+
relations: {}
|
|
1101
|
+
};
|
|
1102
|
+
function hasWhereForced(f) {
|
|
1103
|
+
return Object.keys(f.conditions).length > 0 || Object.keys(f.relations).length > 0;
|
|
992
1104
|
}
|
|
993
1105
|
function tryInlineForcedField(result, field, forcedValue) {
|
|
994
1106
|
if (!(field in result))
|
|
@@ -1007,7 +1119,7 @@ function tryInlineForcedField(result, field, forcedValue) {
|
|
|
1007
1119
|
const merged = { ...existing };
|
|
1008
1120
|
for (const [op, value] of Object.entries(forcedValue)) {
|
|
1009
1121
|
if (op in merged) {
|
|
1010
|
-
if (!
|
|
1122
|
+
if (!deepEqual(merged[op], value)) {
|
|
1011
1123
|
throw new ShapeError(
|
|
1012
1124
|
`Conflicting where values for "${field}.${op}": client provided a different value than the forced shape`
|
|
1013
1125
|
);
|
|
@@ -1038,6 +1150,17 @@ function mergeWhereForced(where, forced) {
|
|
|
1038
1150
|
if (Object.keys(forced.conditions).length > 0) {
|
|
1039
1151
|
const remaining = {};
|
|
1040
1152
|
for (const [field, forcedValue] of Object.entries(forced.conditions)) {
|
|
1153
|
+
if (field === "NOT") {
|
|
1154
|
+
const existing = result[field];
|
|
1155
|
+
const forcedArr = Array.isArray(forcedValue) ? forcedValue.map(deepClone) : [deepClone(forcedValue)];
|
|
1156
|
+
if (existing === void 0) {
|
|
1157
|
+
result[field] = forcedArr.length === 1 ? forcedArr[0] : forcedArr;
|
|
1158
|
+
} else {
|
|
1159
|
+
const existingArr = Array.isArray(existing) ? existing : [existing];
|
|
1160
|
+
result[field] = [...existingArr, ...forcedArr];
|
|
1161
|
+
}
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1041
1164
|
const inlined = tryInlineForcedField(result, field, forcedValue);
|
|
1042
1165
|
if (!inlined) {
|
|
1043
1166
|
remaining[field] = deepClone(forcedValue);
|
|
@@ -1053,32 +1176,6 @@ function mergeWhereForced(where, forced) {
|
|
|
1053
1176
|
}
|
|
1054
1177
|
return result;
|
|
1055
1178
|
}
|
|
1056
|
-
function uniqueValuesEqual(a, b) {
|
|
1057
|
-
if (a === b)
|
|
1058
|
-
return true;
|
|
1059
|
-
if (a === null || b === null)
|
|
1060
|
-
return false;
|
|
1061
|
-
if (typeof a !== typeof b)
|
|
1062
|
-
return false;
|
|
1063
|
-
if (a instanceof Date && b instanceof Date) {
|
|
1064
|
-
return a.getTime() === b.getTime();
|
|
1065
|
-
}
|
|
1066
|
-
if (Array.isArray(a)) {
|
|
1067
|
-
if (!Array.isArray(b))
|
|
1068
|
-
return false;
|
|
1069
|
-
if (a.length !== b.length)
|
|
1070
|
-
return false;
|
|
1071
|
-
return a.every((v, i) => uniqueValuesEqual(v, b[i]));
|
|
1072
|
-
}
|
|
1073
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
1074
|
-
const aKeys = Object.keys(a);
|
|
1075
|
-
const bKeys = Object.keys(b);
|
|
1076
|
-
if (aKeys.length !== bKeys.length)
|
|
1077
|
-
return false;
|
|
1078
|
-
return aKeys.every((key) => key in b && uniqueValuesEqual(a[key], b[key]));
|
|
1079
|
-
}
|
|
1080
|
-
return false;
|
|
1081
|
-
}
|
|
1082
1179
|
function mergeUniqueValue(target, key, value) {
|
|
1083
1180
|
const cloned = deepClone(value);
|
|
1084
1181
|
if (!(key in target)) {
|
|
@@ -1089,7 +1186,7 @@ function mergeUniqueValue(target, key, value) {
|
|
|
1089
1186
|
if (isPlainObject(existing) && isPlainObject(cloned)) {
|
|
1090
1187
|
const merged = { ...existing };
|
|
1091
1188
|
for (const [nestedKey, nestedValue] of Object.entries(cloned)) {
|
|
1092
|
-
if (nestedKey in merged && !
|
|
1189
|
+
if (nestedKey in merged && !deepEqual(merged[nestedKey], nestedValue)) {
|
|
1093
1190
|
throw new ShapeError(
|
|
1094
1191
|
`Conflicting unique where value for "${key}.${nestedKey}"`
|
|
1095
1192
|
);
|
|
@@ -1099,7 +1196,7 @@ function mergeUniqueValue(target, key, value) {
|
|
|
1099
1196
|
target[key] = merged;
|
|
1100
1197
|
return;
|
|
1101
1198
|
}
|
|
1102
|
-
if (!
|
|
1199
|
+
if (!deepEqual(existing, cloned)) {
|
|
1103
1200
|
throw new ShapeError(`Conflicting unique where value for "${key}"`);
|
|
1104
1201
|
}
|
|
1105
1202
|
}
|
|
@@ -1126,7 +1223,30 @@ function applyBuiltShape(built, body, isUniqueMethod, modelName) {
|
|
|
1126
1223
|
throw new ShapeError('Request cannot define both "include" and "select"');
|
|
1127
1224
|
}
|
|
1128
1225
|
if ("where" in bodyObj) {
|
|
1129
|
-
if (
|
|
1226
|
+
if (bodyObj.where === void 0 || bodyObj.where === null) {
|
|
1227
|
+
const { where: _, ...rest } = bodyObj;
|
|
1228
|
+
parseable = rest;
|
|
1229
|
+
} else if (!hasWhereInSchema) {
|
|
1230
|
+
const context = modelName ? ` on model "${modelName}"` : "";
|
|
1231
|
+
if (!hasWhereForced(built.forcedWhere)) {
|
|
1232
|
+
throw new ShapeError(
|
|
1233
|
+
`Guard shape does not allow "where"${context}`
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
if (!isPlainObject(bodyObj.where)) {
|
|
1237
|
+
throw new ShapeError(
|
|
1238
|
+
`Invalid "where"${context}: must be a plain object`
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
const remaining = stripUniqueWhereForcedInput(
|
|
1242
|
+
bodyObj.where,
|
|
1243
|
+
built.forcedWhere
|
|
1244
|
+
);
|
|
1245
|
+
if (Object.keys(remaining).length > 0) {
|
|
1246
|
+
throw new ShapeError(
|
|
1247
|
+
`Guard shape where${context} contains only forced conditions. Client where input is not accepted.`
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1130
1250
|
const { where: _, ...rest } = bodyObj;
|
|
1131
1251
|
parseable = rest;
|
|
1132
1252
|
} else if (isUniqueMethod && hasWhereForced(built.forcedWhere) && isPlainObject(bodyObj.where)) {
|
|
@@ -1202,6 +1322,86 @@ function buildCountForPlacement(countWhere) {
|
|
|
1202
1322
|
}
|
|
1203
1323
|
return { _count: { select: countSelect } };
|
|
1204
1324
|
}
|
|
1325
|
+
function collectSubtree(forced) {
|
|
1326
|
+
const nested = {};
|
|
1327
|
+
if (forced.where && hasWhereForced(forced.where)) {
|
|
1328
|
+
nested.where = mergeWhereForced(void 0, forced.where);
|
|
1329
|
+
}
|
|
1330
|
+
if (forced.include) {
|
|
1331
|
+
nested.include = buildForcedOnlyContainer(forced.include);
|
|
1332
|
+
}
|
|
1333
|
+
if (forced.select) {
|
|
1334
|
+
nested.select = buildForcedOnlyContainer(forced.select);
|
|
1335
|
+
}
|
|
1336
|
+
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
1337
|
+
const placement = forced._countWherePlacement ?? "include";
|
|
1338
|
+
if (!nested[placement])
|
|
1339
|
+
nested[placement] = {};
|
|
1340
|
+
const placementObj = nested[placement];
|
|
1341
|
+
Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
|
|
1342
|
+
}
|
|
1343
|
+
return nested;
|
|
1344
|
+
}
|
|
1345
|
+
function applyForcedToRelValue(relName, relVal, forced) {
|
|
1346
|
+
if (relVal === true) {
|
|
1347
|
+
const expanded = collectSubtree(forced);
|
|
1348
|
+
if (forced.include) {
|
|
1349
|
+
applyForcedTree(expanded, "include", forced.include);
|
|
1350
|
+
}
|
|
1351
|
+
if (forced.select) {
|
|
1352
|
+
applyForcedTree(expanded, "select", forced.select);
|
|
1353
|
+
}
|
|
1354
|
+
if (expanded.include && expanded.select) {
|
|
1355
|
+
throw new ShapeError(
|
|
1356
|
+
`Forced tree for relation "${relName}" produces both "include" and "select". Prisma does not allow both at the same level.`
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
return Object.keys(expanded).length > 0 ? expanded : true;
|
|
1360
|
+
}
|
|
1361
|
+
if (!isPlainObject(relVal))
|
|
1362
|
+
return relVal;
|
|
1363
|
+
const relObj = relVal;
|
|
1364
|
+
if (forced.where && hasWhereForced(forced.where)) {
|
|
1365
|
+
relObj.where = mergeWhereForced(
|
|
1366
|
+
relObj.where,
|
|
1367
|
+
forced.where
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
if (forced.include) {
|
|
1371
|
+
if (!relObj.include) {
|
|
1372
|
+
relObj.include = buildForcedOnlyContainer(forced.include);
|
|
1373
|
+
}
|
|
1374
|
+
applyForcedTree(relObj, "include", forced.include);
|
|
1375
|
+
}
|
|
1376
|
+
if (forced.select) {
|
|
1377
|
+
if (!relObj.select) {
|
|
1378
|
+
relObj.select = buildForcedOnlyContainer(forced.select);
|
|
1379
|
+
}
|
|
1380
|
+
applyForcedTree(relObj, "select", forced.select);
|
|
1381
|
+
}
|
|
1382
|
+
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
1383
|
+
const placement = forced._countWherePlacement ?? "include";
|
|
1384
|
+
const existing = relObj[placement];
|
|
1385
|
+
let placementObj;
|
|
1386
|
+
if (!isPlainObject(existing)) {
|
|
1387
|
+
placementObj = {};
|
|
1388
|
+
relObj[placement] = placementObj;
|
|
1389
|
+
} else {
|
|
1390
|
+
placementObj = existing;
|
|
1391
|
+
}
|
|
1392
|
+
if (!("_count" in placementObj)) {
|
|
1393
|
+
Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
|
|
1394
|
+
} else {
|
|
1395
|
+
applyForcedCountWhere(placementObj, forced._countWhere);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
if (relObj.include && relObj.select) {
|
|
1399
|
+
throw new ShapeError(
|
|
1400
|
+
`Relation "${relName}" has both "include" and "select" after forced tree merge. Prisma does not allow both at the same level.`
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
return relObj;
|
|
1404
|
+
}
|
|
1205
1405
|
function applyForcedTree(validated, key, tree) {
|
|
1206
1406
|
const container = validated[key];
|
|
1207
1407
|
if (!container)
|
|
@@ -1210,89 +1410,13 @@ function applyForcedTree(validated, key, tree) {
|
|
|
1210
1410
|
const relVal = container[relName];
|
|
1211
1411
|
if (relVal === void 0)
|
|
1212
1412
|
continue;
|
|
1213
|
-
|
|
1214
|
-
const expanded = {};
|
|
1215
|
-
if (forced.where && hasWhereForced(forced.where)) {
|
|
1216
|
-
expanded.where = mergeWhereForced(void 0, forced.where);
|
|
1217
|
-
}
|
|
1218
|
-
if (forced.include) {
|
|
1219
|
-
expanded.include = buildForcedOnlyContainer(forced.include);
|
|
1220
|
-
applyForcedTree(expanded, "include", forced.include);
|
|
1221
|
-
}
|
|
1222
|
-
if (forced.select) {
|
|
1223
|
-
expanded.select = buildForcedOnlyContainer(forced.select);
|
|
1224
|
-
applyForcedTree(expanded, "select", forced.select);
|
|
1225
|
-
}
|
|
1226
|
-
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
1227
|
-
const placement = forced._countWherePlacement ?? "include";
|
|
1228
|
-
if (!expanded[placement])
|
|
1229
|
-
expanded[placement] = {};
|
|
1230
|
-
const placementObj = expanded[placement];
|
|
1231
|
-
Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
|
|
1232
|
-
}
|
|
1233
|
-
if (expanded.include && expanded.select) {
|
|
1234
|
-
throw new ShapeError(
|
|
1235
|
-
`Forced tree for relation "${relName}" produces both "include" and "select". Prisma does not allow both at the same level.`
|
|
1236
|
-
);
|
|
1237
|
-
}
|
|
1238
|
-
container[relName] = Object.keys(expanded).length > 0 ? expanded : true;
|
|
1239
|
-
continue;
|
|
1240
|
-
}
|
|
1241
|
-
if (isPlainObject(relVal)) {
|
|
1242
|
-
const relObj = relVal;
|
|
1243
|
-
if (forced.where && hasWhereForced(forced.where)) {
|
|
1244
|
-
relObj.where = mergeWhereForced(
|
|
1245
|
-
relObj.where,
|
|
1246
|
-
forced.where
|
|
1247
|
-
);
|
|
1248
|
-
}
|
|
1249
|
-
if (forced.include) {
|
|
1250
|
-
if (!relObj.include) {
|
|
1251
|
-
relObj.include = buildForcedOnlyContainer(forced.include);
|
|
1252
|
-
}
|
|
1253
|
-
applyForcedTree(relObj, "include", forced.include);
|
|
1254
|
-
}
|
|
1255
|
-
if (forced.select) {
|
|
1256
|
-
if (!relObj.select) {
|
|
1257
|
-
relObj.select = buildForcedOnlyContainer(forced.select);
|
|
1258
|
-
}
|
|
1259
|
-
applyForcedTree(relObj, "select", forced.select);
|
|
1260
|
-
}
|
|
1261
|
-
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
1262
|
-
const placement = forced._countWherePlacement ?? "include";
|
|
1263
|
-
const projContainer = relObj[placement];
|
|
1264
|
-
if (projContainer) {
|
|
1265
|
-
applyForcedCountWhere(projContainer, forced._countWhere);
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
if (relObj.include && relObj.select) {
|
|
1269
|
-
throw new ShapeError(
|
|
1270
|
-
`Relation "${relName}" has both "include" and "select" after forced tree merge. Prisma does not allow both at the same level.`
|
|
1271
|
-
);
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1413
|
+
container[relName] = applyForcedToRelValue(relName, relVal, forced);
|
|
1274
1414
|
}
|
|
1275
1415
|
}
|
|
1276
1416
|
function buildForcedOnlyContainer(tree) {
|
|
1277
1417
|
const result = {};
|
|
1278
1418
|
for (const [relName, forced] of Object.entries(tree)) {
|
|
1279
|
-
const nested =
|
|
1280
|
-
if (forced.where && hasWhereForced(forced.where)) {
|
|
1281
|
-
nested.where = mergeWhereForced(void 0, forced.where);
|
|
1282
|
-
}
|
|
1283
|
-
if (forced.include) {
|
|
1284
|
-
nested.include = buildForcedOnlyContainer(forced.include);
|
|
1285
|
-
}
|
|
1286
|
-
if (forced.select) {
|
|
1287
|
-
nested.select = buildForcedOnlyContainer(forced.select);
|
|
1288
|
-
}
|
|
1289
|
-
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
1290
|
-
const placement = forced._countWherePlacement ?? "include";
|
|
1291
|
-
if (!nested[placement])
|
|
1292
|
-
nested[placement] = {};
|
|
1293
|
-
const placementObj = nested[placement];
|
|
1294
|
-
Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
|
|
1295
|
-
}
|
|
1419
|
+
const nested = collectSubtree(forced);
|
|
1296
1420
|
result[relName] = Object.keys(nested).length > 0 ? nested : true;
|
|
1297
1421
|
}
|
|
1298
1422
|
return result;
|
|
@@ -1308,11 +1432,15 @@ function applyForcedCountWhere(container, forcedCountWhere) {
|
|
|
1308
1432
|
const selectObj = selectVal;
|
|
1309
1433
|
for (const [relName, forced] of Object.entries(forcedCountWhere)) {
|
|
1310
1434
|
const relVal = selectObj[relName];
|
|
1311
|
-
if (relVal === void 0)
|
|
1435
|
+
if (relVal === void 0) {
|
|
1436
|
+
selectObj[relName] = { where: mergeWhereForced(void 0, forced) };
|
|
1312
1437
|
continue;
|
|
1438
|
+
}
|
|
1313
1439
|
if (relVal === true) {
|
|
1314
1440
|
selectObj[relName] = { where: mergeWhereForced(void 0, forced) };
|
|
1315
|
-
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1443
|
+
if (isPlainObject(relVal)) {
|
|
1316
1444
|
const relObj = relVal;
|
|
1317
1445
|
relObj.where = mergeWhereForced(
|
|
1318
1446
|
relObj.where,
|
|
@@ -1321,12 +1449,6 @@ function applyForcedCountWhere(container, forcedCountWhere) {
|
|
|
1321
1449
|
}
|
|
1322
1450
|
}
|
|
1323
1451
|
}
|
|
1324
|
-
function formatUniqueConstraint(constraint) {
|
|
1325
|
-
return constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`;
|
|
1326
|
-
}
|
|
1327
|
-
function formatUniqueConstraints(constraints) {
|
|
1328
|
-
return constraints.map(formatUniqueConstraint).join(" | ");
|
|
1329
|
-
}
|
|
1330
1452
|
function resolvedWhereCoversConstraint(where, constraint) {
|
|
1331
1453
|
if (constraint.fields.length === 1) {
|
|
1332
1454
|
return constraint.fields[0] in where;
|
|
@@ -1432,7 +1554,12 @@ function stripUniqueWhereForcedInput(where, forced) {
|
|
|
1432
1554
|
const currentValue = result[key];
|
|
1433
1555
|
if (isPlainObject(currentValue) && isPlainObject(forcedValue)) {
|
|
1434
1556
|
const nested = { ...currentValue };
|
|
1435
|
-
for (const nestedKey of Object.
|
|
1557
|
+
for (const [nestedKey, nestedForcedValue] of Object.entries(forcedValue)) {
|
|
1558
|
+
if (nestedKey in nested && !deepEqual(nested[nestedKey], nestedForcedValue)) {
|
|
1559
|
+
throw new ShapeError(
|
|
1560
|
+
`Client unique where value for "${key}.${nestedKey}" conflicts with forced value`
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1436
1563
|
delete nested[nestedKey];
|
|
1437
1564
|
}
|
|
1438
1565
|
if (Object.keys(nested).length === 0) {
|
|
@@ -1442,11 +1569,25 @@ function stripUniqueWhereForcedInput(where, forced) {
|
|
|
1442
1569
|
}
|
|
1443
1570
|
continue;
|
|
1444
1571
|
}
|
|
1572
|
+
if (!deepEqual(currentValue, forcedValue)) {
|
|
1573
|
+
throw new ShapeError(
|
|
1574
|
+
`Client unique where value for "${key}" conflicts with forced value`
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1445
1577
|
delete result[key];
|
|
1446
1578
|
}
|
|
1447
1579
|
return result;
|
|
1448
1580
|
}
|
|
1449
1581
|
|
|
1582
|
+
// src/runtime/direct-scalar-schema.ts
|
|
1583
|
+
function buildDirectScalarSchema(fieldMeta, enumMap, scalarBase) {
|
|
1584
|
+
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
1585
|
+
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
1586
|
+
return wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, base);
|
|
1587
|
+
}
|
|
1588
|
+
return base;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1450
1591
|
// src/runtime/query-builder-where.ts
|
|
1451
1592
|
var UNSUPPORTED_WHERE_TYPES = /* @__PURE__ */ new Set(["Bytes"]);
|
|
1452
1593
|
var STRING_MODE_OPS = /* @__PURE__ */ new Set([
|
|
@@ -1460,47 +1601,40 @@ var JSON_STRING_MODE_OPS = /* @__PURE__ */ new Set([
|
|
|
1460
1601
|
"string_starts_with",
|
|
1461
1602
|
"string_ends_with"
|
|
1462
1603
|
]);
|
|
1604
|
+
var NEGATIVE_RELATION_OPS = /* @__PURE__ */ new Set(["none", "isNot"]);
|
|
1463
1605
|
var MAX_WHERE_DEPTH = 10;
|
|
1464
|
-
function safeStringify(
|
|
1465
|
-
if (typeof
|
|
1466
|
-
return `${
|
|
1606
|
+
function safeStringify(value) {
|
|
1607
|
+
if (typeof value === "bigint")
|
|
1608
|
+
return `${value}n`;
|
|
1609
|
+
if (typeof value === "undefined")
|
|
1610
|
+
return "undefined";
|
|
1611
|
+
if (typeof value === "function")
|
|
1612
|
+
return "[function]";
|
|
1613
|
+
if (typeof value === "symbol")
|
|
1614
|
+
return value.toString();
|
|
1615
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1467
1616
|
try {
|
|
1468
|
-
|
|
1617
|
+
const json = JSON.stringify(value, (_key, current) => {
|
|
1618
|
+
if (typeof current === "bigint")
|
|
1619
|
+
return `${current}n`;
|
|
1620
|
+
if (typeof current === "undefined")
|
|
1621
|
+
return "[undefined]";
|
|
1622
|
+
if (typeof current === "function")
|
|
1623
|
+
return "[function]";
|
|
1624
|
+
if (typeof current === "symbol")
|
|
1625
|
+
return current.toString();
|
|
1626
|
+
if (current && typeof current === "object") {
|
|
1627
|
+
if (seen.has(current))
|
|
1628
|
+
return "[Circular]";
|
|
1629
|
+
seen.add(current);
|
|
1630
|
+
}
|
|
1631
|
+
return current;
|
|
1632
|
+
});
|
|
1633
|
+
return json === void 0 ? String(value) : json;
|
|
1469
1634
|
} catch {
|
|
1470
|
-
return String(
|
|
1635
|
+
return String(value);
|
|
1471
1636
|
}
|
|
1472
1637
|
}
|
|
1473
|
-
function forcedValuesEqual(a, b) {
|
|
1474
|
-
if (a === b)
|
|
1475
|
-
return true;
|
|
1476
|
-
if (a === null || b === null)
|
|
1477
|
-
return false;
|
|
1478
|
-
if (typeof a !== typeof b)
|
|
1479
|
-
return false;
|
|
1480
|
-
if (typeof a === "bigint")
|
|
1481
|
-
return a === b;
|
|
1482
|
-
if (a instanceof Date && b instanceof Date)
|
|
1483
|
-
return a.getTime() === b.getTime();
|
|
1484
|
-
if (a instanceof RegExp && b instanceof RegExp) {
|
|
1485
|
-
return a.source === b.source && a.flags === b.flags;
|
|
1486
|
-
}
|
|
1487
|
-
if (typeof a !== "object")
|
|
1488
|
-
return false;
|
|
1489
|
-
if (Array.isArray(a)) {
|
|
1490
|
-
if (!Array.isArray(b))
|
|
1491
|
-
return false;
|
|
1492
|
-
if (a.length !== b.length)
|
|
1493
|
-
return false;
|
|
1494
|
-
return a.every((v, i) => forcedValuesEqual(v, b[i]));
|
|
1495
|
-
}
|
|
1496
|
-
if (!isPlainObject(a) || !isPlainObject(b))
|
|
1497
|
-
return false;
|
|
1498
|
-
const aKeys = Object.keys(a);
|
|
1499
|
-
const bKeys = Object.keys(b);
|
|
1500
|
-
if (aKeys.length !== bKeys.length)
|
|
1501
|
-
return false;
|
|
1502
|
-
return aKeys.every((k) => k in b && forcedValuesEqual(a[k], b[k]));
|
|
1503
|
-
}
|
|
1504
1638
|
function mergeScalarConditions(target, source) {
|
|
1505
1639
|
for (const [field, ops] of Object.entries(source)) {
|
|
1506
1640
|
const existing = target[field];
|
|
@@ -1518,7 +1652,7 @@ function mergeScalarConditions(target, source) {
|
|
|
1518
1652
|
for (const [op, val] of Object.entries(ops)) {
|
|
1519
1653
|
if (op in existing) {
|
|
1520
1654
|
const existingVal = existing[op];
|
|
1521
|
-
if (!
|
|
1655
|
+
if (!deepEqual(existingVal, val)) {
|
|
1522
1656
|
throw new ShapeError(
|
|
1523
1657
|
`Conflicting forced where values for "${field}.${op}": shape defines both ${safeStringify(existingVal)} and ${safeStringify(val)}`
|
|
1524
1658
|
);
|
|
@@ -1528,7 +1662,7 @@ function mergeScalarConditions(target, source) {
|
|
|
1528
1662
|
Object.assign(existing, ops);
|
|
1529
1663
|
continue;
|
|
1530
1664
|
}
|
|
1531
|
-
if (!
|
|
1665
|
+
if (!deepEqual(existing, ops)) {
|
|
1532
1666
|
throw new ShapeError(`Conflicting forced where values for "${field}"`);
|
|
1533
1667
|
}
|
|
1534
1668
|
}
|
|
@@ -1619,27 +1753,27 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1619
1753
|
scalarConditions
|
|
1620
1754
|
);
|
|
1621
1755
|
}
|
|
1622
|
-
|
|
1756
|
+
const forcedOnlyKeys = /* @__PURE__ */ new Set();
|
|
1757
|
+
for (const key of Object.keys(whereConfig)) {
|
|
1623
1758
|
if (COMBINATOR_KEYS.has(key))
|
|
1624
1759
|
continue;
|
|
1625
1760
|
if (!(key in fieldSchemas)) {
|
|
1626
|
-
|
|
1761
|
+
forcedOnlyKeys.add(key);
|
|
1627
1762
|
}
|
|
1628
1763
|
}
|
|
1629
|
-
for (const key of Object.keys(
|
|
1764
|
+
for (const key of Object.keys(scalarConditions)) {
|
|
1765
|
+
if (COMBINATOR_KEYS.has(key))
|
|
1766
|
+
continue;
|
|
1630
1767
|
if (!(key in fieldSchemas)) {
|
|
1631
|
-
fieldSchemas[key] =
|
|
1768
|
+
fieldSchemas[key] = z5.object({}).strict().optional();
|
|
1632
1769
|
}
|
|
1633
1770
|
}
|
|
1634
|
-
const
|
|
1635
|
-
const forcedOnlyKeys = /* @__PURE__ */ new Set();
|
|
1636
|
-
for (const key of Object.keys(whereConfig)) {
|
|
1637
|
-
if (COMBINATOR_KEYS.has(key))
|
|
1638
|
-
continue;
|
|
1771
|
+
for (const key of Object.keys(relationForced)) {
|
|
1639
1772
|
if (!(key in fieldSchemas)) {
|
|
1640
|
-
|
|
1773
|
+
fieldSchemas[key] = z5.object({}).strict().optional();
|
|
1641
1774
|
}
|
|
1642
1775
|
}
|
|
1776
|
+
const schema = Object.keys(fieldSchemas).length > 0 ? z5.object(fieldSchemas).strict().optional() : null;
|
|
1643
1777
|
return {
|
|
1644
1778
|
schema,
|
|
1645
1779
|
forced: {
|
|
@@ -1670,12 +1804,12 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1670
1804
|
);
|
|
1671
1805
|
}
|
|
1672
1806
|
if (key === "NOT") {
|
|
1673
|
-
fieldSchemas[key] =
|
|
1807
|
+
fieldSchemas[key] = z5.union([
|
|
1674
1808
|
elementSchema,
|
|
1675
|
-
|
|
1809
|
+
z5.preprocess(coerceToArray, z5.array(elementSchema).min(1))
|
|
1676
1810
|
]).optional();
|
|
1677
1811
|
} else {
|
|
1678
|
-
fieldSchemas[key] =
|
|
1812
|
+
fieldSchemas[key] = z5.preprocess(coerceToArray, z5.array(elementSchema).min(1)).optional();
|
|
1679
1813
|
}
|
|
1680
1814
|
}
|
|
1681
1815
|
if (hasWhereForced(result.forced)) {
|
|
@@ -1744,6 +1878,11 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1744
1878
|
`Empty nested where for relation "${key}.${op}" on model "${model}". Define at least one field.`
|
|
1745
1879
|
);
|
|
1746
1880
|
}
|
|
1881
|
+
if (NEGATIVE_RELATION_OPS.has(op) && nested.schema && hasWhereForced(nested.forced)) {
|
|
1882
|
+
throw new ShapeError(
|
|
1883
|
+
`Relation filter "${key}.${op}" on model "${model}" mixes client-controlled and forced conditions. Under negative relation operators (none, isNot), merging weakens the filter. Either move forced conditions to a separate top-level "${op}" branch, or make all conditions under this operator client-controlled or all forced.`
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1747
1886
|
if (nested.schema) {
|
|
1748
1887
|
if (!hasWhereForced(nested.forced)) {
|
|
1749
1888
|
opSchemas[op] = nested.schema.refine(
|
|
@@ -1768,7 +1907,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1768
1907
|
}
|
|
1769
1908
|
if (hasClientOps) {
|
|
1770
1909
|
const clientOpKeys = Object.keys(opSchemas);
|
|
1771
|
-
const opObjSchema =
|
|
1910
|
+
const opObjSchema = z5.object(opSchemas).strict();
|
|
1772
1911
|
if (Object.keys(opForced).length === 0 && !hasForcedNull) {
|
|
1773
1912
|
fieldSchemas[key] = opObjSchema.refine(
|
|
1774
1913
|
(v) => clientOpKeys.some(
|
|
@@ -1854,10 +1993,10 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1854
1993
|
);
|
|
1855
1994
|
}
|
|
1856
1995
|
if (modeConfigValue === true) {
|
|
1857
|
-
opSchemas.mode =
|
|
1996
|
+
opSchemas.mode = z5.enum(["default", "insensitive"]).optional();
|
|
1858
1997
|
} else {
|
|
1859
1998
|
const actualModeValue = isForcedValue(modeConfigValue) ? modeConfigValue.value : modeConfigValue;
|
|
1860
|
-
const modeSchema =
|
|
1999
|
+
const modeSchema = z5.enum(["default", "insensitive"]);
|
|
1861
2000
|
let parsed;
|
|
1862
2001
|
try {
|
|
1863
2002
|
parsed = modeSchema.parse(actualModeValue);
|
|
@@ -1869,10 +2008,10 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1869
2008
|
fieldForced.mode = parsed;
|
|
1870
2009
|
}
|
|
1871
2010
|
} else if (hasModeCompatibleOp) {
|
|
1872
|
-
opSchemas.mode =
|
|
2011
|
+
opSchemas.mode = z5.enum(["default", "insensitive"]).optional();
|
|
1873
2012
|
}
|
|
1874
2013
|
if (hasClientOps) {
|
|
1875
|
-
const opObj =
|
|
2014
|
+
const opObj = z5.object(opSchemas).strict();
|
|
1876
2015
|
const refined = opObj.refine(
|
|
1877
2016
|
(v) => clientOpKeys.some(
|
|
1878
2017
|
(k) => v[k] !== void 0
|
|
@@ -1888,7 +2027,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1888
2027
|
enumMap,
|
|
1889
2028
|
scalarBase
|
|
1890
2029
|
);
|
|
1891
|
-
fieldSchemas[fieldName] =
|
|
2030
|
+
fieldSchemas[fieldName] = z5.union([refined, equalsBase]).optional();
|
|
1892
2031
|
} else {
|
|
1893
2032
|
fieldSchemas[fieldName] = refined.optional();
|
|
1894
2033
|
}
|
|
@@ -1902,15 +2041,8 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1902
2041
|
(constraint) => constraint.selector === selector
|
|
1903
2042
|
) ?? null;
|
|
1904
2043
|
}
|
|
1905
|
-
function buildDirectUniqueSchema(fieldMeta) {
|
|
1906
|
-
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
1907
|
-
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
1908
|
-
return wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, base);
|
|
1909
|
-
}
|
|
1910
|
-
return base;
|
|
1911
|
-
}
|
|
1912
2044
|
function parseForcedUniqueValue(model, fieldName, fieldMeta, value) {
|
|
1913
|
-
const schema =
|
|
2045
|
+
const schema = buildDirectScalarSchema(fieldMeta, enumMap, scalarBase);
|
|
1914
2046
|
const actual = isForcedValue(value) ? value.value : value;
|
|
1915
2047
|
try {
|
|
1916
2048
|
return schema.parse(actual);
|
|
@@ -1988,7 +2120,11 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1988
2120
|
`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.`
|
|
1989
2121
|
);
|
|
1990
2122
|
}
|
|
1991
|
-
const directSchema2 =
|
|
2123
|
+
const directSchema2 = buildDirectScalarSchema(
|
|
2124
|
+
fieldMeta2,
|
|
2125
|
+
enumMap,
|
|
2126
|
+
scalarBase
|
|
2127
|
+
);
|
|
1992
2128
|
if (fieldValue === true) {
|
|
1993
2129
|
nestedSchemas[fieldName] = directSchema2;
|
|
1994
2130
|
} else {
|
|
@@ -2001,7 +2137,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2001
2137
|
}
|
|
2002
2138
|
}
|
|
2003
2139
|
if (Object.keys(nestedSchemas).length > 0) {
|
|
2004
|
-
fieldSchemas[key] =
|
|
2140
|
+
fieldSchemas[key] = z5.object(nestedSchemas).strict();
|
|
2005
2141
|
}
|
|
2006
2142
|
if (Object.keys(forcedCompound).length > 0) {
|
|
2007
2143
|
forcedConditions[key] = forcedCompound;
|
|
@@ -2035,7 +2171,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2035
2171
|
`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 }.`
|
|
2036
2172
|
);
|
|
2037
2173
|
}
|
|
2038
|
-
const directSchema =
|
|
2174
|
+
const directSchema = buildDirectScalarSchema(fieldMeta, enumMap, scalarBase);
|
|
2039
2175
|
if (value === true) {
|
|
2040
2176
|
fieldSchemas[key] = directSchema;
|
|
2041
2177
|
continue;
|
|
@@ -2049,7 +2185,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2049
2185
|
forcedOnlyKeys.add(key);
|
|
2050
2186
|
}
|
|
2051
2187
|
return {
|
|
2052
|
-
schema: Object.keys(fieldSchemas).length > 0 ?
|
|
2188
|
+
schema: Object.keys(fieldSchemas).length > 0 ? z5.object(fieldSchemas).strict() : null,
|
|
2053
2189
|
forced: {
|
|
2054
2190
|
conditions: forcedConditions,
|
|
2055
2191
|
relations: {}
|
|
@@ -2061,31 +2197,13 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2061
2197
|
}
|
|
2062
2198
|
|
|
2063
2199
|
// src/runtime/query-builder-args.ts
|
|
2064
|
-
import { z as
|
|
2200
|
+
import { z as z6 } from "zod";
|
|
2065
2201
|
var UNSUPPORTED_BY_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
|
|
2066
|
-
function requireConfigTrue(config, context) {
|
|
2067
|
-
for (const [key, value] of Object.entries(config)) {
|
|
2068
|
-
if (value !== true) {
|
|
2069
|
-
throw new ShapeError(
|
|
2070
|
-
`Config value for "${key}" in ${context} must be true, got ${typeof value}`
|
|
2071
|
-
);
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
2074
|
-
}
|
|
2075
|
-
function isPlainRecord(value) {
|
|
2076
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2077
|
-
}
|
|
2078
|
-
function formatUniqueConstraint2(constraint) {
|
|
2079
|
-
return constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`;
|
|
2080
|
-
}
|
|
2081
|
-
function formatUniqueConstraints2(constraints) {
|
|
2082
|
-
return constraints.map(formatUniqueConstraint2).join(" | ");
|
|
2083
|
-
}
|
|
2084
2202
|
function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
2085
|
-
const sortEnum =
|
|
2086
|
-
const nullsEnum =
|
|
2087
|
-
const sortWithNulls =
|
|
2088
|
-
const scalarOrderSchema =
|
|
2203
|
+
const sortEnum = z6.enum(["asc", "desc"]);
|
|
2204
|
+
const nullsEnum = z6.enum(["first", "last"]);
|
|
2205
|
+
const sortWithNulls = z6.object({ sort: sortEnum, nulls: nullsEnum.optional() }).strict();
|
|
2206
|
+
const scalarOrderSchema = z6.union([sortEnum, sortWithNulls]);
|
|
2089
2207
|
function validateScalarOrderByField(fieldName, model, modelFields) {
|
|
2090
2208
|
const fieldMeta = modelFields[fieldName];
|
|
2091
2209
|
if (!fieldMeta)
|
|
@@ -2104,6 +2222,16 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2104
2222
|
);
|
|
2105
2223
|
}
|
|
2106
2224
|
function buildOrderBySchema(model, orderByConfig) {
|
|
2225
|
+
if (!isPlainObject(orderByConfig)) {
|
|
2226
|
+
throw new ShapeError(
|
|
2227
|
+
`orderBy shape config on model "${model}" must be an object of fields`
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
2230
|
+
if (Object.keys(orderByConfig).length === 0) {
|
|
2231
|
+
throw new ShapeError(
|
|
2232
|
+
`Empty orderBy config on model "${model}". Define at least one field.`
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2107
2235
|
const modelFields = typeMap[model];
|
|
2108
2236
|
if (!modelFields)
|
|
2109
2237
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -2119,53 +2247,35 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2119
2247
|
fieldSchemas[fieldName] = scalarOrderSchema.optional();
|
|
2120
2248
|
continue;
|
|
2121
2249
|
}
|
|
2122
|
-
if (!
|
|
2250
|
+
if (!isPlainObject(config)) {
|
|
2123
2251
|
throw new ShapeError(
|
|
2124
|
-
`orderBy config for "${fieldName}" on model "${model}" must be true or a relation
|
|
2252
|
+
`orderBy config for "${fieldName}" on model "${model}" must be true or a relation config object`
|
|
2125
2253
|
);
|
|
2126
2254
|
}
|
|
2127
2255
|
if (!fieldMeta.isRelation) {
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
fieldMeta.isList
|
|
2256
|
+
throw new ShapeError(
|
|
2257
|
+
`orderBy config for scalar field "${model}.${fieldName}" must be true. Operator objects are not valid in orderBy.`
|
|
2131
2258
|
);
|
|
2132
|
-
const opSchemas = {};
|
|
2133
|
-
for (const [op, enabled] of Object.entries(config)) {
|
|
2134
|
-
if (enabled !== true) {
|
|
2135
|
-
throw new ShapeError(
|
|
2136
|
-
`orderBy operator config for "${model}.${fieldName}.${op}" must be true`
|
|
2137
|
-
);
|
|
2138
|
-
}
|
|
2139
|
-
if (!allowedOps.includes(op)) {
|
|
2140
|
-
throw new ShapeError(
|
|
2141
|
-
`Operator "${op}" not supported for orderBy field "${model}.${fieldName}"`
|
|
2142
|
-
);
|
|
2143
|
-
}
|
|
2144
|
-
opSchemas[op] = scalarOrderSchema.optional();
|
|
2145
|
-
}
|
|
2146
|
-
const opKeys = Object.keys(opSchemas);
|
|
2147
|
-
fieldSchemas[fieldName] = z5.object(opSchemas).strict().refine(
|
|
2148
|
-
(v) => opKeys.some(
|
|
2149
|
-
(k) => v[k] !== void 0
|
|
2150
|
-
),
|
|
2151
|
-
{
|
|
2152
|
-
message: `orderBy field "${fieldName}" must specify at least one operator`
|
|
2153
|
-
}
|
|
2154
|
-
).optional();
|
|
2155
|
-
continue;
|
|
2156
2259
|
}
|
|
2157
2260
|
if (fieldMeta.isList) {
|
|
2158
|
-
|
|
2261
|
+
const configKeys = Object.keys(config);
|
|
2262
|
+
if (!configKeys.includes("_count")) {
|
|
2159
2263
|
throw new ShapeError(
|
|
2160
2264
|
`To-many relation orderBy "${fieldName}" only supports _count`
|
|
2161
2265
|
);
|
|
2162
2266
|
}
|
|
2267
|
+
const extraKeys = configKeys.filter((k) => k !== "_count");
|
|
2268
|
+
if (extraKeys.length > 0) {
|
|
2269
|
+
throw new ShapeError(
|
|
2270
|
+
`To-many relation orderBy "${fieldName}" only supports _count. Unexpected keys: ${extraKeys.join(", ")}`
|
|
2271
|
+
);
|
|
2272
|
+
}
|
|
2163
2273
|
if (config._count !== true) {
|
|
2164
2274
|
throw new ShapeError(
|
|
2165
2275
|
`orderBy relation aggregate "${fieldName}._count" must be true`
|
|
2166
2276
|
);
|
|
2167
2277
|
}
|
|
2168
|
-
fieldSchemas[fieldName] =
|
|
2278
|
+
fieldSchemas[fieldName] = z6.object({
|
|
2169
2279
|
_count: sortEnum.optional()
|
|
2170
2280
|
}).strict().optional();
|
|
2171
2281
|
continue;
|
|
@@ -2176,17 +2286,11 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2176
2286
|
);
|
|
2177
2287
|
fieldSchemas[fieldName] = nested;
|
|
2178
2288
|
}
|
|
2179
|
-
const
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
(k) => v[k] !== void 0
|
|
2183
|
-
),
|
|
2184
|
-
{ message: "orderBy must specify at least one field" }
|
|
2289
|
+
const singleSchema = strictObjectRequiringOne(
|
|
2290
|
+
fieldSchemas,
|
|
2291
|
+
"orderBy must specify at least one field"
|
|
2185
2292
|
);
|
|
2186
|
-
return
|
|
2187
|
-
singleSchema,
|
|
2188
|
-
z5.preprocess(coerceToArray, z5.array(singleSchema).min(1))
|
|
2189
|
-
]).optional();
|
|
2293
|
+
return singleOrArraySchema(singleSchema).optional();
|
|
2190
2294
|
}
|
|
2191
2295
|
function buildTakeSchema(config) {
|
|
2192
2296
|
if (typeof config === "number") {
|
|
@@ -2196,7 +2300,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2196
2300
|
if (config <= 0) {
|
|
2197
2301
|
throw new ShapeError("take must be a positive integer");
|
|
2198
2302
|
}
|
|
2199
|
-
return
|
|
2303
|
+
return z6.number().int().min(1).max(config).default(config);
|
|
2200
2304
|
}
|
|
2201
2305
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
2202
2306
|
throw new ShapeError("take config must be a number or { max, default? }");
|
|
@@ -2221,9 +2325,9 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2221
2325
|
if (config.default > config.max) {
|
|
2222
2326
|
throw new ShapeError("take.default cannot exceed take.max");
|
|
2223
2327
|
}
|
|
2224
|
-
return
|
|
2328
|
+
return z6.number().int().min(1).max(config.max).default(config.default);
|
|
2225
2329
|
}
|
|
2226
|
-
return
|
|
2330
|
+
return z6.number().int().min(1).max(config.max).optional();
|
|
2227
2331
|
}
|
|
2228
2332
|
function buildCursorFieldSchema(model, fieldName) {
|
|
2229
2333
|
const modelFields = typeMap[model];
|
|
@@ -2245,11 +2349,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2245
2349
|
`List field "${fieldName}" cannot be used in cursor`
|
|
2246
2350
|
);
|
|
2247
2351
|
}
|
|
2248
|
-
|
|
2249
|
-
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
2250
|
-
return wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, base);
|
|
2251
|
-
}
|
|
2252
|
-
return base;
|
|
2352
|
+
return buildDirectScalarSchema(fieldMeta, enumMap, scalarBase);
|
|
2253
2353
|
}
|
|
2254
2354
|
function cursorConfigMatchesConstraint(cursorConfig, constraint) {
|
|
2255
2355
|
if (!(constraint.selector in cursorConfig))
|
|
@@ -2258,66 +2358,55 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2258
2358
|
if (constraint.fields.length === 1) {
|
|
2259
2359
|
return value === true;
|
|
2260
2360
|
}
|
|
2261
|
-
if (!
|
|
2361
|
+
if (!isPlainObject(value))
|
|
2262
2362
|
return false;
|
|
2263
2363
|
const keys = Object.keys(value);
|
|
2264
2364
|
if (keys.length !== constraint.fields.length)
|
|
2265
2365
|
return false;
|
|
2266
2366
|
return constraint.fields.every((field) => value[field] === true);
|
|
2267
2367
|
}
|
|
2268
|
-
function getUniqueConstraints(model) {
|
|
2269
|
-
const constraints = uniqueMap[model];
|
|
2270
|
-
if (constraints && constraints.length > 0) {
|
|
2271
|
-
return constraints;
|
|
2272
|
-
}
|
|
2273
|
-
const modelFields = typeMap[model];
|
|
2274
|
-
if (!modelFields) {
|
|
2275
|
-
throw new ShapeError(`Unknown model: ${model}`);
|
|
2276
|
-
}
|
|
2277
|
-
const inferred = [];
|
|
2278
|
-
for (const [fieldName, fieldMeta] of Object.entries(modelFields)) {
|
|
2279
|
-
if (fieldMeta.isRelation)
|
|
2280
|
-
continue;
|
|
2281
|
-
if (fieldMeta.isId || fieldMeta.isUnique) {
|
|
2282
|
-
inferred.push({
|
|
2283
|
-
selector: fieldName,
|
|
2284
|
-
fields: [fieldName]
|
|
2285
|
-
});
|
|
2286
|
-
}
|
|
2287
|
-
}
|
|
2288
|
-
return inferred;
|
|
2289
|
-
}
|
|
2290
2368
|
function buildCursorSchema(model, cursorConfig) {
|
|
2291
|
-
const constraints =
|
|
2369
|
+
const constraints = uniqueMap[model] ?? [];
|
|
2292
2370
|
if (constraints.length === 0) {
|
|
2293
2371
|
throw new ShapeError(
|
|
2294
2372
|
`cursor on model "${model}" requires at least one unique constraint`
|
|
2295
2373
|
);
|
|
2296
2374
|
}
|
|
2297
|
-
const matching = constraints.
|
|
2375
|
+
const matching = constraints.filter(
|
|
2298
2376
|
(constraint) => cursorConfigMatchesConstraint(cursorConfig, constraint)
|
|
2299
2377
|
);
|
|
2300
|
-
if (
|
|
2378
|
+
if (matching.length === 0) {
|
|
2301
2379
|
throw new ShapeError(
|
|
2302
|
-
`cursor on model "${model}" must
|
|
2380
|
+
`cursor on model "${model}" must match a unique selector: ${formatUniqueConstraints(constraints)}`
|
|
2303
2381
|
);
|
|
2304
2382
|
}
|
|
2383
|
+
const coveredKeys = new Set(matching.map((c) => c.selector));
|
|
2384
|
+
for (const key of Object.keys(cursorConfig)) {
|
|
2385
|
+
if (!coveredKeys.has(key)) {
|
|
2386
|
+
throw new ShapeError(
|
|
2387
|
+
`cursor field "${key}" on model "${model}" does not match any unique selector. Unique selectors: ${formatUniqueConstraints(constraints)}`
|
|
2388
|
+
);
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2305
2391
|
const fieldSchemas = {};
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2392
|
+
for (const constraint of matching) {
|
|
2393
|
+
if (constraint.fields.length === 1) {
|
|
2394
|
+
fieldSchemas[constraint.selector] = buildCursorFieldSchema(
|
|
2395
|
+
model,
|
|
2396
|
+
constraint.fields[0]
|
|
2397
|
+
).optional();
|
|
2398
|
+
} else {
|
|
2399
|
+
const nestedSchemas = {};
|
|
2400
|
+
for (const field of constraint.fields) {
|
|
2401
|
+
nestedSchemas[field] = buildCursorFieldSchema(model, field);
|
|
2402
|
+
}
|
|
2403
|
+
fieldSchemas[constraint.selector] = z6.object(nestedSchemas).strict().optional();
|
|
2315
2404
|
}
|
|
2316
|
-
fieldSchemas[matching.selector] = z5.object(nestedSchemas).strict().optional();
|
|
2317
2405
|
}
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2406
|
+
const selectorKeys = matching.map((c) => c.selector);
|
|
2407
|
+
return strictObjectRequiringOne(
|
|
2408
|
+
fieldSchemas,
|
|
2409
|
+
`cursor must specify one of: ${selectorKeys.join(", ")}`
|
|
2321
2410
|
).optional();
|
|
2322
2411
|
}
|
|
2323
2412
|
function buildDistinctSchema(model, distinctConfig) {
|
|
@@ -2342,10 +2431,8 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2342
2431
|
);
|
|
2343
2432
|
allowedFields.add(fieldName);
|
|
2344
2433
|
}
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
z5.array(z5.enum([...allowedFields])).min(1)
|
|
2348
|
-
]).optional();
|
|
2434
|
+
const fieldEnum = z6.enum([...allowedFields]);
|
|
2435
|
+
return z6.union([fieldEnum, z6.array(fieldEnum).min(1)]).optional();
|
|
2349
2436
|
}
|
|
2350
2437
|
function buildBySchema(model, byConfig) {
|
|
2351
2438
|
const modelFields = typeMap[model];
|
|
@@ -2378,7 +2465,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2378
2465
|
}
|
|
2379
2466
|
allowedFields.add(fieldName);
|
|
2380
2467
|
}
|
|
2381
|
-
return
|
|
2468
|
+
return z6.array(z6.enum([...allowedFields])).min(1);
|
|
2382
2469
|
}
|
|
2383
2470
|
function buildHavingSchema(model, havingConfig) {
|
|
2384
2471
|
const modelFields = typeMap[model];
|
|
@@ -2419,22 +2506,21 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2419
2506
|
).optional();
|
|
2420
2507
|
}
|
|
2421
2508
|
if (fieldMeta.type === "String") {
|
|
2422
|
-
opSchemas.mode =
|
|
2509
|
+
opSchemas.mode = z6.enum(["default", "insensitive"]).optional();
|
|
2423
2510
|
}
|
|
2424
2511
|
const opKeys = Object.keys(opSchemas).filter((key) => key !== "mode");
|
|
2425
|
-
|
|
2512
|
+
const opShape = { ...opSchemas };
|
|
2513
|
+
const opObjSchema = z6.object(opShape).strict().refine(
|
|
2426
2514
|
(v) => opKeys.some((k) => v[k] !== void 0),
|
|
2427
2515
|
{
|
|
2428
2516
|
message: `having field "${fieldName}" must specify at least one operator`
|
|
2429
2517
|
}
|
|
2430
|
-
)
|
|
2518
|
+
);
|
|
2519
|
+
fieldSchemas[fieldName] = opObjSchema.optional();
|
|
2431
2520
|
}
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
(k) => v[k] !== void 0
|
|
2436
|
-
),
|
|
2437
|
-
{ message: "having must specify at least one field" }
|
|
2521
|
+
return strictObjectRequiringOne(
|
|
2522
|
+
fieldSchemas,
|
|
2523
|
+
"having must specify at least one field"
|
|
2438
2524
|
).optional();
|
|
2439
2525
|
}
|
|
2440
2526
|
function buildAggregateFieldSchema(model, op, config) {
|
|
@@ -2443,47 +2529,45 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2443
2529
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2444
2530
|
requireConfigTrue(config, `${op} on model "${model}"`);
|
|
2445
2531
|
const allowedTypes = op === "_avg" || op === "_sum" ? NUMERIC_TYPES : COMPARABLE_TYPES;
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2532
|
+
return buildLiteralTrueSchema(
|
|
2533
|
+
Object.keys(config),
|
|
2534
|
+
`${op} must specify at least one field`,
|
|
2535
|
+
(fieldName) => {
|
|
2536
|
+
const fieldMeta = modelFields[fieldName];
|
|
2537
|
+
if (!fieldMeta)
|
|
2538
|
+
throw new ShapeError(
|
|
2539
|
+
`Unknown field "${fieldName}" on model "${model}" in ${op}`
|
|
2540
|
+
);
|
|
2541
|
+
if (fieldMeta.isRelation)
|
|
2542
|
+
throw new ShapeError(
|
|
2543
|
+
`Relation field "${fieldName}" cannot be used in ${op}`
|
|
2544
|
+
);
|
|
2545
|
+
if (fieldMeta.isList)
|
|
2546
|
+
throw new ShapeError(
|
|
2547
|
+
`List field "${fieldName}" cannot be used in ${op}`
|
|
2548
|
+
);
|
|
2549
|
+
if (!allowedTypes.has(fieldMeta.type)) {
|
|
2550
|
+
throw new ShapeError(
|
|
2551
|
+
`Field "${fieldName}" of type "${fieldMeta.type}" cannot be used in ${op}`
|
|
2552
|
+
);
|
|
2553
|
+
}
|
|
2465
2554
|
}
|
|
2466
|
-
|
|
2467
|
-
}
|
|
2468
|
-
const aggregateFieldKeys = Object.keys(fieldSchemas);
|
|
2469
|
-
return z5.object(fieldSchemas).strict().refine(
|
|
2470
|
-
(v) => aggregateFieldKeys.some(
|
|
2471
|
-
(k) => v[k] !== void 0
|
|
2472
|
-
),
|
|
2473
|
-
{ message: `${op} must specify at least one field` }
|
|
2474
|
-
).optional();
|
|
2555
|
+
);
|
|
2475
2556
|
}
|
|
2476
2557
|
function buildCountFieldSchema(model, config, context) {
|
|
2477
2558
|
if (config === true) {
|
|
2478
|
-
return
|
|
2559
|
+
return z6.literal(true).optional();
|
|
2479
2560
|
}
|
|
2480
2561
|
const modelFields = typeMap[model];
|
|
2481
2562
|
if (!modelFields)
|
|
2482
2563
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2483
2564
|
requireConfigTrue(config, `${context} on model "${model}"`);
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2565
|
+
return buildLiteralTrueSchema(
|
|
2566
|
+
Object.keys(config),
|
|
2567
|
+
`${context} must specify at least one field`,
|
|
2568
|
+
(fieldName) => {
|
|
2569
|
+
if (fieldName === "_all")
|
|
2570
|
+
return;
|
|
2487
2571
|
const fieldMeta = modelFields[fieldName];
|
|
2488
2572
|
if (!fieldMeta)
|
|
2489
2573
|
throw new ShapeError(
|
|
@@ -2494,45 +2578,30 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2494
2578
|
`Relation field "${fieldName}" cannot be used in ${context}`
|
|
2495
2579
|
);
|
|
2496
2580
|
}
|
|
2497
|
-
|
|
2498
|
-
}
|
|
2499
|
-
const countFieldKeys = Object.keys(fieldSchemas);
|
|
2500
|
-
return z5.object(fieldSchemas).strict().refine(
|
|
2501
|
-
(v) => countFieldKeys.some(
|
|
2502
|
-
(k) => v[k] !== void 0
|
|
2503
|
-
),
|
|
2504
|
-
{ message: `${context} must specify at least one field` }
|
|
2505
|
-
).optional();
|
|
2581
|
+
);
|
|
2506
2582
|
}
|
|
2507
2583
|
function buildCountSelectSchema(model, selectConfig) {
|
|
2508
2584
|
const modelFields = typeMap[model];
|
|
2509
2585
|
if (!modelFields)
|
|
2510
2586
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2511
2587
|
requireConfigTrue(selectConfig, `count select on model "${model}"`);
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2588
|
+
return buildLiteralTrueSchema(
|
|
2589
|
+
Object.keys(selectConfig),
|
|
2590
|
+
"count select must specify at least one field",
|
|
2591
|
+
(fieldName) => {
|
|
2592
|
+
if (fieldName === "_all")
|
|
2593
|
+
return;
|
|
2594
|
+
const fieldMeta = modelFields[fieldName];
|
|
2595
|
+
if (!fieldMeta)
|
|
2596
|
+
throw new ShapeError(
|
|
2597
|
+
`Unknown field "${fieldName}" on model "${model}" in count select`
|
|
2598
|
+
);
|
|
2599
|
+
if (fieldMeta.isRelation)
|
|
2600
|
+
throw new ShapeError(
|
|
2601
|
+
`Relation field "${fieldName}" cannot be used in count select`
|
|
2602
|
+
);
|
|
2517
2603
|
}
|
|
2518
|
-
|
|
2519
|
-
if (!fieldMeta)
|
|
2520
|
-
throw new ShapeError(
|
|
2521
|
-
`Unknown field "${fieldName}" on model "${model}" in count select`
|
|
2522
|
-
);
|
|
2523
|
-
if (fieldMeta.isRelation)
|
|
2524
|
-
throw new ShapeError(
|
|
2525
|
-
`Relation field "${fieldName}" cannot be used in count select`
|
|
2526
|
-
);
|
|
2527
|
-
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
2528
|
-
}
|
|
2529
|
-
const countSelectKeys = Object.keys(fieldSchemas);
|
|
2530
|
-
return z5.object(fieldSchemas).strict().refine(
|
|
2531
|
-
(v) => countSelectKeys.some(
|
|
2532
|
-
(k) => v[k] !== void 0
|
|
2533
|
-
),
|
|
2534
|
-
{ message: "count select must specify at least one field" }
|
|
2535
|
-
).optional();
|
|
2604
|
+
);
|
|
2536
2605
|
}
|
|
2537
2606
|
return {
|
|
2538
2607
|
buildOrderBySchema,
|
|
@@ -2548,25 +2617,64 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2548
2617
|
}
|
|
2549
2618
|
|
|
2550
2619
|
// src/runtime/query-builder-projection.ts
|
|
2551
|
-
import { z as
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2620
|
+
import { z as z7 } from "zod";
|
|
2621
|
+
|
|
2622
|
+
// src/shared/projection-defaults.ts
|
|
2623
|
+
function buildDefaultProjectionInput(config) {
|
|
2624
|
+
const result = {};
|
|
2625
|
+
for (const [key, value] of Object.entries(config)) {
|
|
2626
|
+
if (key === "_count") {
|
|
2627
|
+
result[key] = buildDefaultCountInput(
|
|
2628
|
+
value
|
|
2629
|
+
);
|
|
2630
|
+
continue;
|
|
2631
|
+
}
|
|
2632
|
+
if (value === true) {
|
|
2633
|
+
result[key] = true;
|
|
2634
|
+
} else {
|
|
2635
|
+
result[key] = buildRelationArgsSkeleton(value);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
return result;
|
|
2639
|
+
}
|
|
2640
|
+
function buildRelationArgsSkeleton(config) {
|
|
2641
|
+
const skeleton = {};
|
|
2642
|
+
if (config.select) {
|
|
2643
|
+
skeleton.select = buildDefaultProjectionInput(config.select);
|
|
2644
|
+
}
|
|
2645
|
+
if (config.include) {
|
|
2646
|
+
skeleton.include = buildDefaultProjectionInput(config.include);
|
|
2647
|
+
}
|
|
2648
|
+
return skeleton;
|
|
2649
|
+
}
|
|
2650
|
+
function buildDefaultCountInput(config) {
|
|
2651
|
+
if (config === true)
|
|
2652
|
+
return true;
|
|
2653
|
+
if (!isPlainObject(config) || !config.select || !isPlainObject(config.select)) {
|
|
2654
|
+
return true;
|
|
2655
|
+
}
|
|
2656
|
+
const selectObj = config.select;
|
|
2657
|
+
const result = {};
|
|
2658
|
+
for (const key of Object.keys(selectObj)) {
|
|
2659
|
+
result[key] = true;
|
|
2660
|
+
}
|
|
2661
|
+
return { select: result };
|
|
2662
|
+
}
|
|
2663
|
+
function buildDefaultProjectionBody(shape) {
|
|
2664
|
+
if (shape.select) {
|
|
2665
|
+
return { select: buildDefaultProjectionInput(shape.select) };
|
|
2666
|
+
}
|
|
2667
|
+
if (shape.include) {
|
|
2668
|
+
return { include: buildDefaultProjectionInput(shape.include) };
|
|
2669
|
+
}
|
|
2670
|
+
return {};
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
// src/runtime/query-builder-projection.ts
|
|
2674
|
+
var KNOWN_NESTED_KEYS = {
|
|
2675
|
+
include: /* @__PURE__ */ new Set(["where", "include", "select", "orderBy", "cursor", "take", "skip"]),
|
|
2676
|
+
select: /* @__PURE__ */ new Set(["select", "include", "where", "orderBy", "cursor", "take", "skip"])
|
|
2677
|
+
};
|
|
2570
2678
|
var KNOWN_COUNT_SELECT_ENTRY_KEYS = /* @__PURE__ */ new Set(["where"]);
|
|
2571
2679
|
var MAX_PROJECTION_DEPTH = 10;
|
|
2572
2680
|
function validateNestedKeys(keys, allowed, context) {
|
|
@@ -2578,13 +2686,25 @@ function validateNestedKeys(keys, allowed, context) {
|
|
|
2578
2686
|
}
|
|
2579
2687
|
}
|
|
2580
2688
|
}
|
|
2581
|
-
function
|
|
2689
|
+
function hasDefinedKeys(v) {
|
|
2690
|
+
return Object.values(v).some((value) => value !== void 0);
|
|
2691
|
+
}
|
|
2692
|
+
function wrapRelationSchema(nestedObj, skeleton) {
|
|
2693
|
+
const collapsed = nestedObj.transform(
|
|
2694
|
+
(v) => isPlainObject(v) && !hasDefinedKeys(v) ? true : v
|
|
2695
|
+
);
|
|
2696
|
+
return z7.preprocess(
|
|
2697
|
+
(v) => v === true ? deepClone(skeleton) : v,
|
|
2698
|
+
collapsed
|
|
2699
|
+
);
|
|
2700
|
+
}
|
|
2701
|
+
function createProjectionBuilder(typeMap, _enumMap, deps) {
|
|
2582
2702
|
function buildIncludeCountSchema(model, config) {
|
|
2583
2703
|
const modelFields = typeMap[model];
|
|
2584
2704
|
if (!modelFields)
|
|
2585
2705
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2586
2706
|
if (config === true) {
|
|
2587
|
-
return { schema:
|
|
2707
|
+
return { schema: z7.literal(true).optional(), forcedCountWhere: {} };
|
|
2588
2708
|
}
|
|
2589
2709
|
if (!isPlainObject(config) || !("select" in config)) {
|
|
2590
2710
|
throw new ShapeError(
|
|
@@ -2620,273 +2740,175 @@ function createProjectionBuilder(typeMap, enumMap, deps) {
|
|
|
2620
2740
|
if (!fieldMeta.isList)
|
|
2621
2741
|
throw new ShapeError(`Field "${fieldName}" is a to-one relation on model "${model}" in _count.select. Only to-many relations support _count.`);
|
|
2622
2742
|
if (fieldConfig === true) {
|
|
2623
|
-
countSelectFields[fieldName] =
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
`Empty config for _count.select.${fieldName} on model "${model}". Use true or { where: { ... } }.`
|
|
2628
|
-
);
|
|
2629
|
-
}
|
|
2630
|
-
validateNestedKeys(
|
|
2631
|
-
Object.keys(fieldConfig),
|
|
2632
|
-
KNOWN_COUNT_SELECT_ENTRY_KEYS,
|
|
2633
|
-
`_count.select.${fieldName} on model "${model}"`
|
|
2634
|
-
);
|
|
2635
|
-
if (fieldConfig.where) {
|
|
2636
|
-
const relatedType = fieldMeta.type;
|
|
2637
|
-
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
2638
|
-
relatedType,
|
|
2639
|
-
fieldConfig.where
|
|
2640
|
-
);
|
|
2641
|
-
const nestedSchemas = {};
|
|
2642
|
-
if (whereSchema)
|
|
2643
|
-
nestedSchemas["where"] = whereSchema;
|
|
2644
|
-
const nestedObj = z6.object(nestedSchemas).strict();
|
|
2645
|
-
countSelectFields[fieldName] = z6.union([z6.literal(true), nestedObj]).optional();
|
|
2646
|
-
if (hasWhereForced(forced)) {
|
|
2647
|
-
forcedCountWhere[fieldName] = forced;
|
|
2648
|
-
}
|
|
2649
|
-
} else {
|
|
2650
|
-
countSelectFields[fieldName] = z6.literal(true).optional();
|
|
2651
|
-
}
|
|
2652
|
-
} else {
|
|
2743
|
+
countSelectFields[fieldName] = z7.literal(true).optional();
|
|
2744
|
+
continue;
|
|
2745
|
+
}
|
|
2746
|
+
if (!isPlainObject(fieldConfig)) {
|
|
2653
2747
|
throw new ShapeError(
|
|
2654
2748
|
`Invalid config for _count.select.${fieldName} on model "${model}". Expected true or { where: { ... } }`
|
|
2655
2749
|
);
|
|
2656
2750
|
}
|
|
2751
|
+
if (Object.keys(fieldConfig).length === 0) {
|
|
2752
|
+
throw new ShapeError(
|
|
2753
|
+
`Empty config for _count.select.${fieldName} on model "${model}". Use true or { where: { ... } }.`
|
|
2754
|
+
);
|
|
2755
|
+
}
|
|
2756
|
+
validateNestedKeys(
|
|
2757
|
+
Object.keys(fieldConfig),
|
|
2758
|
+
KNOWN_COUNT_SELECT_ENTRY_KEYS,
|
|
2759
|
+
`_count.select.${fieldName} on model "${model}"`
|
|
2760
|
+
);
|
|
2761
|
+
if (fieldConfig.where) {
|
|
2762
|
+
const relatedType = fieldMeta.type;
|
|
2763
|
+
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
2764
|
+
relatedType,
|
|
2765
|
+
fieldConfig.where
|
|
2766
|
+
);
|
|
2767
|
+
const nestedSchemas = {};
|
|
2768
|
+
if (whereSchema)
|
|
2769
|
+
nestedSchemas["where"] = whereSchema;
|
|
2770
|
+
const nestedObj = z7.object(nestedSchemas).strict();
|
|
2771
|
+
countSelectFields[fieldName] = z7.union([z7.literal(true), nestedObj]).optional();
|
|
2772
|
+
if (hasWhereForced(forced)) {
|
|
2773
|
+
forcedCountWhere[fieldName] = forced;
|
|
2774
|
+
}
|
|
2775
|
+
} else {
|
|
2776
|
+
countSelectFields[fieldName] = z7.literal(true).optional();
|
|
2777
|
+
}
|
|
2657
2778
|
}
|
|
2658
|
-
const
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
{ message: "_count.select must specify at least one field" }
|
|
2779
|
+
const selectSchema = strictObjectRequiringOne(
|
|
2780
|
+
countSelectFields,
|
|
2781
|
+
"_count.select must specify at least one field"
|
|
2662
2782
|
);
|
|
2663
2783
|
return {
|
|
2664
|
-
schema:
|
|
2784
|
+
schema: z7.object({ select: selectSchema }).strict().optional(),
|
|
2665
2785
|
forcedCountWhere
|
|
2666
2786
|
};
|
|
2667
2787
|
}
|
|
2668
|
-
function
|
|
2788
|
+
function buildNestedRelSchemas(relatedType, config, depth) {
|
|
2789
|
+
const nestedSchemas = {};
|
|
2790
|
+
const relForced = {};
|
|
2791
|
+
if (config.where) {
|
|
2792
|
+
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
2793
|
+
relatedType,
|
|
2794
|
+
config.where
|
|
2795
|
+
);
|
|
2796
|
+
if (whereSchema)
|
|
2797
|
+
nestedSchemas["where"] = whereSchema;
|
|
2798
|
+
if (hasWhereForced(forced))
|
|
2799
|
+
relForced.where = forced;
|
|
2800
|
+
}
|
|
2801
|
+
if (config.include) {
|
|
2802
|
+
const nested = buildProjectionSchema("include", relatedType, config.include, depth + 1);
|
|
2803
|
+
nestedSchemas["include"] = nested.schema;
|
|
2804
|
+
if (Object.keys(nested.forcedTree).length > 0)
|
|
2805
|
+
relForced.include = nested.forcedTree;
|
|
2806
|
+
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2807
|
+
relForced._countWhere = nested.forcedCountWhere;
|
|
2808
|
+
relForced._countWherePlacement = "include";
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
if (config.select) {
|
|
2812
|
+
const nested = buildProjectionSchema("select", relatedType, config.select, depth + 1);
|
|
2813
|
+
nestedSchemas["select"] = nested.schema;
|
|
2814
|
+
if (Object.keys(nested.forcedTree).length > 0)
|
|
2815
|
+
relForced.select = nested.forcedTree;
|
|
2816
|
+
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2817
|
+
relForced._countWhere = nested.forcedCountWhere;
|
|
2818
|
+
relForced._countWherePlacement = "select";
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
if (config.orderBy) {
|
|
2822
|
+
nestedSchemas["orderBy"] = deps.buildOrderBySchema(relatedType, config.orderBy);
|
|
2823
|
+
}
|
|
2824
|
+
if (config.cursor) {
|
|
2825
|
+
nestedSchemas["cursor"] = deps.buildCursorSchema(relatedType, config.cursor);
|
|
2826
|
+
}
|
|
2827
|
+
if (config.take) {
|
|
2828
|
+
nestedSchemas["take"] = deps.buildTakeSchema(config.take);
|
|
2829
|
+
}
|
|
2830
|
+
if (config.skip) {
|
|
2831
|
+
nestedSchemas["skip"] = z7.number().int().min(0).optional();
|
|
2832
|
+
}
|
|
2833
|
+
return { nestedSchemas, relForced };
|
|
2834
|
+
}
|
|
2835
|
+
function buildProjectionSchema(mode, model, projectionConfig, depth) {
|
|
2669
2836
|
const currentDepth = depth ?? 0;
|
|
2670
2837
|
if (currentDepth > MAX_PROJECTION_DEPTH) {
|
|
2671
2838
|
throw new ShapeError(
|
|
2672
|
-
|
|
2839
|
+
`${mode === "include" ? "Include" : "Select"} schema for model "${model}" exceeds maximum nesting depth (${MAX_PROJECTION_DEPTH}). Check for circular relation references in the shape.`
|
|
2673
2840
|
);
|
|
2674
2841
|
}
|
|
2675
|
-
if (Object.keys(
|
|
2842
|
+
if (Object.keys(projectionConfig).length === 0) {
|
|
2676
2843
|
throw new ShapeError(
|
|
2677
|
-
`Empty
|
|
2844
|
+
`Empty ${mode} config on model "${model}". Define at least one ${mode === "include" ? "relation" : "field"}.`
|
|
2678
2845
|
);
|
|
2679
2846
|
}
|
|
2680
2847
|
const modelFields = typeMap[model];
|
|
2681
2848
|
if (!modelFields)
|
|
2682
2849
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2850
|
+
const allowedNestedKeys = KNOWN_NESTED_KEYS[mode];
|
|
2683
2851
|
const fieldSchemas = {};
|
|
2684
2852
|
const forcedTree = {};
|
|
2685
2853
|
let topLevelForcedCountWhere = {};
|
|
2686
|
-
for (const [
|
|
2687
|
-
if (
|
|
2854
|
+
for (const [fieldName, config] of Object.entries(projectionConfig)) {
|
|
2855
|
+
if (fieldName === "_count") {
|
|
2688
2856
|
const countResult = buildIncludeCountSchema(model, config);
|
|
2689
2857
|
fieldSchemas["_count"] = countResult.schema;
|
|
2690
2858
|
topLevelForcedCountWhere = countResult.forcedCountWhere;
|
|
2691
2859
|
continue;
|
|
2692
2860
|
}
|
|
2693
|
-
const fieldMeta = modelFields[
|
|
2861
|
+
const fieldMeta = modelFields[fieldName];
|
|
2694
2862
|
if (!fieldMeta)
|
|
2695
|
-
throw new ShapeError(`Unknown field "${
|
|
2696
|
-
if (!fieldMeta.isRelation)
|
|
2697
|
-
throw new ShapeError(`Field "${
|
|
2863
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
2864
|
+
if (mode === "include" && !fieldMeta.isRelation) {
|
|
2865
|
+
throw new ShapeError(`Field "${fieldName}" is not a relation on model "${model}"`);
|
|
2866
|
+
}
|
|
2698
2867
|
if (config === true) {
|
|
2699
|
-
fieldSchemas[
|
|
2700
|
-
} else {
|
|
2701
|
-
validateNestedKeys(
|
|
2702
|
-
Object.keys(config),
|
|
2703
|
-
KNOWN_NESTED_INCLUDE_KEYS,
|
|
2704
|
-
`nested include for "${relName}" on model "${model}"`
|
|
2705
|
-
);
|
|
2706
|
-
if (config.select && config.include) {
|
|
2707
|
-
throw new ShapeError(`Nested include for "${relName}" cannot define both "select" and "include".`);
|
|
2708
|
-
}
|
|
2709
|
-
if (!fieldMeta.isList) {
|
|
2710
|
-
if (config.where || config.orderBy || config.cursor || config.take || config.skip) {
|
|
2711
|
-
throw new ShapeError(
|
|
2712
|
-
`Relation "${relName}" on model "${model}" is to-one. Only "include" and "select" are supported for to-one nested reads, not where/orderBy/cursor/take/skip.`
|
|
2713
|
-
);
|
|
2714
|
-
}
|
|
2715
|
-
}
|
|
2716
|
-
const nestedSchemas = {};
|
|
2717
|
-
const relForced = {};
|
|
2718
|
-
if (config.where) {
|
|
2719
|
-
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
2720
|
-
fieldMeta.type,
|
|
2721
|
-
config.where
|
|
2722
|
-
);
|
|
2723
|
-
if (whereSchema)
|
|
2724
|
-
nestedSchemas["where"] = whereSchema;
|
|
2725
|
-
if (hasWhereForced(forced))
|
|
2726
|
-
relForced.where = forced;
|
|
2727
|
-
}
|
|
2728
|
-
if (config.include) {
|
|
2729
|
-
const nested = buildIncludeSchema(fieldMeta.type, config.include, currentDepth + 1);
|
|
2730
|
-
nestedSchemas["include"] = nested.schema;
|
|
2731
|
-
if (Object.keys(nested.forcedTree).length > 0)
|
|
2732
|
-
relForced.include = nested.forcedTree;
|
|
2733
|
-
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2734
|
-
relForced._countWhere = nested.forcedCountWhere;
|
|
2735
|
-
relForced._countWherePlacement = "include";
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
if (config.select) {
|
|
2739
|
-
const nested = buildSelectSchema(fieldMeta.type, config.select, currentDepth + 1);
|
|
2740
|
-
nestedSchemas["select"] = nested.schema;
|
|
2741
|
-
if (Object.keys(nested.forcedTree).length > 0)
|
|
2742
|
-
relForced.select = nested.forcedTree;
|
|
2743
|
-
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2744
|
-
relForced._countWhere = nested.forcedCountWhere;
|
|
2745
|
-
relForced._countWherePlacement = "select";
|
|
2746
|
-
}
|
|
2747
|
-
}
|
|
2748
|
-
if (config.orderBy) {
|
|
2749
|
-
nestedSchemas["orderBy"] = deps.buildOrderBySchema(fieldMeta.type, config.orderBy);
|
|
2750
|
-
}
|
|
2751
|
-
if (config.cursor) {
|
|
2752
|
-
nestedSchemas["cursor"] = deps.buildCursorSchema(fieldMeta.type, config.cursor);
|
|
2753
|
-
}
|
|
2754
|
-
if (config.take) {
|
|
2755
|
-
nestedSchemas["take"] = deps.buildTakeSchema(config.take);
|
|
2756
|
-
}
|
|
2757
|
-
if (config.skip) {
|
|
2758
|
-
nestedSchemas["skip"] = z6.number().int().min(0).optional();
|
|
2759
|
-
}
|
|
2760
|
-
const nestedObj = z6.object(nestedSchemas).strict();
|
|
2761
|
-
fieldSchemas[relName] = z6.union([z6.literal(true), nestedObj]).optional();
|
|
2762
|
-
if (Object.keys(relForced).length > 0)
|
|
2763
|
-
forcedTree[relName] = relForced;
|
|
2764
|
-
}
|
|
2765
|
-
}
|
|
2766
|
-
const includeFieldKeys = Object.keys(fieldSchemas);
|
|
2767
|
-
const baseSchema = z6.object(fieldSchemas).strict();
|
|
2768
|
-
const schema = includeFieldKeys.length > 0 ? baseSchema.refine(
|
|
2769
|
-
(v) => includeFieldKeys.some((k) => v[k] !== void 0),
|
|
2770
|
-
{ message: "include must specify at least one field" }
|
|
2771
|
-
).optional() : baseSchema.optional();
|
|
2772
|
-
return {
|
|
2773
|
-
schema,
|
|
2774
|
-
forcedTree,
|
|
2775
|
-
forcedCountWhere: topLevelForcedCountWhere
|
|
2776
|
-
};
|
|
2777
|
-
}
|
|
2778
|
-
function buildSelectSchema(model, selectConfig, depth) {
|
|
2779
|
-
const currentDepth = depth ?? 0;
|
|
2780
|
-
if (currentDepth > MAX_PROJECTION_DEPTH) {
|
|
2781
|
-
throw new ShapeError(
|
|
2782
|
-
`Select schema for model "${model}" exceeds maximum nesting depth (${MAX_PROJECTION_DEPTH}). Check for circular relation references in the shape.`
|
|
2783
|
-
);
|
|
2784
|
-
}
|
|
2785
|
-
if (Object.keys(selectConfig).length === 0) {
|
|
2786
|
-
throw new ShapeError(
|
|
2787
|
-
`Empty select config on model "${model}". Define at least one field.`
|
|
2788
|
-
);
|
|
2789
|
-
}
|
|
2790
|
-
const modelFields = typeMap[model];
|
|
2791
|
-
if (!modelFields)
|
|
2792
|
-
throw new ShapeError(`Unknown model: ${model}`);
|
|
2793
|
-
const fieldSchemas = {};
|
|
2794
|
-
const forcedTree = {};
|
|
2795
|
-
let topLevelForcedCountWhere = {};
|
|
2796
|
-
for (const [fieldName, config] of Object.entries(selectConfig)) {
|
|
2797
|
-
if (fieldName === "_count") {
|
|
2798
|
-
const countResult = buildIncludeCountSchema(model, config);
|
|
2799
|
-
fieldSchemas["_count"] = countResult.schema;
|
|
2800
|
-
topLevelForcedCountWhere = countResult.forcedCountWhere;
|
|
2868
|
+
fieldSchemas[fieldName] = z7.literal(true).optional();
|
|
2801
2869
|
continue;
|
|
2802
2870
|
}
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
`nested select for "${fieldName}" on model "${model}"`
|
|
2816
|
-
);
|
|
2817
|
-
if (config.select && config.include) {
|
|
2818
|
-
throw new ShapeError(`Nested select for "${fieldName}" cannot define both "select" and "include".`);
|
|
2819
|
-
}
|
|
2820
|
-
if (!fieldMeta.isList) {
|
|
2821
|
-
if (config.where || config.orderBy || config.cursor || config.take || config.skip) {
|
|
2822
|
-
throw new ShapeError(
|
|
2823
|
-
`Relation "${fieldName}" on model "${model}" is to-one. Only "select" and "include" are supported for to-one nested reads, not where/orderBy/cursor/take/skip.`
|
|
2824
|
-
);
|
|
2825
|
-
}
|
|
2826
|
-
}
|
|
2827
|
-
const nestedSchemas = {};
|
|
2828
|
-
const relForced = {};
|
|
2829
|
-
if (config.select) {
|
|
2830
|
-
const nested = buildSelectSchema(fieldMeta.type, config.select, currentDepth + 1);
|
|
2831
|
-
nestedSchemas["select"] = nested.schema;
|
|
2832
|
-
if (Object.keys(nested.forcedTree).length > 0)
|
|
2833
|
-
relForced.select = nested.forcedTree;
|
|
2834
|
-
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2835
|
-
relForced._countWhere = nested.forcedCountWhere;
|
|
2836
|
-
relForced._countWherePlacement = "select";
|
|
2837
|
-
}
|
|
2838
|
-
}
|
|
2839
|
-
if (config.include) {
|
|
2840
|
-
const nested = buildIncludeSchema(fieldMeta.type, config.include, currentDepth + 1);
|
|
2841
|
-
nestedSchemas["include"] = nested.schema;
|
|
2842
|
-
if (Object.keys(nested.forcedTree).length > 0)
|
|
2843
|
-
relForced.include = nested.forcedTree;
|
|
2844
|
-
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2845
|
-
relForced._countWhere = nested.forcedCountWhere;
|
|
2846
|
-
relForced._countWherePlacement = "include";
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2849
|
-
if (config.where) {
|
|
2850
|
-
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
2851
|
-
fieldMeta.type,
|
|
2852
|
-
config.where
|
|
2871
|
+
if (mode === "select" && !fieldMeta.isRelation) {
|
|
2872
|
+
throw new ShapeError(`Nested select args only valid for relations, not scalar "${fieldName}" on model "${model}"`);
|
|
2873
|
+
}
|
|
2874
|
+
const contextLabel = `nested ${mode} for "${fieldName}" on model "${model}"`;
|
|
2875
|
+
validateNestedKeys(Object.keys(config), allowedNestedKeys, contextLabel);
|
|
2876
|
+
if (config.select && config.include) {
|
|
2877
|
+
throw new ShapeError(`Nested ${mode} for "${fieldName}" cannot define both "select" and "include".`);
|
|
2878
|
+
}
|
|
2879
|
+
if (!fieldMeta.isList) {
|
|
2880
|
+
if (config.where || config.orderBy || config.cursor || config.take || config.skip) {
|
|
2881
|
+
throw new ShapeError(
|
|
2882
|
+
`Relation "${fieldName}" on model "${model}" is to-one. Only "select" and "include" are supported for to-one nested reads, not where/orderBy/cursor/take/skip.`
|
|
2853
2883
|
);
|
|
2854
|
-
if (whereSchema)
|
|
2855
|
-
nestedSchemas["where"] = whereSchema;
|
|
2856
|
-
if (hasWhereForced(forced))
|
|
2857
|
-
relForced.where = forced;
|
|
2858
|
-
}
|
|
2859
|
-
if (config.orderBy) {
|
|
2860
|
-
nestedSchemas["orderBy"] = deps.buildOrderBySchema(fieldMeta.type, config.orderBy);
|
|
2861
|
-
}
|
|
2862
|
-
if (config.cursor) {
|
|
2863
|
-
nestedSchemas["cursor"] = deps.buildCursorSchema(fieldMeta.type, config.cursor);
|
|
2864
|
-
}
|
|
2865
|
-
if (config.take) {
|
|
2866
|
-
nestedSchemas["take"] = deps.buildTakeSchema(config.take);
|
|
2867
|
-
}
|
|
2868
|
-
if (config.skip) {
|
|
2869
|
-
nestedSchemas["skip"] = z6.number().int().min(0).optional();
|
|
2870
2884
|
}
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2885
|
+
}
|
|
2886
|
+
const { nestedSchemas, relForced } = buildNestedRelSchemas(fieldMeta.type, config, currentDepth);
|
|
2887
|
+
const nestedObj = z7.object(nestedSchemas).strict();
|
|
2888
|
+
fieldSchemas[fieldName] = wrapRelationSchema(
|
|
2889
|
+
nestedObj,
|
|
2890
|
+
buildRelationArgsSkeleton(config)
|
|
2891
|
+
).optional();
|
|
2892
|
+
if (Object.keys(relForced).length > 0)
|
|
2893
|
+
forcedTree[fieldName] = relForced;
|
|
2894
|
+
}
|
|
2895
|
+
const schema = Object.keys(fieldSchemas).length > 0 ? strictObjectRequiringOne(
|
|
2896
|
+
fieldSchemas,
|
|
2897
|
+
`${mode} must specify at least one field`
|
|
2898
|
+
).optional() : z7.object(fieldSchemas).strict().optional();
|
|
2883
2899
|
return {
|
|
2884
2900
|
schema,
|
|
2885
2901
|
forcedTree,
|
|
2886
2902
|
forcedCountWhere: topLevelForcedCountWhere
|
|
2887
2903
|
};
|
|
2888
2904
|
}
|
|
2889
|
-
|
|
2905
|
+
function buildIncludeSchema(model, includeConfig, depth) {
|
|
2906
|
+
return buildProjectionSchema("include", model, includeConfig, depth);
|
|
2907
|
+
}
|
|
2908
|
+
function buildSelectSchema(model, selectConfig, depth) {
|
|
2909
|
+
return buildProjectionSchema("select", model, selectConfig, depth);
|
|
2910
|
+
}
|
|
2911
|
+
return { buildIncludeSchema, buildSelectSchema, buildIncludeCountSchema, buildProjectionSchema };
|
|
2890
2912
|
}
|
|
2891
2913
|
|
|
2892
2914
|
// src/shared/operation-shape-keys.ts
|
|
@@ -2896,7 +2918,6 @@ var OPERATION_SHAPE_KEYS = {
|
|
|
2896
2918
|
findFirstOrThrow: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
|
|
2897
2919
|
findUnique: ["where", "include", "select"],
|
|
2898
2920
|
findUniqueOrThrow: ["where", "include", "select"],
|
|
2899
|
-
findManyPaginated: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
|
|
2900
2921
|
count: ["where", "select", "cursor", "orderBy", "skip", "take"],
|
|
2901
2922
|
aggregate: ["where", "orderBy", "cursor", "take", "skip", "_count", "_avg", "_sum", "_min", "_max"],
|
|
2902
2923
|
groupBy: ["where", "by", "having", "_count", "_avg", "_sum", "_min", "_max", "orderBy", "take", "skip"],
|
|
@@ -2931,6 +2952,99 @@ var MUTATION_SHAPE_KEYS = {
|
|
|
2931
2952
|
delete: new Set(OPERATION_SHAPE_KEYS.delete),
|
|
2932
2953
|
deleteMany: new Set(OPERATION_SHAPE_KEYS.deleteMany)
|
|
2933
2954
|
};
|
|
2955
|
+
var MUTATION_OPERATION_SPECS = {
|
|
2956
|
+
create: {
|
|
2957
|
+
bodyKeysBase: ["data"],
|
|
2958
|
+
bodyKeysProjection: ["data", "select", "include"],
|
|
2959
|
+
shapeKeysBase: ["data"],
|
|
2960
|
+
shapeKeysProjection: ["data", "select", "include"],
|
|
2961
|
+
supportsProjection: true
|
|
2962
|
+
},
|
|
2963
|
+
createMany: {
|
|
2964
|
+
bodyKeysBase: ["data", "skipDuplicates"],
|
|
2965
|
+
bodyKeysProjection: ["data", "select", "include", "skipDuplicates"],
|
|
2966
|
+
shapeKeysBase: ["data"],
|
|
2967
|
+
shapeKeysProjection: ["data"],
|
|
2968
|
+
supportsProjection: false
|
|
2969
|
+
},
|
|
2970
|
+
createManyAndReturn: {
|
|
2971
|
+
bodyKeysBase: ["data", "skipDuplicates"],
|
|
2972
|
+
bodyKeysProjection: ["data", "select", "include", "skipDuplicates"],
|
|
2973
|
+
shapeKeysBase: ["data"],
|
|
2974
|
+
shapeKeysProjection: ["data", "select", "include"],
|
|
2975
|
+
supportsProjection: true
|
|
2976
|
+
},
|
|
2977
|
+
update: {
|
|
2978
|
+
bodyKeysBase: ["data", "where"],
|
|
2979
|
+
bodyKeysProjection: ["data", "where", "select", "include"],
|
|
2980
|
+
shapeKeysBase: ["data", "where"],
|
|
2981
|
+
shapeKeysProjection: ["data", "where", "select", "include"],
|
|
2982
|
+
supportsProjection: true
|
|
2983
|
+
},
|
|
2984
|
+
updateMany: {
|
|
2985
|
+
bodyKeysBase: ["data", "where"],
|
|
2986
|
+
bodyKeysProjection: ["data", "where"],
|
|
2987
|
+
shapeKeysBase: ["data", "where"],
|
|
2988
|
+
shapeKeysProjection: ["data", "where"],
|
|
2989
|
+
supportsProjection: false
|
|
2990
|
+
},
|
|
2991
|
+
updateManyAndReturn: {
|
|
2992
|
+
bodyKeysBase: ["data", "where"],
|
|
2993
|
+
bodyKeysProjection: ["data", "where", "select", "include"],
|
|
2994
|
+
shapeKeysBase: ["data", "where"],
|
|
2995
|
+
shapeKeysProjection: ["data", "where", "select", "include"],
|
|
2996
|
+
supportsProjection: true
|
|
2997
|
+
},
|
|
2998
|
+
upsert: {
|
|
2999
|
+
bodyKeysBase: ["where", "create", "update", "select", "include"],
|
|
3000
|
+
bodyKeysProjection: ["where", "create", "update", "select", "include"],
|
|
3001
|
+
shapeKeysBase: ["where", "create", "update", "select", "include"],
|
|
3002
|
+
shapeKeysProjection: ["where", "create", "update", "select", "include"],
|
|
3003
|
+
supportsProjection: true
|
|
3004
|
+
},
|
|
3005
|
+
delete: {
|
|
3006
|
+
bodyKeysBase: ["where"],
|
|
3007
|
+
bodyKeysProjection: ["where", "select", "include"],
|
|
3008
|
+
shapeKeysBase: ["where"],
|
|
3009
|
+
shapeKeysProjection: ["where", "select", "include"],
|
|
3010
|
+
supportsProjection: true
|
|
3011
|
+
},
|
|
3012
|
+
deleteMany: {
|
|
3013
|
+
bodyKeysBase: ["where"],
|
|
3014
|
+
bodyKeysProjection: ["where"],
|
|
3015
|
+
shapeKeysBase: ["where"],
|
|
3016
|
+
shapeKeysProjection: ["where"],
|
|
3017
|
+
supportsProjection: false
|
|
3018
|
+
}
|
|
3019
|
+
};
|
|
3020
|
+
var bodyKeyCache = /* @__PURE__ */ new Map();
|
|
3021
|
+
var shapeKeyCache = /* @__PURE__ */ new Map();
|
|
3022
|
+
function getAllowedBodyKeys(method, withProjection) {
|
|
3023
|
+
const cacheKey = `${method}\0${withProjection ? "p" : "b"}`;
|
|
3024
|
+
const cached = bodyKeyCache.get(cacheKey);
|
|
3025
|
+
if (cached)
|
|
3026
|
+
return cached;
|
|
3027
|
+
const spec = MUTATION_OPERATION_SPECS[method];
|
|
3028
|
+
if (!spec)
|
|
3029
|
+
throw new Error(`Unknown mutation method "${method}"`);
|
|
3030
|
+
const keys = withProjection && spec.supportsProjection ? spec.bodyKeysProjection : spec.bodyKeysBase;
|
|
3031
|
+
const set = new Set(keys);
|
|
3032
|
+
bodyKeyCache.set(cacheKey, set);
|
|
3033
|
+
return set;
|
|
3034
|
+
}
|
|
3035
|
+
function getAllowedShapeKeys(method, withProjection) {
|
|
3036
|
+
const cacheKey = `${method}\0${withProjection ? "p" : "b"}`;
|
|
3037
|
+
const cached = shapeKeyCache.get(cacheKey);
|
|
3038
|
+
if (cached)
|
|
3039
|
+
return cached;
|
|
3040
|
+
const spec = MUTATION_OPERATION_SPECS[method];
|
|
3041
|
+
if (!spec)
|
|
3042
|
+
throw new Error(`Unknown mutation method "${method}"`);
|
|
3043
|
+
const keys = withProjection && spec.supportsProjection ? spec.shapeKeysProjection : spec.shapeKeysBase;
|
|
3044
|
+
const set = new Set(keys);
|
|
3045
|
+
shapeKeyCache.set(cacheKey, set);
|
|
3046
|
+
return set;
|
|
3047
|
+
}
|
|
2934
3048
|
|
|
2935
3049
|
// src/runtime/query-builder.ts
|
|
2936
3050
|
var METHOD_ALLOWED_ARGS = READ_METHOD_ALLOWED_ARGS;
|
|
@@ -3031,6 +3145,90 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3031
3145
|
}
|
|
3032
3146
|
return shapeOrFn;
|
|
3033
3147
|
}
|
|
3148
|
+
function buildGroupByOrderBySchema(model, orderBy, by, sortEnum) {
|
|
3149
|
+
const bySet = new Set(by);
|
|
3150
|
+
if (orderBy === true) {
|
|
3151
|
+
const groupByOrderFields2 = {};
|
|
3152
|
+
for (const field of by) {
|
|
3153
|
+
groupByOrderFields2[field] = sortEnum.optional();
|
|
3154
|
+
}
|
|
3155
|
+
groupByOrderFields2._count = sortEnum.optional();
|
|
3156
|
+
const singleSchema2 = strictObjectRequiringOne(
|
|
3157
|
+
groupByOrderFields2,
|
|
3158
|
+
"orderBy must specify at least one field"
|
|
3159
|
+
);
|
|
3160
|
+
return z8.union([
|
|
3161
|
+
singleSchema2,
|
|
3162
|
+
z8.preprocess(coerceToArray, z8.array(singleSchema2).min(1))
|
|
3163
|
+
]).optional();
|
|
3164
|
+
}
|
|
3165
|
+
if (!isPlainObject(orderBy)) {
|
|
3166
|
+
throw new ShapeError(
|
|
3167
|
+
`groupBy orderBy shape on model "${model}" must be true or an object of fields`
|
|
3168
|
+
);
|
|
3169
|
+
}
|
|
3170
|
+
if (Object.keys(orderBy).length === 0) {
|
|
3171
|
+
throw new ShapeError(
|
|
3172
|
+
`Empty groupBy orderBy config on model "${model}". Define at least one field.`
|
|
3173
|
+
);
|
|
3174
|
+
}
|
|
3175
|
+
const groupByOrderFields = {};
|
|
3176
|
+
for (const [fieldName, config] of Object.entries(orderBy)) {
|
|
3177
|
+
if (fieldName === "_count") {
|
|
3178
|
+
if (config === true) {
|
|
3179
|
+
groupByOrderFields._count = sortEnum.optional();
|
|
3180
|
+
} else if (isPlainObject(config)) {
|
|
3181
|
+
if (Object.keys(config).length === 0) {
|
|
3182
|
+
throw new ShapeError(
|
|
3183
|
+
`Empty groupBy orderBy "_count" config on model "${model}". Define at least one by-field.`
|
|
3184
|
+
);
|
|
3185
|
+
}
|
|
3186
|
+
const countFields = {};
|
|
3187
|
+
for (const [countField, countConfig] of Object.entries(config)) {
|
|
3188
|
+
if (countConfig !== true) {
|
|
3189
|
+
throw new ShapeError(
|
|
3190
|
+
`groupBy orderBy "_count.${countField}" config on model "${model}" must be true`
|
|
3191
|
+
);
|
|
3192
|
+
}
|
|
3193
|
+
if (!bySet.has(countField)) {
|
|
3194
|
+
throw new ShapeError(
|
|
3195
|
+
`orderBy _count field "${countField}" must be included in "by" for groupBy`
|
|
3196
|
+
);
|
|
3197
|
+
}
|
|
3198
|
+
countFields[countField] = sortEnum.optional();
|
|
3199
|
+
}
|
|
3200
|
+
groupByOrderFields._count = strictObjectRequiringOne(
|
|
3201
|
+
countFields,
|
|
3202
|
+
"orderBy._count must specify at least one field"
|
|
3203
|
+
).optional();
|
|
3204
|
+
} else {
|
|
3205
|
+
throw new ShapeError(
|
|
3206
|
+
`groupBy orderBy "_count" config on model "${model}" must be true or an object of by-fields`
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
continue;
|
|
3210
|
+
}
|
|
3211
|
+
if (config !== true) {
|
|
3212
|
+
throw new ShapeError(
|
|
3213
|
+
`groupBy orderBy field "${fieldName}" config on model "${model}" must be true`
|
|
3214
|
+
);
|
|
3215
|
+
}
|
|
3216
|
+
if (!bySet.has(fieldName)) {
|
|
3217
|
+
throw new ShapeError(
|
|
3218
|
+
`orderBy field "${fieldName}" must be included in "by" for groupBy`
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
groupByOrderFields[fieldName] = sortEnum.optional();
|
|
3222
|
+
}
|
|
3223
|
+
const singleSchema = strictObjectRequiringOne(
|
|
3224
|
+
groupByOrderFields,
|
|
3225
|
+
"orderBy must specify at least one field"
|
|
3226
|
+
);
|
|
3227
|
+
return z8.union([
|
|
3228
|
+
singleSchema,
|
|
3229
|
+
z8.preprocess(coerceToArray, z8.array(singleSchema).min(1))
|
|
3230
|
+
]).optional();
|
|
3231
|
+
}
|
|
3034
3232
|
function buildShapeZodSchema(model, method, shape) {
|
|
3035
3233
|
validateShapeArgs(method, shape);
|
|
3036
3234
|
validateUniqueWhere(model, method, shape);
|
|
@@ -3070,73 +3268,19 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3070
3268
|
}
|
|
3071
3269
|
if (shape.orderBy) {
|
|
3072
3270
|
if (method === "groupBy" && shape.by) {
|
|
3073
|
-
const sortEnum =
|
|
3074
|
-
|
|
3271
|
+
const sortEnum = z8.enum(["asc", "desc"]);
|
|
3272
|
+
schemaFields.orderBy = buildGroupByOrderBySchema(
|
|
3273
|
+
model,
|
|
3274
|
+
shape.orderBy,
|
|
3275
|
+
shape.by,
|
|
3276
|
+
sortEnum
|
|
3277
|
+
);
|
|
3278
|
+
} else {
|
|
3075
3279
|
if (shape.orderBy === true) {
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
groupByOrderFields[field] = sortEnum.optional();
|
|
3079
|
-
}
|
|
3080
|
-
groupByOrderFields._count = sortEnum.optional();
|
|
3081
|
-
const fieldKeys = Object.keys(groupByOrderFields);
|
|
3082
|
-
const singleSchema = z7.object(groupByOrderFields).strict().refine(
|
|
3083
|
-
(v) => fieldKeys.some(
|
|
3084
|
-
(k) => v[k] !== void 0
|
|
3085
|
-
),
|
|
3086
|
-
{ message: "orderBy must specify at least one field" }
|
|
3087
|
-
);
|
|
3088
|
-
schemaFields.orderBy = z7.union([
|
|
3089
|
-
singleSchema,
|
|
3090
|
-
z7.preprocess(coerceToArray, z7.array(singleSchema).min(1))
|
|
3091
|
-
]).optional();
|
|
3092
|
-
} else {
|
|
3093
|
-
const groupByOrderFields = {};
|
|
3094
|
-
for (const [fieldName, config] of Object.entries(shape.orderBy)) {
|
|
3095
|
-
if (fieldName === "_count") {
|
|
3096
|
-
if (config === true) {
|
|
3097
|
-
groupByOrderFields._count = sortEnum.optional();
|
|
3098
|
-
} else if (typeof config === "object" && config !== null) {
|
|
3099
|
-
const countFields = {};
|
|
3100
|
-
for (const countField of Object.keys(config)) {
|
|
3101
|
-
if (!bySet.has(countField)) {
|
|
3102
|
-
throw new ShapeError(
|
|
3103
|
-
`orderBy _count field "${countField}" must be included in "by" for groupBy`
|
|
3104
|
-
);
|
|
3105
|
-
}
|
|
3106
|
-
countFields[countField] = sortEnum.optional();
|
|
3107
|
-
}
|
|
3108
|
-
const countKeys = Object.keys(countFields);
|
|
3109
|
-
groupByOrderFields._count = z7.object(countFields).strict().refine(
|
|
3110
|
-
(v) => countKeys.some(
|
|
3111
|
-
(k) => v[k] !== void 0
|
|
3112
|
-
),
|
|
3113
|
-
{
|
|
3114
|
-
message: "orderBy._count must specify at least one field"
|
|
3115
|
-
}
|
|
3116
|
-
).optional();
|
|
3117
|
-
}
|
|
3118
|
-
continue;
|
|
3119
|
-
}
|
|
3120
|
-
if (!bySet.has(fieldName)) {
|
|
3121
|
-
throw new ShapeError(
|
|
3122
|
-
`orderBy field "${fieldName}" must be included in "by" for groupBy`
|
|
3123
|
-
);
|
|
3124
|
-
}
|
|
3125
|
-
groupByOrderFields[fieldName] = sortEnum.optional();
|
|
3126
|
-
}
|
|
3127
|
-
const fieldKeys = Object.keys(groupByOrderFields);
|
|
3128
|
-
const singleSchema = z7.object(groupByOrderFields).strict().refine(
|
|
3129
|
-
(v) => fieldKeys.some(
|
|
3130
|
-
(k) => v[k] !== void 0
|
|
3131
|
-
),
|
|
3132
|
-
{ message: "orderBy must specify at least one field" }
|
|
3280
|
+
throw new ShapeError(
|
|
3281
|
+
`Shape config "orderBy: true" is only supported for groupBy. Define an object of allowed fields.`
|
|
3133
3282
|
);
|
|
3134
|
-
schemaFields.orderBy = z7.union([
|
|
3135
|
-
singleSchema,
|
|
3136
|
-
z7.preprocess(coerceToArray, z7.array(singleSchema).min(1))
|
|
3137
|
-
]).optional();
|
|
3138
3283
|
}
|
|
3139
|
-
} else {
|
|
3140
3284
|
schemaFields.orderBy = argsBuilder.buildOrderBySchema(
|
|
3141
3285
|
model,
|
|
3142
3286
|
shape.orderBy
|
|
@@ -3144,19 +3288,16 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3144
3288
|
}
|
|
3145
3289
|
}
|
|
3146
3290
|
if (shape.cursor) {
|
|
3147
|
-
schemaFields.cursor = argsBuilder.buildCursorSchema(
|
|
3148
|
-
model,
|
|
3149
|
-
shape.cursor
|
|
3150
|
-
);
|
|
3291
|
+
schemaFields.cursor = argsBuilder.buildCursorSchema(model, shape.cursor);
|
|
3151
3292
|
}
|
|
3152
|
-
if (shape.take) {
|
|
3293
|
+
if (shape.take !== void 0) {
|
|
3153
3294
|
schemaFields.take = argsBuilder.buildTakeSchema(shape.take);
|
|
3154
3295
|
}
|
|
3155
3296
|
if (shape.skip !== void 0) {
|
|
3156
3297
|
if (shape.skip !== true) {
|
|
3157
3298
|
throw new ShapeError('Shape config "skip" must be true');
|
|
3158
3299
|
}
|
|
3159
|
-
schemaFields.skip =
|
|
3300
|
+
schemaFields.skip = z8.number().int().min(0).optional();
|
|
3160
3301
|
}
|
|
3161
3302
|
if (shape.distinct) {
|
|
3162
3303
|
schemaFields.distinct = argsBuilder.buildDistinctSchema(
|
|
@@ -3203,13 +3344,10 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3203
3344
|
schemaFields.by = argsBuilder.buildBySchema(model, shape.by);
|
|
3204
3345
|
}
|
|
3205
3346
|
if (shape.having) {
|
|
3206
|
-
schemaFields.having = argsBuilder.buildHavingSchema(
|
|
3207
|
-
model,
|
|
3208
|
-
shape.having
|
|
3209
|
-
);
|
|
3347
|
+
schemaFields.having = argsBuilder.buildHavingSchema(model, shape.having);
|
|
3210
3348
|
}
|
|
3211
3349
|
return {
|
|
3212
|
-
zodSchema:
|
|
3350
|
+
zodSchema: z8.object(schemaFields).strict(),
|
|
3213
3351
|
forcedWhere,
|
|
3214
3352
|
forcedOnlyWhereKeys,
|
|
3215
3353
|
forcedIncludeTree,
|
|
@@ -3402,22 +3540,18 @@ function buildScopedUniqueWhere(existingWhere, conditions, scopeFks, log, model)
|
|
|
3402
3540
|
allConditions.push(...conditions);
|
|
3403
3541
|
return { ...topLevel, AND: allConditions };
|
|
3404
3542
|
}
|
|
3405
|
-
function
|
|
3406
|
-
const t = typeof v;
|
|
3407
|
-
return t === "string" || t === "number" || t === "bigint";
|
|
3408
|
-
}
|
|
3409
|
-
function looseEqual(a, b, log, fk) {
|
|
3543
|
+
function scopeValuesEqual(a, b) {
|
|
3410
3544
|
if (a === b)
|
|
3411
3545
|
return true;
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
log.warn(
|
|
3417
|
-
`prisma-guard: Scope value for "${fk}" matched via type coercion (${typeof a} ${String(a)} vs ${typeof b} ${String(b)}). Consider normalizing types in the context function.`
|
|
3418
|
-
);
|
|
3546
|
+
const aIsBig = typeof a === "bigint";
|
|
3547
|
+
const bIsBig = typeof b === "bigint";
|
|
3548
|
+
if (aIsBig && typeof b === "number") {
|
|
3549
|
+
return Number.isInteger(b) && a === BigInt(b);
|
|
3419
3550
|
}
|
|
3420
|
-
|
|
3551
|
+
if (bIsBig && typeof a === "number") {
|
|
3552
|
+
return Number.isInteger(a) && b === BigInt(a);
|
|
3553
|
+
}
|
|
3554
|
+
return false;
|
|
3421
3555
|
}
|
|
3422
3556
|
function buildFkSelect(fks) {
|
|
3423
3557
|
const select = {};
|
|
@@ -3707,7 +3841,7 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log,
|
|
|
3707
3841
|
const result = await query(nextArgs);
|
|
3708
3842
|
if (result === null)
|
|
3709
3843
|
return result;
|
|
3710
|
-
if (typeof result !== "object"
|
|
3844
|
+
if (typeof result !== "object") {
|
|
3711
3845
|
throw new ShapeError(
|
|
3712
3846
|
`${operation} on model "${model}" returned a non-object, non-null result`
|
|
3713
3847
|
);
|
|
@@ -3738,7 +3872,7 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log,
|
|
|
3738
3872
|
`prisma-guard: Scope verification re-query on model "${model}" returned null for an existing ${operation} result. The record may have been deleted between the original query and verification.`
|
|
3739
3873
|
);
|
|
3740
3874
|
}
|
|
3741
|
-
if (typeof verifyResult !== "object"
|
|
3875
|
+
if (typeof verifyResult !== "object") {
|
|
3742
3876
|
throw new PolicyError(
|
|
3743
3877
|
`prisma-guard: Scope verification re-query on model "${model}" returned a non-object result.`
|
|
3744
3878
|
);
|
|
@@ -3752,7 +3886,7 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log,
|
|
|
3752
3886
|
`prisma-guard: Cannot verify scope on model "${model}" \u2014 FK field "${fk}" not present in verification result. Ensure "${fk}" is selectable on model "${model}" (not excluded by select or field-level access).`
|
|
3753
3887
|
);
|
|
3754
3888
|
}
|
|
3755
|
-
if (!
|
|
3889
|
+
if (!scopeValuesEqual(verifyObj[fk], value)) {
|
|
3756
3890
|
if (operation === "findUniqueOrThrow") {
|
|
3757
3891
|
throw new PolicyError(
|
|
3758
3892
|
`prisma-guard: Record on model "${model}" not accessible in current scope`
|
|
@@ -3772,97 +3906,158 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log,
|
|
|
3772
3906
|
}
|
|
3773
3907
|
|
|
3774
3908
|
// src/runtime/model-guard.ts
|
|
3909
|
+
import { z as z11 } from "zod";
|
|
3910
|
+
|
|
3911
|
+
// src/runtime/model-guard-data.ts
|
|
3912
|
+
import { z as z10 } from "zod";
|
|
3913
|
+
|
|
3914
|
+
// src/runtime/unique-selector-schema.ts
|
|
3775
3915
|
import { z as z9 } from "zod";
|
|
3916
|
+
function buildUniqueSelectorSchema(parentModel, parentField, relatedModel, config, typeMap, uniqueMap, enumMap, scalarBase, context) {
|
|
3917
|
+
const modelFields = typeMap[relatedModel];
|
|
3918
|
+
if (!modelFields) {
|
|
3919
|
+
throw new ShapeError(
|
|
3920
|
+
`Unknown related model "${relatedModel}" for ${context} on "${parentModel}.${parentField}"`
|
|
3921
|
+
);
|
|
3922
|
+
}
|
|
3923
|
+
const constraints = uniqueMap[relatedModel] ?? [];
|
|
3924
|
+
if (constraints.length === 0) {
|
|
3925
|
+
throw new ShapeError(
|
|
3926
|
+
`${context} on "${parentModel}.${parentField}" requires related model "${relatedModel}" to have at least one unique constraint`
|
|
3927
|
+
);
|
|
3928
|
+
}
|
|
3929
|
+
const configKeys = Object.keys(config);
|
|
3930
|
+
if (configKeys.length === 0) {
|
|
3931
|
+
throw new ShapeError(
|
|
3932
|
+
`${context} on "${parentModel}.${parentField}" must define at least one unique selector. Available: ${formatUniqueConstraints(constraints)}`
|
|
3933
|
+
);
|
|
3934
|
+
}
|
|
3935
|
+
const fieldSchemas = {};
|
|
3936
|
+
const fieldKeys = [];
|
|
3937
|
+
for (const [key, value] of Object.entries(config)) {
|
|
3938
|
+
const compoundConstraint = constraints.find(
|
|
3939
|
+
(c) => c.selector === key && c.fields.length > 1
|
|
3940
|
+
);
|
|
3941
|
+
if (compoundConstraint) {
|
|
3942
|
+
if (!isPlainObject(value)) {
|
|
3943
|
+
throw new ShapeError(
|
|
3944
|
+
`Compound unique selector "${key}" in ${context} on "${parentModel}.${parentField}" must be an object with fields: ${compoundConstraint.fields.join(", ")}`
|
|
3945
|
+
);
|
|
3946
|
+
}
|
|
3947
|
+
const allowed = new Set(compoundConstraint.fields);
|
|
3948
|
+
const nestedKeys = Object.keys(value);
|
|
3949
|
+
for (const nestedKey of nestedKeys) {
|
|
3950
|
+
if (!allowed.has(nestedKey)) {
|
|
3951
|
+
throw new ShapeError(
|
|
3952
|
+
`Unknown field "${nestedKey}" in compound unique selector "${key}" on "${parentModel}.${parentField}". Allowed: ${compoundConstraint.fields.join(", ")}`
|
|
3953
|
+
);
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
3956
|
+
for (const field of compoundConstraint.fields) {
|
|
3957
|
+
if (!(field in value)) {
|
|
3958
|
+
throw new ShapeError(
|
|
3959
|
+
`Missing field "${field}" in compound unique selector "${key}" on "${parentModel}.${parentField}"`
|
|
3960
|
+
);
|
|
3961
|
+
}
|
|
3962
|
+
const fieldValue = value[field];
|
|
3963
|
+
if (fieldValue !== true) {
|
|
3964
|
+
throw new ShapeError(
|
|
3965
|
+
`Field "${field}" in compound unique selector "${key}" on "${parentModel}.${parentField}" must be true`
|
|
3966
|
+
);
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
const nestedSchemas = {};
|
|
3970
|
+
for (const field of compoundConstraint.fields) {
|
|
3971
|
+
const fieldMeta2 = modelFields[field];
|
|
3972
|
+
if (!fieldMeta2) {
|
|
3973
|
+
throw new ShapeError(
|
|
3974
|
+
`Unknown field "${field}" on related model "${relatedModel}"`
|
|
3975
|
+
);
|
|
3976
|
+
}
|
|
3977
|
+
if (fieldMeta2.isRelation) {
|
|
3978
|
+
throw new ShapeError(
|
|
3979
|
+
`Relation field "${field}" cannot be used in compound unique selector`
|
|
3980
|
+
);
|
|
3981
|
+
}
|
|
3982
|
+
nestedSchemas[field] = buildDirectScalarSchema(
|
|
3983
|
+
fieldMeta2,
|
|
3984
|
+
enumMap,
|
|
3985
|
+
scalarBase
|
|
3986
|
+
);
|
|
3987
|
+
}
|
|
3988
|
+
fieldSchemas[key] = z9.object(nestedSchemas).strict().optional();
|
|
3989
|
+
fieldKeys.push(key);
|
|
3990
|
+
continue;
|
|
3991
|
+
}
|
|
3992
|
+
const singleConstraint = constraints.find(
|
|
3993
|
+
(c) => c.fields.length === 1 && c.fields[0] === key && c.selector === key
|
|
3994
|
+
);
|
|
3995
|
+
if (!singleConstraint) {
|
|
3996
|
+
const available = formatUniqueConstraints(constraints);
|
|
3997
|
+
throw new ShapeError(
|
|
3998
|
+
`Field "${key}" in ${context} on "${parentModel}.${parentField}" is not a unique selector on model "${relatedModel}". Available: ${available}`
|
|
3999
|
+
);
|
|
4000
|
+
}
|
|
4001
|
+
if (value !== true) {
|
|
4002
|
+
throw new ShapeError(
|
|
4003
|
+
`Field "${key}" in ${context} on "${parentModel}.${parentField}" must be true`
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
4006
|
+
const fieldMeta = modelFields[key];
|
|
4007
|
+
if (!fieldMeta) {
|
|
4008
|
+
throw new ShapeError(
|
|
4009
|
+
`Unknown field "${key}" on related model "${relatedModel}"`
|
|
4010
|
+
);
|
|
4011
|
+
}
|
|
4012
|
+
if (fieldMeta.isRelation) {
|
|
4013
|
+
throw new ShapeError(
|
|
4014
|
+
`Relation field "${key}" cannot be used in unique selector`
|
|
4015
|
+
);
|
|
4016
|
+
}
|
|
4017
|
+
fieldSchemas[key] = buildDirectScalarSchema(
|
|
4018
|
+
fieldMeta,
|
|
4019
|
+
enumMap,
|
|
4020
|
+
scalarBase
|
|
4021
|
+
).optional();
|
|
4022
|
+
fieldKeys.push(key);
|
|
4023
|
+
}
|
|
4024
|
+
return z9.object(fieldSchemas).strict().refine(
|
|
4025
|
+
(v) => fieldKeys.some((k) => v[k] !== void 0),
|
|
4026
|
+
{
|
|
4027
|
+
message: `${context} on "${parentModel}.${parentField}" requires at least one unique selector value`
|
|
4028
|
+
}
|
|
4029
|
+
);
|
|
4030
|
+
}
|
|
3776
4031
|
|
|
3777
4032
|
// src/runtime/model-guard-data.ts
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
"data",
|
|
3782
|
-
"
|
|
3783
|
-
"
|
|
3784
|
-
])
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
"select",
|
|
3792
|
-
"include",
|
|
3793
|
-
"skipDuplicates"
|
|
3794
|
-
]);
|
|
3795
|
-
var ALLOWED_BODY_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
|
|
3796
|
-
var ALLOWED_BODY_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3797
|
-
"data",
|
|
3798
|
-
"where",
|
|
3799
|
-
"select",
|
|
3800
|
-
"include"
|
|
3801
|
-
]);
|
|
3802
|
-
var ALLOWED_BODY_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
|
|
3803
|
-
var ALLOWED_BODY_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3804
|
-
"where",
|
|
3805
|
-
"select",
|
|
3806
|
-
"include"
|
|
3807
|
-
]);
|
|
3808
|
-
var ALLOWED_BODY_KEYS_UPSERT = /* @__PURE__ */ new Set([
|
|
3809
|
-
"where",
|
|
3810
|
-
"create",
|
|
3811
|
-
"update",
|
|
3812
|
-
"select",
|
|
3813
|
-
"include"
|
|
3814
|
-
]);
|
|
3815
|
-
var VALID_SHAPE_KEYS_CREATE = /* @__PURE__ */ new Set(["data"]);
|
|
3816
|
-
var VALID_SHAPE_KEYS_CREATE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3817
|
-
"data",
|
|
3818
|
-
"select",
|
|
3819
|
-
"include"
|
|
3820
|
-
]);
|
|
3821
|
-
var VALID_SHAPE_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
|
|
3822
|
-
var VALID_SHAPE_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3823
|
-
"data",
|
|
3824
|
-
"where",
|
|
3825
|
-
"select",
|
|
3826
|
-
"include"
|
|
3827
|
-
]);
|
|
3828
|
-
var VALID_SHAPE_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
|
|
3829
|
-
var VALID_SHAPE_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3830
|
-
"where",
|
|
3831
|
-
"select",
|
|
3832
|
-
"include"
|
|
3833
|
-
]);
|
|
3834
|
-
var VALID_SHAPE_KEYS_UPSERT = /* @__PURE__ */ new Set([
|
|
3835
|
-
"where",
|
|
3836
|
-
"create",
|
|
3837
|
-
"update",
|
|
3838
|
-
"select",
|
|
3839
|
-
"include"
|
|
3840
|
-
]);
|
|
3841
|
-
var KNOWN_RELATION_WRITE_OPS = /* @__PURE__ */ new Set([
|
|
3842
|
-
"connect",
|
|
3843
|
-
"connectOrCreate",
|
|
3844
|
-
"create",
|
|
3845
|
-
"createMany",
|
|
3846
|
-
"disconnect",
|
|
3847
|
-
"delete",
|
|
3848
|
-
"set",
|
|
3849
|
-
"update",
|
|
3850
|
-
"updateMany",
|
|
3851
|
-
"upsert",
|
|
3852
|
-
"deleteMany"
|
|
3853
|
-
]);
|
|
3854
|
-
function validateMutationBodyKeys(body, allowed, method) {
|
|
3855
|
-
for (const key of Object.keys(body)) {
|
|
4033
|
+
var RELATION_OP_ALLOWED_KEYS = {
|
|
4034
|
+
connectOrCreate: /* @__PURE__ */ new Set(["where", "create"]),
|
|
4035
|
+
createMany: /* @__PURE__ */ new Set(["data", "skipDuplicates"]),
|
|
4036
|
+
"update.toMany": /* @__PURE__ */ new Set(["where", "data"]),
|
|
4037
|
+
updateMany: /* @__PURE__ */ new Set(["where", "data"]),
|
|
4038
|
+
"upsert.toOne": /* @__PURE__ */ new Set(["where", "create", "update"]),
|
|
4039
|
+
"upsert.toMany": /* @__PURE__ */ new Set(["where", "create", "update"])
|
|
4040
|
+
};
|
|
4041
|
+
function validateRelationOpKeys(actual, opKey, model, field, opLabel) {
|
|
4042
|
+
const allowed = RELATION_OP_ALLOWED_KEYS[opKey];
|
|
4043
|
+
if (!allowed)
|
|
4044
|
+
return;
|
|
4045
|
+
for (const key of Object.keys(actual)) {
|
|
3856
4046
|
if (!allowed.has(key)) {
|
|
3857
4047
|
throw new ShapeError(
|
|
3858
|
-
`
|
|
4048
|
+
`Unknown key "${key}" in ${opLabel} config on "${model}.${field}". Allowed: ${[...allowed].join(", ")}`
|
|
3859
4049
|
);
|
|
3860
4050
|
}
|
|
3861
4051
|
}
|
|
3862
4052
|
}
|
|
3863
|
-
function
|
|
3864
|
-
for (const key of Object.keys(
|
|
4053
|
+
function validateAllowedKeys(value, allowed, method, kind) {
|
|
4054
|
+
for (const key of Object.keys(value)) {
|
|
3865
4055
|
if (!allowed.has(key)) {
|
|
4056
|
+
if (kind === "body") {
|
|
4057
|
+
throw new ShapeError(
|
|
4058
|
+
`Unexpected key "${key}" in ${method} body. Allowed keys: ${[...allowed].join(", ")}`
|
|
4059
|
+
);
|
|
4060
|
+
}
|
|
3866
4061
|
throw new ShapeError(
|
|
3867
4062
|
`Shape key "${key}" not valid for ${method}. Allowed: ${[...allowed].join(", ")}`
|
|
3868
4063
|
);
|
|
@@ -3904,24 +4099,24 @@ function buildWhereFieldsSchema(model, config, typeMap, schemaBuilder) {
|
|
|
3904
4099
|
for (const [fieldName, value] of Object.entries(config)) {
|
|
3905
4100
|
if (value !== true)
|
|
3906
4101
|
throw new ShapeError(
|
|
3907
|
-
`Field "${fieldName}" in
|
|
4102
|
+
`Field "${fieldName}" in filter config must be true`
|
|
3908
4103
|
);
|
|
3909
4104
|
const meta = modelFields[fieldName];
|
|
3910
4105
|
if (!meta)
|
|
3911
4106
|
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
3912
4107
|
if (meta.isRelation)
|
|
3913
4108
|
throw new ShapeError(
|
|
3914
|
-
`Relation field "${fieldName}" cannot be used in
|
|
4109
|
+
`Relation field "${fieldName}" cannot be used in filter`
|
|
3915
4110
|
);
|
|
3916
4111
|
fieldSchemas[fieldName] = schemaBuilder.buildFieldSchema(model, fieldName).optional();
|
|
3917
4112
|
fieldKeys.push(fieldName);
|
|
3918
4113
|
}
|
|
3919
|
-
return
|
|
4114
|
+
return z10.object(fieldSchemas).strict().refine(
|
|
3920
4115
|
(v) => fieldKeys.some((k) => v[k] !== void 0),
|
|
3921
|
-
{ message: `At least one field required in
|
|
4116
|
+
{ message: `At least one field required in filter` }
|
|
3922
4117
|
);
|
|
3923
4118
|
}
|
|
3924
|
-
function buildNestedDataSchema(model, config, typeMap, schemaBuilder) {
|
|
4119
|
+
function buildNestedDataSchema(model, config, mode, typeMap, schemaBuilder) {
|
|
3925
4120
|
const modelFields = typeMap[model];
|
|
3926
4121
|
if (!modelFields)
|
|
3927
4122
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -3942,382 +4137,349 @@ function buildNestedDataSchema(model, config, typeMap, schemaBuilder) {
|
|
|
3942
4137
|
throw new ShapeError(
|
|
3943
4138
|
`updatedAt field "${fieldName}" cannot be used in nested data`
|
|
3944
4139
|
);
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
4140
|
+
if (meta.isUnsupported)
|
|
4141
|
+
throw new ShapeError(
|
|
4142
|
+
`Field "${fieldName}" on model "${model}" has an Unsupported type and cannot be used in nested data`
|
|
4143
|
+
);
|
|
4144
|
+
const baseSchema = schemaBuilder.buildFieldSchema(model, fieldName);
|
|
4145
|
+
fieldSchemas[fieldName] = applyCreateUpdateNullability(meta, baseSchema, {
|
|
4146
|
+
mode,
|
|
4147
|
+
handlesUndefined: false
|
|
4148
|
+
});
|
|
3952
4149
|
}
|
|
3953
|
-
return
|
|
4150
|
+
return z10.object(fieldSchemas).strict();
|
|
3954
4151
|
}
|
|
3955
|
-
function
|
|
3956
|
-
|
|
3957
|
-
|
|
4152
|
+
function requirePlainObjectConfig(value, message) {
|
|
4153
|
+
if (!isPlainObject(value))
|
|
4154
|
+
throw new ShapeError(message);
|
|
4155
|
+
return value;
|
|
4156
|
+
}
|
|
4157
|
+
function requireNestedObject(cfg, key, message) {
|
|
4158
|
+
const v = cfg[key];
|
|
4159
|
+
if (!v || !isPlainObject(v))
|
|
4160
|
+
throw new ShapeError(message);
|
|
4161
|
+
return v;
|
|
4162
|
+
}
|
|
4163
|
+
function buildUniqueSelector(ctx, cfg, context) {
|
|
4164
|
+
return buildUniqueSelectorSchema(
|
|
4165
|
+
ctx.model,
|
|
4166
|
+
ctx.fieldName,
|
|
4167
|
+
ctx.relatedModelName,
|
|
4168
|
+
cfg,
|
|
4169
|
+
ctx.typeMap,
|
|
4170
|
+
ctx.uniqueMap,
|
|
4171
|
+
ctx.enumMap,
|
|
4172
|
+
ctx.scalarBase,
|
|
4173
|
+
context
|
|
4174
|
+
);
|
|
4175
|
+
}
|
|
4176
|
+
function buildNestedData(ctx, cfg, mode) {
|
|
4177
|
+
return buildNestedDataSchema(
|
|
4178
|
+
ctx.relatedModelName,
|
|
4179
|
+
cfg,
|
|
4180
|
+
mode,
|
|
4181
|
+
ctx.typeMap,
|
|
4182
|
+
ctx.schemaBuilder
|
|
4183
|
+
);
|
|
4184
|
+
}
|
|
4185
|
+
var handleConnect = (ctx) => {
|
|
4186
|
+
const cfg = requirePlainObjectConfig(
|
|
4187
|
+
ctx.config,
|
|
4188
|
+
`connect config on "${ctx.model}.${ctx.fieldName}" must be an object of unique selectors`
|
|
4189
|
+
);
|
|
4190
|
+
const schema = buildUniqueSelector(ctx, cfg, "connect");
|
|
4191
|
+
return wrapRelationOp(ctx.isList, schema);
|
|
4192
|
+
};
|
|
4193
|
+
var handleConnectOrCreate = (ctx) => {
|
|
4194
|
+
const cfg = requirePlainObjectConfig(
|
|
4195
|
+
ctx.config,
|
|
4196
|
+
`connectOrCreate config on "${ctx.model}.${ctx.fieldName}" must be an object with "where" and "create"`
|
|
4197
|
+
);
|
|
4198
|
+
validateRelationOpKeys(cfg, "connectOrCreate", ctx.model, ctx.fieldName, "connectOrCreate");
|
|
4199
|
+
const where = requireNestedObject(
|
|
4200
|
+
cfg,
|
|
4201
|
+
"where",
|
|
4202
|
+
`connectOrCreate on "${ctx.model}.${ctx.fieldName}" requires "where" object`
|
|
4203
|
+
);
|
|
4204
|
+
const create = requireNestedObject(
|
|
4205
|
+
cfg,
|
|
4206
|
+
"create",
|
|
4207
|
+
`connectOrCreate on "${ctx.model}.${ctx.fieldName}" requires "create" object`
|
|
4208
|
+
);
|
|
4209
|
+
const whereSchema = buildUniqueSelector(ctx, where, "connectOrCreate.where");
|
|
4210
|
+
const createSchema = buildNestedData(ctx, create, "create");
|
|
4211
|
+
const cocSchema = z10.object({ where: whereSchema, create: createSchema }).strict();
|
|
4212
|
+
return wrapRelationOp(ctx.isList, cocSchema);
|
|
4213
|
+
};
|
|
4214
|
+
var handleCreate = (ctx) => {
|
|
4215
|
+
const cfg = requirePlainObjectConfig(
|
|
4216
|
+
ctx.config,
|
|
4217
|
+
`create config on "${ctx.model}.${ctx.fieldName}" must be an object of field names`
|
|
4218
|
+
);
|
|
4219
|
+
const createSchema = buildNestedData(ctx, cfg, "create");
|
|
4220
|
+
return wrapRelationOp(ctx.isList, createSchema);
|
|
4221
|
+
};
|
|
4222
|
+
var handleCreateMany = (ctx) => {
|
|
4223
|
+
if (!ctx.isList) {
|
|
3958
4224
|
throw new ShapeError(
|
|
3959
|
-
`
|
|
4225
|
+
`createMany is only valid on to-many relations ("${ctx.model}.${ctx.fieldName}")`
|
|
3960
4226
|
);
|
|
3961
|
-
for (const key of Object.keys(config)) {
|
|
3962
|
-
if (!KNOWN_RELATION_WRITE_OPS.has(key)) {
|
|
3963
|
-
throw new ShapeError(
|
|
3964
|
-
`Unknown relation write operation "${key}" on "${model}.${fieldName}". Allowed: ${[...KNOWN_RELATION_WRITE_OPS].join(", ")}`
|
|
3965
|
-
);
|
|
3966
|
-
}
|
|
3967
4227
|
}
|
|
3968
|
-
const
|
|
3969
|
-
|
|
3970
|
-
|
|
4228
|
+
const cfg = requirePlainObjectConfig(
|
|
4229
|
+
ctx.config,
|
|
4230
|
+
`createMany config on "${ctx.model}.${ctx.fieldName}" must be an object`
|
|
4231
|
+
);
|
|
4232
|
+
validateRelationOpKeys(cfg, "createMany", ctx.model, ctx.fieldName, "createMany");
|
|
4233
|
+
const data = requireNestedObject(
|
|
4234
|
+
cfg,
|
|
4235
|
+
"data",
|
|
4236
|
+
`createMany on "${ctx.model}.${ctx.fieldName}" requires "data" object`
|
|
4237
|
+
);
|
|
4238
|
+
const dataSchema = buildNestedData(ctx, data, "create");
|
|
4239
|
+
const cmSchemaFields = {
|
|
4240
|
+
data: z10.preprocess(coerceToArray, z10.array(dataSchema))
|
|
4241
|
+
};
|
|
4242
|
+
if ("skipDuplicates" in cfg) {
|
|
4243
|
+
cmSchemaFields["skipDuplicates"] = z10.boolean().optional();
|
|
4244
|
+
}
|
|
4245
|
+
return z10.object(cmSchemaFields).strict().optional();
|
|
4246
|
+
};
|
|
4247
|
+
var handleDisconnect = (ctx) => {
|
|
4248
|
+
if (ctx.config === true) {
|
|
4249
|
+
if (ctx.isList) {
|
|
3971
4250
|
throw new ShapeError(
|
|
3972
|
-
`
|
|
4251
|
+
`disconnect on to-many relation "${ctx.model}.${ctx.fieldName}" requires unique selector config, not true`
|
|
3973
4252
|
);
|
|
3974
4253
|
}
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
4254
|
+
return z10.literal(true).optional();
|
|
4255
|
+
}
|
|
4256
|
+
if (!isPlainObject(ctx.config)) {
|
|
4257
|
+
throw new ShapeError(
|
|
4258
|
+
`disconnect config on "${ctx.model}.${ctx.fieldName}" must be true (to-one) or an object of unique selectors`
|
|
3980
4259
|
);
|
|
3981
|
-
opSchemas["connect"] = isList ? z8.union([
|
|
3982
|
-
connectSchema,
|
|
3983
|
-
z8.preprocess(coerceToArray, z8.array(connectSchema))
|
|
3984
|
-
]).optional() : connectSchema.optional();
|
|
3985
4260
|
}
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
if (
|
|
3994
|
-
throw new ShapeError(
|
|
3995
|
-
`connectOrCreate on "${model}.${fieldName}" requires "where" object`
|
|
3996
|
-
);
|
|
3997
|
-
}
|
|
3998
|
-
if (!coc.create || !isPlainObject(coc.create)) {
|
|
4261
|
+
const schema = buildUniqueSelector(ctx, ctx.config, "disconnect");
|
|
4262
|
+
if (ctx.isList)
|
|
4263
|
+
return wrapRelationOp(true, schema);
|
|
4264
|
+
return z10.union([z10.literal(true), schema]).optional();
|
|
4265
|
+
};
|
|
4266
|
+
var handleDelete = (ctx) => {
|
|
4267
|
+
if (ctx.config === true) {
|
|
4268
|
+
if (ctx.isList) {
|
|
3999
4269
|
throw new ShapeError(
|
|
4000
|
-
`
|
|
4270
|
+
`delete on to-many relation "${ctx.model}.${ctx.fieldName}" requires unique selector config, not true`
|
|
4001
4271
|
);
|
|
4002
4272
|
}
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
);
|
|
4009
|
-
const createSchema = buildNestedDataSchema(
|
|
4010
|
-
relatedModelName,
|
|
4011
|
-
coc.create,
|
|
4012
|
-
typeMap,
|
|
4013
|
-
schemaBuilder
|
|
4273
|
+
return z10.literal(true).optional();
|
|
4274
|
+
}
|
|
4275
|
+
if (!isPlainObject(ctx.config)) {
|
|
4276
|
+
throw new ShapeError(
|
|
4277
|
+
`delete config on "${ctx.model}.${ctx.fieldName}" must be true (to-one) or an object of unique selectors`
|
|
4014
4278
|
);
|
|
4015
|
-
const cocSchema = z8.object({ where: whereSchema, create: createSchema }).strict();
|
|
4016
|
-
opSchemas["connectOrCreate"] = isList ? z8.union([cocSchema, z8.preprocess(coerceToArray, z8.array(cocSchema))]).optional() : cocSchema.optional();
|
|
4017
4279
|
}
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
typeMap,
|
|
4028
|
-
schemaBuilder
|
|
4280
|
+
const schema = buildUniqueSelector(ctx, ctx.config, "delete");
|
|
4281
|
+
if (ctx.isList)
|
|
4282
|
+
return wrapRelationOp(true, schema);
|
|
4283
|
+
return z10.union([z10.literal(true), schema]).optional();
|
|
4284
|
+
};
|
|
4285
|
+
var handleSet = (ctx) => {
|
|
4286
|
+
if (!ctx.isList) {
|
|
4287
|
+
throw new ShapeError(
|
|
4288
|
+
`set is only valid on to-many relations ("${ctx.model}.${ctx.fieldName}")`
|
|
4029
4289
|
);
|
|
4030
|
-
opSchemas["create"] = isList ? z8.union([
|
|
4031
|
-
createSchema,
|
|
4032
|
-
z8.preprocess(coerceToArray, z8.array(createSchema))
|
|
4033
|
-
]).optional() : createSchema.optional();
|
|
4034
4290
|
}
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
}
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
relatedModelName,
|
|
4054
|
-
cmConfig.data,
|
|
4055
|
-
typeMap,
|
|
4056
|
-
schemaBuilder
|
|
4291
|
+
const cfg = requirePlainObjectConfig(
|
|
4292
|
+
ctx.config,
|
|
4293
|
+
`set config on "${ctx.model}.${ctx.fieldName}" must be an object of unique selectors`
|
|
4294
|
+
);
|
|
4295
|
+
const schema = buildUniqueSelector(ctx, cfg, "set");
|
|
4296
|
+
return wrapRelationOp(true, schema);
|
|
4297
|
+
};
|
|
4298
|
+
var handleUpdate = (ctx) => {
|
|
4299
|
+
const cfg = requirePlainObjectConfig(
|
|
4300
|
+
ctx.config,
|
|
4301
|
+
`update config on "${ctx.model}.${ctx.fieldName}" must be an object`
|
|
4302
|
+
);
|
|
4303
|
+
if (ctx.isList) {
|
|
4304
|
+
validateRelationOpKeys(cfg, "update.toMany", ctx.model, ctx.fieldName, "update");
|
|
4305
|
+
const where = requireNestedObject(
|
|
4306
|
+
cfg,
|
|
4307
|
+
"where",
|
|
4308
|
+
`update on to-many "${ctx.model}.${ctx.fieldName}" requires "where" object`
|
|
4057
4309
|
);
|
|
4058
|
-
const
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4310
|
+
const data = requireNestedObject(
|
|
4311
|
+
cfg,
|
|
4312
|
+
"data",
|
|
4313
|
+
`update on to-many "${ctx.model}.${ctx.fieldName}" requires "data" object`
|
|
4314
|
+
);
|
|
4315
|
+
const whereSchema = buildUniqueSelector(ctx, where, "update.where");
|
|
4316
|
+
const dataSchema2 = buildNestedData(ctx, data, "update");
|
|
4317
|
+
const updateSchema = z10.object({ where: whereSchema, data: dataSchema2 }).strict();
|
|
4318
|
+
return wrapRelationOp(true, updateSchema);
|
|
4065
4319
|
}
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
}
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4320
|
+
const dataSchema = buildNestedData(ctx, cfg, "update");
|
|
4321
|
+
return dataSchema.optional();
|
|
4322
|
+
};
|
|
4323
|
+
var handleUpsert = (ctx) => {
|
|
4324
|
+
const cfg = requirePlainObjectConfig(
|
|
4325
|
+
ctx.config,
|
|
4326
|
+
`upsert config on "${ctx.model}.${ctx.fieldName}" must be an object`
|
|
4327
|
+
);
|
|
4328
|
+
validateRelationOpKeys(
|
|
4329
|
+
cfg,
|
|
4330
|
+
ctx.isList ? "upsert.toMany" : "upsert.toOne",
|
|
4331
|
+
ctx.model,
|
|
4332
|
+
ctx.fieldName,
|
|
4333
|
+
"upsert"
|
|
4334
|
+
);
|
|
4335
|
+
const create = requireNestedObject(
|
|
4336
|
+
cfg,
|
|
4337
|
+
"create",
|
|
4338
|
+
`upsert on "${ctx.model}.${ctx.fieldName}" requires "create" object`
|
|
4339
|
+
);
|
|
4340
|
+
const update = requireNestedObject(
|
|
4341
|
+
cfg,
|
|
4342
|
+
"update",
|
|
4343
|
+
`upsert on "${ctx.model}.${ctx.fieldName}" requires "update" object`
|
|
4344
|
+
);
|
|
4345
|
+
const createSchema = buildNestedData(ctx, create, "create");
|
|
4346
|
+
const updateSchema = buildNestedData(ctx, update, "update");
|
|
4347
|
+
if (ctx.isList) {
|
|
4348
|
+
const where = requireNestedObject(
|
|
4349
|
+
cfg,
|
|
4350
|
+
"where",
|
|
4351
|
+
`upsert on to-many "${ctx.model}.${ctx.fieldName}" requires "where" object`
|
|
4352
|
+
);
|
|
4353
|
+
const whereSchema = buildUniqueSelector(ctx, where, "upsert.where");
|
|
4354
|
+
const upsertSchema2 = z10.object({ where: whereSchema, create: createSchema, update: updateSchema }).strict();
|
|
4355
|
+
return wrapRelationOp(true, upsertSchema2);
|
|
4094
4356
|
}
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
throw new ShapeError(
|
|
4099
|
-
`delete on to-many relation "${model}.${fieldName}" requires field config, not true`
|
|
4100
|
-
);
|
|
4101
|
-
}
|
|
4102
|
-
opSchemas["delete"] = z8.literal(true).optional();
|
|
4103
|
-
} else if (isPlainObject(config.delete)) {
|
|
4104
|
-
const deleteSchema = buildWhereFieldsSchema(
|
|
4105
|
-
relatedModelName,
|
|
4106
|
-
config.delete,
|
|
4107
|
-
typeMap,
|
|
4108
|
-
schemaBuilder
|
|
4109
|
-
);
|
|
4110
|
-
if (isList) {
|
|
4111
|
-
opSchemas["delete"] = z8.union([
|
|
4112
|
-
deleteSchema,
|
|
4113
|
-
z8.preprocess(coerceToArray, z8.array(deleteSchema))
|
|
4114
|
-
]).optional();
|
|
4115
|
-
} else {
|
|
4116
|
-
opSchemas["delete"] = z8.union([z8.literal(true), deleteSchema]).optional();
|
|
4117
|
-
}
|
|
4118
|
-
} else {
|
|
4357
|
+
const hasWhereKey = "where" in cfg;
|
|
4358
|
+
if (hasWhereKey) {
|
|
4359
|
+
if (!isPlainObject(cfg.where)) {
|
|
4119
4360
|
throw new ShapeError(
|
|
4120
|
-
`
|
|
4361
|
+
`upsert on to-one "${ctx.model}.${ctx.fieldName}" has invalid "where": must be a plain object of unique selectors`
|
|
4121
4362
|
);
|
|
4122
4363
|
}
|
|
4364
|
+
const whereSchema = buildUniqueSelector(ctx, cfg.where, "upsert.where");
|
|
4365
|
+
const upsertSchema2 = z10.object({ where: whereSchema, create: createSchema, update: updateSchema }).strict();
|
|
4366
|
+
return upsertSchema2.optional();
|
|
4123
4367
|
}
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
throw new ShapeError(
|
|
4132
|
-
`set config on "${model}.${fieldName}" must be an object of field names`
|
|
4133
|
-
);
|
|
4134
|
-
}
|
|
4135
|
-
const setSchema = buildWhereFieldsSchema(
|
|
4136
|
-
relatedModelName,
|
|
4137
|
-
config.set,
|
|
4138
|
-
typeMap,
|
|
4139
|
-
schemaBuilder
|
|
4368
|
+
const upsertSchema = z10.object({ create: createSchema, update: updateSchema }).strict();
|
|
4369
|
+
return upsertSchema.optional();
|
|
4370
|
+
};
|
|
4371
|
+
var handleUpdateMany = (ctx) => {
|
|
4372
|
+
if (!ctx.isList) {
|
|
4373
|
+
throw new ShapeError(
|
|
4374
|
+
`updateMany is only valid on to-many relations ("${ctx.model}.${ctx.fieldName}")`
|
|
4140
4375
|
);
|
|
4141
|
-
opSchemas["set"] = z8.preprocess(coerceToArray, z8.array(setSchema)).optional();
|
|
4142
4376
|
}
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
}
|
|
4156
|
-
if (!updateConfig.data || !isPlainObject(updateConfig.data)) {
|
|
4157
|
-
throw new ShapeError(
|
|
4158
|
-
`update on to-many "${model}.${fieldName}" requires "data" object`
|
|
4159
|
-
);
|
|
4160
|
-
}
|
|
4161
|
-
const whereSchema = buildWhereFieldsSchema(
|
|
4162
|
-
relatedModelName,
|
|
4163
|
-
updateConfig.where,
|
|
4164
|
-
typeMap,
|
|
4165
|
-
schemaBuilder
|
|
4166
|
-
);
|
|
4167
|
-
const dataSchema = buildNestedDataSchema(
|
|
4168
|
-
relatedModelName,
|
|
4169
|
-
updateConfig.data,
|
|
4170
|
-
typeMap,
|
|
4171
|
-
schemaBuilder
|
|
4172
|
-
);
|
|
4173
|
-
const updateSchema = z8.object({ where: whereSchema, data: dataSchema }).strict();
|
|
4174
|
-
opSchemas["update"] = z8.union([
|
|
4175
|
-
updateSchema,
|
|
4176
|
-
z8.preprocess(coerceToArray, z8.array(updateSchema))
|
|
4177
|
-
]).optional();
|
|
4178
|
-
} else {
|
|
4179
|
-
const dataSchema = buildNestedDataSchema(
|
|
4180
|
-
relatedModelName,
|
|
4181
|
-
updateConfig,
|
|
4182
|
-
typeMap,
|
|
4183
|
-
schemaBuilder
|
|
4184
|
-
);
|
|
4185
|
-
opSchemas["update"] = dataSchema.optional();
|
|
4186
|
-
}
|
|
4187
|
-
}
|
|
4188
|
-
if (config.upsert !== void 0) {
|
|
4189
|
-
if (!isPlainObject(config.upsert)) {
|
|
4190
|
-
throw new ShapeError(
|
|
4191
|
-
`upsert config on "${model}.${fieldName}" must be an object`
|
|
4192
|
-
);
|
|
4193
|
-
}
|
|
4194
|
-
const upsertConfig = config.upsert;
|
|
4195
|
-
if (!upsertConfig.create || !isPlainObject(upsertConfig.create)) {
|
|
4196
|
-
throw new ShapeError(
|
|
4197
|
-
`upsert on "${model}.${fieldName}" requires "create" object`
|
|
4198
|
-
);
|
|
4199
|
-
}
|
|
4200
|
-
if (!upsertConfig.update || !isPlainObject(upsertConfig.update)) {
|
|
4201
|
-
throw new ShapeError(
|
|
4202
|
-
`upsert on "${model}.${fieldName}" requires "update" object`
|
|
4203
|
-
);
|
|
4204
|
-
}
|
|
4205
|
-
const createSchema = buildNestedDataSchema(
|
|
4206
|
-
relatedModelName,
|
|
4207
|
-
upsertConfig.create,
|
|
4208
|
-
typeMap,
|
|
4209
|
-
schemaBuilder
|
|
4377
|
+
const cfg = requirePlainObjectConfig(
|
|
4378
|
+
ctx.config,
|
|
4379
|
+
`updateMany config on "${ctx.model}.${ctx.fieldName}" must be an object`
|
|
4380
|
+
);
|
|
4381
|
+
validateRelationOpKeys(cfg, "updateMany", ctx.model, ctx.fieldName, "updateMany");
|
|
4382
|
+
const where = requireNestedObject(
|
|
4383
|
+
cfg,
|
|
4384
|
+
"where",
|
|
4385
|
+
`updateMany on "${ctx.model}.${ctx.fieldName}" requires "where" object`
|
|
4386
|
+
);
|
|
4387
|
+
if (Object.keys(where).length === 0) {
|
|
4388
|
+
throw new ShapeError(
|
|
4389
|
+
`updateMany "where" on "${ctx.model}.${ctx.fieldName}" must define at least one filter field`
|
|
4210
4390
|
);
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4391
|
+
}
|
|
4392
|
+
const data = requireNestedObject(
|
|
4393
|
+
cfg,
|
|
4394
|
+
"data",
|
|
4395
|
+
`updateMany on "${ctx.model}.${ctx.fieldName}" requires "data" object`
|
|
4396
|
+
);
|
|
4397
|
+
if (Object.keys(data).length === 0) {
|
|
4398
|
+
throw new ShapeError(
|
|
4399
|
+
`updateMany "data" on "${ctx.model}.${ctx.fieldName}" must define at least one field`
|
|
4216
4400
|
);
|
|
4217
|
-
if (isList) {
|
|
4218
|
-
if (!upsertConfig.where || !isPlainObject(upsertConfig.where)) {
|
|
4219
|
-
throw new ShapeError(
|
|
4220
|
-
`upsert on to-many "${model}.${fieldName}" requires "where" object`
|
|
4221
|
-
);
|
|
4222
|
-
}
|
|
4223
|
-
const whereSchema = buildWhereFieldsSchema(
|
|
4224
|
-
relatedModelName,
|
|
4225
|
-
upsertConfig.where,
|
|
4226
|
-
typeMap,
|
|
4227
|
-
schemaBuilder
|
|
4228
|
-
);
|
|
4229
|
-
const upsertSchema = z8.object({
|
|
4230
|
-
where: whereSchema,
|
|
4231
|
-
create: createSchema,
|
|
4232
|
-
update: updateSchema
|
|
4233
|
-
}).strict();
|
|
4234
|
-
opSchemas["upsert"] = z8.union([
|
|
4235
|
-
upsertSchema,
|
|
4236
|
-
z8.preprocess(coerceToArray, z8.array(upsertSchema))
|
|
4237
|
-
]).optional();
|
|
4238
|
-
} else {
|
|
4239
|
-
if (upsertConfig.where) {
|
|
4240
|
-
const whereSchema = buildWhereFieldsSchema(
|
|
4241
|
-
relatedModelName,
|
|
4242
|
-
upsertConfig.where,
|
|
4243
|
-
typeMap,
|
|
4244
|
-
schemaBuilder
|
|
4245
|
-
);
|
|
4246
|
-
const upsertSchema = z8.object({
|
|
4247
|
-
where: whereSchema,
|
|
4248
|
-
create: createSchema,
|
|
4249
|
-
update: updateSchema
|
|
4250
|
-
}).strict();
|
|
4251
|
-
opSchemas["upsert"] = upsertSchema.optional();
|
|
4252
|
-
} else {
|
|
4253
|
-
const upsertSchema = z8.object({ create: createSchema, update: updateSchema }).strict();
|
|
4254
|
-
opSchemas["upsert"] = upsertSchema.optional();
|
|
4255
|
-
}
|
|
4256
|
-
}
|
|
4257
4401
|
}
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
`updateMany on "${model}.${fieldName}" requires "where" object`
|
|
4273
|
-
);
|
|
4274
|
-
}
|
|
4275
|
-
if (!umConfig.data || !isPlainObject(umConfig.data)) {
|
|
4276
|
-
throw new ShapeError(
|
|
4277
|
-
`updateMany on "${model}.${fieldName}" requires "data" object`
|
|
4278
|
-
);
|
|
4279
|
-
}
|
|
4280
|
-
const whereSchema = buildWhereFieldsSchema(
|
|
4281
|
-
relatedModelName,
|
|
4282
|
-
umConfig.where,
|
|
4283
|
-
typeMap,
|
|
4284
|
-
schemaBuilder
|
|
4402
|
+
const whereSchema = buildWhereFieldsSchema(
|
|
4403
|
+
ctx.relatedModelName,
|
|
4404
|
+
where,
|
|
4405
|
+
ctx.typeMap,
|
|
4406
|
+
ctx.schemaBuilder
|
|
4407
|
+
);
|
|
4408
|
+
const dataSchema = buildNestedData(ctx, data, "update");
|
|
4409
|
+
const umSchema = z10.object({ where: whereSchema, data: dataSchema }).strict();
|
|
4410
|
+
return wrapRelationOp(true, umSchema);
|
|
4411
|
+
};
|
|
4412
|
+
var handleDeleteMany = (ctx) => {
|
|
4413
|
+
if (!ctx.isList) {
|
|
4414
|
+
throw new ShapeError(
|
|
4415
|
+
`deleteMany is only valid on to-many relations ("${ctx.model}.${ctx.fieldName}")`
|
|
4285
4416
|
);
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4417
|
+
}
|
|
4418
|
+
const cfg = requirePlainObjectConfig(
|
|
4419
|
+
ctx.config,
|
|
4420
|
+
`deleteMany config on "${ctx.model}.${ctx.fieldName}" must be an object of allowed filter fields`
|
|
4421
|
+
);
|
|
4422
|
+
if (Object.keys(cfg).length === 0) {
|
|
4423
|
+
throw new ShapeError(
|
|
4424
|
+
`deleteMany config on "${ctx.model}.${ctx.fieldName}" is empty. Unconstrained nested deletes are not allowed. Define at least one allowed filter field.`
|
|
4291
4425
|
);
|
|
4292
|
-
const umSchema = z8.object({ where: whereSchema, data: dataSchema }).strict();
|
|
4293
|
-
opSchemas["updateMany"] = z8.union([umSchema, z8.preprocess(coerceToArray, z8.array(umSchema))]).optional();
|
|
4294
4426
|
}
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4427
|
+
const filterSchema = buildWhereFieldsSchema(
|
|
4428
|
+
ctx.relatedModelName,
|
|
4429
|
+
cfg,
|
|
4430
|
+
ctx.typeMap,
|
|
4431
|
+
ctx.schemaBuilder
|
|
4432
|
+
);
|
|
4433
|
+
return wrapRelationOp(true, filterSchema);
|
|
4434
|
+
};
|
|
4435
|
+
var RELATION_OP_HANDLERS = {
|
|
4436
|
+
connect: handleConnect,
|
|
4437
|
+
connectOrCreate: handleConnectOrCreate,
|
|
4438
|
+
create: handleCreate,
|
|
4439
|
+
createMany: handleCreateMany,
|
|
4440
|
+
disconnect: handleDisconnect,
|
|
4441
|
+
delete: handleDelete,
|
|
4442
|
+
set: handleSet,
|
|
4443
|
+
update: handleUpdate,
|
|
4444
|
+
upsert: handleUpsert,
|
|
4445
|
+
updateMany: handleUpdateMany,
|
|
4446
|
+
deleteMany: handleDeleteMany
|
|
4447
|
+
};
|
|
4448
|
+
var KNOWN_RELATION_WRITE_OPS = new Set(Object.keys(RELATION_OP_HANDLERS));
|
|
4449
|
+
function buildRelationWriteSchema(model, fieldName, relatedModelName, isList, config, typeMap, uniqueMap, enumMap, scalarBase, schemaBuilder) {
|
|
4450
|
+
const relatedFields = typeMap[relatedModelName];
|
|
4451
|
+
if (!relatedFields)
|
|
4452
|
+
throw new ShapeError(
|
|
4453
|
+
`Unknown related model "${relatedModelName}" for field "${model}.${fieldName}"`
|
|
4454
|
+
);
|
|
4455
|
+
for (const key of Object.keys(config)) {
|
|
4456
|
+
if (!KNOWN_RELATION_WRITE_OPS.has(key)) {
|
|
4302
4457
|
throw new ShapeError(
|
|
4303
|
-
`
|
|
4458
|
+
`Unknown relation write operation "${key}" on "${model}.${fieldName}". Allowed: ${[...KNOWN_RELATION_WRITE_OPS].join(", ")}`
|
|
4304
4459
|
);
|
|
4305
4460
|
}
|
|
4306
|
-
|
|
4461
|
+
}
|
|
4462
|
+
const opSchemas = {};
|
|
4463
|
+
for (const [op, opConfig] of Object.entries(config)) {
|
|
4464
|
+
if (opConfig === void 0)
|
|
4465
|
+
continue;
|
|
4466
|
+
const handler = RELATION_OP_HANDLERS[op];
|
|
4467
|
+
opSchemas[op] = handler({
|
|
4468
|
+
model,
|
|
4469
|
+
fieldName,
|
|
4307
4470
|
relatedModelName,
|
|
4308
|
-
|
|
4471
|
+
isList,
|
|
4472
|
+
config: opConfig,
|
|
4309
4473
|
typeMap,
|
|
4474
|
+
uniqueMap,
|
|
4475
|
+
enumMap,
|
|
4476
|
+
scalarBase,
|
|
4310
4477
|
schemaBuilder
|
|
4311
|
-
);
|
|
4312
|
-
opSchemas["deleteMany"] = z8.union([
|
|
4313
|
-
filterSchema,
|
|
4314
|
-
z8.preprocess(coerceToArray, z8.array(filterSchema)),
|
|
4315
|
-
z8.object({}).strict()
|
|
4316
|
-
]).optional();
|
|
4478
|
+
});
|
|
4317
4479
|
}
|
|
4318
|
-
return
|
|
4480
|
+
return z10.object(opSchemas).strict();
|
|
4319
4481
|
}
|
|
4320
|
-
function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDefaults) {
|
|
4482
|
+
function buildDataSchema(model, dataConfig, mode, typeMap, uniqueMap, enumMap, scalarBase, schemaBuilder, zodDefaults) {
|
|
4321
4483
|
const modelFields = typeMap[model];
|
|
4322
4484
|
if (!modelFields)
|
|
4323
4485
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -4329,11 +4491,23 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4329
4491
|
const fieldMeta = modelFields[fieldName];
|
|
4330
4492
|
if (!fieldMeta) {
|
|
4331
4493
|
if (isUnsupportedMarker(value)) {
|
|
4332
|
-
schemaMap[fieldName] = z8.unknown().optional();
|
|
4333
4494
|
continue;
|
|
4334
4495
|
}
|
|
4335
4496
|
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
4336
4497
|
}
|
|
4498
|
+
if (fieldMeta.isUnsupported) {
|
|
4499
|
+
if (isUnsupportedMarker(value)) {
|
|
4500
|
+
continue;
|
|
4501
|
+
}
|
|
4502
|
+
if (value === true || typeof value === "function") {
|
|
4503
|
+
throw new ShapeError(
|
|
4504
|
+
`Field "${fieldName}" on model "${model}" has an Unsupported type and cannot be client-controlled. Use unsupported() to acknowledge it or a forced server value.`
|
|
4505
|
+
);
|
|
4506
|
+
}
|
|
4507
|
+
const actualValue = isForcedValue(value) ? value.value : value;
|
|
4508
|
+
forced[fieldName] = deepClone(actualValue);
|
|
4509
|
+
continue;
|
|
4510
|
+
}
|
|
4337
4511
|
if (fieldMeta.isRelation) {
|
|
4338
4512
|
if (!isPlainObject(value)) {
|
|
4339
4513
|
throw new ShapeError(
|
|
@@ -4347,6 +4521,9 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4347
4521
|
fieldMeta.isList,
|
|
4348
4522
|
value,
|
|
4349
4523
|
typeMap,
|
|
4524
|
+
uniqueMap,
|
|
4525
|
+
enumMap,
|
|
4526
|
+
scalarBase,
|
|
4350
4527
|
schemaBuilder
|
|
4351
4528
|
).optional();
|
|
4352
4529
|
continue;
|
|
@@ -4356,7 +4533,7 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4356
4533
|
`updatedAt field "${fieldName}" cannot be used in data shape`
|
|
4357
4534
|
);
|
|
4358
4535
|
if (typeof value === "function") {
|
|
4359
|
-
|
|
4536
|
+
const baseSchema = schemaBuilder.buildBaseFieldSchema(
|
|
4360
4537
|
model,
|
|
4361
4538
|
fieldName
|
|
4362
4539
|
);
|
|
@@ -4374,46 +4551,22 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4374
4551
|
`Inline refine for "${model}.${fieldName}" must return a Zod schema`
|
|
4375
4552
|
);
|
|
4376
4553
|
}
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
fieldSchema = fieldSchema.optional();
|
|
4385
|
-
}
|
|
4386
|
-
}
|
|
4387
|
-
} else {
|
|
4388
|
-
if (!fieldMeta.isRequired) {
|
|
4389
|
-
fieldSchema = fieldSchema.nullable().optional();
|
|
4390
|
-
} else {
|
|
4391
|
-
fieldSchema = fieldSchema.optional();
|
|
4392
|
-
}
|
|
4393
|
-
}
|
|
4394
|
-
schemaMap[fieldName] = fieldSchema;
|
|
4395
|
-
} else if (value === true) {
|
|
4396
|
-
let fieldSchema = schemaBuilder.buildFieldSchema(
|
|
4397
|
-
model,
|
|
4398
|
-
fieldName
|
|
4554
|
+
const handlesUndefined = schemaProducesValueForUndefined(
|
|
4555
|
+
refined
|
|
4556
|
+
);
|
|
4557
|
+
schemaMap[fieldName] = applyCreateUpdateNullability(
|
|
4558
|
+
fieldMeta,
|
|
4559
|
+
refined,
|
|
4560
|
+
{ mode, handlesUndefined }
|
|
4399
4561
|
);
|
|
4562
|
+
} else if (value === true) {
|
|
4563
|
+
const fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
|
|
4400
4564
|
const isZodDefaultField = zodDefaultSet !== void 0 && zodDefaultSet.has(fieldName);
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
fieldSchema = fieldSchema.optional();
|
|
4407
|
-
}
|
|
4408
|
-
}
|
|
4409
|
-
} else {
|
|
4410
|
-
if (!fieldMeta.isRequired) {
|
|
4411
|
-
fieldSchema = fieldSchema.nullable().optional();
|
|
4412
|
-
} else {
|
|
4413
|
-
fieldSchema = fieldSchema.optional();
|
|
4414
|
-
}
|
|
4415
|
-
}
|
|
4416
|
-
schemaMap[fieldName] = fieldSchema;
|
|
4565
|
+
schemaMap[fieldName] = applyCreateUpdateNullability(
|
|
4566
|
+
fieldMeta,
|
|
4567
|
+
fieldSchema,
|
|
4568
|
+
{ mode, handlesUndefined: isZodDefaultField }
|
|
4569
|
+
);
|
|
4417
4570
|
} else {
|
|
4418
4571
|
const actualValue = isForcedValue(value) ? value.value : value;
|
|
4419
4572
|
let fieldSchema = schemaBuilder.buildFieldSchema(
|
|
@@ -4457,7 +4610,7 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4457
4610
|
}
|
|
4458
4611
|
}
|
|
4459
4612
|
return {
|
|
4460
|
-
schema:
|
|
4613
|
+
schema: z10.object(schemaMap).strict(),
|
|
4461
4614
|
forced
|
|
4462
4615
|
};
|
|
4463
4616
|
}
|
|
@@ -4494,106 +4647,120 @@ function isGuardShape(obj) {
|
|
|
4494
4647
|
const keys = Object.keys(obj);
|
|
4495
4648
|
return keys.length === 0 || keys.every((k) => GUARD_SHAPE_KEYS.has(k));
|
|
4496
4649
|
}
|
|
4497
|
-
function
|
|
4498
|
-
|
|
4499
|
-
}
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
return value;
|
|
4503
|
-
if (Array.isArray(value))
|
|
4504
|
-
return value.map(toPlainObject);
|
|
4505
|
-
if (value instanceof Date)
|
|
4506
|
-
return value;
|
|
4507
|
-
if (value instanceof Uint8Array)
|
|
4508
|
-
return value;
|
|
4509
|
-
if (value instanceof RegExp)
|
|
4510
|
-
return value;
|
|
4511
|
-
if (typeof value.toFixed === "function" && typeof value.toNumber === "function")
|
|
4512
|
-
return value;
|
|
4513
|
-
const result = {};
|
|
4514
|
-
for (const [k, v] of Object.entries(value)) {
|
|
4515
|
-
result[k] = toPlainObject(v);
|
|
4650
|
+
function requireBody(body) {
|
|
4651
|
+
if (body === void 0 || body === null)
|
|
4652
|
+
return {};
|
|
4653
|
+
if (!isPlainObject(body)) {
|
|
4654
|
+
throw new ShapeError("Request body must be a plain object");
|
|
4516
4655
|
}
|
|
4517
|
-
return
|
|
4656
|
+
return body;
|
|
4518
4657
|
}
|
|
4519
|
-
function
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4658
|
+
function assertNoCallerInBody(body) {
|
|
4659
|
+
if ("caller" in body) {
|
|
4660
|
+
throw new CallerError(
|
|
4661
|
+
"Pass caller via the guard(input, caller) argument, not in the request body."
|
|
4662
|
+
);
|
|
4663
|
+
}
|
|
4524
4664
|
}
|
|
4525
|
-
function resolveDynamicShape(
|
|
4526
|
-
|
|
4665
|
+
function resolveDynamicShape(shapeFn, ctx, context) {
|
|
4666
|
+
if (ctx === void 0) {
|
|
4667
|
+
throw new ShapeError(
|
|
4668
|
+
`Dynamic ${context} requires a context. Provide contextFn on the extension.`
|
|
4669
|
+
);
|
|
4670
|
+
}
|
|
4527
4671
|
let result;
|
|
4528
4672
|
try {
|
|
4529
|
-
result =
|
|
4673
|
+
result = shapeFn(ctx);
|
|
4530
4674
|
} catch (err) {
|
|
4531
4675
|
throw new ShapeError(
|
|
4532
|
-
`Dynamic
|
|
4676
|
+
`Dynamic ${context} function threw: ${err.message}`,
|
|
4533
4677
|
{ cause: err }
|
|
4534
4678
|
);
|
|
4535
4679
|
}
|
|
4536
|
-
if (!
|
|
4537
|
-
throw new ShapeError(
|
|
4680
|
+
if (!isGuardShape(result)) {
|
|
4681
|
+
throw new ShapeError(
|
|
4682
|
+
`Dynamic ${context} function must return a valid guard shape object`
|
|
4683
|
+
);
|
|
4538
4684
|
}
|
|
4539
4685
|
return result;
|
|
4540
4686
|
}
|
|
4541
|
-
function
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
return { shape: shape2, body: parsed2, matchedKey: "_default", wasDynamic: wasDynamic2 };
|
|
4547
|
-
}
|
|
4548
|
-
const namedMap = input;
|
|
4549
|
-
for (const key of Object.keys(namedMap)) {
|
|
4687
|
+
function resolveNamedShape(input, body, contextFn, explicitCaller) {
|
|
4688
|
+
assertNoCallerInBody(body);
|
|
4689
|
+
const caller = explicitCaller;
|
|
4690
|
+
const keys = Object.keys(input);
|
|
4691
|
+
for (const key of keys) {
|
|
4550
4692
|
if (GUARD_SHAPE_KEYS.has(key)) {
|
|
4551
4693
|
throw new ShapeError(
|
|
4552
|
-
`Caller key "${key}" collides with reserved shape
|
|
4553
|
-
);
|
|
4554
|
-
}
|
|
4555
|
-
const val = namedMap[key];
|
|
4556
|
-
if (typeof val !== "function" && !isGuardShape(val)) {
|
|
4557
|
-
throw new ShapeError(
|
|
4558
|
-
`Named shape value for "${key}" must be a guard shape object or function`
|
|
4694
|
+
`Caller key "${key}" collides with reserved guard shape key. Rename the caller path.`
|
|
4559
4695
|
);
|
|
4560
4696
|
}
|
|
4561
4697
|
}
|
|
4562
|
-
const parsed = body === void 0 || body === null ? {} : requireBody(body);
|
|
4563
|
-
if ("caller" in parsed) {
|
|
4564
|
-
throw new CallerError(
|
|
4565
|
-
"Pass caller as second argument to .guard() or via context function, not in the request body."
|
|
4566
|
-
);
|
|
4567
|
-
}
|
|
4568
4698
|
if (typeof caller !== "string") {
|
|
4569
|
-
if ("default" in
|
|
4570
|
-
|
|
4571
|
-
const wasDynamic2 = typeof shapeOrFn2 === "function";
|
|
4572
|
-
const shape2 = wasDynamic2 ? resolveDynamicShape(shapeOrFn2, contextFn) : shapeOrFn2;
|
|
4573
|
-
return { shape: shape2, body: parsed, matchedKey: "default", wasDynamic: wasDynamic2 };
|
|
4699
|
+
if ("default" in input) {
|
|
4700
|
+
return resolveShapeEntry(input.default, body, contextFn, "default");
|
|
4574
4701
|
}
|
|
4575
|
-
const patterns2 = Object.keys(namedMap);
|
|
4576
4702
|
throw new CallerError(
|
|
4577
|
-
`Missing caller. This guard uses named shape routing with keys: ${
|
|
4703
|
+
`Missing caller. This guard uses named shape routing with keys: ${keys.map((k) => `"${k}"`).join(", ")}. Provide caller via guard(input, caller).`
|
|
4578
4704
|
);
|
|
4579
4705
|
}
|
|
4580
|
-
const
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
}
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4706
|
+
const matched = matchCallerPattern(keys, caller);
|
|
4707
|
+
if (matched) {
|
|
4708
|
+
return resolveShapeEntry(input[matched], body, contextFn, matched);
|
|
4709
|
+
}
|
|
4710
|
+
if ("default" in input) {
|
|
4711
|
+
return resolveShapeEntry(input.default, body, contextFn, "default");
|
|
4712
|
+
}
|
|
4713
|
+
throw new CallerError(
|
|
4714
|
+
`Unknown caller: "${caller}". Allowed: ${keys.map((k) => `"${k}"`).join(", ")}`
|
|
4715
|
+
);
|
|
4716
|
+
}
|
|
4717
|
+
function resolveShapeEntry(entry, body, contextFn, matchedKey) {
|
|
4718
|
+
if (typeof entry === "function") {
|
|
4719
|
+
const ctx = validateContext(contextFn());
|
|
4720
|
+
const shape = resolveDynamicShape(entry, ctx, `shape "${matchedKey}"`);
|
|
4721
|
+
return {
|
|
4722
|
+
shape,
|
|
4723
|
+
body,
|
|
4724
|
+
matchedKey,
|
|
4725
|
+
wasDynamic: true
|
|
4726
|
+
};
|
|
4727
|
+
}
|
|
4728
|
+
return {
|
|
4729
|
+
shape: entry,
|
|
4730
|
+
body,
|
|
4731
|
+
matchedKey,
|
|
4732
|
+
wasDynamic: false
|
|
4733
|
+
};
|
|
4734
|
+
}
|
|
4735
|
+
function resolveShape(input, rawBody, contextFn, explicitCaller) {
|
|
4736
|
+
const body = requireBody(rawBody);
|
|
4737
|
+
if (typeof input === "function") {
|
|
4738
|
+
const ctx = validateContext(contextFn());
|
|
4739
|
+
const shape = resolveDynamicShape(input, ctx, "shape");
|
|
4740
|
+
return {
|
|
4741
|
+
shape,
|
|
4742
|
+
body,
|
|
4743
|
+
matchedKey: "_default",
|
|
4744
|
+
wasDynamic: true
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
if (isGuardShape(input)) {
|
|
4748
|
+
return {
|
|
4749
|
+
shape: input,
|
|
4750
|
+
body,
|
|
4751
|
+
matchedKey: "_default",
|
|
4752
|
+
wasDynamic: false
|
|
4753
|
+
};
|
|
4592
4754
|
}
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
return
|
|
4755
|
+
if (!isPlainObject(input)) {
|
|
4756
|
+
throw new ShapeError("Guard input must be a shape object or a named map of shapes");
|
|
4757
|
+
}
|
|
4758
|
+
return resolveNamedShape(
|
|
4759
|
+
input,
|
|
4760
|
+
body,
|
|
4761
|
+
contextFn,
|
|
4762
|
+
explicitCaller
|
|
4763
|
+
);
|
|
4597
4764
|
}
|
|
4598
4765
|
|
|
4599
4766
|
// src/runtime/model-guard.ts
|
|
@@ -4616,129 +4783,20 @@ var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
|
4616
4783
|
"updateManyAndReturn"
|
|
4617
4784
|
]);
|
|
4618
4785
|
var BATCH_CREATE_METHODS = /* @__PURE__ */ new Set(["createMany", "createManyAndReturn"]);
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
);
|
|
4626
|
-
continue;
|
|
4627
|
-
}
|
|
4628
|
-
if (value === true) {
|
|
4629
|
-
result[key] = true;
|
|
4630
|
-
} else {
|
|
4631
|
-
const nested = {};
|
|
4632
|
-
if (value.select)
|
|
4633
|
-
nested.select = buildDefaultSelectInput(value.select);
|
|
4634
|
-
if (value.include) {
|
|
4635
|
-
nested.include = buildDefaultIncludeInput(value.include);
|
|
4636
|
-
}
|
|
4637
|
-
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
4638
|
-
}
|
|
4639
|
-
}
|
|
4640
|
-
return result;
|
|
4641
|
-
}
|
|
4642
|
-
function buildDefaultIncludeInput(config) {
|
|
4643
|
-
const result = {};
|
|
4644
|
-
for (const [key, value] of Object.entries(config)) {
|
|
4645
|
-
if (key === "_count") {
|
|
4646
|
-
result[key] = buildDefaultCountInput(
|
|
4647
|
-
value
|
|
4648
|
-
);
|
|
4649
|
-
continue;
|
|
4650
|
-
}
|
|
4651
|
-
if (value === true) {
|
|
4652
|
-
result[key] = true;
|
|
4653
|
-
} else {
|
|
4654
|
-
const nested = {};
|
|
4655
|
-
if (value.include) {
|
|
4656
|
-
nested.include = buildDefaultIncludeInput(value.include);
|
|
4657
|
-
}
|
|
4658
|
-
if (value.select)
|
|
4659
|
-
nested.select = buildDefaultSelectInput(value.select);
|
|
4660
|
-
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
4661
|
-
}
|
|
4662
|
-
}
|
|
4663
|
-
return result;
|
|
4664
|
-
}
|
|
4665
|
-
function buildDefaultCountInput(config) {
|
|
4666
|
-
if (config === true)
|
|
4667
|
-
return true;
|
|
4668
|
-
if (!isPlainObject(config) || !config.select || !isPlainObject(config.select)) {
|
|
4669
|
-
return true;
|
|
4670
|
-
}
|
|
4671
|
-
const selectObj = config.select;
|
|
4672
|
-
const result = {};
|
|
4673
|
-
for (const key of Object.keys(selectObj)) {
|
|
4674
|
-
result[key] = true;
|
|
4675
|
-
}
|
|
4676
|
-
return { select: result };
|
|
4677
|
-
}
|
|
4678
|
-
function buildDefaultProjectionBody(shape) {
|
|
4679
|
-
if (shape.select) {
|
|
4680
|
-
return { select: buildDefaultSelectInput(shape.select) };
|
|
4681
|
-
}
|
|
4682
|
-
if (shape.include) {
|
|
4683
|
-
return { include: buildDefaultIncludeInput(shape.include) };
|
|
4684
|
-
}
|
|
4685
|
-
return {};
|
|
4686
|
-
}
|
|
4687
|
-
function applyClientProjectionOverrides(shapeDefault, clientProjection) {
|
|
4688
|
-
const result = { ...shapeDefault };
|
|
4689
|
-
for (const [key, clientValue] of Object.entries(clientProjection)) {
|
|
4690
|
-
const defaultValue = result[key];
|
|
4691
|
-
if (defaultValue === void 0) {
|
|
4692
|
-
result[key] = clientValue;
|
|
4693
|
-
continue;
|
|
4694
|
-
}
|
|
4695
|
-
if (clientValue === true) {
|
|
4696
|
-
result[key] = true;
|
|
4697
|
-
continue;
|
|
4698
|
-
}
|
|
4699
|
-
if (isPlainObject(clientValue)) {
|
|
4700
|
-
if (!isPlainObject(defaultValue)) {
|
|
4701
|
-
result[key] = clientValue;
|
|
4702
|
-
continue;
|
|
4703
|
-
}
|
|
4704
|
-
const merged = { ...defaultValue };
|
|
4705
|
-
const clientObj = clientValue;
|
|
4706
|
-
for (const [argKey, argValue] of Object.entries(clientObj)) {
|
|
4707
|
-
const defaultArgValue = merged[argKey];
|
|
4708
|
-
if ((argKey === "select" || argKey === "include") && isPlainObject(argValue) && isPlainObject(defaultArgValue)) {
|
|
4709
|
-
merged[argKey] = applyClientProjectionOverrides(
|
|
4710
|
-
defaultArgValue,
|
|
4711
|
-
argValue
|
|
4712
|
-
);
|
|
4713
|
-
} else {
|
|
4714
|
-
merged[argKey] = argValue;
|
|
4715
|
-
}
|
|
4716
|
-
}
|
|
4717
|
-
result[key] = merged;
|
|
4718
|
-
}
|
|
4719
|
-
}
|
|
4720
|
-
return result;
|
|
4721
|
-
}
|
|
4722
|
-
function hasClientControlledValues(obj) {
|
|
4723
|
-
for (const value of Object.values(obj)) {
|
|
4724
|
-
if (isForcedValue(value))
|
|
4725
|
-
continue;
|
|
4726
|
-
if (value === true)
|
|
4727
|
-
return true;
|
|
4728
|
-
if (isPlainObject(value) && hasClientControlledValues(value))
|
|
4786
|
+
var MAX_PROJECTION_WALK_DEPTH = 10;
|
|
4787
|
+
function walkForClientContent(obj, predicate, depth) {
|
|
4788
|
+
if (depth > MAX_PROJECTION_WALK_DEPTH)
|
|
4789
|
+
return false;
|
|
4790
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
4791
|
+
if (predicate(value, depth))
|
|
4729
4792
|
return true;
|
|
4730
|
-
}
|
|
4731
|
-
return false;
|
|
4732
|
-
}
|
|
4733
|
-
function checkIncludeForClientArgs(config) {
|
|
4734
|
-
for (const [key, value] of Object.entries(config)) {
|
|
4735
4793
|
if (key === "_count") {
|
|
4736
4794
|
if (value !== true && isPlainObject(value) && isPlainObject(value.select)) {
|
|
4737
4795
|
const selectObj = value.select;
|
|
4738
4796
|
for (const entryVal of Object.values(selectObj)) {
|
|
4739
4797
|
if (isPlainObject(entryVal) && entryVal.where) {
|
|
4740
4798
|
const w = entryVal.where;
|
|
4741
|
-
if (isPlainObject(w) && hasClientControlledValues(w))
|
|
4799
|
+
if (isPlainObject(w) && hasClientControlledValues(w, depth + 1))
|
|
4742
4800
|
return true;
|
|
4743
4801
|
}
|
|
4744
4802
|
}
|
|
@@ -4747,54 +4805,41 @@ function checkIncludeForClientArgs(config) {
|
|
|
4747
4805
|
}
|
|
4748
4806
|
if (value === true)
|
|
4749
4807
|
continue;
|
|
4750
|
-
|
|
4808
|
+
const nested = value;
|
|
4809
|
+
if (nested.orderBy || nested.cursor || nested.take || nested.skip)
|
|
4751
4810
|
return true;
|
|
4752
|
-
if (
|
|
4811
|
+
if (nested.where && isPlainObject(nested.where) && hasClientControlledValues(nested.where, depth + 1)) {
|
|
4753
4812
|
return true;
|
|
4754
4813
|
}
|
|
4755
|
-
if (
|
|
4814
|
+
if (nested.include && walkForClientContent(nested.include, predicate, depth + 1))
|
|
4756
4815
|
return true;
|
|
4757
|
-
if (
|
|
4816
|
+
if (nested.select && walkForClientContent(nested.select, predicate, depth + 1))
|
|
4758
4817
|
return true;
|
|
4759
4818
|
}
|
|
4760
4819
|
return false;
|
|
4761
4820
|
}
|
|
4762
|
-
function
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
for (const entryVal of Object.values(selectObj)) {
|
|
4768
|
-
if (isPlainObject(entryVal) && entryVal.where) {
|
|
4769
|
-
const w = entryVal.where;
|
|
4770
|
-
if (isPlainObject(w) && hasClientControlledValues(w))
|
|
4771
|
-
return true;
|
|
4772
|
-
}
|
|
4773
|
-
}
|
|
4774
|
-
}
|
|
4821
|
+
function hasClientControlledValues(obj, depth = 0) {
|
|
4822
|
+
if (depth > MAX_PROJECTION_WALK_DEPTH)
|
|
4823
|
+
return false;
|
|
4824
|
+
for (const value of Object.values(obj)) {
|
|
4825
|
+
if (isForcedValue(value))
|
|
4775
4826
|
continue;
|
|
4776
|
-
}
|
|
4777
4827
|
if (value === true)
|
|
4778
|
-
continue;
|
|
4779
|
-
if (value.orderBy || value.cursor || value.take || value.skip)
|
|
4780
4828
|
return true;
|
|
4781
|
-
if (
|
|
4829
|
+
if (isPlainObject(value) && hasClientControlledValues(value, depth + 1)) {
|
|
4782
4830
|
return true;
|
|
4783
4831
|
}
|
|
4784
|
-
if (value.select && checkSelectForClientArgs(value.select))
|
|
4785
|
-
return true;
|
|
4786
|
-
if (value.include && checkIncludeForClientArgs(value.include))
|
|
4787
|
-
return true;
|
|
4788
4832
|
}
|
|
4789
4833
|
return false;
|
|
4790
4834
|
}
|
|
4791
4835
|
function hasNestedClientControlledArgs(shape) {
|
|
4836
|
+
const predicate = () => false;
|
|
4792
4837
|
if (shape.include) {
|
|
4793
|
-
if (
|
|
4838
|
+
if (walkForClientContent(shape.include, predicate, 0))
|
|
4794
4839
|
return true;
|
|
4795
4840
|
}
|
|
4796
4841
|
if (shape.select) {
|
|
4797
|
-
if (
|
|
4842
|
+
if (walkForClientContent(shape.select, predicate, 0))
|
|
4798
4843
|
return true;
|
|
4799
4844
|
}
|
|
4800
4845
|
return false;
|
|
@@ -4866,77 +4911,62 @@ function createModelGuardExtension(config) {
|
|
|
4866
4911
|
const whereBuiltCache = /* @__PURE__ */ new Map();
|
|
4867
4912
|
const projectionCache = /* @__PURE__ */ new Map();
|
|
4868
4913
|
const uniqueWhereCache = /* @__PURE__ */ new Map();
|
|
4914
|
+
function memoize(cache, key, wasDynamic, build) {
|
|
4915
|
+
if (wasDynamic)
|
|
4916
|
+
return build();
|
|
4917
|
+
const cached = cache.get(key);
|
|
4918
|
+
if (cached)
|
|
4919
|
+
return cached;
|
|
4920
|
+
const built = build();
|
|
4921
|
+
cache.set(key, built);
|
|
4922
|
+
return built;
|
|
4923
|
+
}
|
|
4869
4924
|
function getReadShape(method, queryShape, matchedKey, wasDynamic) {
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
const built = queryBuilder.buildShapeZodSchema(
|
|
4925
|
+
return memoize(
|
|
4926
|
+
readShapeCache,
|
|
4927
|
+
`${method}\0${matchedKey}`,
|
|
4928
|
+
wasDynamic,
|
|
4929
|
+
() => queryBuilder.buildShapeZodSchema(
|
|
4876
4930
|
modelName,
|
|
4877
4931
|
method,
|
|
4878
4932
|
queryShape
|
|
4879
|
-
)
|
|
4880
|
-
readShapeCache.set(cacheKey, built);
|
|
4881
|
-
return built;
|
|
4882
|
-
}
|
|
4883
|
-
return queryBuilder.buildShapeZodSchema(
|
|
4884
|
-
modelName,
|
|
4885
|
-
method,
|
|
4886
|
-
queryShape
|
|
4933
|
+
)
|
|
4887
4934
|
);
|
|
4888
4935
|
}
|
|
4889
4936
|
function getDataSchema(mode, dataConfig, matchedKey, wasDynamic) {
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4937
|
+
const skipCache = wasDynamic || hasDataRefines(dataConfig);
|
|
4938
|
+
return memoize(
|
|
4939
|
+
dataSchemaCache,
|
|
4940
|
+
`${mode}\0${matchedKey}`,
|
|
4941
|
+
skipCache,
|
|
4942
|
+
() => buildDataSchema(
|
|
4896
4943
|
modelName,
|
|
4897
4944
|
dataConfig,
|
|
4898
4945
|
mode,
|
|
4899
4946
|
typeMap,
|
|
4947
|
+
uniqueMap,
|
|
4948
|
+
enumMap,
|
|
4949
|
+
scalarBase,
|
|
4900
4950
|
schemaBuilder,
|
|
4901
4951
|
zodDefaults
|
|
4902
|
-
)
|
|
4903
|
-
dataSchemaCache.set(cacheKey, built);
|
|
4904
|
-
return built;
|
|
4905
|
-
}
|
|
4906
|
-
return buildDataSchema(
|
|
4907
|
-
modelName,
|
|
4908
|
-
dataConfig,
|
|
4909
|
-
mode,
|
|
4910
|
-
typeMap,
|
|
4911
|
-
schemaBuilder,
|
|
4912
|
-
zodDefaults
|
|
4952
|
+
)
|
|
4913
4953
|
);
|
|
4914
4954
|
}
|
|
4915
4955
|
function getWhereBuilt(whereConfig, matchedKey, wasDynamic) {
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
return built;
|
|
4923
|
-
}
|
|
4924
|
-
return queryBuilder.buildWhereSchema(modelName, whereConfig);
|
|
4956
|
+
return memoize(
|
|
4957
|
+
whereBuiltCache,
|
|
4958
|
+
matchedKey,
|
|
4959
|
+
wasDynamic,
|
|
4960
|
+
() => queryBuilder.buildWhereSchema(modelName, whereConfig)
|
|
4961
|
+
);
|
|
4925
4962
|
}
|
|
4926
4963
|
function getUniqueWhereBuilt(whereConfig, matchedKey, wasDynamic) {
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
modelName,
|
|
4934
|
-
whereConfig
|
|
4935
|
-
);
|
|
4936
|
-
uniqueWhereCache.set(cacheKey, built);
|
|
4937
|
-
return built;
|
|
4938
|
-
}
|
|
4939
|
-
return queryBuilder.buildUniqueWhereSchema(modelName, whereConfig);
|
|
4964
|
+
return memoize(
|
|
4965
|
+
uniqueWhereCache,
|
|
4966
|
+
`unique\0${matchedKey}`,
|
|
4967
|
+
wasDynamic,
|
|
4968
|
+
() => queryBuilder.buildUniqueWhereSchema(modelName, whereConfig)
|
|
4969
|
+
);
|
|
4940
4970
|
}
|
|
4941
4971
|
function buildProjectionSchema(shape) {
|
|
4942
4972
|
const schemaFields = {};
|
|
@@ -4963,7 +4993,7 @@ function createModelGuardExtension(config) {
|
|
|
4963
4993
|
forcedSelectCountWhere = result.forcedCountWhere;
|
|
4964
4994
|
}
|
|
4965
4995
|
return {
|
|
4966
|
-
zodSchema:
|
|
4996
|
+
zodSchema: z11.object(schemaFields).strict(),
|
|
4967
4997
|
forcedIncludeTree,
|
|
4968
4998
|
forcedSelectTree,
|
|
4969
4999
|
forcedIncludeCountWhere,
|
|
@@ -4971,16 +5001,12 @@ function createModelGuardExtension(config) {
|
|
|
4971
5001
|
};
|
|
4972
5002
|
}
|
|
4973
5003
|
function getProjection(shape, matchedKey, wasDynamic) {
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
projectionCache.set(cacheKey, built);
|
|
4981
|
-
return built;
|
|
4982
|
-
}
|
|
4983
|
-
return buildProjectionSchema(shape);
|
|
5004
|
+
return memoize(
|
|
5005
|
+
projectionCache,
|
|
5006
|
+
`projection\0${matchedKey}`,
|
|
5007
|
+
wasDynamic,
|
|
5008
|
+
() => buildProjectionSchema(shape)
|
|
5009
|
+
);
|
|
4984
5010
|
}
|
|
4985
5011
|
function resolveProjection(shape, parsed, method, matchedKey, wasDynamic) {
|
|
4986
5012
|
const hasBodyProjection = "select" in parsed || "include" in parsed;
|
|
@@ -5174,24 +5200,11 @@ function createModelGuardExtension(config) {
|
|
|
5174
5200
|
const hasShapeProjection = !!resolved.shape.select || !!resolved.shape.include;
|
|
5175
5201
|
if (!hasShapeProjection)
|
|
5176
5202
|
return resolved.body;
|
|
5177
|
-
const defaultProjection = buildDefaultProjectionBody(resolved.shape);
|
|
5178
5203
|
const hasBodyProjection = "select" in resolved.body || "include" in resolved.body;
|
|
5179
|
-
if (
|
|
5180
|
-
return
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
if ("select" in resolved.body && "select" in defaultProjection && isPlainObject(resolved.body.select) && isPlainObject(defaultProjection.select)) {
|
|
5184
|
-
merged.select = applyClientProjectionOverrides(
|
|
5185
|
-
defaultProjection.select,
|
|
5186
|
-
resolved.body.select
|
|
5187
|
-
);
|
|
5188
|
-
} else if ("include" in resolved.body && "include" in defaultProjection && isPlainObject(resolved.body.include) && isPlainObject(defaultProjection.include)) {
|
|
5189
|
-
merged.include = applyClientProjectionOverrides(
|
|
5190
|
-
defaultProjection.include,
|
|
5191
|
-
resolved.body.include
|
|
5192
|
-
);
|
|
5193
|
-
}
|
|
5194
|
-
return merged;
|
|
5204
|
+
if (hasBodyProjection)
|
|
5205
|
+
return resolved.body;
|
|
5206
|
+
const defaultProjection = buildDefaultProjectionBody(resolved.shape);
|
|
5207
|
+
return { ...resolved.body, ...defaultProjection };
|
|
5195
5208
|
}
|
|
5196
5209
|
function makeReadMethod(method) {
|
|
5197
5210
|
return (body) => {
|
|
@@ -5224,29 +5237,21 @@ function createModelGuardExtension(config) {
|
|
|
5224
5237
|
function makeCreateMethod(method) {
|
|
5225
5238
|
const isBatch = BATCH_CREATE_METHODS.has(method);
|
|
5226
5239
|
const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_MANY_PROJECTION;
|
|
5230
|
-
} else if (isBatch) {
|
|
5231
|
-
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_MANY;
|
|
5232
|
-
} else if (supportsProjection) {
|
|
5233
|
-
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_PROJECTION;
|
|
5234
|
-
} else {
|
|
5235
|
-
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE;
|
|
5236
|
-
}
|
|
5237
|
-
const allowedShapeKeys = supportsProjection ? VALID_SHAPE_KEYS_CREATE_PROJECTION : VALID_SHAPE_KEYS_CREATE;
|
|
5240
|
+
const allowedBodyKeys = getAllowedBodyKeys(method, supportsProjection);
|
|
5241
|
+
const allowedShapeKeys = getAllowedShapeKeys(method, supportsProjection);
|
|
5238
5242
|
return (body) => {
|
|
5239
5243
|
const caller = resolveCaller();
|
|
5240
5244
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
5241
5245
|
if (!resolved.shape.data) {
|
|
5242
5246
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
5243
5247
|
}
|
|
5244
|
-
|
|
5248
|
+
validateAllowedKeys(
|
|
5245
5249
|
resolved.shape,
|
|
5246
5250
|
allowedShapeKeys,
|
|
5247
|
-
method
|
|
5251
|
+
method,
|
|
5252
|
+
"shape"
|
|
5248
5253
|
);
|
|
5249
|
-
|
|
5254
|
+
validateAllowedKeys(resolved.body, allowedBodyKeys, method, "body");
|
|
5250
5255
|
const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
|
|
5251
5256
|
validateCreateCompleteness(
|
|
5252
5257
|
modelName,
|
|
@@ -5305,20 +5310,21 @@ function createModelGuardExtension(config) {
|
|
|
5305
5310
|
const isUniqueWhere = method === "update";
|
|
5306
5311
|
const isBulk = BULK_MUTATION_METHODS.has(method);
|
|
5307
5312
|
const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
|
|
5308
|
-
const allowedBodyKeys = supportsProjection
|
|
5309
|
-
const allowedShapeKeys = supportsProjection
|
|
5313
|
+
const allowedBodyKeys = getAllowedBodyKeys(method, supportsProjection);
|
|
5314
|
+
const allowedShapeKeys = getAllowedShapeKeys(method, supportsProjection);
|
|
5310
5315
|
return (body) => {
|
|
5311
5316
|
const caller = resolveCaller();
|
|
5312
5317
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
5313
5318
|
if (!resolved.shape.data) {
|
|
5314
5319
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
5315
5320
|
}
|
|
5316
|
-
|
|
5321
|
+
validateAllowedKeys(
|
|
5317
5322
|
resolved.shape,
|
|
5318
5323
|
allowedShapeKeys,
|
|
5319
|
-
method
|
|
5324
|
+
method,
|
|
5325
|
+
"shape"
|
|
5320
5326
|
);
|
|
5321
|
-
|
|
5327
|
+
validateAllowedKeys(resolved.body, allowedBodyKeys, method, "body");
|
|
5322
5328
|
if (isBulk && !resolved.shape.where) {
|
|
5323
5329
|
throw new ShapeError(
|
|
5324
5330
|
`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
|
|
@@ -5386,20 +5392,21 @@ function createModelGuardExtension(config) {
|
|
|
5386
5392
|
const isUniqueWhere = method === "delete";
|
|
5387
5393
|
const isBulk = BULK_MUTATION_METHODS.has(method);
|
|
5388
5394
|
const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
|
|
5389
|
-
const allowedBodyKeys = supportsProjection
|
|
5390
|
-
const allowedShapeKeys = supportsProjection
|
|
5395
|
+
const allowedBodyKeys = getAllowedBodyKeys(method, supportsProjection);
|
|
5396
|
+
const allowedShapeKeys = getAllowedShapeKeys(method, supportsProjection);
|
|
5391
5397
|
return (body) => {
|
|
5392
5398
|
const caller = resolveCaller();
|
|
5393
5399
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
5394
5400
|
if (resolved.shape.data) {
|
|
5395
5401
|
throw new ShapeError(`Guard shape "data" is not valid for ${method}`);
|
|
5396
5402
|
}
|
|
5397
|
-
|
|
5403
|
+
validateAllowedKeys(
|
|
5398
5404
|
resolved.shape,
|
|
5399
5405
|
allowedShapeKeys,
|
|
5400
|
-
method
|
|
5406
|
+
method,
|
|
5407
|
+
"shape"
|
|
5401
5408
|
);
|
|
5402
|
-
|
|
5409
|
+
validateAllowedKeys(resolved.body, allowedBodyKeys, method, "body");
|
|
5403
5410
|
if (isBulk && !resolved.shape.where) {
|
|
5404
5411
|
throw new ShapeError(
|
|
5405
5412
|
`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
|
|
@@ -5452,6 +5459,8 @@ function createModelGuardExtension(config) {
|
|
|
5452
5459
|
};
|
|
5453
5460
|
}
|
|
5454
5461
|
function makeUpsertMethod() {
|
|
5462
|
+
const allowedBodyKeys = getAllowedBodyKeys("upsert", true);
|
|
5463
|
+
const allowedShapeKeys = getAllowedShapeKeys("upsert", true);
|
|
5455
5464
|
return (body) => {
|
|
5456
5465
|
const caller = resolveCaller();
|
|
5457
5466
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
@@ -5469,15 +5478,17 @@ function createModelGuardExtension(config) {
|
|
|
5469
5478
|
if (!resolved.shape.where) {
|
|
5470
5479
|
throw new ShapeError('Guard shape requires "where" for upsert');
|
|
5471
5480
|
}
|
|
5472
|
-
|
|
5481
|
+
validateAllowedKeys(
|
|
5473
5482
|
resolved.shape,
|
|
5474
|
-
|
|
5475
|
-
"upsert"
|
|
5483
|
+
allowedShapeKeys,
|
|
5484
|
+
"upsert",
|
|
5485
|
+
"shape"
|
|
5476
5486
|
);
|
|
5477
|
-
|
|
5487
|
+
validateAllowedKeys(
|
|
5478
5488
|
resolved.body,
|
|
5479
|
-
|
|
5480
|
-
"upsert"
|
|
5489
|
+
allowedBodyKeys,
|
|
5490
|
+
"upsert",
|
|
5491
|
+
"body"
|
|
5481
5492
|
);
|
|
5482
5493
|
validateUniqueWhereShapeConfig(
|
|
5483
5494
|
modelName,
|
|
@@ -5567,12 +5578,7 @@ function createModelGuardExtension(config) {
|
|
|
5567
5578
|
try {
|
|
5568
5579
|
return fn(body);
|
|
5569
5580
|
} catch (err) {
|
|
5570
|
-
|
|
5571
|
-
throw new ShapeError(`Validation failed: ${formatZodError(err)}`, {
|
|
5572
|
-
cause: err
|
|
5573
|
-
});
|
|
5574
|
-
}
|
|
5575
|
-
throw err;
|
|
5581
|
+
throw toShapeError(err);
|
|
5576
5582
|
}
|
|
5577
5583
|
};
|
|
5578
5584
|
}
|
|
@@ -5605,6 +5611,15 @@ function createModelGuardExtension(config) {
|
|
|
5605
5611
|
}
|
|
5606
5612
|
|
|
5607
5613
|
// src/runtime/guard.ts
|
|
5614
|
+
function wrapParseFn(fn) {
|
|
5615
|
+
return (...args) => {
|
|
5616
|
+
try {
|
|
5617
|
+
return fn(...args);
|
|
5618
|
+
} catch (err) {
|
|
5619
|
+
throw toShapeError(err);
|
|
5620
|
+
}
|
|
5621
|
+
};
|
|
5622
|
+
}
|
|
5608
5623
|
function createGuard(config) {
|
|
5609
5624
|
const scalarBase = createScalarBase(
|
|
5610
5625
|
config.guardConfig.strictDecimal ?? false
|
|
@@ -5626,14 +5641,6 @@ function createGuard(config) {
|
|
|
5626
5641
|
warn: (msg) => console.warn(msg)
|
|
5627
5642
|
};
|
|
5628
5643
|
const wrapZodErrors = config.wrapZodErrors ?? false;
|
|
5629
|
-
function rethrowZod(err) {
|
|
5630
|
-
if (err instanceof ZodError) {
|
|
5631
|
-
throw new ShapeError(`Validation failed: ${formatZodError(err)}`, {
|
|
5632
|
-
cause: err
|
|
5633
|
-
});
|
|
5634
|
-
}
|
|
5635
|
-
throw err;
|
|
5636
|
-
}
|
|
5637
5644
|
return {
|
|
5638
5645
|
input: (model, opts) => {
|
|
5639
5646
|
const result = schemaBuilder.buildInputSchema(model, opts);
|
|
@@ -5641,13 +5648,7 @@ function createGuard(config) {
|
|
|
5641
5648
|
return result;
|
|
5642
5649
|
return {
|
|
5643
5650
|
schema: result.schema,
|
|
5644
|
-
parse(
|
|
5645
|
-
try {
|
|
5646
|
-
return result.parse(data);
|
|
5647
|
-
} catch (err) {
|
|
5648
|
-
rethrowZod(err);
|
|
5649
|
-
}
|
|
5650
|
-
}
|
|
5651
|
+
parse: wrapParseFn(result.parse)
|
|
5651
5652
|
};
|
|
5652
5653
|
},
|
|
5653
5654
|
model: (model, opts) => schemaBuilder.buildModelSchema(model, opts),
|
|
@@ -5657,13 +5658,7 @@ function createGuard(config) {
|
|
|
5657
5658
|
return qs;
|
|
5658
5659
|
return {
|
|
5659
5660
|
schemas: qs.schemas,
|
|
5660
|
-
parse(
|
|
5661
|
-
try {
|
|
5662
|
-
return qs.parse(body, opts);
|
|
5663
|
-
} catch (err) {
|
|
5664
|
-
rethrowZod(err);
|
|
5665
|
-
}
|
|
5666
|
-
}
|
|
5661
|
+
parse: wrapParseFn(qs.parse)
|
|
5667
5662
|
};
|
|
5668
5663
|
},
|
|
5669
5664
|
extension: (contextFn) => {
|