prisma-guard 1.19.0 → 1.20.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.
@@ -1420,13 +1420,6 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1420
1420
  }
1421
1421
  }
1422
1422
  function processScalarField(fieldName, operators, model, fieldMeta, fieldSchemas, scalarConditions) {
1423
- if (operators === true) {
1424
- const supportedOps = getSupportedOperators(fieldMeta);
1425
- const expanded = {};
1426
- for (const op of supportedOps)
1427
- expanded[op] = true;
1428
- operators = expanded;
1429
- }
1430
1423
  if (!isPlainObject(operators)) {
1431
1424
  throw new ShapeError(
1432
1425
  `Where config for scalar field "${fieldName}" on model "${model}" must be an object of operators`
@@ -1539,7 +1532,82 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
1539
1532
  scalarConditions[fieldName] = fieldForced;
1540
1533
  }
1541
1534
  }
1542
- return { buildWhereSchema };
1535
+ function buildUniqueWhereSchema(model, whereConfig) {
1536
+ const modelFields = typeMap[model];
1537
+ if (!modelFields)
1538
+ throw new ShapeError(`Unknown model: ${model}`);
1539
+ const fieldSchemas = {};
1540
+ const forced = {};
1541
+ const forcedOnlyKeys = /* @__PURE__ */ new Set();
1542
+ for (const [key, value] of Object.entries(whereConfig)) {
1543
+ if (COMBINATOR_KEYS.has(key)) {
1544
+ throw new ShapeError(
1545
+ `Combinator "${key}" is not supported in unique where for model "${model}"`
1546
+ );
1547
+ }
1548
+ const fieldMeta = modelFields[key];
1549
+ if (!fieldMeta)
1550
+ throw new ShapeError(`Unknown field "${key}" on model "${model}"`);
1551
+ if (fieldMeta.isRelation)
1552
+ throw new ShapeError(
1553
+ `Relation field "${key}" cannot be used in unique where for model "${model}"`
1554
+ );
1555
+ const base = createBaseType(fieldMeta, enumMap, scalarBase);
1556
+ let directSchema;
1557
+ if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
1558
+ directSchema = wrapWithInputCoercion(
1559
+ fieldMeta.type,
1560
+ fieldMeta.isList,
1561
+ base
1562
+ );
1563
+ } else {
1564
+ directSchema = base;
1565
+ }
1566
+ const equalsWrapper = z4.object({ equals: directSchema }).strict().transform((v) => v.equals);
1567
+ if (value === true) {
1568
+ fieldSchemas[key] = z4.union([directSchema, equalsWrapper]);
1569
+ continue;
1570
+ }
1571
+ if (isPlainObject(value)) {
1572
+ const keys = Object.keys(value);
1573
+ if (keys.length === 1 && keys[0] === "equals") {
1574
+ const equalsVal = value.equals;
1575
+ if (equalsVal === true) {
1576
+ fieldSchemas[key] = z4.union([directSchema, equalsWrapper]);
1577
+ } else {
1578
+ const actual2 = isForcedValue(equalsVal) ? equalsVal.value : equalsVal;
1579
+ try {
1580
+ forced[key] = directSchema.parse(actual2);
1581
+ } catch (err) {
1582
+ throw new ShapeError(
1583
+ `Invalid forced value for unique where "${model}.${key}": ${err.message}`
1584
+ );
1585
+ }
1586
+ forcedOnlyKeys.add(key);
1587
+ }
1588
+ continue;
1589
+ }
1590
+ throw new ShapeError(
1591
+ `Unique where field "${key}" on model "${model}" only accepts true or { equals: true/value }. Got operators: ${keys.join(", ")}`
1592
+ );
1593
+ }
1594
+ const actual = isForcedValue(value) ? value.value : value;
1595
+ try {
1596
+ forced[key] = directSchema.parse(actual);
1597
+ } catch (err) {
1598
+ throw new ShapeError(
1599
+ `Invalid forced value for unique where "${model}.${key}": ${err.message}`
1600
+ );
1601
+ }
1602
+ forcedOnlyKeys.add(key);
1603
+ }
1604
+ return {
1605
+ schema: Object.keys(fieldSchemas).length > 0 ? z4.object(fieldSchemas).strict() : null,
1606
+ forced,
1607
+ forcedOnlyKeys
1608
+ };
1609
+ }
1610
+ return { buildWhereSchema, buildUniqueWhereSchema };
1543
1611
  }
