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.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
4722
|
+
where,
|
|
4560
4723
|
create: createData,
|
|
4561
4724
|
update: updateData
|
|
4562
4725
|
};
|