prisma-guard 1.15.0 → 1.17.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.
@@ -84,10 +84,7 @@ function isJsonSafe(value) {
84
84
  return true;
85
85
  }
86
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
- );
87
+ var decimalStringSchema = z.string().refine((s) => DECIMAL_REGEX.test(s), "Invalid decimal string");
91
88
  var decimalObjectSchema = z.custom(
92
89
  (v) => v !== null && typeof v === "object" && typeof v.toFixed === "function" && typeof v.toNumber === "function",
93
90
  "Expected Decimal-compatible object"
@@ -113,18 +110,39 @@ function createScalarBase(strictDecimal) {
113
110
  z.string().regex(/^-?\d+$/).transform((v) => BigInt(v))
114
111
  ]),
115
112
  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
- ])
113
+ DateTime: () => z.union([z.date(), z.string().datetime({ offset: true })]).pipe(z.coerce.date()),
114
+ Json: () => z.unknown().refine(
115
+ isJsonSafe,
116
+ "Value must be JSON-serializable (no undefined, functions, symbols, class instances, NaN, Infinity, or circular references)"
117
+ ),
118
+ Bytes: () => z.union([z.string(), z.custom((v) => v instanceof Uint8Array)])
125
119
  };
126
120
  }
127
121
  var SCALAR_BASE = createScalarBase(false);
122
+ function wrapWithInputCoercion(fieldType, isList, schema) {
123
+ let itemCoercion = null;
124
+ switch (fieldType) {
125
+ case "String":
126
+ itemCoercion = z.union([z.string(), z.number().transform(String)]);
127
+ break;
128
+ case "Int":
129
+ itemCoercion = z.union([
130
+ z.number().int(),
131
+ z.string().regex(/^-?\d+$/).transform(Number)
132
+ ]);
133
+ break;
134
+ case "Float":
135
+ itemCoercion = z.union([
136
+ z.number(),
137
+ z.string().regex(/^-?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/).transform(Number)
138
+ ]);
139
+ break;
140
+ default:
141
+ return schema;
142
+ }
143
+ const coercion = isList ? z.array(itemCoercion) : itemCoercion;
144
+ return coercion.pipe(schema);
145
+ }
128
146
 
129
147
  // src/runtime/schema-builder.ts
130
148
  import { z as z3 } from "zod";
