prisma-guard 1.29.0 → 1.31.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/README.md +130 -5
- package/dist/generator/index.js +6 -0
- package/dist/generator/index.js.map +1 -1
- package/dist/runtime/index.cjs +66 -24
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +2 -0
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +66 -24
- package/dist/runtime/index.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/index.cjs
CHANGED
|
@@ -307,6 +307,9 @@ function isPlainObject(v) {
|
|
|
307
307
|
const proto = Object.getPrototypeOf(v);
|
|
308
308
|
return proto === Object.prototype || proto === null;
|
|
309
309
|
}
|
|
310
|
+
function isObjectLike(v) {
|
|
311
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
312
|
+
}
|
|
310
313
|
function schemaProducesValueForUndefined(schema) {
|
|
311
314
|
const result = schema.safeParse(void 0);
|
|
312
315
|
return result.success && result.data !== void 0;
|
|
@@ -4138,12 +4141,39 @@ function validateAllowedKeys(value, allowed, method, kind) {
|
|
|
4138
4141
|
return `Shape key "${key}" not valid for ${method}. Allowed: ${[...allowed].join(", ")}`;
|
|
4139
4142
|
});
|
|
4140
4143
|
}
|
|
4144
|
+
var RELATION_OPS_COVERING_FK = /* @__PURE__ */ new Set([
|
|
4145
|
+
"connect",
|
|
4146
|
+
"connectOrCreate",
|
|
4147
|
+
"create"
|
|
4148
|
+
]);
|
|
4149
|
+
function collectRelationCoveredFks(modelFields, dataConfig) {
|
|
4150
|
+
const covered = /* @__PURE__ */ new Set();
|
|
4151
|
+
for (const [fieldName, value] of Object.entries(dataConfig)) {
|
|
4152
|
+
const meta = modelFields[fieldName];
|
|
4153
|
+
if (!meta || !meta.isRelation)
|
|
4154
|
+
continue;
|
|
4155
|
+
const fks = meta.relationFromFields;
|
|
4156
|
+
if (!fks || fks.length === 0)
|
|
4157
|
+
continue;
|
|
4158
|
+
if (!isPlainObject(value))
|
|
4159
|
+
continue;
|
|
4160
|
+
const covers = Object.keys(value).some(
|
|
4161
|
+
(op) => RELATION_OPS_COVERING_FK.has(op)
|
|
4162
|
+
);
|
|
4163
|
+
if (!covers)
|
|
4164
|
+
continue;
|
|
4165
|
+
for (const fk of fks)
|
|
4166
|
+
covered.add(fk);
|
|
4167
|
+
}
|
|
4168
|
+
return covered;
|
|
4169
|
+
}
|
|
4141
4170
|
function validateCreateCompleteness(modelName, dataConfig, typeMap, scopeFks, zodDefaults) {
|
|
4142
4171
|
const modelFields = typeMap[modelName];
|
|
4143
4172
|
if (!modelFields)
|
|
4144
4173
|
return;
|
|
4145
4174
|
const zodDefaultFields = zodDefaults[modelName];
|
|
4146
4175
|
const zodDefaultSet = zodDefaultFields ? new Set(zodDefaultFields) : void 0;
|
|
4176
|
+
const relationCoveredFks = collectRelationCoveredFks(modelFields, dataConfig);
|
|
4147
4177
|
for (const [fieldName, meta] of Object.entries(modelFields)) {
|
|
4148
4178
|
if (meta.isRelation)
|
|
4149
4179
|
continue;
|
|
@@ -4157,10 +4187,12 @@ function validateCreateCompleteness(modelName, dataConfig, typeMap, scopeFks, zo
|
|
|
4157
4187
|
continue;
|
|
4158
4188
|
if (scopeFks.has(fieldName))
|
|
4159
4189
|
continue;
|
|
4190
|
+
if (relationCoveredFks.has(fieldName))
|
|
4191
|
+
continue;
|
|
4160
4192
|
if (zodDefaultSet && zodDefaultSet.has(fieldName))
|
|
4161
4193
|
continue;
|
|
4162
4194
|
throw new ShapeError(
|
|
4163
|
-
`Required field "${fieldName}" on model "${modelName}" is missing from create data shape, has no default, and is not a
|
|
4195
|
+
`Required field "${fieldName}" on model "${modelName}" is missing from create data shape, has no default, is not a scope FK, and is not covered by a relation write in the shape`
|
|
4164
4196
|
);
|
|
4165
4197
|
}
|
|
4166
4198
|
}
|
|
@@ -4880,12 +4912,23 @@ function walkForClientContent(obj, predicate, depth) {
|
|
|
4880
4912
|
const nested = value;
|
|
4881
4913
|
if (nested.orderBy || nested.cursor || nested.take || nested.skip)
|
|
4882
4914
|
return true;
|
|
4883
|
-
if (nested.where && isPlainObject(nested.where) && hasClientControlledValues(
|
|
4915
|
+
if (nested.where && isPlainObject(nested.where) && hasClientControlledValues(
|
|
4916
|
+
nested.where,
|
|
4917
|
+
depth + 1
|
|
4918
|
+
)) {
|
|
4884
4919
|
return true;
|
|
4885
4920
|
}
|
|
4886
|
-
if (nested.include && walkForClientContent(
|
|
4921
|
+
if (nested.include && walkForClientContent(
|
|
4922
|
+
nested.include,
|
|
4923
|
+
predicate,
|
|
4924
|
+
depth + 1
|
|
4925
|
+
))
|
|
4887
4926
|
return true;
|
|
4888
|
-
if (nested.select && walkForClientContent(
|
|
4927
|
+
if (nested.select && walkForClientContent(
|
|
4928
|
+
nested.select,
|
|
4929
|
+
predicate,
|
|
4930
|
+
depth + 1
|
|
4931
|
+
))
|
|
4889
4932
|
return true;
|
|
4890
4933
|
}
|
|
4891
4934
|
return false;
|
|
@@ -4907,11 +4950,19 @@ function hasClientControlledValues(obj, depth = 0) {
|
|
|
4907
4950
|
function hasNestedClientControlledArgs(shape) {
|
|
4908
4951
|
const predicate = () => false;
|
|
4909
4952
|
if (shape.include) {
|
|
4910
|
-
if (walkForClientContent(
|
|
4953
|
+
if (walkForClientContent(
|
|
4954
|
+
shape.include,
|
|
4955
|
+
predicate,
|
|
4956
|
+
0
|
|
4957
|
+
))
|
|
4911
4958
|
return true;
|
|
4912
4959
|
}
|
|
4913
4960
|
if (shape.select) {
|
|
4914
|
-
if (walkForClientContent(
|
|
4961
|
+
if (walkForClientContent(
|
|
4962
|
+
shape.select,
|
|
4963
|
+
predicate,
|
|
4964
|
+
0
|
|
4965
|
+
))
|
|
4915
4966
|
return true;
|
|
4916
4967
|
}
|
|
4917
4968
|
return false;
|
|
@@ -5209,15 +5260,13 @@ function createModelGuardExtension(config) {
|
|
|
5209
5260
|
let result = {};
|
|
5210
5261
|
if (built.schema) {
|
|
5211
5262
|
if (bodyWhere !== void 0 && bodyWhere !== null) {
|
|
5212
|
-
if (!
|
|
5263
|
+
if (!isObjectLike(bodyWhere)) {
|
|
5213
5264
|
throw new ShapeError(
|
|
5214
|
-
`Invalid "where" on model "${modelName}": unique where must be
|
|
5265
|
+
`Invalid "where" on model "${modelName}": unique where must be an object`
|
|
5215
5266
|
);
|
|
5216
5267
|
}
|
|
5217
|
-
const
|
|
5218
|
-
|
|
5219
|
-
built.forced
|
|
5220
|
-
) : { ...bodyWhere };
|
|
5268
|
+
const bodyWhereObj = { ...bodyWhere };
|
|
5269
|
+
const sanitized = hasWhereForced(built.forced) ? stripUniqueWhereForcedInput(bodyWhereObj, built.forced) : bodyWhereObj;
|
|
5221
5270
|
try {
|
|
5222
5271
|
result = built.schema.parse(sanitized);
|
|
5223
5272
|
} catch (err) {
|
|
@@ -5229,11 +5278,9 @@ function createModelGuardExtension(config) {
|
|
|
5229
5278
|
}
|
|
5230
5279
|
}
|
|
5231
5280
|
} else if (bodyWhere !== void 0 && bodyWhere !== null) {
|
|
5232
|
-
if (
|
|
5233
|
-
const
|
|
5234
|
-
|
|
5235
|
-
built.forced
|
|
5236
|
-
) : { ...bodyWhere };
|
|
5281
|
+
if (isObjectLike(bodyWhere)) {
|
|
5282
|
+
const bodyWhereObj = { ...bodyWhere };
|
|
5283
|
+
const sanitized = hasWhereForced(built.forced) ? stripUniqueWhereForcedInput(bodyWhereObj, built.forced) : bodyWhereObj;
|
|
5237
5284
|
if (Object.keys(sanitized).length > 0) {
|
|
5238
5285
|
throw new ShapeError(
|
|
5239
5286
|
`Unique where on model "${modelName}" contains only forced values. Client where input is not accepted.`
|
|
@@ -5241,7 +5288,7 @@ function createModelGuardExtension(config) {
|
|
|
5241
5288
|
}
|
|
5242
5289
|
} else {
|
|
5243
5290
|
throw new ShapeError(
|
|
5244
|
-
`Invalid "where" on model "${modelName}": unique where must be
|
|
5291
|
+
`Invalid "where" on model "${modelName}": unique where must be an object`
|
|
5245
5292
|
);
|
|
5246
5293
|
}
|
|
5247
5294
|
}
|
|
@@ -5594,12 +5641,7 @@ function createModelGuardExtension(config) {
|
|
|
5594
5641
|
"upsert",
|
|
5595
5642
|
"shape"
|
|
5596
5643
|
);
|
|
5597
|
-
validateAllowedKeys(
|
|
5598
|
-
resolved.body,
|
|
5599
|
-
allowedBodyKeys,
|
|
5600
|
-
"upsert",
|
|
5601
|
-
"body"
|
|
5602
|
-
);
|
|
5644
|
+
validateAllowedKeys(resolved.body, allowedBodyKeys, "upsert", "body");
|
|
5603
5645
|
validateUniqueWhereShapeConfig(
|
|
5604
5646
|
modelName,
|
|
5605
5647
|
resolved.shape.where,
|