prisma-guard 1.19.0 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -57,11 +57,92 @@ var CallerError = class extends Error {
57
57
  this.name = "CallerError";
58
58
  }
59
59
  };
60
+ function formatIssue(issue) {
61
+ const path = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
62
+ const code = issue.code;
63
+ if (code === "invalid_union") {
64
+ const unionErrors = issue.unionErrors;
65
+ if (unionErrors && unionErrors.length > 0) {
66
+ const branches = unionErrors.map((ue, i) => {
67
+ const nested = ue.issues.map(formatIssue).join(", ");
68
+ return `branch ${i + 1}: [${nested}]`;
69
+ }).join(" | ");
70
+ return `${path}No matching variant (${branches})`;
71
+ }
72
+ return `${path}No matching variant`;
73
+ }
74
+ if (issue.code === "unrecognized_keys") {
75
+ const keys = issue.keys;
76
+ if (keys && keys.length > 0) {
77
+ return `${path}Unrecognized key(s): ${keys.join(", ")}`;
78
+ }
79
+ }
80
+ if (issue.code === "invalid_type") {
81
+ const expected = issue.expected;
82
+ const received = issue.received;
83
+ if (expected && received) {
84
+ return `${path}Expected ${expected}, received ${received}`;
85
+ }
86
+ }
87
+ if (code === "invalid_enum_value") {
88
+ const options = issue.options;
89
+ if (options) {
90
+ return `${path}Invalid value. Expected one of: ${options.join(", ")}`;
91
+ }
92
+ }
93
+ if (code === "too_small") {
94
+ const minimum = issue.minimum;
95
+ const type = issue.type;
96
+ if (type === "string" && minimum !== void 0) {
97
+ return `${path}String must contain at least ${minimum} character(s)`;
98
+ }
99
+ if (type === "array" && minimum !== void 0) {
100
+ return `${path}Array must contain at least ${minimum} element(s)`;
101
+ }
102
+ if (type === "number" && minimum !== void 0) {
103
+ return `${path}Number must be >= ${minimum}`;
104
+ }
105
+ return `${path}${issue.message}`;
106
+ }
107
+ if (code === "too_big") {
108
+ const maximum = issue.maximum;
109
+ const type = issue.type;
110
+ if (type === "string" && maximum !== void 0) {
111
+ return `${path}String must contain at most ${maximum} character(s)`;
112
+ }
113
+ if (type === "array" && maximum !== void 0) {
114
+ return `${path}Array must contain at most ${maximum} element(s)`;
115
+ }
116
+ if (type === "number" && maximum !== void 0) {
117
+ return `${path}Number must be <= ${maximum}`;
118
+ }
119
+ return `${path}${issue.message}`;
120
+ }
121
+ if (code === "invalid_format") {
122
+ const format = issue.format;
123
+ if (format) {
124
+ return `${path}Invalid ${format} format`;
125
+ }
126
+ }
127
+ if (code === "invalid_value") {
128
+ const values = issue.values;
129
+ if (values) {
130
+ return `${path}Invalid value. Expected one of: ${values.join(", ")}`;
131
+ }
132
+ }
133
+ return `${path}${issue.message}`;
134
+ }
60
135
  function formatZodError(err) {
61
- return err.issues.map((i) => {
62
- const p = i.path.length > 0 ? `${i.path.join(".")}: ` : "";
63
- return `${p}${i.message}`;
64
- }).join("; ");
136
+ return err.issues.map(formatIssue).join("; ");
137
+ }
138
+ function wrapParseError(err, context) {
139
+ if (err instanceof ShapeError) {
140
+ throw new ShapeError(`${context}: ${err.message}`, { cause: err });
141
+ }
142
+ if (err && typeof err === "object" && "issues" in err) {
143
+ throw new ShapeError(`${context}: ${formatZodError(err)}`, { cause: err });
144
+ }
145
+ throw err;
65
146
  }
66
147
 
67
148
  // src/shared/scalar-base.ts
@@ -207,15 +288,19 @@ function coerceToArray(value) {
207
288
  return value;
208
289
  const keys = Object.keys(value);
209
290
  if (keys.length === 0)
210
- return [];
291
+ return value;
211
292
  const allNumeric = keys.every((k) => /^\d+$/.test(k));
212
293
  if (!allNumeric)
213
294
  return value;
214
- const sorted = keys.map(Number).sort((a, b) => a - b);
295
+ const indices = keys.map(Number).sort((a, b) => a - b);
296
+ for (let i = 0; i < indices.length; i++) {
297
+ if (indices[i] !== i)
298
+ return value;
299
+ }
215
300
  const obj = value;
216
- const result = [];
217
- for (const idx of sorted) {
218
- result.push(obj[String(idx)]);
301
+ const result = new Array(indices.length);
302
+ for (let i = 0; i < indices.length; i++) {
303
+ result[i] = obj[String(i)];
219
304
  }
220
305
  return result;
221
306
  }
@@ -233,7 +318,8 @@ var SCALAR_OPERATORS = {
233
318
  "gt",
234
319
  "gte",
235
320
  "lt",
236
- "lte"
321
+ "lte",
322
+ "search"
237
323
  ]),
238
324
  Int: /* @__PURE__ */ new Set(["equals", "not", "gt", "gte", "lt", "lte", "in", "notIn"]),
239
325
  Float: /* @__PURE__ */ new Set(["equals", "not", "gt", "gte", "lt", "lte", "in", "notIn"]),
@@ -335,7 +421,7 @@ function createScalarListOperatorSchema(fieldMeta, operator, enumMap, scalarBase
335
421
  }
336
422
  function createJsonOperatorSchema(fieldMeta, operator) {
337
423
  const jsonValue = import_zod2.z.unknown();
338
- if (operator === "equals" || operator === "not") {
424
+ if (operator === "equals") {
339
425
  return !fieldMeta.isRequired ? import_zod2.z.union([jsonValue, import_zod2.z.null()]) : jsonValue;
340
426
  }
341
427
  if (operator === "path") {
@@ -351,6 +437,29 @@ function createJsonOperatorSchema(fieldMeta, operator) {
351
437
  `Operator "${operator}" not supported for Json fields`
352
438
  );
353
439
  }
440
+ function buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase) {
441
+ const allOps = getSupportedOperators(fieldMeta);
442
+ const nestedOps = allOps.filter((o) => o !== "not");
443
+ if (nestedOps.length === 0)
444
+ return scalarSchema;
445
+ const nestedSchemas = {};
446
+ for (const op of nestedOps) {
447
+ nestedSchemas[op] = createOperatorSchema(
448
+ fieldMeta,
449
+ op,
450
+ enumMap,
451
+ scalarBase
452
+ ).optional();
453
+ }
454
+ const nestedKeys = Object.keys(nestedSchemas);
455
+ const nestedObj = import_zod2.z.object(nestedSchemas).strict().refine(
456
+ (v) => nestedKeys.some(
457
+ (k) => v[k] !== void 0
458
+ ),
459
+ { message: "not filter must specify at least one operator" }
460
+ );
461
+ return import_zod2.z.union([scalarSchema, nestedObj]);
462
+ }
354
463
  function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
355
464
  if (fieldMeta.isList) {
356
465
  return createScalarListOperatorSchema(
@@ -371,9 +480,13 @@ function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
371
480
  );
372
481
  }
373
482
  const enumSchema = import_zod2.z.enum(values);
374
- if (operator === "equals" || operator === "not") {
483
+ if (operator === "equals") {
375
484
  return !fieldMeta.isRequired ? import_zod2.z.union([enumSchema, import_zod2.z.null()]) : enumSchema;
376
485
  }
486
+ if (operator === "not") {
487
+ const scalarSchema = !fieldMeta.isRequired ? import_zod2.z.union([enumSchema, import_zod2.z.null()]) : enumSchema;
488
+ return buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase);
489
+ }
377
490
  const itemSchema = !fieldMeta.isRequired ? import_zod2.z.union([enumSchema, import_zod2.z.null()]) : enumSchema;
378
491
  return import_zod2.z.preprocess(coerceToArray, import_zod2.z.array(itemSchema));
379
492
  }