@@ -173,19 +191,65 @@ function coerceToArray(value) {
173
191
 
174
192
  // src/runtime/zod-type-map.ts
175
193
  var SCALAR_OPERATORS = {
176
- String: /* @__PURE__ */ new Set(["equals", "not", "contains", "startsWith", "endsWith", "in", "notIn", "gt", "gte", "lt", "lte"]),
194
+ String: /* @__PURE__ */ new Set([
195
+ "equals",
196
+ "not",
197
+ "contains",
198
+ "startsWith",
199
+ "endsWith",
200
+ "in",
201
+ "notIn",
202
+ "gt",
203
+ "gte",
204
+ "lt",
205
+ "lte"
206
+ ]),
177
207
  Int: /* @__PURE__ */ new Set(["equals", "not", "gt", "gte", "lt", "lte", "in", "notIn"]),
178
208
  Float: /* @__PURE__ */ new Set(["equals", "not", "gt", "gte", "lt", "lte", "in", "notIn"]),
179
209
  Decimal: /* @__PURE__ */ new Set(["equals", "not", "gt", "gte", "lt", "lte", "in", "notIn"]),
180
210
  BigInt: /* @__PURE__ */ new Set(["equals", "not", "gt", "gte", "lt", "lte", "in", "notIn"]),
181
211
  Boolean: /* @__PURE__ */ new Set(["equals", "not"]),
182
212
  DateTime: /* @__PURE__ */ new Set(["equals", "not", "gt", "gte", "lt", "lte", "in", "notIn"]),
183
- Bytes: /* @__PURE__ */ new Set([])
213
+ Bytes: /* @__PURE__ */ new Set([]),
214
+ Json: /* @__PURE__ */ new Set([
215
+ "equals",
216
+ "not",
217
+ "path",
218
+ "string_contains",
219
+ "string_starts_with",
220
+ "string_ends_with",
221
+ "array_contains",
222
+ "array_starts_with",
223
+ "array_ends_with"
224
+ ])
184
225
  };
185
- var SCALAR_LIST_OPERATORS = /* @__PURE__ */ new Set(["has", "hasEvery", "hasSome", "isEmpty", "equals"]);
226
+ var SCALAR_LIST_OPERATORS = /* @__PURE__ */ new Set([
227
+ "has",
228
+ "hasEvery",
229
+ "hasSome",
230
+ "isEmpty",
231
+ "equals"
232
+ ]);
186
233
  var ENUM_OPERATORS = /* @__PURE__ */ new Set(["equals", "not", "in", "notIn"]);
187
234
  var NUMERIC_TYPES = /* @__PURE__ */ new Set(["Int", "Float", "Decimal", "BigInt"]);
188
- var COMPARABLE_TYPES = /* @__PURE__ */ new Set(["Int", "Float", "Decimal", "BigInt", "String", "DateTime"]);
235
+ var COMPARABLE_TYPES = /* @__PURE__ */ new Set([
236
+ "Int",
237
+ "Float",
238
+ "Decimal",
239
+ "BigInt",
240
+ "String",
241
+ "DateTime"
242
+ ]);
243
+ var JSON_STRING_OPERATORS = /* @__PURE__ */ new Set([
244
+ "string_contains",
245
+ "string_starts_with",
246
+ "string_ends_with"
247
+ ]);
248
+ var JSON_ARRAY_OPERATORS = /* @__PURE__ */ new Set([
249
+ "array_contains",
250
+ "array_starts_with",
251
+ "array_ends_with"
252
+ ]);
189
253
  function getSupportedOperators(fieldMeta) {
190
254
  if (fieldMeta.isList)
191
255
  return [...SCALAR_LIST_OPERATORS];
@@ -220,7 +284,9 @@ function createBaseType(fieldMeta, enumMap, scalarBase) {
220
284
  }
221
285
  function createScalarListOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
222
286
  if (!SCALAR_LIST_OPERATORS.has(operator)) {
223
- throw new ShapeError(`Operator "${operator}" not supported for scalar list fields`);
287
+ throw new ShapeError(
288
+ `Operator "${operator}" not supported for scalar list fields`
289
+ );
224
290
  }
225
291
  if (operator === "isEmpty") {
226
292
  return z2.boolean();
@@ -236,9 +302,32 @@ function createScalarListOperatorSchema(fieldMeta, operator, enumMap, scalarBase
236
302
  }
237
303
  return z2.preprocess(coerceToArray, z2.array(itemBase));
238
304
  }
305
+ function createJsonOperatorSchema(fieldMeta, operator) {
306
+ const jsonValue = z2.unknown();
307
+ if (operator === "equals" || operator === "not") {
308
+ return !fieldMeta.isRequired ? z2.union([jsonValue, z2.null()]) : jsonValue;
309
+ }
310
+ if (operator === "path") {
311
+ return z2.preprocess(coerceToArray, z2.array(z2.string()).min(1));
312
+ }
313
+ if (JSON_STRING_OPERATORS.has(operator)) {
314
+ return z2.string();
315
+ }
316
+ if (JSON_ARRAY_OPERATORS.has(operator)) {
317
+ return jsonValue;
318
+ }
319
+ throw new ShapeError(
320
+ `Operator "${operator}" not supported for Json fields`
321
+ );
322
+ }
239
323
  function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
240
324
  if (fieldMeta.isList) {
241
- return createScalarListOperatorSchema(fieldMeta, operator, enumMap, scalarBase);
325
+ return createScalarListOperatorSchema(
326
+ fieldMeta,
327
+ operator,
328
+ enumMap,
329
+ scalarBase
330
+ );
242
331
  }
243
332
  if (fieldMeta.isEnum) {
244
333
  const values = enumMap[fieldMeta.type];
@@ -246,7 +335,9 @@ function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
246
335
  throw new ShapeError(`Unknown enum: ${fieldMeta.type}`);
247
336
  }
248
337
  if (!ENUM_OPERATORS.has(operator)) {
249
- throw new ShapeError(`Operator "${operator}" not supported for enum fields`);
338
+ throw new ShapeError(
339
+ `Operator "${operator}" not supported for enum fields`
340
+ );
250
341
  }
251
342
  const enumSchema = z2.enum(values);
252
343
  if (operator === "equals" || operator === "not") {
@@ -255,29 +346,43 @@ function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
255
346
  const itemSchema = !fieldMeta.isRequired ? z2.union([enumSchema, z2.null()]) : enumSchema;
256
347
  return z2.preprocess(coerceToArray, z2.array(itemSchema));
257
348
  }
349
+ if (fieldMeta.type === "Json") {
350
+ const supportedOps2 = SCALAR_OPERATORS["Json"];
351
+ if (!supportedOps2 || !supportedOps2.has(operator)) {
352
+ throw new ShapeError(
353
+ `Operator "${operator}" not supported for type "Json"`
354
+ );
355
+ }
356
+ return createJsonOperatorSchema(fieldMeta, operator);
357
+ }
258
358
  const supportedOps = SCALAR_OPERATORS[fieldMeta.type];
259
359
  if (!supportedOps) {
260
360
  throw new ShapeError(`Unknown scalar type for operator: ${fieldMeta.type}`);
261
361
  }
262
362
  if (supportedOps.size === 0) {
263
- throw new ShapeError(`Type "${fieldMeta.type}" does not support filter operators`);
363
+ throw new ShapeError(
364
+ `Type "${fieldMeta.type}" does not support filter operators`
365
+ );
264
366
  }
265
367
  if (!supportedOps.has(operator)) {
266
- throw new ShapeError(`Operator "${operator}" not supported for type "${fieldMeta.type}"`);
368
+ throw new ShapeError(
369
+ `Operator "${operator}" not supported for type "${fieldMeta.type}"`
370
+ );
267
371
  }
268
372
  const factory = scalarBase[fieldMeta.type];
269
373
  if (!factory) {
270
374
  throw new ShapeError(`Unknown scalar type: ${fieldMeta.type}`);
271
375
  }
272
376
  const scalar = factory();
377
+ const coerced = wrapWithInputCoercion(fieldMeta.type, false, scalar);
273
378
  if (operator === "equals" || operator === "not") {
274
- return !fieldMeta.isRequired ? z2.union([scalar, z2.null()]) : scalar;
379
+ return !fieldMeta.isRequired ? z2.union([coerced, z2.null()]) : coerced;
275
380
  }
276
381
  if (operator === "in" || operator === "notIn") {
277
- const itemSchema = !fieldMeta.isRequired ? z2.union([scalar, z2.null()]) : scalar;
382
+ const itemSchema = !fieldMeta.isRequired ? z2.union([coerced, z2.null()]) : coerced;
278
383
  return z2.preprocess(coerceToArray, z2.array(itemSchema));
279
384
  }
280
- return scalar;
385
+ return coerced;
281
386
  }
282
387
 
283
388
  // src/runtime/schema-builder.ts
@@ -327,6 +432,9 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
327
432
  );
