prisma-guard 1.27.0 → 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 +1664 -1663
- 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 +1663 -1662
- 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))
|
|
@@ -996,12 +1108,18 @@ function tryInlineForcedField(result, field, forcedValue) {
|
|
|
996
1108
|
if (!isPlainObject(forcedValue))
|
|
997
1109
|
return false;
|
|
998
1110
|
const existing = result[field];
|
|
999
|
-
if (!isPlainObject(existing))
|
|
1000
|
-
|
|
1111
|
+
if (!isPlainObject(existing)) {
|
|
1112
|
+
const merged2 = { equals: existing };
|
|
1113
|
+
for (const [op, value] of Object.entries(forcedValue)) {
|
|
1114
|
+
merged2[op] = deepClone(value);
|
|
1115
|
+
}
|
|
1116
|
+
result[field] = merged2;
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1001
1119
|
const merged = { ...existing };
|
|
1002
1120
|
for (const [op, value] of Object.entries(forcedValue)) {
|
|
1003
1121
|
if (op in merged) {
|
|
1004
|
-
if (!
|
|
1122
|
+
if (!deepEqual(merged[op], value)) {
|
|
1005
1123
|
throw new ShapeError(
|
|
1006
1124
|
`Conflicting where values for "${field}.${op}": client provided a different value than the forced shape`
|
|
1007
1125
|
);
|
|
@@ -1032,6 +1150,17 @@ function mergeWhereForced(where, forced) {
|
|
|
1032
1150
|
if (Object.keys(forced.conditions).length > 0) {
|
|
1033
1151
|
const remaining = {};
|
|
1034
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
|
+
}
|
|
1035
1164
|
const inlined = tryInlineForcedField(result, field, forcedValue);
|
|
1036
1165
|
if (!inlined) {
|
|
1037
1166
|
remaining[field] = deepClone(forcedValue);
|
|
@@ -1047,32 +1176,6 @@ function mergeWhereForced(where, forced) {
|
|
|
1047
1176
|
}
|
|
1048
1177
|
return result;
|
|
1049
1178
|
}
|
|
1050
|
-
function uniqueValuesEqual(a, b) {
|
|
1051
|
-
if (a === b)
|
|
1052
|
-
return true;
|
|
1053
|
-
if (a === null || b === null)
|
|
1054
|
-
return false;
|
|
1055
|
-
if (typeof a !== typeof b)
|
|
1056
|
-
return false;
|
|
1057
|
-
if (a instanceof Date && b instanceof Date) {
|
|
1058
|
-
return a.getTime() === b.getTime();
|
|
1059
|
-
}
|
|
1060
|
-
if (Array.isArray(a)) {
|
|
1061
|
-
if (!Array.isArray(b))
|
|
1062
|
-
return false;
|
|
1063
|
-
if (a.length !== b.length)
|
|
1064
|
-
return false;
|
|
1065
|
-
return a.every((v, i) => uniqueValuesEqual(v, b[i]));
|
|
1066
|
-
}
|
|
1067
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
1068
|
-
const aKeys = Object.keys(a);
|
|
1069
|
-
const bKeys = Object.keys(b);
|
|
1070
|
-
if (aKeys.length !== bKeys.length)
|
|
1071
|
-
return false;
|
|
1072
|
-
return aKeys.every((key) => key in b && uniqueValuesEqual(a[key], b[key]));
|
|
1073
|
-
}
|
|
1074
|
-
return false;
|
|
1075
|
-
}
|
|
1076
1179
|
function mergeUniqueValue(target, key, value) {
|
|
1077
1180
|
const cloned = deepClone(value);
|
|
1078
1181
|
if (!(key in target)) {
|
|
@@ -1083,7 +1186,7 @@ function mergeUniqueValue(target, key, value) {
|
|
|
1083
1186
|
if (isPlainObject(existing) && isPlainObject(cloned)) {
|
|
1084
1187
|
const merged = { ...existing };
|
|
1085
1188
|
for (const [nestedKey, nestedValue] of Object.entries(cloned)) {
|
|
1086
|
-
if (nestedKey in merged && !
|
|
1189
|
+
if (nestedKey in merged && !deepEqual(merged[nestedKey], nestedValue)) {
|
|
1087
1190
|
throw new ShapeError(
|
|
1088
1191
|
`Conflicting unique where value for "${key}.${nestedKey}"`
|
|
1089
1192
|
);
|
|
@@ -1093,7 +1196,7 @@ function mergeUniqueValue(target, key, value) {
|
|
|
1093
1196
|
target[key] = merged;
|
|
1094
1197
|
return;
|
|
1095
1198
|
}
|
|
1096
|
-
if (!
|
|
1199
|
+
if (!deepEqual(existing, cloned)) {
|
|
1097
1200
|
throw new ShapeError(`Conflicting unique where value for "${key}"`);
|
|
1098
1201
|
}
|
|
1099
1202
|
}
|
|
@@ -1120,7 +1223,30 @@ function applyBuiltShape(built, body, isUniqueMethod, modelName) {
|
|
|
1120
1223
|
throw new ShapeError('Request cannot define both "include" and "select"');
|
|
1121
1224
|
}
|
|
1122
1225
|
if ("where" in bodyObj) {
|
|
1123
|
-
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
|
+
}
|
|
1124
1250
|
const { where: _, ...rest } = bodyObj;
|
|
1125
1251
|
parseable = rest;
|
|
1126
1252
|
} else if (isUniqueMethod && hasWhereForced(built.forcedWhere) && isPlainObject(bodyObj.where)) {
|
|
@@ -1196,6 +1322,86 @@ function buildCountForPlacement(countWhere) {
|
|
|
1196
1322
|
}
|
|
1197
1323
|
return { _count: { select: countSelect } };
|
|
1198
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
|
+
}
|
|
1199
1405
|
function applyForcedTree(validated, key, tree) {
|
|
1200
1406
|
const container = validated[key];
|
|
1201
1407
|
if (!container)
|
|
@@ -1204,89 +1410,13 @@ function applyForcedTree(validated, key, tree) {
|
|
|
1204
1410
|
const relVal = container[relName];
|
|
1205
1411
|
if (relVal === void 0)
|
|
1206
1412
|
continue;
|
|
1207
|
-
|
|
1208
|
-
const expanded = {};
|
|
1209
|
-
if (forced.where && hasWhereForced(forced.where)) {
|
|
1210
|
-
expanded.where = mergeWhereForced(void 0, forced.where);
|
|
1211
|
-
}
|
|
1212
|
-
if (forced.include) {
|
|
1213
|
-
expanded.include = buildForcedOnlyContainer(forced.include);
|
|
1214
|
-
applyForcedTree(expanded, "include", forced.include);
|
|
1215
|
-
}
|
|
1216
|
-
if (forced.select) {
|
|
1217
|
-
expanded.select = buildForcedOnlyContainer(forced.select);
|
|
1218
|
-
applyForcedTree(expanded, "select", forced.select);
|
|
1219
|
-
}
|
|
1220
|
-
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
1221
|
-
const placement = forced._countWherePlacement ?? "include";
|
|
1222
|
-
if (!expanded[placement])
|
|
1223
|
-
expanded[placement] = {};
|
|
1224
|
-
const placementObj = expanded[placement];
|
|
1225
|
-
Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
|
|
1226
|
-
}
|
|
1227
|
-
if (expanded.include && expanded.select) {
|
|
1228
|
-
throw new ShapeError(
|
|
1229
|
-
`Forced tree for relation "${relName}" produces both "include" and "select". Prisma does not allow both at the same level.`
|
|
1230
|
-
);
|
|
1231
|
-
}
|
|
1232
|
-
container[relName] = Object.keys(expanded).length > 0 ? expanded : true;
|
|
1233
|
-
continue;
|
|
1234
|
-
}
|
|
1235
|
-
if (isPlainObject(relVal)) {
|
|
1236
|
-
const relObj = relVal;
|
|
1237
|
-
if (forced.where && hasWhereForced(forced.where)) {
|
|
1238
|
-
relObj.where = mergeWhereForced(
|
|
1239
|
-
relObj.where,
|
|
1240
|
-
forced.where
|
|
1241
|
-
);
|
|
1242
|
-
}
|
|
1243
|
-
if (forced.include) {
|
|
1244
|
-
if (!relObj.include) {
|
|
1245
|
-
relObj.include = buildForcedOnlyContainer(forced.include);
|
|
1246
|
-
}
|
|
1247
|
-
applyForcedTree(relObj, "include", forced.include);
|
|
1248
|
-
}
|
|
1249
|
-
if (forced.select) {
|
|
1250
|
-
if (!relObj.select) {
|
|
1251
|
-
relObj.select = buildForcedOnlyContainer(forced.select);
|
|
1252
|
-
}
|
|
1253
|
-
applyForcedTree(relObj, "select", forced.select);
|
|
1254
|
-
}
|
|
1255
|
-
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
1256
|
-
const placement = forced._countWherePlacement ?? "include";
|
|
1257
|
-
const projContainer = relObj[placement];
|
|
1258
|
-
if (projContainer) {
|
|
1259
|
-
applyForcedCountWhere(projContainer, forced._countWhere);
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
if (relObj.include && relObj.select) {
|
|
1263
|
-
throw new ShapeError(
|
|
1264
|
-
`Relation "${relName}" has both "include" and "select" after forced tree merge. Prisma does not allow both at the same level.`
|
|
1265
|
-
);
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1413
|
+
container[relName] = applyForcedToRelValue(relName, relVal, forced);
|
|
1268
1414
|
}
|
|
1269
1415
|
}
|
|
1270
1416
|
function buildForcedOnlyContainer(tree) {
|
|
1271
1417
|
const result = {};
|
|
1272
1418
|
for (const [relName, forced] of Object.entries(tree)) {
|
|
1273
|
-
const nested =
|
|
1274
|
-
if (forced.where && hasWhereForced(forced.where)) {
|
|
1275
|
-
nested.where = mergeWhereForced(void 0, forced.where);
|
|
1276
|
-
}
|
|
1277
|
-
if (forced.include) {
|
|
1278
|
-
nested.include = buildForcedOnlyContainer(forced.include);
|
|
1279
|
-
}
|
|
1280
|
-
if (forced.select) {
|
|
1281
|
-
nested.select = buildForcedOnlyContainer(forced.select);
|
|
1282
|
-
}
|
|
1283
|
-
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
1284
|
-
const placement = forced._countWherePlacement ?? "include";
|
|
1285
|
-
if (!nested[placement])
|
|
1286
|
-
nested[placement] = {};
|
|
1287
|
-
const placementObj = nested[placement];
|
|
1288
|
-
Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
|
|
1289
|
-
}
|
|
1419
|
+
const nested = collectSubtree(forced);
|
|
1290
1420
|
result[relName] = Object.keys(nested).length > 0 ? nested : true;
|
|
1291
1421
|
}
|
|
1292
1422
|
return result;
|
|
@@ -1302,11 +1432,15 @@ function applyForcedCountWhere(container, forcedCountWhere) {
|
|
|
1302
1432
|
const selectObj = selectVal;
|
|
1303
1433
|
for (const [relName, forced] of Object.entries(forcedCountWhere)) {
|
|
1304
1434
|
const relVal = selectObj[relName];
|
|
1305
|
-
if (relVal === void 0)
|
|
1435
|
+
if (relVal === void 0) {
|
|
1436
|
+
selectObj[relName] = { where: mergeWhereForced(void 0, forced) };
|
|
1306
1437
|
continue;
|
|
1438
|
+
}
|
|
1307
1439
|
if (relVal === true) {
|
|
1308
1440
|
selectObj[relName] = { where: mergeWhereForced(void 0, forced) };
|
|
1309
|
-
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1443
|
+
if (isPlainObject(relVal)) {
|
|
1310
1444
|
const relObj = relVal;
|
|
1311
1445
|
relObj.where = mergeWhereForced(
|
|
1312
1446
|
relObj.where,
|
|
@@ -1315,12 +1449,6 @@ function applyForcedCountWhere(container, forcedCountWhere) {
|
|
|
1315
1449
|
}
|
|
1316
1450
|
}
|
|
1317
1451
|
}
|
|
1318
|
-
function formatUniqueConstraint(constraint) {
|
|
1319
|
-
return constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`;
|
|
1320
|
-
}
|
|
1321
|
-
function formatUniqueConstraints(constraints) {
|
|
1322
|
-
return constraints.map(formatUniqueConstraint).join(" | ");
|
|
1323
|
-
}
|
|
1324
1452
|
function resolvedWhereCoversConstraint(where, constraint) {
|
|
1325
1453
|
if (constraint.fields.length === 1) {
|
|
1326
1454
|
return constraint.fields[0] in where;
|
|
@@ -1426,7 +1554,12 @@ function stripUniqueWhereForcedInput(where, forced) {
|
|
|
1426
1554
|
const currentValue = result[key];
|
|
1427
1555
|
if (isPlainObject(currentValue) && isPlainObject(forcedValue)) {
|
|
1428
1556
|
const nested = { ...currentValue };
|
|
1429
|
-
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
|
+
}
|
|
1430
1563
|
delete nested[nestedKey];
|
|
1431
1564
|
}
|
|
1432
1565
|
if (Object.keys(nested).length === 0) {
|
|
@@ -1436,11 +1569,25 @@ function stripUniqueWhereForcedInput(where, forced) {
|
|
|
1436
1569
|
}
|
|
1437
1570
|
continue;
|
|
1438
1571
|
}
|
|
1572
|
+
if (!deepEqual(currentValue, forcedValue)) {
|
|
1573
|
+
throw new ShapeError(
|
|
1574
|
+
`Client unique where value for "${key}" conflicts with forced value`
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1439
1577
|
delete result[key];
|
|
1440
1578
|
}
|
|
1441
1579
|
return result;
|
|
1442
1580
|
}
|
|
1443
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
|
+
|
|
1444
1591
|
// src/runtime/query-builder-where.ts
|
|
1445
1592
|
var UNSUPPORTED_WHERE_TYPES = /* @__PURE__ */ new Set(["Bytes"]);
|
|
1446
1593
|
var STRING_MODE_OPS = /* @__PURE__ */ new Set([
|
|
@@ -1454,47 +1601,40 @@ var JSON_STRING_MODE_OPS = /* @__PURE__ */ new Set([
|
|
|
1454
1601
|
"string_starts_with",
|
|
1455
1602
|
"string_ends_with"
|
|
1456
1603
|
]);
|
|
1604
|
+
var NEGATIVE_RELATION_OPS = /* @__PURE__ */ new Set(["none", "isNot"]);
|
|
1457
1605
|
var MAX_WHERE_DEPTH = 10;
|
|
1458
|
-
function safeStringify(
|
|
1459
|
-
if (typeof
|
|
1460
|
-
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();
|
|
1461
1616
|
try {
|
|
1462
|
-
|
|
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;
|
|
1463
1634
|
} catch {
|
|
1464
|
-
return String(
|
|
1635
|
+
return String(value);
|
|
1465
1636
|
}
|
|
1466
1637
|
}
|
|
1467
|
-
function forcedValuesEqual(a, b) {
|
|
1468
|
-
if (a === b)
|
|
1469
|
-
return true;
|
|
1470
|
-
if (a === null || b === null)
|
|
1471
|
-
return false;
|
|
1472
|
-
if (typeof a !== typeof b)
|
|
1473
|
-
return false;
|
|
1474
|
-
if (typeof a === "bigint")
|
|
1475
|
-
return a === b;
|
|
1476
|
-
if (a instanceof Date && b instanceof Date)
|
|
1477
|
-
return a.getTime() === b.getTime();
|
|
1478
|
-
if (a instanceof RegExp && b instanceof RegExp) {
|
|
1479
|
-
return a.source === b.source && a.flags === b.flags;
|
|
1480
|
-
}
|
|
1481
|
-
if (typeof a !== "object")
|
|
1482
|
-
return false;
|
|
1483
|
-
if (Array.isArray(a)) {
|
|
1484
|
-
if (!Array.isArray(b))
|
|
1485
|
-
return false;
|
|
1486
|
-
if (a.length !== b.length)
|
|
1487
|
-
return false;
|
|
1488
|
-
return a.every((v, i) => forcedValuesEqual(v, b[i]));
|
|
1489
|
-
}
|
|
1490
|
-
if (!isPlainObject(a) || !isPlainObject(b))
|
|
1491
|
-
return false;
|
|
1492
|
-
const aKeys = Object.keys(a);
|
|
1493
|
-
const bKeys = Object.keys(b);
|
|
1494
|
-
if (aKeys.length !== bKeys.length)
|
|
1495
|
-
return false;
|
|
1496
|
-
return aKeys.every((k) => k in b && forcedValuesEqual(a[k], b[k]));
|
|
1497
|
-
}
|
|
1498
1638
|
function mergeScalarConditions(target, source) {
|
|
1499
1639
|
for (const [field, ops] of Object.entries(source)) {
|
|
1500
1640
|
const existing = target[field];
|
|
@@ -1512,7 +1652,7 @@ function mergeScalarConditions(target, source) {
|
|
|
1512
1652
|
for (const [op, val] of Object.entries(ops)) {
|
|
1513
1653
|
if (op in existing) {
|
|
1514
1654
|
const existingVal = existing[op];
|
|
1515
|
-
if (!
|
|
1655
|
+
if (!deepEqual(existingVal, val)) {
|
|
1516
1656
|
throw new ShapeError(
|
|
1517
1657
|
`Conflicting forced where values for "${field}.${op}": shape defines both ${safeStringify(existingVal)} and ${safeStringify(val)}`
|
|
1518
1658
|
);
|
|
@@ -1522,7 +1662,7 @@ function mergeScalarConditions(target, source) {
|
|
|
1522
1662
|
Object.assign(existing, ops);
|
|
1523
1663
|
continue;
|
|
1524
1664
|
}
|
|
1525
|
-
if (!
|
|
1665
|
+
if (!deepEqual(existing, ops)) {
|
|
1526
1666
|
throw new ShapeError(`Conflicting forced where values for "${field}"`);
|
|
1527
1667
|
}
|
|
1528
1668
|
}
|
|
@@ -1613,27 +1753,27 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1613
1753
|
scalarConditions
|
|
1614
1754
|
);
|
|
1615
1755
|
}
|
|
1616
|
-
|
|
1756
|
+
const forcedOnlyKeys = /* @__PURE__ */ new Set();
|
|
1757
|
+
for (const key of Object.keys(whereConfig)) {
|
|
1617
1758
|
if (COMBINATOR_KEYS.has(key))
|
|
1618
1759
|
continue;
|
|
1619
1760
|
if (!(key in fieldSchemas)) {
|
|
1620
|
-
|
|
1761
|
+
forcedOnlyKeys.add(key);
|
|
1621
1762
|
}
|
|
1622
1763
|
}
|
|
1623
|
-
for (const key of Object.keys(
|
|
1764
|
+
for (const key of Object.keys(scalarConditions)) {
|
|
1765
|
+
if (COMBINATOR_KEYS.has(key))
|
|
1766
|
+
continue;
|
|
1624
1767
|
if (!(key in fieldSchemas)) {
|
|
1625
|
-
fieldSchemas[key] =
|
|
1768
|
+
fieldSchemas[key] = z5.object({}).strict().optional();
|
|
1626
1769
|
}
|
|
1627
1770
|
}
|
|
1628
|
-
const
|
|
1629
|
-
const forcedOnlyKeys = /* @__PURE__ */ new Set();
|
|
1630
|
-
for (const key of Object.keys(whereConfig)) {
|
|
1631
|
-
if (COMBINATOR_KEYS.has(key))
|
|
1632
|
-
continue;
|
|
1771
|
+
for (const key of Object.keys(relationForced)) {
|
|
1633
1772
|
if (!(key in fieldSchemas)) {
|
|
1634
|
-
|
|
1773
|
+
fieldSchemas[key] = z5.object({}).strict().optional();
|
|
1635
1774
|
}
|
|
1636
1775
|
}
|
|
1776
|
+
const schema = Object.keys(fieldSchemas).length > 0 ? z5.object(fieldSchemas).strict().optional() : null;
|
|
1637
1777
|
return {
|
|
1638
1778
|
schema,
|
|
1639
1779
|
forced: {
|
|
@@ -1664,12 +1804,12 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1664
1804
|
);
|
|
1665
1805
|
}
|
|
1666
1806
|
if (key === "NOT") {
|
|
1667
|
-
fieldSchemas[key] =
|
|
1807
|
+
fieldSchemas[key] = z5.union([
|
|
1668
1808
|
elementSchema,
|
|
1669
|
-
|
|
1809
|
+
z5.preprocess(coerceToArray, z5.array(elementSchema).min(1))
|
|
1670
1810
|
]).optional();
|
|
1671
1811
|
} else {
|
|
1672
|
-
fieldSchemas[key] =
|
|
1812
|
+
fieldSchemas[key] = z5.preprocess(coerceToArray, z5.array(elementSchema).min(1)).optional();
|
|
1673
1813
|
}
|
|
1674
1814
|
}
|
|
1675
1815
|
if (hasWhereForced(result.forced)) {
|
|
@@ -1738,6 +1878,11 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1738
1878
|
`Empty nested where for relation "${key}.${op}" on model "${model}". Define at least one field.`
|
|
1739
1879
|
);
|
|
1740
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
|
+
}
|
|
1741
1886
|
if (nested.schema) {
|
|
1742
1887
|
if (!hasWhereForced(nested.forced)) {
|
|
1743
1888
|
opSchemas[op] = nested.schema.refine(
|
|
@@ -1762,7 +1907,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1762
1907
|
}
|
|
1763
1908
|
if (hasClientOps) {
|
|
1764
1909
|
const clientOpKeys = Object.keys(opSchemas);
|
|
1765
|
-
const opObjSchema =
|
|
1910
|
+
const opObjSchema = z5.object(opSchemas).strict();
|
|
1766
1911
|
if (Object.keys(opForced).length === 0 && !hasForcedNull) {
|
|
1767
1912
|
fieldSchemas[key] = opObjSchema.refine(
|
|
1768
1913
|
(v) => clientOpKeys.some(
|
|
@@ -1848,10 +1993,10 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1848
1993
|
);
|
|
1849
1994
|
}
|
|
1850
1995
|
if (modeConfigValue === true) {
|
|
1851
|
-
opSchemas.mode =
|
|
1996
|
+
opSchemas.mode = z5.enum(["default", "insensitive"]).optional();
|
|
1852
1997
|
} else {
|
|
1853
1998
|
const actualModeValue = isForcedValue(modeConfigValue) ? modeConfigValue.value : modeConfigValue;
|
|
1854
|
-
const modeSchema =
|
|
1999
|
+
const modeSchema = z5.enum(["default", "insensitive"]);
|
|
1855
2000
|
let parsed;
|
|
1856
2001
|
try {
|
|
1857
2002
|
parsed = modeSchema.parse(actualModeValue);
|
|
@@ -1863,10 +2008,10 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1863
2008
|
fieldForced.mode = parsed;
|
|
1864
2009
|
}
|
|
1865
2010
|
} else if (hasModeCompatibleOp) {
|
|
1866
|
-
opSchemas.mode =
|
|
2011
|
+
opSchemas.mode = z5.enum(["default", "insensitive"]).optional();
|
|
1867
2012
|
}
|
|
1868
2013
|
if (hasClientOps) {
|
|
1869
|
-
const opObj =
|
|
2014
|
+
const opObj = z5.object(opSchemas).strict();
|
|
1870
2015
|
const refined = opObj.refine(
|
|
1871
2016
|
(v) => clientOpKeys.some(
|
|
1872
2017
|
(k) => v[k] !== void 0
|
|
@@ -1882,7 +2027,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1882
2027
|
enumMap,
|
|
1883
2028
|
scalarBase
|
|
1884
2029
|
);
|
|
1885
|
-
fieldSchemas[fieldName] =
|
|
2030
|
+
fieldSchemas[fieldName] = z5.union([refined, equalsBase]).optional();
|
|
1886
2031
|
} else {
|
|
1887
2032
|
fieldSchemas[fieldName] = refined.optional();
|
|
1888
2033
|
}
|
|
@@ -1896,15 +2041,8 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1896
2041
|
(constraint) => constraint.selector === selector
|
|
1897
2042
|
) ?? null;
|
|
1898
2043
|
}
|
|
1899
|
-
function buildDirectUniqueSchema(fieldMeta) {
|
|
1900
|
-
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
1901
|
-
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
1902
|
-
return wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, base);
|
|
1903
|
-
}
|
|
1904
|
-
return base;
|
|
1905
|
-
}
|
|
1906
2044
|
function parseForcedUniqueValue(model, fieldName, fieldMeta, value) {
|
|
1907
|
-
const schema =
|
|
2045
|
+
const schema = buildDirectScalarSchema(fieldMeta, enumMap, scalarBase);
|
|
1908
2046
|
const actual = isForcedValue(value) ? value.value : value;
|
|
1909
2047
|
try {
|
|
1910
2048
|
return schema.parse(actual);
|
|
@@ -1982,7 +2120,11 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1982
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.`
|
|
1983
2121
|
);
|
|
1984
2122
|
}
|
|
1985
|
-
const directSchema2 =
|
|
2123
|
+
const directSchema2 = buildDirectScalarSchema(
|
|
2124
|
+
fieldMeta2,
|
|
2125
|
+
enumMap,
|
|
2126
|
+
scalarBase
|
|
2127
|
+
);
|
|
1986
2128
|
if (fieldValue === true) {
|
|
1987
2129
|
nestedSchemas[fieldName] = directSchema2;
|
|
1988
2130
|
} else {
|
|
@@ -1995,7 +2137,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
1995
2137
|
}
|
|
1996
2138
|
}
|
|
1997
2139
|
if (Object.keys(nestedSchemas).length > 0) {
|
|
1998
|
-
fieldSchemas[key] =
|
|
2140
|
+
fieldSchemas[key] = z5.object(nestedSchemas).strict();
|
|
1999
2141
|
}
|
|
2000
2142
|
if (Object.keys(forcedCompound).length > 0) {
|
|
2001
2143
|
forcedConditions[key] = forcedCompound;
|
|
@@ -2029,7 +2171,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2029
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 }.`
|
|
2030
2172
|
);
|
|
2031
2173
|
}
|
|
2032
|
-
const directSchema =
|
|
2174
|
+
const directSchema = buildDirectScalarSchema(fieldMeta, enumMap, scalarBase);
|
|
2033
2175
|
if (value === true) {
|
|
2034
2176
|
fieldSchemas[key] = directSchema;
|
|
2035
2177
|
continue;
|
|
@@ -2043,7 +2185,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2043
2185
|
forcedOnlyKeys.add(key);
|
|
2044
2186
|
}
|
|
2045
2187
|
return {
|
|
2046
|
-
schema: Object.keys(fieldSchemas).length > 0 ?
|
|
2188
|
+
schema: Object.keys(fieldSchemas).length > 0 ? z5.object(fieldSchemas).strict() : null,
|
|
2047
2189
|
forced: {
|
|
2048
2190
|
conditions: forcedConditions,
|
|
2049
2191
|
relations: {}
|
|
@@ -2055,31 +2197,13 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2055
2197
|
}
|
|
2056
2198
|
|
|
2057
2199
|
// src/runtime/query-builder-args.ts
|
|
2058
|
-
import { z as
|
|
2200
|
+
import { z as z6 } from "zod";
|
|
2059
2201
|
var UNSUPPORTED_BY_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
|
|
2060
|
-
function requireConfigTrue(config, context) {
|
|
2061
|
-
for (const [key, value] of Object.entries(config)) {
|
|
2062
|
-
if (value !== true) {
|
|
2063
|
-
throw new ShapeError(
|
|
2064
|
-
`Config value for "${key}" in ${context} must be true, got ${typeof value}`
|
|
2065
|
-
);
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
}
|
|
2069
|
-
function isPlainRecord(value) {
|
|
2070
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2071
|
-
}
|
|
2072
|
-
function formatUniqueConstraint2(constraint) {
|
|
2073
|
-
return constraint.fields.length === 1 ? constraint.selector : `${constraint.selector}(${constraint.fields.join(", ")})`;
|
|
2074
|
-
}
|
|
2075
|
-
function formatUniqueConstraints2(constraints) {
|
|
2076
|
-
return constraints.map(formatUniqueConstraint2).join(" | ");
|
|
2077
|
-
}
|
|
2078
2202
|
function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
2079
|
-
const sortEnum =
|
|
2080
|
-
const nullsEnum =
|
|
2081
|
-
const sortWithNulls =
|
|
2082
|
-
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]);
|
|
2083
2207
|
function validateScalarOrderByField(fieldName, model, modelFields) {
|
|
2084
2208
|
const fieldMeta = modelFields[fieldName];
|
|
2085
2209
|
if (!fieldMeta)
|
|
@@ -2098,6 +2222,16 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2098
2222
|
);
|
|
2099
2223
|
}
|
|
2100
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
|
+
}
|
|
2101
2235
|
const modelFields = typeMap[model];
|
|
2102
2236
|
if (!modelFields)
|
|
2103
2237
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -2113,53 +2247,35 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2113
2247
|
fieldSchemas[fieldName] = scalarOrderSchema.optional();
|
|
2114
2248
|
continue;
|
|
2115
2249
|
}
|
|
2116
|
-
if (!
|
|
2250
|
+
if (!isPlainObject(config)) {
|
|
2117
2251
|
throw new ShapeError(
|
|
2118
|
-
`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`
|
|
2119
2253
|
);
|
|
2120
2254
|
}
|
|
2121
2255
|
if (!fieldMeta.isRelation) {
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
fieldMeta.isList
|
|
2256
|
+
throw new ShapeError(
|
|
2257
|
+
`orderBy config for scalar field "${model}.${fieldName}" must be true. Operator objects are not valid in orderBy.`
|
|
2125
2258
|
);
|
|
2126
|
-
const opSchemas = {};
|
|
2127
|
-
for (const [op, enabled] of Object.entries(config)) {
|
|
2128
|
-
if (enabled !== true) {
|
|
2129
|
-
throw new ShapeError(
|
|
2130
|
-
`orderBy operator config for "${model}.${fieldName}.${op}" must be true`
|
|
2131
|
-
);
|
|
2132
|
-
}
|
|
2133
|
-
if (!allowedOps.includes(op)) {
|
|
2134
|
-
throw new ShapeError(
|
|
2135
|
-
`Operator "${op}" not supported for orderBy field "${model}.${fieldName}"`
|
|
2136
|
-
);
|
|
2137
|
-
}
|
|
2138
|
-
opSchemas[op] = scalarOrderSchema.optional();
|
|
2139
|
-
}
|
|
2140
|
-
const opKeys = Object.keys(opSchemas);
|
|
2141
|
-
fieldSchemas[fieldName] = z5.object(opSchemas).strict().refine(
|
|
2142
|
-
(v) => opKeys.some(
|
|
2143
|
-
(k) => v[k] !== void 0
|
|
2144
|
-
),
|
|
2145
|
-
{
|
|
2146
|
-
message: `orderBy field "${fieldName}" must specify at least one operator`
|
|
2147
|
-
}
|
|
2148
|
-
).optional();
|
|
2149
|
-
continue;
|
|
2150
2259
|
}
|
|
2151
2260
|
if (fieldMeta.isList) {
|
|
2152
|
-
|
|
2261
|
+
const configKeys = Object.keys(config);
|
|
2262
|
+
if (!configKeys.includes("_count")) {
|
|
2153
2263
|
throw new ShapeError(
|
|
2154
2264
|
`To-many relation orderBy "${fieldName}" only supports _count`
|
|
2155
2265
|
);
|
|
2156
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
|
+
}
|
|
2157
2273
|
if (config._count !== true) {
|
|
2158
2274
|
throw new ShapeError(
|
|
2159
2275
|
`orderBy relation aggregate "${fieldName}._count" must be true`
|
|
2160
2276
|
);
|
|
2161
2277
|
}
|
|
2162
|
-
fieldSchemas[fieldName] =
|
|
2278
|
+
fieldSchemas[fieldName] = z6.object({
|
|
2163
2279
|
_count: sortEnum.optional()
|
|
2164
2280
|
}).strict().optional();
|
|
2165
2281
|
continue;
|
|
@@ -2170,17 +2286,11 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2170
2286
|
);
|
|
2171
2287
|
fieldSchemas[fieldName] = nested;
|
|
2172
2288
|
}
|
|
2173
|
-
const
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
(k) => v[k] !== void 0
|
|
2177
|
-
),
|
|
2178
|
-
{ message: "orderBy must specify at least one field" }
|
|
2289
|
+
const singleSchema = strictObjectRequiringOne(
|
|
2290
|
+
fieldSchemas,
|
|
2291
|
+
"orderBy must specify at least one field"
|
|
2179
2292
|
);
|
|
2180
|
-
return
|
|
2181
|
-
singleSchema,
|
|
2182
|
-
z5.preprocess(coerceToArray, z5.array(singleSchema).min(1))
|
|
2183
|
-
]).optional();
|
|
2293
|
+
return singleOrArraySchema(singleSchema).optional();
|
|
2184
2294
|
}
|
|
2185
2295
|
function buildTakeSchema(config) {
|
|
2186
2296
|
if (typeof config === "number") {
|
|
@@ -2190,7 +2300,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2190
2300
|
if (config <= 0) {
|
|
2191
2301
|
throw new ShapeError("take must be a positive integer");
|
|
2192
2302
|
}
|
|
2193
|
-
return
|
|
2303
|
+
return z6.number().int().min(1).max(config).default(config);
|
|
2194
2304
|
}
|
|
2195
2305
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
2196
2306
|
throw new ShapeError("take config must be a number or { max, default? }");
|
|
@@ -2215,9 +2325,9 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2215
2325
|
if (config.default > config.max) {
|
|
2216
2326
|
throw new ShapeError("take.default cannot exceed take.max");
|
|
2217
2327
|
}
|
|
2218
|
-
return
|
|
2328
|
+
return z6.number().int().min(1).max(config.max).default(config.default);
|
|
2219
2329
|
}
|
|
2220
|
-
return
|
|
2330
|
+
return z6.number().int().min(1).max(config.max).optional();
|
|
2221
2331
|
}
|
|
2222
2332
|
function buildCursorFieldSchema(model, fieldName) {
|
|
2223
2333
|
const modelFields = typeMap[model];
|
|
@@ -2239,11 +2349,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2239
2349
|
`List field "${fieldName}" cannot be used in cursor`
|
|
2240
2350
|
);
|
|
2241
2351
|
}
|
|
2242
|
-
|
|
2243
|
-
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
2244
|
-
return wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, base);
|
|
2245
|
-
}
|
|
2246
|
-
return base;
|
|
2352
|
+
return buildDirectScalarSchema(fieldMeta, enumMap, scalarBase);
|
|
2247
2353
|
}
|
|
2248
2354
|
function cursorConfigMatchesConstraint(cursorConfig, constraint) {
|
|
2249
2355
|
if (!(constraint.selector in cursorConfig))
|
|
@@ -2252,66 +2358,55 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2252
2358
|
if (constraint.fields.length === 1) {
|
|
2253
2359
|
return value === true;
|
|
2254
2360
|
}
|
|
2255
|
-
if (!
|
|
2361
|
+
if (!isPlainObject(value))
|
|
2256
2362
|
return false;
|
|
2257
2363
|
const keys = Object.keys(value);
|
|
2258
2364
|
if (keys.length !== constraint.fields.length)
|
|
2259
2365
|
return false;
|
|
2260
2366
|
return constraint.fields.every((field) => value[field] === true);
|
|
2261
2367
|
}
|
|
2262
|
-
function getUniqueConstraints(model) {
|
|
2263
|
-
const constraints = uniqueMap[model];
|
|
2264
|
-
if (constraints && constraints.length > 0) {
|
|
2265
|
-
return constraints;
|
|
2266
|
-
}
|
|
2267
|
-
const modelFields = typeMap[model];
|
|
2268
|
-
if (!modelFields) {
|
|
2269
|
-
throw new ShapeError(`Unknown model: ${model}`);
|
|
2270
|
-
}
|
|
2271
|
-
const inferred = [];
|
|
2272
|
-
for (const [fieldName, fieldMeta] of Object.entries(modelFields)) {
|
|
2273
|
-
if (fieldMeta.isRelation)
|
|
2274
|
-
continue;
|
|
2275
|
-
if (fieldMeta.isId || fieldMeta.isUnique) {
|
|
2276
|
-
inferred.push({
|
|
2277
|
-
selector: fieldName,
|
|
2278
|
-
fields: [fieldName]
|
|
2279
|
-
});
|
|
2280
|
-
}
|
|
2281
|
-
}
|
|
2282
|
-
return inferred;
|
|
2283
|
-
}
|
|
2284
2368
|
function buildCursorSchema(model, cursorConfig) {
|
|
2285
|
-
const constraints =
|
|
2369
|
+
const constraints = uniqueMap[model] ?? [];
|
|
2286
2370
|
if (constraints.length === 0) {
|
|
2287
2371
|
throw new ShapeError(
|
|
2288
2372
|
`cursor on model "${model}" requires at least one unique constraint`
|
|
2289
2373
|
);
|
|
2290
2374
|
}
|
|
2291
|
-
const matching = constraints.
|
|
2375
|
+
const matching = constraints.filter(
|
|
2292
2376
|
(constraint) => cursorConfigMatchesConstraint(cursorConfig, constraint)
|
|
2293
2377
|
);
|
|
2294
|
-
if (
|
|
2378
|
+
if (matching.length === 0) {
|
|
2295
2379
|
throw new ShapeError(
|
|
2296
|
-
`cursor on model "${model}" must
|
|
2380
|
+
`cursor on model "${model}" must match a unique selector: ${formatUniqueConstraints(constraints)}`
|
|
2297
2381
|
);
|
|
2298
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
|
+
}
|
|
2299
2391
|
const fieldSchemas = {};
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
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();
|
|
2309
2404
|
}
|
|
2310
|
-
fieldSchemas[matching.selector] = z5.object(nestedSchemas).strict().optional();
|
|
2311
2405
|
}
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2406
|
+
const selectorKeys = matching.map((c) => c.selector);
|
|
2407
|
+
return strictObjectRequiringOne(
|
|
2408
|
+
fieldSchemas,
|
|
2409
|
+
`cursor must specify one of: ${selectorKeys.join(", ")}`
|
|
2315
2410
|
).optional();
|
|
2316
2411
|
}
|
|
2317
2412
|
function buildDistinctSchema(model, distinctConfig) {
|
|
@@ -2336,10 +2431,8 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2336
2431
|
);
|
|
2337
2432
|
allowedFields.add(fieldName);
|
|
2338
2433
|
}
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
z5.array(z5.enum([...allowedFields])).min(1)
|
|
2342
|
-
]).optional();
|
|
2434
|
+
const fieldEnum = z6.enum([...allowedFields]);
|
|
2435
|
+
return z6.union([fieldEnum, z6.array(fieldEnum).min(1)]).optional();
|
|
2343
2436
|
}
|
|
2344
2437
|
function buildBySchema(model, byConfig) {
|
|
2345
2438
|
const modelFields = typeMap[model];
|
|
@@ -2372,7 +2465,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2372
2465
|
}
|
|
2373
2466
|
allowedFields.add(fieldName);
|
|
2374
2467
|
}
|
|
2375
|
-
return
|
|
2468
|
+
return z6.array(z6.enum([...allowedFields])).min(1);
|
|
2376
2469
|
}
|
|
2377
2470
|
function buildHavingSchema(model, havingConfig) {
|
|
2378
2471
|
const modelFields = typeMap[model];
|
|
@@ -2413,22 +2506,21 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2413
2506
|
).optional();
|
|
2414
2507
|
}
|
|
2415
2508
|
if (fieldMeta.type === "String") {
|
|
2416
|
-
opSchemas.mode =
|
|
2509
|
+
opSchemas.mode = z6.enum(["default", "insensitive"]).optional();
|
|
2417
2510
|
}
|
|
2418
2511
|
const opKeys = Object.keys(opSchemas).filter((key) => key !== "mode");
|
|
2419
|
-
|
|
2512
|
+
const opShape = { ...opSchemas };
|
|
2513
|
+
const opObjSchema = z6.object(opShape).strict().refine(
|
|
2420
2514
|
(v) => opKeys.some((k) => v[k] !== void 0),
|
|
2421
2515
|
{
|
|
2422
2516
|
message: `having field "${fieldName}" must specify at least one operator`
|
|
2423
2517
|
}
|
|
2424
|
-
)
|
|
2518
|
+
);
|
|
2519
|
+
fieldSchemas[fieldName] = opObjSchema.optional();
|
|
2425
2520
|
}
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
(k) => v[k] !== void 0
|
|
2430
|
-
),
|
|
2431
|
-
{ message: "having must specify at least one field" }
|
|
2521
|
+
return strictObjectRequiringOne(
|
|
2522
|
+
fieldSchemas,
|
|
2523
|
+
"having must specify at least one field"
|
|
2432
2524
|
).optional();
|
|
2433
2525
|
}
|
|
2434
2526
|
function buildAggregateFieldSchema(model, op, config) {
|
|
@@ -2437,47 +2529,45 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2437
2529
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2438
2530
|
requireConfigTrue(config, `${op} on model "${model}"`);
|
|
2439
2531
|
const allowedTypes = op === "_avg" || op === "_sum" ? NUMERIC_TYPES : COMPARABLE_TYPES;
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
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
|
+
}
|
|
2459
2554
|
}
|
|
2460
|
-
|
|
2461
|
-
}
|
|
2462
|
-
const aggregateFieldKeys = Object.keys(fieldSchemas);
|
|
2463
|
-
return z5.object(fieldSchemas).strict().refine(
|
|
2464
|
-
(v) => aggregateFieldKeys.some(
|
|
2465
|
-
(k) => v[k] !== void 0
|
|
2466
|
-
),
|
|
2467
|
-
{ message: `${op} must specify at least one field` }
|
|
2468
|
-
).optional();
|
|
2555
|
+
);
|
|
2469
2556
|
}
|
|
2470
2557
|
function buildCountFieldSchema(model, config, context) {
|
|
2471
2558
|
if (config === true) {
|
|
2472
|
-
return
|
|
2559
|
+
return z6.literal(true).optional();
|
|
2473
2560
|
}
|
|
2474
2561
|
const modelFields = typeMap[model];
|
|
2475
2562
|
if (!modelFields)
|
|
2476
2563
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2477
2564
|
requireConfigTrue(config, `${context} on model "${model}"`);
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2565
|
+
return buildLiteralTrueSchema(
|
|
2566
|
+
Object.keys(config),
|
|
2567
|
+
`${context} must specify at least one field`,
|
|
2568
|
+
(fieldName) => {
|
|
2569
|
+
if (fieldName === "_all")
|
|
2570
|
+
return;
|
|
2481
2571
|
const fieldMeta = modelFields[fieldName];
|
|
2482
2572
|
if (!fieldMeta)
|
|
2483
2573
|
throw new ShapeError(
|
|
@@ -2488,45 +2578,30 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2488
2578
|
`Relation field "${fieldName}" cannot be used in ${context}`
|
|
2489
2579
|
);
|
|
2490
2580
|
}
|
|
2491
|
-
|
|
2492
|
-
}
|
|
2493
|
-
const countFieldKeys = Object.keys(fieldSchemas);
|
|
2494
|
-
return z5.object(fieldSchemas).strict().refine(
|
|
2495
|
-
(v) => countFieldKeys.some(
|
|
2496
|
-
(k) => v[k] !== void 0
|
|
2497
|
-
),
|
|
2498
|
-
{ message: `${context} must specify at least one field` }
|
|
2499
|
-
).optional();
|
|
2581
|
+
);
|
|
2500
2582
|
}
|
|
2501
2583
|
function buildCountSelectSchema(model, selectConfig) {
|
|
2502
2584
|
const modelFields = typeMap[model];
|
|
2503
2585
|
if (!modelFields)
|
|
2504
2586
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2505
2587
|
requireConfigTrue(selectConfig, `count select on model "${model}"`);
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
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
|
+
);
|
|
2511
2603
|
}
|
|
2512
|
-
|
|
2513
|
-
if (!fieldMeta)
|
|
2514
|
-
throw new ShapeError(
|
|
2515
|
-
`Unknown field "${fieldName}" on model "${model}" in count select`
|
|
2516
|
-
);
|
|
2517
|
-
if (fieldMeta.isRelation)
|
|
2518
|
-
throw new ShapeError(
|
|
2519
|
-
`Relation field "${fieldName}" cannot be used in count select`
|
|
2520
|
-
);
|
|
2521
|
-
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
2522
|
-
}
|
|
2523
|
-
const countSelectKeys = Object.keys(fieldSchemas);
|
|
2524
|
-
return z5.object(fieldSchemas).strict().refine(
|
|
2525
|
-
(v) => countSelectKeys.some(
|
|
2526
|
-
(k) => v[k] !== void 0
|
|
2527
|
-
),
|
|
2528
|
-
{ message: "count select must specify at least one field" }
|
|
2529
|
-
).optional();
|
|
2604
|
+
);
|
|
2530
2605
|
}
|
|
2531
2606
|
return {
|
|
2532
2607
|
buildOrderBySchema,
|
|
@@ -2542,25 +2617,64 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2542
2617
|
}
|
|
2543
2618
|
|
|
2544
2619
|
// src/runtime/query-builder-projection.ts
|
|
2545
|
-
import { z as
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
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
|
+
};
|
|
2564
2678
|
var KNOWN_COUNT_SELECT_ENTRY_KEYS = /* @__PURE__ */ new Set(["where"]);
|
|
2565
2679
|
var MAX_PROJECTION_DEPTH = 10;
|
|
2566
2680
|
function validateNestedKeys(keys, allowed, context) {
|
|
@@ -2572,13 +2686,25 @@ function validateNestedKeys(keys, allowed, context) {
|
|
|
2572
2686
|
}
|
|
2573
2687
|
}
|
|
2574
2688
|
}
|
|
2575
|
-
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) {
|
|
2576
2702
|
function buildIncludeCountSchema(model, config) {
|
|
2577
2703
|
const modelFields = typeMap[model];
|
|
2578
2704
|
if (!modelFields)
|
|
2579
2705
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2580
2706
|
if (config === true) {
|
|
2581
|
-
return { schema:
|
|
2707
|
+
return { schema: z7.literal(true).optional(), forcedCountWhere: {} };
|
|
2582
2708
|
}
|
|
2583
2709
|
if (!isPlainObject(config) || !("select" in config)) {
|
|
2584
2710
|
throw new ShapeError(
|
|
@@ -2614,273 +2740,175 @@ function createProjectionBuilder(typeMap, enumMap, deps) {
|
|
|
2614
2740
|
if (!fieldMeta.isList)
|
|
2615
2741
|
throw new ShapeError(`Field "${fieldName}" is a to-one relation on model "${model}" in _count.select. Only to-many relations support _count.`);
|
|
2616
2742
|
if (fieldConfig === true) {
|
|
2617
|
-
countSelectFields[fieldName] =
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
`Empty config for _count.select.${fieldName} on model "${model}". Use true or { where: { ... } }.`
|
|
2622
|
-
);
|
|
2623
|
-
}
|
|
2624
|
-
validateNestedKeys(
|
|
2625
|
-
Object.keys(fieldConfig),
|
|
2626
|
-
KNOWN_COUNT_SELECT_ENTRY_KEYS,
|
|
2627
|
-
`_count.select.${fieldName} on model "${model}"`
|
|
2628
|
-
);
|
|
2629
|
-
if (fieldConfig.where) {
|
|
2630
|
-
const relatedType = fieldMeta.type;
|
|
2631
|
-
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
2632
|
-
relatedType,
|
|
2633
|
-
fieldConfig.where
|
|
2634
|
-
);
|
|
2635
|
-
const nestedSchemas = {};
|
|
2636
|
-
if (whereSchema)
|
|
2637
|
-
nestedSchemas["where"] = whereSchema;
|
|
2638
|
-
const nestedObj = z6.object(nestedSchemas).strict();
|
|
2639
|
-
countSelectFields[fieldName] = z6.union([z6.literal(true), nestedObj]).optional();
|
|
2640
|
-
if (hasWhereForced(forced)) {
|
|
2641
|
-
forcedCountWhere[fieldName] = forced;
|
|
2642
|
-
}
|
|
2643
|
-
} else {
|
|
2644
|
-
countSelectFields[fieldName] = z6.literal(true).optional();
|
|
2645
|
-
}
|
|
2646
|
-
} else {
|
|
2743
|
+
countSelectFields[fieldName] = z7.literal(true).optional();
|
|
2744
|
+
continue;
|
|
2745
|
+
}
|
|
2746
|
+
if (!isPlainObject(fieldConfig)) {
|
|
2647
2747
|
throw new ShapeError(
|
|
2648
2748
|
`Invalid config for _count.select.${fieldName} on model "${model}". Expected true or { where: { ... } }`
|
|
2649
2749
|
);
|
|
2650
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
|
+
}
|
|
2651
2778
|
}
|
|
2652
|
-
const
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
{ message: "_count.select must specify at least one field" }
|
|
2779
|
+
const selectSchema = strictObjectRequiringOne(
|
|
2780
|
+
countSelectFields,
|
|
2781
|
+
"_count.select must specify at least one field"
|
|
2656
2782
|
);
|
|
2657
2783
|
return {
|
|
2658
|
-
schema:
|
|
2784
|
+
schema: z7.object({ select: selectSchema }).strict().optional(),
|
|
2659
2785
|
forcedCountWhere
|
|
2660
2786
|
};
|
|
2661
2787
|
}
|
|
2662
|
-
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) {
|
|
2663
2836
|
const currentDepth = depth ?? 0;
|
|
2664
2837
|
if (currentDepth > MAX_PROJECTION_DEPTH) {
|
|
2665
2838
|
throw new ShapeError(
|
|
2666
|
-
|
|
2839
|
+
`${mode === "include" ? "Include" : "Select"} schema for model "${model}" exceeds maximum nesting depth (${MAX_PROJECTION_DEPTH}). Check for circular relation references in the shape.`
|
|
2667
2840
|
);
|
|
2668
2841
|
}
|
|
2669
|
-
if (Object.keys(
|
|
2842
|
+
if (Object.keys(projectionConfig).length === 0) {
|
|
2670
2843
|
throw new ShapeError(
|
|
2671
|
-
`Empty
|
|
2844
|
+
`Empty ${mode} config on model "${model}". Define at least one ${mode === "include" ? "relation" : "field"}.`
|
|
2672
2845
|
);
|
|
2673
2846
|
}
|
|
2674
2847
|
const modelFields = typeMap[model];
|
|
2675
2848
|
if (!modelFields)
|
|
2676
2849
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2850
|
+
const allowedNestedKeys = KNOWN_NESTED_KEYS[mode];
|
|
2677
2851
|
const fieldSchemas = {};
|
|
2678
2852
|
const forcedTree = {};
|
|
2679
2853
|
let topLevelForcedCountWhere = {};
|
|
2680
|
-
for (const [
|
|
2681
|
-
if (
|
|
2854
|
+
for (const [fieldName, config] of Object.entries(projectionConfig)) {
|
|
2855
|
+
if (fieldName === "_count") {
|
|
2682
2856
|
const countResult = buildIncludeCountSchema(model, config);
|
|
2683
2857
|
fieldSchemas["_count"] = countResult.schema;
|
|
2684
2858
|
topLevelForcedCountWhere = countResult.forcedCountWhere;
|
|
2685
2859
|
continue;
|
|
2686
2860
|
}
|
|
2687
|
-
const fieldMeta = modelFields[
|
|
2861
|
+
const fieldMeta = modelFields[fieldName];
|
|
2688
2862
|
if (!fieldMeta)
|
|
2689
|
-
throw new ShapeError(`Unknown field "${
|
|
2690
|
-
if (!fieldMeta.isRelation)
|
|
2691
|
-
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
|
+
}
|
|
2692
2867
|
if (config === true) {
|
|
2693
|
-
fieldSchemas[
|
|
2694
|
-
} else {
|
|
2695
|
-
validateNestedKeys(
|
|
2696
|
-
Object.keys(config),
|
|
2697
|
-
KNOWN_NESTED_INCLUDE_KEYS,
|
|
2698
|
-
`nested include for "${relName}" on model "${model}"`
|
|
2699
|
-
);
|
|
2700
|
-
if (config.select && config.include) {
|
|
2701
|
-
throw new ShapeError(`Nested include for "${relName}" cannot define both "select" and "include".`);
|
|
2702
|
-
}
|
|
2703
|
-
if (!fieldMeta.isList) {
|
|
2704
|
-
if (config.where || config.orderBy || config.cursor || config.take || config.skip) {
|
|
2705
|
-
throw new ShapeError(
|
|
2706
|
-
`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.`
|
|
2707
|
-
);
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
|
-
const nestedSchemas = {};
|
|
2711
|
-
const relForced = {};
|
|
2712
|
-
if (config.where) {
|
|
2713
|
-
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
2714
|
-
fieldMeta.type,
|
|
2715
|
-
config.where
|
|
2716
|
-
);
|
|
2717
|
-
if (whereSchema)
|
|
2718
|
-
nestedSchemas["where"] = whereSchema;
|
|
2719
|
-
if (hasWhereForced(forced))
|
|
2720
|
-
relForced.where = forced;
|
|
2721
|
-
}
|
|
2722
|
-
if (config.include) {
|
|
2723
|
-
const nested = buildIncludeSchema(fieldMeta.type, config.include, currentDepth + 1);
|
|
2724
|
-
nestedSchemas["include"] = nested.schema;
|
|
2725
|
-
if (Object.keys(nested.forcedTree).length > 0)
|
|
2726
|
-
relForced.include = nested.forcedTree;
|
|
2727
|
-
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2728
|
-
relForced._countWhere = nested.forcedCountWhere;
|
|
2729
|
-
relForced._countWherePlacement = "include";
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
if (config.select) {
|
|
2733
|
-
const nested = buildSelectSchema(fieldMeta.type, config.select, currentDepth + 1);
|
|
2734
|
-
nestedSchemas["select"] = nested.schema;
|
|
2735
|
-
if (Object.keys(nested.forcedTree).length > 0)
|
|
2736
|
-
relForced.select = nested.forcedTree;
|
|
2737
|
-
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2738
|
-
relForced._countWhere = nested.forcedCountWhere;
|
|
2739
|
-
relForced._countWherePlacement = "select";
|
|
2740
|
-
}
|
|
2741
|
-
}
|
|
2742
|
-
if (config.orderBy) {
|
|
2743
|
-
nestedSchemas["orderBy"] = deps.buildOrderBySchema(fieldMeta.type, config.orderBy);
|
|
2744
|
-
}
|
|
2745
|
-
if (config.cursor) {
|
|
2746
|
-
nestedSchemas["cursor"] = deps.buildCursorSchema(fieldMeta.type, config.cursor);
|
|
2747
|
-
}
|
|
2748
|
-
if (config.take) {
|
|
2749
|
-
nestedSchemas["take"] = deps.buildTakeSchema(config.take);
|
|
2750
|
-
}
|
|
2751
|
-
if (config.skip) {
|
|
2752
|
-
nestedSchemas["skip"] = z6.number().int().min(0).optional();
|
|
2753
|
-
}
|
|
2754
|
-
const nestedObj = z6.object(nestedSchemas).strict();
|
|
2755
|
-
fieldSchemas[relName] = z6.union([z6.literal(true), nestedObj]).optional();
|
|
2756
|
-
if (Object.keys(relForced).length > 0)
|
|
2757
|
-
forcedTree[relName] = relForced;
|
|
2758
|
-
}
|
|
2759
|
-
}
|
|
2760
|
-
const includeFieldKeys = Object.keys(fieldSchemas);
|
|
2761
|
-
const baseSchema = z6.object(fieldSchemas).strict();
|
|
2762
|
-
const schema = includeFieldKeys.length > 0 ? baseSchema.refine(
|
|
2763
|
-
(v) => includeFieldKeys.some((k) => v[k] !== void 0),
|
|
2764
|
-
{ message: "include must specify at least one field" }
|
|
2765
|
-
).optional() : baseSchema.optional();
|
|
2766
|
-
return {
|
|
2767
|
-
schema,
|
|
2768
|
-
forcedTree,
|
|
2769
|
-
forcedCountWhere: topLevelForcedCountWhere
|
|
2770
|
-
};
|
|
2771
|
-
}
|
|
2772
|
-
function buildSelectSchema(model, selectConfig, depth) {
|
|
2773
|
-
const currentDepth = depth ?? 0;
|
|
2774
|
-
if (currentDepth > MAX_PROJECTION_DEPTH) {
|
|
2775
|
-
throw new ShapeError(
|
|
2776
|
-
`Select schema for model "${model}" exceeds maximum nesting depth (${MAX_PROJECTION_DEPTH}). Check for circular relation references in the shape.`
|
|
2777
|
-
);
|
|
2778
|
-
}
|
|
2779
|
-
if (Object.keys(selectConfig).length === 0) {
|
|
2780
|
-
throw new ShapeError(
|
|
2781
|
-
`Empty select config on model "${model}". Define at least one field.`
|
|
2782
|
-
);
|
|
2783
|
-
}
|
|
2784
|
-
const modelFields = typeMap[model];
|
|
2785
|
-
if (!modelFields)
|
|
2786
|
-
throw new ShapeError(`Unknown model: ${model}`);
|
|
2787
|
-
const fieldSchemas = {};
|
|
2788
|
-
const forcedTree = {};
|
|
2789
|
-
let topLevelForcedCountWhere = {};
|
|
2790
|
-
for (const [fieldName, config] of Object.entries(selectConfig)) {
|
|
2791
|
-
if (fieldName === "_count") {
|
|
2792
|
-
const countResult = buildIncludeCountSchema(model, config);
|
|
2793
|
-
fieldSchemas["_count"] = countResult.schema;
|
|
2794
|
-
topLevelForcedCountWhere = countResult.forcedCountWhere;
|
|
2868
|
+
fieldSchemas[fieldName] = z7.literal(true).optional();
|
|
2795
2869
|
continue;
|
|
2796
2870
|
}
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
`nested select for "${fieldName}" on model "${model}"`
|
|
2810
|
-
);
|
|
2811
|
-
if (config.select && config.include) {
|
|
2812
|
-
throw new ShapeError(`Nested select for "${fieldName}" cannot define both "select" and "include".`);
|
|
2813
|
-
}
|
|
2814
|
-
if (!fieldMeta.isList) {
|
|
2815
|
-
if (config.where || config.orderBy || config.cursor || config.take || config.skip) {
|
|
2816
|
-
throw new ShapeError(
|
|
2817
|
-
`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.`
|
|
2818
|
-
);
|
|
2819
|
-
}
|
|
2820
|
-
}
|
|
2821
|
-
const nestedSchemas = {};
|
|
2822
|
-
const relForced = {};
|
|
2823
|
-
if (config.select) {
|
|
2824
|
-
const nested = buildSelectSchema(fieldMeta.type, config.select, currentDepth + 1);
|
|
2825
|
-
nestedSchemas["select"] = nested.schema;
|
|
2826
|
-
if (Object.keys(nested.forcedTree).length > 0)
|
|
2827
|
-
relForced.select = nested.forcedTree;
|
|
2828
|
-
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2829
|
-
relForced._countWhere = nested.forcedCountWhere;
|
|
2830
|
-
relForced._countWherePlacement = "select";
|
|
2831
|
-
}
|
|
2832
|
-
}
|
|
2833
|
-
if (config.include) {
|
|
2834
|
-
const nested = buildIncludeSchema(fieldMeta.type, config.include, currentDepth + 1);
|
|
2835
|
-
nestedSchemas["include"] = nested.schema;
|
|
2836
|
-
if (Object.keys(nested.forcedTree).length > 0)
|
|
2837
|
-
relForced.include = nested.forcedTree;
|
|
2838
|
-
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
2839
|
-
relForced._countWhere = nested.forcedCountWhere;
|
|
2840
|
-
relForced._countWherePlacement = "include";
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2843
|
-
if (config.where) {
|
|
2844
|
-
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
2845
|
-
fieldMeta.type,
|
|
2846
|
-
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.`
|
|
2847
2883
|
);
|
|
2848
|
-
if (whereSchema)
|
|
2849
|
-
nestedSchemas["where"] = whereSchema;
|
|
2850
|
-
if (hasWhereForced(forced))
|
|
2851
|
-
relForced.where = forced;
|
|
2852
|
-
}
|
|
2853
|
-
if (config.orderBy) {
|
|
2854
|
-
nestedSchemas["orderBy"] = deps.buildOrderBySchema(fieldMeta.type, config.orderBy);
|
|
2855
2884
|
}
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
}
|
|
2871
|
-
const selectFieldKeys = Object.keys(fieldSchemas);
|
|
2872
|
-
const baseSchema = z6.object(fieldSchemas).strict();
|
|
2873
|
-
const schema = selectFieldKeys.length > 0 ? baseSchema.refine(
|
|
2874
|
-
(v) => selectFieldKeys.some((k) => v[k] !== void 0),
|
|
2875
|
-
{ message: "select must specify at least one field" }
|
|
2876
|
-
).optional() : baseSchema.optional();
|
|
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();
|
|
2877
2899
|
return {
|
|
2878
2900
|
schema,
|
|
2879
2901
|
forcedTree,
|
|
2880
2902
|
forcedCountWhere: topLevelForcedCountWhere
|
|
2881
2903
|
};
|
|
2882
2904
|
}
|
|
2883
|
-
|
|
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 };
|
|
2884
2912
|
}
|
|
2885
2913
|
|
|
2886
2914
|
// src/shared/operation-shape-keys.ts
|
|
@@ -2890,7 +2918,6 @@ var OPERATION_SHAPE_KEYS = {
|
|
|
2890
2918
|
findFirstOrThrow: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
|
|
2891
2919
|
findUnique: ["where", "include", "select"],
|
|
2892
2920
|
findUniqueOrThrow: ["where", "include", "select"],
|
|
2893
|
-
findManyPaginated: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
|
|
2894
2921
|
count: ["where", "select", "cursor", "orderBy", "skip", "take"],
|
|
2895
2922
|
aggregate: ["where", "orderBy", "cursor", "take", "skip", "_count", "_avg", "_sum", "_min", "_max"],
|
|
2896
2923
|
groupBy: ["where", "by", "having", "_count", "_avg", "_sum", "_min", "_max", "orderBy", "take", "skip"],
|
|
@@ -2925,6 +2952,99 @@ var MUTATION_SHAPE_KEYS = {
|
|
|
2925
2952
|
delete: new Set(OPERATION_SHAPE_KEYS.delete),
|
|
2926
2953
|
deleteMany: new Set(OPERATION_SHAPE_KEYS.deleteMany)
|
|
2927
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
|
+
}
|
|
2928
3048
|
|
|
2929
3049
|
// src/runtime/query-builder.ts
|
|
2930
3050
|
var METHOD_ALLOWED_ARGS = READ_METHOD_ALLOWED_ARGS;
|
|
@@ -3025,6 +3145,90 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3025
3145
|
}
|
|
3026
3146
|
return shapeOrFn;
|
|
3027
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
|
+
}
|
|
3028
3232
|
function buildShapeZodSchema(model, method, shape) {
|
|
3029
3233
|
validateShapeArgs(method, shape);
|
|
3030
3234
|
validateUniqueWhere(model, method, shape);
|
|
@@ -3064,73 +3268,19 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3064
3268
|
}
|
|
3065
3269
|
if (shape.orderBy) {
|
|
3066
3270
|
if (method === "groupBy" && shape.by) {
|
|
3067
|
-
const sortEnum =
|
|
3068
|
-
|
|
3271
|
+
const sortEnum = z8.enum(["asc", "desc"]);
|
|
3272
|
+
schemaFields.orderBy = buildGroupByOrderBySchema(
|
|
3273
|
+
model,
|
|
3274
|
+
shape.orderBy,
|
|
3275
|
+
shape.by,
|
|
3276
|
+
sortEnum
|
|
3277
|
+
);
|
|
3278
|
+
} else {
|
|
3069
3279
|
if (shape.orderBy === true) {
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
groupByOrderFields[field] = sortEnum.optional();
|
|
3073
|
-
}
|
|
3074
|
-
groupByOrderFields._count = sortEnum.optional();
|
|
3075
|
-
const fieldKeys = Object.keys(groupByOrderFields);
|
|
3076
|
-
const singleSchema = z7.object(groupByOrderFields).strict().refine(
|
|
3077
|
-
(v) => fieldKeys.some(
|
|
3078
|
-
(k) => v[k] !== void 0
|
|
3079
|
-
),
|
|
3080
|
-
{ message: "orderBy must specify at least one field" }
|
|
3081
|
-
);
|
|
3082
|
-
schemaFields.orderBy = z7.union([
|
|
3083
|
-
singleSchema,
|
|
3084
|
-
z7.preprocess(coerceToArray, z7.array(singleSchema).min(1))
|
|
3085
|
-
]).optional();
|
|
3086
|
-
} else {
|
|
3087
|
-
const groupByOrderFields = {};
|
|
3088
|
-
for (const [fieldName, config] of Object.entries(shape.orderBy)) {
|
|
3089
|
-
if (fieldName === "_count") {
|
|
3090
|
-
if (config === true) {
|
|
3091
|
-
groupByOrderFields._count = sortEnum.optional();
|
|
3092
|
-
} else if (typeof config === "object" && config !== null) {
|
|
3093
|
-
const countFields = {};
|
|
3094
|
-
for (const countField of Object.keys(config)) {
|
|
3095
|
-
if (!bySet.has(countField)) {
|
|
3096
|
-
throw new ShapeError(
|
|
3097
|
-
`orderBy _count field "${countField}" must be included in "by" for groupBy`
|
|
3098
|
-
);
|
|
3099
|
-
}
|
|
3100
|
-
countFields[countField] = sortEnum.optional();
|
|
3101
|
-
}
|
|
3102
|
-
const countKeys = Object.keys(countFields);
|
|
3103
|
-
groupByOrderFields._count = z7.object(countFields).strict().refine(
|
|
3104
|
-
(v) => countKeys.some(
|
|
3105
|
-
(k) => v[k] !== void 0
|
|
3106
|
-
),
|
|
3107
|
-
{
|
|
3108
|
-
message: "orderBy._count must specify at least one field"
|
|
3109
|
-
}
|
|
3110
|
-
).optional();
|
|
3111
|
-
}
|
|
3112
|
-
continue;
|
|
3113
|
-
}
|
|
3114
|
-
if (!bySet.has(fieldName)) {
|
|
3115
|
-
throw new ShapeError(
|
|
3116
|
-
`orderBy field "${fieldName}" must be included in "by" for groupBy`
|
|
3117
|
-
);
|
|
3118
|
-
}
|
|
3119
|
-
groupByOrderFields[fieldName] = sortEnum.optional();
|
|
3120
|
-
}
|
|
3121
|
-
const fieldKeys = Object.keys(groupByOrderFields);
|
|
3122
|
-
const singleSchema = z7.object(groupByOrderFields).strict().refine(
|
|
3123
|
-
(v) => fieldKeys.some(
|
|
3124
|
-
(k) => v[k] !== void 0
|
|
3125
|
-
),
|
|
3126
|
-
{ 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.`
|
|
3127
3282
|
);
|
|
3128
|
-
schemaFields.orderBy = z7.union([
|
|
3129
|
-
singleSchema,
|
|
3130
|
-
z7.preprocess(coerceToArray, z7.array(singleSchema).min(1))
|
|
3131
|
-
]).optional();
|
|
3132
3283
|
}
|
|
3133
|
-
} else {
|
|
3134
3284
|
schemaFields.orderBy = argsBuilder.buildOrderBySchema(
|
|
3135
3285
|
model,
|
|
3136
3286
|
shape.orderBy
|
|
@@ -3138,19 +3288,16 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3138
3288
|
}
|
|
3139
3289
|
}
|
|
3140
3290
|
if (shape.cursor) {
|
|
3141
|
-
schemaFields.cursor = argsBuilder.buildCursorSchema(
|
|
3142
|
-
model,
|
|
3143
|
-
shape.cursor
|
|
3144
|
-
);
|
|
3291
|
+
schemaFields.cursor = argsBuilder.buildCursorSchema(model, shape.cursor);
|
|
3145
3292
|
}
|
|
3146
|
-
if (shape.take) {
|
|
3293
|
+
if (shape.take !== void 0) {
|
|
3147
3294
|
schemaFields.take = argsBuilder.buildTakeSchema(shape.take);
|
|
3148
3295
|
}
|
|
3149
3296
|
if (shape.skip !== void 0) {
|
|
3150
3297
|
if (shape.skip !== true) {
|
|
3151
3298
|
throw new ShapeError('Shape config "skip" must be true');
|
|
3152
3299
|
}
|
|
3153
|
-
schemaFields.skip =
|
|
3300
|
+
schemaFields.skip = z8.number().int().min(0).optional();
|
|
3154
3301
|
}
|
|
3155
3302
|
if (shape.distinct) {
|
|
3156
3303
|
schemaFields.distinct = argsBuilder.buildDistinctSchema(
|
|
@@ -3197,13 +3344,10 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3197
3344
|
schemaFields.by = argsBuilder.buildBySchema(model, shape.by);
|
|
3198
3345
|
}
|
|
3199
3346
|
if (shape.having) {
|
|
3200
|
-
schemaFields.having = argsBuilder.buildHavingSchema(
|
|
3201
|
-
model,
|
|
3202
|
-
shape.having
|
|
3203
|
-
);
|
|
3347
|
+
schemaFields.having = argsBuilder.buildHavingSchema(model, shape.having);
|
|
3204
3348
|
}
|
|
3205
3349
|
return {
|
|
3206
|
-
zodSchema:
|
|
3350
|
+
zodSchema: z8.object(schemaFields).strict(),
|
|
3207
3351
|
forcedWhere,
|
|
3208
3352
|
forcedOnlyWhereKeys,
|
|
3209
3353
|
forcedIncludeTree,
|
|
@@ -3396,22 +3540,18 @@ function buildScopedUniqueWhere(existingWhere, conditions, scopeFks, log, model)
|
|
|
3396
3540
|
allConditions.push(...conditions);
|
|
3397
3541
|
return { ...topLevel, AND: allConditions };
|
|
3398
3542
|
}
|
|
3399
|
-
function
|
|
3400
|
-
const t = typeof v;
|
|
3401
|
-
return t === "string" || t === "number" || t === "bigint";
|
|
3402
|
-
}
|
|
3403
|
-
function looseEqual(a, b, log, fk) {
|
|
3543
|
+
function scopeValuesEqual(a, b) {
|
|
3404
3544
|
if (a === b)
|
|
3405
3545
|
return true;
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
log.warn(
|
|
3411
|
-
`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.`
|
|
3412
|
-
);
|
|
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);
|
|
3413
3550
|
}
|
|
3414
|
-
|
|
3551
|
+
if (bIsBig && typeof a === "number") {
|
|
3552
|
+
return Number.isInteger(a) && b === BigInt(a);
|
|
3553
|
+
}
|
|
3554
|
+
return false;
|
|
3415
3555
|
}
|
|
3416
3556
|
function buildFkSelect(fks) {
|
|
3417
3557
|
const select = {};
|
|
@@ -3701,7 +3841,7 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log,
|
|
|
3701
3841
|
const result = await query(nextArgs);
|
|
3702
3842
|
if (result === null)
|
|
3703
3843
|
return result;
|
|
3704
|
-
if (typeof result !== "object"
|
|
3844
|
+
if (typeof result !== "object") {
|
|
3705
3845
|
throw new ShapeError(
|
|
3706
3846
|
`${operation} on model "${model}" returned a non-object, non-null result`
|
|
3707
3847
|
);
|
|
@@ -3732,7 +3872,7 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log,
|
|
|
3732
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.`
|
|
3733
3873
|
);
|
|
3734
3874
|
}
|
|
3735
|
-
if (typeof verifyResult !== "object"
|
|
3875
|
+
if (typeof verifyResult !== "object") {
|
|
3736
3876
|
throw new PolicyError(
|
|
3737
3877
|
`prisma-guard: Scope verification re-query on model "${model}" returned a non-object result.`
|
|
3738
3878
|
);
|
|
@@ -3746,7 +3886,7 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log,
|
|
|
3746
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).`
|
|
3747
3887
|
);
|
|
3748
3888
|
}
|
|
3749
|
-
if (!
|
|
3889
|
+
if (!scopeValuesEqual(verifyObj[fk], value)) {
|
|
3750
3890
|
if (operation === "findUniqueOrThrow") {
|
|
3751
3891
|
throw new PolicyError(
|
|
3752
3892
|
`prisma-guard: Record on model "${model}" not accessible in current scope`
|
|
@@ -3766,97 +3906,158 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log,
|
|
|
3766
3906
|
}
|
|
3767
3907
|
|
|
3768
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
|
|
3769
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
|
+
}
|
|
3770
4031
|
|
|
3771
4032
|
// src/runtime/model-guard-data.ts
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
"data",
|
|
3776
|
-
"
|
|
3777
|
-
"
|
|
3778
|
-
])
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
"select",
|
|
3786
|
-
"include",
|
|
3787
|
-
"skipDuplicates"
|
|
3788
|
-
]);
|
|
3789
|
-
var ALLOWED_BODY_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
|
|
3790
|
-
var ALLOWED_BODY_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3791
|
-
"data",
|
|
3792
|
-
"where",
|
|
3793
|
-
"select",
|
|
3794
|
-
"include"
|
|
3795
|
-
]);
|
|
3796
|
-
var ALLOWED_BODY_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
|
|
3797
|
-
var ALLOWED_BODY_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3798
|
-
"where",
|
|
3799
|
-
"select",
|
|
3800
|
-
"include"
|
|
3801
|
-
]);
|
|
3802
|
-
var ALLOWED_BODY_KEYS_UPSERT = /* @__PURE__ */ new Set([
|
|
3803
|
-
"where",
|
|
3804
|
-
"create",
|
|
3805
|
-
"update",
|
|
3806
|
-
"select",
|
|
3807
|
-
"include"
|
|
3808
|
-
]);
|
|
3809
|
-
var VALID_SHAPE_KEYS_CREATE = /* @__PURE__ */ new Set(["data"]);
|
|
3810
|
-
var VALID_SHAPE_KEYS_CREATE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3811
|
-
"data",
|
|
3812
|
-
"select",
|
|
3813
|
-
"include"
|
|
3814
|
-
]);
|
|
3815
|
-
var VALID_SHAPE_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
|
|
3816
|
-
var VALID_SHAPE_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3817
|
-
"data",
|
|
3818
|
-
"where",
|
|
3819
|
-
"select",
|
|
3820
|
-
"include"
|
|
3821
|
-
]);
|
|
3822
|
-
var VALID_SHAPE_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
|
|
3823
|
-
var VALID_SHAPE_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set([
|
|
3824
|
-
"where",
|
|
3825
|
-
"select",
|
|
3826
|
-
"include"
|
|
3827
|
-
]);
|
|
3828
|
-
var VALID_SHAPE_KEYS_UPSERT = /* @__PURE__ */ new Set([
|
|
3829
|
-
"where",
|
|
3830
|
-
"create",
|
|
3831
|
-
"update",
|
|
3832
|
-
"select",
|
|
3833
|
-
"include"
|
|
3834
|
-
]);
|
|
3835
|
-
var KNOWN_RELATION_WRITE_OPS = /* @__PURE__ */ new Set([
|
|
3836
|
-
"connect",
|
|
3837
|
-
"connectOrCreate",
|
|
3838
|
-
"create",
|
|
3839
|
-
"createMany",
|
|
3840
|
-
"disconnect",
|
|
3841
|
-
"delete",
|
|
3842
|
-
"set",
|
|
3843
|
-
"update",
|
|
3844
|
-
"updateMany",
|
|
3845
|
-
"upsert",
|
|
3846
|
-
"deleteMany"
|
|
3847
|
-
]);
|
|
3848
|
-
function validateMutationBodyKeys(body, allowed, method) {
|
|
3849
|
-
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)) {
|
|
3850
4046
|
if (!allowed.has(key)) {
|
|
3851
4047
|
throw new ShapeError(
|
|
3852
|
-
`
|
|
4048
|
+
`Unknown key "${key}" in ${opLabel} config on "${model}.${field}". Allowed: ${[...allowed].join(", ")}`
|
|
3853
4049
|
);
|
|
3854
4050
|
}
|
|
3855
4051
|
}
|
|
3856
4052
|
}
|
|
3857
|
-
function
|
|
3858
|
-
for (const key of Object.keys(
|
|
4053
|
+
function validateAllowedKeys(value, allowed, method, kind) {
|
|
4054
|
+
for (const key of Object.keys(value)) {
|
|
3859
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
|
+
}
|
|
3860
4061
|
throw new ShapeError(
|
|
3861
4062
|
`Shape key "${key}" not valid for ${method}. Allowed: ${[...allowed].join(", ")}`
|
|
3862
4063
|
);
|
|
@@ -3898,24 +4099,24 @@ function buildWhereFieldsSchema(model, config, typeMap, schemaBuilder) {
|
|
|
3898
4099
|
for (const [fieldName, value] of Object.entries(config)) {
|
|
3899
4100
|
if (value !== true)
|
|
3900
4101
|
throw new ShapeError(
|
|
3901
|
-
`Field "${fieldName}" in
|
|
4102
|
+
`Field "${fieldName}" in filter config must be true`
|
|
3902
4103
|
);
|
|
3903
4104
|
const meta = modelFields[fieldName];
|
|
3904
4105
|
if (!meta)
|
|
3905
4106
|
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
3906
4107
|
if (meta.isRelation)
|
|
3907
4108
|
throw new ShapeError(
|
|
3908
|
-
`Relation field "${fieldName}" cannot be used in
|
|
4109
|
+
`Relation field "${fieldName}" cannot be used in filter`
|
|
3909
4110
|
);
|
|
3910
4111
|
fieldSchemas[fieldName] = schemaBuilder.buildFieldSchema(model, fieldName).optional();
|
|
3911
4112
|
fieldKeys.push(fieldName);
|
|
3912
4113
|
}
|
|
3913
|
-
return
|
|
4114
|
+
return z10.object(fieldSchemas).strict().refine(
|
|
3914
4115
|
(v) => fieldKeys.some((k) => v[k] !== void 0),
|
|
3915
|
-
{ message: `At least one field required in
|
|
4116
|
+
{ message: `At least one field required in filter` }
|
|
3916
4117
|
);
|
|
3917
4118
|
}
|
|
3918
|
-
function buildNestedDataSchema(model, config, typeMap, schemaBuilder) {
|
|
4119
|
+
function buildNestedDataSchema(model, config, mode, typeMap, schemaBuilder) {
|
|
3919
4120
|
const modelFields = typeMap[model];
|
|
3920
4121
|
if (!modelFields)
|
|
3921
4122
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -3936,382 +4137,349 @@ function buildNestedDataSchema(model, config, typeMap, schemaBuilder) {
|
|
|
3936
4137
|
throw new ShapeError(
|
|
3937
4138
|
`updatedAt field "${fieldName}" cannot be used in nested data`
|
|
3938
4139
|
);
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
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
|
+
});
|
|
3946
4149
|
}
|
|
3947
|
-
return
|
|
4150
|
+
return z10.object(fieldSchemas).strict();
|
|
3948
4151
|
}
|
|
3949
|
-
function
|
|
3950
|
-
|
|
3951
|
-
|
|
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) {
|
|
3952
4224
|
throw new ShapeError(
|
|
3953
|
-
`
|
|
4225
|
+
`createMany is only valid on to-many relations ("${ctx.model}.${ctx.fieldName}")`
|
|
3954
4226
|
);
|
|
3955
|
-
for (const key of Object.keys(config)) {
|
|
3956
|
-
if (!KNOWN_RELATION_WRITE_OPS.has(key)) {
|
|
3957
|
-
throw new ShapeError(
|
|
3958
|
-
`Unknown relation write operation "${key}" on "${model}.${fieldName}". Allowed: ${[...KNOWN_RELATION_WRITE_OPS].join(", ")}`
|
|
3959
|
-
);
|
|
3960
|
-
}
|
|
3961
4227
|
}
|
|
3962
|
-
const
|
|
3963
|
-
|
|
3964
|
-
|
|
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) {
|
|
3965
4250
|
throw new ShapeError(
|
|
3966
|
-
`
|
|
4251
|
+
`disconnect on to-many relation "${ctx.model}.${ctx.fieldName}" requires unique selector config, not true`
|
|
3967
4252
|
);
|
|
3968
4253
|
}
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
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`
|
|
3974
4259
|
);
|
|
3975
|
-
opSchemas["connect"] = isList ? z8.union([
|
|
3976
|
-
connectSchema,
|
|
3977
|
-
z8.preprocess(coerceToArray, z8.array(connectSchema))
|
|
3978
|
-
]).optional() : connectSchema.optional();
|
|
3979
4260
|
}
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
if (
|
|
3988
|
-
throw new ShapeError(
|
|
3989
|
-
`connectOrCreate on "${model}.${fieldName}" requires "where" object`
|
|
3990
|
-
);
|
|
3991
|
-
}
|
|
3992
|
-
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) {
|
|
3993
4269
|
throw new ShapeError(
|
|
3994
|
-
`
|
|
4270
|
+
`delete on to-many relation "${ctx.model}.${ctx.fieldName}" requires unique selector config, not true`
|
|
3995
4271
|
);
|
|
3996
4272
|
}
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
);
|
|
4003
|
-
const createSchema = buildNestedDataSchema(
|
|
4004
|
-
relatedModelName,
|
|
4005
|
-
coc.create,
|
|
4006
|
-
typeMap,
|
|
4007
|
-
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`
|
|
4008
4278
|
);
|
|
4009
|
-
const cocSchema = z8.object({ where: whereSchema, create: createSchema }).strict();
|
|
4010
|
-
opSchemas["connectOrCreate"] = isList ? z8.union([cocSchema, z8.preprocess(coerceToArray, z8.array(cocSchema))]).optional() : cocSchema.optional();
|
|
4011
4279
|
}
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
typeMap,
|
|
4022
|
-
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}")`
|
|
4023
4289
|
);
|
|
4024
|
-
opSchemas["create"] = isList ? z8.union([
|
|
4025
|
-
createSchema,
|
|
4026
|
-
z8.preprocess(coerceToArray, z8.array(createSchema))
|
|
4027
|
-
]).optional() : createSchema.optional();
|
|
4028
4290
|
}
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
}
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
relatedModelName,
|
|
4048
|
-
cmConfig.data,
|
|
4049
|
-
typeMap,
|
|
4050
|
-
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`
|
|
4051
4309
|
);
|
|
4052
|
-
const
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
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);
|
|
4059
4319
|
}
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
}
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
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);
|
|
4088
4356
|
}
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
throw new ShapeError(
|
|
4093
|
-
`delete on to-many relation "${model}.${fieldName}" requires field config, not true`
|
|
4094
|
-
);
|
|
4095
|
-
}
|
|
4096
|
-
opSchemas["delete"] = z8.literal(true).optional();
|
|
4097
|
-
} else if (isPlainObject(config.delete)) {
|
|
4098
|
-
const deleteSchema = buildWhereFieldsSchema(
|
|
4099
|
-
relatedModelName,
|
|
4100
|
-
config.delete,
|
|
4101
|
-
typeMap,
|
|
4102
|
-
schemaBuilder
|
|
4103
|
-
);
|
|
4104
|
-
if (isList) {
|
|
4105
|
-
opSchemas["delete"] = z8.union([
|
|
4106
|
-
deleteSchema,
|
|
4107
|
-
z8.preprocess(coerceToArray, z8.array(deleteSchema))
|
|
4108
|
-
]).optional();
|
|
4109
|
-
} else {
|
|
4110
|
-
opSchemas["delete"] = z8.union([z8.literal(true), deleteSchema]).optional();
|
|
4111
|
-
}
|
|
4112
|
-
} else {
|
|
4357
|
+
const hasWhereKey = "where" in cfg;
|
|
4358
|
+
if (hasWhereKey) {
|
|
4359
|
+
if (!isPlainObject(cfg.where)) {
|
|
4113
4360
|
throw new ShapeError(
|
|
4114
|
-
`
|
|
4361
|
+
`upsert on to-one "${ctx.model}.${ctx.fieldName}" has invalid "where": must be a plain object of unique selectors`
|
|
4115
4362
|
);
|
|
4116
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();
|
|
4117
4367
|
}
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
throw new ShapeError(
|
|
4126
|
-
`set config on "${model}.${fieldName}" must be an object of field names`
|
|
4127
|
-
);
|
|
4128
|
-
}
|
|
4129
|
-
const setSchema = buildWhereFieldsSchema(
|
|
4130
|
-
relatedModelName,
|
|
4131
|
-
config.set,
|
|
4132
|
-
typeMap,
|
|
4133
|
-
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}")`
|
|
4134
4375
|
);
|
|
4135
|
-
opSchemas["set"] = z8.preprocess(coerceToArray, z8.array(setSchema)).optional();
|
|
4136
|
-
}
|
|
4137
|
-
if (config.update !== void 0) {
|
|
4138
|
-
if (!isPlainObject(config.update)) {
|
|
4139
|
-
throw new ShapeError(
|
|
4140
|
-
`update config on "${model}.${fieldName}" must be an object`
|
|
4141
|
-
);
|
|
4142
|
-
}
|
|
4143
|
-
const updateConfig = config.update;
|
|
4144
|
-
if (isList) {
|
|
4145
|
-
if (!updateConfig.where || !isPlainObject(updateConfig.where)) {
|
|
4146
|
-
throw new ShapeError(
|
|
4147
|
-
`update on to-many "${model}.${fieldName}" requires "where" object`
|
|
4148
|
-
);
|
|
4149
|
-
}
|
|
4150
|
-
if (!updateConfig.data || !isPlainObject(updateConfig.data)) {
|
|
4151
|
-
throw new ShapeError(
|
|
4152
|
-
`update on to-many "${model}.${fieldName}" requires "data" object`
|
|
4153
|
-
);
|
|
4154
|
-
}
|
|
4155
|
-
const whereSchema = buildWhereFieldsSchema(
|
|
4156
|
-
relatedModelName,
|
|
4157
|
-
updateConfig.where,
|
|
4158
|
-
typeMap,
|
|
4159
|
-
schemaBuilder
|
|
4160
|
-
);
|
|
4161
|
-
const dataSchema = buildNestedDataSchema(
|
|
4162
|
-
relatedModelName,
|
|
4163
|
-
updateConfig.data,
|
|
4164
|
-
typeMap,
|
|
4165
|
-
schemaBuilder
|
|
4166
|
-
);
|
|
4167
|
-
const updateSchema = z8.object({ where: whereSchema, data: dataSchema }).strict();
|
|
4168
|
-
opSchemas["update"] = z8.union([
|
|
4169
|
-
updateSchema,
|
|
4170
|
-
z8.preprocess(coerceToArray, z8.array(updateSchema))
|
|
4171
|
-
]).optional();
|
|
4172
|
-
} else {
|
|
4173
|
-
const dataSchema = buildNestedDataSchema(
|
|
4174
|
-
relatedModelName,
|
|
4175
|
-
updateConfig,
|
|
4176
|
-
typeMap,
|
|
4177
|
-
schemaBuilder
|
|
4178
|
-
);
|
|
4179
|
-
opSchemas["update"] = dataSchema.optional();
|
|
4180
|
-
}
|
|
4181
4376
|
}
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
throw new ShapeError(
|
|
4196
|
-
`upsert on "${model}.${fieldName}" requires "update" object`
|
|
4197
|
-
);
|
|
4198
|
-
}
|
|
4199
|
-
const createSchema = buildNestedDataSchema(
|
|
4200
|
-
relatedModelName,
|
|
4201
|
-
upsertConfig.create,
|
|
4202
|
-
typeMap,
|
|
4203
|
-
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`
|
|
4204
4390
|
);
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
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`
|
|
4210
4400
|
);
|
|
4211
|
-
if (isList) {
|
|
4212
|
-
if (!upsertConfig.where || !isPlainObject(upsertConfig.where)) {
|
|
4213
|
-
throw new ShapeError(
|
|
4214
|
-
`upsert on to-many "${model}.${fieldName}" requires "where" object`
|
|
4215
|
-
);
|
|
4216
|
-
}
|
|
4217
|
-
const whereSchema = buildWhereFieldsSchema(
|
|
4218
|
-
relatedModelName,
|
|
4219
|
-
upsertConfig.where,
|
|
4220
|
-
typeMap,
|
|
4221
|
-
schemaBuilder
|
|
4222
|
-
);
|
|
4223
|
-
const upsertSchema = z8.object({
|
|
4224
|
-
where: whereSchema,
|
|
4225
|
-
create: createSchema,
|
|
4226
|
-
update: updateSchema
|
|
4227
|
-
}).strict();
|
|
4228
|
-
opSchemas["upsert"] = z8.union([
|
|
4229
|
-
upsertSchema,
|
|
4230
|
-
z8.preprocess(coerceToArray, z8.array(upsertSchema))
|
|
4231
|
-
]).optional();
|
|
4232
|
-
} else {
|
|
4233
|
-
if (upsertConfig.where) {
|
|
4234
|
-
const whereSchema = buildWhereFieldsSchema(
|
|
4235
|
-
relatedModelName,
|
|
4236
|
-
upsertConfig.where,
|
|
4237
|
-
typeMap,
|
|
4238
|
-
schemaBuilder
|
|
4239
|
-
);
|
|
4240
|
-
const upsertSchema = z8.object({
|
|
4241
|
-
where: whereSchema,
|
|
4242
|
-
create: createSchema,
|
|
4243
|
-
update: updateSchema
|
|
4244
|
-
}).strict();
|
|
4245
|
-
opSchemas["upsert"] = upsertSchema.optional();
|
|
4246
|
-
} else {
|
|
4247
|
-
const upsertSchema = z8.object({ create: createSchema, update: updateSchema }).strict();
|
|
4248
|
-
opSchemas["upsert"] = upsertSchema.optional();
|
|
4249
|
-
}
|
|
4250
|
-
}
|
|
4251
4401
|
}
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
`updateMany on "${model}.${fieldName}" requires "where" object`
|
|
4267
|
-
);
|
|
4268
|
-
}
|
|
4269
|
-
if (!umConfig.data || !isPlainObject(umConfig.data)) {
|
|
4270
|
-
throw new ShapeError(
|
|
4271
|
-
`updateMany on "${model}.${fieldName}" requires "data" object`
|
|
4272
|
-
);
|
|
4273
|
-
}
|
|
4274
|
-
const whereSchema = buildWhereFieldsSchema(
|
|
4275
|
-
relatedModelName,
|
|
4276
|
-
umConfig.where,
|
|
4277
|
-
typeMap,
|
|
4278
|
-
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}")`
|
|
4279
4416
|
);
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
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.`
|
|
4285
4425
|
);
|
|
4286
|
-
const umSchema = z8.object({ where: whereSchema, data: dataSchema }).strict();
|
|
4287
|
-
opSchemas["updateMany"] = z8.union([umSchema, z8.preprocess(coerceToArray, z8.array(umSchema))]).optional();
|
|
4288
4426
|
}
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
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)) {
|
|
4296
4457
|
throw new ShapeError(
|
|
4297
|
-
`
|
|
4458
|
+
`Unknown relation write operation "${key}" on "${model}.${fieldName}". Allowed: ${[...KNOWN_RELATION_WRITE_OPS].join(", ")}`
|
|
4298
4459
|
);
|
|
4299
4460
|
}
|
|
4300
|
-
|
|
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,
|
|
4301
4470
|
relatedModelName,
|
|
4302
|
-
|
|
4471
|
+
isList,
|
|
4472
|
+
config: opConfig,
|
|
4303
4473
|
typeMap,
|
|
4474
|
+
uniqueMap,
|
|
4475
|
+
enumMap,
|
|
4476
|
+
scalarBase,
|
|
4304
4477
|
schemaBuilder
|
|
4305
|
-
);
|
|
4306
|
-
opSchemas["deleteMany"] = z8.union([
|
|
4307
|
-
filterSchema,
|
|
4308
|
-
z8.preprocess(coerceToArray, z8.array(filterSchema)),
|
|
4309
|
-
z8.object({}).strict()
|
|
4310
|
-
]).optional();
|
|
4478
|
+
});
|
|
4311
4479
|
}
|
|
4312
|
-
return
|
|
4480
|
+
return z10.object(opSchemas).strict();
|
|
4313
4481
|
}
|
|
4314
|
-
function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDefaults) {
|
|
4482
|
+
function buildDataSchema(model, dataConfig, mode, typeMap, uniqueMap, enumMap, scalarBase, schemaBuilder, zodDefaults) {
|
|
4315
4483
|
const modelFields = typeMap[model];
|
|
4316
4484
|
if (!modelFields)
|
|
4317
4485
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -4323,11 +4491,23 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4323
4491
|
const fieldMeta = modelFields[fieldName];
|
|
4324
4492
|
if (!fieldMeta) {
|
|
4325
4493
|
if (isUnsupportedMarker(value)) {
|
|
4326
|
-
schemaMap[fieldName] = z8.unknown().optional();
|
|
4327
4494
|
continue;
|
|
4328
4495
|
}
|
|
4329
4496
|
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
4330
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
|
+
}
|
|
4331
4511
|
if (fieldMeta.isRelation) {
|
|
4332
4512
|
if (!isPlainObject(value)) {
|
|
4333
4513
|
throw new ShapeError(
|
|
@@ -4341,6 +4521,9 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4341
4521
|
fieldMeta.isList,
|
|
4342
4522
|
value,
|
|
4343
4523
|
typeMap,
|
|
4524
|
+
uniqueMap,
|
|
4525
|
+
enumMap,
|
|
4526
|
+
scalarBase,
|
|
4344
4527
|
schemaBuilder
|
|
4345
4528
|
).optional();
|
|
4346
4529
|
continue;
|
|
@@ -4350,7 +4533,7 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4350
4533
|
`updatedAt field "${fieldName}" cannot be used in data shape`
|
|
4351
4534
|
);
|
|
4352
4535
|
if (typeof value === "function") {
|
|
4353
|
-
|
|
4536
|
+
const baseSchema = schemaBuilder.buildBaseFieldSchema(
|
|
4354
4537
|
model,
|
|
4355
4538
|
fieldName
|
|
4356
4539
|
);
|
|
@@ -4368,46 +4551,22 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4368
4551
|
`Inline refine for "${model}.${fieldName}" must return a Zod schema`
|
|
4369
4552
|
);
|
|
4370
4553
|
}
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
fieldSchema = fieldSchema.optional();
|
|
4379
|
-
}
|
|
4380
|
-
}
|
|
4381
|
-
} else {
|
|
4382
|
-
if (!fieldMeta.isRequired) {
|
|
4383
|
-
fieldSchema = fieldSchema.nullable().optional();
|
|
4384
|
-
} else {
|
|
4385
|
-
fieldSchema = fieldSchema.optional();
|
|
4386
|
-
}
|
|
4387
|
-
}
|
|
4388
|
-
schemaMap[fieldName] = fieldSchema;
|
|
4389
|
-
} else if (value === true) {
|
|
4390
|
-
let fieldSchema = schemaBuilder.buildFieldSchema(
|
|
4391
|
-
model,
|
|
4392
|
-
fieldName
|
|
4554
|
+
const handlesUndefined = schemaProducesValueForUndefined(
|
|
4555
|
+
refined
|
|
4556
|
+
);
|
|
4557
|
+
schemaMap[fieldName] = applyCreateUpdateNullability(
|
|
4558
|
+
fieldMeta,
|
|
4559
|
+
refined,
|
|
4560
|
+
{ mode, handlesUndefined }
|
|
4393
4561
|
);
|
|
4562
|
+
} else if (value === true) {
|
|
4563
|
+
const fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
|
|
4394
4564
|
const isZodDefaultField = zodDefaultSet !== void 0 && zodDefaultSet.has(fieldName);
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
fieldSchema = fieldSchema.optional();
|
|
4401
|
-
}
|
|
4402
|
-
}
|
|
4403
|
-
} else {
|
|
4404
|
-
if (!fieldMeta.isRequired) {
|
|
4405
|
-
fieldSchema = fieldSchema.nullable().optional();
|
|
4406
|
-
} else {
|
|
4407
|
-
fieldSchema = fieldSchema.optional();
|
|
4408
|
-
}
|
|
4409
|
-
}
|
|
4410
|
-
schemaMap[fieldName] = fieldSchema;
|
|
4565
|
+
schemaMap[fieldName] = applyCreateUpdateNullability(
|
|
4566
|
+
fieldMeta,
|
|
4567
|
+
fieldSchema,
|
|
4568
|
+
{ mode, handlesUndefined: isZodDefaultField }
|
|
4569
|
+
);
|
|
4411
4570
|
} else {
|
|
4412
4571
|
const actualValue = isForcedValue(value) ? value.value : value;
|
|
4413
4572
|
let fieldSchema = schemaBuilder.buildFieldSchema(
|
|
@@ -4451,7 +4610,7 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
|
|
|
4451
4610
|
}
|
|
4452
4611
|
}
|
|
4453
4612
|
return {
|
|
4454
|
-
schema:
|
|
4613
|
+
schema: z10.object(schemaMap).strict(),
|
|
4455
4614
|
forced
|
|
4456
4615
|
};
|
|
4457
4616
|
}
|
|
@@ -4488,106 +4647,120 @@ function isGuardShape(obj) {
|
|
|
4488
4647
|
const keys = Object.keys(obj);
|
|
4489
4648
|
return keys.length === 0 || keys.every((k) => GUARD_SHAPE_KEYS.has(k));
|
|
4490
4649
|
}
|
|
4491
|
-
function
|
|
4492
|
-
|
|
4493
|
-
}
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
return value;
|
|
4497
|
-
if (Array.isArray(value))
|
|
4498
|
-
return value.map(toPlainObject);
|
|
4499
|
-
if (value instanceof Date)
|
|
4500
|
-
return value;
|
|
4501
|
-
if (value instanceof Uint8Array)
|
|
4502
|
-
return value;
|
|
4503
|
-
if (value instanceof RegExp)
|
|
4504
|
-
return value;
|
|
4505
|
-
if (typeof value.toFixed === "function" && typeof value.toNumber === "function")
|
|
4506
|
-
return value;
|
|
4507
|
-
const result = {};
|
|
4508
|
-
for (const [k, v] of Object.entries(value)) {
|
|
4509
|
-
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");
|
|
4510
4655
|
}
|
|
4511
|
-
return
|
|
4656
|
+
return body;
|
|
4512
4657
|
}
|
|
4513
|
-
function
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
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
|
+
}
|
|
4518
4664
|
}
|
|
4519
|
-
function resolveDynamicShape(
|
|
4520
|
-
|
|
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
|
+
}
|
|
4521
4671
|
let result;
|
|
4522
4672
|
try {
|
|
4523
|
-
result =
|
|
4673
|
+
result = shapeFn(ctx);
|
|
4524
4674
|
} catch (err) {
|
|
4525
4675
|
throw new ShapeError(
|
|
4526
|
-
`Dynamic
|
|
4676
|
+
`Dynamic ${context} function threw: ${err.message}`,
|
|
4527
4677
|
{ cause: err }
|
|
4528
4678
|
);
|
|
4529
4679
|
}
|
|
4530
|
-
if (!
|
|
4531
|
-
throw new ShapeError(
|
|
4680
|
+
if (!isGuardShape(result)) {
|
|
4681
|
+
throw new ShapeError(
|
|
4682
|
+
`Dynamic ${context} function must return a valid guard shape object`
|
|
4683
|
+
);
|
|
4532
4684
|
}
|
|
4533
4685
|
return result;
|
|
4534
4686
|
}
|
|
4535
|
-
function
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
return { shape: shape2, body: parsed2, matchedKey: "_default", wasDynamic: wasDynamic2 };
|
|
4541
|
-
}
|
|
4542
|
-
const namedMap = input;
|
|
4543
|
-
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) {
|
|
4544
4692
|
if (GUARD_SHAPE_KEYS.has(key)) {
|
|
4545
4693
|
throw new ShapeError(
|
|
4546
|
-
`Caller key "${key}" collides with reserved shape
|
|
4547
|
-
);
|
|
4548
|
-
}
|
|
4549
|
-
const val = namedMap[key];
|
|
4550
|
-
if (typeof val !== "function" && !isGuardShape(val)) {
|
|
4551
|
-
throw new ShapeError(
|
|
4552
|
-
`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.`
|
|
4553
4695
|
);
|
|
4554
4696
|
}
|
|
4555
4697
|
}
|
|
4556
|
-
const parsed = body === void 0 || body === null ? {} : requireBody(body);
|
|
4557
|
-
if ("caller" in parsed) {
|
|
4558
|
-
throw new CallerError(
|
|
4559
|
-
"Pass caller as second argument to .guard() or via context function, not in the request body."
|
|
4560
|
-
);
|
|
4561
|
-
}
|
|
4562
4698
|
if (typeof caller !== "string") {
|
|
4563
|
-
if ("default" in
|
|
4564
|
-
|
|
4565
|
-
const wasDynamic2 = typeof shapeOrFn2 === "function";
|
|
4566
|
-
const shape2 = wasDynamic2 ? resolveDynamicShape(shapeOrFn2, contextFn) : shapeOrFn2;
|
|
4567
|
-
return { shape: shape2, body: parsed, matchedKey: "default", wasDynamic: wasDynamic2 };
|
|
4699
|
+
if ("default" in input) {
|
|
4700
|
+
return resolveShapeEntry(input.default, body, contextFn, "default");
|
|
4568
4701
|
}
|
|
4569
|
-
const patterns2 = Object.keys(namedMap);
|
|
4570
4702
|
throw new CallerError(
|
|
4571
|
-
`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).`
|
|
4572
4704
|
);
|
|
4573
4705
|
}
|
|
4574
|
-
const
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
}
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
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
|
+
};
|
|
4586
4754
|
}
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
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
|
+
);
|
|
4591
4764
|
}
|
|
4592
4765
|
|
|
4593
4766
|
// src/runtime/model-guard.ts
|
|
@@ -4610,129 +4783,20 @@ var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
|
4610
4783
|
"updateManyAndReturn"
|
|
4611
4784
|
]);
|
|
4612
4785
|
var BATCH_CREATE_METHODS = /* @__PURE__ */ new Set(["createMany", "createManyAndReturn"]);
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
);
|
|
4620
|
-
continue;
|
|
4621
|
-
}
|
|
4622
|
-
if (value === true) {
|
|
4623
|
-
result[key] = true;
|
|
4624
|
-
} else {
|
|
4625
|
-
const nested = {};
|
|
4626
|
-
if (value.select)
|
|
4627
|
-
nested.select = buildDefaultSelectInput(value.select);
|
|
4628
|
-
if (value.include) {
|
|
4629
|
-
nested.include = buildDefaultIncludeInput(value.include);
|
|
4630
|
-
}
|
|
4631
|
-
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
4632
|
-
}
|
|
4633
|
-
}
|
|
4634
|
-
return result;
|
|
4635
|
-
}
|
|
4636
|
-
function buildDefaultIncludeInput(config) {
|
|
4637
|
-
const result = {};
|
|
4638
|
-
for (const [key, value] of Object.entries(config)) {
|
|
4639
|
-
if (key === "_count") {
|
|
4640
|
-
result[key] = buildDefaultCountInput(
|
|
4641
|
-
value
|
|
4642
|
-
);
|
|
4643
|
-
continue;
|
|
4644
|
-
}
|
|
4645
|
-
if (value === true) {
|
|
4646
|
-
result[key] = true;
|
|
4647
|
-
} else {
|
|
4648
|
-
const nested = {};
|
|
4649
|
-
if (value.include) {
|
|
4650
|
-
nested.include = buildDefaultIncludeInput(value.include);
|
|
4651
|
-
}
|
|
4652
|
-
if (value.select)
|
|
4653
|
-
nested.select = buildDefaultSelectInput(value.select);
|
|
4654
|
-
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
4655
|
-
}
|
|
4656
|
-
}
|
|
4657
|
-
return result;
|
|
4658
|
-
}
|
|
4659
|
-
function buildDefaultCountInput(config) {
|
|
4660
|
-
if (config === true)
|
|
4661
|
-
return true;
|
|
4662
|
-
if (!isPlainObject(config) || !config.select || !isPlainObject(config.select)) {
|
|
4663
|
-
return true;
|
|
4664
|
-
}
|
|
4665
|
-
const selectObj = config.select;
|
|
4666
|
-
const result = {};
|
|
4667
|
-
for (const key of Object.keys(selectObj)) {
|
|
4668
|
-
result[key] = true;
|
|
4669
|
-
}
|
|
4670
|
-
return { select: result };
|
|
4671
|
-
}
|
|
4672
|
-
function buildDefaultProjectionBody(shape) {
|
|
4673
|
-
if (shape.select) {
|
|
4674
|
-
return { select: buildDefaultSelectInput(shape.select) };
|
|
4675
|
-
}
|
|
4676
|
-
if (shape.include) {
|
|
4677
|
-
return { include: buildDefaultIncludeInput(shape.include) };
|
|
4678
|
-
}
|
|
4679
|
-
return {};
|
|
4680
|
-
}
|
|
4681
|
-
function applyClientProjectionOverrides(shapeDefault, clientProjection) {
|
|
4682
|
-
const result = { ...shapeDefault };
|
|
4683
|
-
for (const [key, clientValue] of Object.entries(clientProjection)) {
|
|
4684
|
-
const defaultValue = result[key];
|
|
4685
|
-
if (defaultValue === void 0) {
|
|
4686
|
-
result[key] = clientValue;
|
|
4687
|
-
continue;
|
|
4688
|
-
}
|
|
4689
|
-
if (clientValue === true) {
|
|
4690
|
-
result[key] = true;
|
|
4691
|
-
continue;
|
|
4692
|
-
}
|
|
4693
|
-
if (isPlainObject(clientValue)) {
|
|
4694
|
-
if (!isPlainObject(defaultValue)) {
|
|
4695
|
-
result[key] = clientValue;
|
|
4696
|
-
continue;
|
|
4697
|
-
}
|
|
4698
|
-
const merged = { ...defaultValue };
|
|
4699
|
-
const clientObj = clientValue;
|
|
4700
|
-
for (const [argKey, argValue] of Object.entries(clientObj)) {
|
|
4701
|
-
const defaultArgValue = merged[argKey];
|
|
4702
|
-
if ((argKey === "select" || argKey === "include") && isPlainObject(argValue) && isPlainObject(defaultArgValue)) {
|
|
4703
|
-
merged[argKey] = applyClientProjectionOverrides(
|
|
4704
|
-
defaultArgValue,
|
|
4705
|
-
argValue
|
|
4706
|
-
);
|
|
4707
|
-
} else {
|
|
4708
|
-
merged[argKey] = argValue;
|
|
4709
|
-
}
|
|
4710
|
-
}
|
|
4711
|
-
result[key] = merged;
|
|
4712
|
-
}
|
|
4713
|
-
}
|
|
4714
|
-
return result;
|
|
4715
|
-
}
|
|
4716
|
-
function hasClientControlledValues(obj) {
|
|
4717
|
-
for (const value of Object.values(obj)) {
|
|
4718
|
-
if (isForcedValue(value))
|
|
4719
|
-
continue;
|
|
4720
|
-
if (value === true)
|
|
4721
|
-
return true;
|
|
4722
|
-
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))
|
|
4723
4792
|
return true;
|
|
4724
|
-
}
|
|
4725
|
-
return false;
|
|
4726
|
-
}
|
|
4727
|
-
function checkIncludeForClientArgs(config) {
|
|
4728
|
-
for (const [key, value] of Object.entries(config)) {
|
|
4729
4793
|
if (key === "_count") {
|
|
4730
4794
|
if (value !== true && isPlainObject(value) && isPlainObject(value.select)) {
|
|
4731
4795
|
const selectObj = value.select;
|
|
4732
4796
|
for (const entryVal of Object.values(selectObj)) {
|
|
4733
4797
|
if (isPlainObject(entryVal) && entryVal.where) {
|
|
4734
4798
|
const w = entryVal.where;
|
|
4735
|
-
if (isPlainObject(w) && hasClientControlledValues(w))
|
|
4799
|
+
if (isPlainObject(w) && hasClientControlledValues(w, depth + 1))
|
|
4736
4800
|
return true;
|
|
4737
4801
|
}
|
|
4738
4802
|
}
|
|
@@ -4741,54 +4805,41 @@ function checkIncludeForClientArgs(config) {
|
|
|
4741
4805
|
}
|
|
4742
4806
|
if (value === true)
|
|
4743
4807
|
continue;
|
|
4744
|
-
|
|
4808
|
+
const nested = value;
|
|
4809
|
+
if (nested.orderBy || nested.cursor || nested.take || nested.skip)
|
|
4745
4810
|
return true;
|
|
4746
|
-
if (
|
|
4811
|
+
if (nested.where && isPlainObject(nested.where) && hasClientControlledValues(nested.where, depth + 1)) {
|
|
4747
4812
|
return true;
|
|
4748
4813
|
}
|
|
4749
|
-
if (
|
|
4814
|
+
if (nested.include && walkForClientContent(nested.include, predicate, depth + 1))
|
|
4750
4815
|
return true;
|
|
4751
|
-
if (
|
|
4816
|
+
if (nested.select && walkForClientContent(nested.select, predicate, depth + 1))
|
|
4752
4817
|
return true;
|
|
4753
4818
|
}
|
|
4754
4819
|
return false;
|
|
4755
4820
|
}
|
|
4756
|
-
function
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
for (const entryVal of Object.values(selectObj)) {
|
|
4762
|
-
if (isPlainObject(entryVal) && entryVal.where) {
|
|
4763
|
-
const w = entryVal.where;
|
|
4764
|
-
if (isPlainObject(w) && hasClientControlledValues(w))
|
|
4765
|
-
return true;
|
|
4766
|
-
}
|
|
4767
|
-
}
|
|
4768
|
-
}
|
|
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))
|
|
4769
4826
|
continue;
|
|
4770
|
-
}
|
|
4771
4827
|
if (value === true)
|
|
4772
|
-
continue;
|
|
4773
|
-
if (value.orderBy || value.cursor || value.take || value.skip)
|
|
4774
4828
|
return true;
|
|
4775
|
-
if (
|
|
4829
|
+
if (isPlainObject(value) && hasClientControlledValues(value, depth + 1)) {
|
|
4776
4830
|
return true;
|
|
4777
4831
|
}
|
|
4778
|
-
if (value.select && checkSelectForClientArgs(value.select))
|
|
4779
|
-
return true;
|
|
4780
|
-
if (value.include && checkIncludeForClientArgs(value.include))
|
|
4781
|
-
return true;
|
|
4782
4832
|
}
|
|
4783
4833
|
return false;
|
|
4784
4834
|
}
|
|
4785
4835
|
function hasNestedClientControlledArgs(shape) {
|
|
4836
|
+
const predicate = () => false;
|
|
4786
4837
|
if (shape.include) {
|
|
4787
|
-
if (
|
|
4838
|
+
if (walkForClientContent(shape.include, predicate, 0))
|
|
4788
4839
|
return true;
|
|
4789
4840
|
}
|
|
4790
4841
|
if (shape.select) {
|
|
4791
|
-
if (
|
|
4842
|
+
if (walkForClientContent(shape.select, predicate, 0))
|
|
4792
4843
|
return true;
|
|
4793
4844
|
}
|
|
4794
4845
|
return false;
|
|
@@ -4860,77 +4911,62 @@ function createModelGuardExtension(config) {
|
|
|
4860
4911
|
const whereBuiltCache = /* @__PURE__ */ new Map();
|
|
4861
4912
|
const projectionCache = /* @__PURE__ */ new Map();
|
|
4862
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
|
+
}
|
|
4863
4924
|
function getReadShape(method, queryShape, matchedKey, wasDynamic) {
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
const built = queryBuilder.buildShapeZodSchema(
|
|
4925
|
+
return memoize(
|
|
4926
|
+
readShapeCache,
|
|
4927
|
+
`${method}\0${matchedKey}`,
|
|
4928
|
+
wasDynamic,
|
|
4929
|
+
() => queryBuilder.buildShapeZodSchema(
|
|
4870
4930
|
modelName,
|
|
4871
4931
|
method,
|
|
4872
4932
|
queryShape
|
|
4873
|
-
)
|
|
4874
|
-
readShapeCache.set(cacheKey, built);
|
|
4875
|
-
return built;
|
|
4876
|
-
}
|
|
4877
|
-
return queryBuilder.buildShapeZodSchema(
|
|
4878
|
-
modelName,
|
|
4879
|
-
method,
|
|
4880
|
-
queryShape
|
|
4933
|
+
)
|
|
4881
4934
|
);
|
|
4882
4935
|
}
|
|
4883
4936
|
function getDataSchema(mode, dataConfig, matchedKey, wasDynamic) {
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4937
|
+
const skipCache = wasDynamic || hasDataRefines(dataConfig);
|
|
4938
|
+
return memoize(
|
|
4939
|
+
dataSchemaCache,
|
|
4940
|
+
`${mode}\0${matchedKey}`,
|
|
4941
|
+
skipCache,
|
|
4942
|
+
() => buildDataSchema(
|
|
4890
4943
|
modelName,
|
|
4891
4944
|
dataConfig,
|
|
4892
4945
|
mode,
|
|
4893
4946
|
typeMap,
|
|
4947
|
+
uniqueMap,
|
|
4948
|
+
enumMap,
|
|
4949
|
+
scalarBase,
|
|
4894
4950
|
schemaBuilder,
|
|
4895
4951
|
zodDefaults
|
|
4896
|
-
)
|
|
4897
|
-
dataSchemaCache.set(cacheKey, built);
|
|
4898
|
-
return built;
|
|
4899
|
-
}
|
|
4900
|
-
return buildDataSchema(
|
|
4901
|
-
modelName,
|
|
4902
|
-
dataConfig,
|
|
4903
|
-
mode,
|
|
4904
|
-
typeMap,
|
|
4905
|
-
schemaBuilder,
|
|
4906
|
-
zodDefaults
|
|
4952
|
+
)
|
|
4907
4953
|
);
|
|
4908
4954
|
}
|
|
4909
4955
|
function getWhereBuilt(whereConfig, matchedKey, wasDynamic) {
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
return built;
|
|
4917
|
-
}
|
|
4918
|
-
return queryBuilder.buildWhereSchema(modelName, whereConfig);
|
|
4956
|
+
return memoize(
|
|
4957
|
+
whereBuiltCache,
|
|
4958
|
+
matchedKey,
|
|
4959
|
+
wasDynamic,
|
|
4960
|
+
() => queryBuilder.buildWhereSchema(modelName, whereConfig)
|
|
4961
|
+
);
|
|
4919
4962
|
}
|
|
4920
4963
|
function getUniqueWhereBuilt(whereConfig, matchedKey, wasDynamic) {
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
modelName,
|
|
4928
|
-
whereConfig
|
|
4929
|
-
);
|
|
4930
|
-
uniqueWhereCache.set(cacheKey, built);
|
|
4931
|
-
return built;
|
|
4932
|
-
}
|
|
4933
|
-
return queryBuilder.buildUniqueWhereSchema(modelName, whereConfig);
|
|
4964
|
+
return memoize(
|
|
4965
|
+
uniqueWhereCache,
|
|
4966
|
+
`unique\0${matchedKey}`,
|
|
4967
|
+
wasDynamic,
|
|
4968
|
+
() => queryBuilder.buildUniqueWhereSchema(modelName, whereConfig)
|
|
4969
|
+
);
|
|
4934
4970
|
}
|
|
4935
4971
|
function buildProjectionSchema(shape) {
|
|
4936
4972
|
const schemaFields = {};
|
|
@@ -4957,7 +4993,7 @@ function createModelGuardExtension(config) {
|
|
|
4957
4993
|
forcedSelectCountWhere = result.forcedCountWhere;
|
|
4958
4994
|
}
|
|
4959
4995
|
return {
|
|
4960
|
-
zodSchema:
|
|
4996
|
+
zodSchema: z11.object(schemaFields).strict(),
|
|
4961
4997
|
forcedIncludeTree,
|
|
4962
4998
|
forcedSelectTree,
|
|
4963
4999
|
forcedIncludeCountWhere,
|
|
@@ -4965,16 +5001,12 @@ function createModelGuardExtension(config) {
|
|
|
4965
5001
|
};
|
|
4966
5002
|
}
|
|
4967
5003
|
function getProjection(shape, matchedKey, wasDynamic) {
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
projectionCache.set(cacheKey, built);
|
|
4975
|
-
return built;
|
|
4976
|
-
}
|
|
4977
|
-
return buildProjectionSchema(shape);
|
|
5004
|
+
return memoize(
|
|
5005
|
+
projectionCache,
|
|
5006
|
+
`projection\0${matchedKey}`,
|
|
5007
|
+
wasDynamic,
|
|
5008
|
+
() => buildProjectionSchema(shape)
|
|
5009
|
+
);
|
|
4978
5010
|
}
|
|
4979
5011
|
function resolveProjection(shape, parsed, method, matchedKey, wasDynamic) {
|
|
4980
5012
|
const hasBodyProjection = "select" in parsed || "include" in parsed;
|
|
@@ -5168,24 +5200,11 @@ function createModelGuardExtension(config) {
|
|
|
5168
5200
|
const hasShapeProjection = !!resolved.shape.select || !!resolved.shape.include;
|
|
5169
5201
|
if (!hasShapeProjection)
|
|
5170
5202
|
return resolved.body;
|
|
5171
|
-
const defaultProjection = buildDefaultProjectionBody(resolved.shape);
|
|
5172
5203
|
const hasBodyProjection = "select" in resolved.body || "include" in resolved.body;
|
|
5173
|
-
if (
|
|
5174
|
-
return
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
if ("select" in resolved.body && "select" in defaultProjection && isPlainObject(resolved.body.select) && isPlainObject(defaultProjection.select)) {
|
|
5178
|
-
merged.select = applyClientProjectionOverrides(
|
|
5179
|
-
defaultProjection.select,
|
|
5180
|
-
resolved.body.select
|
|
5181
|
-
);
|
|
5182
|
-
} else if ("include" in resolved.body && "include" in defaultProjection && isPlainObject(resolved.body.include) && isPlainObject(defaultProjection.include)) {
|
|
5183
|
-
merged.include = applyClientProjectionOverrides(
|
|
5184
|
-
defaultProjection.include,
|
|
5185
|
-
resolved.body.include
|
|
5186
|
-
);
|
|
5187
|
-
}
|
|
5188
|
-
return merged;
|
|
5204
|
+
if (hasBodyProjection)
|
|
5205
|
+
return resolved.body;
|
|
5206
|
+
const defaultProjection = buildDefaultProjectionBody(resolved.shape);
|
|
5207
|
+
return { ...resolved.body, ...defaultProjection };
|
|
5189
5208
|
}
|
|
5190
5209
|
function makeReadMethod(method) {
|
|
5191
5210
|
return (body) => {
|
|
@@ -5218,29 +5237,21 @@ function createModelGuardExtension(config) {
|
|
|
5218
5237
|
function makeCreateMethod(method) {
|
|
5219
5238
|
const isBatch = BATCH_CREATE_METHODS.has(method);
|
|
5220
5239
|
const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_MANY_PROJECTION;
|
|
5224
|
-
} else if (isBatch) {
|
|
5225
|
-
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_MANY;
|
|
5226
|
-
} else if (supportsProjection) {
|
|
5227
|
-
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_PROJECTION;
|
|
5228
|
-
} else {
|
|
5229
|
-
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE;
|
|
5230
|
-
}
|
|
5231
|
-
const allowedShapeKeys = supportsProjection ? VALID_SHAPE_KEYS_CREATE_PROJECTION : VALID_SHAPE_KEYS_CREATE;
|
|
5240
|
+
const allowedBodyKeys = getAllowedBodyKeys(method, supportsProjection);
|
|
5241
|
+
const allowedShapeKeys = getAllowedShapeKeys(method, supportsProjection);
|
|
5232
5242
|
return (body) => {
|
|
5233
5243
|
const caller = resolveCaller();
|
|
5234
5244
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
5235
5245
|
if (!resolved.shape.data) {
|
|
5236
5246
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
5237
5247
|
}
|
|
5238
|
-
|
|
5248
|
+
validateAllowedKeys(
|
|
5239
5249
|
resolved.shape,
|
|
5240
5250
|
allowedShapeKeys,
|
|
5241
|
-
method
|
|
5251
|
+
method,
|
|
5252
|
+
"shape"
|
|
5242
5253
|
);
|
|
5243
|
-
|
|
5254
|
+
validateAllowedKeys(resolved.body, allowedBodyKeys, method, "body");
|
|
5244
5255
|
const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
|
|
5245
5256
|
validateCreateCompleteness(
|
|
5246
5257
|
modelName,
|
|
@@ -5299,20 +5310,21 @@ function createModelGuardExtension(config) {
|
|
|
5299
5310
|
const isUniqueWhere = method === "update";
|
|
5300
5311
|
const isBulk = BULK_MUTATION_METHODS.has(method);
|
|
5301
5312
|
const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
|
|
5302
|
-
const allowedBodyKeys = supportsProjection
|
|
5303
|
-
const allowedShapeKeys = supportsProjection
|
|
5313
|
+
const allowedBodyKeys = getAllowedBodyKeys(method, supportsProjection);
|
|
5314
|
+
const allowedShapeKeys = getAllowedShapeKeys(method, supportsProjection);
|
|
5304
5315
|
return (body) => {
|
|
5305
5316
|
const caller = resolveCaller();
|
|
5306
5317
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
5307
5318
|
if (!resolved.shape.data) {
|
|
5308
5319
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
5309
5320
|
}
|
|
5310
|
-
|
|
5321
|
+
validateAllowedKeys(
|
|
5311
5322
|
resolved.shape,
|
|
5312
5323
|
allowedShapeKeys,
|
|
5313
|
-
method
|
|
5324
|
+
method,
|
|
5325
|
+
"shape"
|
|
5314
5326
|
);
|
|
5315
|
-
|
|
5327
|
+
validateAllowedKeys(resolved.body, allowedBodyKeys, method, "body");
|
|
5316
5328
|
if (isBulk && !resolved.shape.where) {
|
|
5317
5329
|
throw new ShapeError(
|
|
5318
5330
|
`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
|
|
@@ -5380,20 +5392,21 @@ function createModelGuardExtension(config) {
|
|
|
5380
5392
|
const isUniqueWhere = method === "delete";
|
|
5381
5393
|
const isBulk = BULK_MUTATION_METHODS.has(method);
|
|
5382
5394
|
const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
|
|
5383
|
-
const allowedBodyKeys = supportsProjection
|
|
5384
|
-
const allowedShapeKeys = supportsProjection
|
|
5395
|
+
const allowedBodyKeys = getAllowedBodyKeys(method, supportsProjection);
|
|
5396
|
+
const allowedShapeKeys = getAllowedShapeKeys(method, supportsProjection);
|
|
5385
5397
|
return (body) => {
|
|
5386
5398
|
const caller = resolveCaller();
|
|
5387
5399
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
5388
5400
|
if (resolved.shape.data) {
|
|
5389
5401
|
throw new ShapeError(`Guard shape "data" is not valid for ${method}`);
|
|
5390
5402
|
}
|
|
5391
|
-
|
|
5403
|
+
validateAllowedKeys(
|
|
5392
5404
|
resolved.shape,
|
|
5393
5405
|
allowedShapeKeys,
|
|
5394
|
-
method
|
|
5406
|
+
method,
|
|
5407
|
+
"shape"
|
|
5395
5408
|
);
|
|
5396
|
-
|
|
5409
|
+
validateAllowedKeys(resolved.body, allowedBodyKeys, method, "body");
|
|
5397
5410
|
if (isBulk && !resolved.shape.where) {
|
|
5398
5411
|
throw new ShapeError(
|
|
5399
5412
|
`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
|
|
@@ -5446,6 +5459,8 @@ function createModelGuardExtension(config) {
|
|
|
5446
5459
|
};
|
|
5447
5460
|
}
|
|
5448
5461
|
function makeUpsertMethod() {
|
|
5462
|
+
const allowedBodyKeys = getAllowedBodyKeys("upsert", true);
|
|
5463
|
+
const allowedShapeKeys = getAllowedShapeKeys("upsert", true);
|
|
5449
5464
|
return (body) => {
|
|
5450
5465
|
const caller = resolveCaller();
|
|
5451
5466
|
const resolved = resolveShape(input, body, contextFn, caller);
|
|
@@ -5463,15 +5478,17 @@ function createModelGuardExtension(config) {
|
|
|
5463
5478
|
if (!resolved.shape.where) {
|
|
5464
5479
|
throw new ShapeError('Guard shape requires "where" for upsert');
|
|
5465
5480
|
}
|
|
5466
|
-
|
|
5481
|
+
validateAllowedKeys(
|
|
5467
5482
|
resolved.shape,
|
|
5468
|
-
|
|
5469
|
-
"upsert"
|
|
5483
|
+
allowedShapeKeys,
|
|
5484
|
+
"upsert",
|
|
5485
|
+
"shape"
|
|
5470
5486
|
);
|
|
5471
|
-
|
|
5487
|
+
validateAllowedKeys(
|
|
5472
5488
|
resolved.body,
|
|
5473
|
-
|
|
5474
|
-
"upsert"
|
|
5489
|
+
allowedBodyKeys,
|
|
5490
|
+
"upsert",
|
|
5491
|
+
"body"
|
|
5475
5492
|
);
|
|
5476
5493
|
validateUniqueWhereShapeConfig(
|
|
5477
5494
|
modelName,
|
|
@@ -5561,12 +5578,7 @@ function createModelGuardExtension(config) {
|
|
|
5561
5578
|
try {
|
|
5562
5579
|
return fn(body);
|
|
5563
5580
|
} catch (err) {
|
|
5564
|
-
|
|
5565
|
-
throw new ShapeError(`Validation failed: ${formatZodError(err)}`, {
|
|
5566
|
-
cause: err
|
|
5567
|
-
});
|
|
5568
|
-
}
|
|
5569
|
-
throw err;
|
|
5581
|
+
throw toShapeError(err);
|
|
5570
5582
|
}
|
|
5571
5583
|
};
|
|
5572
5584
|
}
|
|
@@ -5599,6 +5611,15 @@ function createModelGuardExtension(config) {
|
|
|
5599
5611
|
}
|
|
5600
5612
|
|
|
5601
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
|
+
}
|
|
5602
5623
|
function createGuard(config) {
|
|
5603
5624
|
const scalarBase = createScalarBase(
|
|
5604
5625
|
config.guardConfig.strictDecimal ?? false
|
|
@@ -5620,14 +5641,6 @@ function createGuard(config) {
|
|
|
5620
5641
|
warn: (msg) => console.warn(msg)
|
|
5621
5642
|
};
|
|
5622
5643
|
const wrapZodErrors = config.wrapZodErrors ?? false;
|
|
5623
|
-
function rethrowZod(err) {
|
|
5624
|
-
if (err instanceof ZodError) {
|
|
5625
|
-
throw new ShapeError(`Validation failed: ${formatZodError(err)}`, {
|
|
5626
|
-
cause: err
|
|
5627
|
-
});
|
|
5628
|
-
}
|
|
5629
|
-
throw err;
|
|
5630
|
-
}
|
|
5631
5644
|
return {
|
|
5632
5645
|
input: (model, opts) => {
|
|
5633
5646
|
const result = schemaBuilder.buildInputSchema(model, opts);
|
|
@@ -5635,13 +5648,7 @@ function createGuard(config) {
|
|
|
5635
5648
|
return result;
|
|
5636
5649
|
return {
|
|
5637
5650
|
schema: result.schema,
|
|
5638
|
-
parse(
|
|
5639
|
-
try {
|
|
5640
|
-
return result.parse(data);
|
|
5641
|
-
} catch (err) {
|
|
5642
|
-
rethrowZod(err);
|
|
5643
|
-
}
|
|
5644
|
-
}
|
|
5651
|
+
parse: wrapParseFn(result.parse)
|
|
5645
5652
|
};
|
|
5646
5653
|
},
|
|
5647
5654
|
model: (model, opts) => schemaBuilder.buildModelSchema(model, opts),
|
|
@@ -5651,13 +5658,7 @@ function createGuard(config) {
|
|
|
5651
5658
|
return qs;
|
|
5652
5659
|
return {
|
|
5653
5660
|
schemas: qs.schemas,
|
|
5654
|
-
parse(
|
|
5655
|
-
try {
|
|
5656
|
-
return qs.parse(body, opts);
|
|
5657
|
-
} catch (err) {
|
|
5658
|
-
rethrowZod(err);
|
|
5659
|
-
}
|
|
5660
|
-
}
|
|
5661
|
+
parse: wrapParseFn(qs.parse)
|
|
5661
5662
|
};
|
|
5662
5663
|
},
|
|
5663
5664
|
extension: (contextFn) => {
|