@@ -384,6 +497,11 @@ function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
384
497
  `Operator "${operator}" not supported for type "Json"`
385
498
  );
386
499
  }
500
+ if (operator === "not") {
501
+ const jsonValue = import_zod2.z.unknown();
502
+ const scalarSchema = !fieldMeta.isRequired ? import_zod2.z.union([jsonValue, import_zod2.z.null()]) : jsonValue;
503
+ return buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase);
504
+ }
387
505
  return createJsonOperatorSchema(fieldMeta, operator);
388
506
  }
389
507
  const supportedOps = SCALAR_OPERATORS[fieldMeta.type];
@@ -406,9 +524,13 @@ function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
406
524
  }
407
525
  const scalar = factory();
408
526
  const coerced = wrapWithInputCoercion(fieldMeta.type, false, scalar);
409
- if (operator === "equals" || operator === "not") {
527
+ if (operator === "equals") {
410
528
  return !fieldMeta.isRequired ? import_zod2.z.union([coerced, import_zod2.z.null()]) : coerced;
411
529
  }
530
+ if (operator === "not") {
531
+ const scalarSchema = !fieldMeta.isRequired ? import_zod2.z.union([coerced, import_zod2.z.null()]) : coerced;
532
+ return buildNotFilterSchema(fieldMeta, scalarSchema, enumMap, scalarBase);
533
+ }
412
534
  if (operator === "in" || operator === "notIn") {
413
535
  const itemSchema = !fieldMeta.isRequired ? import_zod2.z.union([coerced, import_zod2.z.null()]) : coerced;
414
536
  return import_zod2.z.preprocess(coerceToArray, import_zod2.z.array(itemSchema));
@@ -817,6 +939,8 @@ function deepClone(value) {
817
939
  return new Date(value.getTime());
818
940
  if (value instanceof Uint8Array)
819
941
  return value.slice();
942
+ if (value instanceof RegExp)
943
+ return new RegExp(value.source, value.flags);
820
944
  if (Array.isArray(value))
821
945
  return value.map(deepClone);
822
946
  const proto = Object.getPrototypeOf(value);
@@ -834,7 +958,10 @@ function deepClone(value) {
834
958
  }
835
959
 
836
960
  // src/runtime/query-builder-forced.ts
837
- var EMPTY_WHERE_FORCED = { conditions: {}, relations: {} };
961
+ var EMPTY_WHERE_FORCED = {
962
+ conditions: {},
963
+ relations: {}
964
+ };
838
965
  function hasWhereForced(f) {
839
966
  return Object.keys(f.conditions).length > 0 || Object.keys(f.relations).length > 0;
840
967
  }
@@ -894,7 +1021,7 @@ function mergeUniqueWhereForced(where, forced) {
894
1021
  }
895
1022
  return result;
896
1023
  }
897
- function applyBuiltShape(built, body, isUniqueMethod) {
1024
+ function applyBuiltShape(built, body, isUniqueMethod, modelName) {
898
1025
  let parseable = body;
899
1026
  const hasWhereInSchema = "where" in built.zodSchema.shape;
900
1027
  if (isPlainObject(body)) {
@@ -920,7 +1047,20 @@ function applyBuiltShape(built, body, isUniqueMethod) {
920
1047
  }
921
1048
  }
922
1049
  }
923
- const validated = built.zodSchema.parse(parseable);
1050
+ let validated;
1051
+ try {
1052
+ validated = built.zodSchema.parse(parseable);
1053
+ } catch (err) {
1054
+ if (err instanceof ShapeError)
1055
+ throw err;
1056
+ if (err && typeof err === "object" && "issues" in err) {
1057
+ const context = modelName ? `Invalid query on model "${modelName}"` : "Invalid query";
1058
+ throw new ShapeError(`${context}: ${formatZodError(err)}`, {
1059
+ cause: err
1060
+ });
1061
+ }
1062
+ throw err;
1063
+ }
924
1064
  if (hasWhereForced(built.forcedWhere)) {
925
1065
  validated.where = isUniqueMethod ? mergeUniqueWhereForced(
926
1066
  validated.where,
@@ -1295,7 +1435,8 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1295
1435
  model,
1296
1436
  fieldSchemas,
1297
1437
  relationForced,
1298
- currentDepth
1438
+ currentDepth,
1439
+ scalarConditions
1299
1440
  );
1300
1441
  continue;
1301
1442
  }
@@ -1377,7 +1518,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1377
1518
  }
1378
1519
  }
1379
1520
  }