328
433
  }
329
434
  }
435
+ if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
436
+ result = wrapWithInputCoercion(fieldMeta.type, fieldMeta.isList, result);
437
+ }
330
438
  lruSet(chainCache, cacheKey, result, DEFAULT_MAX_CACHE);
331
439
  return result;
332
440
  }
@@ -359,9 +467,13 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
359
467
  if (!modelFields[name])
360
468
  throw new ShapeError(`Unknown field "${name}" on model "${model}"`);
361
469
  if (modelFields[name].isRelation)
362
- throw new ShapeError(`Field "${name}" cannot be used in input schema (relation field)`);
470
+ throw new ShapeError(
471
+ `Field "${name}" cannot be used in input schema (relation field)`
472
+ );
363
473
  if (modelFields[name].isUpdatedAt)
364
- throw new ShapeError(`Field "${name}" cannot be used in input schema (updatedAt field)`);
474
+ throw new ShapeError(
475
+ `Field "${name}" cannot be used in input schema (updatedAt field)`
476
+ );
365
477
  }
366
478
  fieldNames = fieldNames.filter((n) => opts.pick.includes(n));
367
479
  } else if (opts.omit) {
@@ -435,7 +547,9 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
435
547
  }
436
548
  const effectiveMaxDepth = maxDepth ?? opts.maxDepth ?? DEFAULT_MAX_DEPTH;
437
549
  if (depth > effectiveMaxDepth) {
438
- throw new ShapeError(`Maximum include depth (${effectiveMaxDepth}) exceeded`);
550
+ throw new ShapeError(
551
+ `Maximum include depth (${effectiveMaxDepth}) exceeded`
552
+ );
439
553
  }
