prisma-guard 1.1.0 → 1.2.1

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.
@@ -26,19 +26,25 @@ var CallerError = class extends Error {
26
26
  this.name = "CallerError";
27
27
  }
28
28
  };
29
-
30
- // src/runtime/schema-builder.ts
31
- import { z as z3 } from "zod";
32
-
33
- // src/runtime/zod-type-map.ts
34
- import { z as z2 } from "zod";
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
+ }
35
35
 
36
36
  // src/shared/scalar-base.ts
37
37
  import { z } from "zod";
38
38
  function isJsonSafe(value) {
39
- const stack = [value];
39
+ const stack = [{ tag: "visit", value }];
40
+ const ancestors = /* @__PURE__ */ new Set();
40
41
  while (stack.length > 0) {
41
- const current = stack.pop();
42
+ const entry = stack.pop();
43
+ if (entry.tag === "exit") {
44
+ ancestors.delete(entry.ref);
45
+ continue;
46
+ }
47
+ const current = entry.value;
42
48
  if (current === void 0)
43
49
  return false;
44
50
  if (current === null)
@@ -52,9 +58,13 @@ function isJsonSafe(value) {
52
58
  return false;
53
59
  continue;
54
60
  case "object": {
61
+ if (ancestors.has(current))
62
+ return false;
63
+ ancestors.add(current);
64
+ stack.push({ tag: "exit", ref: current });
55
65
  if (Array.isArray(current)) {
56
66
  for (let i = 0; i < current.length; i++) {
57
- stack.push(current[i]);
67
+ stack.push({ tag: "visit", value: current[i] });
58
68
  }
59
69
  continue;
60
70
  }
@@ -63,7 +73,7 @@ function isJsonSafe(value) {
63
73
  return false;
64
74
  const values = Object.values(current);
65
75
  for (let i = 0; i < values.length; i++) {
66
- stack.push(values[i]);
76
+ stack.push({ tag: "visit", value: values[i] });
67
77
  }
68
78
  continue;
69
79
  }
@@ -73,39 +83,54 @@ function isJsonSafe(value) {
73
83
  }
74
84
  return true;
75
85
  }
76
- var SCALAR_BASE = {
77
- String: () => z.string(),
78
- Int: () => z.number().int(),
79
- Float: () => z.number(),
80
- Decimal: () => z.union([
81
- z.number(),
82
- z.string().refine(
83
- (s) => /^-?(\d+\.?\d*|\.\d+)([eE]-?\d+)?$/.test(s),
84
- "Invalid decimal string"
85
- )
86
- ]),
87
- BigInt: () => z.union([
88
- z.bigint(),
89
- z.number().int().refine(
90
- (v) => v >= Number.MIN_SAFE_INTEGER && v <= Number.MAX_SAFE_INTEGER,
91
- "Number exceeds safe integer range for BigInt conversion"
92
- ).transform((v) => BigInt(v)),
93
- z.string().regex(/^-?\d+$/).transform((v) => BigInt(v))
94
- ]),
95
- Boolean: () => z.boolean(),
96
- DateTime: () => z.union([
97
- z.date(),
98
- z.string().datetime({ offset: true }),
99
- z.string().datetime()
100
- ]).pipe(z.coerce.date()),
101
- Json: () => z.unknown().refine(isJsonSafe, "Value must be JSON-serializable (no undefined, functions, symbols, class instances, NaN, or Infinity)"),
102
- Bytes: () => z.union([
103
- z.string(),
104
- z.custom((v) => v instanceof Uint8Array)
105
- ])
106
- };
86
+ var DECIMAL_REGEX = /^-?(\d+\.?\d*|\.\d+)([eE]-?\d+)?$/;
87
+ var decimalStringSchema = z.string().refine(
88
+ (s) => DECIMAL_REGEX.test(s),
89
+ "Invalid decimal string"
90
+ );
91
+ var decimalObjectSchema = z.custom(
92
+ (v) => v !== null && typeof v === "object" && typeof v.toFixed === "function" && typeof v.toNumber === "function",
93
+ "Expected Decimal-compatible object"
94
+ );
95
+ function createDecimalFactory(strict) {
96
+ if (strict) {
97
+ return () => z.union([decimalStringSchema, decimalObjectSchema]);
98
+ }
99
+ return () => z.union([z.number(), decimalStringSchema, decimalObjectSchema]);
100
+ }
101
+ function createScalarBase(strictDecimal) {
102
+ return {
103
+ String: () => z.string(),
104
+ Int: () => z.number().int(),
105
+ Float: () => z.number(),
106
+ Decimal: createDecimalFactory(strictDecimal),
107
+ BigInt: () => z.union([
108
+ z.bigint(),
109
+ z.number().int().refine(
110
+ (v) => v >= Number.MIN_SAFE_INTEGER && v <= Number.MAX_SAFE_INTEGER,
111
+ "Number exceeds safe integer range for BigInt conversion"
112
+ ).transform((v) => BigInt(v)),
113
+ z.string().regex(/^-?\d+$/).transform((v) => BigInt(v))
114
+ ]),
115
+ Boolean: () => z.boolean(),
116
+ DateTime: () => z.union([
117
+ z.date(),
118
+ z.string().datetime({ offset: true })
119
+ ]).pipe(z.coerce.date()),
120
+ Json: () => z.unknown().refine(isJsonSafe, "Value must be JSON-serializable (no undefined, functions, symbols, class instances, NaN, Infinity, or circular references)"),
121
+ Bytes: () => z.union([
122
+ z.string(),
123
+ z.custom((v) => v instanceof Uint8Array)
124
+ ])
125
+ };
126
+ }
127
+ var SCALAR_BASE = createScalarBase(false);
128
+
129
+ // src/runtime/schema-builder.ts
130
+ import { z as z3 } from "zod";
107
131
 
108
132
  // src/runtime/zod-type-map.ts
133
+ import { z as z2 } from "zod";
109
134
  var SCALAR_OPERATORS = {
110
135
  String: /* @__PURE__ */ new Set(["equals", "not", "contains", "startsWith", "endsWith", "in", "notIn"]),
111
136
  Int: /* @__PURE__ */ new Set(["equals", "not", "gt", "gte", "lt", "lte", "in", "notIn"]),
@@ -130,7 +155,7 @@ function getSupportedOperators(fieldMeta) {
130
155
  return [];
131
156
  return [...ops];
132
157
  }
133
- function createBaseType(fieldMeta, enumMap) {
158
+ function createBaseType(fieldMeta, enumMap, scalarBase) {
134
159
  let base;
135
160
  if (fieldMeta.isEnum) {
136
161
  const values = enumMap[fieldMeta.type];
@@ -139,7 +164,7 @@ function createBaseType(fieldMeta, enumMap) {
139
164
  }
140
165
  base = z2.enum(values);
141
166
  } else {
142
- const factory = SCALAR_BASE[fieldMeta.type];
167
+ const factory = scalarBase[fieldMeta.type];
143
168
  if (!factory) {
144
169
  throw new ShapeError(`Unknown scalar type: ${fieldMeta.type}`);
145
170
  }
@@ -150,7 +175,7 @@ function createBaseType(fieldMeta, enumMap) {
150
175
  }
151
176
  return base;
152
177
  }
153
- function createScalarListOperatorSchema(fieldMeta, operator, enumMap) {
178
+ function createScalarListOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
154
179
  if (!SCALAR_LIST_OPERATORS.has(operator)) {
155
180
  throw new ShapeError(`Operator "${operator}" not supported for scalar list fields`);
156
181
  }
@@ -159,19 +184,19 @@ function createScalarListOperatorSchema(fieldMeta, operator, enumMap) {
159
184
  }
160
185
  if (operator === "equals") {
161
186
  const itemMeta2 = { ...fieldMeta, isList: false };
162
- const itemBase2 = createBaseType(itemMeta2, enumMap);
187
+ const itemBase2 = createBaseType(itemMeta2, enumMap, scalarBase);
163
188
  return fieldMeta.isRequired ? z2.array(itemBase2) : z2.union([z2.array(itemBase2), z2.null()]);
164
189
  }
165
190
  const itemMeta = { ...fieldMeta, isList: false };
166
- const itemBase = createBaseType(itemMeta, enumMap);
191
+ const itemBase = createBaseType(itemMeta, enumMap, scalarBase);
167
192
  if (operator === "has") {
168
193
  return !fieldMeta.isRequired ? z2.union([itemBase, z2.null()]) : itemBase;
169
194
  }
170
195
  return z2.array(itemBase);
171
196
  }
172
- function createOperatorSchema(fieldMeta, operator, enumMap) {
197
+ function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
173
198
  if (fieldMeta.isList) {
174
- return createScalarListOperatorSchema(fieldMeta, operator, enumMap);
199
+ return createScalarListOperatorSchema(fieldMeta, operator, enumMap, scalarBase);
175
200
  }
176
201
  if (fieldMeta.isEnum) {
177
202
  const values = enumMap[fieldMeta.type];
@@ -200,7 +225,7 @@ function createOperatorSchema(fieldMeta, operator, enumMap) {
200
225
  if (!supportedOps.has(operator)) {
201
226
  throw new ShapeError(`Operator "${operator}" not supported for type "${fieldMeta.type}"`);
202
227
  }
203
- const factory = SCALAR_BASE[fieldMeta.type];
228
+ const factory = scalarBase[fieldMeta.type];
204
229
  if (!factory) {
205
230
  throw new ShapeError(`Unknown scalar type: ${fieldMeta.type}`);
206
231
  }
@@ -217,6 +242,24 @@ function createOperatorSchema(fieldMeta, operator, enumMap) {
217
242
  return scalar;
218
243
  }
219
244
 
245
+ // src/shared/utils.ts
246
+ function isPlainObject(v) {
247
+ if (typeof v !== "object" || v === null || Array.isArray(v))
248
+ return false;
249
+ const proto = Object.getPrototypeOf(v);
250
+ return proto === Object.prototype || proto === null;
251
+ }
252
+ function schemaProducesValueForUndefined(schema) {
253
+ const result = schema.safeParse(void 0);
254
+ return result.success && result.data !== void 0;
255
+ }
256
+ function isZodSchema(value) {
257
+ if (value == null || typeof value !== "object")
258
+ return false;
259
+ const v = value;
260
+ return typeof v.parse === "function" && typeof v.optional === "function";
261
+ }
262
+
220
263
  // src/runtime/schema-builder.ts
221
264
  var DEFAULT_MAX_CACHE = 500;
222
265
  var DEFAULT_MAX_DEPTH = 5;
@@ -238,7 +281,7 @@ function lruSet(cache, key, value, maxSize) {
238
281
  cache.delete(oldest);
239
282
  }
240
283
  }
241
- function createSchemaBuilder(typeMap, zodChains, enumMap) {
284
+ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefaults) {
242
285
  const chainCache = /* @__PURE__ */ new Map();
243
286
  function buildFieldSchema(model, field) {
244
287
  const cacheKey = `${model}.${field}`;
@@ -251,7 +294,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
251
294
  const fieldMeta = modelFields[field];
252
295
  if (!fieldMeta)
253
296
  throw new ShapeError(`Unknown field "${field}" on model "${model}"`);
254
- const base = createBaseType(fieldMeta, enumMap);
297
+ const base = createBaseType(fieldMeta, enumMap, scalarBase);
255
298
  let result = base;
256
299
  const chainFn = zodChains[model]?.[field];
257
300
  if (chainFn) {
@@ -274,17 +317,19 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
274
317
  const fieldMeta = modelFields[field];
275
318
  if (!fieldMeta)
276
319
  throw new ShapeError(`Unknown field "${field}" on model "${model}"`);
277
- return createBaseType(fieldMeta, enumMap);
320
+ return createBaseType(fieldMeta, enumMap, scalarBase);
278
321
  }
279
322
  function buildInputSchema(model, opts) {
280
323
  if (opts.pick && opts.omit) {
281
324
  throw new ShapeError('InputOpts cannot define both "pick" and "omit"');
282
325
  }
283
326
  const mode = opts.mode ?? "create";
284
- const allowNull = opts.allowNull ?? false;
327
+ const allowNull = opts.allowNull ?? true;
285
328
  const modelFields = typeMap[model];
286
329
  if (!modelFields)
287
330
  throw new ShapeError(`Unknown model: ${model}`);
331
+ const zodDefaultFields = zodDefaults[model];
332
+ const zodDefaultSet = zodDefaultFields ? new Set(zodDefaultFields) : void 0;
288
333
  let fieldNames = Object.keys(modelFields).filter((name) => {
289
334
  const meta = modelFields[name];
290
335
  return !meta.isRelation && !meta.isUpdatedAt;
@@ -310,16 +355,39 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
310
355
  for (const name of fieldNames) {
311
356
  const fieldMeta = modelFields[name];
312
357
  let fieldSchema;
358
+ let handlesUndefined;
313
359
  if (opts.refine?.[name]) {
314
- fieldSchema = opts.refine[name](buildBaseFieldSchema(model, name));
360
+ let refined;
361
+ try {
362
+ refined = opts.refine[name](buildBaseFieldSchema(model, name));
363
+ } catch (err) {
364
+ throw new ShapeError(
365
+ `Refine function for "${model}.${name}" threw: ${err.message}`,
366
+ { cause: err }
367
+ );
368
+ }
369
+ if (!isZodSchema(refined)) {
370
+ throw new ShapeError(
371
+ `Refine function for "${model}.${name}" must return a Zod schema`
372
+ );
373
+ }
374
+ fieldSchema = refined;
375
+ handlesUndefined = schemaProducesValueForUndefined(fieldSchema);
315
376
  } else {
316
377
  fieldSchema = buildFieldSchema(model, name);
378
+ handlesUndefined = zodDefaultSet !== void 0 && zodDefaultSet.has(name);
317
379
  }
318
380
  if (mode === "create") {
319
381
  if (!fieldMeta.isRequired) {
320
- fieldSchema = allowNull ? fieldSchema.nullable().optional() : fieldSchema.optional();
382
+ if (handlesUndefined) {
383
+ fieldSchema = allowNull ? fieldSchema.nullable() : fieldSchema;
384
+ } else {
385
+ fieldSchema = allowNull ? fieldSchema.nullable().optional() : fieldSchema.optional();
386
+ }
321
387
  } else if (fieldMeta.hasDefault) {
322
- fieldSchema = fieldSchema.optional();
388
+ if (!handlesUndefined) {
389
+ fieldSchema = fieldSchema.optional();
390
+ }
323
391
  }
324
392
  } else {
325
393
  if (!fieldMeta.isRequired && allowNull) {
@@ -380,7 +448,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
380
448
  const schemaMap = {};
381
449
  for (const name of scalarNames) {
382
450
  const fieldMeta = modelFields[name];
383
- let fieldSchema = createBaseType(fieldMeta, enumMap);
451
+ let fieldSchema = createBaseType(fieldMeta, enumMap, scalarBase);
384
452
  if (!fieldMeta.isRequired) {
385
453
  fieldSchema = fieldSchema.nullable();
386
454
  }
@@ -410,10 +478,12 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
410
478
  schemaMap[relName] = relSchema;
411
479
  }
412
480
  if (opts._count) {
413
- const relationNames = Object.keys(modelFields).filter((n) => modelFields[n].isRelation);
481
+ const listRelationNames = Object.keys(modelFields).filter(
482
+ (n) => modelFields[n].isRelation && modelFields[n].isList
483
+ );
414
484
  if (opts._count === true) {
415
485
  const countFields = {};
416
- for (const relName of relationNames) {
486
+ for (const relName of listRelationNames) {
417
487
  countFields[relName] = z3.number().int().min(0);
418
488
  }
419
489
  schemaMap["_count"] = z3.object(countFields);
@@ -424,6 +494,8 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
424
494
  throw new ShapeError(`Unknown field "${relName}" on model "${model}" in _count`);
425
495
  if (!modelFields[relName].isRelation)
426
496
  throw new ShapeError(`Field "${relName}" is not a relation on model "${model}" in _count`);
497
+ if (!modelFields[relName].isList)
498
+ throw new ShapeError(`Field "${relName}" is a to-one relation on model "${model}" in _count. Only to-many relations support _count.`);
427
499
  countFields[relName] = z3.number().int().min(0);
428
500
  }
429
501
  schemaMap["_count"] = z3.object(countFields);
@@ -439,7 +511,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
439
511
  }
440
512
 
441
513
  // src/runtime/query-builder.ts
442
- import { z as z4 } from "zod";
514
+ import { z as z7 } from "zod";
443
515
 
444
516
  // src/shared/constants.ts
445
517
  var SHAPE_CONFIG_KEYS = /* @__PURE__ */ new Set([
@@ -461,8 +533,26 @@ var SHAPE_CONFIG_KEYS = /* @__PURE__ */ new Set([
461
533
  ]);
462
534
  var GUARD_SHAPE_KEYS = /* @__PURE__ */ new Set([
463
535
  "data",
536
+ "create",
537
+ "update",
464
538
  ...SHAPE_CONFIG_KEYS
465
539
  ]);
540
+ var COMBINATOR_KEYS = /* @__PURE__ */ new Set(["AND", "OR", "NOT"]);
541
+ var TO_MANY_RELATION_OPS = /* @__PURE__ */ new Set(["some", "every", "none"]);
542
+ var TO_ONE_RELATION_OPS = /* @__PURE__ */ new Set(["is", "isNot"]);
543
+ var ALL_RELATION_OPS = /* @__PURE__ */ new Set([...TO_MANY_RELATION_OPS, ...TO_ONE_RELATION_OPS]);
544
+ function toDelegateKey(modelName) {
545
+ return modelName[0].toLowerCase() + modelName.slice(1);
546
+ }
547
+ var FORCED_MARKER = Symbol.for("prisma-guard.forced");
548
+ function isForcedValue(v) {
549
+ return v !== null && typeof v === "object" && v[FORCED_MARKER] === true;
550
+ }
551
+ function force(value) {
552
+ const wrapper = { value };
553
+ wrapper[FORCED_MARKER] = true;
554
+ return wrapper;
555
+ }
466
556
 
467
557
  // src/shared/match-caller.ts
468
558
  function matchCallerPattern(patterns, caller) {
@@ -504,124 +594,124 @@ function requireContext(ctx, label) {
504
594
  throw new PolicyError(`Context required for ${label}`);
505
595
  }
506
596
  }
597
+ function validateContext(ctx) {
598
+ if (ctx === null || ctx === void 0) {
599
+ throw new PolicyError(
600
+ `guard.extension() context function must return a plain object, got ${String(ctx)}`
601
+ );
602
+ }
603
+ if (!isPlainObject(ctx)) {
604
+ throw new PolicyError(
605
+ `guard.extension() context function must return a plain object, got ${Array.isArray(ctx) ? "array" : typeof ctx}`
606
+ );
607
+ }
608
+ return ctx;
609
+ }
507
610
 
508
- // src/runtime/query-builder.ts
509
- var METHOD_ALLOWED_ARGS = {
510
- findMany: /* @__PURE__ */ new Set([
511
- "where",
512
- "include",
513
- "select",
514
- "orderBy",
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;
611
+ // src/runtime/query-builder-where.ts
612
+ import { z as z4 } from "zod";
613
+
614
+ // src/shared/deep-clone.ts
615
+ function deepClone(value) {
616
+ if (value === null || value === void 0)
617
+ return value;
618
+ switch (typeof value) {
619
+ case "string":
620
+ case "number":
621
+ case "boolean":
622
+ case "bigint":
623
+ return value;
624
+ case "object": {
625
+ if (value instanceof Date)
626
+ return new Date(value.getTime());
627
+ if (value instanceof Uint8Array)
628
+ return value.slice();
629
+ if (Array.isArray(value))
630
+ return value.map(deepClone);
631
+ const proto = Object.getPrototypeOf(value);
632
+ if (proto !== Object.prototype && proto !== null)
633
+ return value;
634
+ const result = {};
635
+ for (const [k, v] of Object.entries(value)) {
636
+ result[k] = deepClone(v);
637
+ }
638
+ return result;
639
+ }
640
+ default:
641
+ return value;
642
+ }
604
643
  }
605
- function mergeForced(where, forced) {
606
- if (!where)
607
- return forced;
608
- return { AND: [where, forced] };
644
+
645
+ // src/runtime/query-builder-forced.ts
646
+ var EMPTY_WHERE_FORCED = { conditions: {}, relations: {} };
647
+ function hasWhereForced(f) {
648
+ return Object.keys(f.conditions).length > 0 || Object.keys(f.relations).length > 0;
649
+ }
650
+ function mergeWhereForced(where, forced) {
651
+ if (!hasWhereForced(forced))
652
+ return where ?? {};
653
+ let result = where ? deepClone(where) : {};
654
+ for (const [relName, opMap] of Object.entries(forced.relations)) {
655
+ if (!result[relName] || typeof result[relName] !== "object") {
656
+ result[relName] = {};
657
+ }
658
+ const relObj = result[relName];
659
+ for (const [op, nestedForced] of Object.entries(opMap)) {
660
+ relObj[op] = mergeWhereForced(
661
+ relObj[op],
662
+ nestedForced
663
+ );
664
+ }
665
+ }
666
+ if (Object.keys(forced.conditions).length > 0) {
667
+ const scalarClone = deepClone(forced.conditions);
668
+ if (Object.keys(result).length === 0) {
669
+ result = scalarClone;
670
+ } else {
671
+ result = { AND: [result, scalarClone] };
672
+ }
673
+ }
674
+ return result;
609
675
  }
610
- function mergeUniqueForced(where, forced) {
611
- if (!where)
612
- return { ...forced };
613
- return { ...where, AND: [forced] };
676
+ function mergeUniqueWhereForced(where, forced) {
677
+ if (!hasWhereForced(forced))
678
+ return where ?? {};
679
+ let result = where ? { ...where } : {};
680
+ for (const [relName, opMap] of Object.entries(forced.relations)) {
681
+ if (!result[relName] || typeof result[relName] !== "object") {
682
+ result[relName] = {};
683
+ }
684
+ const relObj = result[relName];
685
+ for (const [op, nestedForced] of Object.entries(opMap)) {
686
+ relObj[op] = mergeWhereForced(
687
+ relObj[op],
688
+ nestedForced
689
+ );
690
+ }
691
+ }
692
+ if (Object.keys(forced.conditions).length > 0) {
693
+ const conditions = [deepClone(forced.conditions)];
694
+ const existing = result.AND;
695
+ if (existing) {
696
+ if (Array.isArray(existing)) {
697
+ conditions.unshift(...existing);
698
+ } else {
699
+ conditions.unshift(existing);
700
+ }
701
+ }
702
+ result.AND = conditions;
703
+ }
704
+ return result;
614
705
  }
615
706
  function applyBuiltShape(built, body, isUniqueMethod) {
616
707
  const validated = built.zodSchema.parse(body);
617
- if (Object.keys(built.forcedWhere).length > 0) {
618
- const cloned = structuredClone(built.forcedWhere);
619
- validated.where = isUniqueMethod ? mergeUniqueForced(
708
+ if (hasWhereForced(built.forcedWhere)) {
709
+ validated.where = isUniqueMethod ? mergeUniqueWhereForced(
620
710
  validated.where,
621
- cloned
622
- ) : mergeForced(
711
+ built.forcedWhere
712
+ ) : mergeWhereForced(
623
713
  validated.where,
624
- cloned
714
+ built.forcedWhere
625
715
  );
626
716
  }
627
717
  if (Object.keys(built.forcedIncludeTree).length > 0) {
@@ -631,19 +721,24 @@ function applyBuiltShape(built, body, isUniqueMethod) {
631
721
  applyForcedTree(validated, "select", built.forcedSelectTree);
632
722
  }
633
723
  if (Object.keys(built.forcedIncludeCountWhere).length > 0) {
634
- const includeContainer = validated.include;
635
- if (includeContainer) {
636
- applyForcedCountWhere(includeContainer, built.forcedIncludeCountWhere);
637
- }
724
+ const ic = validated.include;
725
+ if (ic)
726
+ applyForcedCountWhere(ic, built.forcedIncludeCountWhere);
638
727
  }
639
728
  if (Object.keys(built.forcedSelectCountWhere).length > 0) {
640
- const selectContainer = validated.select;
641
- if (selectContainer) {
642
- applyForcedCountWhere(selectContainer, built.forcedSelectCountWhere);
643
- }
729
+ const sc = validated.select;
730
+ if (sc)
731
+ applyForcedCountWhere(sc, built.forcedSelectCountWhere);
644
732
  }
645
733
  return validated;
646
734
  }
735
+ function buildCountForPlacement(countWhere) {
736
+ const countSelect = {};
737
+ for (const [countRel, countForced] of Object.entries(countWhere)) {
738
+ countSelect[countRel] = { where: mergeWhereForced(void 0, countForced) };
739
+ }
740
+ return { _count: { select: countSelect } };
741
+ }
647
742
  function applyForcedTree(validated, key, tree) {
648
743
  const container = validated[key];
649
744
  if (!container)
@@ -654,8 +749,9 @@ function applyForcedTree(validated, key, tree) {
654
749
  continue;
655
750
  if (relVal === true) {
656
751
  const expanded = {};
657
- if (forced.where)
658
- expanded.where = structuredClone(forced.where);
752
+ if (forced.where && hasWhereForced(forced.where)) {
753
+ expanded.where = mergeWhereForced(void 0, forced.where);
754
+ }
659
755
  if (forced.include) {
660
756
  expanded.include = buildForcedOnlyContainer(forced.include);
661
757
  applyForcedTree(expanded, "include", forced.include);
@@ -665,13 +761,11 @@ function applyForcedTree(validated, key, tree) {
665
761
  applyForcedTree(expanded, "select", forced.select);
666
762
  }
667
763
  if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
668
- const countSelect = {};
669
- for (const [countRel, countForced] of Object.entries(
670
- forced._countWhere
671
- )) {
672
- countSelect[countRel] = { where: structuredClone(countForced) };
673
- }
674
- expanded._count = { select: countSelect };
764
+ const placement = forced._countWherePlacement ?? "include";
765
+ if (!expanded[placement])
766
+ expanded[placement] = {};
767
+ const placementObj = expanded[placement];
768
+ Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
675
769
  }
676
770
  if (expanded.include && expanded.select) {
677
771
  throw new ShapeError(
@@ -683,10 +777,10 @@ function applyForcedTree(validated, key, tree) {
683
777
  }
684
778
  if (isPlainObject(relVal)) {
685
779
  const relObj = relVal;
686
- if (forced.where) {
687
- relObj.where = mergeForced(
780
+ if (forced.where && hasWhereForced(forced.where)) {
781
+ relObj.where = mergeWhereForced(
688
782
  relObj.where,
689
- structuredClone(forced.where)
783
+ forced.where
690
784
  );
691
785
  }
692
786
  if (forced.include) {
@@ -700,7 +794,11 @@ function applyForcedTree(validated, key, tree) {
700
794
  applyForcedTree(relObj, "select", forced.select);
701
795
  }
702
796
  if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
703
- applyForcedCountWhere(relObj, forced._countWhere);
797
+ const placement = forced._countWherePlacement ?? "include";
798
+ const projContainer = relObj[placement];
799
+ if (projContainer) {
800
+ applyForcedCountWhere(projContainer, forced._countWhere);
801
+ }
704
802
  }
705
803
  if (relObj.include && relObj.select) {
706
804
  throw new ShapeError(
@@ -714,20 +812,19 @@ function buildForcedOnlyContainer(tree) {
714
812
  const result = {};
715
813
  for (const [relName, forced] of Object.entries(tree)) {
716
814
  const nested = {};
717
- if (forced.where)
718
- nested.where = structuredClone(forced.where);
815
+ if (forced.where && hasWhereForced(forced.where)) {
816
+ nested.where = mergeWhereForced(void 0, forced.where);
817
+ }
719
818
  if (forced.include)
720
819
  nested.include = buildForcedOnlyContainer(forced.include);
721
820
  if (forced.select)
722
821
  nested.select = buildForcedOnlyContainer(forced.select);
723
822
  if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
724
- const countSelect = {};
725
- for (const [countRel, countForced] of Object.entries(
726
- forced._countWhere
727
- )) {
728
- countSelect[countRel] = { where: structuredClone(countForced) };
729
- }
730
- nested._count = { select: countSelect };
823
+ const placement = forced._countWherePlacement ?? "include";
824
+ if (!nested[placement])
825
+ nested[placement] = {};
826
+ const placementObj = nested[placement];
827
+ Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
731
828
  }
732
829
  result[relName] = Object.keys(nested).length > 0 ? nested : true;
733
830
  }
@@ -747,12 +844,12 @@ function applyForcedCountWhere(container, forcedCountWhere) {
747
844
  if (relVal === void 0)
748
845
  continue;
749
846
  if (relVal === true) {
750
- selectObj[relName] = { where: structuredClone(forced) };
847
+ selectObj[relName] = { where: mergeWhereForced(void 0, forced) };
751
848
  } else if (isPlainObject(relVal)) {
752
849
  const relObj = relVal;
753
- relObj.where = mergeForced(
850
+ relObj.where = mergeWhereForced(
754
851
  relObj.where,
755
- structuredClone(forced)
852
+ forced
756
853
  );
757
854
  }
758
855
  }
@@ -789,21 +886,38 @@ function validateResolvedUniqueWhere(model, where, method, uniqueMap) {
789
886
  );
790
887
  }
791
888
  }
792
- function validateUniqueEquality(model, where, method, uniqueMap) {
889
+ function collectEqualityFields(where, model, typeMap) {
890
+ const combinators = /* @__PURE__ */ new Set(["AND", "OR", "NOT"]);
891
+ const fields = /* @__PURE__ */ new Set();
892
+ for (const [key, value] of Object.entries(where)) {
893
+ if (key === "AND") {
894
+ if (isPlainObject(value)) {
895
+ for (const f of collectEqualityFields(value, model, typeMap)) {
896
+ fields.add(f);
897
+ }
898
+ }
899
+ continue;
900
+ }
901
+ if (combinators.has(key))
902
+ continue;
903
+ if (typeMap && model && typeMap[model]?.[key]?.isRelation)
904
+ continue;
905
+ if (!value || !isPlainObject(value))
906
+ continue;
907
+ if (Object.keys(value).every((op) => op === "equals")) {
908
+ fields.add(key);
909
+ }
910
+ }
911
+ return fields;
912
+ }
913
+ function validateUniqueEquality(model, where, method, uniqueMap, typeMap) {
793
914
  const constraints = uniqueMap[model];
794
915
  if (!constraints || constraints.length === 0)
795
916
  return;
796
- const whereFields = new Set(Object.keys(where));
797
- const valid = constraints.some((constraint) => {
798
- if (!constraint.every((field) => whereFields.has(field)))
799
- return false;
800
- return constraint.every((field) => {
801
- const ops = where[field];
802
- if (!ops)
803
- return false;
804
- return Object.keys(ops).every((op) => op === "equals");
805
- });
806
- });
917
+ const equalityFields = collectEqualityFields(where, model, typeMap);
918
+ const valid = constraints.some(
919
+ (constraint) => constraint.every((field) => equalityFields.has(field))
920
+ );
807
921
  if (!valid) {
808
922
  const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
809
923
  throw new ShapeError(
@@ -811,226 +925,430 @@ function validateUniqueEquality(model, where, method, uniqueMap) {
811
925
  );
812
926
  }
813
927
  }
814
- function validateNestedKeys(keys, allowed, context) {
815
- for (const key of keys) {
816
- if (!allowed.has(key)) {
928
+
929
+ // src/runtime/query-builder-where.ts
930
+ var UNSUPPORTED_WHERE_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
931
+ var STRING_MODE_OPS = /* @__PURE__ */ new Set(["contains", "startsWith", "endsWith", "equals"]);
932
+ var MAX_WHERE_DEPTH = 10;
933
+ function safeStringify(v) {
934
+ if (typeof v === "bigint")
935
+ return `${v}n`;
936
+ try {
937
+ return JSON.stringify(v);
938
+ } catch {
939
+ return String(v);
940
+ }
941
+ }
942
+ function forcedValuesEqual(a, b) {
943
+ if (a === b)
944
+ return true;
945
+ if (a === null || b === null)
946
+ return false;
947
+ if (typeof a !== typeof b)
948
+ return false;
949
+ if (typeof a === "bigint")
950
+ return a === b;
951
+ if (a instanceof Date && b instanceof Date)
952
+ return a.getTime() === b.getTime();
953
+ if (a instanceof RegExp && b instanceof RegExp) {
954
+ return a.source === b.source && a.flags === b.flags;
955
+ }
956
+ if (typeof a !== "object")
957
+ return false;
958
+ if (Array.isArray(a)) {
959
+ if (!Array.isArray(b))
960
+ return false;
961
+ if (a.length !== b.length)
962
+ return false;
963
+ return a.every((v, i) => forcedValuesEqual(v, b[i]));
964
+ }
965
+ if (!isPlainObject(a) || !isPlainObject(b))
966
+ return false;
967
+ const aKeys = Object.keys(a);
968
+ const bKeys = Object.keys(b);
969
+ if (aKeys.length !== bKeys.length)
970
+ return false;
971
+ return aKeys.every((k) => k in b && forcedValuesEqual(a[k], b[k]));
972
+ }
973
+ function mergeScalarConditions(target, source) {
974
+ for (const [field, ops] of Object.entries(source)) {
975
+ const existing = target[field];
976
+ if (existing === void 0) {
977
+ target[field] = ops;
978
+ continue;
979
+ }
980
+ if (field === "NOT") {
981
+ const existingArr = Array.isArray(existing) ? existing : [existing];
982
+ const sourceArr = Array.isArray(ops) ? ops : [ops];
983
+ target[field] = [...existingArr, ...sourceArr];
984
+ continue;
985
+ }
986
+ if (isPlainObject(existing) && isPlainObject(ops)) {
987
+ for (const [op, val] of Object.entries(ops)) {
988
+ if (op in existing) {
989
+ const existingVal = existing[op];
990
+ if (!forcedValuesEqual(existingVal, val)) {
991
+ throw new ShapeError(
992
+ `Conflicting forced where values for "${field}.${op}": shape defines both ${safeStringify(existingVal)} and ${safeStringify(val)}`
993
+ );
994
+ }
995
+ }
996
+ }
997
+ Object.assign(existing, ops);
998
+ continue;
999
+ }
1000
+ if (!forcedValuesEqual(existing, ops)) {
817
1001
  throw new ShapeError(
818
- `Unknown key "${key}" in ${context}. Allowed: ${[...allowed].join(", ")}`
1002
+ `Conflicting forced where values for "${field}"`
819
1003
  );
820
1004
  }
821
1005
  }
822
1006
  }
823
- function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
824
- function isShapeConfig(obj) {
825
- if (!isPlainObject(obj))
826
- return false;
827
- const keys = Object.keys(obj);
828
- return keys.length === 0 || keys.every((k) => SHAPE_CONFIG_KEYS.has(k));
1007
+ function mergeRelationForcedMaps(target, source) {
1008
+ for (const [relName, ops] of Object.entries(source)) {
1009
+ if (!target[relName]) {
1010
+ target[relName] = ops;
1011
+ continue;
1012
+ }
1013
+ for (const [op, forced] of Object.entries(ops)) {
1014
+ if (!target[relName][op]) {
1015
+ target[relName][op] = forced;
1016
+ } else {
1017
+ mergeScalarConditions(target[relName][op].conditions, forced.conditions);
1018
+ mergeRelationForcedMaps(target[relName][op].relations, forced.relations);
1019
+ }
1020
+ }
829
1021
  }
830
- function buildWhereSchema(model, whereConfig) {
1022
+ }
1023
+ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1024
+ function buildWhereSchema(model, whereConfig, depth) {
1025
+ const currentDepth = depth ?? 0;
1026
+ if (currentDepth > MAX_WHERE_DEPTH) {
1027
+ throw new ShapeError(
1028
+ `Where schema for model "${model}" exceeds maximum nesting depth (${MAX_WHERE_DEPTH}). Check for circular relation references in the shape.`
1029
+ );
1030
+ }
831
1031
  const modelFields = typeMap[model];
832
1032
  if (!modelFields)
833
1033
  throw new ShapeError(`Unknown model: ${model}`);
834
1034
  const fieldSchemas = {};
835
- const forced = {};
836
- for (const [fieldName, operators] of Object.entries(whereConfig)) {
837
- const fieldMeta = modelFields[fieldName];
838
- if (!fieldMeta)
839
- throw new ShapeError(
840
- `Unknown field "${fieldName}" on model "${model}"`
1035
+ const scalarConditions = {};
1036
+ const relationForced = {};
1037
+ for (const [key, value] of Object.entries(whereConfig)) {
1038
+ if (COMBINATOR_KEYS.has(key)) {
1039
+ processCombinator(
1040
+ key,
1041
+ value,
1042
+ model,
1043
+ fieldSchemas,
1044
+ scalarConditions,
1045
+ relationForced,
1046
+ currentDepth
841
1047
  );
842
- if (fieldMeta.isRelation)
843
- throw new ShapeError(
844
- `Relation field "${fieldName}" cannot be used in where`
1048
+ continue;
1049
+ }
1050
+ const fieldMeta = modelFields[key];
1051
+ if (!fieldMeta)
1052
+ throw new ShapeError(`Unknown field "${key}" on model "${model}"`);
1053
+ if (fieldMeta.isRelation) {
1054
+ processRelationFilter(
1055
+ key,
1056
+ value,
1057
+ fieldMeta,
1058
+ model,
1059
+ fieldSchemas,
1060
+ relationForced,
1061
+ currentDepth
845
1062
  );
1063
+ continue;
1064
+ }
846
1065
  if (UNSUPPORTED_WHERE_TYPES.has(fieldMeta.type) && !fieldMeta.isList) {
847
1066
  throw new ShapeError(
848
- `${fieldMeta.type} field "${fieldName}" cannot be used in where filters`
1067
+ `${fieldMeta.type} field "${key}" cannot be used in where filters`
849
1068
  );
850
1069
  }
851
- const opSchemas = {};
852
- const fieldForced = {};
853
- let hasClientOps = false;
854
- let hasStringModeOp = false;
855
- const clientOpKeys = [];
856
- for (const [op, value] of Object.entries(operators)) {
857
- if (value === true) {
858
- opSchemas[op] = createOperatorSchema(
859
- fieldMeta,
860
- op,
861
- enumMap
862
- ).optional();
863
- hasClientOps = true;
864
- clientOpKeys.push(op);
865
- if (fieldMeta.type === "String" && !fieldMeta.isList && STRING_MODE_OPS.has(op)) {
866
- hasStringModeOp = true;
867
- }
868
- } else {
869
- const opSchema = createOperatorSchema(fieldMeta, op, enumMap);
870
- let parsed;
871
- try {
872
- parsed = opSchema.parse(value);
873
- } catch (err) {
874
- throw new ShapeError(
875
- `Invalid forced value for "${model}.${fieldName}.${op}": ${err.message}`
876
- );
877
- }
878
- fieldForced[op] = parsed;
879
- }
1070
+ processScalarField(key, value, model, fieldMeta, fieldSchemas, scalarConditions);
1071
+ }
1072
+ const schema = Object.keys(fieldSchemas).length > 0 ? z4.object(fieldSchemas).strict().optional() : null;
1073
+ return {
1074
+ schema,
1075
+ forced: {
1076
+ conditions: scalarConditions,
1077
+ relations: relationForced
880
1078
  }
881
- if (!hasClientOps && Object.keys(fieldForced).length === 0) {
882
- throw new ShapeError(
883
- `Empty operator config for where field "${fieldName}" on model "${model}". Define at least one operator.`
1079
+ };
1080
+ }
1081
+ function processCombinator(key, value, model, fieldSchemas, parentConditions, parentRelations, depth) {
1082
+ if (!isPlainObject(value)) {
1083
+ throw new ShapeError(`"${key}" in where shape must be an object defining allowed fields`);
1084
+ }
1085
+ const result = buildWhereSchema(model, value, depth + 1);
1086
+ if (!result.schema && !hasWhereForced(result.forced)) {
1087
+ throw new ShapeError(
1088
+ `Empty "${key}" combinator in where shape for model "${model}". Define at least one field.`
1089
+ );
1090
+ }
1091
+ if (result.schema) {
1092
+ let elementSchema = result.schema;
1093
+ if (!hasWhereForced(result.forced)) {
1094
+ elementSchema = result.schema.refine(
1095
+ (v) => v !== void 0 && v !== null && Object.keys(v).some((k) => v[k] !== void 0),
1096
+ { message: `"${key}" member must specify at least one condition` }
884
1097
  );
885
1098
  }
886
- if (hasStringModeOp) {
887
- opSchemas["mode"] = z4.enum(["default", "insensitive"]).optional();
888
- }
889
- if (hasClientOps) {
890
- const opObj = z4.object(opSchemas).strict();
891
- fieldSchemas[fieldName] = opObj.refine(
892
- (v) => clientOpKeys.some(
893
- (k) => v[k] !== void 0
894
- ),
895
- {
896
- message: `At least one operator required for where field "${fieldName}"`
897
- }
898
- ).optional();
899
- }
900
- if (Object.keys(fieldForced).length > 0) {
901
- forced[fieldName] = fieldForced;
1099
+ if (key === "NOT") {
1100
+ fieldSchemas[key] = z4.union([elementSchema, z4.array(elementSchema).min(1)]).optional();
1101
+ } else {
1102
+ fieldSchemas[key] = z4.array(elementSchema).min(1).optional();
902
1103
  }
903
1104
  }
904
- const schema = Object.keys(fieldSchemas).length > 0 ? z4.object(fieldSchemas).strict().optional() : null;
905
- return { schema, forced };
906
- }
907
- function buildCountFieldSchema(model, config, context) {
908
- if (config === true) {
909
- return z4.literal(true).optional();
910
- }
911
- const modelFields = typeMap[model];
912
- if (!modelFields)
913
- throw new ShapeError(`Unknown model: ${model}`);
914
- const fieldSchemas = {};
915
- for (const fieldName of Object.keys(config)) {
916
- if (fieldName !== "_all") {
917
- const fieldMeta = modelFields[fieldName];
918
- if (!fieldMeta)
919
- throw new ShapeError(
920
- `Unknown field "${fieldName}" on model "${model}" in ${context}`
921
- );
922
- if (fieldMeta.isRelation)
923
- throw new ShapeError(
924
- `Relation field "${fieldName}" cannot be used in ${context}`
925
- );
1105
+ if (hasWhereForced(result.forced)) {
1106
+ if (key === "AND" || key === "OR") {
1107
+ mergeScalarConditions(parentConditions, result.forced.conditions);
1108
+ mergeRelationForcedMaps(parentRelations, result.forced.relations);
1109
+ } else {
1110
+ const notWhere = mergeWhereForced(void 0, result.forced);
1111
+ if (Object.keys(notWhere).length > 0) {
1112
+ const existing = parentConditions[key];
1113
+ if (existing) {
1114
+ parentConditions[key] = Array.isArray(existing) ? [...existing, notWhere] : [existing, notWhere];
1115
+ } else {
1116
+ parentConditions[key] = notWhere;
1117
+ }
1118
+ }
926
1119
  }
927
- fieldSchemas[fieldName] = z4.literal(true).optional();
928
1120
  }
929
- return z4.object(fieldSchemas).strict().optional();
930
1121
  }
931
- function buildIncludeCountSchema(model, config) {
932
- const modelFields = typeMap[model];
933
- if (!modelFields)
934
- throw new ShapeError(`Unknown model: ${model}`);
935
- if (config === true) {
936
- return { schema: z4.literal(true).optional(), forcedCountWhere: {} };
1122
+ function processRelationFilter(key, value, fieldMeta, model, fieldSchemas, parentRelations, depth) {
1123
+ if (!isPlainObject(value)) {
1124
+ throw new ShapeError(`Relation filter for "${key}" must be an object with operators (some, every, none, is, isNot)`);
937
1125
  }
938
- if (!isPlainObject(config) || !("select" in config)) {
1126
+ const allowedOps = fieldMeta.isList ? TO_MANY_RELATION_OPS : TO_ONE_RELATION_OPS;
1127
+ if (Object.keys(value).length === 0) {
939
1128
  throw new ShapeError(
940
- `Invalid _count config on model "${model}". Expected true or { select: { ... } }`
1129
+ `Empty relation filter for "${key}" on model "${model}". Define at least one operator: ${[...allowedOps].join(", ")}`
941
1130
  );
942
1131
  }
943
- for (const key of Object.keys(config)) {
944
- if (key !== "select") {
1132
+ const opSchemas = {};
1133
+ const opForced = {};
1134
+ let hasClientOps = false;
1135
+ for (const [op, opValue] of Object.entries(value)) {
1136
+ if (!allowedOps.has(op)) {
1137
+ const allowed = [...allowedOps].join(", ");
945
1138
  throw new ShapeError(
946
- `Unknown key "${key}" in _count config on model "${model}". Only "select" is allowed.`
1139
+ `Operator "${op}" not supported for ${fieldMeta.isList ? "to-many" : "to-one"} relation "${key}". Allowed: ${allowed}`
947
1140
  );
948
1141
  }
949
- }
950
- const selectObj = config.select;
951
- const countSelectFields = {};
952
- const forcedCountWhere = {};
953
- for (const [fieldName, fieldConfig] of Object.entries(selectObj)) {
954
- const fieldMeta = modelFields[fieldName];
955
- if (!fieldMeta)
1142
+ if (!isPlainObject(opValue)) {
956
1143
  throw new ShapeError(
957
- `Unknown field "${fieldName}" on model "${model}" in _count.select`
1144
+ `Relation filter operator "${op}" on "${key}" must be an object defining nested where fields`
958
1145
  );
959
- if (!fieldMeta.isRelation)
1146
+ }
1147
+ const nested = buildWhereSchema(fieldMeta.type, opValue, depth + 1);
1148
+ if (!nested.schema && !hasWhereForced(nested.forced)) {
960
1149
  throw new ShapeError(
961
- `Field "${fieldName}" is not a relation on model "${model}" in _count.select`
962
- );
963
- if (fieldConfig === true) {
964
- countSelectFields[fieldName] = z4.literal(true).optional();
965
- } else if (isPlainObject(fieldConfig)) {
966
- validateNestedKeys(
967
- Object.keys(fieldConfig),
968
- KNOWN_COUNT_SELECT_ENTRY_KEYS,
969
- `_count.select.${fieldName} on model "${model}"`
1150
+ `Empty nested where for relation "${key}.${op}" on model "${model}". Define at least one field.`
970
1151
  );
971
- if (fieldConfig.where) {
972
- const relatedType = fieldMeta.type;
973
- const { schema: whereSchema, forced } = buildWhereSchema(
974
- relatedType,
975
- fieldConfig.where
1152
+ }
1153
+ if (nested.schema) {
1154
+ if (!hasWhereForced(nested.forced)) {
1155
+ opSchemas[op] = nested.schema.refine(
1156
+ (v) => v === void 0 || Object.keys(v).length > 0,
1157
+ { message: `Relation filter "${key}.${op}" requires at least one condition` }
976
1158
  );
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
1159
  } else {
986
- countSelectFields[fieldName] = z4.literal(true).optional();
1160
+ opSchemas[op] = nested.schema;
987
1161
  }
1162
+ hasClientOps = true;
1163
+ }
1164
+ if (hasWhereForced(nested.forced)) {
1165
+ opForced[op] = nested.forced;
1166
+ }
1167
+ }
1168
+ if (!hasClientOps && Object.keys(opForced).length === 0) {
1169
+ throw new ShapeError(
1170
+ `Relation filter for "${key}" on model "${model}" produced no conditions. Define at least one nested field in the operator shape.`
1171
+ );
1172
+ }
1173
+ if (hasClientOps) {
1174
+ const clientOpKeys = Object.keys(opSchemas);
1175
+ const opObjSchema = z4.object(opSchemas).strict();
1176
+ if (Object.keys(opForced).length === 0) {
1177
+ fieldSchemas[key] = opObjSchema.refine(
1178
+ (v) => clientOpKeys.some((k) => v[k] !== void 0),
1179
+ { message: `At least one relation operator required for "${key}"` }
1180
+ ).optional();
988
1181
  } else {
989
- throw new ShapeError(
990
- `Invalid config for _count.select.${fieldName} on model "${model}". Expected true or { where: { ... } }`
991
- );
1182
+ fieldSchemas[key] = opObjSchema.optional();
992
1183
  }
993
1184
  }
994
- const selectSchema = z4.object(countSelectFields).strict();
995
- return {
996
- schema: z4.object({ select: selectSchema }).strict().optional(),
997
- forcedCountWhere
998
- };
1185
+ if (Object.keys(opForced).length > 0) {
1186
+ parentRelations[key] = opForced;
1187
+ }
999
1188
  }
1000
- function buildAggregateFieldSchema(model, opName, fieldConfig) {
1189
+ function processScalarField(fieldName, operators, model, fieldMeta, fieldSchemas, scalarConditions) {
1190
+ if (!isPlainObject(operators)) {
1191
+ throw new ShapeError(
1192
+ `Where config for scalar field "${fieldName}" on model "${model}" must be an object of operators`
1193
+ );
1194
+ }
1195
+ const opSchemas = {};
1196
+ const fieldForced = {};
1197
+ let hasClientOps = false;
1198
+ let hasStringModeOp = false;
1199
+ const clientOpKeys = [];
1200
+ for (const [op, opValue] of Object.entries(operators)) {
1201
+ if (opValue === true) {
1202
+ opSchemas[op] = createOperatorSchema(fieldMeta, op, enumMap, scalarBase).optional();
1203
+ hasClientOps = true;
1204
+ clientOpKeys.push(op);
1205
+ if (fieldMeta.type === "String" && !fieldMeta.isList && STRING_MODE_OPS.has(op)) {
1206
+ hasStringModeOp = true;
1207
+ }
1208
+ } else {
1209
+ const actualOpValue = isForcedValue(opValue) ? opValue.value : opValue;
1210
+ const opSchema = createOperatorSchema(fieldMeta, op, enumMap, scalarBase);
1211
+ let parsed;
1212
+ try {
1213
+ parsed = opSchema.parse(actualOpValue);
1214
+ } catch (err) {
1215
+ throw new ShapeError(
1216
+ `Invalid forced value for "${model}.${fieldName}.${op}": ${err.message}`
1217
+ );
1218
+ }
1219
+ fieldForced[op] = parsed;
1220
+ }
1221
+ }
1222
+ if (!hasClientOps && Object.keys(fieldForced).length === 0) {
1223
+ throw new ShapeError(
1224
+ `Empty operator config for where field "${fieldName}" on model "${model}". Define at least one operator.`
1225
+ );
1226
+ }
1227
+ if (hasStringModeOp) {
1228
+ opSchemas["mode"] = z4.enum(["default", "insensitive"]).optional();
1229
+ }
1230
+ if (hasClientOps) {
1231
+ const opObj = z4.object(opSchemas).strict();
1232
+ fieldSchemas[fieldName] = opObj.refine(
1233
+ (v) => clientOpKeys.some((k) => v[k] !== void 0),
1234
+ { message: `At least one operator required for where field "${fieldName}"` }
1235
+ ).optional();
1236
+ }
1237
+ if (Object.keys(fieldForced).length > 0) {
1238
+ scalarConditions[fieldName] = fieldForced;
1239
+ }
1240
+ }
1241
+ return { buildWhereSchema };
1242
+ }
1243
+
1244
+ // src/runtime/query-builder-args.ts
1245
+ import { z as z5 } from "zod";
1246
+ var UNSUPPORTED_BY_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
1247
+ function requireConfigTrue(config, context) {
1248
+ for (const [key, value] of Object.entries(config)) {
1249
+ if (value !== true) {
1250
+ throw new ShapeError(
1251
+ `Config value for "${key}" in ${context} must be true, got ${typeof value}`
1252
+ );
1253
+ }
1254
+ }
1255
+ }
1256
+ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
1257
+ function buildOrderBySchema(model, orderByConfig) {
1001
1258
  const modelFields = typeMap[model];
1002
1259
  if (!modelFields)
1003
1260
  throw new ShapeError(`Unknown model: ${model}`);
1004
- const isNumericOnly = opName === "_avg" || opName === "_sum";
1005
- const isComparableOnly = opName === "_min" || opName === "_max";
1261
+ requireConfigTrue(orderByConfig, `orderBy on model "${model}"`);
1006
1262
  const fieldSchemas = {};
1007
- for (const fieldName of Object.keys(fieldConfig)) {
1008
- if (fieldName === "_all" && opName === "_count") {
1009
- fieldSchemas[fieldName] = z4.literal(true).optional();
1010
- continue;
1011
- }
1263
+ for (const fieldName of Object.keys(orderByConfig)) {
1012
1264
  const fieldMeta = modelFields[fieldName];
1013
1265
  if (!fieldMeta)
1014
- throw new ShapeError(
1015
- `Unknown field "${fieldName}" on model "${model}" in ${opName}`
1016
- );
1266
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
1017
1267
  if (fieldMeta.isRelation)
1018
- throw new ShapeError(
1019
- `Relation field "${fieldName}" cannot be used in ${opName}`
1020
- );
1021
- if (isNumericOnly && !NUMERIC_TYPES.has(fieldMeta.type)) {
1022
- throw new ShapeError(
1023
- `Field "${fieldName}" (${fieldMeta.type}) cannot be used in ${opName}. Only numeric types (Int, Float, Decimal, BigInt) are supported.`
1024
- );
1268
+ throw new ShapeError(`Relation field "${fieldName}" cannot be used in orderBy`);
1269
+ if (fieldMeta.type === "Json")
1270
+ throw new ShapeError(`Json field "${fieldName}" cannot be used in orderBy`);
1271
+ if (fieldMeta.isList)
1272
+ throw new ShapeError(`List field "${fieldName}" cannot be used in orderBy`);
1273
+ fieldSchemas[fieldName] = z5.enum(["asc", "desc"]).optional();
1274
+ }
1275
+ const fieldKeys = Object.keys(fieldSchemas);
1276
+ const singleSchema = z5.object(fieldSchemas).strict().refine(
1277
+ (v) => fieldKeys.some((k) => v[k] !== void 0),
1278
+ { message: "orderBy must specify at least one field" }
1279
+ );
1280
+ return z5.union([singleSchema, z5.array(singleSchema).min(1)]).optional();
1281
+ }
1282
+ function buildTakeSchema(config) {
1283
+ if (!Number.isFinite(config.max) || !Number.isInteger(config.max)) {
1284
+ throw new ShapeError(`take max must be a finite integer, got ${config.max}`);
1285
+ }
1286
+ if (config.max < 1) {
1287
+ throw new ShapeError(`take max must be at least 1, got ${config.max}`);
1288
+ }
1289
+ if (config.default !== void 0) {
1290
+ if (!Number.isFinite(config.default) || !Number.isInteger(config.default)) {
1291
+ throw new ShapeError(`take default must be a finite integer, got ${config.default}`);
1025
1292
  }
1026
- if (isComparableOnly && !COMPARABLE_TYPES.has(fieldMeta.type)) {
1293
+ if (config.default < 1) {
1294
+ throw new ShapeError(`take default must be at least 1, got ${config.default}`);
1295
+ }
1296
+ if (config.default > config.max) {
1297
+ throw new ShapeError("take default cannot exceed max");
1298
+ }
1299
+ return z5.number().int().min(1).max(config.max).default(config.default);
1300
+ }
1301
+ return z5.number().int().min(1).max(config.max).optional();
1302
+ }
1303
+ function buildCursorSchema(model, cursorConfig) {
1304
+ const modelFields = typeMap[model];
1305
+ if (!modelFields)
1306
+ throw new ShapeError(`Unknown model: ${model}`);
1307
+ requireConfigTrue(cursorConfig, `cursor on model "${model}"`);
1308
+ const cursorFields = new Set(Object.keys(cursorConfig));
1309
+ const constraints = uniqueMap[model];
1310
+ if (constraints && constraints.length > 0) {
1311
+ const covered = constraints.some(
1312
+ (constraint) => constraint.length === cursorFields.size && constraint.every((field) => cursorFields.has(field))
1313
+ );
1314
+ if (!covered) {
1315
+ const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
1027
1316
  throw new ShapeError(
1028
- `Field "${fieldName}" (${fieldMeta.type}) cannot be used in ${opName}. Only comparable types (Int, Float, Decimal, BigInt, String, DateTime) are supported.`
1317
+ `cursor on model "${model}" must exactly match a unique constraint: ${constraintDesc}`
1029
1318
  );
1030
1319
  }
1031
- fieldSchemas[fieldName] = z4.literal(true).optional();
1032
1320
  }
1033
- return z4.object(fieldSchemas).strict().optional();
1321
+ const fieldSchemas = {};
1322
+ for (const fieldName of Object.keys(cursorConfig)) {
1323
+ const fieldMeta = modelFields[fieldName];
1324
+ if (!fieldMeta)
1325
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in cursor`);
1326
+ if (fieldMeta.isRelation)
1327
+ throw new ShapeError(`Relation field "${fieldName}" cannot be used in cursor`);
1328
+ if (fieldMeta.isList)
1329
+ throw new ShapeError(`List field "${fieldName}" cannot be used in cursor`);
1330
+ fieldSchemas[fieldName] = createBaseType(fieldMeta, enumMap, scalarBase);
1331
+ }
1332
+ return z5.object(fieldSchemas).strict().optional();
1333
+ }
1334
+ function buildDistinctSchema(model, distinctConfig) {
1335
+ if (distinctConfig.length === 0) {
1336
+ throw new ShapeError("distinct must contain at least one field");
1337
+ }
1338
+ const modelFields = typeMap[model];
1339
+ if (!modelFields)
1340
+ throw new ShapeError(`Unknown model: ${model}`);
1341
+ for (const fieldName of distinctConfig) {
1342
+ const fieldMeta = modelFields[fieldName];
1343
+ if (!fieldMeta)
1344
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in distinct`);
1345
+ if (fieldMeta.isRelation)
1346
+ throw new ShapeError(`Relation field "${fieldName}" cannot be used in distinct`);
1347
+ if (fieldMeta.isList)
1348
+ throw new ShapeError(`List field "${fieldName}" cannot be used in distinct`);
1349
+ }
1350
+ const enumSchema = z5.enum(distinctConfig);
1351
+ return z5.union([enumSchema, z5.array(enumSchema).min(1)]).optional();
1034
1352
  }
1035
1353
  function buildBySchema(model, byConfig) {
1036
1354
  if (byConfig.length === 0) {
@@ -1042,84 +1360,285 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1042
1360
  for (const fieldName of byConfig) {
1043
1361
  const fieldMeta = modelFields[fieldName];
1044
1362
  if (!fieldMeta)
1045
- throw new ShapeError(
1046
- `Unknown field "${fieldName}" on model "${model}" in by`
1047
- );
1363
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in by`);
1048
1364
  if (fieldMeta.isRelation)
1049
- throw new ShapeError(
1050
- `Relation field "${fieldName}" cannot be used in by`
1051
- );
1365
+ throw new ShapeError(`Relation field "${fieldName}" cannot be used in by`);
1052
1366
  if (UNSUPPORTED_BY_TYPES.has(fieldMeta.type)) {
1053
- throw new ShapeError(
1054
- `${fieldMeta.type} field "${fieldName}" cannot be used in by`
1055
- );
1367
+ throw new ShapeError(`${fieldMeta.type} field "${fieldName}" cannot be used in by`);
1056
1368
  }
1057
- if (fieldMeta.isList) {
1369
+ if (fieldMeta.isList)
1058
1370
  throw new ShapeError(`List field "${fieldName}" cannot be used in by`);
1059
- }
1060
1371
  }
1061
- const enumSchema = z4.enum(byConfig);
1062
- return z4.union([enumSchema, z4.array(enumSchema).min(1)]);
1372
+ const enumSchema = z5.enum(byConfig);
1373
+ return z5.union([enumSchema, z5.array(enumSchema).min(1)]);
1063
1374
  }
1064
- function buildCursorSchema(model, cursorConfig) {
1375
+ function buildHavingSchema(model, havingConfig) {
1065
1376
  const modelFields = typeMap[model];
1066
1377
  if (!modelFields)
1067
1378
  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
- );
1379
+ requireConfigTrue(havingConfig, `having on model "${model}"`);
1380
+ const fieldSchemas = {};
1381
+ for (const fieldName of Object.keys(havingConfig)) {
1382
+ const fieldMeta = modelFields[fieldName];
1383
+ if (!fieldMeta)
1384
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in having`);
1385
+ if (fieldMeta.isRelation)
1386
+ throw new ShapeError(`Relation field "${fieldName}" cannot be used in having`);
1387
+ if (fieldMeta.isList)
1388
+ throw new ShapeError(`List field "${fieldName}" cannot be used in having`);
1389
+ const ops = getSupportedOperators(fieldMeta);
1390
+ if (ops.length === 0) {
1391
+ throw new ShapeError(`${fieldMeta.type} field "${fieldName}" cannot be used in having filters`);
1079
1392
  }
1393
+ const opSchemas = {};
1394
+ const opKeys = [];
1395
+ for (const op of ops) {
1396
+ opSchemas[op] = createOperatorSchema(fieldMeta, op, enumMap, scalarBase).optional();
1397
+ opKeys.push(op);
1398
+ }
1399
+ if (fieldMeta.type === "String" && !fieldMeta.isList) {
1400
+ opSchemas["mode"] = z5.enum(["default", "insensitive"]).optional();
1401
+ }
1402
+ fieldSchemas[fieldName] = z5.object(opSchemas).strict().refine(
1403
+ (v) => opKeys.some((k) => v[k] !== void 0),
1404
+ { message: `At least one operator required for having field "${fieldName}"` }
1405
+ ).optional();
1080
1406
  }
1407
+ const havingFieldKeys = Object.keys(fieldSchemas);
1408
+ return z5.object(fieldSchemas).strict().refine(
1409
+ (v) => havingFieldKeys.some((k) => v[k] !== void 0),
1410
+ { message: "having must specify at least one field" }
1411
+ ).optional();
1412
+ }
1413
+ function buildAggregateFieldSchema(model, opName, fieldConfig) {
1414
+ const modelFields = typeMap[model];
1415
+ if (!modelFields)
1416
+ throw new ShapeError(`Unknown model: ${model}`);
1417
+ requireConfigTrue(fieldConfig, `${opName} on model "${model}"`);
1418
+ const isNumericOnly = opName === "_avg" || opName === "_sum";
1419
+ const isComparableOnly = opName === "_min" || opName === "_max";
1081
1420
  const fieldSchemas = {};
1082
- for (const fieldName of Object.keys(cursorConfig)) {
1421
+ for (const fieldName of Object.keys(fieldConfig)) {
1422
+ if (fieldName === "_all" && opName === "_count") {
1423
+ fieldSchemas[fieldName] = z5.literal(true).optional();
1424
+ continue;
1425
+ }
1083
1426
  const fieldMeta = modelFields[fieldName];
1084
1427
  if (!fieldMeta)
1085
- throw new ShapeError(
1086
- `Unknown field "${fieldName}" on model "${model}" in cursor`
1087
- );
1428
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in ${opName}`);
1088
1429
  if (fieldMeta.isRelation)
1430
+ throw new ShapeError(`Relation field "${fieldName}" cannot be used in ${opName}`);
1431
+ if (fieldMeta.isList)
1432
+ throw new ShapeError(`List field "${fieldName}" cannot be used in ${opName}`);
1433
+ if (isNumericOnly && !NUMERIC_TYPES.has(fieldMeta.type)) {
1089
1434
  throw new ShapeError(
1090
- `Relation field "${fieldName}" cannot be used in cursor`
1435
+ `Field "${fieldName}" (${fieldMeta.type}) cannot be used in ${opName}. Only numeric types (Int, Float, Decimal, BigInt) are supported.`
1091
1436
  );
1092
- if (fieldMeta.isList)
1437
+ }
1438
+ if (isComparableOnly && !COMPARABLE_TYPES.has(fieldMeta.type)) {
1093
1439
  throw new ShapeError(
1094
- `List field "${fieldName}" cannot be used in cursor`
1440
+ `Field "${fieldName}" (${fieldMeta.type}) cannot be used in ${opName}. Only comparable types (Int, Float, Decimal, BigInt, String, DateTime) are supported.`
1095
1441
  );
1096
- fieldSchemas[fieldName] = createBaseType(fieldMeta, enumMap);
1442
+ }
1443
+ fieldSchemas[fieldName] = z5.literal(true).optional();
1097
1444
  }
1098
- return z4.object(fieldSchemas).strict().optional();
1445
+ const aggFieldKeys = Object.keys(fieldSchemas);
1446
+ return z5.object(fieldSchemas).strict().refine(
1447
+ (v) => aggFieldKeys.some((k) => v[k] !== void 0),
1448
+ { message: `${opName} must specify at least one field` }
1449
+ ).optional();
1099
1450
  }
1100
- function buildDistinctSchema(model, distinctConfig) {
1451
+ function buildCountFieldSchema(model, config, context) {
1452
+ if (config === true) {
1453
+ return z5.literal(true).optional();
1454
+ }
1101
1455
  const modelFields = typeMap[model];
1102
1456
  if (!modelFields)
1103
1457
  throw new ShapeError(`Unknown model: ${model}`);
1104
- for (const fieldName of distinctConfig) {
1458
+ requireConfigTrue(config, `${context} on model "${model}"`);
1459
+ const fieldSchemas = {};
1460
+ for (const fieldName of Object.keys(config)) {
1461
+ if (fieldName !== "_all") {
1462
+ const fieldMeta = modelFields[fieldName];
1463
+ if (!fieldMeta)
1464
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in ${context}`);
1465
+ if (fieldMeta.isRelation)
1466
+ throw new ShapeError(`Relation field "${fieldName}" cannot be used in ${context}`);
1467
+ }
1468
+ fieldSchemas[fieldName] = z5.literal(true).optional();
1469
+ }
1470
+ const countFieldKeys = Object.keys(fieldSchemas);
1471
+ return z5.object(fieldSchemas).strict().refine(
1472
+ (v) => countFieldKeys.some((k) => v[k] !== void 0),
1473
+ { message: `${context} must specify at least one field` }
1474
+ ).optional();
1475
+ }
1476
+ function buildCountSelectSchema(model, selectConfig) {
1477
+ const modelFields = typeMap[model];
1478
+ if (!modelFields)
1479
+ throw new ShapeError(`Unknown model: ${model}`);
1480
+ requireConfigTrue(selectConfig, `count select on model "${model}"`);
1481
+ const fieldSchemas = {};
1482
+ for (const fieldName of Object.keys(selectConfig)) {
1483
+ if (fieldName === "_all") {
1484
+ fieldSchemas["_all"] = z5.literal(true).optional();
1485
+ continue;
1486
+ }
1105
1487
  const fieldMeta = modelFields[fieldName];
1106
1488
  if (!fieldMeta)
1107
- throw new ShapeError(
1108
- `Unknown field "${fieldName}" on model "${model}" in distinct`
1109
- );
1489
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in count select`);
1110
1490
  if (fieldMeta.isRelation)
1491
+ throw new ShapeError(`Relation field "${fieldName}" cannot be used in count select`);
1492
+ fieldSchemas[fieldName] = z5.literal(true).optional();
1493
+ }
1494
+ const countSelectKeys = Object.keys(fieldSchemas);
1495
+ return z5.object(fieldSchemas).strict().refine(
1496
+ (v) => countSelectKeys.some((k) => v[k] !== void 0),
1497
+ { message: "count select must specify at least one field" }
1498
+ ).optional();
1499
+ }
1500
+ return {
1501
+ buildOrderBySchema,
1502
+ buildTakeSchema,
1503
+ buildCursorSchema,
1504
+ buildDistinctSchema,
1505
+ buildBySchema,
1506
+ buildHavingSchema,
1507
+ buildAggregateFieldSchema,
1508
+ buildCountFieldSchema,
1509
+ buildCountSelectSchema
1510
+ };
1511
+ }
1512
+
1513
+ // src/runtime/query-builder-projection.ts
1514
+ import { z as z6 } from "zod";
1515
+ var KNOWN_NESTED_INCLUDE_KEYS = /* @__PURE__ */ new Set([
1516
+ "where",
1517
+ "include",
1518
+ "select",
1519
+ "orderBy",
1520
+ "cursor",
1521
+ "take",
1522
+ "skip"
1523
+ ]);
1524
+ var KNOWN_NESTED_SELECT_KEYS = /* @__PURE__ */ new Set([
1525
+ "select",
1526
+ "where",
1527
+ "orderBy",
1528
+ "cursor",
1529
+ "take",
1530
+ "skip"
1531
+ ]);
1532
+ var KNOWN_COUNT_SELECT_ENTRY_KEYS = /* @__PURE__ */ new Set(["where"]);
1533
+ var MAX_PROJECTION_DEPTH = 10;
1534
+ function validateNestedKeys(keys, allowed, context) {
1535
+ for (const key of keys) {
1536
+ if (!allowed.has(key)) {
1537
+ throw new ShapeError(
1538
+ `Unknown key "${key}" in ${context}. Allowed: ${[...allowed].join(", ")}`
1539
+ );
1540
+ }
1541
+ }
1542
+ }
1543
+ function createProjectionBuilder(typeMap, enumMap, deps) {
1544
+ function buildIncludeCountSchema(model, config) {
1545
+ const modelFields = typeMap[model];
1546
+ if (!modelFields)
1547
+ throw new ShapeError(`Unknown model: ${model}`);
1548
+ if (config === true) {
1549
+ return { schema: z6.literal(true).optional(), forcedCountWhere: {} };
1550
+ }
1551
+ if (!isPlainObject(config) || !("select" in config)) {
1552
+ throw new ShapeError(
1553
+ `Invalid _count config on model "${model}". Expected true or { select: { ... } }`
1554
+ );
1555
+ }
1556
+ for (const key of Object.keys(config)) {
1557
+ if (key !== "select") {
1111
1558
  throw new ShapeError(
1112
- `Relation field "${fieldName}" cannot be used in distinct`
1559
+ `Unknown key "${key}" in _count config on model "${model}". Only "select" is allowed.`
1113
1560
  );
1114
- if (fieldMeta.isList)
1561
+ }
1562
+ }
1563
+ if (!isPlainObject(config.select)) {
1564
+ throw new ShapeError(
1565
+ `Invalid _count.select on model "${model}". Expected a plain object with relation field keys.`
1566
+ );
1567
+ }
1568
+ const selectObj = config.select;
1569
+ if (Object.keys(selectObj).length === 0) {
1570
+ throw new ShapeError(
1571
+ `Empty _count.select on model "${model}". Define at least one relation field.`
1572
+ );
1573
+ }
1574
+ const countSelectFields = {};
1575
+ const forcedCountWhere = {};
1576
+ for (const [fieldName, fieldConfig] of Object.entries(selectObj)) {
1577
+ const fieldMeta = modelFields[fieldName];
1578
+ if (!fieldMeta)
1579
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in _count.select`);
1580
+ if (!fieldMeta.isRelation)
1581
+ throw new ShapeError(`Field "${fieldName}" is not a relation on model "${model}" in _count.select`);
1582
+ if (!fieldMeta.isList)
1583
+ throw new ShapeError(`Field "${fieldName}" is a to-one relation on model "${model}" in _count.select. Only to-many relations support _count.`);
1584
+ if (fieldConfig === true) {
1585
+ countSelectFields[fieldName] = z6.literal(true).optional();
1586
+ } else if (isPlainObject(fieldConfig)) {
1587
+ if (Object.keys(fieldConfig).length === 0) {
1588
+ throw new ShapeError(
1589
+ `Empty config for _count.select.${fieldName} on model "${model}". Use true or { where: { ... } }.`
1590
+ );
1591
+ }
1592
+ validateNestedKeys(
1593
+ Object.keys(fieldConfig),
1594
+ KNOWN_COUNT_SELECT_ENTRY_KEYS,
1595
+ `_count.select.${fieldName} on model "${model}"`
1596
+ );
1597
+ if (fieldConfig.where) {
1598
+ const relatedType = fieldMeta.type;
1599
+ const { schema: whereSchema, forced } = deps.buildWhereSchema(
1600
+ relatedType,
1601
+ fieldConfig.where
1602
+ );
1603
+ const nestedSchemas = {};
1604
+ if (whereSchema)
1605
+ nestedSchemas["where"] = whereSchema;
1606
+ const nestedObj = z6.object(nestedSchemas).strict();
1607
+ countSelectFields[fieldName] = z6.union([z6.literal(true), nestedObj]).optional();
1608
+ if (hasWhereForced(forced)) {
1609
+ forcedCountWhere[fieldName] = forced;
1610
+ }
1611
+ } else {
1612
+ countSelectFields[fieldName] = z6.literal(true).optional();
1613
+ }
1614
+ } else {
1115
1615
  throw new ShapeError(
1116
- `List field "${fieldName}" cannot be used in distinct`
1616
+ `Invalid config for _count.select.${fieldName} on model "${model}". Expected true or { where: { ... } }`
1117
1617
  );
1618
+ }
1118
1619
  }
1119
- const enumSchema = z4.enum(distinctConfig);
1120
- return z4.union([enumSchema, z4.array(enumSchema).min(1)]).optional();
1620
+ const countSelectKeys = Object.keys(countSelectFields);
1621
+ const selectSchema = z6.object(countSelectFields).strict().refine(
1622
+ (v) => countSelectKeys.some((k) => v[k] !== void 0),
1623
+ { message: "_count.select must specify at least one field" }
1624
+ );
1625
+ return {
1626
+ schema: z6.object({ select: selectSchema }).strict().optional(),
1627
+ forcedCountWhere
1628
+ };
1121
1629
  }
1122
- function buildIncludeSchema(model, includeConfig) {
1630
+ function buildIncludeSchema(model, includeConfig, depth) {
1631
+ const currentDepth = depth ?? 0;
1632
+ if (currentDepth > MAX_PROJECTION_DEPTH) {
1633
+ throw new ShapeError(
1634
+ `Include schema for model "${model}" exceeds maximum nesting depth (${MAX_PROJECTION_DEPTH}). Check for circular relation references in the shape.`
1635
+ );
1636
+ }
1637
+ if (Object.keys(includeConfig).length === 0) {
1638
+ throw new ShapeError(
1639
+ `Empty include config on model "${model}". Define at least one relation.`
1640
+ );
1641
+ }
1123
1642
  const modelFields = typeMap[model];
1124
1643
  if (!modelFields)
1125
1644
  throw new ShapeError(`Unknown model: ${model}`);
@@ -1128,10 +1647,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1128
1647
  let topLevelForcedCountWhere = {};
1129
1648
  for (const [relName, config] of Object.entries(includeConfig)) {
1130
1649
  if (relName === "_count") {
1131
- const countResult = buildIncludeCountSchema(
1132
- model,
1133
- config
1134
- );
1650
+ const countResult = buildIncludeCountSchema(model, config);
1135
1651
  fieldSchemas["_count"] = countResult.schema;
1136
1652
  topLevelForcedCountWhere = countResult.forcedCountWhere;
1137
1653
  continue;
@@ -1140,11 +1656,9 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1140
1656
  if (!fieldMeta)
1141
1657
  throw new ShapeError(`Unknown field "${relName}" on model "${model}"`);
1142
1658
  if (!fieldMeta.isRelation)
1143
- throw new ShapeError(
1144
- `Field "${relName}" is not a relation on model "${model}"`
1145
- );
1659
+ throw new ShapeError(`Field "${relName}" is not a relation on model "${model}"`);
1146
1660
  if (config === true) {
1147
- fieldSchemas[relName] = z4.literal(true).optional();
1661
+ fieldSchemas[relName] = z6.literal(true).optional();
1148
1662
  } else {
1149
1663
  validateNestedKeys(
1150
1664
  Object.keys(config),
@@ -1152,9 +1666,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1152
1666
  `nested include for "${relName}" on model "${model}"`
1153
1667
  );
1154
1668
  if (config.select && config.include) {
1155
- throw new ShapeError(
1156
- `Nested include for "${relName}" cannot define both "select" and "include".`
1157
- );
1669
+ throw new ShapeError(`Nested include for "${relName}" cannot define both "select" and "include".`);
1158
1670
  }
1159
1671
  if (!fieldMeta.isList) {
1160
1672
  if (config.where || config.orderBy || config.cursor || config.take || config.skip) {
@@ -1166,62 +1678,77 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1166
1678
  const nestedSchemas = {};
1167
1679
  const relForced = {};
1168
1680
  if (config.where) {
1169
- const { schema: whereSchema, forced } = buildWhereSchema(
1681
+ const { schema: whereSchema, forced } = deps.buildWhereSchema(
1170
1682
  fieldMeta.type,
1171
1683
  config.where
1172
1684
  );
1173
1685
  if (whereSchema)
1174
1686
  nestedSchemas["where"] = whereSchema;
1175
- if (Object.keys(forced).length > 0)
1687
+ if (hasWhereForced(forced))
1176
1688
  relForced.where = forced;
1177
1689
  }
1178
1690
  if (config.include) {
1179
- const nested = buildIncludeSchema(fieldMeta.type, config.include);
1691
+ const nested = buildIncludeSchema(fieldMeta.type, config.include, currentDepth + 1);
1180
1692
  nestedSchemas["include"] = nested.schema;
1181
1693
  if (Object.keys(nested.forcedTree).length > 0)
1182
1694
  relForced.include = nested.forcedTree;
1183
- if (Object.keys(nested.forcedCountWhere).length > 0)
1695
+ if (Object.keys(nested.forcedCountWhere).length > 0) {
1184
1696
  relForced._countWhere = nested.forcedCountWhere;
1697
+ relForced._countWherePlacement = "include";
1698
+ }
1185
1699
  }
1186
1700
  if (config.select) {
1187
- const nested = buildSelectSchema(fieldMeta.type, config.select);
1701
+ const nested = buildSelectSchema(fieldMeta.type, config.select, currentDepth + 1);
1188
1702
  nestedSchemas["select"] = nested.schema;
1189
1703
  if (Object.keys(nested.forcedTree).length > 0)
1190
1704
  relForced.select = nested.forcedTree;
1191
- if (Object.keys(nested.forcedCountWhere).length > 0)
1705
+ if (Object.keys(nested.forcedCountWhere).length > 0) {
1192
1706
  relForced._countWhere = nested.forcedCountWhere;
1707
+ relForced._countWherePlacement = "select";
1708
+ }
1193
1709
  }
1194
1710
  if (config.orderBy) {
1195
- nestedSchemas["orderBy"] = buildOrderBySchema(
1196
- fieldMeta.type,
1197
- config.orderBy
1198
- );
1711
+ nestedSchemas["orderBy"] = deps.buildOrderBySchema(fieldMeta.type, config.orderBy);
1199
1712
  }
1200
1713
  if (config.cursor) {
1201
- nestedSchemas["cursor"] = buildCursorSchema(
1202
- fieldMeta.type,
1203
- config.cursor
1204
- );
1714
+ nestedSchemas["cursor"] = deps.buildCursorSchema(fieldMeta.type, config.cursor);
1205
1715
  }
1206
1716
  if (config.take) {
1207
- nestedSchemas["take"] = buildTakeSchema(config.take);
1717
+ nestedSchemas["take"] = deps.buildTakeSchema(config.take);
1208
1718
  }
1209
1719
  if (config.skip) {
1210
- nestedSchemas["skip"] = z4.number().int().min(0).optional();
1720
+ nestedSchemas["skip"] = z6.number().int().min(0).optional();
1211
1721
  }
1212
- const nestedObj = z4.object(nestedSchemas).strict();
1213
- fieldSchemas[relName] = z4.union([z4.literal(true), nestedObj]).optional();
1722
+ const nestedObj = z6.object(nestedSchemas).strict();
1723
+ fieldSchemas[relName] = z6.union([z6.literal(true), nestedObj]).optional();
1214
1724
  if (Object.keys(relForced).length > 0)
1215
1725
  forcedTree[relName] = relForced;
1216
1726
  }
1217
1727
  }
1728
+ const includeFieldKeys = Object.keys(fieldSchemas);
1729
+ const baseSchema = z6.object(fieldSchemas).strict();
1730
+ const schema = includeFieldKeys.length > 0 ? baseSchema.refine(
1731
+ (v) => includeFieldKeys.some((k) => v[k] !== void 0),
1732
+ { message: "include must specify at least one field" }
1733
+ ).optional() : baseSchema.optional();
1218
1734
  return {
1219
- schema: z4.object(fieldSchemas).strict().optional(),
1735
+ schema,
1220
1736
  forcedTree,
1221
1737
  forcedCountWhere: topLevelForcedCountWhere
1222
1738
  };
1223
1739
  }
1224
- function buildSelectSchema(model, selectConfig) {
1740
+ function buildSelectSchema(model, selectConfig, depth) {
1741
+ const currentDepth = depth ?? 0;
1742
+ if (currentDepth > MAX_PROJECTION_DEPTH) {
1743
+ throw new ShapeError(
1744
+ `Select schema for model "${model}" exceeds maximum nesting depth (${MAX_PROJECTION_DEPTH}). Check for circular relation references in the shape.`
1745
+ );
1746
+ }
1747
+ if (Object.keys(selectConfig).length === 0) {
1748
+ throw new ShapeError(
1749
+ `Empty select config on model "${model}". Define at least one field.`
1750
+ );
1751
+ }
1225
1752
  const modelFields = typeMap[model];
1226
1753
  if (!modelFields)
1227
1754
  throw new ShapeError(`Unknown model: ${model}`);
@@ -1230,26 +1757,19 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1230
1757
  let topLevelForcedCountWhere = {};
1231
1758
  for (const [fieldName, config] of Object.entries(selectConfig)) {
1232
1759
  if (fieldName === "_count") {
1233
- const countResult = buildIncludeCountSchema(
1234
- model,
1235
- config
1236
- );
1760
+ const countResult = buildIncludeCountSchema(model, config);
1237
1761
  fieldSchemas["_count"] = countResult.schema;
1238
1762
  topLevelForcedCountWhere = countResult.forcedCountWhere;
1239
1763
  continue;
1240
1764
  }
1241
1765
  const fieldMeta = modelFields[fieldName];
1242
1766
  if (!fieldMeta)
1243
- throw new ShapeError(
1244
- `Unknown field "${fieldName}" on model "${model}"`
1245
- );
1767
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
1246
1768
  if (config === true) {
1247
- fieldSchemas[fieldName] = z4.literal(true).optional();
1769
+ fieldSchemas[fieldName] = z6.literal(true).optional();
1248
1770
  } else {
1249
1771
  if (!fieldMeta.isRelation) {
1250
- throw new ShapeError(
1251
- `Nested select args only valid for relations, not scalar "${fieldName}"`
1252
- );
1772
+ throw new ShapeError(`Nested select args only valid for relations, not scalar "${fieldName}" on model "${model}"`);
1253
1773
  }
1254
1774
  validateNestedKeys(
1255
1775
  Object.keys(config),
@@ -1266,208 +1786,114 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1266
1786
  const nestedSchemas = {};
1267
1787
  const relForced = {};
1268
1788
  if (config.select) {
1269
- const nested = buildSelectSchema(fieldMeta.type, config.select);
1789
+ const nested = buildSelectSchema(fieldMeta.type, config.select, currentDepth + 1);
1270
1790
  nestedSchemas["select"] = nested.schema;
1271
1791
  if (Object.keys(nested.forcedTree).length > 0)
1272
1792
  relForced.select = nested.forcedTree;
1273
- if (Object.keys(nested.forcedCountWhere).length > 0)
1793
+ if (Object.keys(nested.forcedCountWhere).length > 0) {
1274
1794
  relForced._countWhere = nested.forcedCountWhere;
1795
+ relForced._countWherePlacement = "select";
1796
+ }
1275
1797
  }
1276
1798
  if (config.where) {
1277
- const { schema: whereSchema, forced } = buildWhereSchema(
1799
+ const { schema: whereSchema, forced } = deps.buildWhereSchema(
1278
1800
  fieldMeta.type,
1279
1801
  config.where
1280
1802
  );
1281
1803
  if (whereSchema)
1282
1804
  nestedSchemas["where"] = whereSchema;
1283
- if (Object.keys(forced).length > 0)
1805
+ if (hasWhereForced(forced))
1284
1806
  relForced.where = forced;
1285
1807
  }
1286
1808
  if (config.orderBy) {
1287
- nestedSchemas["orderBy"] = buildOrderBySchema(
1288
- fieldMeta.type,
1289
- config.orderBy
1290
- );
1291
- }
1292
- if (config.cursor) {
1293
- nestedSchemas["cursor"] = buildCursorSchema(
1294
- fieldMeta.type,
1295
- config.cursor
1296
- );
1297
- }
1298
- if (config.take) {
1299
- nestedSchemas["take"] = buildTakeSchema(config.take);
1300
- }
1301
- if (config.skip) {
1302
- nestedSchemas["skip"] = z4.number().int().min(0).optional();
1303
- }
1304
- const nestedObj = z4.object(nestedSchemas).strict();
1305
- fieldSchemas[fieldName] = z4.union([z4.literal(true), nestedObj]).optional();
1306
- if (Object.keys(relForced).length > 0)
1307
- forcedTree[fieldName] = relForced;
1308
- }
1309
- }
1310
- return {
1311
- schema: z4.object(fieldSchemas).strict().optional(),
1312
- forcedTree,
1313
- forcedCountWhere: topLevelForcedCountWhere
1314
- };
1315
- }
1316
- function buildOrderBySchema(model, orderByConfig) {
1317
- const modelFields = typeMap[model];
1318
- if (!modelFields)
1319
- throw new ShapeError(`Unknown model: ${model}`);
1320
- const fieldSchemas = {};
1321
- for (const fieldName of Object.keys(orderByConfig)) {
1322
- const fieldMeta = modelFields[fieldName];
1323
- if (!fieldMeta)
1324
- throw new ShapeError(
1325
- `Unknown field "${fieldName}" on model "${model}"`
1326
- );
1327
- if (fieldMeta.isRelation)
1328
- throw new ShapeError(
1329
- `Relation field "${fieldName}" cannot be used in orderBy`
1330
- );
1331
- if (fieldMeta.type === "Json")
1332
- throw new ShapeError(
1333
- `Json field "${fieldName}" cannot be used in orderBy`
1334
- );
1335
- if (fieldMeta.isList)
1336
- throw new ShapeError(
1337
- `List field "${fieldName}" cannot be used in orderBy`
1338
- );
1339
- fieldSchemas[fieldName] = z4.enum(["asc", "desc"]).optional();
1340
- }
1341
- const singleSchema = z4.object(fieldSchemas).strict();
1342
- return z4.union([singleSchema, z4.array(singleSchema)]).optional();
1343
- }
1344
- function buildTakeSchema(config) {
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;
1809
+ nestedSchemas["orderBy"] = deps.buildOrderBySchema(fieldMeta.type, config.orderBy);
1810
+ }
1811
+ if (config.cursor) {
1812
+ nestedSchemas["cursor"] = deps.buildCursorSchema(fieldMeta.type, config.cursor);
1813
+ }
1814
+ if (config.take) {
1815
+ nestedSchemas["take"] = deps.buildTakeSchema(config.take);
1816
+ }
1817
+ if (config.skip) {
1818
+ nestedSchemas["skip"] = z6.number().int().min(0).optional();
1819
+ }
1820
+ const nestedObj = z6.object(nestedSchemas).strict();
1821
+ fieldSchemas[fieldName] = z6.union([z6.literal(true), nestedObj]).optional();
1822
+ if (Object.keys(relForced).length > 0)
1823
+ forcedTree[fieldName] = relForced;
1423
1824
  }
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
1825
  }
1435
- return z4.object(fieldSchemas).strict().optional();
1826
+ const selectFieldKeys = Object.keys(fieldSchemas);
1827
+ const baseSchema = z6.object(fieldSchemas).strict();
1828
+ const schema = selectFieldKeys.length > 0 ? baseSchema.refine(
1829
+ (v) => selectFieldKeys.some((k) => v[k] !== void 0),
1830
+ { message: "select must specify at least one field" }
1831
+ ).optional() : baseSchema.optional();
1832
+ return {
1833
+ schema,
1834
+ forcedTree,
1835
+ forcedCountWhere: topLevelForcedCountWhere
1836
+ };
1837
+ }
1838
+ return { buildIncludeSchema, buildSelectSchema, buildIncludeCountSchema };
1839
+ }
1840
+
1841
+ // src/runtime/query-builder.ts
1842
+ var METHOD_ALLOWED_ARGS = {
1843
+ findMany: /* @__PURE__ */ new Set(["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"]),
1844
+ findFirst: /* @__PURE__ */ new Set(["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"]),
1845
+ findFirstOrThrow: /* @__PURE__ */ new Set(["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"]),
1846
+ findUnique: /* @__PURE__ */ new Set(["where", "include", "select"]),
1847
+ findUniqueOrThrow: /* @__PURE__ */ new Set(["where", "include", "select"]),
1848
+ count: /* @__PURE__ */ new Set(["where", "select", "cursor", "orderBy", "skip", "take"]),
1849
+ aggregate: /* @__PURE__ */ new Set(["where", "orderBy", "cursor", "take", "skip", "_count", "_avg", "_sum", "_min", "_max"]),
1850
+ groupBy: /* @__PURE__ */ new Set(["where", "by", "having", "_count", "_avg", "_sum", "_min", "_max", "orderBy", "take", "skip"])
1851
+ };
1852
+ var UNIQUE_WHERE_METHODS = /* @__PURE__ */ new Set(["findUnique", "findUniqueOrThrow"]);
1853
+ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
1854
+ const whereBuilder = createWhereBuilder(typeMap, enumMap, scalarBase);
1855
+ const argsBuilder = createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase);
1856
+ const projectionBuilder = createProjectionBuilder(typeMap, enumMap, {
1857
+ buildWhereSchema: whereBuilder.buildWhereSchema,
1858
+ buildOrderBySchema: argsBuilder.buildOrderBySchema,
1859
+ buildCursorSchema: argsBuilder.buildCursorSchema,
1860
+ buildTakeSchema: argsBuilder.buildTakeSchema
1861
+ });
1862
+ function isShapeConfig(obj) {
1863
+ if (!isPlainObject(obj))
1864
+ return false;
1865
+ const keys = Object.keys(obj);
1866
+ return keys.length === 0 || keys.every((k) => SHAPE_CONFIG_KEYS.has(k));
1436
1867
  }
1437
1868
  function validateShapeArgs(method, shape) {
1438
1869
  const allowed = METHOD_ALLOWED_ARGS[method];
1439
1870
  for (const key of Object.keys(shape)) {
1440
- if (!SHAPE_CONFIG_KEYS.has(key)) {
1871
+ if (!SHAPE_CONFIG_KEYS.has(key))
1441
1872
  throw new ShapeError(`Unknown shape config key "${key}"`);
1442
- }
1443
- if (!allowed.has(key)) {
1873
+ if (!allowed.has(key))
1444
1874
  throw new ShapeError(`Arg "${key}" not allowed for method "${method}"`);
1445
- }
1875
+ }
1876
+ if (UNIQUE_WHERE_METHODS.has(method) && !shape.where) {
1877
+ throw new ShapeError(`${method} shape must define "where"`);
1446
1878
  }
1447
1879
  if (shape.include && shape.select) {
1448
- throw new ShapeError(
1449
- 'Shape config cannot define both "include" and "select".'
1450
- );
1880
+ throw new ShapeError('Shape config cannot define both "include" and "select".');
1451
1881
  }
1452
- if (method === "groupBy" && !shape.by) {
1882
+ if (method === "groupBy" && !shape.by)
1453
1883
  throw new ShapeError('groupBy shape must define "by"');
1454
- }
1455
1884
  if (method === "groupBy" && (shape.include || shape.select)) {
1456
1885
  throw new ShapeError('groupBy does not support "include" or "select"');
1457
1886
  }
1458
1887
  if (method === "aggregate" && (shape.include || shape.select)) {
1459
1888
  throw new ShapeError('aggregate does not support "include" or "select"');
1460
1889
  }
1461
- if (method === "count" && shape.include) {
1890
+ if (method === "count" && shape.include)
1462
1891
  throw new ShapeError('count does not support "include"');
1463
- }
1464
1892
  if (method === "groupBy" && shape.orderBy) {
1465
1893
  const bySet = new Set(shape.by);
1466
1894
  for (const fieldName of Object.keys(shape.orderBy)) {
1467
1895
  if (!bySet.has(fieldName)) {
1468
- throw new ShapeError(
1469
- `orderBy field "${fieldName}" must be included in "by" for groupBy`
1470
- );
1896
+ throw new ShapeError(`orderBy field "${fieldName}" must be included in "by" for groupBy`);
1471
1897
  }
1472
1898
  }
1473
1899
  }
@@ -1475,9 +1901,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1475
1901
  const bySet = new Set(shape.by);
1476
1902
  for (const fieldName of Object.keys(shape.having)) {
1477
1903
  if (!bySet.has(fieldName)) {
1478
- throw new ShapeError(
1479
- `having field "${fieldName}" must be included in "by" for groupBy`
1480
- );
1904
+ throw new ShapeError(`having field "${fieldName}" must be included in "by" for groupBy`);
1481
1905
  }
1482
1906
  }
1483
1907
  }
@@ -1487,97 +1911,80 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1487
1911
  return;
1488
1912
  if (!shape.where)
1489
1913
  return;
1490
- validateUniqueEquality(model, shape.where, method, uniqueMap);
1914
+ validateUniqueEquality(model, shape.where, method, uniqueMap, typeMap);
1915
+ }
1916
+ function resolveAndValidateShape(shapeOrFn, ctx) {
1917
+ if (typeof shapeOrFn === "function") {
1918
+ requireContext(ctx, "shape function");
1919
+ const result = shapeOrFn(ctx);
1920
+ if (!isPlainObject(result)) {
1921
+ throw new ShapeError("Dynamic shape function must return a plain object");
1922
+ }
1923
+ return result;
1924
+ }
1925
+ return shapeOrFn;
1491
1926
  }
1492
1927
  function buildShapeZodSchema(model, method, shape) {
1493
1928
  validateShapeArgs(method, shape);
1494
1929
  validateUniqueWhere(model, method, shape);
1495
1930
  const schemaFields = {};
1496
- let forcedWhere = {};
1931
+ let forcedWhere = EMPTY_WHERE_FORCED;
1497
1932
  let forcedIncludeTree = {};
1498
1933
  let forcedSelectTree = {};
1499
1934
  let forcedIncludeCountWhere = {};
1500
1935
  let forcedSelectCountWhere = {};
1501
1936
  if (shape.where) {
1502
- const { schema, forced } = buildWhereSchema(model, shape.where);
1937
+ const { schema, forced } = whereBuilder.buildWhereSchema(model, shape.where);
1503
1938
  if (schema)
1504
1939
  schemaFields["where"] = schema;
1505
1940
  forcedWhere = forced;
1506
1941
  }
1507
1942
  if (shape.include) {
1508
- const result = buildIncludeSchema(model, shape.include);
1943
+ const result = projectionBuilder.buildIncludeSchema(model, shape.include);
1509
1944
  schemaFields["include"] = result.schema;
1510
1945
  forcedIncludeTree = result.forcedTree;
1511
1946
  forcedIncludeCountWhere = result.forcedCountWhere;
1512
1947
  }
1513
1948
  if (shape.select) {
1514
1949
  if (method === "count") {
1515
- schemaFields["select"] = buildCountSelectSchema(model, shape.select);
1950
+ schemaFields["select"] = argsBuilder.buildCountSelectSchema(model, shape.select);
1516
1951
  } else {
1517
- const result = buildSelectSchema(model, shape.select);
1952
+ const result = projectionBuilder.buildSelectSchema(model, shape.select);
1518
1953
  schemaFields["select"] = result.schema;
1519
1954
  forcedSelectTree = result.forcedTree;
1520
1955
  forcedSelectCountWhere = result.forcedCountWhere;
1521
1956
  }
1522
1957
  }
1523
- if (shape.orderBy) {
1524
- schemaFields["orderBy"] = buildOrderBySchema(model, shape.orderBy);
1525
- }
1526
- if (shape.cursor) {
1527
- schemaFields["cursor"] = buildCursorSchema(model, shape.cursor);
1528
- }
1529
- if (shape.take) {
1530
- schemaFields["take"] = buildTakeSchema(shape.take);
1531
- }
1532
- if (shape.skip) {
1533
- schemaFields["skip"] = z4.number().int().min(0).optional();
1534
- }
1535
- if (shape.distinct) {
1536
- schemaFields["distinct"] = buildDistinctSchema(model, shape.distinct);
1537
- }
1538
- if (shape._count) {
1539
- schemaFields["_count"] = buildCountFieldSchema(
1540
- model,
1541
- shape._count,
1542
- "_count"
1543
- );
1544
- }
1545
- if (shape._avg) {
1546
- schemaFields["_avg"] = buildAggregateFieldSchema(
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
- }
1958
+ if (shape.orderBy)
1959
+ schemaFields["orderBy"] = argsBuilder.buildOrderBySchema(model, shape.orderBy);
1960
+ if (shape.cursor)
1961
+ schemaFields["cursor"] = argsBuilder.buildCursorSchema(model, shape.cursor);
1962
+ if (shape.take)
1963
+ schemaFields["take"] = argsBuilder.buildTakeSchema(shape.take);
1964
+ if (shape.skip !== void 0) {
1965
+ if (shape.skip !== true) {
1966
+ throw new ShapeError('Shape config "skip" must be true');
1967
+ }
1968
+ schemaFields["skip"] = z7.number().int().min(0).optional();
1969
+ }
1970
+ if (shape.distinct)
1971
+ schemaFields["distinct"] = argsBuilder.buildDistinctSchema(model, shape.distinct);
1972
+ if (shape._count)
1973
+ schemaFields["_count"] = argsBuilder.buildCountFieldSchema(model, shape._count, "_count");
1974
+ if (shape._avg)
1975
+ schemaFields["_avg"] = argsBuilder.buildAggregateFieldSchema(model, "_avg", shape._avg);
1976
+ if (shape._sum)
1977
+ schemaFields["_sum"] = argsBuilder.buildAggregateFieldSchema(model, "_sum", shape._sum);
1978
+ if (shape._min)
1979
+ schemaFields["_min"] = argsBuilder.buildAggregateFieldSchema(model, "_min", shape._min);
1980
+ if (shape._max)
1981
+ schemaFields["_max"] = argsBuilder.buildAggregateFieldSchema(model, "_max", shape._max);
1982
+ if (shape.by)
1983
+ schemaFields["by"] = argsBuilder.buildBySchema(model, shape.by);
1984
+ if (shape.having)
1985
+ schemaFields["having"] = argsBuilder.buildHavingSchema(model, shape.having);
1579
1986
  return {
1580
- zodSchema: z4.object(schemaFields).strict(),
1987
+ zodSchema: z7.object(schemaFields).strict(),
1581
1988
  forcedWhere,
1582
1989
  forcedIncludeTree,
1583
1990
  forcedSelectTree,
@@ -1592,30 +1999,20 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1592
1999
  return { key: matched, shape: shapes[matched] };
1593
2000
  }
1594
2001
  function buildQuerySchema(model, method, config) {
1595
- const isSingleShape2 = typeof config === "function" || isShapeConfig(config);
2002
+ const isSingle = typeof config === "function" || isShapeConfig(config);
1596
2003
  const builtCache = /* @__PURE__ */ new Map();
1597
- if (isSingleShape2 && typeof config !== "function") {
1598
- const built = buildShapeZodSchema(model, method, config);
1599
- builtCache.set("_default", built);
2004
+ if (isSingle && typeof config !== "function") {
2005
+ builtCache.set("_default", buildShapeZodSchema(model, method, config));
1600
2006
  }
1601
- if (!isSingleShape2) {
2007
+ if (!isSingle) {
1602
2008
  for (const key of Object.keys(config)) {
1603
2009
  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
- );
2010
+ throw new ShapeError(`Caller key "${key}" collides with reserved shape config key. Rename the caller path.`);
1607
2011
  }
1608
2012
  }
1609
- for (const [key, shapeOrFn] of Object.entries(
1610
- config
1611
- )) {
2013
+ for (const [key, shapeOrFn] of Object.entries(config)) {
1612
2014
  if (typeof shapeOrFn !== "function") {
1613
- const built = buildShapeZodSchema(
1614
- model,
1615
- method,
1616
- shapeOrFn
1617
- );
1618
- builtCache.set(key, built);
2015
+ builtCache.set(key, buildShapeZodSchema(model, method, shapeOrFn));
1619
2016
  }
1620
2017
  }
1621
2018
  }
@@ -1625,57 +2022,54 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
1625
2022
  [...builtCache.entries()].map(([k, v]) => [k, v.zodSchema])
1626
2023
  ),
1627
2024
  parse(body, opts) {
2025
+ const normalizedBody = body === void 0 || body === null ? {} : body;
1628
2026
  let built;
1629
- if (isSingleShape2) {
2027
+ if (isSingle) {
1630
2028
  if (typeof config === "function") {
1631
- requireContext(opts?.ctx, "shape function");
1632
- const resolvedShape = config(opts.ctx);
1633
- built = buildShapeZodSchema(model, method, resolvedShape);
2029
+ const resolved = resolveAndValidateShape(config, opts?.ctx);
2030
+ built = buildShapeZodSchema(model, method, resolved);
1634
2031
  } else {
1635
2032
  built = builtCache.get("_default");
1636
2033
  }
1637
2034
  } else {
1638
- if (!isPlainObject(body)) {
2035
+ if (!isPlainObject(normalizedBody))
1639
2036
  throw new ShapeError("Request body must be an object");
2037
+ if ("caller" in normalizedBody) {
2038
+ throw new CallerError(
2039
+ "Pass caller via opts.caller, not in the request body."
2040
+ );
1640
2041
  }
1641
- const caller = body.caller;
2042
+ const caller = opts?.caller;
1642
2043
  if (typeof caller !== "string") {
1643
- throw new CallerError('Missing "caller" field in request body');
2044
+ const allowed = Object.keys(config);
2045
+ throw new CallerError(
2046
+ `Missing caller. This query uses named shape routing with keys: ${allowed.map((k) => `"${k}"`).join(", ")}. Provide caller via opts.caller.`
2047
+ );
1644
2048
  }
1645
- const matched = matchCaller(
1646
- config,
1647
- caller
1648
- );
2049
+ const matched = matchCaller(config, caller);
1649
2050
  if (!matched) {
1650
- const allowed = Object.keys(
1651
- config
1652
- );
2051
+ const allowed = Object.keys(config);
1653
2052
  throw new CallerError(
1654
2053
  `Unknown caller: "${caller}". Allowed: ${allowed.map((k) => `"${k}"`).join(", ")}`
1655
2054
  );
1656
2055
  }
1657
- const shapeKey = matched.key;
1658
- const shapeOrFn = matched.shape;
1659
- if (typeof shapeOrFn === "function") {
1660
- requireContext(opts?.ctx, "shape function");
1661
- const resolvedShape = shapeOrFn(opts.ctx);
1662
- built = buildShapeZodSchema(model, method, resolvedShape);
2056
+ if (typeof matched.shape === "function") {
2057
+ const resolved = resolveAndValidateShape(matched.shape, opts?.ctx);
2058
+ built = buildShapeZodSchema(model, method, resolved);
1663
2059
  } else {
1664
- built = builtCache.get(shapeKey);
2060
+ built = builtCache.get(matched.key);
1665
2061
  }
1666
- const { caller: _, ...rest } = body;
1667
- body = rest;
1668
2062
  }
1669
- return applyBuiltShape(built, body, isUnique);
2063
+ return applyBuiltShape(built, normalizedBody, isUnique);
1670
2064
  }
1671
2065
  };
1672
2066
  }
1673
2067
  return {
1674
2068
  buildQuerySchema,
1675
2069
  buildShapeZodSchema,
1676
- buildWhereSchema,
1677
- buildIncludeSchema,
1678
- buildSelectSchema
2070
+ buildWhereSchema: whereBuilder.buildWhereSchema,
2071
+ buildIncludeSchema: projectionBuilder.buildIncludeSchema,
2072
+ buildSelectSchema: projectionBuilder.buildSelectSchema
1679
2073
  };
1680
2074
  }
1681
2075
 
@@ -1853,10 +2247,24 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
1853
2247
  const overrides = Object.fromEntries(
1854
2248
  presentScopes.map((s) => [s.fk, ctx[s.root]])
1855
2249
  );
2250
+ const nextArgs = { ...args };
1856
2251
  if (operation === "upsert") {
1857
- throw new PolicyError(
1858
- `Scoped model "${model}" cannot use upsert via extension. Handle upsert explicitly in route logic.`
1859
- );
2252
+ nextArgs.where = buildScopedUniqueWhere(args.where, conditions);
2253
+ if (args.create !== void 0 && args.create !== null) {
2254
+ if (typeof args.create !== "object" || Array.isArray(args.create)) {
2255
+ throw new ShapeError(`upsert expects create to be an object`);
2256
+ }
2257
+ nextArgs.create = { ...args.create };
2258
+ enforceDataScope(nextArgs.create, scopes, overrides, log, model, operation, onScopeRelationWrite, "create");
2259
+ }
2260
+ if (args.update !== void 0 && args.update !== null) {
2261
+ if (typeof args.update !== "object" || Array.isArray(args.update)) {
2262
+ throw new ShapeError(`upsert expects update to be an object`);
2263
+ }
2264
+ nextArgs.update = { ...args.update };
2265
+ enforceDataScope(nextArgs.update, scopes, overrides, log, model, operation, onScopeRelationWrite, "mutate");
2266
+ }
2267
+ return query(nextArgs);
1860
2268
  }
1861
2269
  if (FIND_UNIQUE_OPS.has(operation)) {
1862
2270
  if (findUniqueMode === "reject") {
@@ -1866,7 +2274,6 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
1866
2274
  }
1867
2275
  return handleFindUnique(args, query, conditions, scopes, operation, log);
1868
2276
  }
1869
- const nextArgs = { ...args };
1870
2277
  if (READ_OPS.has(operation)) {
1871
2278
  nextArgs.where = buildAndConditions(args.where, conditions);
1872
2279
  return query(nextArgs);
@@ -1948,77 +2355,332 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log)
1948
2355
  }
1949
2356
  }
1950
2357
  }
1951
- const result = await query(nextArgs);
1952
- if (result === null)
1953
- return result;
1954
- if (typeof result !== "object" || result === null) {
1955
- throw new ShapeError("findUnique result must be an object or null");
2358
+ const result = await query(nextArgs);
2359
+ if (result === null)
2360
+ return result;
2361
+ if (typeof result !== "object" || result === null) {
2362
+ throw new ShapeError("findUnique result must be an object or null");
2363
+ }
2364
+ const resultObj = result;
2365
+ let verifyObj = resultObj;
2366
+ const missingFks = pickMissingFksFromResult(resultObj, fks);
2367
+ if (missingFks.length > 0) {
2368
+ const where = args?.where;
2369
+ if (!where || typeof where !== "object" || Array.isArray(where)) {
2370
+ throw new PolicyError(
2371
+ `prisma-guard: Cannot verify scope \u2014 missing FK fields (${missingFks.join(", ")}) and findUnique args.where is not a valid object.`
2372
+ );
2373
+ }
2374
+ let verifyResult;
2375
+ try {
2376
+ verifyResult = await query({ where, select: buildFkSelect(fks) });
2377
+ } catch (err) {
2378
+ throw new PolicyError(
2379
+ `prisma-guard: Scope verification query failed for findUnique: ${err?.message ?? String(err)}`
2380
+ );
2381
+ }
2382
+ if (verifyResult === null) {
2383
+ throw new PolicyError("prisma-guard: Scope verification query returned null for an existing findUnique result");
2384
+ }
2385
+ if (typeof verifyResult !== "object" || verifyResult === null) {
2386
+ throw new PolicyError("prisma-guard: Scope verification result must be an object");
2387
+ }
2388
+ verifyObj = verifyResult;
2389
+ }
2390
+ for (const condition of conditions) {
2391
+ const [fk, value] = Object.entries(condition)[0];
2392
+ if (!(fk in verifyObj)) {
2393
+ throw new PolicyError(
2394
+ `prisma-guard: Cannot verify scope on "${fk}" \u2014 field not present in verification result. Ensure FK fields are selectable.`
2395
+ );
2396
+ }
2397
+ if (!looseEqual(verifyObj[fk], value, log, fk)) {
2398
+ if (operation === "findUniqueOrThrow") {
2399
+ throw new PolicyError("Record not accessible in current scope");
2400
+ }
2401
+ return null;
2402
+ }
2403
+ }
2404
+ if (injectedFks.length > 0) {
2405
+ const cleaned = { ...resultObj };
2406
+ for (const fk of injectedFks) {
2407
+ delete cleaned[fk];
2408
+ }
2409
+ return cleaned;
2410
+ }
2411
+ return result;
2412
+ }
2413
+
2414
+ // src/runtime/model-guard.ts
2415
+ import { z as z9 } from "zod";
2416
+
2417
+ // src/runtime/model-guard-data.ts
2418
+ import { z as z8 } from "zod";
2419
+ var ALLOWED_BODY_KEYS_CREATE = /* @__PURE__ */ new Set(["data"]);
2420
+ var ALLOWED_BODY_KEYS_CREATE_PROJECTION = /* @__PURE__ */ new Set(["data", "select", "include"]);
2421
+ var ALLOWED_BODY_KEYS_CREATE_MANY = /* @__PURE__ */ new Set(["data", "skipDuplicates"]);
2422
+ var ALLOWED_BODY_KEYS_CREATE_MANY_PROJECTION = /* @__PURE__ */ new Set(["data", "select", "include", "skipDuplicates"]);
2423
+ var ALLOWED_BODY_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
2424
+ var ALLOWED_BODY_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set(["data", "where", "select", "include"]);
2425
+ var ALLOWED_BODY_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
2426
+ var ALLOWED_BODY_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set(["where", "select", "include"]);
2427
+ var ALLOWED_BODY_KEYS_UPSERT = /* @__PURE__ */ new Set(["where", "create", "update", "select", "include"]);
2428
+ var VALID_SHAPE_KEYS_CREATE = /* @__PURE__ */ new Set(["data"]);
2429
+ var VALID_SHAPE_KEYS_CREATE_PROJECTION = /* @__PURE__ */ new Set(["data", "select", "include"]);
2430
+ var VALID_SHAPE_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
2431
+ var VALID_SHAPE_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set(["data", "where", "select", "include"]);
2432
+ var VALID_SHAPE_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
2433
+ var VALID_SHAPE_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set(["where", "select", "include"]);
2434
+ var VALID_SHAPE_KEYS_UPSERT = /* @__PURE__ */ new Set(["where", "create", "update", "select", "include"]);
2435
+ function validateMutationBodyKeys(body, allowed, method) {
2436
+ for (const key of Object.keys(body)) {
2437
+ if (!allowed.has(key)) {
2438
+ throw new ShapeError(
2439
+ `Unexpected key "${key}" in ${method} body. Allowed keys: ${[...allowed].join(", ")}`
2440
+ );
2441
+ }
2442
+ }
2443
+ }
2444
+ function validateMutationShapeKeys(shape, allowed, method) {
2445
+ for (const key of Object.keys(shape)) {
2446
+ if (!allowed.has(key)) {
2447
+ throw new ShapeError(
2448
+ `Shape key "${key}" not valid for ${method}. Allowed: ${[...allowed].join(", ")}`
2449
+ );
2450
+ }
2451
+ }
2452
+ }
2453
+ function validateCreateCompleteness(modelName, dataConfig, typeMap, scopeFks, zodDefaults) {
2454
+ const modelFields = typeMap[modelName];
2455
+ if (!modelFields)
2456
+ return;
2457
+ const zodDefaultFields = zodDefaults[modelName];
2458
+ const zodDefaultSet = zodDefaultFields ? new Set(zodDefaultFields) : void 0;
2459
+ for (const [fieldName, meta] of Object.entries(modelFields)) {
2460
+ if (meta.isRelation)
2461
+ continue;
2462
+ if (meta.isUpdatedAt)
2463
+ continue;
2464
+ if (meta.hasDefault)
2465
+ continue;
2466
+ if (!meta.isRequired)
2467
+ continue;
2468
+ if (fieldName in dataConfig)
2469
+ continue;
2470
+ if (scopeFks.has(fieldName))
2471
+ continue;
2472
+ if (zodDefaultSet && zodDefaultSet.has(fieldName))
2473
+ continue;
2474
+ throw new ShapeError(
2475
+ `Required field "${fieldName}" on model "${modelName}" is missing from create data shape, has no default, and is not a scope FK`
2476
+ );
2477
+ }
2478
+ }
2479
+ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDefaults) {
2480
+ const modelFields = typeMap[model];
2481
+ if (!modelFields)
2482
+ throw new ShapeError(`Unknown model: ${model}`);
2483
+ const zodDefaultFields = zodDefaults[model];
2484
+ const zodDefaultSet = zodDefaultFields ? new Set(zodDefaultFields) : void 0;
2485
+ const schemaMap = {};
2486
+ const forced = {};
2487
+ for (const [fieldName, value] of Object.entries(dataConfig)) {
2488
+ const fieldMeta = modelFields[fieldName];
2489
+ if (!fieldMeta)
2490
+ throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
2491
+ if (fieldMeta.isRelation)
2492
+ throw new ShapeError(`Relation field "${fieldName}" cannot be used in data shape`);
2493
+ if (fieldMeta.isUpdatedAt)
2494
+ throw new ShapeError(`updatedAt field "${fieldName}" cannot be used in data shape`);
2495
+ if (typeof value === "function") {
2496
+ let baseSchema = schemaBuilder.buildBaseFieldSchema(model, fieldName);
2497
+ let refined;
2498
+ try {
2499
+ refined = value(baseSchema);
2500
+ } catch (err) {
2501
+ throw new ShapeError(
2502
+ `Invalid inline refine for "${model}.${fieldName}": ${err.message}`,
2503
+ { cause: err }
2504
+ );
2505
+ }
2506
+ if (!isZodSchema(refined)) {
2507
+ throw new ShapeError(`Inline refine for "${model}.${fieldName}" must return a Zod schema`);
2508
+ }
2509
+ let fieldSchema = refined;
2510
+ const handlesUndefined = schemaProducesValueForUndefined(fieldSchema);
2511
+ if (mode === "create") {
2512
+ if (!fieldMeta.isRequired) {
2513
+ fieldSchema = handlesUndefined ? fieldSchema.nullable() : fieldSchema.nullable().optional();
2514
+ } else if (fieldMeta.hasDefault) {
2515
+ if (!handlesUndefined) {
2516
+ fieldSchema = fieldSchema.optional();
2517
+ }
2518
+ }
2519
+ } else {
2520
+ if (!fieldMeta.isRequired) {
2521
+ fieldSchema = fieldSchema.nullable().optional();
2522
+ } else {
2523
+ fieldSchema = fieldSchema.optional();
2524
+ }
2525
+ }
2526
+ schemaMap[fieldName] = fieldSchema;
2527
+ } else if (value === true) {
2528
+ let fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
2529
+ const isZodDefaultField = zodDefaultSet !== void 0 && zodDefaultSet.has(fieldName);
2530
+ if (mode === "create") {
2531
+ if (!fieldMeta.isRequired) {
2532
+ fieldSchema = isZodDefaultField ? fieldSchema.nullable() : fieldSchema.nullable().optional();
2533
+ } else if (fieldMeta.hasDefault) {
2534
+ if (!isZodDefaultField) {
2535
+ fieldSchema = fieldSchema.optional();
2536
+ }
2537
+ }
2538
+ } else {
2539
+ if (!fieldMeta.isRequired) {
2540
+ fieldSchema = fieldSchema.nullable().optional();
2541
+ } else {
2542
+ fieldSchema = fieldSchema.optional();
2543
+ }
2544
+ }
2545
+ schemaMap[fieldName] = fieldSchema;
2546
+ } else {
2547
+ const actualValue = isForcedValue(value) ? value.value : value;
2548
+ let fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
2549
+ if (!fieldMeta.isRequired) {
2550
+ fieldSchema = fieldSchema.nullable();
2551
+ }
2552
+ let parsed;
2553
+ try {
2554
+ parsed = fieldSchema.parse(actualValue);
2555
+ } catch (err) {
2556
+ throw new ShapeError(
2557
+ `Invalid forced data value for "${model}.${fieldName}": ${err.message}`
2558
+ );
2559
+ }
2560
+ forced[fieldName] = parsed;
2561
+ }
2562
+ }
2563
+ if (mode === "create" && zodDefaultFields) {
2564
+ for (const fieldName of zodDefaultFields) {
2565
+ if (fieldName in dataConfig)
2566
+ continue;
2567
+ const fieldMeta = modelFields[fieldName];
2568
+ if (!fieldMeta)
2569
+ continue;
2570
+ if (fieldMeta.isRelation)
2571
+ continue;
2572
+ if (fieldMeta.isUpdatedAt)
2573
+ continue;
2574
+ const fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
2575
+ const result = fieldSchema.safeParse(void 0);
2576
+ if (result.success && result.data !== void 0) {
2577
+ forced[fieldName] = result.data;
2578
+ } else {
2579
+ throw new ShapeError(
2580
+ `Field "${fieldName}" on model "${model}" has @zod default/catch but its schema does not produce a value for undefined input`
2581
+ );
2582
+ }
2583
+ }
2584
+ }
2585
+ return {
2586
+ schema: z8.object(schemaMap).strict(),
2587
+ forced
2588
+ };
2589
+ }
2590
+ function validateAndMergeData(bodyData, cached, method) {
2591
+ if (bodyData === void 0 || bodyData === null) {
2592
+ throw new ShapeError(`${method} requires "data" in request body`);
2593
+ }
2594
+ const validated = cached.schema.parse(bodyData);
2595
+ return { ...validated, ...deepClone(cached.forced) };
2596
+ }
2597
+ function hasDataRefines(dataConfig) {
2598
+ for (const value of Object.values(dataConfig)) {
2599
+ if (typeof value === "function")
2600
+ return true;
2601
+ }
2602
+ return false;
2603
+ }
2604
+
2605
+ // src/runtime/model-guard-resolve.ts
2606
+ function isGuardShape(obj) {
2607
+ if (!isPlainObject(obj))
2608
+ return false;
2609
+ const keys = Object.keys(obj);
2610
+ return keys.length === 0 || keys.every((k) => GUARD_SHAPE_KEYS.has(k));
2611
+ }
2612
+ function isSingleShape(input) {
2613
+ return typeof input === "function" || isGuardShape(input);
2614
+ }
2615
+ function requireBody(body) {
2616
+ if (!isPlainObject(body))
2617
+ throw new ShapeError("Request body must be an object");
2618
+ return body;
2619
+ }
2620
+ function resolveDynamicShape(fn, contextFn) {
2621
+ const ctx = validateContext(contextFn());
2622
+ let result;
2623
+ try {
2624
+ result = fn(ctx);
2625
+ } catch (err) {
2626
+ throw new ShapeError(
2627
+ `Dynamic shape function threw: ${err.message}`,
2628
+ { cause: err }
2629
+ );
2630
+ }
2631
+ if (!isPlainObject(result)) {
2632
+ throw new ShapeError("Dynamic shape function must return a plain object");
1956
2633
  }
1957
- const resultObj = result;
1958
- let verifyObj = resultObj;
1959
- const missingFks = pickMissingFksFromResult(resultObj, fks);
1960
- if (missingFks.length > 0) {
1961
- const where = args?.where;
1962
- if (!where || typeof where !== "object" || Array.isArray(where)) {
1963
- throw new PolicyError(
1964
- `prisma-guard: Cannot verify scope \u2014 missing FK fields (${missingFks.join(", ")}) and findUnique args.where is not a valid object.`
2634
+ return result;
2635
+ }
2636
+ function resolveShape(input, body, contextFn, caller) {
2637
+ if (isSingleShape(input)) {
2638
+ const wasDynamic2 = typeof input === "function";
2639
+ const shape2 = wasDynamic2 ? resolveDynamicShape(input, contextFn) : input;
2640
+ const parsed2 = body === void 0 || body === null ? {} : requireBody(body);
2641
+ return { shape: shape2, body: parsed2, matchedKey: "_default", wasDynamic: wasDynamic2 };
2642
+ }
2643
+ const namedMap = input;
2644
+ for (const key of Object.keys(namedMap)) {
2645
+ if (GUARD_SHAPE_KEYS.has(key)) {
2646
+ throw new ShapeError(
2647
+ `Caller key "${key}" collides with reserved shape config key. Rename the caller path.`
1965
2648
  );
1966
2649
  }
1967
- let verifyResult;
1968
- try {
1969
- verifyResult = await query({ where, select: buildFkSelect(fks) });
1970
- } catch (err) {
1971
- throw new PolicyError(
1972
- `prisma-guard: Scope verification query failed for findUnique: ${err?.message ?? String(err)}`
2650
+ const val = namedMap[key];
2651
+ if (typeof val !== "function" && !isGuardShape(val)) {
2652
+ throw new ShapeError(
2653
+ `Named shape value for "${key}" must be a guard shape object or function`
1973
2654
  );
1974
2655
  }
1975
- if (verifyResult === null) {
1976
- throw new PolicyError("prisma-guard: Scope verification query returned null for an existing findUnique result");
1977
- }
1978
- if (typeof verifyResult !== "object" || verifyResult === null) {
1979
- throw new PolicyError("prisma-guard: Scope verification result must be an object");
1980
- }
1981
- verifyObj = verifyResult;
1982
2656
  }
1983
- for (const condition of conditions) {
1984
- const [fk, value] = Object.entries(condition)[0];
1985
- if (!(fk in verifyObj)) {
1986
- throw new PolicyError(
1987
- `prisma-guard: Cannot verify scope on "${fk}" \u2014 field not present in verification result. Ensure FK fields are selectable.`
1988
- );
1989
- }
1990
- if (!looseEqual(verifyObj[fk], value, log, fk)) {
1991
- if (operation === "findUniqueOrThrow") {
1992
- throw new PolicyError("Record not accessible in current scope");
1993
- }
1994
- return null;
1995
- }
2657
+ const parsed = body === void 0 || body === null ? {} : requireBody(body);
2658
+ if ("caller" in parsed) {
2659
+ throw new CallerError(
2660
+ "Pass caller as second argument to .guard() or via context function, not in the request body."
2661
+ );
1996
2662
  }
1997
- if (injectedFks.length > 0) {
1998
- const cleaned = { ...resultObj };
1999
- for (const fk of injectedFks) {
2000
- delete cleaned[fk];
2001
- }
2002
- return cleaned;
2663
+ if (typeof caller !== "string") {
2664
+ const patterns2 = Object.keys(namedMap);
2665
+ throw new CallerError(
2666
+ `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.`
2667
+ );
2003
2668
  }
2004
- return result;
2669
+ const patterns = Object.keys(namedMap);
2670
+ const matched = matchCallerPattern(patterns, caller);
2671
+ if (!matched) {
2672
+ throw new CallerError(
2673
+ `Unknown caller: "${caller}". Allowed: ${patterns.map((k) => `"${k}"`).join(", ")}`
2674
+ );
2675
+ }
2676
+ const shapeOrFn = namedMap[matched];
2677
+ const wasDynamic = typeof shapeOrFn === "function";
2678
+ const shape = wasDynamic ? resolveDynamicShape(shapeOrFn, contextFn) : shapeOrFn;
2679
+ return { shape, body: parsed, matchedKey: matched, wasDynamic };
2005
2680
  }
2006
2681
 
2007
2682
  // src/runtime/model-guard.ts
2008
- import { z as z5 } from "zod";
2009
- var ALLOWED_BODY_KEYS_CREATE = /* @__PURE__ */ new Set(["data"]);
2010
- var ALLOWED_BODY_KEYS_CREATE_PROJECTION = /* @__PURE__ */ new Set(["data", "select", "include"]);
2011
- var ALLOWED_BODY_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
2012
- var ALLOWED_BODY_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set(["data", "where", "select", "include"]);
2013
- var ALLOWED_BODY_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
2014
- var ALLOWED_BODY_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set(["where", "select", "include"]);
2015
- var VALID_SHAPE_KEYS_CREATE = /* @__PURE__ */ new Set(["data"]);
2016
- var VALID_SHAPE_KEYS_CREATE_PROJECTION = /* @__PURE__ */ new Set(["data", "select", "include"]);
2017
- var VALID_SHAPE_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
2018
- var VALID_SHAPE_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set(["data", "where", "select", "include"]);
2019
- var VALID_SHAPE_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
2020
- var VALID_SHAPE_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set(["where", "select", "include"]);
2021
- var UNIQUE_MUTATION_METHODS = /* @__PURE__ */ new Set(["update", "delete"]);
2683
+ var UNIQUE_MUTATION_METHODS = /* @__PURE__ */ new Set(["update", "delete", "upsert"]);
2022
2684
  var UNIQUE_READ_METHODS = /* @__PURE__ */ new Set(["findUnique", "findUniqueOrThrow"]);
2023
2685
  var BULK_MUTATION_METHODS = /* @__PURE__ */ new Set([
2024
2686
  "updateMany",
@@ -2028,228 +2690,187 @@ var BULK_MUTATION_METHODS = /* @__PURE__ */ new Set([
2028
2690
  var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
2029
2691
  "create",
2030
2692
  "update",
2693
+ "upsert",
2031
2694
  "delete",
2032
2695
  "createManyAndReturn",
2033
2696
  "updateManyAndReturn"
2034
2697
  ]);
2035
- function isGuardShape(obj) {
2036
- if (!isPlainObject(obj))
2037
- return false;
2038
- const keys = Object.keys(obj);
2039
- return keys.length === 0 || keys.every((k) => GUARD_SHAPE_KEYS.has(k));
2040
- }
2041
- function isSingleShape(input) {
2042
- return typeof input === "function" || isGuardShape(input);
2043
- }
2044
- function validateMutationBodyKeys(body, allowed, method) {
2045
- for (const key of Object.keys(body)) {
2046
- if (!allowed.has(key)) {
2047
- throw new ShapeError(
2048
- `Unexpected key "${key}" in ${method} body. Allowed keys: ${[...allowed].join(", ")}`
2049
- );
2698
+ var BATCH_CREATE_METHODS = /* @__PURE__ */ new Set([
2699
+ "createMany",
2700
+ "createManyAndReturn"
2701
+ ]);
2702
+ function buildDefaultSelectInput(config) {
2703
+ const result = {};
2704
+ for (const [key, value] of Object.entries(config)) {
2705
+ if (key === "_count") {
2706
+ result[key] = buildDefaultCountInput(value);
2707
+ continue;
2708
+ }
2709
+ if (value === true) {
2710
+ result[key] = true;
2711
+ } else {
2712
+ const nested = {};
2713
+ if (value.select)
2714
+ nested.select = buildDefaultSelectInput(value.select);
2715
+ result[key] = Object.keys(nested).length > 0 ? nested : true;
2050
2716
  }
2051
2717
  }
2718
+ return result;
2052
2719
  }
2053
- function validateMutationShapeKeys(shape, allowed, method) {
2054
- for (const key of Object.keys(shape)) {
2055
- if (!allowed.has(key)) {
2056
- throw new ShapeError(
2057
- `Shape key "${key}" not valid for ${method}. Allowed: ${[...allowed].join(", ")}`
2058
- );
2720
+ function buildDefaultIncludeInput(config) {
2721
+ const result = {};
2722
+ for (const [key, value] of Object.entries(config)) {
2723
+ if (key === "_count") {
2724
+ result[key] = buildDefaultCountInput(value);
2725
+ continue;
2726
+ }
2727
+ if (value === true) {
2728
+ result[key] = true;
2729
+ } else {
2730
+ const nested = {};
2731
+ if (value.include)
2732
+ nested.include = buildDefaultIncludeInput(value.include);
2733
+ if (value.select)
2734
+ nested.select = buildDefaultSelectInput(value.select);
2735
+ result[key] = Object.keys(nested).length > 0 ? nested : true;
2059
2736
  }
2060
2737
  }
2738
+ return result;
2061
2739
  }
2062
- function formatZodError(err) {
2063
- return err.issues.map((i) => {
2064
- const p = i.path.length > 0 ? `${i.path.join(".")}: ` : "";
2065
- return `${p}${i.message}`;
2066
- }).join("; ");
2740
+ function buildDefaultCountInput(config) {
2741
+ if (config === true)
2742
+ return true;
2743
+ if (!isPlainObject(config) || !config.select || !isPlainObject(config.select))
2744
+ return true;
2745
+ const selectObj = config.select;
2746
+ const result = {};
2747
+ for (const key of Object.keys(selectObj)) {
2748
+ result[key] = true;
2749
+ }
2750
+ return { select: result };
2067
2751
  }
2068
- function createModelGuardExtension(config) {
2069
- const { typeMap, enumMap, zodChains, uniqueMap, scopeMap, contextFn } = config;
2070
- const wrapZodErrors = config.wrapZodErrors ?? false;
2071
- const schemaBuilder = createSchemaBuilder(typeMap, zodChains, enumMap);
2072
- const queryBuilder = createQueryBuilder(typeMap, enumMap, uniqueMap);
2073
- const modelScopeFks = /* @__PURE__ */ new Map();
2074
- for (const [model, entries] of Object.entries(scopeMap)) {
2075
- const fks = /* @__PURE__ */ new Set();
2076
- for (const entry of entries) {
2077
- fks.add(entry.fk);
2078
- }
2079
- modelScopeFks.set(model, fks);
2752
+ function buildDefaultProjectionBody(shape) {
2753
+ if (shape.select) {
2754
+ return { select: buildDefaultSelectInput(shape.select) };
2080
2755
  }
2081
- function validateCreateCompleteness(modelName, dataConfig) {
2082
- const modelFields = typeMap[modelName];
2083
- if (!modelFields)
2084
- return;
2085
- const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
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
- }
2756
+ if (shape.include) {
2757
+ return { include: buildDefaultIncludeInput(shape.include) };
2103
2758
  }
2104
- function buildDataSchema(model, dataConfig, mode) {
2105
- const modelFields = typeMap[model];
2106
- if (!modelFields)
2107
- throw new ShapeError(`Unknown model: ${model}`);
2108
- const schemaMap = {};
2109
- const forced = {};
2110
- for (const [fieldName, value] of Object.entries(dataConfig)) {
2111
- const fieldMeta = modelFields[fieldName];
2112
- if (!fieldMeta)
2113
- throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
2114
- if (fieldMeta.isRelation)
2115
- throw new ShapeError(`Relation field "${fieldName}" cannot be used in data shape`);
2116
- if (fieldMeta.isUpdatedAt)
2117
- throw new ShapeError(`updatedAt field "${fieldName}" cannot be used in data shape`);
2118
- if (typeof value === "function") {
2119
- let baseSchema = schemaBuilder.buildBaseFieldSchema(model, fieldName);
2120
- let fieldSchema;
2121
- try {
2122
- fieldSchema = value(baseSchema);
2123
- } catch (err) {
2124
- throw new ShapeError(
2125
- `Invalid inline refine for "${model}.${fieldName}": ${err.message}`,
2126
- { cause: err }
2127
- );
2128
- }
2129
- if (mode === "create") {
2130
- if (!fieldMeta.isRequired || fieldMeta.hasDefault) {
2131
- fieldSchema = fieldSchema.optional();
2132
- }
2133
- } else {
2134
- if (!fieldMeta.isRequired) {
2135
- fieldSchema = fieldSchema.nullable().optional();
2136
- } else {
2137
- fieldSchema = fieldSchema.optional();
2138
- }
2139
- }
2140
- schemaMap[fieldName] = fieldSchema;
2141
- } else if (value === true) {
2142
- let fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
2143
- if (mode === "create") {
2144
- if (!fieldMeta.isRequired || fieldMeta.hasDefault) {
2145
- fieldSchema = fieldSchema.optional();
2146
- }
2147
- } else {
2148
- if (!fieldMeta.isRequired) {
2149
- fieldSchema = fieldSchema.nullable().optional();
2150
- } else {
2151
- fieldSchema = fieldSchema.optional();
2759
+ return {};
2760
+ }
2761
+ function hasClientControlledValues(obj) {
2762
+ for (const value of Object.values(obj)) {
2763
+ if (isForcedValue(value))
2764
+ continue;
2765
+ if (value === true)
2766
+ return true;
2767
+ if (isPlainObject(value) && hasClientControlledValues(value))
2768
+ return true;
2769
+ }
2770
+ return false;
2771
+ }
2772
+ function checkIncludeForClientArgs(config) {
2773
+ for (const [key, value] of Object.entries(config)) {
2774
+ if (key === "_count") {
2775
+ if (value !== true && isPlainObject(value) && isPlainObject(value.select)) {
2776
+ const selectObj = value.select;
2777
+ for (const entryVal of Object.values(selectObj)) {
2778
+ if (isPlainObject(entryVal) && entryVal.where) {
2779
+ const w = entryVal.where;
2780
+ if (isPlainObject(w) && hasClientControlledValues(w))
2781
+ return true;
2152
2782
  }
2153
2783
  }
2154
- schemaMap[fieldName] = fieldSchema;
2155
- } else {
2156
- let fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
2157
- if (!fieldMeta.isRequired) {
2158
- fieldSchema = fieldSchema.nullable();
2159
- }
2160
- let parsed;
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
- );
2167
- }
2168
- forced[fieldName] = parsed;
2169
2784
  }
2785
+ continue;
2170
2786
  }
2171
- return {
2172
- schema: z5.object(schemaMap).strict(),
2173
- forced
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
- );
2194
- }
2195
- const val = namedMap[key];
2196
- if (typeof val !== "function" && !isGuardShape(val)) {
2197
- throw new ShapeError(
2198
- `Named shape value for "${key}" must be a guard shape object or function`
2199
- );
2787
+ if (value === true)
2788
+ continue;
2789
+ if (value.orderBy || value.cursor || value.take || value.skip)
2790
+ return true;
2791
+ if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where))
2792
+ return true;
2793
+ if (value.include && checkIncludeForClientArgs(value.include))
2794
+ return true;
2795
+ if (value.select && checkSelectForClientArgs(value.select))
2796
+ return true;
2797
+ }
2798
+ return false;
2799
+ }
2800
+ function checkSelectForClientArgs(config) {
2801
+ for (const [key, value] of Object.entries(config)) {
2802
+ if (key === "_count") {
2803
+ if (value !== true && isPlainObject(value) && isPlainObject(value.select)) {
2804
+ const selectObj = value.select;
2805
+ for (const entryVal of Object.values(selectObj)) {
2806
+ if (isPlainObject(entryVal) && entryVal.where) {
2807
+ const w = entryVal.where;
2808
+ if (isPlainObject(w) && hasClientControlledValues(w))
2809
+ return true;
2810
+ }
2811
+ }
2200
2812
  }
2813
+ continue;
2201
2814
  }
2202
- const parsed = requireBody(body);
2203
- const caller = parsed.caller;
2204
- if (typeof caller !== "string") {
2205
- throw new CallerError('Missing "caller" field in request body');
2206
- }
2207
- const patterns = Object.keys(namedMap);
2208
- const matched = matchCallerPattern(patterns, caller);
2209
- if (!matched) {
2210
- throw new CallerError(
2211
- `Unknown caller: "${caller}". Allowed: ${patterns.map((k) => `"${k}"`).join(", ")}`
2212
- );
2213
- }
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 };
2815
+ if (value === true)
2816
+ continue;
2817
+ if (value.orderBy || value.cursor || value.take || value.skip)
2818
+ return true;
2819
+ if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where))
2820
+ return true;
2821
+ if (value.select && checkSelectForClientArgs(value.select))
2822
+ return true;
2823
+ }
2824
+ return false;
2825
+ }
2826
+ function hasNestedClientControlledArgs(shape) {
2827
+ if (shape.include) {
2828
+ if (checkIncludeForClientArgs(shape.include))
2829
+ return true;
2219
2830
  }
2220
- function requireBody(body) {
2221
- if (!isPlainObject(body))
2222
- throw new ShapeError("Request body must be an object");
2223
- return body;
2831
+ if (shape.select) {
2832
+ if (checkSelectForClientArgs(shape.select))
2833
+ return true;
2224
2834
  }
2225
- function validateAndMergeData(bodyData, cached, method) {
2226
- if (bodyData === void 0 || bodyData === null) {
2227
- throw new ShapeError(`${method} requires "data" in request body`);
2228
- }
2229
- const validated = cached.schema.parse(bodyData);
2230
- return { ...validated, ...cached.forced };
2835
+ return false;
2836
+ }
2837
+ function createModelGuardExtension(config) {
2838
+ const { typeMap, enumMap, zodChains, zodDefaults, uniqueMap, scopeMap, guardConfig, contextFn } = config;
2839
+ const wrapZodErrors = config.wrapZodErrors ?? false;
2840
+ const enforceProjection = guardConfig.enforceProjection ?? false;
2841
+ const scalarBase = createScalarBase(guardConfig.strictDecimal ?? false);
2842
+ const schemaBuilder = createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefaults);
2843
+ const queryBuilder = createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase);
2844
+ const modelScopeFks = /* @__PURE__ */ new Map();
2845
+ for (const [model, entries] of Object.entries(scopeMap)) {
2846
+ const fks = /* @__PURE__ */ new Set();
2847
+ for (const entry of entries)
2848
+ fks.add(entry.fk);
2849
+ modelScopeFks.set(model, fks);
2231
2850
  }
2232
2851
  function maybeValidateUniqueWhere(modelName, shape, method) {
2233
2852
  if (!UNIQUE_MUTATION_METHODS.has(method))
2234
2853
  return;
2235
2854
  if (!shape.where)
2236
2855
  return;
2237
- validateUniqueEquality(modelName, shape.where, method, uniqueMap);
2238
- }
2239
- function hasDataRefines(dataConfig) {
2240
- for (const value of Object.values(dataConfig)) {
2241
- if (typeof value === "function")
2242
- return true;
2243
- }
2244
- return false;
2856
+ validateUniqueEquality(modelName, shape.where, method, uniqueMap, typeMap);
2245
2857
  }
2246
- function createGuardedMethods(modelName, modelDelegate, input) {
2858
+ function createGuardedMethods(modelName, modelDelegate, input, explicitCaller) {
2247
2859
  function callDelegate(method, args) {
2248
2860
  if (typeof modelDelegate[method] !== "function") {
2249
2861
  throw new ShapeError(`Method "${method}" is not available on this model`);
2250
2862
  }
2251
2863
  return modelDelegate[method](args);
2252
2864
  }
2865
+ function resolveCaller() {
2866
+ if (explicitCaller !== void 0)
2867
+ return explicitCaller;
2868
+ const ctx = validateContext(contextFn());
2869
+ const c = ctx.caller;
2870
+ if (typeof c === "string")
2871
+ return c;
2872
+ return void 0;
2873
+ }
2253
2874
  const readShapeCache = /* @__PURE__ */ new Map();
2254
2875
  const dataSchemaCache = /* @__PURE__ */ new Map();
2255
2876
  const whereBuiltCache = /* @__PURE__ */ new Map();
@@ -2272,11 +2893,11 @@ function createModelGuardExtension(config) {
2272
2893
  const cached = dataSchemaCache.get(cacheKey);
2273
2894
  if (cached)
2274
2895
  return cached;
2275
- const built = buildDataSchema(modelName, dataConfig, mode);
2896
+ const built = buildDataSchema(modelName, dataConfig, mode, typeMap, schemaBuilder, zodDefaults);
2276
2897
  dataSchemaCache.set(cacheKey, built);
2277
2898
  return built;
2278
2899
  }
2279
- return buildDataSchema(modelName, dataConfig, mode);
2900
+ return buildDataSchema(modelName, dataConfig, mode, typeMap, schemaBuilder, zodDefaults);
2280
2901
  }
2281
2902
  function getWhereBuilt(whereConfig, matchedKey, wasDynamic) {
2282
2903
  if (!wasDynamic) {
@@ -2317,7 +2938,7 @@ function createModelGuardExtension(config) {
2317
2938
  forcedSelectCountWhere = result.forcedCountWhere;
2318
2939
  }
2319
2940
  return {
2320
- zodSchema: z5.object(schemaFields).strict(),
2941
+ zodSchema: z9.object(schemaFields).strict(),
2321
2942
  forcedIncludeTree,
2322
2943
  forcedSelectTree,
2323
2944
  forcedIncludeCountWhere,
@@ -2346,12 +2967,26 @@ function createModelGuardExtension(config) {
2346
2967
  }
2347
2968
  if (!hasShapeProjection)
2348
2969
  return {};
2970
+ if (!hasBodyProjection && !enforceProjection)
2971
+ return {};
2972
+ if (!hasBodyProjection && enforceProjection) {
2973
+ if (hasNestedClientControlledArgs(shape)) {
2974
+ throw new ShapeError(
2975
+ `Guard shape defines nested client-controlled projection args (orderBy/take/cursor/skip/where) but the client did not provide select/include in the ${method} body. With enforceProjection enabled, either always provide projection in the body or remove client-controlled nested args from the shape.`
2976
+ );
2977
+ }
2978
+ }
2349
2979
  const projection = getProjection(shape, matchedKey, wasDynamic);
2350
- const projectionBody = {};
2351
- if ("select" in parsed)
2352
- projectionBody.select = parsed.select;
2353
- if ("include" in parsed)
2354
- projectionBody.include = parsed.include;
2980
+ let projectionBody;
2981
+ if (hasBodyProjection) {
2982
+ projectionBody = {};
2983
+ if ("select" in parsed)
2984
+ projectionBody.select = parsed.select;
2985
+ if ("include" in parsed)
2986
+ projectionBody.include = parsed.include;
2987
+ } else {
2988
+ projectionBody = buildDefaultProjectionBody(shape);
2989
+ }
2355
2990
  const validated = projection.zodSchema.parse(projectionBody);
2356
2991
  if (Object.keys(projection.forcedIncludeTree).length > 0) {
2357
2992
  applyForcedTree(validated, "include", projection.forcedIncludeTree);
@@ -2382,9 +3017,15 @@ function createModelGuardExtension(config) {
2382
3017
  let validatedWhere;
2383
3018
  if (built.schema) {
2384
3019
  validatedWhere = built.schema.parse(bodyWhere);
3020
+ } else if (bodyWhere !== void 0) {
3021
+ if (bodyWhere === null || !isPlainObject(bodyWhere) || Object.keys(bodyWhere).length > 0) {
3022
+ throw new ShapeError(
3023
+ "Guard shape where contains only forced conditions. Client where input is not accepted."
3024
+ );
3025
+ }
2385
3026
  }
2386
- if (Object.keys(built.forced).length > 0) {
2387
- return preserveUnique ? mergeUniqueForced(validatedWhere, built.forced) : mergeForced(validatedWhere, built.forced);
3027
+ if (hasWhereForced(built.forced)) {
3028
+ return preserveUnique ? mergeUniqueWhereForced(validatedWhere, built.forced) : mergeWhereForced(validatedWhere, built.forced);
2388
3029
  }
2389
3030
  return validatedWhere ?? {};
2390
3031
  }
@@ -2397,14 +3038,15 @@ function createModelGuardExtension(config) {
2397
3038
  }
2398
3039
  function makeReadMethod(method) {
2399
3040
  return (body) => {
2400
- const { shape, body: parsed, matchedKey, wasDynamic } = resolveShape(input, body);
2401
- if (shape.data) {
3041
+ const caller = resolveCaller();
3042
+ const resolved = resolveShape(input, body, contextFn, caller);
3043
+ if (resolved.shape.data) {
2402
3044
  throw new ShapeError(`Guard shape "data" is not valid for ${method}`);
2403
3045
  }
2404
- const { data: _, ...queryShape } = shape;
2405
- const built = getReadShape(method, queryShape, matchedKey, wasDynamic);
3046
+ const { data: _, ...queryShape } = resolved.shape;
3047
+ const built = getReadShape(method, queryShape, resolved.matchedKey, resolved.wasDynamic);
2406
3048
  const isUnique = UNIQUE_READ_METHODS.has(method);
2407
- const args = applyBuiltShape(built, parsed, isUnique);
3049
+ const args = applyBuiltShape(built, resolved.body, isUnique);
2408
3050
  if (isUnique && args.where) {
2409
3051
  validateResolvedUniqueWhere(
2410
3052
  modelName,
@@ -2417,33 +3059,51 @@ function createModelGuardExtension(config) {
2417
3059
  };
2418
3060
  }
2419
3061
  function makeCreateMethod(method) {
3062
+ const isBatch = BATCH_CREATE_METHODS.has(method);
2420
3063
  const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
2421
- const allowedBodyKeys = supportsProjection ? ALLOWED_BODY_KEYS_CREATE_PROJECTION : ALLOWED_BODY_KEYS_CREATE;
3064
+ let allowedBodyKeys;
3065
+ if (isBatch && supportsProjection) {
3066
+ allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_MANY_PROJECTION;
3067
+ } else if (isBatch) {
3068
+ allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_MANY;
3069
+ } else if (supportsProjection) {
3070
+ allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE_PROJECTION;
3071
+ } else {
3072
+ allowedBodyKeys = ALLOWED_BODY_KEYS_CREATE;
3073
+ }
2422
3074
  const allowedShapeKeys = supportsProjection ? VALID_SHAPE_KEYS_CREATE_PROJECTION : VALID_SHAPE_KEYS_CREATE;
2423
3075
  return (body) => {
2424
- const { shape, body: parsed, matchedKey, wasDynamic } = resolveShape(input, body);
2425
- if (!shape.data)
3076
+ const caller = resolveCaller();
3077
+ const resolved = resolveShape(input, body, contextFn, caller);
3078
+ if (!resolved.shape.data)
2426
3079
  throw new ShapeError(`Guard shape requires "data" for ${method}`);
2427
- validateMutationShapeKeys(shape, allowedShapeKeys, method);
2428
- validateMutationBodyKeys(parsed, allowedBodyKeys, method);
2429
- validateCreateCompleteness(modelName, shape.data);
2430
- const dataSchema = getDataSchema("create", shape.data, matchedKey, wasDynamic);
3080
+ validateMutationShapeKeys(resolved.shape, allowedShapeKeys, method);
3081
+ validateMutationBodyKeys(resolved.body, allowedBodyKeys, method);
3082
+ const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
3083
+ validateCreateCompleteness(modelName, resolved.shape.data, typeMap, fks, zodDefaults);
3084
+ const dataSchema = getDataSchema("create", resolved.shape.data, resolved.matchedKey, resolved.wasDynamic);
2431
3085
  let args;
2432
3086
  if (method === "create") {
2433
- const data = validateAndMergeData(parsed.data, dataSchema, method);
3087
+ const data = validateAndMergeData(resolved.body.data, dataSchema, method);
2434
3088
  args = { data };
2435
3089
  } else {
2436
- if (!Array.isArray(parsed.data))
3090
+ if (!Array.isArray(resolved.body.data))
2437
3091
  throw new ShapeError(`${method} expects data to be an array`);
2438
- if (parsed.data.length === 0)
3092
+ if (resolved.body.data.length === 0)
2439
3093
  throw new ShapeError(`${method} received empty data array`);
2440
- const data = parsed.data.map(
3094
+ const data = resolved.body.data.map(
2441
3095
  (item) => validateAndMergeData(item, dataSchema, method)
2442
3096
  );
2443
3097
  args = { data };
2444
3098
  }
3099
+ if (isBatch && resolved.body.skipDuplicates !== void 0) {
3100
+ if (typeof resolved.body.skipDuplicates !== "boolean") {
3101
+ throw new ShapeError(`${method} skipDuplicates must be a boolean`);
3102
+ }
3103
+ args.skipDuplicates = resolved.body.skipDuplicates;
3104
+ }
2445
3105
  if (supportsProjection) {
2446
- const projectionArgs = resolveProjection(shape, parsed, method, matchedKey, wasDynamic);
3106
+ const projectionArgs = resolveProjection(resolved.shape, resolved.body, method, resolved.matchedKey, resolved.wasDynamic);
2447
3107
  Object.assign(args, projectionArgs);
2448
3108
  }
2449
3109
  return callDelegate(method, args);
@@ -2456,18 +3116,19 @@ function createModelGuardExtension(config) {
2456
3116
  const allowedBodyKeys = supportsProjection ? ALLOWED_BODY_KEYS_UPDATE_PROJECTION : ALLOWED_BODY_KEYS_UPDATE;
2457
3117
  const allowedShapeKeys = supportsProjection ? VALID_SHAPE_KEYS_UPDATE_PROJECTION : VALID_SHAPE_KEYS_UPDATE;
2458
3118
  return (body) => {
2459
- const { shape, body: parsed, matchedKey, wasDynamic } = resolveShape(input, body);
2460
- if (!shape.data)
3119
+ const caller = resolveCaller();
3120
+ const resolved = resolveShape(input, body, contextFn, caller);
3121
+ if (!resolved.shape.data)
2461
3122
  throw new ShapeError(`Guard shape requires "data" for ${method}`);
2462
- validateMutationShapeKeys(shape, allowedShapeKeys, method);
2463
- validateMutationBodyKeys(parsed, allowedBodyKeys, method);
2464
- if (isBulk && !shape.where) {
3123
+ validateMutationShapeKeys(resolved.shape, allowedShapeKeys, method);
3124
+ validateMutationBodyKeys(resolved.body, allowedBodyKeys, method);
3125
+ if (isBulk && !resolved.shape.where) {
2465
3126
  throw new ShapeError(`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`);
2466
3127
  }
2467
- maybeValidateUniqueWhere(modelName, shape, method);
2468
- const dataSchema = getDataSchema("update", shape.data, matchedKey, wasDynamic);
2469
- const data = validateAndMergeData(parsed.data, dataSchema, method);
2470
- const where = isUniqueWhere ? requireWhere(shape, parsed.where, method, true, matchedKey, wasDynamic) : buildWhereFromShape(shape, parsed.where, false, matchedKey, wasDynamic);
3128
+ maybeValidateUniqueWhere(modelName, resolved.shape, method);
3129
+ const dataSchema = getDataSchema("update", resolved.shape.data, resolved.matchedKey, resolved.wasDynamic);
3130
+ const data = validateAndMergeData(resolved.body.data, dataSchema, method);
3131
+ 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
3132
  if (isBulk && Object.keys(where).length === 0) {
2472
3133
  throw new ShapeError(`${method} requires at least one where condition`);
2473
3134
  }
@@ -2476,7 +3137,7 @@ function createModelGuardExtension(config) {
2476
3137
  }
2477
3138
  const args = { data, where };
2478
3139
  if (supportsProjection) {
2479
- const projectionArgs = resolveProjection(shape, parsed, method, matchedKey, wasDynamic);
3140
+ const projectionArgs = resolveProjection(resolved.shape, resolved.body, method, resolved.matchedKey, resolved.wasDynamic);
2480
3141
  Object.assign(args, projectionArgs);
2481
3142
  }
2482
3143
  return callDelegate(method, args);
@@ -2489,16 +3150,17 @@ function createModelGuardExtension(config) {
2489
3150
  const allowedBodyKeys = supportsProjection ? ALLOWED_BODY_KEYS_DELETE_PROJECTION : ALLOWED_BODY_KEYS_DELETE;
2490
3151
  const allowedShapeKeys = supportsProjection ? VALID_SHAPE_KEYS_DELETE_PROJECTION : VALID_SHAPE_KEYS_DELETE;
2491
3152
  return (body) => {
2492
- const { shape, body: parsed, matchedKey, wasDynamic } = resolveShape(input, body);
2493
- if (shape.data)
3153
+ const caller = resolveCaller();
3154
+ const resolved = resolveShape(input, body, contextFn, caller);
3155
+ if (resolved.shape.data)
2494
3156
  throw new ShapeError(`Guard shape "data" is not valid for ${method}`);
2495
- validateMutationShapeKeys(shape, allowedShapeKeys, method);
2496
- validateMutationBodyKeys(parsed, allowedBodyKeys, method);
2497
- if (isBulk && !shape.where) {
3157
+ validateMutationShapeKeys(resolved.shape, allowedShapeKeys, method);
3158
+ validateMutationBodyKeys(resolved.body, allowedBodyKeys, method);
3159
+ if (isBulk && !resolved.shape.where) {
2498
3160
  throw new ShapeError(`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`);
2499
3161
  }
2500
- maybeValidateUniqueWhere(modelName, shape, method);
2501
- const where = isUniqueWhere ? requireWhere(shape, parsed.where, method, true, matchedKey, wasDynamic) : buildWhereFromShape(shape, parsed.where, false, matchedKey, wasDynamic);
3162
+ maybeValidateUniqueWhere(modelName, resolved.shape, method);
3163
+ 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
3164
  if (isBulk && Object.keys(where).length === 0) {
2503
3165
  throw new ShapeError(`${method} requires at least one where condition`);
2504
3166
  }
@@ -2507,12 +3169,76 @@ function createModelGuardExtension(config) {
2507
3169
  }
2508
3170
  const args = { where };
2509
3171
  if (supportsProjection) {
2510
- const projectionArgs = resolveProjection(shape, parsed, method, matchedKey, wasDynamic);
3172
+ const projectionArgs = resolveProjection(resolved.shape, resolved.body, method, resolved.matchedKey, resolved.wasDynamic);
2511
3173
  Object.assign(args, projectionArgs);
2512
3174
  }
2513
3175
  return callDelegate(method, args);
2514
3176
  };
2515
3177
  }
3178
+ function makeUpsertMethod() {
3179
+ return (body) => {
3180
+ const caller = resolveCaller();
3181
+ const resolved = resolveShape(input, body, contextFn, caller);
3182
+ if (resolved.shape.data) {
3183
+ throw new ShapeError('Guard shape "data" is not valid for upsert. Use "create" and "update" instead.');
3184
+ }
3185
+ if (!resolved.shape.create) {
3186
+ throw new ShapeError('Guard shape requires "create" for upsert');
3187
+ }
3188
+ if (!resolved.shape.update) {
3189
+ throw new ShapeError('Guard shape requires "update" for upsert');
3190
+ }
3191
+ if (!resolved.shape.where) {
3192
+ throw new ShapeError('Guard shape requires "where" for upsert');
3193
+ }
3194
+ validateMutationShapeKeys(
3195
+ resolved.shape,
3196
+ VALID_SHAPE_KEYS_UPSERT,
3197
+ "upsert"
3198
+ );
3199
+ validateMutationBodyKeys(resolved.body, ALLOWED_BODY_KEYS_UPSERT, "upsert");
3200
+ maybeValidateUniqueWhere(modelName, resolved.shape, "upsert");
3201
+ const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
3202
+ validateCreateCompleteness(modelName, resolved.shape.create, typeMap, fks, zodDefaults);
3203
+ const createDataSchema = getDataSchema(
3204
+ "create",
3205
+ resolved.shape.create,
3206
+ `upsert:create\0${resolved.matchedKey}`,
3207
+ resolved.wasDynamic
3208
+ );
3209
+ const createData = validateAndMergeData(resolved.body.create, createDataSchema, "upsert (create)");
3210
+ const updateDataSchema = getDataSchema(
3211
+ "update",
3212
+ resolved.shape.update,
3213
+ `upsert:update\0${resolved.matchedKey}`,
3214
+ resolved.wasDynamic
3215
+ );
3216
+ const updateData = validateAndMergeData(resolved.body.update, updateDataSchema, "upsert (update)");
3217
+ const where = requireWhere(
3218
+ resolved.shape,
3219
+ resolved.body.where,
3220
+ "upsert",
3221
+ true,
3222
+ resolved.matchedKey,
3223
+ resolved.wasDynamic
3224
+ );
3225
+ validateResolvedUniqueWhere(modelName, where, "upsert", uniqueMap);
3226
+ const args = {
3227
+ where,
3228
+ create: createData,
3229
+ update: updateData
3230
+ };
3231
+ const projectionArgs = resolveProjection(
3232
+ resolved.shape,
3233
+ resolved.body,
3234
+ "upsert",
3235
+ resolved.matchedKey,
3236
+ resolved.wasDynamic
3237
+ );
3238
+ Object.assign(args, projectionArgs);
3239
+ return callDelegate("upsert", args);
3240
+ };
3241
+ }
2516
3242
  return {
2517
3243
  findMany: makeReadMethod("findMany"),
2518
3244
  findFirst: makeReadMethod("findFirst"),
@@ -2528,6 +3254,7 @@ function createModelGuardExtension(config) {
2528
3254
  update: makeUpdateMethod("update"),
2529
3255
  updateMany: makeUpdateMethod("updateMany"),
2530
3256
  updateManyAndReturn: makeUpdateMethod("updateManyAndReturn"),
3257
+ upsert: makeUpsertMethod(),
2531
3258
  delete: makeDeleteMethod("delete"),
2532
3259
  deleteMany: makeDeleteMethod("deleteMany")
2533
3260
  };
@@ -2539,7 +3266,7 @@ function createModelGuardExtension(config) {
2539
3266
  try {
2540
3267
  return fn(body);
2541
3268
  } catch (err) {
2542
- if (err instanceof z5.ZodError) {
3269
+ if (err instanceof z9.ZodError) {
2543
3270
  throw new ShapeError(
2544
3271
  `Validation failed: ${formatZodError(err)}`,
2545
3272
  { cause: err }
@@ -2553,16 +3280,16 @@ function createModelGuardExtension(config) {
2553
3280
  }
2554
3281
  return {
2555
3282
  $allModels: {
2556
- guard(input) {
3283
+ guard(input, caller) {
2557
3284
  const modelName = this.$name;
2558
- const delegateKey = modelName[0].toLowerCase() + modelName.slice(1);
3285
+ const delegateKey = toDelegateKey(modelName);
2559
3286
  const modelDelegate = this.$parent[delegateKey];
2560
3287
  if (!modelDelegate) {
2561
3288
  throw new ShapeError(
2562
3289
  `Could not resolve Prisma delegate for model "${modelName}" (key: "${delegateKey}")`
2563
3290
  );
2564
3291
  }
2565
- const methods = createGuardedMethods(modelName, modelDelegate, input);
3292
+ const methods = createGuardedMethods(modelName, modelDelegate, input, caller);
2566
3293
  if (!wrapZodErrors)
2567
3294
  return methods;
2568
3295
  return wrapMethods(methods);
@@ -2572,28 +3299,26 @@ function createModelGuardExtension(config) {
2572
3299
  }
2573
3300
 
2574
3301
  // 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
3302
  function createGuard(config) {
3303
+ const scalarBase = createScalarBase(config.guardConfig.strictDecimal ?? false);
2582
3304
  const schemaBuilder = createSchemaBuilder(
2583
3305
  config.typeMap,
2584
3306
  config.zodChains,
2585
- config.enumMap
3307
+ config.enumMap,
3308
+ scalarBase,
3309
+ config.zodDefaults ?? {}
2586
3310
  );
2587
3311
  const queryBuilder = createQueryBuilder(
2588
3312
  config.typeMap,
2589
3313
  config.enumMap,
2590
- config.uniqueMap ?? {}
3314
+ config.uniqueMap ?? {},
3315
+ scalarBase
2591
3316
  );
2592
3317
  const log = config.logger ?? { warn: (msg) => console.warn(msg) };
2593
3318
  const wrapZodErrors = config.wrapZodErrors ?? false;
2594
3319
  function rethrowZod(err) {
2595
3320
  if (err instanceof ZodError) {
2596
- throw new ShapeError(`Validation failed: ${formatZodError2(err)}`, { cause: err });
3321
+ throw new ShapeError(`Validation failed: ${formatZodError(err)}`, { cause: err });
2597
3322
  }
2598
3323
  throw err;
2599
3324
  }
@@ -2630,16 +3355,24 @@ function createGuard(config) {
2630
3355
  };
2631
3356
  },
2632
3357
  extension: (contextFn) => {
3358
+ const scopeRoots = /* @__PURE__ */ new Set();
3359
+ for (const entries of Object.values(config.scopeMap)) {
3360
+ for (const entry of entries) {
3361
+ scopeRoots.add(entry.root);
3362
+ }
3363
+ }
2633
3364
  const scopeCtxFn = () => {
2634
- const ctx = contextFn();
3365
+ const ctx = validateContext(contextFn());
2635
3366
  const scopeCtx = {};
2636
3367
  for (const key of Object.keys(ctx)) {
3368
+ if (!scopeRoots.has(key))
3369
+ continue;
2637
3370
  const val = ctx[key];
2638
3371
  if (typeof val === "string" || typeof val === "number" || typeof val === "bigint") {
2639
3372
  scopeCtx[key] = val;
2640
3373
  } else if (val !== null && val !== void 0) {
2641
- log.warn(
2642
- `prisma-guard: Context key "${key}" has non-primitive value (${typeof val}). Only string, number, and bigint values are used for scope context.`
3374
+ throw new PolicyError(
3375
+ `prisma-guard: Scope root "${key}" has non-primitive value (${typeof val}). Only string, number, and bigint values are accepted for scope context.`
2643
3376
  );
2644
3377
  }
2645
3378
  }
@@ -2655,8 +3388,10 @@ function createGuard(config) {
2655
3388
  typeMap: config.typeMap,
2656
3389
  enumMap: config.enumMap,
2657
3390
  zodChains: config.zodChains,
3391
+ zodDefaults: config.zodDefaults ?? {},
2658
3392
  uniqueMap: config.uniqueMap ?? {},
2659
3393
  scopeMap: config.scopeMap,
3394
+ guardConfig: config.guardConfig,
2660
3395
  contextFn,
2661
3396
  wrapZodErrors
2662
3397
  });
@@ -2672,6 +3407,7 @@ export {
2672
3407
  CallerError,
2673
3408
  PolicyError,
2674
3409
  ShapeError,
2675
- createGuard
3410
+ createGuard,
3411
+ force
2676
3412
  };
2677
3413
  //# sourceMappingURL=index.js.map