1380
- function processRelationFilter(key, value, fieldMeta, model, fieldSchemas, parentRelations, depth) {
1521
+ function processRelationFilter(key, value, fieldMeta, model, fieldSchemas, parentRelations, depth, parentConditions) {
1381
1522
  if (!isPlainObject(value)) {
1382
1523
  throw new ShapeError(
1383
1524
  `Relation filter for "${key}" must be an object with operators (some, every, none, is, isNot)`
@@ -1392,6 +1533,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1392
1533
  const opSchemas = {};
1393
1534
  const opForced = {};
1394
1535
  let hasClientOps = false;
1536
+ let hasForcedNull = false;
1395
1537
  for (const [op, opValue] of Object.entries(value)) {
1396
1538
  if (!allowedOps.has(op)) {
1397
1539
  const allowed = [...allowedOps].join(", ");
@@ -1399,9 +1541,24 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1399
1541
  `Operator "${op}" not supported for ${fieldMeta.isList ? "to-many" : "to-one"} relation "${key}". Allowed: ${allowed}`
1400
1542
  );
1401
1543
  }
1544
+ if (opValue === null) {
1545
+ if (!TO_ONE_RELATION_OPS.has(op)) {
1546
+ throw new ShapeError(
1547
+ `Null value for operator "${op}" is only valid on to-one relation operators (is, isNot), not on "${key}"`
1548
+ );
1549
+ }
1550
+ const existing = parentConditions[key];
1551
+ if (existing && isPlainObject(existing)) {
1552
+ existing[op] = null;
1553
+ } else {
1554
+ parentConditions[key] = { [op]: null };
1555
+ }
1556
+ hasForcedNull = true;
1557
+ continue;
1558
+ }
1402
1559
  if (!isPlainObject(opValue)) {
1403
1560
  throw new ShapeError(
1404
- `Relation filter operator "${op}" on "${key}" must be an object defining nested where fields`
1561
+ `Relation filter operator "${op}" on "${key}" must be an object defining nested where fields, or null for to-one existence checks`
1405
1562
  );
1406
1563
  }
1407
1564
  const nested = buildWhereSchema(fieldMeta.type, opValue, depth + 1);
@@ -1427,7 +1584,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1427
1584
  opForced[op] = nested.forced;
1428
1585
  }
1429
1586
  }
1430
- if (!hasClientOps && Object.keys(opForced).length === 0) {
1587
+ if (!hasClientOps && Object.keys(opForced).length === 0 && !hasForcedNull) {
1431
1588
  throw new ShapeError(
1432
1589
  `Relation filter for "${key}" on model "${model}" produced no conditions. Define at least one nested field in the operator shape.`
1433
1590
  );
@@ -1435,7 +1592,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1435
1592
  if (hasClientOps) {
1436
1593
  const clientOpKeys = Object.keys(opSchemas);
1437
1594
  const opObjSchema = import_zod4.z.object(opSchemas).strict();
1438
- if (Object.keys(opForced).length === 0) {
1595
+ if (Object.keys(opForced).length === 0 && !hasForcedNull) {
1439
1596
  fieldSchemas[key] = opObjSchema.refine(
1440
1597
  (v) => clientOpKeys.some(
1441
1598
  (k) => v[k] !== void 0
@@ -1451,13 +1608,6 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1451
1608
  }
1452
1609
  }
1453
1610
  function processScalarField(fieldName, operators, model, fieldMeta, fieldSchemas, scalarConditions) {
1454
- if (operators === true) {
1455
- const supportedOps = getSupportedOperators(fieldMeta);
1456
- const expanded = {};
1457
- for (const op of supportedOps)
1458
- expanded[op] = true;
1459
- operators = expanded;
1460
- }
1461
1611
  if (!isPlainObject(operators)) {
1462
1612
  throw new ShapeError(
1463
1613
  `Where config for scalar field "${fieldName}" on model "${model}" must be an object of operators`
@@ -1570,7 +1720,82 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1570
1720
  scalarConditions[fieldName] = fieldForced;
1571
1721
  }
1572
1722
  }
1573
- return { buildWhereSchema };
1723
+ function buildUniqueWhereSchema(model, whereConfig) {
1724
+ const modelFields = typeMap[model];
1725
+ if (!modelFields)
1726
+ throw new ShapeError(`Unknown model: ${model}`);
1727
+ const fieldSchemas = {};
1728
+ const forced = {};
1729
+ const forcedOnlyKeys = /* @__PURE__ */ new Set();
1730
+ for (const [key, value] of Object.entries(whereConfig)) {
1731
+ if (COMBINATOR_KEYS.has(key)) {
1732
+ throw new ShapeError(
1733
+ `Combinator "${key}" is not supported in unique where for model "${model}"`
1734
+ );
1735
+ }
1736
+ const fieldMeta = modelFields[key];
1737
+ if (!fieldMeta)
1738
+ throw new ShapeError(`Unknown field "${key}" on model "${model}"`);
1739
+ if (fieldMeta.isRelation)
1740
+ throw new ShapeError(
1741
+ `Relation field "${key}" cannot be used in unique where for model "${model}"`
1742
+ );
1743
+ const base = createBaseType(fieldMeta, enumMap, scalarBase);
1744
+ let directSchema;
1745
+ if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
1746
+ directSchema = wrapWithInputCoercion(
1747
+ fieldMeta.type,
1748
+ fieldMeta.isList,
1749
+ base
1750
+ );
1751
+ } else {
1752
+ directSchema = base;
1753
+ }
1754
+ const equalsWrapper = import_zod4.z.object({ equals: directSchema }).strict().transform((v) => v.equals);
1755
+ if (value === true) {
1756
+ fieldSchemas[key] = import_zod4.z.union([directSchema, equalsWrapper]);
1757
+ continue;
1758
+ }
1759
+ if (isPlainObject(value)) {
1760
+ const keys = Object.keys(value);
1761
+ if (keys.length === 1 && keys[0] === "equals") {
1762
+ const equalsVal = value.equals;
1763
+ if (equalsVal === true) {
1764
+ fieldSchemas[key] = import_zod4.z.union([directSchema, equalsWrapper]);
1765
+ } else {
1766
+ const actual2 = isForcedValue(equalsVal) ? equalsVal.value : equalsVal;
1767
+ try {
1768
+ forced[key] = directSchema.parse(actual2);
1769
+ } catch (err) {
1770
+ throw new ShapeError(
1771
+ `Invalid forced value for unique where "${model}.${key}": ${err.message}`
1772
+ );
1773
+ }
1774
+ forcedOnlyKeys.add(key);
1775
+ }
1776
+ continue;
1777
+ }
1778
+ throw new ShapeError(
1779
+ `Unique where field "${key}" on model "${model}" only accepts true or { equals: true/value }. Got operators: ${keys.join(", ")}`
1780
+ );
1781
+ }
1782
+ const actual = isForcedValue(value) ? value.value : value;
1783
+ try {
1784
+ forced[key] = directSchema.parse(actual);
1785
+ } catch (err) {
1786
+ throw new ShapeError(
1787
+ `Invalid forced value for unique where "${model}.${key}": ${err.message}`
1788
+ );
1789
+ }
1790
+ forcedOnlyKeys.add(key);
1791
+ }
1792
+ return {
1793
+ schema: Object.keys(fieldSchemas).length > 0 ? import_zod4.z.object(fieldSchemas).strict() : null,
1794
+ forced,
1795
+ forcedOnlyKeys
1796
+ };
1797
+ }
1798
+ return { buildWhereSchema, buildUniqueWhereSchema };
1574
1799
  }
1575
1800
 
1576
1801
  // src/runtime/query-builder-args.ts
@@ -1669,25 +1894,26 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
1669
1894
  return import_zod5.z.union([singleSchema, import_zod5.z.preprocess(coerceToArray, import_zod5.z.array(singleSchema).min(1))]).optional();
1670
1895
  }
1671
1896
  function buildTakeSchema(config) {
1672
- if (!Number.isFinite(config.max) || !Number.isInteger(config.max)) {
1673
- throw new ShapeError(`take max must be a finite integer, got ${config.max}`);
1897
+ const normalized = typeof config === "number" ? { max: config, default: config } : config;
1898
+ if (!Number.isFinite(normalized.max) || !Number.isInteger(normalized.max)) {
1899
+ throw new ShapeError(`take max must be a finite integer, got ${normalized.max}`);
1674
1900
  }
1675
- if (config.max < 1) {
1676
- throw new ShapeError(`take max must be at least 1, got ${config.max}`);
1901
+ if (normalized.max < 1) {
1902
+ throw new ShapeError(`take max must be at least 1, got ${normalized.max}`);
1677
1903
  }
1678
- if (config.default !== void 0) {
1679
- if (!Number.isFinite(config.default) || !Number.isInteger(config.default)) {
1680
- throw new ShapeError(`take default must be a finite integer, got ${config.default}`);
1904
+ if (normalized.default !== void 0) {
1905
+ if (!Number.isFinite(normalized.default) || !Number.isInteger(normalized.default)) {
1906
+ throw new ShapeError(`take default must be a finite integer, got ${normalized.default}`);
1681
1907
  }
1682
- if (config.default < 1) {
1683
- throw new ShapeError(`take default must be at least 1, got ${config.default}`);
1908
+ if (normalized.default < 1) {
1909
+ throw new ShapeError(`take default must be at least 1, got ${normalized.default}`);
1684
1910
  }
1685
- if (config.default > config.max) {
1911
+ if (normalized.default > normalized.max) {
1686
1912
  throw new ShapeError("take default cannot exceed max");
1687
1913
  }
1688
- return import_zod5.z.number().int().min(1).max(config.max).default(config.default);
1914
+ return import_zod5.z.number().int().min(1).max(normalized.max).default(normalized.default);
1689
1915
  }
1690
- return import_zod5.z.number().int().min(1).max(config.max).optional();
1916
+ return import_zod5.z.number().int().min(1).max(normalized.max).optional();
1691
1917
  }
1692
1918
  function buildCursorSchema(model, cursorConfig) {
1693
1919
  const modelFields = typeMap[model];
@@ -1794,10 +2020,21 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
1794
2020
  ).optional();
1795
2021
  }
1796
2022
  const havingFieldKeys = Object.keys(fieldSchemas);
1797
- return import_zod5.z.object(fieldSchemas).strict().refine(
1798
- (v) => havingFieldKeys.some((k) => v[k] !== void 0),
1799
- { message: "having must specify at least one field" }
1800
- ).optional();
2023
+ const havingSchema = import_zod5.z.lazy(() => {
2024
+ const allSchemas = { ...fieldSchemas };
2025
+ allSchemas["AND"] = import_zod5.z.preprocess(coerceToArray, import_zod5.z.array(havingSchema).min(1)).optional();
2026
+ allSchemas["OR"] = import_zod5.z.preprocess(coerceToArray, import_zod5.z.array(havingSchema).min(1)).optional();
2027
+ allSchemas["NOT"] = import_zod5.z.union([
2028
+ havingSchema,
2029
+ import_zod5.z.preprocess(coerceToArray, import_zod5.z.array(havingSchema).min(1))
2030
+ ]).optional();
2031
+ const allKeys = [...havingFieldKeys, "AND", "OR", "NOT"];
2032
+ return import_zod5.z.object(allSchemas).strict().refine(
2033
+ (v) => allKeys.some((k) => v[k] !== void 0),
2034
+ { message: "having must specify at least one field or combinator" }
2035
+ );
2036
+ });
2037
+ return havingSchema.optional();
1801
2038
  }
1802
2039
  function buildAggregateFieldSchema(model, opName, fieldConfig) {
1803
2040
  const modelFields = typeMap[model];
@@ -2569,6 +2806,17 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
2569
2806
  return null;
2570
2807
  return { key: matched, shape: shapes[matched] };
2571
2808
  }
2809
+ function resolveDefaultShape(config, model, method, normalizedBody, opts, builtCache, isUnique) {
2810
+ const shapeOrFn = config["default"];
2811
+ let built;
2812
+ if (typeof shapeOrFn === "function") {
2813
+ const resolved = resolveAndValidateShape(shapeOrFn, opts?.ctx);
2814
+ built = buildShapeZodSchema(model, method, resolved);
2815
+ } else {
2816
+ built = builtCache.get("default");
2817
+ }
2818
+ return applyBuiltShape(built, normalizedBody, isUnique);
2819
+ }
2572
2820
  function buildQuerySchema(model, method, config) {
2573
2821
  const isSingle = typeof config === "function" || isShapeConfig(config);
2574
2822
  const builtCache = /* @__PURE__ */ new Map();
@@ -2620,23 +2868,39 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
2620
2868
  "Pass caller via opts.caller, not in the request body."
2621
2869
  );
2622
2870
  }
2871
+ const namedConfig = config;
2623
2872
  const caller = opts?.caller;
2624
2873
  if (typeof caller !== "string") {
2625
- const allowed = Object.keys(
2626
- config
2627
- );
2874
+ if ("default" in namedConfig) {
2875
+ return resolveDefaultShape(
2876
+ namedConfig,
2877
+ model,
2878
+ method,
2879
+ normalizedBody,
2880
+ opts,
2881
+ builtCache,
2882
+ isUnique
2883
+ );
2884
+ }
2885
+ const allowed = Object.keys(namedConfig);
2628
2886
  throw new CallerError(
2629
2887
  `Missing caller. This query uses named shape routing with keys: ${allowed.map((k) => `"${k}"`).join(", ")}. Provide caller via opts.caller.`
2630
2888
  );
2631
2889
  }
2632
- const matched = matchCaller(
2633
- config,
2634
- caller
2635
- );
2890
+ const matched = matchCaller(namedConfig, caller);
2636
2891
  if (!matched) {
2637
- const allowed = Object.keys(
2638
- config
2639
- );
2892
+ if ("default" in namedConfig) {
2893
+ return resolveDefaultShape(
2894
+ namedConfig,
2895
+ model,
2896
+ method,
2897
+ normalizedBody,
2898
+ opts,
2899
+ builtCache,
2900
+ isUnique
2901
+ );
2902
+ }
2903
+ const allowed = Object.keys(namedConfig);
2640
2904
  throw new CallerError(
2641
2905
  `Unknown caller: "${caller}". Allowed: ${allowed.map((k) => `"${k}"`).join(", ")}`
2642
2906
  );
@@ -2656,6 +2920,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
2656
2920
  buildQuerySchema,
2657
2921
  buildShapeZodSchema,
2658
2922
  buildWhereSchema: whereBuilder.buildWhereSchema,
2923
+ buildUniqueWhereSchema: whereBuilder.buildUniqueWhereSchema,
2659
2924
  buildIncludeSchema: projectionBuilder.buildIncludeSchema,
2660
2925
  buildSelectSchema: projectionBuilder.buildSelectSchema
2661
2926
  };
@@ -2668,23 +2933,10 @@ var READ_OPS = /* @__PURE__ */ new Set([
2668
2933
  "findFirstOrThrow",
2669
2934
  "count"
2670
2935
  ]);
2671
- var AGGREGATE_OPS = /* @__PURE__ */ new Set([
2672
- "aggregate",
2673
- "groupBy"
2674
- ]);
2675
- var FIND_UNIQUE_OPS = /* @__PURE__ */ new Set([
2676
- "findUnique",
2677
- "findUniqueOrThrow"
2678
- ]);
2679
- var CREATE_OPS = /* @__PURE__ */ new Set([
2680
- "create",
2681
- "createMany",
2682
- "createManyAndReturn"
2683
- ]);
2684
- var UNIQUE_MUTATION_OPS = /* @__PURE__ */ new Set([
2685
- "update",
2686
- "delete"
2687
- ]);
2936
+ var AGGREGATE_OPS = /* @__PURE__ */ new Set(["aggregate", "groupBy"]);
2937
+ var FIND_UNIQUE_OPS = /* @__PURE__ */ new Set(["findUnique", "findUniqueOrThrow"]);
2938
+ var CREATE_OPS = /* @__PURE__ */ new Set(["create", "createMany", "createManyAndReturn"]);
2939
+ var UNIQUE_MUTATION_OPS = /* @__PURE__ */ new Set(["update", "delete"]);
2688
2940
  var MULTI_MUTATION_OPS = /* @__PURE__ */ new Set([
2689
2941
  "updateMany",
2690
2942
  "updateManyAndReturn",
@@ -2813,7 +3065,9 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
2813
3065
  for (const s of presentScopes) {
2814
3066
  validateScopeValue(s.root, ctx[s.root]);
2815
3067
  }
2816
- const presentConditions = presentScopes.map((s) => ({ [s.fk]: ctx[s.root] }));
3068
+ const presentConditions = presentScopes.map((s) => ({
3069
+ [s.fk]: ctx[s.root]
3070
+ }));
2817
3071
  const missingRoots = scopes.filter((s) => ctx[s.root] == null).map((s) => s.root);
2818
3072
  const isMutation = CREATE_OPS.has(operation) || UNIQUE_MUTATION_OPS.has(operation) || MULTI_MUTATION_OPS.has(operation) || operation === "upsert";
2819
3073
  if (missingRoots.length > 0) {
@@ -2843,14 +3097,32 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
2843
3097
  throw new ShapeError(`upsert expects create to be an object`);
2844
3098
  }
2845
3099
  nextArgs.create = { ...args.create };
2846
- enforceDataScope(nextArgs.create, scopes, overrides, log, model, operation, onScopeRelationWrite, "create");
3100
+ enforceDataScope(
3101
+ nextArgs.create,
3102
+ scopes,
3103
+ overrides,
3104
+ log,
3105
+ model,
3106
+ operation,
3107
+ onScopeRelationWrite,
3108
+ "create"
3109
+ );
2847
3110
  }
2848
3111
  if (args.update !== void 0 && args.update !== null) {
2849
3112
  if (typeof args.update !== "object" || Array.isArray(args.update)) {
2850
3113
  throw new ShapeError(`upsert expects update to be an object`);
2851
3114
  }
2852
3115
  nextArgs.update = { ...args.update };
2853
- enforceDataScope(nextArgs.update, scopes, overrides, log, model, operation, onScopeRelationWrite, "mutate");
3116
+ enforceDataScope(
3117
+ nextArgs.update,
3118
+ scopes,
3119
+ overrides,
3120
+ log,
3121
+ model,
3122
+ operation,
3123
+ onScopeRelationWrite,
3124
+ "mutate"
3125
+ );
2854
3126
  }
2855
3127
  return query(nextArgs);
2856
3128
  }
@@ -2860,7 +3132,15 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
2860
3132
  `Scoped model "${model}" does not allow ${operation} via scope extension (findUniqueMode is "reject"). Use findFirst with explicit where conditions instead.`
2861
3133
  );
2862
3134
  }
2863
- return handleFindUnique(args, query, conditions, scopes, operation, log);
3135
+ return handleFindUnique(
3136
+ args,
3137
+ query,
3138
+ conditions,
3139
+ scopes,
3140
+ operation,
3141
+ log,
3142
+ model
3143
+ );
2864
3144
  }
2865
3145
  if (READ_OPS.has(operation)) {
2866
3146
  nextArgs.where = buildAndConditions(args.where, conditions);
@@ -2888,7 +3168,16 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
2888
3168
  }
2889
3169
  nextArgs.data = args.data.map((d) => {
2890
3170
  const item = { ...d };
2891
- enforceDataScope(item, scopes, overrides, log, model, operation, onScopeRelationWrite, "create");
3171
+ enforceDataScope(
3172
+ item,
3173
+ scopes,
3174
+ overrides,
3175
+ log,
3176
+ model,
3177
+ operation,
3178
+ onScopeRelationWrite,
3179
+ "create"
3180
+ );
2892
3181
  return item;
2893
3182
  });
2894
3183
  } else {
@@ -2896,7 +3185,16 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
2896
3185
  throw new ShapeError(`${operation} expects data to be an object`);
2897
3186
  }
2898
3187
  nextArgs.data = { ...args.data };
2899
- enforceDataScope(nextArgs.data, scopes, overrides, log, model, operation, onScopeRelationWrite, "create");
3188
+ enforceDataScope(
3189
+ nextArgs.data,
3190
+ scopes,
3191
+ overrides,
3192
+ log,
3193
+ model,
3194
+ operation,
3195
+ onScopeRelationWrite,
3196
+ "create"
3197
+ );
2900
3198
  }
2901
3199
  return query(nextArgs);
2902
3200
  }
@@ -2907,7 +3205,16 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
2907
3205
  throw new ShapeError(`${operation} expects data to be an object`);
2908
3206
  }
2909
3207
  nextArgs.data = { ...args.data };
2910
- enforceDataScope(nextArgs.data, scopes, overrides, log, model, operation, onScopeRelationWrite, "mutate");
3208
+ enforceDataScope(
3209
+ nextArgs.data,
3210
+ scopes,
3211
+ overrides,
3212
+ log,
3213
+ model,
3214
+ operation,
3215
+ onScopeRelationWrite,
3216
+ "mutate"
3217
+ );
2911
3218
  }