440
554
  const modelFields = typeMap[model];
441
555
  if (!modelFields)
@@ -446,7 +560,9 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
446
560
  if (!modelFields[name])
447
561
  throw new ShapeError(`Unknown field "${name}" on model "${model}"`);
448
562
  if (modelFields[name].isRelation && !includeKeys.has(name)) {
449
- throw new ShapeError(`Field "${name}" is a relation on model "${model}". Use include: { ${name}: ... } instead of pick.`);
563
+ throw new ShapeError(
564
+ `Field "${name}" is a relation on model "${model}". Use include: { ${name}: ... } instead of pick.`
565
+ );
450
566
  }
451
567
  }
452
568
  }
@@ -479,10 +595,14 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
479
595
  if (!fieldMeta)
480
596
  throw new ShapeError(`Unknown field "${relName}" on model "${model}"`);
481
597
  if (!fieldMeta.isRelation)
482
- throw new ShapeError(`Field "${relName}" is not a relation on model "${model}"`);
598
+ throw new ShapeError(
599
+ `Field "${relName}" is not a relation on model "${model}"`
600
+ );
483
601
  const relatedModel = fieldMeta.type;
484
602
  if (!typeMap[relatedModel]) {
485
- throw new ShapeError(`Related model "${relatedModel}" not found in type map`);
603
+ throw new ShapeError(
604
+ `Related model "${relatedModel}" not found in type map`
605
+ );
486
606
  }
487
607
  let relSchema = buildModelSchema(
488
608
  relatedModel,
@@ -511,11 +631,17 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
511
631
  const countFields = {};
512
632
  for (const relName of Object.keys(opts._count)) {
513
633
  if (!modelFields[relName])
514
- throw new ShapeError(`Unknown field "${relName}" on model "${model}" in _count`);
634
+ throw new ShapeError(
635
+ `Unknown field "${relName}" on model "${model}" in _count`
636
+ );
515
637
  if (!modelFields[relName].isRelation)
516
- throw new ShapeError(`Field "${relName}" is not a relation on model "${model}" in _count`);
638
+ throw new ShapeError(
639
+ `Field "${relName}" is not a relation on model "${model}" in _count`
640
+ );
517
641
  if (!modelFields[relName].isList)
518
- throw new ShapeError(`Field "${relName}" is a to-one relation on model "${model}" in _count. Only to-many relations support _count.`);
642
+ throw new ShapeError(
643
+ `Field "${relName}" is a to-one relation on model "${model}" in _count. Only to-many relations support _count.`
644
+ );
519
645
  countFields[relName] = z3.number().int().min(0);
520
646
  }
521
647
  schemaMap["_count"] = z3.object(countFields);
@@ -527,7 +653,12 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
527
653
  }
528
654
  return schema;
529
655
  }
530
- return { buildFieldSchema, buildBaseFieldSchema, buildInputSchema, buildModelSchema };
656
+ return {
657
+ buildFieldSchema,
658
+ buildBaseFieldSchema,
659
+ buildInputSchema,
660
+ buildModelSchema
661
+ };
531
662
  }
532
663
 
533
664
  // src/runtime/query-builder.ts
@@ -981,13 +1112,18 @@ function validateUniqueEquality(model, where, method, uniqueMap, typeMap) {
981
1112
  }
982
1113
 
983
1114
  // src/runtime/query-builder-where.ts
984
- var UNSUPPORTED_WHERE_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
1115
+ var UNSUPPORTED_WHERE_TYPES = /* @__PURE__ */ new Set(["Bytes"]);
985
1116
  var STRING_MODE_OPS = /* @__PURE__ */ new Set([
986
1117
  "contains",
987
1118
  "startsWith",
988
1119
  "endsWith",
989
1120
  "equals"
990
1121
  ]);
1122
+ var JSON_STRING_MODE_OPS = /* @__PURE__ */ new Set([
1123
+ "string_contains",
1124
+ "string_starts_with",
1125
+ "string_ends_with"
1126
+ ]);
991
1127
  var MAX_WHERE_DEPTH = 10;
992
1128
  function safeStringify(v) {
993
1129
  if (typeof v === "bigint")
@@ -1083,6 +1219,13 @@ function mergeRelationForcedMaps(target, source) {
1083
1219
  }
1084
1220
  }