1544
1612
 
1545
1613
  // src/runtime/query-builder-args.ts
@@ -2625,6 +2693,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
2625
2693
  buildQuerySchema,
2626
2694
  buildShapeZodSchema,
2627
2695
  buildWhereSchema: whereBuilder.buildWhereSchema,
2696
+ buildUniqueWhereSchema: whereBuilder.buildUniqueWhereSchema,
2628
2697
  buildIncludeSchema: projectionBuilder.buildIncludeSchema,
2629
2698
  buildSelectSchema: projectionBuilder.buildSelectSchema
2630
2699
  };
@@ -3770,17 +3839,6 @@ var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
3770
3839
  "updateManyAndReturn"
3771
3840
  ]);
3772
3841
  var BATCH_CREATE_METHODS = /* @__PURE__ */ new Set(["createMany", "createManyAndReturn"]);
3773
- function normalizeUniqueWhere(where) {
3774
- const result = {};
3775
- for (const [key, value] of Object.entries(where)) {
3776
- if (key !== "AND" && key !== "OR" && key !== "NOT" && isPlainObject(value) && "equals" in value && Object.keys(value).length === 1) {
3777
- result[key] = value.equals;
3778
- } else {
3779
- result[key] = value;
3780
- }
3781
- }
3782
- return result;
3783
- }
3784
3842
  function buildDefaultSelectInput(config) {
3785
3843
  const result = {};
3786
3844
  for (const [key, value] of Object.entries(config)) {
@@ -4000,6 +4058,36 @@ function createModelGuardExtension(config) {
4000
4058
  return;
4001
4059
  validateUniqueEquality(modelName, shape.where, method, uniqueMap, typeMap);
4002
4060
  }
4061
+ function validateUniqueWhereShapeConfig(modelName, where, method) {
4062
+ const constraints = uniqueMap[modelName];
4063
+ if (!constraints || constraints.length === 0)
4064
+ return;
4065
+ const equalityFields = /* @__PURE__ */ new Set();
4066
+ for (const [key, value] of Object.entries(where)) {
4067
+ if (key === "AND" || key === "OR" || key === "NOT")
4068
+ continue;
4069
+ if (value === true) {
4070
+ equalityFields.add(key);
4071
+ continue;
4072
+ }
4073
+ if (isPlainObject(value) && "equals" in value) {
4074
+ equalityFields.add(key);
4075
+ continue;
4076
+ }
4077
+ if (value !== null && value !== void 0) {
4078
+ equalityFields.add(key);
4079
+ }
4080
+ }
4081
+ const valid = constraints.some(
4082
+ (constraint) => constraint.every((field) => equalityFields.has(field))
4083
+ );
4084
+ if (!valid) {
4085
+ const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
4086
+ throw new ShapeError(
4087
+ `${method} on model "${modelName}" requires where to cover a unique constraint with equality operators only: ${constraintDesc}`
4088
+ );
4089
+ }
4090
+ }
4003
4091
  function createGuardedMethods(modelName, modelDelegate, input, explicitCaller) {
4004
4092
  function callDelegate(method, args) {
4005
4093
  if (typeof modelDelegate[method] !== "function") {
@@ -4022,6 +4110,7 @@ function createModelGuardExtension(config) {
4022
4110
  const dataSchemaCache = /* @__PURE__ */ new Map();
4023
4111
  const whereBuiltCache = /* @__PURE__ */ new Map();
4024
4112
  const projectionCache = /* @__PURE__ */ new Map();
4113
+ const uniqueWhereCache = /* @__PURE__ */ new Map();
4025
4114
  function getReadShape(method, queryShape, matchedKey, wasDynamic) {
4026
4115
  if (!wasDynamic) {
4027
4116
  const cacheKey = `${method}\0${matchedKey}`;
@@ -4079,6 +4168,18 @@ function createModelGuardExtension(config) {
4079
4168
  }
4080
4169
  return queryBuilder.buildWhereSchema(modelName, whereConfig);
4081
4170
  }
4171
+ function getUniqueWhereBuilt(whereConfig, matchedKey, wasDynamic) {
4172
+ if (!wasDynamic) {
4173
+ const cacheKey = `unique\0${matchedKey}`;
4174
+ const cached = uniqueWhereCache.get(cacheKey);
4175
+ if (cached)
4176
+ return cached;
4177
+ const built = queryBuilder.buildUniqueWhereSchema(modelName, whereConfig);
4178
+ uniqueWhereCache.set(cacheKey, built);
4179
+ return built;
4180
+ }
4181
+ return queryBuilder.buildUniqueWhereSchema(modelName, whereConfig);
4182
+ }
4082
4183
  function buildProjectionSchema(shape) {
4083
4184
  const schemaFields = {};
4084
4185
  let forcedIncludeTree = {};
@@ -4229,6 +4330,49 @@ function createModelGuardExtension(config) {
4229
4330
  }
4230
4331
  return where;
4231
4332
  }
4333
+ function buildUniqueWhereFromShape(shape, bodyWhere, matchedKey, wasDynamic) {
4334
+ if (!shape.where)
4335
+ return {};
4336
+ const built = getUniqueWhereBuilt(shape.where, matchedKey, wasDynamic);
4337
+ let result = {};
4338
+ if (built.schema) {
4339
+ if (bodyWhere !== void 0 && bodyWhere !== null) {
4340
+ if (!isPlainObject(bodyWhere)) {
4341
+ throw new ShapeError("Unique where must be an object");
4342
+ }
4343
+ const sanitized = { ...bodyWhere };
4344
+ for (const key of built.forcedOnlyKeys) {
4345
+ delete sanitized[key];
4346
+ }
4347
+ result = built.schema.parse(sanitized);
4348
+ }
4349
+ } else if (bodyWhere !== void 0 && bodyWhere !== null) {
4350
+ if (isPlainObject(bodyWhere)) {
4351
+ const hasOnlyForcedKeys = built.forcedOnlyKeys.size > 0 && Object.keys(bodyWhere).every((k) => built.forcedOnlyKeys.has(k));
4352
+ if (!hasOnlyForcedKeys && Object.keys(bodyWhere).length > 0) {
4353
+ throw new ShapeError(
4354
+ "Unique where shape contains only forced values. Client where input is not accepted."
4355
+ );
4356
+ }
4357
+ }
4358
+ }
4359
+ for (const [key, value] of Object.entries(built.forced)) {
4360
+ result[key] = value;
4361
+ }
4362
+ return result;
4363
+ }
4364
+ function requireUniqueWhere(shape, bodyWhere, method, matchedKey, wasDynamic) {
4365
+ const where = buildUniqueWhereFromShape(
4366
+ shape,
4367
+ bodyWhere,
4368
+ matchedKey,
4369
+ wasDynamic
4370
+ );
4371
+ if (Object.keys(where).length === 0) {
4372
+ throw new ShapeError(`${method} requires a where condition`);
4373
+ }
4374
+ return where;
4375
+ }
4232
4376
  function buildEffectiveReadBody(resolved) {
4233
4377
  const hasShapeProjection = !!resolved.shape.select || !!resolved.shape.include;
4234
4378
  if (!hasShapeProjection)
@@ -4378,7 +4522,6 @@ function createModelGuardExtension(config) {
4378
4522
  `Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
4379
4523
  );
4380
4524
  }
4381
- maybeValidateUniqueWhere(modelName, resolved.shape, method);
4382
4525
  const dataSchema = getDataSchema(
4383
4526
  "update",
4384
4527
  resolved.shape.data,
@@ -4390,28 +4533,37 @@ function createModelGuardExtension(config) {
4390
4533
  dataSchema,
4391
4534
  method
4392
4535
  );
4393
- let where = isUniqueWhere ? requireWhere(
4394
- resolved.shape,
4395
- resolved.body.where,
4396
- method,
4397
- true,
4398
- resolved.matchedKey,
4399
- resolved.wasDynamic
4400
- ) : buildWhereFromShape(
4401
- resolved.shape,
4402
- resolved.body.where,
4403
- false,
4404
- resolved.matchedKey,
4405
- resolved.wasDynamic
4406
- );
4407
- if (isBulk && Object.keys(where).length === 0) {
4408
- throw new ShapeError(
4409
- `${method} requires at least one where condition`
4410
- );
4411
- }
4536
+ let where;
4412
4537
  if (isUniqueWhere) {
4538
+ if (resolved.shape.where) {
4539
+ validateUniqueWhereShapeConfig(
4540
+ modelName,
4541
+ resolved.shape.where,
4542
+ method
4543
+ );
4544
+ }
4545
+ where = requireUniqueWhere(
4546
+ resolved.shape,
4547
+ resolved.body.where,
4548
+ method,
4549
+ resolved.matchedKey,
4550
+ resolved.wasDynamic
4551
+ );
4413
4552
  validateResolvedUniqueWhere(modelName, where, method, uniqueMap);
4414
- where = normalizeUniqueWhere(where);
4553
+ } else {
4554
+ maybeValidateUniqueWhere(modelName, resolved.shape, method);
4555
+ where = buildWhereFromShape(
4556
+ resolved.shape,
4557
+ resolved.body.where,
4558
+ false,
4559
+ resolved.matchedKey,
4560
+ resolved.wasDynamic
4561
+ );
4562
+ if (isBulk && Object.keys(where).length === 0) {
4563
+ throw new ShapeError(
4564
+ `${method} requires at least one where condition`
4565
+ );
4566
+ }
4415
4567
  }
4416
4568
  const args = { data, where };
4417
4569
  if (supportsProjection) {
@@ -4449,29 +4601,37 @@ function createModelGuardExtension(config) {
4449
4601
  `Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
4450
4602
  );
4451
4603
  }
4452
- maybeValidateUniqueWhere(modelName, resolved.shape, method);
4453
- let where = isUniqueWhere ? requireWhere(
4454
- resolved.shape,
4455
- resolved.body.where,
4456
- method,
4457
- true,
4458
- resolved.matchedKey,
4459
- resolved.wasDynamic
4460
- ) : buildWhereFromShape(
4461
- resolved.shape,
4462
- resolved.body.where,
4463
- false,
4464
- resolved.matchedKey,
4465
- resolved.wasDynamic
4466
- );
4467
- if (isBulk && Object.keys(where).length === 0) {
4468
- throw new ShapeError(
4469
- `${method} requires at least one where condition`
4470
- );
4471
- }
4604
+ let where;
4472
4605
  if (isUniqueWhere) {
4606
+ if (resolved.shape.where) {
4607
+ validateUniqueWhereShapeConfig(
4608
+ modelName,
4609
+ resolved.shape.where,
4610
+ method
4611
+ );
4612
+ }
4613
+ where = requireUniqueWhere(
4614
+ resolved.shape,
4615
+ resolved.body.where,
4616
+ method,
4617
+ resolved.matchedKey,
4618
+ resolved.wasDynamic
4619
+ );
4473
4620
  validateResolvedUniqueWhere(modelName, where, method, uniqueMap);
4474
- where = normalizeUniqueWhere(where);
4621
+ } else {
4622
+ maybeValidateUniqueWhere(modelName, resolved.shape, method);
4623
+ where = buildWhereFromShape(
4624
+ resolved.shape,
4625
+ resolved.body.where,
4626
+ false,
4627
+ resolved.matchedKey,
4628
+ resolved.wasDynamic
4629
+ );
4630
+ if (isBulk && Object.keys(where).length === 0) {
4631
+ throw new ShapeError(
4632
+ `${method} requires at least one where condition`
4633
+ );
4634
+ }
4475
4635
  }
4476
4636
  const args = { where };
4477
4637
  if (supportsProjection) {
@@ -4515,7 +4675,11 @@ function createModelGuardExtension(config) {
4515
4675
  ALLOWED_BODY_KEYS_UPSERT,
4516
4676
  "upsert"
4517
4677
  );
4518
- maybeValidateUniqueWhere(modelName, resolved.shape, "upsert");
4678
+ validateUniqueWhereShapeConfig(
4679
+ modelName,
4680
+ resolved.shape.where,
4681
+ "upsert"
4682
+ );
4519
4683
  const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
4520
4684
  validateCreateCompleteness(
4521
4685
  modelName,
@@ -4546,17 +4710,16 @@ function createModelGuardExtension(config) {
4546
4710
  updateDataSchema,
4547
4711
  "upsert (update)"
4548
4712
  );
4549
- const where = requireWhere(
4713
+ const where = requireUniqueWhere(
4550
4714
  resolved.shape,
4551
4715
  resolved.body.where,
4552
4716
  "upsert",
4553
- true,
4554
4717
  resolved.matchedKey,
4555
4718
  resolved.wasDynamic
4556
4719
  );
4557
4720
  validateResolvedUniqueWhere(modelName, where, "upsert", uniqueMap);
4558
4721
  const args = {
4559
- where: normalizeUniqueWhere(where),
4722
+ where,
4560
4723
  create: createData,
4561
4724
  update: updateData
4562
4725
  };