2912
3219
  return query(nextArgs);
2913
3220
  }
@@ -2918,7 +3225,16 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
2918
3225
  throw new ShapeError(`${operation} expects data to be an object`);
2919
3226
  }
2920
3227
  nextArgs.data = { ...args.data };
2921
- enforceDataScope(nextArgs.data, scopes, overrides, log, model, operation, onScopeRelationWrite, "mutate");
3228
+ enforceDataScope(
3229
+ nextArgs.data,
3230
+ scopes,
3231
+ overrides,
3232
+ log,
3233
+ model,
3234
+ operation,
3235
+ onScopeRelationWrite,
3236
+ "mutate"
3237
+ );
2922
3238
  }
2923
3239
  return query(nextArgs);
2924
3240
  }
@@ -2929,7 +3245,7 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
2929
3245
  }
2930
3246
  };
2931
3247
  }
2932
- async function handleFindUnique(args, query, conditions, scopes, operation, log) {
3248
+ async function handleFindUnique(args, query, conditions, scopes, operation, log, model) {
2933
3249
  const nextArgs = { ...args };
2934
3250
  const injectedFks = [];
2935
3251
  const originalSelect = args?.select;
@@ -2947,7 +3263,9 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log)
2947
3263
  if (result === null)
2948
3264
  return result;
2949
3265
  if (typeof result !== "object" || result === null) {
2950
- throw new ShapeError("findUnique result must be an object or null");
3266
+ throw new ShapeError(
3267
+ `${operation} on model "${model}" returned a non-object, non-null result`
3268
+ );
2951
3269
  }
2952
3270
  const resultObj = result;
2953
3271
  let verifyObj = resultObj;
@@ -2956,22 +3274,29 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log)
2956
3274
  const where = args?.where;