1085
1221
  }
1222
+ function isModeCompatibleOp(fieldType, op) {
1223
+ if (fieldType === "String")
1224
+ return STRING_MODE_OPS.has(op);
1225
+ if (fieldType === "Json")
1226
+ return JSON_STRING_MODE_OPS.has(op);
1227
+ return false;
1228
+ }
1086
1229
  function createWhereBuilder(typeMap, enumMap, scalarBase) {
1087
1230
  function buildWhereSchema(model, whereConfig, depth) {
1088
1231
  const currentDepth = depth ?? 0;
@@ -1285,7 +1428,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1285
1428
  const opSchemas = {};
1286
1429
  const fieldForced = {};
1287
1430
  let hasClientOps = false;
1288
- let hasStringModeOp = false;
1431
+ let hasModeCompatibleOp = false;
1289
1432
  const clientOpKeys = [];
1290
1433
  let modeConfigValue = void 0;
1291
1434
  let hasModeConfig = false;
@@ -1304,8 +1447,8 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1304
1447
  ).optional();
1305
1448
  hasClientOps = true;
1306
1449
  clientOpKeys.push(op);
1307
- if (fieldMeta.type === "String" && !fieldMeta.isList && STRING_MODE_OPS.has(op)) {
1308
- hasStringModeOp = true;
1450
+ if (!fieldMeta.isList && isModeCompatibleOp(fieldMeta.type, op)) {
1451
+ hasModeCompatibleOp = true;
1309
1452
  }
1310
1453
  } else {
1311
1454
  const actualOpValue = isForcedValue(opValue) ? opValue.value : opValue;
@@ -1324,15 +1467,15 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1324
1467
  );
1325
1468
  }
1326
1469
  fieldForced[op] = parsed;
1327
- if (fieldMeta.type === "String" && !fieldMeta.isList && STRING_MODE_OPS.has(op)) {
1328
- hasStringModeOp = true;
1470
+ if (!fieldMeta.isList && isModeCompatibleOp(fieldMeta.type, op)) {
1471
+ hasModeCompatibleOp = true;
1329
1472
  }
1330
1473
  }
1331
1474
  }
1332
1475
  if (!hasClientOps && Object.keys(fieldForced).length === 0) {
1333
1476
  if (hasModeConfig) {
1334
1477
  throw new ShapeError(
1335
- `Where field "${fieldName}" on model "${model}" has "mode" but no operators. Add at least one operator (contains, startsWith, endsWith, equals).`
1478
+ `Where field "${fieldName}" on model "${model}" has "mode" but no operators. Add at least one compatible operator.`
1336
1479
  );
1337
1480
  }
1338
1481
  throw new ShapeError(
@@ -1340,9 +1483,9 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1340
1483
  );
1341
1484
  }
1342
1485
  if (hasModeConfig) {
1343
- if (!hasStringModeOp) {
1486
+ if (!hasModeCompatibleOp) {
1344
1487
  throw new ShapeError(
1345
- `"mode" on where field "${fieldName}" on model "${model}" requires a compatible String operator (contains, startsWith, endsWith, equals)`
1488
+ `"mode" on where field "${fieldName}" on model "${model}" requires a compatible operator (String: contains, startsWith, endsWith, equals; Json: string_contains, string_starts_with, string_ends_with)`
1346
1489
  );
1347
1490
  }
1348
1491
  if (modeConfigValue === true) {
@@ -1360,7 +1503,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1360
1503
  }
1361
1504
  fieldForced["mode"] = parsed;
1362
1505
  }
1363
- } else if (hasStringModeOp) {
1506
+ } else if (hasModeCompatibleOp) {
1364
1507
  opSchemas["mode"] = z4.enum(["default", "insensitive"]).optional();
1365
1508
  }
1366
1509
  if (hasClientOps) {
@@ -1373,7 +1516,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1373
1516
  message: `At least one operator required for where field "${fieldName}"`
1374
1517
  }
1375
1518
  );
1376
- if ("equals" in opSchemas) {
1519
+ if ("equals" in opSchemas && fieldMeta.type !== "Json") {
1377
1520
  const equalsBase = createOperatorSchema(
1378
1521
  fieldMeta,
1379
1522
  "equals",