prisma-guard 1.28.0 → 1.29.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.
@@ -212,7 +212,7 @@ function createScalarBase(strictDecimal) {
212
212
  return {
213
213
  String: () => z.string(),
214
214
  Int: () => z.number().int(),
215
- Float: () => z.number(),
215
+ Float: () => z.number().finite(),
216
216
  Decimal: createDecimalFactory(strictDecimal),
217
217
  BigInt: () => z.union([
218
218
  z.bigint(),
@@ -353,6 +353,19 @@ function requireConfigTrue(config, context) {
353
353
  }
354
354
  }
355
355
  }
356
+ function requirePlainObjectConfig(value, message) {
357
+ if (!isPlainObject(value)) {
358
+ throw new ShapeError(message);
359
+ }
360
+ return value;
361
+ }
362
+ function assertAllowedKeys(value, allowed, makeError) {
363
+ for (const key of Object.keys(value)) {
364
+ if (!allowed.has(key)) {
365
+ throw new ShapeError(makeError(key));
366
+ }
367
+ }
368
+ }
356
369
 
357
370
  // src/runtime/zod-type-map.ts
358
371
  var SCALAR_OPERATORS = {
@@ -416,24 +429,12 @@ var JSON_ARRAY_OPERATORS = /* @__PURE__ */ new Set([
416
429
  "array_starts_with",
417
430
  "array_ends_with"
418
431
  ]);
419
- function getSupportedOperators(input, isList = false, isEnum = false) {
420
- let fieldType;
421
- let list;
422
- let enumField;
423
- if (typeof input === "string") {
424
- fieldType = input;
425
- list = isList;
426
- enumField = isEnum;
427
- } else {
428
- fieldType = input.type;
429
- list = input.isList;
430
- enumField = input.isEnum === true;
431
- }
432
- if (list)
432
+ function getSupportedOperators(fieldMeta) {
433
+ if (fieldMeta.isList)
433
434
  return [...SCALAR_LIST_OPERATORS];
434
- if (enumField)
435
+ if (fieldMeta.isEnum === true)
435
436
  return [...ENUM_OPERATORS];
436
- const ops = SCALAR_OPERATORS[fieldType];
437
+ const ops = SCALAR_OPERATORS[fieldMeta.type];
437
438
  if (!ops)
438
439
  return [];
439
440
  return [...ops];
@@ -998,6 +999,7 @@ function validateContext(ctx) {
998
999
 
999
1000
  // src/runtime/query-builder-where.ts
1000
1001
  import { z as z5 } from "zod";
1002
+ import { inspect } from "util";
1001
1003
 
1002
1004
  // src/shared/deep-clone.ts
1003
1005
  function deepClone(value) {
@@ -1278,15 +1280,10 @@ function applyBuiltShape(built, body, isUniqueMethod, modelName) {
1278
1280
  try {
1279
1281
  validated = built.zodSchema.parse(parseable);
1280
1282
  } catch (err) {
1281
- if (err instanceof ShapeError)
1282
- throw err;
1283
- if (err && typeof err === "object" && "issues" in err) {
1284
- const context = modelName ? `Invalid query on model "${modelName}"` : "Invalid query";
1285
- throw new ShapeError(`${context}: ${formatZodError(err)}`, {
1286
- cause: err
1287
- });
1288
- }
1289
- throw err;
1283
+ wrapParseError(
1284
+ err,
1285
+ modelName ? `Invalid query on model "${modelName}"` : "Invalid query"
1286
+ );
1290
1287
  }
1291
1288
  if (hasWhereForced(built.forcedWhere)) {
1292
1289
  validated.where = isUniqueMethod ? mergeUniqueWhereForced(
@@ -1603,37 +1600,8 @@ var JSON_STRING_MODE_OPS = /* @__PURE__ */ new Set([
1603
1600
  ]);
1604
1601
  var NEGATIVE_RELATION_OPS = /* @__PURE__ */ new Set(["none", "isNot"]);
1605
1602
  var MAX_WHERE_DEPTH = 10;
1606
- function safeStringify(value) {
1607
- if (typeof value === "bigint")
1608
- return `${value}n`;
1609
- if (typeof value === "undefined")
1610
- return "undefined";
1611
- if (typeof value === "function")
1612
- return "[function]";
1613
- if (typeof value === "symbol")
1614
- return value.toString();
1615
- const seen = /* @__PURE__ */ new WeakSet();
1616
- try {
1617
- const json = JSON.stringify(value, (_key, current) => {
1618
- if (typeof current === "bigint")
1619
- return `${current}n`;
1620
- if (typeof current === "undefined")
1621
- return "[undefined]";
1622
- if (typeof current === "function")
1623
- return "[function]";
1624
- if (typeof current === "symbol")
1625
- return current.toString();
1626
- if (current && typeof current === "object") {
1627
- if (seen.has(current))
1628
- return "[Circular]";
1629
- seen.add(current);
1630
- }
1631
- return current;
1632
- });
1633
- return json === void 0 ? String(value) : json;
1634
- } catch {
1635
- return String(value);
1636
- }
1603
+ function formatValue(value) {
1604
+ return inspect(value, { depth: 3, breakLength: Infinity });
1637
1605
  }
1638
1606
  function mergeScalarConditions(target, source) {
1639
1607
  for (const [field, ops] of Object.entries(source)) {
@@ -1654,7 +1622,7 @@ function mergeScalarConditions(target, source) {
1654
1622
  const existingVal = existing[op];
1655
1623
  if (!deepEqual(existingVal, val)) {
1656
1624
  throw new ShapeError(
1657
- `Conflicting forced where values for "${field}.${op}": shape defines both ${safeStringify(existingVal)} and ${safeStringify(val)}`
1625
+ `Conflicting forced where values for "${field}.${op}": shape defines both ${formatValue(existingVal)} and ${formatValue(val)}`
1658
1626
  );
1659
1627
  }
1660
1628
  }
@@ -2492,10 +2460,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
2492
2460
  `${fieldMeta.type} field "${fieldName}" cannot be used in having`
2493
2461
  );
2494
2462
  }
2495
- const allowedOps = getSupportedOperators(
2496
- fieldMeta.type,
2497
- fieldMeta.isList
2498
- );
2463
+ const allowedOps = getSupportedOperators(fieldMeta);
2499
2464
  const opSchemas = {};
2500
2465
  for (const op of allowedOps) {
2501
2466
  opSchemas[op] = createOperatorSchema(
@@ -2672,20 +2637,27 @@ function buildDefaultProjectionBody(shape) {
2672
2637
 
2673
2638
  // src/runtime/query-builder-projection.ts
2674
2639
  var KNOWN_NESTED_KEYS = {
2675
- include: /* @__PURE__ */ new Set(["where", "include", "select", "orderBy", "cursor", "take", "skip"]),
2676
- select: /* @__PURE__ */ new Set(["select", "include", "where", "orderBy", "cursor", "take", "skip"])
2640
+ include: /* @__PURE__ */ new Set([
2641
+ "where",
2642
+ "include",
2643
+ "select",
2644
+ "orderBy",
2645
+ "cursor",
2646
+ "take",
2647
+ "skip"
2648
+ ]),
2649
+ select: /* @__PURE__ */ new Set([
2650
+ "select",
2651
+ "include",
2652
+ "where",
2653
+ "orderBy",
2654
+ "cursor",
2655
+ "take",
2656
+ "skip"
2657
+ ])
2677
2658
  };
2678
2659
  var KNOWN_COUNT_SELECT_ENTRY_KEYS = /* @__PURE__ */ new Set(["where"]);
2679
2660
  var MAX_PROJECTION_DEPTH = 10;
2680
- function validateNestedKeys(keys, allowed, context) {
2681
- for (const key of keys) {
2682
- if (!allowed.has(key)) {
2683
- throw new ShapeError(
2684
- `Unknown key "${key}" in ${context}. Allowed: ${[...allowed].join(", ")}`
2685
- );
2686
- }
2687
- }
2688
- }
2689
2661
  function hasDefinedKeys(v) {
2690
2662
  return Object.values(v).some((value) => value !== void 0);
2691
2663
  }
@@ -2698,6 +2670,20 @@ function wrapRelationSchema(nestedObj, skeleton) {
2698
2670
  collapsed
2699
2671
  );
2700
2672
  }
2673
+ function buildRelationWhere(relatedType, whereConfig, context, buildWhereSchema) {
2674
+ if (Object.keys(whereConfig).length === 0) {
2675
+ throw new ShapeError(
2676
+ `Empty "where" in ${context}. Define at least one field.`
2677
+ );
2678
+ }
2679
+ const { schema, forced } = buildWhereSchema(relatedType, whereConfig);
2680
+ if (!schema && !hasWhereForced(forced)) {
2681
+ throw new ShapeError(
2682
+ `"where" in ${context} produced no schema and no forced conditions. Define at least one field.`
2683
+ );
2684
+ }
2685
+ return { schema, forced };
2686
+ }
2701
2687
  function createProjectionBuilder(typeMap, _enumMap, deps) {
2702
2688
  function buildIncludeCountSchema(model, config) {
2703
2689
  const modelFields = typeMap[model];
@@ -2711,13 +2697,11 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2711
2697
  `Invalid _count config on model "${model}". Expected true or { select: { ... } }`
2712
2698
  );
2713
2699
  }
2714
- for (const key of Object.keys(config)) {
2715
- if (key !== "select") {
2716
- throw new ShapeError(
2717
- `Unknown key "${key}" in _count config on model "${model}". Only "select" is allowed.`
2718
- );
2719
- }
2720
- }
2700
+ assertAllowedKeys(
2701
+ config,
2702
+ /* @__PURE__ */ new Set(["select"]),
2703
+ (key) => `Unknown key "${key}" in _count config on model "${model}". Only "select" is allowed.`
2704
+ );
2721
2705
  if (!isPlainObject(config.select)) {
2722
2706
  throw new ShapeError(
2723
2707
  `Invalid _count.select on model "${model}". Expected a plain object with relation field keys.`
@@ -2734,11 +2718,17 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2734
2718
  for (const [fieldName, fieldConfig] of Object.entries(selectObj)) {
2735
2719
  const fieldMeta = modelFields[fieldName];
2736
2720
  if (!fieldMeta)
2737
- throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in _count.select`);
2721
+ throw new ShapeError(
2722
+ `Unknown field "${fieldName}" on model "${model}" in _count.select`
2723
+ );
2738
2724
  if (!fieldMeta.isRelation)
2739
- throw new ShapeError(`Field "${fieldName}" is not a relation on model "${model}" in _count.select`);
2725
+ throw new ShapeError(
2726
+ `Field "${fieldName}" is not a relation on model "${model}" in _count.select`
2727
+ );
2740
2728
  if (!fieldMeta.isList)
2741
- throw new ShapeError(`Field "${fieldName}" is a to-one relation on model "${model}" in _count.select. Only to-many relations support _count.`);
2729
+ throw new ShapeError(
2730
+ `Field "${fieldName}" is a to-one relation on model "${model}" in _count.select. Only to-many relations support _count.`
2731
+ );
2742
2732
  if (fieldConfig === true) {
2743
2733
  countSelectFields[fieldName] = z7.literal(true).optional();
2744
2734
  continue;
@@ -2753,16 +2743,18 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2753
2743
  `Empty config for _count.select.${fieldName} on model "${model}". Use true or { where: { ... } }.`
2754
2744
  );
2755
2745
  }
2756
- validateNestedKeys(
2757
- Object.keys(fieldConfig),
2746
+ assertAllowedKeys(
2747
+ fieldConfig,
2758
2748
  KNOWN_COUNT_SELECT_ENTRY_KEYS,
2759
- `_count.select.${fieldName} on model "${model}"`
2749
+ (key) => `Unknown key "${key}" in _count.select.${fieldName} on model "${model}". Allowed: ${[...KNOWN_COUNT_SELECT_ENTRY_KEYS].join(", ")}`
2760
2750
  );
2761
2751
  if (fieldConfig.where) {
2762
2752
  const relatedType = fieldMeta.type;
2763
- const { schema: whereSchema, forced } = deps.buildWhereSchema(
2753
+ const { schema: whereSchema, forced } = buildRelationWhere(
2764
2754
  relatedType,
2765
- fieldConfig.where
2755
+ fieldConfig.where,
2756
+ `_count.select.${fieldName} on model "${model}"`,
2757
+ deps.buildWhereSchema
2766
2758
  );
2767
2759
  const nestedSchemas = {};
2768
2760
  if (whereSchema)
@@ -2785,13 +2777,15 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2785
2777
  forcedCountWhere
2786
2778
  };
2787
2779
  }
2788
- function buildNestedRelSchemas(relatedType, config, depth) {
2780
+ function buildNestedRelSchemas(relatedType, config, depth, context) {
2789
2781
  const nestedSchemas = {};
2790
2782
  const relForced = {};
2791
2783
  if (config.where) {
2792
- const { schema: whereSchema, forced } = deps.buildWhereSchema(
2784
+ const { schema: whereSchema, forced } = buildRelationWhere(
2793
2785
  relatedType,
2794
- config.where
2786
+ config.where,
2787
+ context,
2788
+ deps.buildWhereSchema
2795
2789
  );
2796
2790
  if (whereSchema)
2797
2791
  nestedSchemas["where"] = whereSchema;
@@ -2799,7 +2793,12 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2799
2793
  relForced.where = forced;
2800
2794
  }
2801
2795
  if (config.include) {
2802
- const nested = buildProjectionSchema("include", relatedType, config.include, depth + 1);
2796
+ const nested = buildProjectionSchema(
2797
+ "include",
2798
+ relatedType,
2799
+ config.include,
2800
+ depth + 1
2801
+ );
2803
2802
  nestedSchemas["include"] = nested.schema;
2804
2803
  if (Object.keys(nested.forcedTree).length > 0)
2805
2804
  relForced.include = nested.forcedTree;
@@ -2809,7 +2808,12 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2809
2808
  }
2810
2809
  }
2811
2810
  if (config.select) {
2812
- const nested = buildProjectionSchema("select", relatedType, config.select, depth + 1);
2811
+ const nested = buildProjectionSchema(
2812
+ "select",
2813
+ relatedType,
2814
+ config.select,
2815
+ depth + 1
2816
+ );
2813
2817
  nestedSchemas["select"] = nested.schema;
2814
2818
  if (Object.keys(nested.forcedTree).length > 0)
2815
2819
  relForced.select = nested.forcedTree;
@@ -2819,15 +2823,26 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2819
2823
  }
2820
2824
  }
2821
2825
  if (config.orderBy) {
2822
- nestedSchemas["orderBy"] = deps.buildOrderBySchema(relatedType, config.orderBy);
2826
+ nestedSchemas["orderBy"] = deps.buildOrderBySchema(
2827
+ relatedType,
2828
+ config.orderBy
2829
+ );
2823
2830
  }
2824
2831
  if (config.cursor) {
2825
- nestedSchemas["cursor"] = deps.buildCursorSchema(relatedType, config.cursor);
2832
+ nestedSchemas["cursor"] = deps.buildCursorSchema(
2833
+ relatedType,
2834
+ config.cursor
2835
+ );
2826
2836
  }
2827
- if (config.take) {
2837
+ if ("take" in config && config.take !== void 0) {
2828
2838
  nestedSchemas["take"] = deps.buildTakeSchema(config.take);
2829
2839
  }
2830
- if (config.skip) {
2840
+ if ("skip" in config && config.skip !== void 0) {
2841
+ if (config.skip !== true) {
2842
+ throw new ShapeError(
2843
+ `Nested "skip" in ${context} must be true`
2844
+ );
2845
+ }
2831
2846
  nestedSchemas["skip"] = z7.number().int().min(0).optional();
2832
2847
  }
2833
2848
  return { nestedSchemas, relForced };
@@ -2853,28 +2868,43 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2853
2868
  let topLevelForcedCountWhere = {};
2854
2869
  for (const [fieldName, config] of Object.entries(projectionConfig)) {
2855
2870
  if (fieldName === "_count") {
2856
- const countResult = buildIncludeCountSchema(model, config);
2871
+ const countResult = buildIncludeCountSchema(
2872
+ model,
2873
+ config
2874
+ );
2857
2875
  fieldSchemas["_count"] = countResult.schema;
2858
2876
  topLevelForcedCountWhere = countResult.forcedCountWhere;
2859
2877
  continue;
2860
2878
  }
2861
2879
  const fieldMeta = modelFields[fieldName];
2862
2880
  if (!fieldMeta)
2863
- throw new ShapeError(`Unknown field "${fieldName}" on model "${model}"`);
2881
+ throw new ShapeError(
2882
+ `Unknown field "${fieldName}" on model "${model}"`
2883
+ );
2864
2884
  if (mode === "include" && !fieldMeta.isRelation) {
2865
- throw new ShapeError(`Field "${fieldName}" is not a relation on model "${model}"`);
2885
+ throw new ShapeError(
2886
+ `Field "${fieldName}" is not a relation on model "${model}"`
2887
+ );
2866
2888
  }
2867
2889
  if (config === true) {
2868
2890
  fieldSchemas[fieldName] = z7.literal(true).optional();
2869
2891
  continue;
2870
2892
  }
2871
2893
  if (mode === "select" && !fieldMeta.isRelation) {
2872
- throw new ShapeError(`Nested select args only valid for relations, not scalar "${fieldName}" on model "${model}"`);
2894
+ throw new ShapeError(
2895
+ `Nested select args only valid for relations, not scalar "${fieldName}" on model "${model}"`
2896
+ );
2873
2897
  }
2874
2898
  const contextLabel = `nested ${mode} for "${fieldName}" on model "${model}"`;
2875
- validateNestedKeys(Object.keys(config), allowedNestedKeys, contextLabel);
2899
+ assertAllowedKeys(
2900
+ config,
2901
+ allowedNestedKeys,
2902
+ (key) => `Unknown key "${key}" in ${contextLabel}. Allowed: ${[...allowedNestedKeys].join(", ")}`
2903
+ );
2876
2904
  if (config.select && config.include) {
2877
- throw new ShapeError(`Nested ${mode} for "${fieldName}" cannot define both "select" and "include".`);
2905
+ throw new ShapeError(
2906
+ `Nested ${mode} for "${fieldName}" cannot define both "select" and "include".`
2907
+ );
2878
2908
  }
2879
2909
  if (!fieldMeta.isList) {
2880
2910
  if (config.where || config.orderBy || config.cursor || config.take || config.skip) {
@@ -2883,7 +2913,12 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2883
2913
  );
2884
2914
  }
2885
2915
  }
2886
- const { nestedSchemas, relForced } = buildNestedRelSchemas(fieldMeta.type, config, currentDepth);
2916
+ const { nestedSchemas, relForced } = buildNestedRelSchemas(
2917
+ fieldMeta.type,
2918
+ config,
2919
+ currentDepth,
2920
+ contextLabel
2921
+ );
2887
2922
  const nestedObj = z7.object(nestedSchemas).strict();
2888
2923
  fieldSchemas[fieldName] = wrapRelationSchema(
2889
2924
  nestedObj,
@@ -2908,27 +2943,33 @@ function createProjectionBuilder(typeMap, _enumMap, deps) {
2908
2943
  function buildSelectSchema(model, selectConfig, depth) {
2909
2944
  return buildProjectionSchema("select", model, selectConfig, depth);
2910
2945
  }
2911
- return { buildIncludeSchema, buildSelectSchema, buildIncludeCountSchema, buildProjectionSchema };
2946
+ return {
2947
+ buildIncludeSchema,
2948
+ buildSelectSchema,
2949
+ buildIncludeCountSchema,
2950
+ buildProjectionSchema
2951
+ };
2912
2952
  }
2913
2953
 
2914
2954
  // src/shared/operation-shape-keys.ts
2915
2955
  var OPERATION_SHAPE_KEYS = {
2916
2956
  findMany: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
2957
+ findManyPaginated: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
2917
2958
  findFirst: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
2918
2959
  findFirstOrThrow: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
2919
2960
  findUnique: ["where", "include", "select"],
2920
2961
  findUniqueOrThrow: ["where", "include", "select"],
2921
2962
  count: ["where", "select", "cursor", "orderBy", "skip", "take"],
2922
2963
  aggregate: ["where", "orderBy", "cursor", "take", "skip", "_count", "_avg", "_sum", "_min", "_max"],
2923
- groupBy: ["where", "by", "having", "_count", "_avg", "_sum", "_min", "_max", "orderBy", "take", "skip"],
2924
- create: ["data", "select", "include"],
2925
- createMany: ["data"],
2926
- createManyAndReturn: ["data", "select", "include"],
2927
- update: ["data", "where", "select", "include"],
2928
- updateMany: ["data", "where"],
2929
- updateManyAndReturn: ["data", "where", "select", "include"],
2930
- upsert: ["where", "create", "update", "select", "include"],
2931
- delete: ["where", "select", "include"],
2964
+ groupBy: ["where", "orderBy", "by", "having", "take", "skip", "_count", "_avg", "_sum", "_min", "_max"],
2965
+ create: ["data", "include", "select"],
2966
+ createMany: ["data", "skipDuplicates"],
2967
+ createManyAndReturn: ["data", "select", "include", "skipDuplicates"],
2968
+ update: ["where", "data", "include", "select"],
2969
+ updateMany: ["where", "data"],
2970
+ updateManyAndReturn: ["where", "data", "select", "include"],
2971
+ upsert: ["where", "create", "update", "include", "select"],
2972
+ delete: ["where", "include", "select"],
2932
2973
  deleteMany: ["where"]
2933
2974
  };
2934
2975
  var READ_METHOD_ALLOWED_ARGS = {
@@ -3240,7 +3281,17 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
3240
3281
  let forcedIncludeCountWhere = {};
3241
3282
  let forcedSelectCountWhere = {};
3242
3283
  if (shape.where) {
3284
+ if (Object.keys(shape.where).length === 0) {
3285
+ throw new ShapeError(
3286
+ `Empty "where" in shape for model "${model}" method "${method}". Define at least one field.`
3287
+ );
3288
+ }
3243
3289
  const builtWhere = UNIQUE_WHERE_METHODS.has(method) ? whereBuilder.buildUniqueWhereSchema(model, shape.where) : whereBuilder.buildWhereSchema(model, shape.where);
3290
+ if (!builtWhere.schema && !hasWhereForced(builtWhere.forced)) {
3291
+ throw new ShapeError(
3292
+ `"where" in shape for model "${model}" method "${method}" produced no schema and no forced conditions.`
3293
+ );
3294
+ }
3244
3295
  if (builtWhere.schema) {
3245
3296
  schemaFields.where = builtWhere.schema;
3246
3297
  }
@@ -4042,27 +4093,19 @@ function validateRelationOpKeys(actual, opKey, model, field, opLabel) {
4042
4093
  const allowed = RELATION_OP_ALLOWED_KEYS[opKey];
4043
4094
  if (!allowed)
4044
4095
  return;
4045
- for (const key of Object.keys(actual)) {
4046
- if (!allowed.has(key)) {
4047
- throw new ShapeError(
4048
- `Unknown key "${key}" in ${opLabel} config on "${model}.${field}". Allowed: ${[...allowed].join(", ")}`
4049
- );
4050
- }
4051
- }
4096
+ assertAllowedKeys(
4097
+ actual,
4098
+ allowed,
4099
+ (key) => `Unknown key "${key}" in ${opLabel} config on "${model}.${field}". Allowed: ${[...allowed].join(", ")}`
4100
+ );
4052
4101
  }
4053
4102
  function validateAllowedKeys(value, allowed, method, kind) {
4054
- for (const key of Object.keys(value)) {
4055
- if (!allowed.has(key)) {
4056
- if (kind === "body") {
4057
- throw new ShapeError(
4058
- `Unexpected key "${key}" in ${method} body. Allowed keys: ${[...allowed].join(", ")}`
4059
- );
4060
- }
4061
- throw new ShapeError(
4062
- `Shape key "${key}" not valid for ${method}. Allowed: ${[...allowed].join(", ")}`
4063
- );
4103
+ assertAllowedKeys(value, allowed, (key) => {
4104
+ if (kind === "body") {
4105
+ return `Unexpected key "${key}" in ${method} body. Allowed keys: ${[...allowed].join(", ")}`;
4064
4106
  }
4065
- }
4107
+ return `Shape key "${key}" not valid for ${method}. Allowed: ${[...allowed].join(", ")}`;
4108
+ });
4066
4109
  }
4067
4110
  function validateCreateCompleteness(modelName, dataConfig, typeMap, scopeFks, zodDefaults) {
4068
4111
  const modelFields = typeMap[modelName];
@@ -4149,18 +4192,10 @@ function buildNestedDataSchema(model, config, mode, typeMap, schemaBuilder) {
4149
4192
  }
4150
4193
  return z10.object(fieldSchemas).strict();
4151
4194
  }
4152
- function requirePlainObjectConfig(value, message) {
4153
- if (!isPlainObject(value))
4154
- throw new ShapeError(message);
4155
- return value;
4156
- }
4157
4195
  function requireNestedObject(cfg, key, message) {
4158
- const v = cfg[key];
4159
- if (!v || !isPlainObject(v))
4160
- throw new ShapeError(message);
4161
- return v;
4196
+ return requirePlainObjectConfig(cfg[key], message);
4162
4197
  }
4163
- function buildUniqueSelector(ctx, cfg, context) {
4198
+ function buildRelatedUniqueSelector(ctx, cfg, context) {
4164
4199
  return buildUniqueSelectorSchema(
4165
4200
  ctx.model,
4166
4201
  ctx.fieldName,
@@ -4173,7 +4208,7 @@ function buildUniqueSelector(ctx, cfg, context) {
4173
4208
  context
4174
4209
  );
4175
4210
  }
4176
- function buildNestedData(ctx, cfg, mode) {
4211
+ function buildRelatedNestedData(ctx, cfg, mode) {
4177
4212
  return buildNestedDataSchema(
4178
4213
  ctx.relatedModelName,
4179
4214
  cfg,
@@ -4187,7 +4222,7 @@ var handleConnect = (ctx) => {
4187
4222
  ctx.config,
4188
4223
  `connect config on "${ctx.model}.${ctx.fieldName}" must be an object of unique selectors`
4189
4224
  );
4190
- const schema = buildUniqueSelector(ctx, cfg, "connect");
4225
+ const schema = buildRelatedUniqueSelector(ctx, cfg, "connect");
4191
4226
  return wrapRelationOp(ctx.isList, schema);
4192
4227
  };
4193
4228
  var handleConnectOrCreate = (ctx) => {
@@ -4206,8 +4241,8 @@ var handleConnectOrCreate = (ctx) => {
4206
4241
  "create",
4207
4242
  `connectOrCreate on "${ctx.model}.${ctx.fieldName}" requires "create" object`
4208
4243
  );
4209
- const whereSchema = buildUniqueSelector(ctx, where, "connectOrCreate.where");
4210
- const createSchema = buildNestedData(ctx, create, "create");
4244
+ const whereSchema = buildRelatedUniqueSelector(ctx, where, "connectOrCreate.where");
4245
+ const createSchema = buildRelatedNestedData(ctx, create, "create");
4211
4246
  const cocSchema = z10.object({ where: whereSchema, create: createSchema }).strict();
4212
4247
  return wrapRelationOp(ctx.isList, cocSchema);
4213
4248
  };
@@ -4216,7 +4251,7 @@ var handleCreate = (ctx) => {
4216
4251
  ctx.config,
4217
4252
  `create config on "${ctx.model}.${ctx.fieldName}" must be an object of field names`
4218
4253
  );
4219
- const createSchema = buildNestedData(ctx, cfg, "create");
4254
+ const createSchema = buildRelatedNestedData(ctx, cfg, "create");
4220
4255
  return wrapRelationOp(ctx.isList, createSchema);
4221
4256
  };
4222
4257
  var handleCreateMany = (ctx) => {
@@ -4235,7 +4270,7 @@ var handleCreateMany = (ctx) => {
4235
4270
  "data",
4236
4271
  `createMany on "${ctx.model}.${ctx.fieldName}" requires "data" object`
4237
4272
  );
4238
- const dataSchema = buildNestedData(ctx, data, "create");
4273
+ const dataSchema = buildRelatedNestedData(ctx, data, "create");
4239
4274
  const cmSchemaFields = {
4240
4275
  data: z10.preprocess(coerceToArray, z10.array(dataSchema))
4241
4276
  };
@@ -4258,7 +4293,7 @@ var handleDisconnect = (ctx) => {
4258
4293
  `disconnect config on "${ctx.model}.${ctx.fieldName}" must be true (to-one) or an object of unique selectors`
4259
4294
  );
4260
4295
  }
4261
- const schema = buildUniqueSelector(ctx, ctx.config, "disconnect");
4296
+ const schema = buildRelatedUniqueSelector(ctx, ctx.config, "disconnect");
4262
4297
  if (ctx.isList)
4263
4298
  return wrapRelationOp(true, schema);
4264
4299
  return z10.union([z10.literal(true), schema]).optional();
@@ -4277,7 +4312,7 @@ var handleDelete = (ctx) => {
4277
4312
  `delete config on "${ctx.model}.${ctx.fieldName}" must be true (to-one) or an object of unique selectors`
4278
4313
  );
4279
4314
  }
4280
- const schema = buildUniqueSelector(ctx, ctx.config, "delete");
4315
+ const schema = buildRelatedUniqueSelector(ctx, ctx.config, "delete");
4281
4316
  if (ctx.isList)
4282
4317
  return wrapRelationOp(true, schema);
4283
4318
  return z10.union([z10.literal(true), schema]).optional();
@@ -4292,7 +4327,7 @@ var handleSet = (ctx) => {
4292
4327
  ctx.config,
4293
4328
  `set config on "${ctx.model}.${ctx.fieldName}" must be an object of unique selectors`
4294
4329
  );
4295
- const schema = buildUniqueSelector(ctx, cfg, "set");
4330
+ const schema = buildRelatedUniqueSelector(ctx, cfg, "set");
4296
4331
  return wrapRelationOp(true, schema);
4297
4332
  };
4298
4333
  var handleUpdate = (ctx) => {
@@ -4312,12 +4347,12 @@ var handleUpdate = (ctx) => {
4312
4347
  "data",
4313
4348
  `update on to-many "${ctx.model}.${ctx.fieldName}" requires "data" object`
4314
4349
  );
4315
- const whereSchema = buildUniqueSelector(ctx, where, "update.where");
4316
- const dataSchema2 = buildNestedData(ctx, data, "update");
4350
+ const whereSchema = buildRelatedUniqueSelector(ctx, where, "update.where");
4351
+ const dataSchema2 = buildRelatedNestedData(ctx, data, "update");
4317
4352
  const updateSchema = z10.object({ where: whereSchema, data: dataSchema2 }).strict();
4318
4353
  return wrapRelationOp(true, updateSchema);
4319
4354
  }
4320
- const dataSchema = buildNestedData(ctx, cfg, "update");
4355
+ const dataSchema = buildRelatedNestedData(ctx, cfg, "update");
4321
4356
  return dataSchema.optional();
4322
4357
  };
4323
4358
  var handleUpsert = (ctx) => {
@@ -4342,15 +4377,15 @@ var handleUpsert = (ctx) => {
4342
4377
  "update",
4343
4378
  `upsert on "${ctx.model}.${ctx.fieldName}" requires "update" object`
4344
4379
  );
4345
- const createSchema = buildNestedData(ctx, create, "create");
4346
- const updateSchema = buildNestedData(ctx, update, "update");
4380
+ const createSchema = buildRelatedNestedData(ctx, create, "create");
4381
+ const updateSchema = buildRelatedNestedData(ctx, update, "update");
4347
4382
  if (ctx.isList) {
4348
4383
  const where = requireNestedObject(
4349
4384
  cfg,
4350
4385
  "where",
4351
4386
  `upsert on to-many "${ctx.model}.${ctx.fieldName}" requires "where" object`
4352
4387
  );
4353
- const whereSchema = buildUniqueSelector(ctx, where, "upsert.where");
4388
+ const whereSchema = buildRelatedUniqueSelector(ctx, where, "upsert.where");
4354
4389
  const upsertSchema2 = z10.object({ where: whereSchema, create: createSchema, update: updateSchema }).strict();
4355
4390
  return wrapRelationOp(true, upsertSchema2);
4356
4391
  }
@@ -4361,7 +4396,7 @@ var handleUpsert = (ctx) => {
4361
4396
  `upsert on to-one "${ctx.model}.${ctx.fieldName}" has invalid "where": must be a plain object of unique selectors`
4362
4397
  );
4363
4398
  }
4364
- const whereSchema = buildUniqueSelector(ctx, cfg.where, "upsert.where");
4399
+ const whereSchema = buildRelatedUniqueSelector(ctx, cfg.where, "upsert.where");
4365
4400
  const upsertSchema2 = z10.object({ where: whereSchema, create: createSchema, update: updateSchema }).strict();
4366
4401
  return upsertSchema2.optional();
4367
4402
  }
@@ -4405,7 +4440,7 @@ var handleUpdateMany = (ctx) => {
4405
4440
  ctx.typeMap,
4406
4441
  ctx.schemaBuilder
4407
4442
  );
4408
- const dataSchema = buildNestedData(ctx, data, "update");
4443
+ const dataSchema = buildRelatedNestedData(ctx, data, "update");
4409
4444
  const umSchema = z10.object({ where: whereSchema, data: dataSchema }).strict();
4410
4445
  return wrapRelationOp(true, umSchema);
4411
4446
  };
@@ -4452,17 +4487,21 @@ function buildRelationWriteSchema(model, fieldName, relatedModelName, isList, co
4452
4487
  throw new ShapeError(
4453
4488
  `Unknown related model "${relatedModelName}" for field "${model}.${fieldName}"`
4454
4489
  );
4455
- for (const key of Object.keys(config)) {
4456
- if (!KNOWN_RELATION_WRITE_OPS.has(key)) {
4457
- throw new ShapeError(
4458
- `Unknown relation write operation "${key}" on "${model}.${fieldName}". Allowed: ${[...KNOWN_RELATION_WRITE_OPS].join(", ")}`
4459
- );
4460
- }
4490
+ assertAllowedKeys(
4491
+ config,
4492
+ KNOWN_RELATION_WRITE_OPS,
4493
+ (key) => `Unknown relation write operation "${key}" on "${model}.${fieldName}". Allowed: ${[...KNOWN_RELATION_WRITE_OPS].join(", ")}`
4494
+ );
4495
+ const definedEntries = Object.entries(config).filter(
4496
+ ([, opConfig]) => opConfig !== void 0
4497
+ );
4498
+ if (definedEntries.length === 0) {
4499
+ throw new ShapeError(
4500
+ `Empty relation write config on "${model}.${fieldName}". Define at least one operation: ${[...KNOWN_RELATION_WRITE_OPS].join(", ")}`
4501
+ );
4461
4502
  }
4462
4503
  const opSchemas = {};
4463
- for (const [op, opConfig] of Object.entries(config)) {
4464
- if (opConfig === void 0)
4465
- continue;
4504
+ for (const [op, opConfig] of definedEntries) {
4466
4505
  const handler = RELATION_OP_HANDLERS[op];
4467
4506
  opSchemas[op] = handler({
4468
4507
  model,
@@ -4479,7 +4518,7 @@ function buildRelationWriteSchema(model, fieldName, relatedModelName, isList, co
4479
4518
  }
4480
4519
  return z10.object(opSchemas).strict();
4481
4520
  }
4482
- function buildDataSchema(model, dataConfig, mode, typeMap, uniqueMap, enumMap, scalarBase, schemaBuilder, zodDefaults) {
4521
+ function buildDataSchema(model, dataConfig, mode, typeMap, uniqueMap, enumMap, scalarBase, schemaBuilder, zodDefaults, allowRelationWrites) {
4483
4522
  const modelFields = typeMap[model];
4484
4523
  if (!modelFields)
4485
4524
  throw new ShapeError(`Unknown model: ${model}`);
@@ -4509,6 +4548,11 @@ function buildDataSchema(model, dataConfig, mode, typeMap, uniqueMap, enumMap, s
4509
4548
  continue;
4510
4549
  }
4511
4550
  if (fieldMeta.isRelation) {
4551
+ if (!allowRelationWrites) {
4552
+ throw new ShapeError(
4553
+ `Field "${fieldName}" on model "${model}" is a relation. Relation writes are not supported for this method.`
4554
+ );
4555
+ }
4512
4556
  if (!isPlainObject(value)) {
4513
4557
  throw new ShapeError(
4514
4558
  `Relation field "${fieldName}" on model "${model}" requires a relation write config object`
@@ -4622,13 +4666,8 @@ function validateAndMergeData(bodyData, cached, method, modelName) {
4622
4666
  try {
4623
4667
  validated = cached.schema.parse(bodyData);
4624
4668
  } catch (err) {
4625
- if (err instanceof ShapeError)
4626
- throw err;
4627
- if (err && typeof err === "object" && "issues" in err) {
4628
- const context = modelName ? `Invalid data for ${method} on model "${modelName}"` : `Invalid data for ${method}`;
4629
- throw new ShapeError(`${context}: ${formatZodError(err)}`, { cause: err });
4630
- }
4631
- throw err;
4669
+ const context = modelName ? `Invalid data for ${method} on model "${modelName}"` : `Invalid data for ${method}`;
4670
+ wrapParseError(err, context);
4632
4671
  }
4633
4672
  return { ...validated, ...deepClone(cached.forced) };
4634
4673
  }
@@ -4783,6 +4822,8 @@ var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
4783
4822
  "updateManyAndReturn"
4784
4823
  ]);
4785
4824
  var BATCH_CREATE_METHODS = /* @__PURE__ */ new Set(["createMany", "createManyAndReturn"]);
4825
+ var RELATION_WRITE_CREATE_METHODS = /* @__PURE__ */ new Set(["create"]);
4826
+ var RELATION_WRITE_UPDATE_METHODS = /* @__PURE__ */ new Set(["update"]);
4786
4827
  var MAX_PROJECTION_WALK_DEPTH = 10;
4787
4828
  function walkForClientContent(obj, predicate, depth) {
4788
4829
  if (depth > MAX_PROJECTION_WALK_DEPTH)
@@ -4933,11 +4974,11 @@ function createModelGuardExtension(config) {
4933
4974
  )
4934
4975
  );
4935
4976
  }
4936
- function getDataSchema(mode, dataConfig, matchedKey, wasDynamic) {
4977
+ function getDataSchema(mode, dataConfig, matchedKey, wasDynamic, allowRelationWrites) {
4937
4978
  const skipCache = wasDynamic || hasDataRefines(dataConfig);
4938
4979
  return memoize(
4939
4980
  dataSchemaCache,
4940
- `${mode}\0${matchedKey}`,
4981
+ `${mode}\0${matchedKey}\0${allowRelationWrites ? "r" : "n"}`,
4941
4982
  skipCache,
4942
4983
  () => buildDataSchema(
4943
4984
  modelName,
@@ -4948,7 +4989,8 @@ function createModelGuardExtension(config) {
4948
4989
  enumMap,
4949
4990
  scalarBase,
4950
4991
  schemaBuilder,
4951
- zodDefaults
4992
+ zodDefaults,
4993
+ allowRelationWrites
4952
4994
  )
4953
4995
  );
4954
4996
  }
@@ -5206,6 +5248,39 @@ function createModelGuardExtension(config) {
5206
5248
  const defaultProjection = buildDefaultProjectionBody(resolved.shape);
5207
5249
  return { ...resolved.body, ...defaultProjection };
5208
5250
  }
5251
+ function makeResolveMethod() {
5252
+ const WRITE_KEYS = ["data", "create", "update"];
5253
+ const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
5254
+ return (body) => {
5255
+ const caller = resolveCaller();
5256
+ const resolved = resolveShape(input, body, contextFn, caller);
5257
+ for (const key of WRITE_KEYS) {
5258
+ if (hasOwn(resolved.shape, key)) {
5259
+ throw new ShapeError(
5260
+ `.resolve() is a read-only planning helper. Guard shape contains write key "${key}". Use the corresponding write method instead.`
5261
+ );
5262
+ }
5263
+ }
5264
+ for (const key of WRITE_KEYS) {
5265
+ if (hasOwn(resolved.body, key)) {
5266
+ throw new ShapeError(
5267
+ `.resolve() is a read-only planning helper. Request body contains write key "${key}".`
5268
+ );
5269
+ }
5270
+ }
5271
+ const effectiveReadBody = buildEffectiveReadBody({
5272
+ shape: resolved.shape,
5273
+ body: resolved.body
5274
+ });
5275
+ return {
5276
+ shape: resolved.shape,
5277
+ body: resolved.body,
5278
+ effectiveReadBody,
5279
+ matchedKey: resolved.matchedKey,
5280
+ wasDynamic: resolved.wasDynamic
5281
+ };
5282
+ };
5283
+ }
5209
5284
  function makeReadMethod(method) {
5210
5285
  return (body) => {
5211
5286
  const caller = resolveCaller();
@@ -5239,6 +5314,7 @@ function createModelGuardExtension(config) {
5239
5314
  const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
5240
5315
  const allowedBodyKeys = getAllowedBodyKeys(method, supportsProjection);
5241
5316
  const allowedShapeKeys = getAllowedShapeKeys(method, supportsProjection);
5317
+ const allowRelationWrites = RELATION_WRITE_CREATE_METHODS.has(method);
5242
5318
  return (body) => {
5243
5319
  const caller = resolveCaller();
5244
5320
  const resolved = resolveShape(input, body, contextFn, caller);
@@ -5264,7 +5340,8 @@ function createModelGuardExtension(config) {
5264
5340
  "create",
5265
5341
  resolved.shape.data,
5266
5342
  resolved.matchedKey,
5267
- resolved.wasDynamic
5343
+ resolved.wasDynamic,
5344
+ allowRelationWrites
5268
5345
  );
5269
5346
  let args;
5270
5347
  if (method === "create") {
@@ -5312,6 +5389,7 @@ function createModelGuardExtension(config) {
5312
5389
  const supportsProjection = PROJECTION_MUTATION_METHODS.has(method);
5313
5390
  const allowedBodyKeys = getAllowedBodyKeys(method, supportsProjection);
5314
5391
  const allowedShapeKeys = getAllowedShapeKeys(method, supportsProjection);
5392
+ const allowRelationWrites = RELATION_WRITE_UPDATE_METHODS.has(method);
5315
5393
  return (body) => {
5316
5394
  const caller = resolveCaller();
5317
5395
  const resolved = resolveShape(input, body, contextFn, caller);
@@ -5334,7 +5412,8 @@ function createModelGuardExtension(config) {
5334
5412
  "update",
5335
5413
  resolved.shape.data,
5336
5414
  resolved.matchedKey,
5337
- resolved.wasDynamic
5415
+ resolved.wasDynamic,
5416
+ allowRelationWrites
5338
5417
  );
5339
5418
  const data = validateAndMergeData(
5340
5419
  resolved.body.data,
@@ -5507,7 +5586,8 @@ function createModelGuardExtension(config) {
5507
5586
  "create",
5508
5587
  resolved.shape.create,
5509
5588
  `upsert:create\0${resolved.matchedKey}`,
5510
- resolved.wasDynamic
5589
+ resolved.wasDynamic,
5590
+ true
5511
5591
  );
5512
5592
  const createData = validateAndMergeData(
5513
5593
  resolved.body.create,
@@ -5519,7 +5599,8 @@ function createModelGuardExtension(config) {
5519
5599
  "update",
5520
5600
  resolved.shape.update,
5521
5601
  `upsert:update\0${resolved.matchedKey}`,
5522
- resolved.wasDynamic
5602
+ resolved.wasDynamic,
5603
+ true
5523
5604
  );
5524
5605
  const updateData = validateAndMergeData(
5525
5606
  resolved.body.update,
@@ -5552,6 +5633,7 @@ function createModelGuardExtension(config) {
5552
5633
  };
5553
5634
  }
5554
5635
  return {
5636
+ resolve: makeResolveMethod(),
5555
5637
  findMany: makeReadMethod("findMany"),
5556
5638
  findFirst: makeReadMethod("findFirst"),
5557
5639
  findFirstOrThrow: makeReadMethod("findFirstOrThrow"),