2957
3275
  if (!where || typeof where !== "object" || Array.isArray(where)) {
2958
3276
  throw new PolicyError(
2959
- `prisma-guard: Cannot verify scope \u2014 missing FK fields (${missingFks.join(", ")}) and findUnique args.where is not a valid object.`
3277
+ `prisma-guard: Cannot verify scope on model "${model}" for ${operation} \u2014 missing FK fields (${missingFks.join(", ")}) in result and args.where is not a valid object. Ensure the where clause is a plain object so the verification query can re-fetch FK values.`
2960
3278
  );
2961
3279
  }
2962
3280
  let verifyResult;
2963
3281
  try {
2964
3282
  verifyResult = await query({ where, select: buildFkSelect(fks) });
2965
3283
  } catch (err) {
3284
+ log.warn(
3285
+ `prisma-guard: Scope verification re-query failed for ${operation} on model "${model}": ${err?.message ?? String(err)}`
3286
+ );
2966
3287
  throw new PolicyError(
2967
- `prisma-guard: Scope verification query failed for findUnique: ${err?.message ?? String(err)}`
3288
+ `prisma-guard: Scope verification re-query failed for ${operation} on model "${model}". The re-query to fetch FK fields (${fks.join(", ")}) returned a database error. Check server logs for the underlying cause.`
2968
3289
  );
2969
3290
  }
