prisma-guard 1.18.1 → 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 +229 -63
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.js +229 -63
- package/dist/runtime/index.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/index.js
CHANGED
|
@@ -1087,13 +1087,9 @@ function collectEqualityFields(where, model, typeMap) {
|
|
|
1087
1087
|
continue;
|
|
1088
1088
|
if (typeMap && model && typeMap[model]?.[key]?.isRelation)
|
|
1089
1089
|
continue;
|
|
1090
|
-
if (value !== null && value !== void 0 && !isPlainObject(value) && !Array.isArray(value)) {
|
|
1091
|
-
fields.add(key);
|
|
1092
|
-
continue;
|
|
1093
|
-
}
|
|
1094
1090
|
if (!value || !isPlainObject(value))
|
|
1095
1091
|
continue;
|
|
1096
|
-
if (
|
|
1092
|
+
if ("equals" in value) {
|
|
1097
1093
|
fields.add(key);
|
|
1098
1094
|
}
|
|
1099
1095
|
}
|
|
@@ -1536,7 +1532,82 @@ function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
|
1536
1532
|
scalarConditions[fieldName] = fieldForced;
|
|
1537
1533
|
}
|
|
1538
1534
|
}
|
|
1539
|
-
|
|
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 };
|
|
1540
1611
|
}
|
|
1541
1612
|
|
|
1542
1613
|
// src/runtime/query-builder-args.ts
|
|
@@ -2622,6 +2693,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
2622
2693
|
buildQuerySchema,
|
|
2623
2694
|
buildShapeZodSchema,
|
|
2624
2695
|
buildWhereSchema: whereBuilder.buildWhereSchema,
|
|
2696
|
+
buildUniqueWhereSchema: whereBuilder.buildUniqueWhereSchema,
|
|
2625
2697
|
buildIncludeSchema: projectionBuilder.buildIncludeSchema,
|
|
2626
2698
|
buildSelectSchema: projectionBuilder.buildSelectSchema
|
|
2627
2699
|
};
|
|
@@ -3767,17 +3839,6 @@ var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
|
3767
3839
|
"updateManyAndReturn"
|
|
3768
3840
|
]);
|
|
3769
3841
|
var BATCH_CREATE_METHODS = /* @__PURE__ */ new Set(["createMany", "createManyAndReturn"]);
|
|
3770
|
-
function normalizeUniqueWhere(where) {
|
|
3771
|
-
const result = {};
|
|
3772
|
-
for (const [key, value] of Object.entries(where)) {
|
|
3773
|
-
if (key !== "AND" && key !== "OR" && key !== "NOT" && isPlainObject(value) && "equals" in value && Object.keys(value).length === 1) {
|
|
3774
|
-
result[key] = value.equals;
|
|
3775
|
-
} else {
|
|
3776
|
-
result[key] = value;
|
|
3777
|
-
}
|
|
3778
|
-
}
|
|
3779
|
-
return result;
|
|
3780
|
-
}
|
|
3781
3842
|
function buildDefaultSelectInput(config) {
|
|
3782
3843
|
const result = {};
|
|
3783
3844
|
for (const [key, value] of Object.entries(config)) {
|
|
@@ -3997,6 +4058,36 @@ function createModelGuardExtension(config) {
|
|
|
3997
4058
|
return;
|
|
3998
4059
|
validateUniqueEquality(modelName, shape.where, method, uniqueMap, typeMap);
|
|
3999
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
|
+
}
|
|
4000
4091
|
function createGuardedMethods(modelName, modelDelegate, input, explicitCaller) {
|
|
4001
4092
|
function callDelegate(method, args) {
|
|
4002
4093
|
if (typeof modelDelegate[method] !== "function") {
|
|
@@ -4019,6 +4110,7 @@ function createModelGuardExtension(config) {
|
|
|
4019
4110
|
const dataSchemaCache = /* @__PURE__ */ new Map();
|
|
4020
4111
|
const whereBuiltCache = /* @__PURE__ */ new Map();
|
|
4021
4112
|
const projectionCache = /* @__PURE__ */ new Map();
|
|
4113
|
+
const uniqueWhereCache = /* @__PURE__ */ new Map();
|
|
4022
4114
|
function getReadShape(method, queryShape, matchedKey, wasDynamic) {
|
|
4023
4115
|
if (!wasDynamic) {
|
|
4024
4116
|
const cacheKey = `${method}\0${matchedKey}`;
|
|
@@ -4076,6 +4168,18 @@ function createModelGuardExtension(config) {
|
|
|
4076
4168
|
}
|
|
4077
4169
|
return queryBuilder.buildWhereSchema(modelName, whereConfig);
|
|
4078
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
|
+
}
|
|
4079
4183
|
function buildProjectionSchema(shape) {
|
|
4080
4184
|
const schemaFields = {};
|
|
4081
4185
|
let forcedIncludeTree = {};
|
|
@@ -4226,6 +4330,49 @@ function createModelGuardExtension(config) {
|
|
|
4226
4330
|
}
|
|
4227
4331
|
return where;
|
|
4228
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
|
+
}
|
|
4229
4376
|
function buildEffectiveReadBody(resolved) {
|
|
4230
4377
|
const hasShapeProjection = !!resolved.shape.select || !!resolved.shape.include;
|
|
4231
4378
|
if (!hasShapeProjection)
|
|
@@ -4375,7 +4522,6 @@ function createModelGuardExtension(config) {
|
|
|
4375
4522
|
`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
|
|
4376
4523
|
);
|
|
4377
4524
|
}
|
|
4378
|
-
maybeValidateUniqueWhere(modelName, resolved.shape, method);
|
|
4379
4525
|
const dataSchema = getDataSchema(
|
|
4380
4526
|
"update",
|
|
4381
4527
|
resolved.shape.data,
|
|
@@ -4387,28 +4533,37 @@ function createModelGuardExtension(config) {
|
|
|
4387
4533
|
dataSchema,
|
|
4388
4534
|
method
|
|
4389
4535
|
);
|
|
4390
|
-
let where
|
|
4391
|
-
resolved.shape,
|
|
4392
|
-
resolved.body.where,
|
|
4393
|
-
method,
|
|
4394
|
-
true,
|
|
4395
|
-
resolved.matchedKey,
|
|
4396
|
-
resolved.wasDynamic
|
|
4397
|
-
) : buildWhereFromShape(
|
|
4398
|
-
resolved.shape,
|
|
4399
|
-
resolved.body.where,
|
|
4400
|
-
false,
|
|
4401
|
-
resolved.matchedKey,
|
|
4402
|
-
resolved.wasDynamic
|
|
4403
|
-
);
|
|
4404
|
-
if (isBulk && Object.keys(where).length === 0) {
|
|
4405
|
-
throw new ShapeError(
|
|
4406
|
-
`${method} requires at least one where condition`
|
|
4407
|
-
);
|
|
4408
|
-
}
|
|
4536
|
+
let where;
|
|
4409
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
|
+
);
|
|
4410
4552
|
validateResolvedUniqueWhere(modelName, where, method, uniqueMap);
|
|
4411
|
-
|
|
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
|
+
}
|
|
4412
4567
|
}
|
|
4413
4568
|
const args = { data, where };
|
|
4414
4569
|
if (supportsProjection) {
|
|
@@ -4446,29 +4601,37 @@ function createModelGuardExtension(config) {
|
|
|
4446
4601
|
`Guard shape requires "where" for ${method} to prevent unconstrained bulk mutations`
|
|
4447
4602
|
);
|
|
4448
4603
|
}
|
|
4449
|
-
|
|
4450
|
-
let where = isUniqueWhere ? requireWhere(
|
|
4451
|
-
resolved.shape,
|
|
4452
|
-
resolved.body.where,
|
|
4453
|
-
method,
|
|
4454
|
-
true,
|
|
4455
|
-
resolved.matchedKey,
|
|
4456
|
-
resolved.wasDynamic
|
|
4457
|
-
) : buildWhereFromShape(
|
|
4458
|
-
resolved.shape,
|
|
4459
|
-
resolved.body.where,
|
|
4460
|
-
false,
|
|
4461
|
-
resolved.matchedKey,
|
|
4462
|
-
resolved.wasDynamic
|
|
4463
|
-
);
|
|
4464
|
-
if (isBulk && Object.keys(where).length === 0) {
|
|
4465
|
-
throw new ShapeError(
|
|
4466
|
-
`${method} requires at least one where condition`
|
|
4467
|
-
);
|
|
4468
|
-
}
|
|
4604
|
+
let where;
|
|
4469
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
|
+
);
|
|
4470
4620
|
validateResolvedUniqueWhere(modelName, where, method, uniqueMap);
|
|
4471
|
-
|
|
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
|
+
}
|
|
4472
4635
|
}
|
|
4473
4636
|
const args = { where };
|
|
4474
4637
|
if (supportsProjection) {
|
|
@@ -4512,7 +4675,11 @@ function createModelGuardExtension(config) {
|
|
|
4512
4675
|
ALLOWED_BODY_KEYS_UPSERT,
|
|
4513
4676
|
"upsert"
|
|
4514
4677
|
);
|
|
4515
|
-
|
|
4678
|
+
validateUniqueWhereShapeConfig(
|
|
4679
|
+
modelName,
|
|
4680
|
+
resolved.shape.where,
|
|
4681
|
+
"upsert"
|
|
4682
|
+
);
|
|
4516
4683
|
const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
|
|
4517
4684
|
validateCreateCompleteness(
|
|
4518
4685
|
modelName,
|
|
@@ -4543,17 +4710,16 @@ function createModelGuardExtension(config) {
|
|
|
4543
4710
|
updateDataSchema,
|
|
4544
4711
|
"upsert (update)"
|
|
4545
4712
|
);
|
|
4546
|
-
const where =
|
|
4713
|
+
const where = requireUniqueWhere(
|
|
4547
4714
|
resolved.shape,
|
|
4548
4715
|
resolved.body.where,
|
|
4549
4716
|
"upsert",
|
|
4550
|
-
true,
|
|
4551
4717
|
resolved.matchedKey,
|
|
4552
4718
|
resolved.wasDynamic
|
|
4553
4719
|
);
|
|
4554
4720
|
validateResolvedUniqueWhere(modelName, where, "upsert", uniqueMap);
|
|
4555
4721
|
const args = {
|
|
4556
|
-
where
|
|
4722
|
+
where,
|
|
4557
4723
|
create: createData,
|
|
4558
4724
|
update: updateData
|
|
4559
4725
|
};
|