prisma-guard 1.1.0 → 1.2.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 +365 -89
- package/dist/generator/index.js +98 -16
- package/dist/generator/index.js.map +1 -1
- package/dist/runtime/index.d.ts +7 -4
- package/dist/runtime/index.js +1165 -890
- package/dist/runtime/index.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/index.js
CHANGED
|
@@ -26,6 +26,12 @@ var CallerError = class extends Error {
|
|
|
26
26
|
this.name = "CallerError";
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
|
+
function formatZodError(err) {
|
|
30
|
+
return err.issues.map((i) => {
|
|
31
|
+
const p = i.path.length > 0 ? `${i.path.join(".")}: ` : "";
|
|
32
|
+
return `${p}${i.message}`;
|
|
33
|
+
}).join("; ");
|
|
34
|
+
}
|
|
29
35
|
|
|
30
36
|
// src/runtime/schema-builder.ts
|
|
31
37
|
import { z as z3 } from "zod";
|
|
@@ -36,9 +42,15 @@ import { z as z2 } from "zod";
|
|
|
36
42
|
// src/shared/scalar-base.ts
|
|
37
43
|
import { z } from "zod";
|
|
38
44
|
function isJsonSafe(value) {
|
|
39
|
-
const stack = [value];
|
|
45
|
+
const stack = [{ tag: "visit", value }];
|
|
46
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
40
47
|
while (stack.length > 0) {
|
|
41
|
-
const
|
|
48
|
+
const entry = stack.pop();
|
|
49
|
+
if (entry.tag === "exit") {
|
|
50
|
+
ancestors.delete(entry.ref);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const current = entry.value;
|
|
42
54
|
if (current === void 0)
|
|
43
55
|
return false;
|
|
44
56
|
if (current === null)
|
|
@@ -52,9 +64,13 @@ function isJsonSafe(value) {
|
|
|
52
64
|
return false;
|
|
53
65
|
continue;
|
|
54
66
|
case "object": {
|
|
67
|
+
if (ancestors.has(current))
|
|
68
|
+
return false;
|
|
69
|
+
ancestors.add(current);
|
|
70
|
+
stack.push({ tag: "exit", ref: current });
|
|
55
71
|
if (Array.isArray(current)) {
|
|
56
72
|
for (let i = 0; i < current.length; i++) {
|
|
57
|
-
stack.push(current[i]);
|
|
73
|
+
stack.push({ tag: "visit", value: current[i] });
|
|
58
74
|
}
|
|
59
75
|
continue;
|
|
60
76
|
}
|
|
@@ -63,7 +79,7 @@ function isJsonSafe(value) {
|
|
|
63
79
|
return false;
|
|
64
80
|
const values = Object.values(current);
|
|
65
81
|
for (let i = 0; i < values.length; i++) {
|
|
66
|
-
stack.push(values[i]);
|
|
82
|
+
stack.push({ tag: "visit", value: values[i] });
|
|
67
83
|
}
|
|
68
84
|
continue;
|
|
69
85
|
}
|
|
@@ -82,6 +98,10 @@ var SCALAR_BASE = {
|
|
|
82
98
|
z.string().refine(
|
|
83
99
|
(s) => /^-?(\d+\.?\d*|\.\d+)([eE]-?\d+)?$/.test(s),
|
|
84
100
|
"Invalid decimal string"
|
|
101
|
+
),
|
|
102
|
+
z.custom(
|
|
103
|
+
(v) => v !== null && typeof v === "object" && typeof v.toFixed === "function" && typeof v.toNumber === "function",
|
|
104
|
+
"Expected Decimal-compatible object"
|
|
85
105
|
)
|
|
86
106
|
]),
|
|
87
107
|
BigInt: () => z.union([
|
|
@@ -98,7 +118,7 @@ var SCALAR_BASE = {
|
|
|
98
118
|
z.string().datetime({ offset: true }),
|
|
99
119
|
z.string().datetime()
|
|
100
120
|
]).pipe(z.coerce.date()),
|
|
101
|
-
Json: () => z.unknown().refine(isJsonSafe, "Value must be JSON-serializable (no undefined, functions, symbols, class instances, NaN, or
|
|
121
|
+
Json: () => z.unknown().refine(isJsonSafe, "Value must be JSON-serializable (no undefined, functions, symbols, class instances, NaN, Infinity, or circular references)"),
|
|
102
122
|
Bytes: () => z.union([
|
|
103
123
|
z.string(),
|
|
104
124
|
z.custom((v) => v instanceof Uint8Array)
|
|
@@ -238,6 +258,12 @@ function lruSet(cache, key, value, maxSize) {
|
|
|
238
258
|
cache.delete(oldest);
|
|
239
259
|
}
|
|
240
260
|
}
|
|
261
|
+
function isZodSchema(value) {
|
|
262
|
+
if (value == null || typeof value !== "object")
|
|
263
|
+
return false;
|
|
264
|
+
const v = value;
|
|
265
|
+
return typeof v.parse === "function" && typeof v.optional === "function";
|
|
266
|
+
}
|
|
241
267
|
function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
242
268
|
const chainCache = /* @__PURE__ */ new Map();
|
|
243
269
|
function buildFieldSchema(model, field) {
|
|
@@ -281,7 +307,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
281
307
|
throw new ShapeError('InputOpts cannot define both "pick" and "omit"');
|
|
282
308
|
}
|
|
283
309
|
const mode = opts.mode ?? "create";
|
|
284
|
-
const allowNull = opts.allowNull ??
|
|
310
|
+
const allowNull = opts.allowNull ?? true;
|
|
285
311
|
const modelFields = typeMap[model];
|
|
286
312
|
if (!modelFields)
|
|
287
313
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -311,7 +337,21 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
311
337
|
const fieldMeta = modelFields[name];
|
|
312
338
|
let fieldSchema;
|
|
313
339
|
if (opts.refine?.[name]) {
|
|
314
|
-
|
|
340
|
+
let refined;
|
|
341
|
+
try {
|
|
342
|
+
refined = opts.refine[name](buildBaseFieldSchema(model, name));
|
|
343
|
+
} catch (err) {
|
|
344
|
+
throw new ShapeError(
|
|
345
|
+
`Refine function for "${model}.${name}" threw: ${err.message}`,
|
|
346
|
+
{ cause: err }
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (!isZodSchema(refined)) {
|
|
350
|
+
throw new ShapeError(
|
|
351
|
+
`Refine function for "${model}.${name}" must return a Zod schema`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
fieldSchema = refined;
|
|
315
355
|
} else {
|
|
316
356
|
fieldSchema = buildFieldSchema(model, name);
|
|
317
357
|
}
|
|
@@ -410,10 +450,12 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
410
450
|
schemaMap[relName] = relSchema;
|
|
411
451
|
}
|
|
412
452
|
if (opts._count) {
|
|
413
|
-
const
|
|
453
|
+
const listRelationNames = Object.keys(modelFields).filter(
|
|
454
|
+
(n) => modelFields[n].isRelation && modelFields[n].isList
|
|
455
|
+
);
|
|
414
456
|
if (opts._count === true) {
|
|
415
457
|
const countFields = {};
|
|
416
|
-
for (const relName of
|
|
458
|
+
for (const relName of listRelationNames) {
|
|
417
459
|
countFields[relName] = z3.number().int().min(0);
|
|
418
460
|
}
|
|
419
461
|
schemaMap["_count"] = z3.object(countFields);
|
|
@@ -424,6 +466,8 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
424
466
|
throw new ShapeError(`Unknown field "${relName}" on model "${model}" in _count`);
|
|
425
467
|
if (!modelFields[relName].isRelation)
|
|
426
468
|
throw new ShapeError(`Field "${relName}" is not a relation on model "${model}" in _count`);
|
|
469
|
+
if (!modelFields[relName].isList)
|
|
470
|
+
throw new ShapeError(`Field "${relName}" is a to-one relation on model "${model}" in _count. Only to-many relations support _count.`);
|
|
427
471
|
countFields[relName] = z3.number().int().min(0);
|
|
428
472
|
}
|
|
429
473
|
schemaMap["_count"] = z3.object(countFields);
|
|
@@ -439,7 +483,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
439
483
|
}
|
|
440
484
|
|
|
441
485
|
// src/runtime/query-builder.ts
|
|
442
|
-
import { z as
|
|
486
|
+
import { z as z7 } from "zod";
|
|
443
487
|
|
|
444
488
|
// src/shared/constants.ts
|
|
445
489
|
var SHAPE_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -463,6 +507,13 @@ var GUARD_SHAPE_KEYS = /* @__PURE__ */ new Set([
|
|
|
463
507
|
"data",
|
|
464
508
|
...SHAPE_CONFIG_KEYS
|
|
465
509
|
]);
|
|
510
|
+
var COMBINATOR_KEYS = /* @__PURE__ */ new Set(["AND", "OR", "NOT"]);
|
|
511
|
+
var TO_MANY_RELATION_OPS = /* @__PURE__ */ new Set(["some", "every", "none"]);
|
|
512
|
+
var TO_ONE_RELATION_OPS = /* @__PURE__ */ new Set(["is", "isNot"]);
|
|
513
|
+
var ALL_RELATION_OPS = /* @__PURE__ */ new Set([...TO_MANY_RELATION_OPS, ...TO_ONE_RELATION_OPS]);
|
|
514
|
+
function toDelegateKey(modelName) {
|
|
515
|
+
return modelName[0].toLowerCase() + modelName.slice(1);
|
|
516
|
+
}
|
|
466
517
|
|
|
467
518
|
// src/shared/match-caller.ts
|
|
468
519
|
function matchCallerPattern(patterns, caller) {
|
|
@@ -498,130 +549,107 @@ function matchCallerPattern(patterns, caller) {
|
|
|
498
549
|
return matches[0];
|
|
499
550
|
}
|
|
500
551
|
|
|
552
|
+
// src/shared/is-plain-object.ts
|
|
553
|
+
function isPlainObject(v) {
|
|
554
|
+
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
555
|
+
return false;
|
|
556
|
+
const proto = Object.getPrototypeOf(v);
|
|
557
|
+
return proto === Object.prototype || proto === null;
|
|
558
|
+
}
|
|
559
|
+
|
|
501
560
|
// src/runtime/policy.ts
|
|
502
561
|
function requireContext(ctx, label) {
|
|
503
562
|
if (ctx === void 0 || ctx === null) {
|
|
504
563
|
throw new PolicyError(`Context required for ${label}`);
|
|
505
564
|
}
|
|
506
565
|
}
|
|
566
|
+
function validateContext(ctx) {
|
|
567
|
+
if (ctx === null || ctx === void 0) {
|
|
568
|
+
throw new PolicyError(
|
|
569
|
+
`guard.extension() context function must return a plain object, got ${String(ctx)}`
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
if (!isPlainObject(ctx)) {
|
|
573
|
+
throw new PolicyError(
|
|
574
|
+
`guard.extension() context function must return a plain object, got ${Array.isArray(ctx) ? "array" : typeof ctx}`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return ctx;
|
|
578
|
+
}
|
|
507
579
|
|
|
508
|
-
// src/runtime/query-builder.ts
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
"cursor",
|
|
516
|
-
"take",
|
|
517
|
-
"skip",
|
|
518
|
-
"distinct"
|
|
519
|
-
]),
|
|
520
|
-
findFirst: /* @__PURE__ */ new Set([
|
|
521
|
-
"where",
|
|
522
|
-
"include",
|
|
523
|
-
"select",
|
|
524
|
-
"orderBy",
|
|
525
|
-
"cursor",
|
|
526
|
-
"take",
|
|
527
|
-
"skip",
|
|
528
|
-
"distinct"
|
|
529
|
-
]),
|
|
530
|
-
findFirstOrThrow: /* @__PURE__ */ new Set([
|
|
531
|
-
"where",
|
|
532
|
-
"include",
|
|
533
|
-
"select",
|
|
534
|
-
"orderBy",
|
|
535
|
-
"cursor",
|
|
536
|
-
"take",
|
|
537
|
-
"skip",
|
|
538
|
-
"distinct"
|
|
539
|
-
]),
|
|
540
|
-
findUnique: /* @__PURE__ */ new Set(["where", "include", "select"]),
|
|
541
|
-
findUniqueOrThrow: /* @__PURE__ */ new Set(["where", "include", "select"]),
|
|
542
|
-
count: /* @__PURE__ */ new Set(["where", "select", "cursor", "orderBy", "skip", "take"]),
|
|
543
|
-
aggregate: /* @__PURE__ */ new Set([
|
|
544
|
-
"where",
|
|
545
|
-
"orderBy",
|
|
546
|
-
"cursor",
|
|
547
|
-
"take",
|
|
548
|
-
"skip",
|
|
549
|
-
"_count",
|
|
550
|
-
"_avg",
|
|
551
|
-
"_sum",
|
|
552
|
-
"_min",
|
|
553
|
-
"_max"
|
|
554
|
-
]),
|
|
555
|
-
groupBy: /* @__PURE__ */ new Set([
|
|
556
|
-
"where",
|
|
557
|
-
"by",
|
|
558
|
-
"having",
|
|
559
|
-
"_count",
|
|
560
|
-
"_avg",
|
|
561
|
-
"_sum",
|
|
562
|
-
"_min",
|
|
563
|
-
"_max",
|
|
564
|
-
"orderBy",
|
|
565
|
-
"take",
|
|
566
|
-
"skip"
|
|
567
|
-
])
|
|
568
|
-
};
|
|
569
|
-
var UNIQUE_WHERE_METHODS = /* @__PURE__ */ new Set([
|
|
570
|
-
"findUnique",
|
|
571
|
-
"findUniqueOrThrow"
|
|
572
|
-
]);
|
|
573
|
-
var STRING_MODE_OPS = /* @__PURE__ */ new Set([
|
|
574
|
-
"contains",
|
|
575
|
-
"startsWith",
|
|
576
|
-
"endsWith",
|
|
577
|
-
"equals"
|
|
578
|
-
]);
|
|
579
|
-
var UNSUPPORTED_WHERE_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
|
|
580
|
-
var UNSUPPORTED_BY_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
|
|
581
|
-
var KNOWN_NESTED_INCLUDE_KEYS = /* @__PURE__ */ new Set([
|
|
582
|
-
"where",
|
|
583
|
-
"include",
|
|
584
|
-
"select",
|
|
585
|
-
"orderBy",
|
|
586
|
-
"cursor",
|
|
587
|
-
"take",
|
|
588
|
-
"skip"
|
|
589
|
-
]);
|
|
590
|
-
var KNOWN_NESTED_SELECT_KEYS = /* @__PURE__ */ new Set([
|
|
591
|
-
"select",
|
|
592
|
-
"where",
|
|
593
|
-
"orderBy",
|
|
594
|
-
"cursor",
|
|
595
|
-
"take",
|
|
596
|
-
"skip"
|
|
597
|
-
]);
|
|
598
|
-
var KNOWN_COUNT_SELECT_ENTRY_KEYS = /* @__PURE__ */ new Set(["where"]);
|
|
599
|
-
function isPlainObject(v) {
|
|
600
|
-
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
601
|
-
return false;
|
|
602
|
-
const proto = Object.getPrototypeOf(v);
|
|
603
|
-
return proto === Object.prototype || proto === null;
|
|
580
|
+
// src/runtime/query-builder-where.ts
|
|
581
|
+
import { z as z4 } from "zod";
|
|
582
|
+
|
|
583
|
+
// src/runtime/query-builder-forced.ts
|
|
584
|
+
var EMPTY_WHERE_FORCED = { conditions: {}, relations: {} };
|
|
585
|
+
function hasWhereForced(f) {
|
|
586
|
+
return Object.keys(f.conditions).length > 0 || Object.keys(f.relations).length > 0;
|
|
604
587
|
}
|
|
605
|
-
function
|
|
606
|
-
if (!
|
|
607
|
-
return
|
|
608
|
-
|
|
588
|
+
function mergeWhereForced(where, forced) {
|
|
589
|
+
if (!hasWhereForced(forced))
|
|
590
|
+
return where ?? {};
|
|
591
|
+
let result = where ? structuredClone(where) : {};
|
|
592
|
+
for (const [relName, opMap] of Object.entries(forced.relations)) {
|
|
593
|
+
if (!result[relName] || typeof result[relName] !== "object") {
|
|
594
|
+
result[relName] = {};
|
|
595
|
+
}
|
|
596
|
+
const relObj = result[relName];
|
|
597
|
+
for (const [op, nestedForced] of Object.entries(opMap)) {
|
|
598
|
+
relObj[op] = mergeWhereForced(
|
|
599
|
+
relObj[op],
|
|
600
|
+
nestedForced
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (Object.keys(forced.conditions).length > 0) {
|
|
605
|
+
const scalarClone = structuredClone(forced.conditions);
|
|
606
|
+
if (Object.keys(result).length === 0) {
|
|
607
|
+
result = scalarClone;
|
|
608
|
+
} else {
|
|
609
|
+
result = { AND: [result, scalarClone] };
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return result;
|
|
609
613
|
}
|
|
610
|
-
function
|
|
611
|
-
if (!
|
|
612
|
-
return
|
|
613
|
-
|
|
614
|
+
function mergeUniqueWhereForced(where, forced) {
|
|
615
|
+
if (!hasWhereForced(forced))
|
|
616
|
+
return where ?? {};
|
|
617
|
+
let result = where ? { ...where } : {};
|
|
618
|
+
for (const [relName, opMap] of Object.entries(forced.relations)) {
|
|
619
|
+
if (!result[relName] || typeof result[relName] !== "object") {
|
|
620
|
+
result[relName] = {};
|
|
621
|
+
}
|
|
622
|
+
const relObj = result[relName];
|
|
623
|
+
for (const [op, nestedForced] of Object.entries(opMap)) {
|
|
624
|
+
relObj[op] = mergeWhereForced(
|
|
625
|
+
relObj[op],
|
|
626
|
+
nestedForced
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
if (Object.keys(forced.conditions).length > 0) {
|
|
631
|
+
const conditions = [structuredClone(forced.conditions)];
|
|
632
|
+
const existing = result.AND;
|
|
633
|
+
if (existing) {
|
|
634
|
+
if (Array.isArray(existing)) {
|
|
635
|
+
conditions.unshift(...existing);
|
|
636
|
+
} else {
|
|
637
|
+
conditions.unshift(existing);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
result.AND = conditions;
|
|
641
|
+
}
|
|
642
|
+
return result;
|
|
614
643
|
}
|
|
615
644
|
function applyBuiltShape(built, body, isUniqueMethod) {
|
|
616
645
|
const validated = built.zodSchema.parse(body);
|
|
617
|
-
if (
|
|
618
|
-
|
|
619
|
-
validated.where = isUniqueMethod ? mergeUniqueForced(
|
|
646
|
+
if (hasWhereForced(built.forcedWhere)) {
|
|
647
|
+
validated.where = isUniqueMethod ? mergeUniqueWhereForced(
|
|
620
648
|
validated.where,
|
|
621
|
-
|
|
622
|
-
) :
|
|
649
|
+
built.forcedWhere
|
|
650
|
+
) : mergeWhereForced(
|
|
623
651
|
validated.where,
|
|
624
|
-
|
|
652
|
+
built.forcedWhere
|
|
625
653
|
);
|
|
626
654
|
}
|
|
627
655
|
if (Object.keys(built.forcedIncludeTree).length > 0) {
|
|
@@ -631,16 +659,14 @@ function applyBuiltShape(built, body, isUniqueMethod) {
|
|
|
631
659
|
applyForcedTree(validated, "select", built.forcedSelectTree);
|
|
632
660
|
}
|
|
633
661
|
if (Object.keys(built.forcedIncludeCountWhere).length > 0) {
|
|
634
|
-
const
|
|
635
|
-
if (
|
|
636
|
-
applyForcedCountWhere(
|
|
637
|
-
}
|
|
662
|
+
const ic = validated.include;
|
|
663
|
+
if (ic)
|
|
664
|
+
applyForcedCountWhere(ic, built.forcedIncludeCountWhere);
|
|
638
665
|
}
|
|
639
666
|
if (Object.keys(built.forcedSelectCountWhere).length > 0) {
|
|
640
|
-
const
|
|
641
|
-
if (
|
|
642
|
-
applyForcedCountWhere(
|
|
643
|
-
}
|
|
667
|
+
const sc = validated.select;
|
|
668
|
+
if (sc)
|
|
669
|
+
applyForcedCountWhere(sc, built.forcedSelectCountWhere);
|
|
644
670
|
}
|
|
645
671
|
return validated;
|
|
646
672
|
}
|
|
@@ -654,8 +680,9 @@ function applyForcedTree(validated, key, tree) {
|
|
|
654
680
|
continue;
|
|
655
681
|
if (relVal === true) {
|
|
656
682
|
const expanded = {};
|
|
657
|
-
if (forced.where)
|
|
658
|
-
expanded.where =
|
|
683
|
+
if (forced.where && hasWhereForced(forced.where)) {
|
|
684
|
+
expanded.where = mergeWhereForced(void 0, forced.where);
|
|
685
|
+
}
|
|
659
686
|
if (forced.include) {
|
|
660
687
|
expanded.include = buildForcedOnlyContainer(forced.include);
|
|
661
688
|
applyForcedTree(expanded, "include", forced.include);
|
|
@@ -666,10 +693,8 @@ function applyForcedTree(validated, key, tree) {
|
|
|
666
693
|
}
|
|
667
694
|
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
668
695
|
const countSelect = {};
|
|
669
|
-
for (const [countRel, countForced] of Object.entries(
|
|
670
|
-
|
|
671
|
-
)) {
|
|
672
|
-
countSelect[countRel] = { where: structuredClone(countForced) };
|
|
696
|
+
for (const [countRel, countForced] of Object.entries(forced._countWhere)) {
|
|
697
|
+
countSelect[countRel] = { where: mergeWhereForced(void 0, countForced) };
|
|
673
698
|
}
|
|
674
699
|
expanded._count = { select: countSelect };
|
|
675
700
|
}
|
|
@@ -683,10 +708,10 @@ function applyForcedTree(validated, key, tree) {
|
|
|
683
708
|
}
|
|
684
709
|
if (isPlainObject(relVal)) {
|
|
685
710
|
const relObj = relVal;
|
|
686
|
-
if (forced.where) {
|
|
687
|
-
relObj.where =
|
|
711
|
+
if (forced.where && hasWhereForced(forced.where)) {
|
|
712
|
+
relObj.where = mergeWhereForced(
|
|
688
713
|
relObj.where,
|
|
689
|
-
|
|
714
|
+
forced.where
|
|
690
715
|
);
|
|
691
716
|
}
|
|
692
717
|
if (forced.include) {
|
|
@@ -714,18 +739,17 @@ function buildForcedOnlyContainer(tree) {
|
|
|
714
739
|
const result = {};
|
|
715
740
|
for (const [relName, forced] of Object.entries(tree)) {
|
|
716
741
|
const nested = {};
|
|
717
|
-
if (forced.where)
|
|
718
|
-
nested.where =
|
|
742
|
+
if (forced.where && hasWhereForced(forced.where)) {
|
|
743
|
+
nested.where = mergeWhereForced(void 0, forced.where);
|
|
744
|
+
}
|
|
719
745
|
if (forced.include)
|
|
720
746
|
nested.include = buildForcedOnlyContainer(forced.include);
|
|
721
747
|
if (forced.select)
|
|
722
748
|
nested.select = buildForcedOnlyContainer(forced.select);
|
|
723
749
|
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
724
750
|
const countSelect = {};
|
|
725
|
-
for (const [countRel, countForced] of Object.entries(
|
|
726
|
-
|
|
727
|
-
)) {
|
|
728
|
-
countSelect[countRel] = { where: structuredClone(countForced) };
|
|
751
|
+
for (const [countRel, countForced] of Object.entries(forced._countWhere)) {
|
|
752
|
+
countSelect[countRel] = { where: mergeWhereForced(void 0, countForced) };
|
|
729
753
|
}
|
|
730
754
|
nested._count = { select: countSelect };
|
|
731
755
|
}
|
|
@@ -747,12 +771,12 @@ function applyForcedCountWhere(container, forcedCountWhere) {
|
|
|
747
771
|
if (relVal === void 0)
|
|
748
772
|
continue;
|
|
749
773
|
if (relVal === true) {
|
|
750
|
-
selectObj[relName] = { where:
|
|
774
|
+
selectObj[relName] = { where: mergeWhereForced(void 0, forced) };
|
|
751
775
|
} else if (isPlainObject(relVal)) {
|
|
752
776
|
const relObj = relVal;
|
|
753
|
-
relObj.where =
|
|
777
|
+
relObj.where = mergeWhereForced(
|
|
754
778
|
relObj.where,
|
|
755
|
-
|
|
779
|
+
forced
|
|
756
780
|
);
|
|
757
781
|
}
|
|
758
782
|
}
|
|
@@ -789,17 +813,25 @@ function validateResolvedUniqueWhere(model, where, method, uniqueMap) {
|
|
|
789
813
|
);
|
|
790
814
|
}
|
|
791
815
|
}
|
|
792
|
-
function validateUniqueEquality(model, where, method, uniqueMap) {
|
|
816
|
+
function validateUniqueEquality(model, where, method, uniqueMap, typeMap) {
|
|
793
817
|
const constraints = uniqueMap[model];
|
|
794
818
|
if (!constraints || constraints.length === 0)
|
|
795
819
|
return;
|
|
796
|
-
const
|
|
820
|
+
const combinators = /* @__PURE__ */ new Set(["AND", "OR", "NOT"]);
|
|
821
|
+
const whereFields = /* @__PURE__ */ new Set();
|
|
822
|
+
for (const key of Object.keys(where)) {
|
|
823
|
+
if (combinators.has(key))
|
|
824
|
+
continue;
|
|
825
|
+
if (typeMap && typeMap[model]?.[key]?.isRelation)
|
|
826
|
+
continue;
|
|
827
|
+
whereFields.add(key);
|
|
828
|
+
}
|
|
797
829
|
const valid = constraints.some((constraint) => {
|
|
798
830
|
if (!constraint.every((field) => whereFields.has(field)))
|
|
799
831
|
return false;
|
|
800
832
|
return constraint.every((field) => {
|
|
801
833
|
const ops = where[field];
|
|
802
|
-
if (!ops)
|
|
834
|
+
if (!ops || !isPlainObject(ops))
|
|
803
835
|
return false;
|
|
804
836
|
return Object.keys(ops).every((op) => op === "equals");
|
|
805
837
|
});
|
|
@@ -811,191 +843,440 @@ function validateUniqueEquality(model, where, method, uniqueMap) {
|
|
|
811
843
|
);
|
|
812
844
|
}
|
|
813
845
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
846
|
+
|
|
847
|
+
// src/runtime/query-builder-where.ts
|
|
848
|
+
var UNSUPPORTED_WHERE_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
|
|
849
|
+
var STRING_MODE_OPS = /* @__PURE__ */ new Set(["contains", "startsWith", "endsWith", "equals"]);
|
|
850
|
+
function safeStringify(v) {
|
|
851
|
+
if (typeof v === "bigint")
|
|
852
|
+
return `${v}n`;
|
|
853
|
+
try {
|
|
854
|
+
return JSON.stringify(v);
|
|
855
|
+
} catch {
|
|
856
|
+
return String(v);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
function forcedValuesEqual(a, b) {
|
|
860
|
+
if (a === b)
|
|
861
|
+
return true;
|
|
862
|
+
if (a === null || b === null)
|
|
863
|
+
return false;
|
|
864
|
+
if (typeof a !== typeof b)
|
|
865
|
+
return false;
|
|
866
|
+
if (typeof a === "bigint")
|
|
867
|
+
return a === b;
|
|
868
|
+
if (a instanceof Date && b instanceof Date)
|
|
869
|
+
return a.getTime() === b.getTime();
|
|
870
|
+
if (a instanceof RegExp && b instanceof RegExp) {
|
|
871
|
+
return a.source === b.source && a.flags === b.flags;
|
|
872
|
+
}
|
|
873
|
+
if (typeof a !== "object")
|
|
874
|
+
return false;
|
|
875
|
+
if (Array.isArray(a)) {
|
|
876
|
+
if (!Array.isArray(b))
|
|
877
|
+
return false;
|
|
878
|
+
if (a.length !== b.length)
|
|
879
|
+
return false;
|
|
880
|
+
return a.every((v, i) => forcedValuesEqual(v, b[i]));
|
|
881
|
+
}
|
|
882
|
+
if (!isPlainObject(a) || !isPlainObject(b))
|
|
883
|
+
return false;
|
|
884
|
+
const aKeys = Object.keys(a);
|
|
885
|
+
const bKeys = Object.keys(b);
|
|
886
|
+
if (aKeys.length !== bKeys.length)
|
|
887
|
+
return false;
|
|
888
|
+
return aKeys.every((k) => k in b && forcedValuesEqual(a[k], b[k]));
|
|
889
|
+
}
|
|
890
|
+
function mergeScalarConditions(target, source) {
|
|
891
|
+
for (const [field, ops] of Object.entries(source)) {
|
|
892
|
+
const existing = target[field];
|
|
893
|
+
if (existing === void 0) {
|
|
894
|
+
target[field] = ops;
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
if (field === "NOT") {
|
|
898
|
+
const existingArr = Array.isArray(existing) ? existing : [existing];
|
|
899
|
+
const sourceArr = Array.isArray(ops) ? ops : [ops];
|
|
900
|
+
target[field] = [...existingArr, ...sourceArr];
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
if (isPlainObject(existing) && isPlainObject(ops)) {
|
|
904
|
+
for (const [op, val] of Object.entries(ops)) {
|
|
905
|
+
if (op in existing) {
|
|
906
|
+
const existingVal = existing[op];
|
|
907
|
+
if (!forcedValuesEqual(existingVal, val)) {
|
|
908
|
+
throw new ShapeError(
|
|
909
|
+
`Conflicting forced where values for "${field}.${op}": shape defines both ${safeStringify(existingVal)} and ${safeStringify(val)}`
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
Object.assign(existing, ops);
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
if (!forcedValuesEqual(existing, ops)) {
|
|
817
918
|
throw new ShapeError(
|
|
818
|
-
`
|
|
919
|
+
`Conflicting forced where values for "${field}"`
|
|
819
920
|
);
|
|
820
921
|
}
|
|
821
922
|
}
|
|
822
923
|
}
|
|
823
|
-
function
|
|
824
|
-
|
|
825
|
-
if (!
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
924
|
+
function mergeRelationForcedMaps(target, source) {
|
|
925
|
+
for (const [relName, ops] of Object.entries(source)) {
|
|
926
|
+
if (!target[relName]) {
|
|
927
|
+
target[relName] = ops;
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
for (const [op, forced] of Object.entries(ops)) {
|
|
931
|
+
if (!target[relName][op]) {
|
|
932
|
+
target[relName][op] = forced;
|
|
933
|
+
} else {
|
|
934
|
+
mergeScalarConditions(target[relName][op].conditions, forced.conditions);
|
|
935
|
+
mergeRelationForcedMaps(target[relName][op].relations, forced.relations);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
829
938
|
}
|
|
939
|
+
}
|
|
940
|
+
function createWhereBuilder(typeMap, enumMap) {
|
|
830
941
|
function buildWhereSchema(model, whereConfig) {
|
|
831
942
|
const modelFields = typeMap[model];
|
|
832
943
|
if (!modelFields)
|
|
833
944
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
834
945
|
const fieldSchemas = {};
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
if (
|
|
839
|
-
|
|
840
|
-
|
|
946
|
+
const scalarConditions = {};
|
|
947
|
+
const relationForced = {};
|
|
948
|
+
for (const [key, value] of Object.entries(whereConfig)) {
|
|
949
|
+
if (COMBINATOR_KEYS.has(key)) {
|
|
950
|
+
processCombinator(
|
|
951
|
+
key,
|
|
952
|
+
value,
|
|
953
|
+
model,
|
|
954
|
+
fieldSchemas,
|
|
955
|
+
scalarConditions,
|
|
956
|
+
relationForced
|
|
841
957
|
);
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
const fieldMeta = modelFields[key];
|
|
961
|
+
if (!fieldMeta)
|
|
962
|
+
throw new ShapeError(`Unknown field "${key}" on model "${model}"`);
|
|
963
|
+
if (fieldMeta.isRelation) {
|
|
964
|
+
processRelationFilter(
|
|
965
|
+
key,
|
|
966
|
+
value,
|
|
967
|
+
fieldMeta,
|
|
968
|
+
fieldSchemas,
|
|
969
|
+
relationForced
|
|
845
970
|
);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
846
973
|
if (UNSUPPORTED_WHERE_TYPES.has(fieldMeta.type) && !fieldMeta.isList) {
|
|
847
974
|
throw new ShapeError(
|
|
848
|
-
`${fieldMeta.type} field "${
|
|
975
|
+
`${fieldMeta.type} field "${key}" cannot be used in where filters`
|
|
849
976
|
);
|
|
850
977
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
978
|
+
processScalarField(key, value, model, fieldMeta, fieldSchemas, scalarConditions);
|
|
979
|
+
}
|
|
980
|
+
const schema = Object.keys(fieldSchemas).length > 0 ? z4.object(fieldSchemas).strict().optional() : null;
|
|
981
|
+
return {
|
|
982
|
+
schema,
|
|
983
|
+
forced: {
|
|
984
|
+
conditions: scalarConditions,
|
|
985
|
+
relations: relationForced
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
function processCombinator(key, value, model, fieldSchemas, parentConditions, parentRelations) {
|
|
990
|
+
if (!isPlainObject(value)) {
|
|
991
|
+
throw new ShapeError(`"${key}" in where shape must be an object defining allowed fields`);
|
|
992
|
+
}
|
|
993
|
+
const result = buildWhereSchema(model, value);
|
|
994
|
+
if (result.schema) {
|
|
995
|
+
if (key === "NOT") {
|
|
996
|
+
fieldSchemas[key] = z4.union([result.schema, z4.array(result.schema)]).optional();
|
|
997
|
+
} else {
|
|
998
|
+
fieldSchemas[key] = z4.array(result.schema).optional();
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (hasWhereForced(result.forced)) {
|
|
1002
|
+
if (key === "AND" || key === "OR") {
|
|
1003
|
+
mergeScalarConditions(parentConditions, result.forced.conditions);
|
|
1004
|
+
mergeRelationForcedMaps(parentRelations, result.forced.relations);
|
|
1005
|
+
} else {
|
|
1006
|
+
const notWhere = mergeWhereForced(void 0, result.forced);
|
|
1007
|
+
if (Object.keys(notWhere).length > 0) {
|
|
1008
|
+
const existing = parentConditions[key];
|
|
1009
|
+
if (existing) {
|
|
1010
|
+
parentConditions[key] = Array.isArray(existing) ? [...existing, notWhere] : [existing, notWhere];
|
|
1011
|
+
} else {
|
|
1012
|
+
parentConditions[key] = notWhere;
|
|
877
1013
|
}
|
|
878
|
-
fieldForced[op] = parsed;
|
|
879
1014
|
}
|
|
880
1015
|
}
|
|
881
|
-
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
function processRelationFilter(key, value, fieldMeta, fieldSchemas, parentRelations) {
|
|
1019
|
+
if (!isPlainObject(value)) {
|
|
1020
|
+
throw new ShapeError(`Relation filter for "${key}" must be an object with operators (some, every, none, is, isNot)`);
|
|
1021
|
+
}
|
|
1022
|
+
const allowedOps = fieldMeta.isList ? TO_MANY_RELATION_OPS : TO_ONE_RELATION_OPS;
|
|
1023
|
+
const opSchemas = {};
|
|
1024
|
+
const opForced = {};
|
|
1025
|
+
let hasClientOps = false;
|
|
1026
|
+
for (const [op, opValue] of Object.entries(value)) {
|
|
1027
|
+
if (!allowedOps.has(op)) {
|
|
1028
|
+
const allowed = [...allowedOps].join(", ");
|
|
1029
|
+
throw new ShapeError(
|
|
1030
|
+
`Operator "${op}" not supported for ${fieldMeta.isList ? "to-many" : "to-one"} relation "${key}". Allowed: ${allowed}`
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
if (!isPlainObject(opValue)) {
|
|
882
1034
|
throw new ShapeError(
|
|
883
|
-
`
|
|
1035
|
+
`Relation filter operator "${op}" on "${key}" must be an object defining nested where fields`
|
|
884
1036
|
);
|
|
885
1037
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
}
|
|
898
|
-
).optional();
|
|
1038
|
+
const nested = buildWhereSchema(fieldMeta.type, opValue);
|
|
1039
|
+
if (nested.schema) {
|
|
1040
|
+
if (!hasWhereForced(nested.forced)) {
|
|
1041
|
+
opSchemas[op] = nested.schema.refine(
|
|
1042
|
+
(v) => v === void 0 || Object.keys(v).length > 0,
|
|
1043
|
+
{ message: `Relation filter "${key}.${op}" requires at least one condition` }
|
|
1044
|
+
);
|
|
1045
|
+
} else {
|
|
1046
|
+
opSchemas[op] = nested.schema;
|
|
1047
|
+
}
|
|
1048
|
+
hasClientOps = true;
|
|
899
1049
|
}
|
|
900
|
-
if (
|
|
901
|
-
|
|
1050
|
+
if (hasWhereForced(nested.forced)) {
|
|
1051
|
+
opForced[op] = nested.forced;
|
|
902
1052
|
}
|
|
903
1053
|
}
|
|
904
|
-
|
|
905
|
-
|
|
1054
|
+
if (hasClientOps) {
|
|
1055
|
+
const clientOpKeys = Object.keys(opSchemas);
|
|
1056
|
+
const opObjSchema = z4.object(opSchemas).strict();
|
|
1057
|
+
if (Object.keys(opForced).length === 0) {
|
|
1058
|
+
fieldSchemas[key] = opObjSchema.refine(
|
|
1059
|
+
(v) => clientOpKeys.some((k) => v[k] !== void 0),
|
|
1060
|
+
{ message: `At least one relation operator required for "${key}"` }
|
|
1061
|
+
).optional();
|
|
1062
|
+
} else {
|
|
1063
|
+
fieldSchemas[key] = opObjSchema.optional();
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
if (Object.keys(opForced).length > 0) {
|
|
1067
|
+
parentRelations[key] = opForced;
|
|
1068
|
+
}
|
|
906
1069
|
}
|
|
907
|
-
function
|
|
908
|
-
if (
|
|
909
|
-
|
|
1070
|
+
function processScalarField(fieldName, operators, model, fieldMeta, fieldSchemas, scalarConditions) {
|
|
1071
|
+
if (!isPlainObject(operators)) {
|
|
1072
|
+
throw new ShapeError(
|
|
1073
|
+
`Where config for scalar field "${fieldName}" on model "${model}" must be an object of operators`
|
|
1074
|
+
);
|
|
910
1075
|
}
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1076
|
+
const opSchemas = {};
|
|
1077
|
+
const fieldForced = {};
|
|
1078
|
+
let hasClientOps = false;
|
|
1079
|
+
let hasStringModeOp = false;
|
|
1080
|
+
const clientOpKeys = [];
|
|
1081
|
+
for (const [op, opValue] of Object.entries(operators)) {
|
|
1082
|
+
if (opValue === true) {
|
|
1083
|
+
opSchemas[op] = createOperatorSchema(fieldMeta, op, enumMap).optional();
|
|
1084
|
+
hasClientOps = true;
|
|
1085
|
+
clientOpKeys.push(op);
|
|
1086
|
+
if (fieldMeta.type === "String" && !fieldMeta.isList && STRING_MODE_OPS.has(op)) {
|
|
1087
|
+
hasStringModeOp = true;
|
|
1088
|
+
}
|
|
1089
|
+
} else {
|
|
1090
|
+
const opSchema = createOperatorSchema(fieldMeta, op, enumMap);
|
|
1091
|
+
let parsed;
|
|
1092
|
+
try {
|
|
1093
|
+
parsed = opSchema.parse(opValue);
|
|
1094
|
+
} catch (err) {
|
|
923
1095
|
throw new ShapeError(
|
|
924
|
-
`
|
|
1096
|
+
`Invalid forced value for "${model}.${fieldName}.${op}": ${err.message}`
|
|
925
1097
|
);
|
|
1098
|
+
}
|
|
1099
|
+
fieldForced[op] = parsed;
|
|
926
1100
|
}
|
|
927
|
-
fieldSchemas[fieldName] = z4.literal(true).optional();
|
|
928
1101
|
}
|
|
929
|
-
|
|
1102
|
+
if (!hasClientOps && Object.keys(fieldForced).length === 0) {
|
|
1103
|
+
throw new ShapeError(
|
|
1104
|
+
`Empty operator config for where field "${fieldName}" on model "${model}". Define at least one operator.`
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
if (hasStringModeOp) {
|
|
1108
|
+
opSchemas["mode"] = z4.enum(["default", "insensitive"]).optional();
|
|
1109
|
+
}
|
|
1110
|
+
if (hasClientOps) {
|
|
1111
|
+
const opObj = z4.object(opSchemas).strict();
|
|
1112
|
+
fieldSchemas[fieldName] = opObj.refine(
|
|
1113
|
+
(v) => clientOpKeys.some((k) => v[k] !== void 0),
|
|
1114
|
+
{ message: `At least one operator required for where field "${fieldName}"` }
|
|
1115
|
+
).optional();
|
|
1116
|
+
}
|
|
1117
|
+
if (Object.keys(fieldForced).length > 0) {
|
|
1118
|
+
scalarConditions[fieldName] = fieldForced;
|
|
1119
|
+
}
|
|
930
1120
|
}
|
|
931
|
-
|
|
1121
|
+
return { buildWhereSchema };
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// src/runtime/query-builder-args.ts
|
|
1125
|
+
import { z as z5 } from "zod";
|
|
1126
|
+
var UNSUPPORTED_BY_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
|
|
1127
|
+
function createArgsBuilder(typeMap, enumMap, uniqueMap) {
|
|
1128
|
+
function buildOrderBySchema(model, orderByConfig) {
|
|
932
1129
|
const modelFields = typeMap[model];
|
|
933
1130
|
if (!modelFields)
|
|
934
1131
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
935
|
-
|
|
936
|
-
|
|
1132
|
+
const fieldSchemas = {};
|
|
1133
|
+
for (const fieldName of Object.keys(orderByConfig)) {
|
|
1134
|
+
const fieldMeta = modelFields[fieldName];
|
|
1135
|
+
if (!fieldMeta)
|
|
1136
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
1137
|
+
if (fieldMeta.isRelation)
|
|
1138
|
+
throw new ShapeError(`Relation field "${fieldName}" cannot be used in orderBy`);
|
|
1139
|
+
if (fieldMeta.type === "Json")
|
|
1140
|
+
throw new ShapeError(`Json field "${fieldName}" cannot be used in orderBy`);
|
|
1141
|
+
if (fieldMeta.isList)
|
|
1142
|
+
throw new ShapeError(`List field "${fieldName}" cannot be used in orderBy`);
|
|
1143
|
+
fieldSchemas[fieldName] = z5.enum(["asc", "desc"]).optional();
|
|
937
1144
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1145
|
+
const fieldKeys = Object.keys(fieldSchemas);
|
|
1146
|
+
const singleSchema = z5.object(fieldSchemas).strict().refine(
|
|
1147
|
+
(v) => fieldKeys.some((k) => v[k] !== void 0),
|
|
1148
|
+
{ message: "orderBy must specify at least one field" }
|
|
1149
|
+
);
|
|
1150
|
+
return z5.union([singleSchema, z5.array(singleSchema).min(1)]).optional();
|
|
1151
|
+
}
|
|
1152
|
+
function buildTakeSchema(config) {
|
|
1153
|
+
if (!Number.isFinite(config.max) || !Number.isInteger(config.max)) {
|
|
1154
|
+
throw new ShapeError(`take max must be a finite integer, got ${config.max}`);
|
|
942
1155
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
throw new ShapeError(
|
|
946
|
-
`Unknown key "${key}" in _count config on model "${model}". Only "select" is allowed.`
|
|
947
|
-
);
|
|
948
|
-
}
|
|
1156
|
+
if (config.max < 1) {
|
|
1157
|
+
throw new ShapeError(`take max must be at least 1, got ${config.max}`);
|
|
949
1158
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
);
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
);
|
|
977
|
-
const nestedSchemas = {};
|
|
978
|
-
if (whereSchema)
|
|
979
|
-
nestedSchemas["where"] = whereSchema;
|
|
980
|
-
const nestedObj = z4.object(nestedSchemas).strict();
|
|
981
|
-
countSelectFields[fieldName] = z4.union([z4.literal(true), nestedObj]).optional();
|
|
982
|
-
if (Object.keys(forced).length > 0) {
|
|
983
|
-
forcedCountWhere[fieldName] = forced;
|
|
984
|
-
}
|
|
985
|
-
} else {
|
|
986
|
-
countSelectFields[fieldName] = z4.literal(true).optional();
|
|
987
|
-
}
|
|
988
|
-
} else {
|
|
1159
|
+
if (config.default !== void 0) {
|
|
1160
|
+
if (!Number.isFinite(config.default) || !Number.isInteger(config.default)) {
|
|
1161
|
+
throw new ShapeError(`take default must be a finite integer, got ${config.default}`);
|
|
1162
|
+
}
|
|
1163
|
+
if (config.default < 1) {
|
|
1164
|
+
throw new ShapeError(`take default must be at least 1, got ${config.default}`);
|
|
1165
|
+
}
|
|
1166
|
+
if (config.default > config.max) {
|
|
1167
|
+
throw new ShapeError("take default cannot exceed max");
|
|
1168
|
+
}
|
|
1169
|
+
return z5.number().int().min(1).max(config.max).default(config.default);
|
|
1170
|
+
}
|
|
1171
|
+
return z5.number().int().min(1).max(config.max).optional();
|
|
1172
|
+
}
|
|
1173
|
+
function buildCursorSchema(model, cursorConfig) {
|
|
1174
|
+
const modelFields = typeMap[model];
|
|
1175
|
+
if (!modelFields)
|
|
1176
|
+
throw new ShapeError(`Unknown model: ${model}`);
|
|
1177
|
+
const cursorFields = new Set(Object.keys(cursorConfig));
|
|
1178
|
+
const constraints = uniqueMap[model];
|
|
1179
|
+
if (constraints && constraints.length > 0) {
|
|
1180
|
+
const covered = constraints.some(
|
|
1181
|
+
(constraint) => constraint.every((field) => cursorFields.has(field))
|
|
1182
|
+
);
|
|
1183
|
+
if (!covered) {
|
|
1184
|
+
const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
|
|
989
1185
|
throw new ShapeError(
|
|
990
|
-
`
|
|
1186
|
+
`cursor on model "${model}" must cover a unique constraint: ${constraintDesc}`
|
|
991
1187
|
);
|
|
992
1188
|
}
|
|
993
1189
|
}
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1190
|
+
const fieldSchemas = {};
|
|
1191
|
+
for (const fieldName of Object.keys(cursorConfig)) {
|
|
1192
|
+
const fieldMeta = modelFields[fieldName];
|
|
1193
|
+
if (!fieldMeta)
|
|
1194
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in cursor`);
|
|
1195
|
+
if (fieldMeta.isRelation)
|
|
1196
|
+
throw new ShapeError(`Relation field "${fieldName}" cannot be used in cursor`);
|
|
1197
|
+
if (fieldMeta.isList)
|
|
1198
|
+
throw new ShapeError(`List field "${fieldName}" cannot be used in cursor`);
|
|
1199
|
+
fieldSchemas[fieldName] = createBaseType(fieldMeta, enumMap);
|
|
1200
|
+
}
|
|
1201
|
+
return z5.object(fieldSchemas).strict().optional();
|
|
1202
|
+
}
|
|
1203
|
+
function buildDistinctSchema(model, distinctConfig) {
|
|
1204
|
+
if (distinctConfig.length === 0) {
|
|
1205
|
+
throw new ShapeError("distinct must contain at least one field");
|
|
1206
|
+
}
|
|
1207
|
+
const modelFields = typeMap[model];
|
|
1208
|
+
if (!modelFields)
|
|
1209
|
+
throw new ShapeError(`Unknown model: ${model}`);
|
|
1210
|
+
for (const fieldName of distinctConfig) {
|
|
1211
|
+
const fieldMeta = modelFields[fieldName];
|
|
1212
|
+
if (!fieldMeta)
|
|
1213
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in distinct`);
|
|
1214
|
+
if (fieldMeta.isRelation)
|
|
1215
|
+
throw new ShapeError(`Relation field "${fieldName}" cannot be used in distinct`);
|
|
1216
|
+
if (fieldMeta.isList)
|
|
1217
|
+
throw new ShapeError(`List field "${fieldName}" cannot be used in distinct`);
|
|
1218
|
+
}
|
|
1219
|
+
const enumSchema = z5.enum(distinctConfig);
|
|
1220
|
+
return z5.union([enumSchema, z5.array(enumSchema).min(1)]).optional();
|
|
1221
|
+
}
|
|
1222
|
+
function buildBySchema(model, byConfig) {
|
|
1223
|
+
if (byConfig.length === 0) {
|
|
1224
|
+
throw new ShapeError('groupBy "by" must contain at least one field');
|
|
1225
|
+
}
|
|
1226
|
+
const modelFields = typeMap[model];
|
|
1227
|
+
if (!modelFields)
|
|
1228
|
+
throw new ShapeError(`Unknown model: ${model}`);
|
|
1229
|
+
for (const fieldName of byConfig) {
|
|
1230
|
+
const fieldMeta = modelFields[fieldName];
|
|
1231
|
+
if (!fieldMeta)
|
|
1232
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in by`);
|
|
1233
|
+
if (fieldMeta.isRelation)
|
|
1234
|
+
throw new ShapeError(`Relation field "${fieldName}" cannot be used in by`);
|
|
1235
|
+
if (UNSUPPORTED_BY_TYPES.has(fieldMeta.type)) {
|
|
1236
|
+
throw new ShapeError(`${fieldMeta.type} field "${fieldName}" cannot be used in by`);
|
|
1237
|
+
}
|
|
1238
|
+
if (fieldMeta.isList)
|
|
1239
|
+
throw new ShapeError(`List field "${fieldName}" cannot be used in by`);
|
|
1240
|
+
}
|
|
1241
|
+
const enumSchema = z5.enum(byConfig);
|
|
1242
|
+
return z5.union([enumSchema, z5.array(enumSchema).min(1)]);
|
|
1243
|
+
}
|
|
1244
|
+
function buildHavingSchema(model, havingConfig) {
|
|
1245
|
+
const modelFields = typeMap[model];
|
|
1246
|
+
if (!modelFields)
|
|
1247
|
+
throw new ShapeError(`Unknown model: ${model}`);
|
|
1248
|
+
const fieldSchemas = {};
|
|
1249
|
+
for (const fieldName of Object.keys(havingConfig)) {
|
|
1250
|
+
const fieldMeta = modelFields[fieldName];
|
|
1251
|
+
if (!fieldMeta)
|
|
1252
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in having`);
|
|
1253
|
+
if (fieldMeta.isRelation)
|
|
1254
|
+
throw new ShapeError(`Relation field "${fieldName}" cannot be used in having`);
|
|
1255
|
+
if (fieldMeta.isList)
|
|
1256
|
+
throw new ShapeError(`List field "${fieldName}" cannot be used in having`);
|
|
1257
|
+
const ops = getSupportedOperators(fieldMeta);
|
|
1258
|
+
if (ops.length === 0) {
|
|
1259
|
+
throw new ShapeError(`${fieldMeta.type} field "${fieldName}" cannot be used in having filters`);
|
|
1260
|
+
}
|
|
1261
|
+
const opSchemas = {};
|
|
1262
|
+
const opKeys = [];
|
|
1263
|
+
for (const op of ops) {
|
|
1264
|
+
opSchemas[op] = createOperatorSchema(fieldMeta, op, enumMap).optional();
|
|
1265
|
+
opKeys.push(op);
|
|
1266
|
+
}
|
|
1267
|
+
if (fieldMeta.type === "String" && !fieldMeta.isList) {
|
|
1268
|
+
opSchemas["mode"] = z5.enum(["default", "insensitive"]).optional();
|
|
1269
|
+
}
|
|
1270
|
+
fieldSchemas[fieldName] = z5.object(opSchemas).strict().refine(
|
|
1271
|
+
(v) => opKeys.some((k) => v[k] !== void 0),
|
|
1272
|
+
{ message: `At least one operator required for having field "${fieldName}"` }
|
|
1273
|
+
).optional();
|
|
1274
|
+
}
|
|
1275
|
+
const havingFieldKeys = Object.keys(fieldSchemas);
|
|
1276
|
+
return z5.object(fieldSchemas).strict().refine(
|
|
1277
|
+
(v) => havingFieldKeys.some((k) => v[k] !== void 0),
|
|
1278
|
+
{ message: "having must specify at least one field" }
|
|
1279
|
+
).optional();
|
|
999
1280
|
}
|
|
1000
1281
|
function buildAggregateFieldSchema(model, opName, fieldConfig) {
|
|
1001
1282
|
const modelFields = typeMap[model];
|
|
@@ -1006,18 +1287,14 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1006
1287
|
const fieldSchemas = {};
|
|
1007
1288
|
for (const fieldName of Object.keys(fieldConfig)) {
|
|
1008
1289
|
if (fieldName === "_all" && opName === "_count") {
|
|
1009
|
-
fieldSchemas[fieldName] =
|
|
1290
|
+
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
1010
1291
|
continue;
|
|
1011
1292
|
}
|
|
1012
1293
|
const fieldMeta = modelFields[fieldName];
|
|
1013
1294
|
if (!fieldMeta)
|
|
1014
|
-
throw new ShapeError(
|
|
1015
|
-
`Unknown field "${fieldName}" on model "${model}" in ${opName}`
|
|
1016
|
-
);
|
|
1295
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in ${opName}`);
|
|
1017
1296
|
if (fieldMeta.isRelation)
|
|
1018
|
-
throw new ShapeError(
|
|
1019
|
-
`Relation field "${fieldName}" cannot be used in ${opName}`
|
|
1020
|
-
);
|
|
1297
|
+
throw new ShapeError(`Relation field "${fieldName}" cannot be used in ${opName}`);
|
|
1021
1298
|
if (isNumericOnly && !NUMERIC_TYPES.has(fieldMeta.type)) {
|
|
1022
1299
|
throw new ShapeError(
|
|
1023
1300
|
`Field "${fieldName}" (${fieldMeta.type}) cannot be used in ${opName}. Only numeric types (Int, Float, Decimal, BigInt) are supported.`
|
|
@@ -1028,96 +1305,180 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1028
1305
|
`Field "${fieldName}" (${fieldMeta.type}) cannot be used in ${opName}. Only comparable types (Int, Float, Decimal, BigInt, String, DateTime) are supported.`
|
|
1029
1306
|
);
|
|
1030
1307
|
}
|
|
1031
|
-
fieldSchemas[fieldName] =
|
|
1308
|
+
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
1032
1309
|
}
|
|
1033
|
-
|
|
1310
|
+
const aggFieldKeys = Object.keys(fieldSchemas);
|
|
1311
|
+
return z5.object(fieldSchemas).strict().refine(
|
|
1312
|
+
(v) => aggFieldKeys.some((k) => v[k] !== void 0),
|
|
1313
|
+
{ message: `${opName} must specify at least one field` }
|
|
1314
|
+
).optional();
|
|
1034
1315
|
}
|
|
1035
|
-
function
|
|
1036
|
-
if (
|
|
1037
|
-
|
|
1316
|
+
function buildCountFieldSchema(model, config, context) {
|
|
1317
|
+
if (config === true) {
|
|
1318
|
+
return z5.literal(true).optional();
|
|
1038
1319
|
}
|
|
1039
1320
|
const modelFields = typeMap[model];
|
|
1040
1321
|
if (!modelFields)
|
|
1041
1322
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
if (
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
`Relation field "${fieldName}" cannot be used in by`
|
|
1051
|
-
);
|
|
1052
|
-
if (UNSUPPORTED_BY_TYPES.has(fieldMeta.type)) {
|
|
1053
|
-
throw new ShapeError(
|
|
1054
|
-
`${fieldMeta.type} field "${fieldName}" cannot be used in by`
|
|
1055
|
-
);
|
|
1056
|
-
}
|
|
1057
|
-
if (fieldMeta.isList) {
|
|
1058
|
-
throw new ShapeError(`List field "${fieldName}" cannot be used in by`);
|
|
1323
|
+
const fieldSchemas = {};
|
|
1324
|
+
for (const fieldName of Object.keys(config)) {
|
|
1325
|
+
if (fieldName !== "_all") {
|
|
1326
|
+
const fieldMeta = modelFields[fieldName];
|
|
1327
|
+
if (!fieldMeta)
|
|
1328
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in ${context}`);
|
|
1329
|
+
if (fieldMeta.isRelation)
|
|
1330
|
+
throw new ShapeError(`Relation field "${fieldName}" cannot be used in ${context}`);
|
|
1059
1331
|
}
|
|
1332
|
+
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
1060
1333
|
}
|
|
1061
|
-
const
|
|
1062
|
-
return
|
|
1334
|
+
const countFieldKeys = Object.keys(fieldSchemas);
|
|
1335
|
+
return z5.object(fieldSchemas).strict().refine(
|
|
1336
|
+
(v) => countFieldKeys.some((k) => v[k] !== void 0),
|
|
1337
|
+
{ message: `${context} must specify at least one field` }
|
|
1338
|
+
).optional();
|
|
1063
1339
|
}
|
|
1064
|
-
function
|
|
1340
|
+
function buildCountSelectSchema(model, selectConfig) {
|
|
1065
1341
|
const modelFields = typeMap[model];
|
|
1066
1342
|
if (!modelFields)
|
|
1067
1343
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1068
|
-
const cursorFields = new Set(Object.keys(cursorConfig));
|
|
1069
|
-
const constraints = uniqueMap[model];
|
|
1070
|
-
if (constraints && constraints.length > 0) {
|
|
1071
|
-
const covered = constraints.some(
|
|
1072
|
-
(constraint) => constraint.every((field) => cursorFields.has(field))
|
|
1073
|
-
);
|
|
1074
|
-
if (!covered) {
|
|
1075
|
-
const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
|
|
1076
|
-
throw new ShapeError(
|
|
1077
|
-
`cursor on model "${model}" must cover a unique constraint: ${constraintDesc}`
|
|
1078
|
-
);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
1344
|
const fieldSchemas = {};
|
|
1082
|
-
for (const fieldName of Object.keys(
|
|
1345
|
+
for (const fieldName of Object.keys(selectConfig)) {
|
|
1346
|
+
if (fieldName === "_all") {
|
|
1347
|
+
fieldSchemas["_all"] = z5.literal(true).optional();
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1083
1350
|
const fieldMeta = modelFields[fieldName];
|
|
1084
1351
|
if (!fieldMeta)
|
|
1085
|
-
throw new ShapeError(
|
|
1086
|
-
`Unknown field "${fieldName}" on model "${model}" in cursor`
|
|
1087
|
-
);
|
|
1352
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in count select`);
|
|
1088
1353
|
if (fieldMeta.isRelation)
|
|
1089
|
-
throw new ShapeError(
|
|
1090
|
-
|
|
1091
|
-
);
|
|
1092
|
-
if (fieldMeta.isList)
|
|
1093
|
-
throw new ShapeError(
|
|
1094
|
-
`List field "${fieldName}" cannot be used in cursor`
|
|
1095
|
-
);
|
|
1096
|
-
fieldSchemas[fieldName] = createBaseType(fieldMeta, enumMap);
|
|
1354
|
+
throw new ShapeError(`Relation field "${fieldName}" cannot be used in count select`);
|
|
1355
|
+
fieldSchemas[fieldName] = z5.literal(true).optional();
|
|
1097
1356
|
}
|
|
1098
|
-
|
|
1357
|
+
const countSelectKeys = Object.keys(fieldSchemas);
|
|
1358
|
+
return z5.object(fieldSchemas).strict().refine(
|
|
1359
|
+
(v) => countSelectKeys.some((k) => v[k] !== void 0),
|
|
1360
|
+
{ message: "count select must specify at least one field" }
|
|
1361
|
+
).optional();
|
|
1099
1362
|
}
|
|
1100
|
-
|
|
1363
|
+
return {
|
|
1364
|
+
buildOrderBySchema,
|
|
1365
|
+
buildTakeSchema,
|
|
1366
|
+
buildCursorSchema,
|
|
1367
|
+
buildDistinctSchema,
|
|
1368
|
+
buildBySchema,
|
|
1369
|
+
buildHavingSchema,
|
|
1370
|
+
buildAggregateFieldSchema,
|
|
1371
|
+
buildCountFieldSchema,
|
|
1372
|
+
buildCountSelectSchema
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// src/runtime/query-builder-projection.ts
|
|
1377
|
+
import { z as z6 } from "zod";
|
|
1378
|
+
var KNOWN_NESTED_INCLUDE_KEYS = /* @__PURE__ */ new Set([
|
|
1379
|
+
"where",
|
|
1380
|
+
"include",
|
|
1381
|
+
"select",
|
|
1382
|
+
"orderBy",
|
|
1383
|
+
"cursor",
|
|
1384
|
+
"take",
|
|
1385
|
+
"skip"
|
|
1386
|
+
]);
|
|
1387
|
+
var KNOWN_NESTED_SELECT_KEYS = /* @__PURE__ */ new Set([
|
|
1388
|
+
"select",
|
|
1389
|
+
"where",
|
|
1390
|
+
"orderBy",
|
|
1391
|
+
"cursor",
|
|
1392
|
+
"take",
|
|
1393
|
+
"skip"
|
|
1394
|
+
]);
|
|
1395
|
+
var KNOWN_COUNT_SELECT_ENTRY_KEYS = /* @__PURE__ */ new Set(["where"]);
|
|
1396
|
+
function validateNestedKeys(keys, allowed, context) {
|
|
1397
|
+
for (const key of keys) {
|
|
1398
|
+
if (!allowed.has(key)) {
|
|
1399
|
+
throw new ShapeError(
|
|
1400
|
+
`Unknown key "${key}" in ${context}. Allowed: ${[...allowed].join(", ")}`
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
function createProjectionBuilder(typeMap, enumMap, deps) {
|
|
1406
|
+
function buildIncludeCountSchema(model, config) {
|
|
1101
1407
|
const modelFields = typeMap[model];
|
|
1102
1408
|
if (!modelFields)
|
|
1103
1409
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1410
|
+
if (config === true) {
|
|
1411
|
+
return { schema: z6.literal(true).optional(), forcedCountWhere: {} };
|
|
1412
|
+
}
|
|
1413
|
+
if (!isPlainObject(config) || !("select" in config)) {
|
|
1414
|
+
throw new ShapeError(
|
|
1415
|
+
`Invalid _count config on model "${model}". Expected true or { select: { ... } }`
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
for (const key of Object.keys(config)) {
|
|
1419
|
+
if (key !== "select") {
|
|
1107
1420
|
throw new ShapeError(
|
|
1108
|
-
`Unknown
|
|
1421
|
+
`Unknown key "${key}" in _count config on model "${model}". Only "select" is allowed.`
|
|
1109
1422
|
);
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
if (!isPlainObject(config.select)) {
|
|
1426
|
+
throw new ShapeError(
|
|
1427
|
+
`Invalid _count.select on model "${model}". Expected a plain object with relation field keys.`
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
const selectObj = config.select;
|
|
1431
|
+
if (Object.keys(selectObj).length === 0) {
|
|
1432
|
+
throw new ShapeError(
|
|
1433
|
+
`Empty _count.select on model "${model}". Define at least one relation field.`
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
const countSelectFields = {};
|
|
1437
|
+
const forcedCountWhere = {};
|
|
1438
|
+
for (const [fieldName, fieldConfig] of Object.entries(selectObj)) {
|
|
1439
|
+
const fieldMeta = modelFields[fieldName];
|
|
1440
|
+
if (!fieldMeta)
|
|
1441
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in _count.select`);
|
|
1442
|
+
if (!fieldMeta.isRelation)
|
|
1443
|
+
throw new ShapeError(`Field "${fieldName}" is not a relation on model "${model}" in _count.select`);
|
|
1444
|
+
if (!fieldMeta.isList)
|
|
1445
|
+
throw new ShapeError(`Field "${fieldName}" is a to-one relation on model "${model}" in _count.select. Only to-many relations support _count.`);
|
|
1446
|
+
if (fieldConfig === true) {
|
|
1447
|
+
countSelectFields[fieldName] = z6.literal(true).optional();
|
|
1448
|
+
} else if (isPlainObject(fieldConfig)) {
|
|
1449
|
+
validateNestedKeys(
|
|
1450
|
+
Object.keys(fieldConfig),
|
|
1451
|
+
KNOWN_COUNT_SELECT_ENTRY_KEYS,
|
|
1452
|
+
`_count.select.${fieldName} on model "${model}"`
|
|
1113
1453
|
);
|
|
1114
|
-
|
|
1454
|
+
if (fieldConfig.where) {
|
|
1455
|
+
const relatedType = fieldMeta.type;
|
|
1456
|
+
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
1457
|
+
relatedType,
|
|
1458
|
+
fieldConfig.where
|
|
1459
|
+
);
|
|
1460
|
+
const nestedSchemas = {};
|
|
1461
|
+
if (whereSchema)
|
|
1462
|
+
nestedSchemas["where"] = whereSchema;
|
|
1463
|
+
const nestedObj = z6.object(nestedSchemas).strict();
|
|
1464
|
+
countSelectFields[fieldName] = z6.union([z6.literal(true), nestedObj]).optional();
|
|
1465
|
+
if (hasWhereForced(forced)) {
|
|
1466
|
+
forcedCountWhere[fieldName] = forced;
|
|
1467
|
+
}
|
|
1468
|
+
} else {
|
|
1469
|
+
countSelectFields[fieldName] = z6.literal(true).optional();
|
|
1470
|
+
}
|
|
1471
|
+
} else {
|
|
1115
1472
|
throw new ShapeError(
|
|
1116
|
-
`
|
|
1473
|
+
`Invalid config for _count.select.${fieldName} on model "${model}". Expected true or { where: { ... } }`
|
|
1117
1474
|
);
|
|
1475
|
+
}
|
|
1118
1476
|
}
|
|
1119
|
-
const
|
|
1120
|
-
return
|
|
1477
|
+
const selectSchema = z6.object(countSelectFields).strict();
|
|
1478
|
+
return {
|
|
1479
|
+
schema: z6.object({ select: selectSchema }).strict().optional(),
|
|
1480
|
+
forcedCountWhere
|
|
1481
|
+
};
|
|
1121
1482
|
}
|
|
1122
1483
|
function buildIncludeSchema(model, includeConfig) {
|
|
1123
1484
|
const modelFields = typeMap[model];
|
|
@@ -1128,10 +1489,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1128
1489
|
let topLevelForcedCountWhere = {};
|
|
1129
1490
|
for (const [relName, config] of Object.entries(includeConfig)) {
|
|
1130
1491
|
if (relName === "_count") {
|
|
1131
|
-
const countResult = buildIncludeCountSchema(
|
|
1132
|
-
model,
|
|
1133
|
-
config
|
|
1134
|
-
);
|
|
1492
|
+
const countResult = buildIncludeCountSchema(model, config);
|
|
1135
1493
|
fieldSchemas["_count"] = countResult.schema;
|
|
1136
1494
|
topLevelForcedCountWhere = countResult.forcedCountWhere;
|
|
1137
1495
|
continue;
|
|
@@ -1140,11 +1498,9 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1140
1498
|
if (!fieldMeta)
|
|
1141
1499
|
throw new ShapeError(`Unknown field "${relName}" on model "${model}"`);
|
|
1142
1500
|
if (!fieldMeta.isRelation)
|
|
1143
|
-
throw new ShapeError(
|
|
1144
|
-
`Field "${relName}" is not a relation on model "${model}"`
|
|
1145
|
-
);
|
|
1501
|
+
throw new ShapeError(`Field "${relName}" is not a relation on model "${model}"`);
|
|
1146
1502
|
if (config === true) {
|
|
1147
|
-
fieldSchemas[relName] =
|
|
1503
|
+
fieldSchemas[relName] = z6.literal(true).optional();
|
|
1148
1504
|
} else {
|
|
1149
1505
|
validateNestedKeys(
|
|
1150
1506
|
Object.keys(config),
|
|
@@ -1152,9 +1508,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1152
1508
|
`nested include for "${relName}" on model "${model}"`
|
|
1153
1509
|
);
|
|
1154
1510
|
if (config.select && config.include) {
|
|
1155
|
-
throw new ShapeError(
|
|
1156
|
-
`Nested include for "${relName}" cannot define both "select" and "include".`
|
|
1157
|
-
);
|
|
1511
|
+
throw new ShapeError(`Nested include for "${relName}" cannot define both "select" and "include".`);
|
|
1158
1512
|
}
|
|
1159
1513
|
if (!fieldMeta.isList) {
|
|
1160
1514
|
if (config.where || config.orderBy || config.cursor || config.take || config.skip) {
|
|
@@ -1166,13 +1520,13 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1166
1520
|
const nestedSchemas = {};
|
|
1167
1521
|
const relForced = {};
|
|
1168
1522
|
if (config.where) {
|
|
1169
|
-
const { schema: whereSchema, forced } = buildWhereSchema(
|
|
1523
|
+
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
1170
1524
|
fieldMeta.type,
|
|
1171
1525
|
config.where
|
|
1172
1526
|
);
|
|
1173
1527
|
if (whereSchema)
|
|
1174
1528
|
nestedSchemas["where"] = whereSchema;
|
|
1175
|
-
if (
|
|
1529
|
+
if (hasWhereForced(forced))
|
|
1176
1530
|
relForced.where = forced;
|
|
1177
1531
|
}
|
|
1178
1532
|
if (config.include) {
|
|
@@ -1192,31 +1546,25 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1192
1546
|
relForced._countWhere = nested.forcedCountWhere;
|
|
1193
1547
|
}
|
|
1194
1548
|
if (config.orderBy) {
|
|
1195
|
-
nestedSchemas["orderBy"] = buildOrderBySchema(
|
|
1196
|
-
fieldMeta.type,
|
|
1197
|
-
config.orderBy
|
|
1198
|
-
);
|
|
1549
|
+
nestedSchemas["orderBy"] = deps.buildOrderBySchema(fieldMeta.type, config.orderBy);
|
|
1199
1550
|
}
|
|
1200
1551
|
if (config.cursor) {
|
|
1201
|
-
nestedSchemas["cursor"] = buildCursorSchema(
|
|
1202
|
-
fieldMeta.type,
|
|
1203
|
-
config.cursor
|
|
1204
|
-
);
|
|
1552
|
+
nestedSchemas["cursor"] = deps.buildCursorSchema(fieldMeta.type, config.cursor);
|
|
1205
1553
|
}
|
|
1206
1554
|
if (config.take) {
|
|
1207
|
-
nestedSchemas["take"] = buildTakeSchema(config.take);
|
|
1555
|
+
nestedSchemas["take"] = deps.buildTakeSchema(config.take);
|
|
1208
1556
|
}
|
|
1209
1557
|
if (config.skip) {
|
|
1210
|
-
nestedSchemas["skip"] =
|
|
1558
|
+
nestedSchemas["skip"] = z6.number().int().min(0).optional();
|
|
1211
1559
|
}
|
|
1212
|
-
const nestedObj =
|
|
1213
|
-
fieldSchemas[relName] =
|
|
1560
|
+
const nestedObj = z6.object(nestedSchemas).strict();
|
|
1561
|
+
fieldSchemas[relName] = z6.union([z6.literal(true), nestedObj]).optional();
|
|
1214
1562
|
if (Object.keys(relForced).length > 0)
|
|
1215
1563
|
forcedTree[relName] = relForced;
|
|
1216
1564
|
}
|
|
1217
1565
|
}
|
|
1218
1566
|
return {
|
|
1219
|
-
schema:
|
|
1567
|
+
schema: z6.object(fieldSchemas).strict().optional(),
|
|
1220
1568
|
forcedTree,
|
|
1221
1569
|
forcedCountWhere: topLevelForcedCountWhere
|
|
1222
1570
|
};
|
|
@@ -1230,26 +1578,19 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1230
1578
|
let topLevelForcedCountWhere = {};
|
|
1231
1579
|
for (const [fieldName, config] of Object.entries(selectConfig)) {
|
|
1232
1580
|
if (fieldName === "_count") {
|
|
1233
|
-
const countResult = buildIncludeCountSchema(
|
|
1234
|
-
model,
|
|
1235
|
-
config
|
|
1236
|
-
);
|
|
1581
|
+
const countResult = buildIncludeCountSchema(model, config);
|
|
1237
1582
|
fieldSchemas["_count"] = countResult.schema;
|
|
1238
1583
|
topLevelForcedCountWhere = countResult.forcedCountWhere;
|
|
1239
1584
|
continue;
|
|
1240
1585
|
}
|
|
1241
1586
|
const fieldMeta = modelFields[fieldName];
|
|
1242
1587
|
if (!fieldMeta)
|
|
1243
|
-
throw new ShapeError(
|
|
1244
|
-
`Unknown field "${fieldName}" on model "${model}"`
|
|
1245
|
-
);
|
|
1588
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
1246
1589
|
if (config === true) {
|
|
1247
|
-
fieldSchemas[fieldName] =
|
|
1590
|
+
fieldSchemas[fieldName] = z6.literal(true).optional();
|
|
1248
1591
|
} else {
|
|
1249
1592
|
if (!fieldMeta.isRelation) {
|
|
1250
|
-
throw new ShapeError(
|
|
1251
|
-
`Nested select args only valid for relations, not scalar "${fieldName}"`
|
|
1252
|
-
);
|
|
1593
|
+
throw new ShapeError(`Nested select args only valid for relations, not scalar "${fieldName}" on model "${model}"`);
|
|
1253
1594
|
}
|
|
1254
1595
|
validateNestedKeys(
|
|
1255
1596
|
Object.keys(config),
|
|
@@ -1274,200 +1615,98 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1274
1615
|
relForced._countWhere = nested.forcedCountWhere;
|
|
1275
1616
|
}
|
|
1276
1617
|
if (config.where) {
|
|
1277
|
-
const { schema: whereSchema, forced } = buildWhereSchema(
|
|
1618
|
+
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
1278
1619
|
fieldMeta.type,
|
|
1279
1620
|
config.where
|
|
1280
1621
|
);
|
|
1281
1622
|
if (whereSchema)
|
|
1282
1623
|
nestedSchemas["where"] = whereSchema;
|
|
1283
|
-
if (
|
|
1624
|
+
if (hasWhereForced(forced))
|
|
1284
1625
|
relForced.where = forced;
|
|
1285
1626
|
}
|
|
1286
1627
|
if (config.orderBy) {
|
|
1287
|
-
nestedSchemas["orderBy"] = buildOrderBySchema(
|
|
1288
|
-
fieldMeta.type,
|
|
1289
|
-
config.orderBy
|
|
1290
|
-
);
|
|
1628
|
+
nestedSchemas["orderBy"] = deps.buildOrderBySchema(fieldMeta.type, config.orderBy);
|
|
1291
1629
|
}
|
|
1292
1630
|
if (config.cursor) {
|
|
1293
|
-
nestedSchemas["cursor"] = buildCursorSchema(
|
|
1294
|
-
fieldMeta.type,
|
|
1295
|
-
config.cursor
|
|
1296
|
-
);
|
|
1631
|
+
nestedSchemas["cursor"] = deps.buildCursorSchema(fieldMeta.type, config.cursor);
|
|
1297
1632
|
}
|
|
1298
1633
|
if (config.take) {
|
|
1299
|
-
nestedSchemas["take"] = buildTakeSchema(config.take);
|
|
1634
|
+
nestedSchemas["take"] = deps.buildTakeSchema(config.take);
|
|
1300
1635
|
}
|
|
1301
1636
|
if (config.skip) {
|
|
1302
|
-
nestedSchemas["skip"] =
|
|
1637
|
+
nestedSchemas["skip"] = z6.number().int().min(0).optional();
|
|
1303
1638
|
}
|
|
1304
|
-
const nestedObj =
|
|
1305
|
-
fieldSchemas[fieldName] =
|
|
1639
|
+
const nestedObj = z6.object(nestedSchemas).strict();
|
|
1640
|
+
fieldSchemas[fieldName] = z6.union([z6.literal(true), nestedObj]).optional();
|
|
1306
1641
|
if (Object.keys(relForced).length > 0)
|
|
1307
1642
|
forcedTree[fieldName] = relForced;
|
|
1308
1643
|
}
|
|
1309
1644
|
}
|
|
1310
1645
|
return {
|
|
1311
|
-
schema:
|
|
1646
|
+
schema: z6.object(fieldSchemas).strict().optional(),
|
|
1312
1647
|
forcedTree,
|
|
1313
1648
|
forcedCountWhere: topLevelForcedCountWhere
|
|
1314
1649
|
};
|
|
1315
1650
|
}
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
if (!Number.isFinite(config.max) || !Number.isInteger(config.max)) {
|
|
1346
|
-
throw new ShapeError(
|
|
1347
|
-
`take max must be a finite integer, got ${config.max}`
|
|
1348
|
-
);
|
|
1349
|
-
}
|
|
1350
|
-
if (config.max < 1) {
|
|
1351
|
-
throw new ShapeError(`take max must be at least 1, got ${config.max}`);
|
|
1352
|
-
}
|
|
1353
|
-
if (config.default !== void 0) {
|
|
1354
|
-
if (!Number.isFinite(config.default) || !Number.isInteger(config.default)) {
|
|
1355
|
-
throw new ShapeError(
|
|
1356
|
-
`take default must be a finite integer, got ${config.default}`
|
|
1357
|
-
);
|
|
1358
|
-
}
|
|
1359
|
-
if (config.default < 1) {
|
|
1360
|
-
throw new ShapeError(
|
|
1361
|
-
`take default must be at least 1, got ${config.default}`
|
|
1362
|
-
);
|
|
1363
|
-
}
|
|
1364
|
-
if (config.default > config.max) {
|
|
1365
|
-
throw new ShapeError("take default cannot exceed max");
|
|
1366
|
-
}
|
|
1367
|
-
return z4.number().int().min(1).max(config.max).default(config.default);
|
|
1368
|
-
}
|
|
1369
|
-
return z4.number().int().min(1).max(config.max).optional();
|
|
1370
|
-
}
|
|
1371
|
-
function buildHavingSchema(model, havingConfig) {
|
|
1372
|
-
const modelFields = typeMap[model];
|
|
1373
|
-
if (!modelFields)
|
|
1374
|
-
throw new ShapeError(`Unknown model: ${model}`);
|
|
1375
|
-
const fieldSchemas = {};
|
|
1376
|
-
for (const fieldName of Object.keys(havingConfig)) {
|
|
1377
|
-
const fieldMeta = modelFields[fieldName];
|
|
1378
|
-
if (!fieldMeta)
|
|
1379
|
-
throw new ShapeError(
|
|
1380
|
-
`Unknown field "${fieldName}" on model "${model}" in having`
|
|
1381
|
-
);
|
|
1382
|
-
if (fieldMeta.isRelation)
|
|
1383
|
-
throw new ShapeError(
|
|
1384
|
-
`Relation field "${fieldName}" cannot be used in having`
|
|
1385
|
-
);
|
|
1386
|
-
if (fieldMeta.isList)
|
|
1387
|
-
throw new ShapeError(
|
|
1388
|
-
`List field "${fieldName}" cannot be used in having`
|
|
1389
|
-
);
|
|
1390
|
-
const ops = getSupportedOperators(fieldMeta);
|
|
1391
|
-
if (ops.length === 0) {
|
|
1392
|
-
throw new ShapeError(
|
|
1393
|
-
`${fieldMeta.type} field "${fieldName}" cannot be used in having filters`
|
|
1394
|
-
);
|
|
1395
|
-
}
|
|
1396
|
-
const opSchemas = {};
|
|
1397
|
-
const opKeys = [];
|
|
1398
|
-
for (const op of ops) {
|
|
1399
|
-
opSchemas[op] = createOperatorSchema(fieldMeta, op, enumMap).optional();
|
|
1400
|
-
opKeys.push(op);
|
|
1401
|
-
}
|
|
1402
|
-
if (fieldMeta.type === "String" && !fieldMeta.isList) {
|
|
1403
|
-
opSchemas["mode"] = z4.enum(["default", "insensitive"]).optional();
|
|
1404
|
-
}
|
|
1405
|
-
fieldSchemas[fieldName] = z4.object(opSchemas).strict().refine(
|
|
1406
|
-
(v) => opKeys.some((k) => v[k] !== void 0),
|
|
1407
|
-
{
|
|
1408
|
-
message: `At least one operator required for having field "${fieldName}"`
|
|
1409
|
-
}
|
|
1410
|
-
).optional();
|
|
1411
|
-
}
|
|
1412
|
-
return z4.object(fieldSchemas).strict().optional();
|
|
1413
|
-
}
|
|
1414
|
-
function buildCountSelectSchema(model, selectConfig) {
|
|
1415
|
-
const modelFields = typeMap[model];
|
|
1416
|
-
if (!modelFields)
|
|
1417
|
-
throw new ShapeError(`Unknown model: ${model}`);
|
|
1418
|
-
const fieldSchemas = {};
|
|
1419
|
-
for (const fieldName of Object.keys(selectConfig)) {
|
|
1420
|
-
if (fieldName === "_all") {
|
|
1421
|
-
fieldSchemas["_all"] = z4.literal(true).optional();
|
|
1422
|
-
continue;
|
|
1423
|
-
}
|
|
1424
|
-
const fieldMeta = modelFields[fieldName];
|
|
1425
|
-
if (!fieldMeta)
|
|
1426
|
-
throw new ShapeError(
|
|
1427
|
-
`Unknown field "${fieldName}" on model "${model}" in count select`
|
|
1428
|
-
);
|
|
1429
|
-
if (fieldMeta.isRelation)
|
|
1430
|
-
throw new ShapeError(
|
|
1431
|
-
`Relation field "${fieldName}" cannot be used in count select`
|
|
1432
|
-
);
|
|
1433
|
-
fieldSchemas[fieldName] = z4.literal(true).optional();
|
|
1434
|
-
}
|
|
1435
|
-
return z4.object(fieldSchemas).strict().optional();
|
|
1651
|
+
return { buildIncludeSchema, buildSelectSchema, buildIncludeCountSchema };
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
// src/runtime/query-builder.ts
|
|
1655
|
+
var METHOD_ALLOWED_ARGS = {
|
|
1656
|
+
findMany: /* @__PURE__ */ new Set(["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"]),
|
|
1657
|
+
findFirst: /* @__PURE__ */ new Set(["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"]),
|
|
1658
|
+
findFirstOrThrow: /* @__PURE__ */ new Set(["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"]),
|
|
1659
|
+
findUnique: /* @__PURE__ */ new Set(["where", "include", "select"]),
|
|
1660
|
+
findUniqueOrThrow: /* @__PURE__ */ new Set(["where", "include", "select"]),
|
|
1661
|
+
count: /* @__PURE__ */ new Set(["where", "select", "cursor", "orderBy", "skip", "take"]),
|
|
1662
|
+
aggregate: /* @__PURE__ */ new Set(["where", "orderBy", "cursor", "take", "skip", "_count", "_avg", "_sum", "_min", "_max"]),
|
|
1663
|
+
groupBy: /* @__PURE__ */ new Set(["where", "by", "having", "_count", "_avg", "_sum", "_min", "_max", "orderBy", "take", "skip"])
|
|
1664
|
+
};
|
|
1665
|
+
var UNIQUE_WHERE_METHODS = /* @__PURE__ */ new Set(["findUnique", "findUniqueOrThrow"]);
|
|
1666
|
+
function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
1667
|
+
const whereBuilder = createWhereBuilder(typeMap, enumMap);
|
|
1668
|
+
const argsBuilder = createArgsBuilder(typeMap, enumMap, uniqueMap);
|
|
1669
|
+
const projectionBuilder = createProjectionBuilder(typeMap, enumMap, {
|
|
1670
|
+
buildWhereSchema: whereBuilder.buildWhereSchema,
|
|
1671
|
+
buildOrderBySchema: argsBuilder.buildOrderBySchema,
|
|
1672
|
+
buildCursorSchema: argsBuilder.buildCursorSchema,
|
|
1673
|
+
buildTakeSchema: argsBuilder.buildTakeSchema
|
|
1674
|
+
});
|
|
1675
|
+
function isShapeConfig(obj) {
|
|
1676
|
+
if (!isPlainObject(obj))
|
|
1677
|
+
return false;
|
|
1678
|
+
const keys = Object.keys(obj);
|
|
1679
|
+
return keys.length === 0 || keys.every((k) => SHAPE_CONFIG_KEYS.has(k));
|
|
1436
1680
|
}
|
|
1437
1681
|
function validateShapeArgs(method, shape) {
|
|
1438
1682
|
const allowed = METHOD_ALLOWED_ARGS[method];
|
|
1439
1683
|
for (const key of Object.keys(shape)) {
|
|
1440
|
-
if (!SHAPE_CONFIG_KEYS.has(key))
|
|
1684
|
+
if (!SHAPE_CONFIG_KEYS.has(key))
|
|
1441
1685
|
throw new ShapeError(`Unknown shape config key "${key}"`);
|
|
1442
|
-
|
|
1443
|
-
if (!allowed.has(key)) {
|
|
1686
|
+
if (!allowed.has(key))
|
|
1444
1687
|
throw new ShapeError(`Arg "${key}" not allowed for method "${method}"`);
|
|
1445
|
-
|
|
1688
|
+
}
|
|
1689
|
+
if (UNIQUE_WHERE_METHODS.has(method) && !shape.where) {
|
|
1690
|
+
throw new ShapeError(`${method} shape must define "where"`);
|
|
1446
1691
|
}
|
|
1447
1692
|
if (shape.include && shape.select) {
|
|
1448
|
-
throw new ShapeError(
|
|
1449
|
-
'Shape config cannot define both "include" and "select".'
|
|
1450
|
-
);
|
|
1693
|
+
throw new ShapeError('Shape config cannot define both "include" and "select".');
|
|
1451
1694
|
}
|
|
1452
|
-
if (method === "groupBy" && !shape.by)
|
|
1695
|
+
if (method === "groupBy" && !shape.by)
|
|
1453
1696
|
throw new ShapeError('groupBy shape must define "by"');
|
|
1454
|
-
}
|
|
1455
1697
|
if (method === "groupBy" && (shape.include || shape.select)) {
|
|
1456
1698
|
throw new ShapeError('groupBy does not support "include" or "select"');
|
|
1457
1699
|
}
|
|
1458
1700
|
if (method === "aggregate" && (shape.include || shape.select)) {
|
|
1459
1701
|
throw new ShapeError('aggregate does not support "include" or "select"');
|
|
1460
1702
|
}
|
|
1461
|
-
if (method === "count" && shape.include)
|
|
1703
|
+
if (method === "count" && shape.include)
|
|
1462
1704
|
throw new ShapeError('count does not support "include"');
|
|
1463
|
-
}
|
|
1464
1705
|
if (method === "groupBy" && shape.orderBy) {
|
|
1465
1706
|
const bySet = new Set(shape.by);
|
|
1466
1707
|
for (const fieldName of Object.keys(shape.orderBy)) {
|
|
1467
1708
|
if (!bySet.has(fieldName)) {
|
|
1468
|
-
throw new ShapeError(
|
|
1469
|
-
`orderBy field "${fieldName}" must be included in "by" for groupBy`
|
|
1470
|
-
);
|
|
1709
|
+
throw new ShapeError(`orderBy field "${fieldName}" must be included in "by" for groupBy`);
|
|
1471
1710
|
}
|
|
1472
1711
|
}
|
|
1473
1712
|
}
|
|
@@ -1475,9 +1714,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1475
1714
|
const bySet = new Set(shape.by);
|
|
1476
1715
|
for (const fieldName of Object.keys(shape.having)) {
|
|
1477
1716
|
if (!bySet.has(fieldName)) {
|
|
1478
|
-
throw new ShapeError(
|
|
1479
|
-
`having field "${fieldName}" must be included in "by" for groupBy`
|
|
1480
|
-
);
|
|
1717
|
+
throw new ShapeError(`having field "${fieldName}" must be included in "by" for groupBy`);
|
|
1481
1718
|
}
|
|
1482
1719
|
}
|
|
1483
1720
|
}
|
|
@@ -1487,97 +1724,76 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1487
1724
|
return;
|
|
1488
1725
|
if (!shape.where)
|
|
1489
1726
|
return;
|
|
1490
|
-
validateUniqueEquality(model, shape.where, method, uniqueMap);
|
|
1727
|
+
validateUniqueEquality(model, shape.where, method, uniqueMap, typeMap);
|
|
1728
|
+
}
|
|
1729
|
+
function resolveAndValidateShape(shapeOrFn, ctx) {
|
|
1730
|
+
if (typeof shapeOrFn === "function") {
|
|
1731
|
+
requireContext(ctx, "shape function");
|
|
1732
|
+
const result = shapeOrFn(ctx);
|
|
1733
|
+
if (!isPlainObject(result)) {
|
|
1734
|
+
throw new ShapeError("Dynamic shape function must return a plain object");
|
|
1735
|
+
}
|
|
1736
|
+
return result;
|
|
1737
|
+
}
|
|
1738
|
+
return shapeOrFn;
|
|
1491
1739
|
}
|
|
1492
1740
|
function buildShapeZodSchema(model, method, shape) {
|
|
1493
1741
|
validateShapeArgs(method, shape);
|
|
1494
1742
|
validateUniqueWhere(model, method, shape);
|
|
1495
1743
|
const schemaFields = {};
|
|
1496
|
-
let forcedWhere =
|
|
1744
|
+
let forcedWhere = EMPTY_WHERE_FORCED;
|
|
1497
1745
|
let forcedIncludeTree = {};
|
|
1498
1746
|
let forcedSelectTree = {};
|
|
1499
1747
|
let forcedIncludeCountWhere = {};
|
|
1500
1748
|
let forcedSelectCountWhere = {};
|
|
1501
1749
|
if (shape.where) {
|
|
1502
|
-
const { schema, forced } = buildWhereSchema(model, shape.where);
|
|
1750
|
+
const { schema, forced } = whereBuilder.buildWhereSchema(model, shape.where);
|
|
1503
1751
|
if (schema)
|
|
1504
1752
|
schemaFields["where"] = schema;
|
|
1505
1753
|
forcedWhere = forced;
|
|
1506
1754
|
}
|
|
1507
1755
|
if (shape.include) {
|
|
1508
|
-
const result = buildIncludeSchema(model, shape.include);
|
|
1756
|
+
const result = projectionBuilder.buildIncludeSchema(model, shape.include);
|
|
1509
1757
|
schemaFields["include"] = result.schema;
|
|
1510
1758
|
forcedIncludeTree = result.forcedTree;
|
|
1511
1759
|
forcedIncludeCountWhere = result.forcedCountWhere;
|
|
1512
1760
|
}
|
|
1513
1761
|
if (shape.select) {
|
|
1514
1762
|
if (method === "count") {
|
|
1515
|
-
schemaFields["select"] = buildCountSelectSchema(model, shape.select);
|
|
1763
|
+
schemaFields["select"] = argsBuilder.buildCountSelectSchema(model, shape.select);
|
|
1516
1764
|
} else {
|
|
1517
|
-
const result = buildSelectSchema(model, shape.select);
|
|
1765
|
+
const result = projectionBuilder.buildSelectSchema(model, shape.select);
|
|
1518
1766
|
schemaFields["select"] = result.schema;
|
|
1519
1767
|
forcedSelectTree = result.forcedTree;
|
|
1520
1768
|
forcedSelectCountWhere = result.forcedCountWhere;
|
|
1521
1769
|
}
|
|
1522
1770
|
}
|
|
1523
|
-
if (shape.orderBy)
|
|
1524
|
-
schemaFields["orderBy"] = buildOrderBySchema(model, shape.orderBy);
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
if (shape.
|
|
1530
|
-
schemaFields["
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
if (shape.
|
|
1536
|
-
schemaFields["
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
if (shape.
|
|
1546
|
-
schemaFields["
|
|
1547
|
-
model,
|
|
1548
|
-
"_avg",
|
|
1549
|
-
shape._avg
|
|
1550
|
-
);
|
|
1551
|
-
}
|
|
1552
|
-
if (shape._sum) {
|
|
1553
|
-
schemaFields["_sum"] = buildAggregateFieldSchema(
|
|
1554
|
-
model,
|
|
1555
|
-
"_sum",
|
|
1556
|
-
shape._sum
|
|
1557
|
-
);
|
|
1558
|
-
}
|
|
1559
|
-
if (shape._min) {
|
|
1560
|
-
schemaFields["_min"] = buildAggregateFieldSchema(
|
|
1561
|
-
model,
|
|
1562
|
-
"_min",
|
|
1563
|
-
shape._min
|
|
1564
|
-
);
|
|
1565
|
-
}
|
|
1566
|
-
if (shape._max) {
|
|
1567
|
-
schemaFields["_max"] = buildAggregateFieldSchema(
|
|
1568
|
-
model,
|
|
1569
|
-
"_max",
|
|
1570
|
-
shape._max
|
|
1571
|
-
);
|
|
1572
|
-
}
|
|
1573
|
-
if (shape.by) {
|
|
1574
|
-
schemaFields["by"] = buildBySchema(model, shape.by);
|
|
1575
|
-
}
|
|
1576
|
-
if (shape.having) {
|
|
1577
|
-
schemaFields["having"] = buildHavingSchema(model, shape.having);
|
|
1578
|
-
}
|
|
1771
|
+
if (shape.orderBy)
|
|
1772
|
+
schemaFields["orderBy"] = argsBuilder.buildOrderBySchema(model, shape.orderBy);
|
|
1773
|
+
if (shape.cursor)
|
|
1774
|
+
schemaFields["cursor"] = argsBuilder.buildCursorSchema(model, shape.cursor);
|
|
1775
|
+
if (shape.take)
|
|
1776
|
+
schemaFields["take"] = argsBuilder.buildTakeSchema(shape.take);
|
|
1777
|
+
if (shape.skip)
|
|
1778
|
+
schemaFields["skip"] = z7.number().int().min(0).optional();
|
|
1779
|
+
if (shape.distinct)
|
|
1780
|
+
schemaFields["distinct"] = argsBuilder.buildDistinctSchema(model, shape.distinct);
|
|
1781
|
+
if (shape._count)
|
|
1782
|
+
schemaFields["_count"] = argsBuilder.buildCountFieldSchema(model, shape._count, "_count");
|
|
1783
|
+
if (shape._avg)
|
|
1784
|
+
schemaFields["_avg"] = argsBuilder.buildAggregateFieldSchema(model, "_avg", shape._avg);
|
|
1785
|
+
if (shape._sum)
|
|
1786
|
+
schemaFields["_sum"] = argsBuilder.buildAggregateFieldSchema(model, "_sum", shape._sum);
|
|
1787
|
+
if (shape._min)
|
|
1788
|
+
schemaFields["_min"] = argsBuilder.buildAggregateFieldSchema(model, "_min", shape._min);
|
|
1789
|
+
if (shape._max)
|
|
1790
|
+
schemaFields["_max"] = argsBuilder.buildAggregateFieldSchema(model, "_max", shape._max);
|
|
1791
|
+
if (shape.by)
|
|
1792
|
+
schemaFields["by"] = argsBuilder.buildBySchema(model, shape.by);
|
|
1793
|
+
if (shape.having)
|
|
1794
|
+
schemaFields["having"] = argsBuilder.buildHavingSchema(model, shape.having);
|
|
1579
1795
|
return {
|
|
1580
|
-
zodSchema:
|
|
1796
|
+
zodSchema: z7.object(schemaFields).strict(),
|
|
1581
1797
|
forcedWhere,
|
|
1582
1798
|
forcedIncludeTree,
|
|
1583
1799
|
forcedSelectTree,
|
|
@@ -1592,30 +1808,20 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1592
1808
|
return { key: matched, shape: shapes[matched] };
|
|
1593
1809
|
}
|
|
1594
1810
|
function buildQuerySchema(model, method, config) {
|
|
1595
|
-
const
|
|
1811
|
+
const isSingle = typeof config === "function" || isShapeConfig(config);
|
|
1596
1812
|
const builtCache = /* @__PURE__ */ new Map();
|
|
1597
|
-
if (
|
|
1598
|
-
|
|
1599
|
-
builtCache.set("_default", built);
|
|
1813
|
+
if (isSingle && typeof config !== "function") {
|
|
1814
|
+
builtCache.set("_default", buildShapeZodSchema(model, method, config));
|
|
1600
1815
|
}
|
|
1601
|
-
if (!
|
|
1816
|
+
if (!isSingle) {
|
|
1602
1817
|
for (const key of Object.keys(config)) {
|
|
1603
1818
|
if (SHAPE_CONFIG_KEYS.has(key)) {
|
|
1604
|
-
throw new ShapeError(
|
|
1605
|
-
`Caller key "${key}" collides with reserved shape config key. Rename the caller path.`
|
|
1606
|
-
);
|
|
1819
|
+
throw new ShapeError(`Caller key "${key}" collides with reserved shape config key. Rename the caller path.`);
|
|
1607
1820
|
}
|
|
1608
1821
|
}
|
|
1609
|
-
for (const [key, shapeOrFn] of Object.entries(
|
|
1610
|
-
config
|
|
1611
|
-
)) {
|
|
1822
|
+
for (const [key, shapeOrFn] of Object.entries(config)) {
|
|
1612
1823
|
if (typeof shapeOrFn !== "function") {
|
|
1613
|
-
|
|
1614
|
-
model,
|
|
1615
|
-
method,
|
|
1616
|
-
shapeOrFn
|
|
1617
|
-
);
|
|
1618
|
-
builtCache.set(key, built);
|
|
1824
|
+
builtCache.set(key, buildShapeZodSchema(model, method, shapeOrFn));
|
|
1619
1825
|
}
|
|
1620
1826
|
}
|
|
1621
1827
|
}
|
|
@@ -1626,45 +1832,41 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1626
1832
|
),
|
|
1627
1833
|
parse(body, opts) {
|
|
1628
1834
|
let built;
|
|
1629
|
-
if (
|
|
1835
|
+
if (isSingle) {
|
|
1630
1836
|
if (typeof config === "function") {
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
built = buildShapeZodSchema(model, method, resolvedShape);
|
|
1837
|
+
const resolved = resolveAndValidateShape(config, opts?.ctx);
|
|
1838
|
+
built = buildShapeZodSchema(model, method, resolved);
|
|
1634
1839
|
} else {
|
|
1635
1840
|
built = builtCache.get("_default");
|
|
1636
1841
|
}
|
|
1637
1842
|
} else {
|
|
1638
|
-
if (!isPlainObject(body))
|
|
1843
|
+
if (!isPlainObject(body))
|
|
1639
1844
|
throw new ShapeError("Request body must be an object");
|
|
1845
|
+
if ("caller" in body) {
|
|
1846
|
+
throw new CallerError(
|
|
1847
|
+
"Pass caller via opts.caller, not in the request body."
|
|
1848
|
+
);
|
|
1640
1849
|
}
|
|
1641
|
-
const caller =
|
|
1850
|
+
const caller = opts?.caller;
|
|
1642
1851
|
if (typeof caller !== "string") {
|
|
1643
|
-
|
|
1852
|
+
const allowed = Object.keys(config);
|
|
1853
|
+
throw new CallerError(
|
|
1854
|
+
`Missing caller. This query uses named shape routing with keys: ${allowed.map((k) => `"${k}"`).join(", ")}. Provide caller via opts.caller.`
|
|
1855
|
+
);
|
|
1644
1856
|
}
|
|
1645
|
-
const matched = matchCaller(
|
|
1646
|
-
config,
|
|
1647
|
-
caller
|
|
1648
|
-
);
|
|
1857
|
+
const matched = matchCaller(config, caller);
|
|
1649
1858
|
if (!matched) {
|
|
1650
|
-
const allowed = Object.keys(
|
|
1651
|
-
config
|
|
1652
|
-
);
|
|
1859
|
+
const allowed = Object.keys(config);
|
|
1653
1860
|
throw new CallerError(
|
|
1654
1861
|
`Unknown caller: "${caller}". Allowed: ${allowed.map((k) => `"${k}"`).join(", ")}`
|
|
1655
1862
|
);
|
|
1656
1863
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
requireContext(opts?.ctx, "shape function");
|
|
1661
|
-
const resolvedShape = shapeOrFn(opts.ctx);
|
|
1662
|
-
built = buildShapeZodSchema(model, method, resolvedShape);
|
|
1864
|
+
if (typeof matched.shape === "function") {
|
|
1865
|
+
const resolved = resolveAndValidateShape(matched.shape, opts?.ctx);
|
|
1866
|
+
built = buildShapeZodSchema(model, method, resolved);
|
|
1663
1867
|
} else {
|
|
1664
|
-
built = builtCache.get(
|
|
1868
|
+
built = builtCache.get(matched.key);
|
|
1665
1869
|
}
|
|
1666
|
-
const { caller: _, ...rest } = body;
|
|
1667
|
-
body = rest;
|
|
1668
1870
|
}
|
|
1669
1871
|
return applyBuiltShape(built, body, isUnique);
|
|
1670
1872
|
}
|
|
@@ -1673,9 +1875,9 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1673
1875
|
return {
|
|
1674
1876
|
buildQuerySchema,
|
|
1675
1877
|
buildShapeZodSchema,
|
|
1676
|
-
buildWhereSchema,
|
|
1677
|
-
buildIncludeSchema,
|
|
1678
|
-
buildSelectSchema
|
|
1878
|
+
buildWhereSchema: whereBuilder.buildWhereSchema,
|
|
1879
|
+
buildIncludeSchema: projectionBuilder.buildIncludeSchema,
|
|
1880
|
+
buildSelectSchema: projectionBuilder.buildSelectSchema
|
|
1679
1881
|
};
|
|
1680
1882
|
}
|
|
1681
1883
|
|
|
@@ -2005,9 +2207,14 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log)
|
|
|
2005
2207
|
}
|
|
2006
2208
|
|
|
2007
2209
|
// src/runtime/model-guard.ts
|
|
2008
|
-
import { z as
|
|
2210
|
+
import { z as z9 } from "zod";
|
|
2211
|
+
|
|
2212
|
+
// src/runtime/model-guard-data.ts
|
|
2213
|
+
import { z as z8 } from "zod";
|
|
2009
2214
|
var ALLOWED_BODY_KEYS_CREATE = /* @__PURE__ */ new Set(["data"]);
|
|
2010
2215
|
var ALLOWED_BODY_KEYS_CREATE_PROJECTION = /* @__PURE__ */ new Set(["data", "select", "include"]);
|
|
2216
|
+
var ALLOWED_BODY_KEYS_CREATE_MANY = /* @__PURE__ */ new Set(["data", "skipDuplicates"]);
|
|
2217
|
+
var ALLOWED_BODY_KEYS_CREATE_MANY_PROJECTION = /* @__PURE__ */ new Set(["data", "select", "include", "skipDuplicates"]);
|
|
2011
2218
|
var ALLOWED_BODY_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
|
|
2012
2219
|
var ALLOWED_BODY_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set(["data", "where", "select", "include"]);
|
|
2013
2220
|
var ALLOWED_BODY_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
|
|
@@ -2018,28 +2225,11 @@ var VALID_SHAPE_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
|
|
|
2018
2225
|
var VALID_SHAPE_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set(["data", "where", "select", "include"]);
|
|
2019
2226
|
var VALID_SHAPE_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
|
|
2020
2227
|
var VALID_SHAPE_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set(["where", "select", "include"]);
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
var BULK_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
2024
|
-
"updateMany",
|
|
2025
|
-
"updateManyAndReturn",
|
|
2026
|
-
"deleteMany"
|
|
2027
|
-
]);
|
|
2028
|
-
var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
2029
|
-
"create",
|
|
2030
|
-
"update",
|
|
2031
|
-
"delete",
|
|
2032
|
-
"createManyAndReturn",
|
|
2033
|
-
"updateManyAndReturn"
|
|
2034
|
-
]);
|
|
2035
|
-
function isGuardShape(obj) {
|
|
2036
|
-
if (!isPlainObject(obj))
|
|
2228
|
+
function isZodSchema2(value) {
|
|
2229
|
+
if (value == null || typeof value !== "object")
|
|
2037
2230
|
return false;
|
|
2038
|
-
const
|
|
2039
|
-
return
|
|
2040
|
-
}
|
|
2041
|
-
function isSingleShape(input) {
|
|
2042
|
-
return typeof input === "function" || isGuardShape(input);
|
|
2231
|
+
const v = value;
|
|
2232
|
+
return typeof v.parse === "function" && typeof v.optional === "function";
|
|
2043
2233
|
}
|
|
2044
2234
|
function validateMutationBodyKeys(body, allowed, method) {
|
|
2045
2235
|
for (const key of Object.keys(body)) {
|
|
@@ -2059,197 +2249,258 @@ function validateMutationShapeKeys(shape, allowed, method) {
|
|
|
2059
2249
|
}
|
|
2060
2250
|
}
|
|
2061
2251
|
}
|
|
2062
|
-
function
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
return
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
for (const [fieldName, meta] of Object.entries(modelFields)) {
|
|
2087
|
-
if (meta.isRelation)
|
|
2088
|
-
continue;
|
|
2089
|
-
if (meta.isUpdatedAt)
|
|
2090
|
-
continue;
|
|
2091
|
-
if (meta.hasDefault)
|
|
2092
|
-
continue;
|
|
2093
|
-
if (!meta.isRequired)
|
|
2094
|
-
continue;
|
|
2095
|
-
if (fieldName in dataConfig)
|
|
2096
|
-
continue;
|
|
2097
|
-
if (fks.has(fieldName))
|
|
2098
|
-
continue;
|
|
2099
|
-
throw new ShapeError(
|
|
2100
|
-
`Required field "${fieldName}" on model "${modelName}" is missing from create data shape, has no default, and is not a scope FK`
|
|
2101
|
-
);
|
|
2102
|
-
}
|
|
2252
|
+
function validateCreateCompleteness(modelName, dataConfig, typeMap, scopeFks, zodDefaults) {
|
|
2253
|
+
const modelFields = typeMap[modelName];
|
|
2254
|
+
if (!modelFields)
|
|
2255
|
+
return;
|
|
2256
|
+
const zodDefaultFields = zodDefaults[modelName];
|
|
2257
|
+
const zodDefaultSet = zodDefaultFields ? new Set(zodDefaultFields) : void 0;
|
|
2258
|
+
for (const [fieldName, meta] of Object.entries(modelFields)) {
|
|
2259
|
+
if (meta.isRelation)
|
|
2260
|
+
continue;
|
|
2261
|
+
if (meta.isUpdatedAt)
|
|
2262
|
+
continue;
|
|
2263
|
+
if (meta.hasDefault)
|
|
2264
|
+
continue;
|
|
2265
|
+
if (!meta.isRequired)
|
|
2266
|
+
continue;
|
|
2267
|
+
if (fieldName in dataConfig)
|
|
2268
|
+
continue;
|
|
2269
|
+
if (scopeFks.has(fieldName))
|
|
2270
|
+
continue;
|
|
2271
|
+
if (zodDefaultSet && zodDefaultSet.has(fieldName))
|
|
2272
|
+
continue;
|
|
2273
|
+
throw new ShapeError(
|
|
2274
|
+
`Required field "${fieldName}" on model "${modelName}" is missing from create data shape, has no default, and is not a scope FK`
|
|
2275
|
+
);
|
|
2103
2276
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2277
|
+
}
|
|
2278
|
+
function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder) {
|
|
2279
|
+
const modelFields = typeMap[model];
|
|
2280
|
+
if (!modelFields)
|
|
2281
|
+
throw new ShapeError(`Unknown model: ${model}`);
|
|
2282
|
+
const schemaMap = {};
|
|
2283
|
+
const forced = {};
|
|
2284
|
+
for (const [fieldName, value] of Object.entries(dataConfig)) {
|
|
2285
|
+
const fieldMeta = modelFields[fieldName];
|
|
2286
|
+
if (!fieldMeta)
|
|
2287
|
+
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
|
|
2288
|
+
if (fieldMeta.isRelation)
|
|
2289
|
+
throw new ShapeError(`Relation field "${fieldName}" cannot be used in data shape`);
|
|
2290
|
+
if (fieldMeta.isUpdatedAt)
|
|
2291
|
+
throw new ShapeError(`updatedAt field "${fieldName}" cannot be used in data shape`);
|
|
2292
|
+
if (typeof value === "function") {
|
|
2293
|
+
let baseSchema = schemaBuilder.buildBaseFieldSchema(model, fieldName);
|
|
2294
|
+
let refined;
|
|
2295
|
+
try {
|
|
2296
|
+
refined = value(baseSchema);
|
|
2297
|
+
} catch (err) {
|
|
2298
|
+
throw new ShapeError(
|
|
2299
|
+
`Invalid inline refine for "${model}.${fieldName}": ${err.message}`,
|
|
2300
|
+
{ cause: err }
|
|
2301
|
+
);
|
|
2302
|
+
}
|
|
2303
|
+
if (!isZodSchema2(refined)) {
|
|
2304
|
+
throw new ShapeError(`Inline refine for "${model}.${fieldName}" must return a Zod schema`);
|
|
2305
|
+
}
|
|
2306
|
+
let fieldSchema = refined;
|
|
2307
|
+
if (mode === "create") {
|
|
2308
|
+
if (!fieldMeta.isRequired) {
|
|
2309
|
+
fieldSchema = fieldSchema.nullable().optional();
|
|
2310
|
+
} else if (fieldMeta.hasDefault) {
|
|
2311
|
+
fieldSchema = fieldSchema.optional();
|
|
2128
2312
|
}
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
}
|
|
2313
|
+
} else {
|
|
2314
|
+
if (!fieldMeta.isRequired) {
|
|
2315
|
+
fieldSchema = fieldSchema.nullable().optional();
|
|
2133
2316
|
} else {
|
|
2134
|
-
|
|
2135
|
-
fieldSchema = fieldSchema.nullable().optional();
|
|
2136
|
-
} else {
|
|
2137
|
-
fieldSchema = fieldSchema.optional();
|
|
2138
|
-
}
|
|
2317
|
+
fieldSchema = fieldSchema.optional();
|
|
2139
2318
|
}
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
} else {
|
|
2148
|
-
|
|
2149
|
-
fieldSchema = fieldSchema.nullable().optional();
|
|
2150
|
-
} else {
|
|
2151
|
-
fieldSchema = fieldSchema.optional();
|
|
2152
|
-
}
|
|
2319
|
+
}
|
|
2320
|
+
schemaMap[fieldName] = fieldSchema;
|
|
2321
|
+
} else if (value === true) {
|
|
2322
|
+
let fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
|
|
2323
|
+
if (mode === "create") {
|
|
2324
|
+
if (!fieldMeta.isRequired) {
|
|
2325
|
+
fieldSchema = fieldSchema.nullable().optional();
|
|
2326
|
+
} else if (fieldMeta.hasDefault) {
|
|
2327
|
+
fieldSchema = fieldSchema.optional();
|
|
2153
2328
|
}
|
|
2154
|
-
schemaMap[fieldName] = fieldSchema;
|
|
2155
2329
|
} else {
|
|
2156
|
-
let fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
|
|
2157
2330
|
if (!fieldMeta.isRequired) {
|
|
2158
|
-
fieldSchema = fieldSchema.nullable();
|
|
2159
|
-
}
|
|
2160
|
-
|
|
2161
|
-
try {
|
|
2162
|
-
parsed = fieldSchema.parse(value);
|
|
2163
|
-
} catch (err) {
|
|
2164
|
-
throw new ShapeError(
|
|
2165
|
-
`Invalid forced data value for "${model}.${fieldName}": ${err.message}`
|
|
2166
|
-
);
|
|
2331
|
+
fieldSchema = fieldSchema.nullable().optional();
|
|
2332
|
+
} else {
|
|
2333
|
+
fieldSchema = fieldSchema.optional();
|
|
2167
2334
|
}
|
|
2168
|
-
forced[fieldName] = parsed;
|
|
2169
2335
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
}
|
|
2176
|
-
function resolveShape(input, body) {
|
|
2177
|
-
if (isSingleShape(input)) {
|
|
2178
|
-
const wasDynamic2 = typeof input === "function";
|
|
2179
|
-
const shape2 = wasDynamic2 ? input(contextFn()) : input;
|
|
2180
|
-
const parsed2 = body === void 0 || body === null ? {} : requireBody(body);
|
|
2181
|
-
if ("caller" in parsed2) {
|
|
2182
|
-
throw new CallerError(
|
|
2183
|
-
'Named shape routing is not configured on this guard. Remove "caller" from the request body or use a named shape map.'
|
|
2184
|
-
);
|
|
2185
|
-
}
|
|
2186
|
-
return { shape: shape2, body: parsed2, matchedKey: "_default", wasDynamic: wasDynamic2 };
|
|
2187
|
-
}
|
|
2188
|
-
const namedMap = input;
|
|
2189
|
-
for (const key of Object.keys(namedMap)) {
|
|
2190
|
-
if (GUARD_SHAPE_KEYS.has(key)) {
|
|
2191
|
-
throw new ShapeError(
|
|
2192
|
-
`Caller key "${key}" collides with reserved shape config key. Rename the caller path.`
|
|
2193
|
-
);
|
|
2336
|
+
schemaMap[fieldName] = fieldSchema;
|
|
2337
|
+
} else {
|
|
2338
|
+
let fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
|
|
2339
|
+
if (!fieldMeta.isRequired) {
|
|
2340
|
+
fieldSchema = fieldSchema.nullable();
|
|
2194
2341
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2342
|
+
let parsed;
|
|
2343
|
+
try {
|
|
2344
|
+
parsed = fieldSchema.parse(value);
|
|
2345
|
+
} catch (err) {
|
|
2197
2346
|
throw new ShapeError(
|
|
2198
|
-
`
|
|
2347
|
+
`Invalid forced data value for "${model}.${fieldName}": ${err.message}`
|
|
2199
2348
|
);
|
|
2200
2349
|
}
|
|
2350
|
+
forced[fieldName] = parsed;
|
|
2201
2351
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2352
|
+
}
|
|
2353
|
+
return {
|
|
2354
|
+
schema: z8.object(schemaMap).strict(),
|
|
2355
|
+
forced
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
function validateAndMergeData(bodyData, cached, method) {
|
|
2359
|
+
if (bodyData === void 0 || bodyData === null) {
|
|
2360
|
+
throw new ShapeError(`${method} requires "data" in request body`);
|
|
2361
|
+
}
|
|
2362
|
+
const validated = cached.schema.parse(bodyData);
|
|
2363
|
+
return { ...validated, ...cached.forced };
|
|
2364
|
+
}
|
|
2365
|
+
function hasDataRefines(dataConfig) {
|
|
2366
|
+
for (const value of Object.values(dataConfig)) {
|
|
2367
|
+
if (typeof value === "function")
|
|
2368
|
+
return true;
|
|
2369
|
+
}
|
|
2370
|
+
return false;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
// src/runtime/model-guard-resolve.ts
|
|
2374
|
+
function isGuardShape(obj) {
|
|
2375
|
+
if (!isPlainObject(obj))
|
|
2376
|
+
return false;
|
|
2377
|
+
const keys = Object.keys(obj);
|
|
2378
|
+
return keys.length === 0 || keys.every((k) => GUARD_SHAPE_KEYS.has(k));
|
|
2379
|
+
}
|
|
2380
|
+
function isSingleShape(input) {
|
|
2381
|
+
return typeof input === "function" || isGuardShape(input);
|
|
2382
|
+
}
|
|
2383
|
+
function requireBody(body) {
|
|
2384
|
+
if (!isPlainObject(body))
|
|
2385
|
+
throw new ShapeError("Request body must be an object");
|
|
2386
|
+
return body;
|
|
2387
|
+
}
|
|
2388
|
+
function resolveDynamicShape(fn, contextFn) {
|
|
2389
|
+
const ctx = validateContext(contextFn());
|
|
2390
|
+
let result;
|
|
2391
|
+
try {
|
|
2392
|
+
result = fn(ctx);
|
|
2393
|
+
} catch (err) {
|
|
2394
|
+
throw new ShapeError(
|
|
2395
|
+
`Dynamic shape function threw: ${err.message}`,
|
|
2396
|
+
{ cause: err }
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
if (!isPlainObject(result)) {
|
|
2400
|
+
throw new ShapeError("Dynamic shape function must return a plain object");
|
|
2401
|
+
}
|
|
2402
|
+
return result;
|
|
2403
|
+
}
|
|
2404
|
+
function resolveShape(input, body, contextFn, caller) {
|
|
2405
|
+
if (isSingleShape(input)) {
|
|
2406
|
+
const wasDynamic2 = typeof input === "function";
|
|
2407
|
+
const shape2 = wasDynamic2 ? resolveDynamicShape(input, contextFn) : input;
|
|
2408
|
+
const parsed2 = body === void 0 || body === null ? {} : requireBody(body);
|
|
2409
|
+
return { shape: shape2, body: parsed2, matchedKey: "_default", wasDynamic: wasDynamic2 };
|
|
2410
|
+
}
|
|
2411
|
+
const namedMap = input;
|
|
2412
|
+
for (const key of Object.keys(namedMap)) {
|
|
2413
|
+
if (GUARD_SHAPE_KEYS.has(key)) {
|
|
2414
|
+
throw new ShapeError(
|
|
2415
|
+
`Caller key "${key}" collides with reserved shape config key. Rename the caller path.`
|
|
2416
|
+
);
|
|
2206
2417
|
}
|
|
2207
|
-
const
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
`Unknown caller: "${caller}". Allowed: ${patterns.map((k) => `"${k}"`).join(", ")}`
|
|
2418
|
+
const val = namedMap[key];
|
|
2419
|
+
if (typeof val !== "function" && !isGuardShape(val)) {
|
|
2420
|
+
throw new ShapeError(
|
|
2421
|
+
`Named shape value for "${key}" must be a guard shape object or function`
|
|
2212
2422
|
);
|
|
2213
2423
|
}
|
|
2214
|
-
const shapeOrFn = namedMap[matched];
|
|
2215
|
-
const wasDynamic = typeof shapeOrFn === "function";
|
|
2216
|
-
const shape = wasDynamic ? shapeOrFn(contextFn()) : shapeOrFn;
|
|
2217
|
-
const { caller: _, ...rest } = parsed;
|
|
2218
|
-
return { shape, body: rest, matchedKey: matched, wasDynamic };
|
|
2219
2424
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2425
|
+
const parsed = requireBody(body);
|
|
2426
|
+
if ("caller" in parsed) {
|
|
2427
|
+
throw new CallerError(
|
|
2428
|
+
"Pass caller as second argument to .guard() or via context function, not in the request body."
|
|
2429
|
+
);
|
|
2430
|
+
}
|
|
2431
|
+
if (typeof caller !== "string") {
|
|
2432
|
+
const patterns2 = Object.keys(namedMap);
|
|
2433
|
+
throw new CallerError(
|
|
2434
|
+
`Missing caller. This guard uses named shape routing with keys: ${patterns2.map((k) => `"${k}"`).join(", ")}. Provide caller as second argument to .guard() or set "caller" in the context function.`
|
|
2435
|
+
);
|
|
2224
2436
|
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2437
|
+
const patterns = Object.keys(namedMap);
|
|
2438
|
+
const matched = matchCallerPattern(patterns, caller);
|
|
2439
|
+
if (!matched) {
|
|
2440
|
+
throw new CallerError(
|
|
2441
|
+
`Unknown caller: "${caller}". Allowed: ${patterns.map((k) => `"${k}"`).join(", ")}`
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
const shapeOrFn = namedMap[matched];
|
|
2445
|
+
const wasDynamic = typeof shapeOrFn === "function";
|
|
2446
|
+
const shape = wasDynamic ? resolveDynamicShape(shapeOrFn, contextFn) : shapeOrFn;
|
|
2447
|
+
return { shape, body: parsed, matchedKey: matched, wasDynamic };
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
// src/runtime/model-guard.ts
|
|
2451
|
+
var UNIQUE_MUTATION_METHODS = /* @__PURE__ */ new Set(["update", "delete"]);
|
|
2452
|
+
var UNIQUE_READ_METHODS = /* @__PURE__ */ new Set(["findUnique", "findUniqueOrThrow"]);
|
|
2453
|
+
var BULK_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
2454
|
+
"updateMany",
|
|
2455
|
+
"updateManyAndReturn",
|
|
2456
|
+
"deleteMany"
|
|
2457
|
+
]);
|
|
2458
|
+
var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
2459
|
+
"create",
|
|
2460
|
+
"update",
|
|
2461
|
+
"delete",
|
|
2462
|
+
"createManyAndReturn",
|
|
2463
|
+
"updateManyAndReturn"
|
|
2464
|
+
]);
|
|
2465
|
+
var BATCH_CREATE_METHODS = /* @__PURE__ */ new Set([
|
|
2466
|
+
"createMany",
|
|
2467
|
+
"createManyAndReturn"
|
|
2468
|
+
]);
|
|
2469
|
+
function createModelGuardExtension(config) {
|
|
2470
|
+
const { typeMap, enumMap, zodChains, zodDefaults, uniqueMap, scopeMap, contextFn } = config;
|
|
2471
|
+
const wrapZodErrors = config.wrapZodErrors ?? false;
|
|
2472
|
+
const schemaBuilder = createSchemaBuilder(typeMap, zodChains, enumMap);
|
|
2473
|
+
const queryBuilder = createQueryBuilder(typeMap, enumMap, uniqueMap);
|
|
2474
|
+
const modelScopeFks = /* @__PURE__ */ new Map();
|
|
2475
|
+
for (const [model, entries] of Object.entries(scopeMap)) {
|
|
2476
|
+
const fks = /* @__PURE__ */ new Set();
|
|
2477
|
+
for (const entry of entries)
|
|
2478
|
+
fks.add(entry.fk);
|
|
2479
|
+
modelScopeFks.set(model, fks);
|
|
2231
2480
|
}
|
|
2232
2481
|
function maybeValidateUniqueWhere(modelName, shape, method) {
|
|
2233
2482
|
if (!UNIQUE_MUTATION_METHODS.has(method))
|
|
2234
2483
|
return;
|
|
2235
2484
|
if (!shape.where)
|
|
2236
2485
|
return;
|
|
2237
|
-
validateUniqueEquality(modelName, shape.where, method, uniqueMap);
|
|
2486
|
+
validateUniqueEquality(modelName, shape.where, method, uniqueMap, typeMap);
|
|
2238
2487
|
}
|
|
2239
|
-
function
|
|
2240
|
-
for (const value of Object.values(dataConfig)) {
|
|
2241
|
-
if (typeof value === "function")
|
|
2242
|
-
return true;
|
|
2243
|
-
}
|
|
2244
|
-
return false;
|
|
2245
|
-
}
|
|
2246
|
-
function createGuardedMethods(modelName, modelDelegate, input) {
|
|
2488
|
+
function createGuardedMethods(modelName, modelDelegate, input, explicitCaller) {
|
|
2247
2489
|
function callDelegate(method, args) {
|
|
2248
2490
|
if (typeof modelDelegate[method] !== "function") {
|
|
2249
2491
|
throw new ShapeError(`Method "${method}" is not available on this model`);
|
|
2250
2492
|
}
|
|
2251
2493
|
return modelDelegate[method](args);
|
|
2252
2494
|
}
|
|
2495
|
+
function resolveCaller() {
|
|
2496
|
+
if (explicitCaller !== void 0)
|
|
2497
|
+
return explicitCaller;
|
|
2498
|
+
const ctx = validateContext(contextFn());
|
|
2499
|
+
const c = ctx.caller;
|
|
2500
|
+
if (typeof c === "string")
|
|
2501
|
+
return c;
|
|
2502
|
+
return void 0;
|
|
2503
|
+
}
|
|
2253
2504
|
const readShapeCache = /* @__PURE__ */ new Map();
|
|
2254
2505
|
const dataSchemaCache = /* @__PURE__ */ new Map();
|
|
2255
2506
|
const whereBuiltCache = /* @__PURE__ */ new Map();
|
|
@@ -2272,11 +2523,11 @@ function createModelGuardExtension(config) {
|
|
|
2272
2523
|
const cached = dataSchemaCache.get(cacheKey);
|
|
2273
2524
|
if (cached)
|
|
2274
2525
|
return cached;
|
|
2275
|
-
const built = buildDataSchema(modelName, dataConfig, mode);
|
|
2526
|
+
const built = buildDataSchema(modelName, dataConfig, mode, typeMap, schemaBuilder);
|
|
2276
2527
|
dataSchemaCache.set(cacheKey, built);
|
|
2277
2528
|
return built;
|
|
2278
2529
|
}
|
|
2279
|
-
return buildDataSchema(modelName, dataConfig, mode);
|
|
2530
|
+
return buildDataSchema(modelName, dataConfig, mode, typeMap, schemaBuilder);
|
|
2280
2531
|
}
|
|
2281
2532
|
function getWhereBuilt(whereConfig, matchedKey, wasDynamic) {
|
|
2282
2533
|
if (!wasDynamic) {
|
|
@@ -2317,7 +2568,7 @@ function createModelGuardExtension(config) {
|
|
|
2317
2568
|
forcedSelectCountWhere = result.forcedCountWhere;
|
|
2318
2569
|
}
|
|
2319
2570
|
return {
|
|
2320
|
-
zodSchema:
|
|
2571
|
+
zodSchema: z9.object(schemaFields).strict(),
|
|
2321
2572
|
forcedIncludeTree,
|
|
2322
2573
|
forcedSelectTree,
|
|
2323
2574
|
forcedIncludeCountWhere,
|
|
@@ -2383,8 +2634,8 @@ function createModelGuardExtension(config) {
|
|
|
2383
2634
|
if (built.schema) {
|
|
2384
2635
|
validatedWhere = built.schema.parse(bodyWhere);
|
|
2385
2636
|
}
|
|
2386
|
-
if (
|
|
2387
|
-
return preserveUnique ?
|
|
2637
|
+
if (hasWhereForced(built.forced)) {
|
|
2638
|
+
return preserveUnique ? mergeUniqueWhereForced(validatedWhere, built.forced) : mergeWhereForced(validatedWhere, built.forced);
|
|
2388
2639
|
}
|
|
2389
2640
|
return validatedWhere ?? {};
|
|
2390
2641
|
}
|
|
@@ -2397,14 +2648,15 @@ function createModelGuardExtension(config) {
|
|
|
2397
2648
|
}
|
|
2398
2649
|
function makeReadMethod(method) {
|
|
2399
2650
|
return (body) => {
|
|
2400
|
-
const
|
|
2401
|
-
|
|
2651
|
+
const caller = resolveCaller();
|
|
2652
|
+
const resolved = resolveShape(input, body, contextFn, caller);
|
|
2653
|
+
if (resolved.shape.data) {
|
|
2402
2654
|
throw new ShapeError(`Guard shape "data" is not valid for ${method}`);
|
|
2403
2655
|
}
|
|
2404
|
-
const { data: _, ...queryShape } = shape;
|
|
2405
|
-
const built = getReadShape(method, queryShape, matchedKey, wasDynamic);
|
|
2656
|
+
const { data: _, ...queryShape } = resolved.shape;
|
|
2657
|
+
const built = getReadShape(method, queryShape, resolved.matchedKey, resolved.wasDynamic);
|
|
2406
2658
|
const isUnique = UNIQUE_READ_METHODS.has(method);
|
|
2407
|
-
const args = applyBuiltShape(built,
|
|
2659
|
+
const args = applyBuiltShape(built, resolved.body, isUnique);
|
|
2408
2660
|
if (isUnique && args.where) {
|
|
2409
2661
|
validateResolvedUniqueWhere(
|
|
2410
2662
|
modelName,
|
|
@@ -2417,33 +2669,51 @@ function createModelGuardExtension(config) {
|
|
|
2417
2669
|
};
|
|
2418
2670
|
}
|
|
2419
2671
|
function makeCreateMethod(method) {
|
|
2672
|
+
const isBatch = BATCH_CREATE_METHODS.has(method);
|
|
2420
2673
|
const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
|
|
2421
|
-
|
|
2674
|
+
let allowedBodyKeys;
|
|
2675
|
+
if (isBatch && supportsProjection) {
|
|
2676
|
+
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_MANY_PROJECTION;
|
|
2677
|
+
} else if (isBatch) {
|
|
2678
|
+
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_MANY;
|
|
2679
|
+
} else if (supportsProjection) {
|
|
2680
|
+
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_PROJECTION;
|
|
2681
|
+
} else {
|
|
2682
|
+
allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE;
|
|
2683
|
+
}
|
|
2422
2684
|
const allowedShapeKeys = supportsProjection ? VALID_SHAPE_KEYS_CREATE_PROJECTION : VALID_SHAPE_KEYS_CREATE;
|
|
2423
2685
|
return (body) => {
|
|
2424
|
-
const
|
|
2425
|
-
|
|
2686
|
+
const caller = resolveCaller();
|
|
2687
|
+
const resolved = resolveShape(input, body, contextFn, caller);
|
|
2688
|
+
if (!resolved.shape.data)
|
|
2426
2689
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
2427
|
-
validateMutationShapeKeys(shape, allowedShapeKeys, method);
|
|
2428
|
-
validateMutationBodyKeys(
|
|
2429
|
-
|
|
2430
|
-
|
|
2690
|
+
validateMutationShapeKeys(resolved.shape, allowedShapeKeys, method);
|
|
2691
|
+
validateMutationBodyKeys(resolved.body, allowedBodyKeys, method);
|
|
2692
|
+
const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
|
|
2693
|
+
validateCreateCompleteness(modelName, resolved.shape.data, typeMap, fks, zodDefaults);
|
|
2694
|
+
const dataSchema = getDataSchema("create", resolved.shape.data, resolved.matchedKey, resolved.wasDynamic);
|
|
2431
2695
|
let args;
|
|
2432
2696
|
if (method === "create") {
|
|
2433
|
-
const data = validateAndMergeData(
|
|
2697
|
+
const data = validateAndMergeData(resolved.body.data, dataSchema, method);
|
|
2434
2698
|
args = { data };
|
|
2435
2699
|
} else {
|
|
2436
|
-
if (!Array.isArray(
|
|
2700
|
+
if (!Array.isArray(resolved.body.data))
|
|
2437
2701
|
throw new ShapeError(`${method} expects data to be an array`);
|
|
2438
|
-
if (
|
|
2702
|
+
if (resolved.body.data.length === 0)
|
|
2439
2703
|
throw new ShapeError(`${method} received empty data array`);
|
|
2440
|
-
const data =
|
|
2704
|
+
const data = resolved.body.data.map(
|
|
2441
2705
|
(item) => validateAndMergeData(item, dataSchema, method)
|
|
2442
2706
|
);
|
|
2443
2707
|
args = { data };
|
|
2444
2708
|
}
|
|
2709
|
+
if (isBatch && resolved.body.skipDuplicates !== void 0) {
|
|
2710
|
+
if (typeof resolved.body.skipDuplicates !== "boolean") {
|
|
2711
|
+
throw new ShapeError(`${method} skipDuplicates must be a boolean`);
|
|
2712
|
+
}
|
|
2713
|
+
args.skipDuplicates = resolved.body.skipDuplicates;
|
|
2714
|
+
}
|
|
2445
2715
|
if (supportsProjection) {
|
|
2446
|
-
const projectionArgs = resolveProjection(shape,
|
|
2716
|
+
const projectionArgs = resolveProjection(resolved.shape, resolved.body, method, resolved.matchedKey, resolved.wasDynamic);
|
|
2447
2717
|
Object.assign(args, projectionArgs);
|
|
2448
2718
|
}
|
|
2449
2719
|
return callDelegate(method, args);
|
|
@@ -2456,18 +2726,19 @@ function createModelGuardExtension(config) {
|
|
|
2456
2726
|
const allowedBodyKeys = supportsProjection ? ALLOWED_BODY_KEYS_UPDATE_PROJECTION : ALLOWED_BODY_KEYS_UPDATE;
|
|
2457
2727
|
const allowedShapeKeys = supportsProjection ? VALID_SHAPE_KEYS_UPDATE_PROJECTION : VALID_SHAPE_KEYS_UPDATE;
|
|
2458
2728
|
return (body) => {
|
|
2459
|
-
const
|
|
2460
|
-
|
|
2729
|
+
const caller = resolveCaller();
|
|
2730
|
+
const resolved = resolveShape(input, body, contextFn, caller);
|
|
2731
|
+
if (!resolved.shape.data)
|
|
2461
2732
|
throw new ShapeError(`Guard shape requires "data" for ${method}`);
|
|
2462
|
-
validateMutationShapeKeys(shape, allowedShapeKeys, method);
|
|
2463
|
-
validateMutationBodyKeys(
|
|
2464
|
-
if (isBulk && !shape.where) {
|
|
2733
|
+
validateMutationShapeKeys(resolved.shape, allowedShapeKeys, method);
|
|
2734
|
+
validateMutationBodyKeys(resolved.body, allowedBodyKeys, method);
|
|
2735
|
+
if (isBulk && !resolved.shape.where) {
|
|
2465
2736
|
throw new ShapeError(`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`);
|
|
2466
2737
|
}
|
|
2467
|
-
maybeValidateUniqueWhere(modelName, shape, method);
|
|
2468
|
-
const dataSchema = getDataSchema("update", shape.data, matchedKey, wasDynamic);
|
|
2469
|
-
const data = validateAndMergeData(
|
|
2470
|
-
const where = isUniqueWhere ? requireWhere(shape,
|
|
2738
|
+
maybeValidateUniqueWhere(modelName, resolved.shape, method);
|
|
2739
|
+
const dataSchema = getDataSchema("update", resolved.shape.data, resolved.matchedKey, resolved.wasDynamic);
|
|
2740
|
+
const data = validateAndMergeData(resolved.body.data, dataSchema, method);
|
|
2741
|
+
const where = isUniqueWhere ? requireWhere(resolved.shape, resolved.body.where, method, true, resolved.matchedKey, resolved.wasDynamic) : buildWhereFromShape(resolved.shape, resolved.body.where, false, resolved.matchedKey, resolved.wasDynamic);
|
|
2471
2742
|
if (isBulk && Object.keys(where).length === 0) {
|
|
2472
2743
|
throw new ShapeError(`${method} requires at least one where condition`);
|
|
2473
2744
|
}
|
|
@@ -2476,7 +2747,7 @@ function createModelGuardExtension(config) {
|
|
|
2476
2747
|
}
|
|
2477
2748
|
const args = { data, where };
|
|
2478
2749
|
if (supportsProjection) {
|
|
2479
|
-
const projectionArgs = resolveProjection(shape,
|
|
2750
|
+
const projectionArgs = resolveProjection(resolved.shape, resolved.body, method, resolved.matchedKey, resolved.wasDynamic);
|
|
2480
2751
|
Object.assign(args, projectionArgs);
|
|
2481
2752
|
}
|
|
2482
2753
|
return callDelegate(method, args);
|
|
@@ -2489,16 +2760,17 @@ function createModelGuardExtension(config) {
|
|
|
2489
2760
|
const allowedBodyKeys = supportsProjection ? ALLOWED_BODY_KEYS_DELETE_PROJECTION : ALLOWED_BODY_KEYS_DELETE;
|
|
2490
2761
|
const allowedShapeKeys = supportsProjection ? VALID_SHAPE_KEYS_DELETE_PROJECTION : VALID_SHAPE_KEYS_DELETE;
|
|
2491
2762
|
return (body) => {
|
|
2492
|
-
const
|
|
2493
|
-
|
|
2763
|
+
const caller = resolveCaller();
|
|
2764
|
+
const resolved = resolveShape(input, body, contextFn, caller);
|
|
2765
|
+
if (resolved.shape.data)
|
|
2494
2766
|
throw new ShapeError(`Guard shape "data" is not valid for ${method}`);
|
|
2495
|
-
validateMutationShapeKeys(shape, allowedShapeKeys, method);
|
|
2496
|
-
validateMutationBodyKeys(
|
|
2497
|
-
if (isBulk && !shape.where) {
|
|
2767
|
+
validateMutationShapeKeys(resolved.shape, allowedShapeKeys, method);
|
|
2768
|
+
validateMutationBodyKeys(resolved.body, allowedBodyKeys, method);
|
|
2769
|
+
if (isBulk && !resolved.shape.where) {
|
|
2498
2770
|
throw new ShapeError(`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`);
|
|
2499
2771
|
}
|
|
2500
|
-
maybeValidateUniqueWhere(modelName, shape, method);
|
|
2501
|
-
const where = isUniqueWhere ? requireWhere(shape,
|
|
2772
|
+
maybeValidateUniqueWhere(modelName, resolved.shape, method);
|
|
2773
|
+
const where = isUniqueWhere ? requireWhere(resolved.shape, resolved.body.where, method, true, resolved.matchedKey, resolved.wasDynamic) : buildWhereFromShape(resolved.shape, resolved.body.where, false, resolved.matchedKey, resolved.wasDynamic);
|
|
2502
2774
|
if (isBulk && Object.keys(where).length === 0) {
|
|
2503
2775
|
throw new ShapeError(`${method} requires at least one where condition`);
|
|
2504
2776
|
}
|
|
@@ -2507,7 +2779,7 @@ function createModelGuardExtension(config) {
|
|
|
2507
2779
|
}
|
|
2508
2780
|
const args = { where };
|
|
2509
2781
|
if (supportsProjection) {
|
|
2510
|
-
const projectionArgs = resolveProjection(shape,
|
|
2782
|
+
const projectionArgs = resolveProjection(resolved.shape, resolved.body, method, resolved.matchedKey, resolved.wasDynamic);
|
|
2511
2783
|
Object.assign(args, projectionArgs);
|
|
2512
2784
|
}
|
|
2513
2785
|
return callDelegate(method, args);
|
|
@@ -2539,7 +2811,7 @@ function createModelGuardExtension(config) {
|
|
|
2539
2811
|
try {
|
|
2540
2812
|
return fn(body);
|
|
2541
2813
|
} catch (err) {
|
|
2542
|
-
if (err instanceof
|
|
2814
|
+
if (err instanceof z9.ZodError) {
|
|
2543
2815
|
throw new ShapeError(
|
|
2544
2816
|
`Validation failed: ${formatZodError(err)}`,
|
|
2545
2817
|
{ cause: err }
|
|
@@ -2553,16 +2825,16 @@ function createModelGuardExtension(config) {
|
|
|
2553
2825
|
}
|
|
2554
2826
|
return {
|
|
2555
2827
|
$allModels: {
|
|
2556
|
-
guard(input) {
|
|
2828
|
+
guard(input, caller) {
|
|
2557
2829
|
const modelName = this.$name;
|
|
2558
|
-
const delegateKey =
|
|
2830
|
+
const delegateKey = toDelegateKey(modelName);
|
|
2559
2831
|
const modelDelegate = this.$parent[delegateKey];
|
|
2560
2832
|
if (!modelDelegate) {
|
|
2561
2833
|
throw new ShapeError(
|
|
2562
2834
|
`Could not resolve Prisma delegate for model "${modelName}" (key: "${delegateKey}")`
|
|
2563
2835
|
);
|
|
2564
2836
|
}
|
|
2565
|
-
const methods = createGuardedMethods(modelName, modelDelegate, input);
|
|
2837
|
+
const methods = createGuardedMethods(modelName, modelDelegate, input, caller);
|
|
2566
2838
|
if (!wrapZodErrors)
|
|
2567
2839
|
return methods;
|
|
2568
2840
|
return wrapMethods(methods);
|
|
@@ -2572,12 +2844,6 @@ function createModelGuardExtension(config) {
|
|
|
2572
2844
|
}
|
|
2573
2845
|
|
|
2574
2846
|
// src/runtime/guard.ts
|
|
2575
|
-
function formatZodError2(err) {
|
|
2576
|
-
return err.issues.map((i) => {
|
|
2577
|
-
const p = i.path.length > 0 ? `${i.path.join(".")}: ` : "";
|
|
2578
|
-
return `${p}${i.message}`;
|
|
2579
|
-
}).join("; ");
|
|
2580
|
-
}
|
|
2581
2847
|
function createGuard(config) {
|
|
2582
2848
|
const schemaBuilder = createSchemaBuilder(
|
|
2583
2849
|
config.typeMap,
|
|
@@ -2593,7 +2859,7 @@ function createGuard(config) {
|
|
|
2593
2859
|
const wrapZodErrors = config.wrapZodErrors ?? false;
|
|
2594
2860
|
function rethrowZod(err) {
|
|
2595
2861
|
if (err instanceof ZodError) {
|
|
2596
|
-
throw new ShapeError(`Validation failed: ${
|
|
2862
|
+
throw new ShapeError(`Validation failed: ${formatZodError(err)}`, { cause: err });
|
|
2597
2863
|
}
|
|
2598
2864
|
throw err;
|
|
2599
2865
|
}
|
|
@@ -2630,16 +2896,24 @@ function createGuard(config) {
|
|
|
2630
2896
|
};
|
|
2631
2897
|
},
|
|
2632
2898
|
extension: (contextFn) => {
|
|
2899
|
+
const scopeRoots = /* @__PURE__ */ new Set();
|
|
2900
|
+
for (const entries of Object.values(config.scopeMap)) {
|
|
2901
|
+
for (const entry of entries) {
|
|
2902
|
+
scopeRoots.add(entry.root);
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2633
2905
|
const scopeCtxFn = () => {
|
|
2634
|
-
const ctx = contextFn();
|
|
2906
|
+
const ctx = validateContext(contextFn());
|
|
2635
2907
|
const scopeCtx = {};
|
|
2636
2908
|
for (const key of Object.keys(ctx)) {
|
|
2909
|
+
if (!scopeRoots.has(key))
|
|
2910
|
+
continue;
|
|
2637
2911
|
const val = ctx[key];
|
|
2638
2912
|
if (typeof val === "string" || typeof val === "number" || typeof val === "bigint") {
|
|
2639
2913
|
scopeCtx[key] = val;
|
|
2640
2914
|
} else if (val !== null && val !== void 0) {
|
|
2641
2915
|
log.warn(
|
|
2642
|
-
`prisma-guard:
|
|
2916
|
+
`prisma-guard: Scope root "${key}" has non-primitive value (${typeof val}). Only string, number, and bigint values are used for scope context.`
|
|
2643
2917
|
);
|
|
2644
2918
|
}
|
|
2645
2919
|
}
|
|
@@ -2655,6 +2929,7 @@ function createGuard(config) {
|
|
|
2655
2929
|
typeMap: config.typeMap,
|
|
2656
2930
|
enumMap: config.enumMap,
|
|
2657
2931
|
zodChains: config.zodChains,
|
|
2932
|
+
zodDefaults: config.zodDefaults ?? {},
|
|
2658
2933
|
uniqueMap: config.uniqueMap ?? {},
|
|
2659
2934
|
scopeMap: config.scopeMap,
|
|
2660
2935
|
contextFn,
|