2970
3291
  if (verifyResult === null) {
2971
- throw new PolicyError("prisma-guard: Scope verification query returned null for an existing findUnique result");
3292
+ throw new PolicyError(
3293
+ `prisma-guard: Scope verification re-query on model "${model}" returned null for an existing ${operation} result. The record may have been deleted between the original query and verification.`
3294
+ );
2972
3295
  }
2973
3296
  if (typeof verifyResult !== "object" || verifyResult === null) {
2974
- throw new PolicyError("prisma-guard: Scope verification result must be an object");
3297
+ throw new PolicyError(
3298
+ `prisma-guard: Scope verification re-query on model "${model}" returned a non-object result.`
3299
+ );
2975
3300
  }
2976
3301
  verifyObj = verifyResult;
2977
3302
  }
@@ -2979,12 +3304,14 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log)
2979
3304
  const [fk, value] = Object.entries(condition)[0];
2980
3305
  if (!(fk in verifyObj)) {
2981
3306
  throw new PolicyError(
2982
- `prisma-guard: Cannot verify scope on "${fk}" \u2014 field not present in verification result. Ensure FK fields are selectable.`
3307
+ `prisma-guard: Cannot verify scope on model "${model}" \u2014 FK field "${fk}" not present in verification result. Ensure "${fk}" is selectable on model "${model}" (not excluded by select or field-level access).`
2983
3308
  );
2984
3309
  }
2985
3310
  if (!looseEqual(verifyObj[fk], value, log, fk)) {
2986
3311
  if (operation === "findUniqueOrThrow") {
2987
- throw new PolicyError("Record not accessible in current scope");
3312
+ throw new PolicyError(
3313
+ `Record on model "${model}" not accessible in current scope`
3314
+ );
2988
3315
  }
2989
3316
  return null;
2990
3317
  }
@@ -3689,11 +4016,22 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDef
3689
4016
  forced
3690
4017
  };
3691
4018
  }
3692
- function validateAndMergeData(bodyData, cached, method) {
4019
+ function validateAndMergeData(bodyData, cached, method, modelName) {
3693
4020
  if (bodyData === void 0 || bodyData === null) {
3694
4021
  throw new ShapeError(`${method} requires "data" in request body`);
3695
4022
  }
3696
- const validated = cached.schema.parse(bodyData);
4023
+ let validated;
4024
+ try {
4025
+ validated = cached.schema.parse(bodyData);
4026
+ } catch (err) {
4027
+ if (err instanceof ShapeError)
4028
+ throw err;
4029
+ if (err && typeof err === "object" && "issues" in err) {
4030
+ const context = modelName ? `Invalid data for ${method} on model "${modelName}"` : `Invalid data for ${method}`;
4031
+ throw new ShapeError(`${context}: ${formatZodError(err)}`, { cause: err });
4032
+ }
4033
+ throw err;
4034
+ }
3697
4035
  return { ...validated, ...deepClone(cached.forced) };
3698
4036
  }
3699
4037
  function hasDataRefines(dataConfig) {
@@ -3763,14 +4101,26 @@ function resolveShape(input, body, contextFn, caller) {
3763
4101
  );
3764
4102
  }
3765
4103
  if (typeof caller !== "string") {
4104
+ if ("default" in namedMap) {
4105
+ const shapeOrFn2 = namedMap["default"];
4106
+ const wasDynamic2 = typeof shapeOrFn2 === "function";
4107
+ const shape2 = wasDynamic2 ? resolveDynamicShape(shapeOrFn2, contextFn) : shapeOrFn2;
4108
+ return { shape: shape2, body: parsed, matchedKey: "default", wasDynamic: wasDynamic2 };
4109
+ }
3766
4110
  const patterns2 = Object.keys(namedMap);
3767
4111
  throw new CallerError(
3768
- `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.`
4112
+ `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, or add a "default" variant.`
3769
4113
  );
3770
4114
  }
3771
4115
  const patterns = Object.keys(namedMap);
3772
4116
  const matched = matchCallerPattern(patterns, caller);
3773
4117
  if (!matched) {
4118
+ if ("default" in namedMap) {
4119
+ const shapeOrFn2 = namedMap["default"];
4120
+ const wasDynamic2 = typeof shapeOrFn2 === "function";
4121
+ const shape2 = wasDynamic2 ? resolveDynamicShape(shapeOrFn2, contextFn) : shapeOrFn2;
4122
+ return { shape: shape2, body: parsed, matchedKey: "default", wasDynamic: wasDynamic2 };
4123
+ }
3774
4124
  throw new CallerError(
3775
4125
  `Unknown caller: "${caller}". Allowed: ${patterns.map((k) => `"${k}"`).join(", ")}`
3776
4126
  );
@@ -3801,17 +4151,6 @@ var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
3801
4151
  "updateManyAndReturn"
3802
4152
  ]);
3803
4153
  var BATCH_CREATE_METHODS = /* @__PURE__ */ new Set(["createMany", "createManyAndReturn"]);
