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.
- package/dist/runtime/index.cjs +228 -65
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.js +228 -65
- package/dist/runtime/index.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/index.cjs
CHANGED
|
@@ -1451,13 +1451,6 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1451
1451
|
}
|
|
1452
1452
|
}
|
|
1453
1453
|
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
1454
|
if (!isPlainObject(operators)) {
|
|
1462
1455
|
throw new ShapeError(
|
|
1463
1456
|
`Where config for scalar field "${fieldName}" on model "${model}" must be an object of operators`
|
|
@@ -1570,7 +1563,82 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1570
1563
|
scalarConditions[fieldName] = fieldForced;
|
|
1571
1564
|
}
|
|
1572
1565
|
}
|
|
1573
|
-
|
|
1566
|
+
function buildUniqueWhereSchema(model, whereConfig) {
|
|
1567
|
+
const modelFields = typeMap[model];
|
|
1568
|
+
if (!modelFields)
|
|
1569
|
+
throw new ShapeError(`Unknown model: ${model}`);
|
|
1570
|
+
const fieldSchemas = {};
|
|
1571
|
+
const forced = {};
|
|
1572
|
+
const forcedOnlyKeys = /* @__PURE__ */ new Set();
|
|
1573
|
+
for (const [key, value] of Object.entries(whereConfig)) {
|
|
1574
|
+
if (COMBINATOR_KEYS.has(key)) {
|
|
1575
|
+
throw new ShapeError(
|
|
1576
|
+
`Combinator "${key}" is not supported in unique where for model "${model}"`
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
const fieldMeta = modelFields[key];
|
|
1580
|
+
if (!fieldMeta)
|
|
1581
|
+
throw new ShapeError(`Unknown field "${key}" on model "${model}"`);
|
|
1582
|
+
if (fieldMeta.isRelation)
|
|
1583
|
+
throw new ShapeError(
|
|
1584
|
+
`Relation field "${key}" cannot be used in unique where for model "${model}"`
|
|
1585
|
+
);
|
|
1586
|
+
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
1587
|
+
let directSchema;
|
|
1588
|
+
if (!fieldMeta.isEnum && !fieldMeta.isRelation && !fieldMeta.isUnsupported) {
|
|
1589
|
+
directSchema = wrapWithInputCoercion(
|
|
1590
|
+
fieldMeta.type,
|
|
1591
|
+
fieldMeta.isList,
|
|
1592
|
+
base
|
|
1593
|
+
);
|
|
1594
|
+
} else {
|
|
1595
|
+
directSchema = base;
|
|
1596
|
+
}
|
|
1597
|
+
const equalsWrapper = import_zod4.z.object({ equals: directSchema }).strict().transform((v) => v.equals);
|
|
1598
|
+
if (value === true) {
|
|
1599
|
+
fieldSchemas[key] = import_zod4.z.union([directSchema, equalsWrapper]);
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
if (isPlainObject(value)) {
|
|
1603
|
+
const keys = Object.keys(value);
|
|
1604
|
+
if (keys.length === 1 && keys[0] === "equals") {
|
|
1605
|
+
const equalsVal = value.equals;
|
|
1606
|
+
if (equalsVal === true) {
|
|
1607
|
+
fieldSchemas[key] = import_zod4.z.union([directSchema, equalsWrapper]);
|
|
1608
|
+
} else {
|
|
1609
|
+
const actual2 = isForcedValue(equalsVal) ? equalsVal.value : equalsVal;
|
|
1610
|
+
try {
|
|
1611
|
+
forced[key] = directSchema.parse(actual2);
|
|
1612
|
+
} catch (err) {
|
|
1613
|
+
throw new ShapeError(
|
|
1614
|
+
`Invalid forced value for unique where "${model}.${key}": ${err.message}`
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
forcedOnlyKeys.add(key);
|
|
1618
|
+
}
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1621
|
+
throw new ShapeError(
|
|
1622
|
+
`Unique where field "${key}" on model "${model}" only accepts true or { equals: true/value }. Got operators: ${keys.join(", ")}`
|
|
1623
|
+
);
|
|
1624
|
+
}
|
|
1625
|
+
const actual = isForcedValue(value) ? value.value : value;
|
|
1626
|
+
try {
|
|
1627
|
+
forced[key] = directSchema.parse(actual);
|
|
1628
|
+
} catch (err) {
|
|
1629
|
+
throw new ShapeError(
|
|
1630
|
+
`Invalid forced value for unique where "${model}.${key}": ${err.message}`
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
forcedOnlyKeys.add(key);
|
|
1634
|
+
}
|
|
1635
|
+
return {
|
|
1636
|
+
schema: Object.keys(fieldSchemas).length > 0 ? import_zod4.z.object(fieldSchemas).strict() : null,
|
|
1637
|
+
forced,
|
|
1638
|
+
forcedOnlyKeys
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
return { buildWhereSchema, buildUniqueWhereSchema };
|
|
1574
1642
|
}
|
|
1575
1643
|
|
|
1576
1644
|
// src/runtime/query-builder-args.ts
|
|
@@ -2656,6 +2724,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2656
2724
|
buildQuerySchema,
|
|
2657
2725
|
buildShapeZodSchema,
|
|
2658
2726
|
buildWhereSchema: whereBuilder.buildWhereSchema,
|
|
2727
|
+
buildUniqueWhereSchema: whereBuilder.buildUniqueWhereSchema,
|
|
2659
2728
|
buildIncludeSchema: projectionBuilder.buildIncludeSchema,
|
|
2660
2729
|
buildSelectSchema: projectionBuilder.buildSelectSchema
|
|
2661
2730
|
};
|
|
@@ -3801,17 +3870,6 @@ var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
|
3801
3870
|
"updateManyAndReturn"
|
|
3802
3871
|
]);
|
|
3803
3872
|
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
3873
|
function buildDefaultSelectInput(config) {
|
|
3816
3874
|
const result = {};
|
|
3817
3875
|
for (const [key, value] of Object.entries(config)) {
|
|
@@ -4031,6 +4089,36 @@ function createModelGuardExtension(config) {
|
|
|
4031
4089
|
return;
|
|
4032
4090
|
validateUniqueEquality(modelName, shape.where, method, uniqueMap, typeMap);
|
|
4033
4091
|
}
|
|
4092
|
+
function validateUniqueWhereShapeConfig(modelName, where, method) {
|
|
4093
|
+
const constraints = uniqueMap[modelName];
|
|
4094
|
+
if (!constraints || constraints.length === 0)
|
|
4095
|
+
return;
|
|
4096
|
+
const equalityFields = /* @__PURE__ */ new Set();
|
|
4097
|
+
for (const [key, value] of Object.entries(where)) {
|
|
4098
|
+
if (key === "AND" || key === "OR" || key === "NOT")
|
|
4099
|
+
continue;
|
|
4100
|
+
if (value === true) {
|
|
4101
|
+
equalityFields.add(key);
|
|
4102
|
+
continue;
|
|
4103
|
+
}
|
|
4104
|
+
if (isPlainObject(value) && "equals" in value) {
|
|
4105
|
+
equalityFields.add(key);
|
|
4106
|
+
continue;
|
|
4107
|
+
}
|
|
4108
|
+
if (value !== null && value !== void 0) {
|
|
4109
|
+
equalityFields.add(key);
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
const valid = constraints.some(
|
|
4113
|
+
(constraint) => constraint.every((field) => equalityFields.has(field))
|
|
4114
|
+
);
|
|
4115
|
+
if (!valid) {
|
|
4116
|
+
const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
|
|
4117
|
+
throw new ShapeError(
|
|
4118
|
+
`${method} on model "${modelName}" requires where to cover a unique constraint with equality operators only: ${constraintDesc}`
|
|
4119
|
+
);
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4034
4122
|
function createGuardedMethods(modelName, modelDelegate, input, explicitCaller) {
|
|
4035
4123
|
function callDelegate(method, args) {
|
|
4036
4124
|
if (typeof modelDelegate[method] !== "function") {
|
|
@@ -4053,6 +4141,7 @@ function createModelGuardExtension(config) {
|
|
|
4053
4141
|
const dataSchemaCache = /* @__PURE__ */ new Map();
|
|
4054
4142
|
const whereBuiltCache = /* @__PURE__ */ new Map();
|
|
4055
4143
|
const projectionCache = /* @__PURE__ */ new Map();
|
|
4144
|
+
const uniqueWhereCache = /* @__PURE__ */ new Map();
|
|
4056
4145
|
function getReadShape(method, queryShape, matchedKey, wasDynamic) {
|
|
4057
4146
|
if (!wasDynamic) {
|
|
4058
4147
|
const cacheKey = `${method}\0${matchedKey}`;
|
|
@@ -4110,6 +4199,18 @@ function createModelGuardExtension(config) {
|
|
|
4110
4199
|
}
|
|
4111
4200
|
return queryBuilder.buildWhereSchema(modelName, whereConfig);
|
|
4112
4201
|
}
|
|
4202
|
+
function getUniqueWhereBuilt(whereConfig, matchedKey, wasDynamic) {
|
|
4203
|
+
if (!wasDynamic) {
|
|
4204
|
+
const cacheKey = `unique\0${matchedKey}`;
|
|
4205
|
+
const cached = uniqueWhereCache.get(cacheKey);
|
|
4206
|
+
if (cached)
|
|
4207
|
+
return cached;
|
|
4208
|
+
const built = queryBuilder.buildUniqueWhereSchema(modelName, whereConfig);
|
|
4209
|
+
uniqueWhereCache.set(cacheKey, built);
|
|
4210
|
+
return built;
|
|
4211
|
+
}
|
|
4212
|
+
return queryBuilder.buildUniqueWhereSchema(modelName, whereConfig);
|
|
4213
|
+
}
|
|
4113
4214
|
function buildProjectionSchema(shape) {
|
|
4114
4215
|
const schemaFields = {};
|
|
4115
4216
|
let forcedIncludeTree = {};
|
|
@@ -4260,6 +4361,49 @@ function createModelGuardExtension(config) {
|
|
|
4260
4361
|
}
|
|
4261
4362
|
return where;
|
|
4262
4363
|
}
|
|
4364
|
+
function buildUniqueWhereFromShape(shape, bodyWhere, matchedKey, wasDynamic) {
|
|
4365
|
+
if (!shape.where)
|
|
4366
|
+
return {};
|
|
4367
|
+
const built = getUniqueWhereBuilt(shape.where, matchedKey, wasDynamic);
|
|
4368
|
+
let result = {};
|
|
4369
|
+
if (built.schema) {
|
|
4370
|
+
if (bodyWhere !== void 0 && bodyWhere !== null) {
|
|
4371
|
+
if (!isPlainObject(bodyWhere)) {
|
|
4372
|
+
throw new ShapeError("Unique where must be an object");
|
|
4373
|
+
}
|
|
4374
|
+
const sanitized = { ...bodyWhere };
|
|
4375
|
+
for (const key of built.forcedOnlyKeys) {
|
|
4376
|
+
delete sanitized[key];
|
|
4377
|
+
}
|
|
4378
|
+
result = built.schema.parse(sanitized);
|
|
4379
|
+
}
|
|
4380
|
+
} else if (bodyWhere !== void 0 && bodyWhere !== null) {
|
|
4381
|
+
if (isPlainObject(bodyWhere)) {
|
|
4382
|
+
const hasOnlyForcedKeys = built.forcedOnlyKeys.size > 0 && Object.keys(bodyWhere).every((k) => built.forcedOnlyKeys.has(k));
|
|
4383
|
+
if (!hasOnlyForcedKeys && Object.keys(bodyWhere).length > 0) {
|
|
4384
|
+
throw new ShapeError(
|
|
4385
|
+
"Unique where shape contains only forced values. Client where input is not accepted."
|
|
4386
|
+
);
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
for (const [key, value] of Object.entries(built.forced)) {
|
|
4391
|
+
result[key] = value;
|
|
4392
|
+
}
|
|
4393
|
+
return result;
|
|
4394
|
+
}
|
|
4395
|
+
function requireUniqueWhere(shape, bodyWhere, method, matchedKey, wasDynamic) {
|
|
4396
|
+
const where = buildUniqueWhereFromShape(
|
|
4397
|
+
shape,
|
|
4398
|
+
bodyWhere,
|
|
4399
|
+
matchedKey,
|
|
4400
|
+
wasDynamic
|
|
4401
|
+
);
|
|
4402
|
+
if (Object.keys(where).length === 0) {
|
|
4403
|
+
throw new ShapeError(`${method} requires a where condition`);
|
|
4404
|
+
}
|
|
4405
|
+
return where;
|
|
4406
|
+
}
|
|
4263
4407
|
function buildEffectiveReadBody(resolved) {
|
|
4264
4408
|
const hasShapeProjection = !!resolved.shape.select || !!resolved.shape.include;
|
|
4265
4409
|
if (!hasShapeProjection)
|
|
@@ -4409,7 +4553,6 @@ function createModelGuardExtension(config) {
|
|
|
4409
4553
|
`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
|
|
4410
4554
|
);
|
|
4411
4555
|
}
|
|
4412
|
-
maybeValidateUniqueWhere(modelName, resolved.shape, method);
|
|
4413
4556
|
const dataSchema = getDataSchema(
|
|
4414
4557
|
"update",
|
|
4415
4558
|
resolved.shape.data,
|
|
@@ -4421,28 +4564,37 @@ function createModelGuardExtension(config) {
|
|
|
4421
4564
|
dataSchema,
|
|
4422
4565
|
method
|
|
4423
4566
|
);
|
|
4424
|
-
let where
|
|
4425
|
-
resolved.shape,
|
|
4426
|
-
resolved.body.where,
|
|
4427
|
-
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
|
|
4437
|
-
);
|
|
4438
|
-
if (isBulk && Object.keys(where).length === 0) {
|
|
4439
|
-
throw new ShapeError(
|
|
4440
|
-
`${method} requires at least one where condition`
|
|
4441
|
-
);
|
|
4442
|
-
}
|
|
4567
|
+
let where;
|
|
4443
4568
|
if (isUniqueWhere) {
|
|
4569
|
+
if (resolved.shape.where) {
|
|
4570
|
+
validateUniqueWhereShapeConfig(
|
|
4571
|
+
modelName,
|
|
4572
|
+
resolved.shape.where,
|
|
4573
|
+
method
|
|
4574
|
+
);
|
|
4575
|
+
}
|
|
4576
|
+
where = requireUniqueWhere(
|
|
4577
|
+
resolved.shape,
|
|
4578
|
+
resolved.body.where,
|
|
4579
|
+
method,
|
|
4580
|
+
resolved.matchedKey,
|
|
4581
|
+
resolved.wasDynamic
|
|
4582
|
+
);
|
|
4444
4583
|
validateResolvedUniqueWhere(modelName, where, method, uniqueMap);
|
|
4445
|
-
|
|
4584
|
+
} else {
|
|
4585
|
+
maybeValidateUniqueWhere(modelName, resolved.shape, method);
|
|
4586
|
+
where = buildWhereFromShape(
|
|
4587
|
+
resolved.shape,
|
|
4588
|
+
resolved.body.where,
|
|
4589
|
+
false,
|
|
4590
|
+
resolved.matchedKey,
|
|
4591
|
+
resolved.wasDynamic
|
|
4592
|
+
);
|
|
4593
|
+
if (isBulk && Object.keys(where).length === 0) {
|
|
4594
|
+
throw new ShapeError(
|
|
4595
|
+
`${method} requires at least one where condition`
|
|
4596
|
+
);
|
|
4597
|
+
}
|
|
4446
4598
|
}
|
|
4447
4599
|
const args = { data, where };
|
|
4448
4600
|
if (supportsProjection) {
|
|
@@ -4480,29 +4632,37 @@ function createModelGuardExtension(config) {
|
|
|
4480
4632
|
`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
|
|
4481
4633
|
);
|
|
4482
4634
|
}
|
|
4483
|
-
|
|
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
|
-
}
|
|
4635
|
+
let where;
|
|
4503
4636
|
if (isUniqueWhere) {
|
|
4637
|
+
if (resolved.shape.where) {
|
|
4638
|
+
validateUniqueWhereShapeConfig(
|
|
4639
|
+
modelName,
|
|
4640
|
+
resolved.shape.where,
|
|
4641
|
+
method
|
|
4642
|
+
);
|
|
4643
|
+
}
|
|
4644
|
+
where = requireUniqueWhere(
|
|
4645
|
+
resolved.shape,
|
|
4646
|
+
resolved.body.where,
|
|
4647
|
+
method,
|
|
4648
|
+
resolved.matchedKey,
|
|
4649
|
+
resolved.wasDynamic
|
|
4650
|
+
);
|
|
4504
4651
|
validateResolvedUniqueWhere(modelName, where, method, uniqueMap);
|
|
4505
|
-
|
|
4652
|
+
} else {
|
|
4653
|
+
maybeValidateUniqueWhere(modelName, resolved.shape, method);
|
|
4654
|
+
where = buildWhereFromShape(
|
|
4655
|
+
resolved.shape,
|
|
4656
|
+
resolved.body.where,
|
|
4657
|
+
false,
|
|
4658
|
+
resolved.matchedKey,
|
|
4659
|
+
resolved.wasDynamic
|
|
4660
|
+
);
|
|
4661
|
+
if (isBulk && Object.keys(where).length === 0) {
|
|
4662
|
+
throw new ShapeError(
|
|
4663
|
+
`${method} requires at least one where condition`
|
|
4664
|
+
);
|
|
4665
|
+
}
|
|
4506
4666
|
}
|
|
4507
4667
|
const args = { where };
|
|
4508
4668
|
if (supportsProjection) {
|
|
@@ -4546,7 +4706,11 @@ function createModelGuardExtension(config) {
|
|
|
4546
4706
|
ALLOWED_BODY_KEYS_UPSERT,
|
|
4547
4707
|
"upsert"
|
|
4548
4708
|
);
|
|
4549
|
-
|
|
4709
|
+
validateUniqueWhereShapeConfig(
|
|
4710
|
+
modelName,
|
|
4711
|
+
resolved.shape.where,
|
|
4712
|
+
"upsert"
|
|
4713
|
+
);
|
|
4550
4714
|
const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
|
|
4551
4715
|
validateCreateCompleteness(
|
|
4552
4716
|
modelName,
|
|
@@ -4577,17 +4741,16 @@ function createModelGuardExtension(config) {
|
|
|
4577
4741
|
updateDataSchema,
|
|
4578
4742
|
"upsert (update)"
|
|
4579
4743
|
);
|
|
4580
|
-
const where =
|
|
4744
|
+
const where = requireUniqueWhere(
|
|
4581
4745
|
resolved.shape,
|
|
4582
4746
|
resolved.body.where,
|
|
4583
4747
|
"upsert",
|
|
4584
|
-
true,
|
|
4585
4748
|
resolved.matchedKey,
|
|
4586
4749
|
resolved.wasDynamic
|
|
4587
4750
|
);
|
|
4588
4751
|
validateResolvedUniqueWhere(modelName, where, "upsert", uniqueMap);
|
|
4589
4752
|
const args = {
|
|
4590
|
-
where
|
|
4753
|
+
where,
|
|
4591
4754
|
create: createData,
|
|
4592
4755
|
update: updateData
|
|
4593
4756
|
};
|