3804
- function normalizeUniqueWhere(where) {
3805
- const result = {};
3806
- for (const [key, value] of Object.entries(where)) {
3807
- if (key !== "AND" && key !== "OR" && key !== "NOT" && isPlainObject(value) && "equals" in value && Object.keys(value).length === 1) {
3808
- result[key] = value.equals;
3809
- } else {
3810
- result[key] = value;
3811
- }
3812
- }
3813
- return result;
3814
- }
3815
4154
  function buildDefaultSelectInput(config) {
3816
4155
  const result = {};
3817
4156
  for (const [key, value] of Object.entries(config)) {
@@ -4031,6 +4370,36 @@ function createModelGuardExtension(config) {
4031
4370
  return;
4032
4371
  validateUniqueEquality(modelName, shape.where, method, uniqueMap, typeMap);
4033
4372
  }
4373
+ function validateUniqueWhereShapeConfig(modelName, where, method) {
4374
+ const constraints = uniqueMap[modelName];
4375
+ if (!constraints || constraints.length === 0)
4376
+ return;
4377
+ const equalityFields = /* @__PURE__ */ new Set();
4378
+ for (const [key, value] of Object.entries(where)) {
4379
+ if (key === "AND" || key === "OR" || key === "NOT")
4380
+ continue;
4381
+ if (value === true) {
4382
+ equalityFields.add(key);
4383
+ continue;
4384
+ }
4385
+ if (isPlainObject(value) && "equals" in value) {
4386
+ equalityFields.add(key);
4387
+ continue;
4388
+ }
4389
+ if (value !== null && value !== void 0) {
4390
+ equalityFields.add(key);
4391
+ }
4392
+ }
4393
+ const valid = constraints.some(
4394
+ (constraint) => constraint.every((field) => equalityFields.has(field))
4395
+ );
4396
+ if (!valid) {
4397
+ const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
4398
+ throw new ShapeError(
4399
+ `${method} on model "${modelName}" requires where to cover a unique constraint with equality operators only: ${constraintDesc}`
4400
+ );
4401
+ }
4402
+ }
4034
4403
  function createGuardedMethods(modelName, modelDelegate, input, explicitCaller) {
4035
4404
  function callDelegate(method, args) {
4036
4405
  if (typeof modelDelegate[method] !== "function") {
@@ -4053,6 +4422,7 @@ function createModelGuardExtension(config) {
4053
4422
  const dataSchemaCache = /* @__PURE__ */ new Map();
4054
4423
  const whereBuiltCache = /* @__PURE__ */ new Map();
4055
4424
  const projectionCache = /* @__PURE__ */ new Map();
4425
+ const uniqueWhereCache = /* @__PURE__ */ new Map();
4056
4426
  function getReadShape(method, queryShape, matchedKey, wasDynamic) {
4057
4427
  if (!wasDynamic) {
4058
4428
  const cacheKey = `${method}\0${matchedKey}`;
@@ -4110,6 +4480,21 @@ function createModelGuardExtension(config) {
4110
4480
  }
4111
4481
  return queryBuilder.buildWhereSchema(modelName, whereConfig);
4112
4482
  }
4483
+ function getUniqueWhereBuilt(whereConfig, matchedKey, wasDynamic) {
4484
+ if (!wasDynamic) {
4485
+ const cacheKey = `unique\0${matchedKey}`;
4486
+ const cached = uniqueWhereCache.get(cacheKey);
4487
+ if (cached)
4488
+ return cached;
4489
+ const built = queryBuilder.buildUniqueWhereSchema(
4490
+ modelName,
4491
+ whereConfig
4492
+ );
4493
+ uniqueWhereCache.set(cacheKey, built);
4494
+ return built;
4495
+ }
4496
+ return queryBuilder.buildUniqueWhereSchema(modelName, whereConfig);
4497
+ }
4113
4498
  function buildProjectionSchema(shape) {
4114
4499
  const schemaFields = {};
4115
4500
  let forcedIncludeTree = {};
@@ -4189,7 +4574,15 @@ function createModelGuardExtension(config) {
4189
4574
  } else {
4190
4575
  projectionBody = buildDefaultProjectionBody(shape);
4191
4576
  }
4192
- const validated = projection.zodSchema.parse(projectionBody);
4577
+ let validated;
4578
+ try {
4579
+ validated = projection.zodSchema.parse(projectionBody);
4580
+ } catch (err) {
4581
+ wrapParseError(
4582
+ err,
4583
+ `Invalid select/include projection on model "${modelName}" for ${method}`
4584
+ );
4585
+ }
4193
4586
  if (Object.keys(projection.forcedIncludeTree).length > 0) {
4194
4587
  applyForcedTree(validated, "include", projection.forcedIncludeTree);
4195
4588
  }
@@ -4226,7 +4619,11 @@ function createModelGuardExtension(config) {
4226
4619
  }
4227
4620
  sanitizedWhere = w;
4228
4621
  }
4229
- validatedWhere = built.schema.parse(sanitizedWhere);
4622
+ try {
4623
+ validatedWhere = built.schema.parse(sanitizedWhere);
4624
+ } catch (err) {
4625
+ wrapParseError(err, `Invalid "where" clause on model "${modelName}"`);
4626
+ }
4230
4627
  } else if (bodyWhere !== void 0) {
4231
4628
  if (bodyWhere === null || !isPlainObject(bodyWhere) || Object.keys(bodyWhere).length > 0) {
4232
4629
  let hasOnlyForcedKeys = false;
@@ -4256,7 +4653,68 @@ function createModelGuardExtension(config) {
4256
4653
  wasDynamic
4257
4654
  );
4258
4655
  if (Object.keys(where).length === 0) {
4259
- throw new ShapeError(`${method} requires a where condition`);
4656
+ const expectedFields = shape.where ? Object.keys(shape.where).join(", ") : "none defined";
4657
+ throw new ShapeError(
4658
+ `${method} on model "${modelName}" requires a where condition. Expected fields: ${expectedFields}`
4659
+ );
4660
+ }
4661
+ return where;
4662
+ }
4663
+ function buildUniqueWhereFromShape(shape, bodyWhere, matchedKey, wasDynamic) {
4664
+ if (!shape.where)
4665
+ return {};
4666
+ const built = getUniqueWhereBuilt(shape.where, matchedKey, wasDynamic);
4667
+ let result = {};
4668
+ if (built.schema) {
4669
+ if (bodyWhere !== void 0 && bodyWhere !== null) {
4670
+ if (!isPlainObject(bodyWhere)) {
4671
+ throw new ShapeError(
4672
+ `Invalid "where" on model "${modelName}": unique where must be a plain object`
4673
+ );
4674
+ }
4675
+ const sanitized = { ...bodyWhere };
4676
+ for (const key of built.forcedOnlyKeys) {
4677
+ delete sanitized[key];
4678
+ }
4679
+ try {
4680
+ result = built.schema.parse(sanitized);
4681
+ } catch (err) {
4682
+ const allowedFields = Object.keys(shape.where).join(", ");
4683
+ wrapParseError(
4684
+ err,
4685
+ `Invalid unique "where" on model "${modelName}". Allowed fields: ${allowedFields}`
4686
+ );
4687
+ }
4688
+ }
4689
+ } else if (bodyWhere !== void 0 && bodyWhere !== null) {
4690
+ if (isPlainObject(bodyWhere)) {
4691
+ const hasOnlyForcedKeys = built.forcedOnlyKeys.size > 0 && Object.keys(bodyWhere).every((k) => built.forcedOnlyKeys.has(k));
4692
+ if (!hasOnlyForcedKeys && Object.keys(bodyWhere).length > 0) {
4693
+ throw new ShapeError(
4694
+ `Unique where on model "${modelName}" contains only forced values. Client where input is not accepted.`
4695
+ );
4696
+ }
4697
+ }
4698
+ }
4699
+ for (const [key, value] of Object.entries(built.forced)) {
4700
+ result[key] = value;
4701
+ }
4702
+ return result;
4703
+ }
4704
+ function requireUniqueWhere(shape, bodyWhere, method, matchedKey, wasDynamic) {
4705
+ const where = buildUniqueWhereFromShape(
4706
+ shape,
4707
+ bodyWhere,
4708
+ matchedKey,
4709
+ wasDynamic
4710
+ );
4711
+ if (Object.keys(where).length === 0) {
4712
+ const constraints = uniqueMap[modelName];
4713
+ const constraintDesc = constraints ? constraints.map((c) => `(${c.join(", ")})`).join(" | ") : "unknown";
4714
+ const expectedFields = shape.where ? Object.keys(shape.where).join(", ") : "none defined";
4715
+ throw new ShapeError(
4716
+ `${method} on model "${modelName}" requires a unique where condition. Unique constraints: ${constraintDesc}. Shape allows: ${expectedFields}`
4717
+ );
4260
4718
  }
4261
4719
  return where;
4262
4720
  }
@@ -4299,7 +4757,7 @@ function createModelGuardExtension(config) {
4299
4757
  );
4300
4758
  const isUnique = UNIQUE_READ_METHODS.has(method);
4301
4759
  const effectiveBody = buildEffectiveReadBody(resolved);
4302
- const args = applyBuiltShape(built, effectiveBody, isUnique);
4760
+ const args = applyBuiltShape(built, effectiveBody, isUnique, modelName);
4303
4761
  if (isUnique && args.where) {
4304
4762
  validateResolvedUniqueWhere(
4305
4763
  modelName,
@@ -4355,7 +4813,8 @@ function createModelGuardExtension(config) {
4355
4813
  const data = validateAndMergeData(
4356
4814
  resolved.body.data,
4357
4815
  dataSchema,
4358
- method
4816
+ method,
4817
+ modelName
4359
4818
  );
4360
4819
  args = { data };
4361
4820
  } else {
@@ -4364,7 +4823,7 @@ function createModelGuardExtension(config) {
4364
4823
  if (resolved.body.data.length === 0)
4365
4824
  throw new ShapeError(`${method} received empty data array`);
4366
4825
  const data = resolved.body.data.map(
4367
- (item) => validateAndMergeData(item, dataSchema, method)
4826
+ (item) => validateAndMergeData(item, dataSchema, method, modelName)
4368
4827
  );
4369
4828
  args = { data };
4370
4829
  }
@@ -4409,7 +4868,6 @@ function createModelGuardExtension(config) {
4409
4868
  `Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
4410
4869
  );
4411
4870
  }
4412
- maybeValidateUniqueWhere(modelName, resolved.shape, method);
4413
4871
  const dataSchema = getDataSchema(
4414
4872
  "update",
4415
4873
  resolved.shape.data,
@@ -4419,30 +4877,40 @@ function createModelGuardExtension(config) {
4419
4877
  const data = validateAndMergeData(
4420
4878
  resolved.body.data,
4421
4879
  dataSchema,
4422
- method
4423
- );
4424
- let where = isUniqueWhere ? requireWhere(
4425
- resolved.shape,
4426
- resolved.body.where,
4427
4880
  method,
4428
- true,
4429
- resolved.matchedKey,
4430
- resolved.wasDynamic
4431
- ) : buildWhereFromShape(
4432
- resolved.shape,
4433
- resolved.body.where,
4434
- false,
4435
- resolved.matchedKey,
4436
- resolved.wasDynamic
4881
+ modelName
4437
4882
  );
4438
- if (isBulk && Object.keys(where).length === 0) {
4439
- throw new ShapeError(
4440
- `${method} requires at least one where condition`
4441
- );
4442
- }
4883
+ let where;
4443
4884
  if (isUniqueWhere) {
4885
+ if (resolved.shape.where) {
4886
+ validateUniqueWhereShapeConfig(
4887
+ modelName,
4888
+ resolved.shape.where,
4889
+ method
4890
+ );
4891
+ }
4892
+ where = requireUniqueWhere(
4893
+ resolved.shape,
4894
+ resolved.body.where,
4895
+ method,
4896
+ resolved.matchedKey,
4897
+ resolved.wasDynamic
4898
+ );
4444
4899
  validateResolvedUniqueWhere(modelName, where, method, uniqueMap);
4445
- where = normalizeUniqueWhere(where);
4900
+ } else {
4901
+ maybeValidateUniqueWhere(modelName, resolved.shape, method);
4902
+ where = buildWhereFromShape(
4903
+ resolved.shape,
4904
+ resolved.body.where,
4905
+ false,
4906
+ resolved.matchedKey,
4907
+ resolved.wasDynamic
4908
+ );
4909
+ if (isBulk && Object.keys(where).length === 0) {
4910
+ throw new ShapeError(
4911
+ `${method} requires at least one where condition`
4912
+ );
4913
+ }
4446
4914
  }
4447
4915
  const args = { data, where };
4448
4916
  if (supportsProjection) {
@@ -4480,29 +4948,37 @@ function createModelGuardExtension(config) {
4480
4948
  `Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
4481
4949
  );
4482
4950
  }
4483
- maybeValidateUniqueWhere(modelName, resolved.shape, method);
4484
- let where = isUniqueWhere ? requireWhere(
4485
- resolved.shape,
4486
- resolved.body.where,
4487
- method,
4488
- true,
4489
- resolved.matchedKey,
4490
- resolved.wasDynamic
4491
- ) : buildWhereFromShape(
4492
- resolved.shape,
4493
- resolved.body.where,
4494
- false,
4495
- resolved.matchedKey,
4496
- resolved.wasDynamic
4497
- );
4498
- if (isBulk && Object.keys(where).length === 0) {
4499
- throw new ShapeError(
4500
- `${method} requires at least one where condition`
4501
- );
4502
- }
4951
+ let where;
4503
4952
  if (isUniqueWhere) {
4953
+ if (resolved.shape.where) {
4954
+ validateUniqueWhereShapeConfig(
4955
+ modelName,
4956
+ resolved.shape.where,
4957
+ method
4958
+ );
4959
+ }
4960
+ where = requireUniqueWhere(
4961
+ resolved.shape,
4962
+ resolved.body.where,
4963
+ method,
4964
+ resolved.matchedKey,
4965
+ resolved.wasDynamic
4966
+ );
4504
4967
  validateResolvedUniqueWhere(modelName, where, method, uniqueMap);
4505
- where = normalizeUniqueWhere(where);
4968
+ } else {
4969
+ maybeValidateUniqueWhere(modelName, resolved.shape, method);
4970
+ where = buildWhereFromShape(
4971
+ resolved.shape,
4972
+ resolved.body.where,
4973
+ false,
4974
+ resolved.matchedKey,
4975
+ resolved.wasDynamic
4976
+ );
4977
+ if (isBulk && Object.keys(where).length === 0) {
4978
+ throw new ShapeError(
4979
+ `${method} requires at least one where condition`
4980
+ );
4981
+ }
4506
4982
  }
4507
4983
  const args = { where };
4508
4984
  if (supportsProjection) {
@@ -4546,7 +5022,11 @@ function createModelGuardExtension(config) {
4546
5022
  ALLOWED_BODY_KEYS_UPSERT,
4547
5023
  "upsert"
4548
5024
  );
4549
- maybeValidateUniqueWhere(modelName, resolved.shape, "upsert");
5025
+ validateUniqueWhereShapeConfig(
5026
+ modelName,
5027
+ resolved.shape.where,
5028
+ "upsert"
5029
+ );
4550
5030
  const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
4551
5031
  validateCreateCompleteness(
4552
5032
  modelName,
@@ -4564,7 +5044,8 @@ function createModelGuardExtension(config) {
4564
5044
  const createData = validateAndMergeData(
4565
5045
  resolved.body.create,
4566
5046
  createDataSchema,
4567
- "upsert (create)"
5047
+ "upsert (create)",
5048
+ modelName
4568
5049
  );
4569
5050
  const updateDataSchema = getDataSchema(
4570
5051
  "update",
@@ -4575,19 +5056,19 @@ function createModelGuardExtension(config) {
4575
5056
  const updateData = validateAndMergeData(
4576
5057
  resolved.body.update,
4577
5058
  updateDataSchema,
4578
- "upsert (update)"
5059
+ "upsert (update)",
5060
+ modelName
4579
5061
  );
4580
- const where = requireWhere(
5062
+ const where = requireUniqueWhere(
4581
5063
  resolved.shape,
4582
5064
  resolved.body.where,
4583
5065
  "upsert",
4584
- true,
4585
5066
  resolved.matchedKey,
4586
5067
  resolved.wasDynamic
4587
5068
  );
4588
5069
  validateResolvedUniqueWhere(modelName, where, "upsert", uniqueMap);
4589
5070
  const args = {
4590
- where: normalizeUniqueWhere(where),
5071
+ where,
4591
5072
  create: createData,
4592
5073
  update: updateData
4593
5074
  };