prisma-guard 1.31.0 → 1.32.1
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 +61 -11
- package/dist/runtime/index.cjs +172 -121
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.js +172 -121
- package/dist/runtime/index.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/index.js
CHANGED
|
@@ -944,40 +944,75 @@ function unsupported() {
|
|
|
944
944
|
return marker;
|
|
945
945
|
}
|
|
946
946
|
|
|
947
|
-
// src/shared/
|
|
948
|
-
function
|
|
949
|
-
|
|
950
|
-
return null;
|
|
951
|
-
if (patterns.includes(caller))
|
|
952
|
-
return caller;
|
|
947
|
+
// src/shared/guard-variant-routing.ts
|
|
948
|
+
function matchVariantPatterns(keys, caller) {
|
|
949
|
+
const callerParts = caller.split("/");
|
|
953
950
|
const matches = [];
|
|
954
|
-
for (const pattern of
|
|
951
|
+
for (const pattern of keys) {
|
|
955
952
|
if (!pattern.includes(":"))
|
|
956
953
|
continue;
|
|
957
954
|
const patternParts = pattern.split("/");
|
|
958
|
-
const callerParts = caller.split("/");
|
|
959
955
|
if (patternParts.length !== callerParts.length)
|
|
960
956
|
continue;
|
|
961
|
-
let
|
|
962
|
-
for (let
|
|
963
|
-
if (patternParts[
|
|
957
|
+
let matchesCaller = true;
|
|
958
|
+
for (let index = 0; index < patternParts.length; index++) {
|
|
959
|
+
if (patternParts[index].startsWith(":"))
|
|
964
960
|
continue;
|
|
965
|
-
if (patternParts[
|
|
966
|
-
|
|
961
|
+
if (patternParts[index] !== callerParts[index]) {
|
|
962
|
+
matchesCaller = false;
|
|
967
963
|
break;
|
|
968
964
|
}
|
|
969
965
|
}
|
|
970
|
-
if (
|
|
966
|
+
if (matchesCaller)
|
|
971
967
|
matches.push(pattern);
|
|
972
968
|
}
|
|
973
|
-
|
|
974
|
-
|
|
969
|
+
return matches;
|
|
970
|
+
}
|
|
971
|
+
function resolveGuardVariantKey(input) {
|
|
972
|
+
if (input.kind === "single") {
|
|
973
|
+
return { ok: true, key: "_default" };
|
|
974
|
+
}
|
|
975
|
+
const { keys, caller, reservedKeys } = input;
|
|
976
|
+
for (const key of keys) {
|
|
977
|
+
if (reservedKeys.has(key)) {
|
|
978
|
+
return {
|
|
979
|
+
ok: false,
|
|
980
|
+
code: "reserved-key",
|
|
981
|
+
key,
|
|
982
|
+
keys
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
const hasDefault = keys.includes("default");
|
|
987
|
+
if (typeof caller !== "string") {
|
|
988
|
+
if (hasDefault)
|
|
989
|
+
return { ok: true, key: "default" };
|
|
990
|
+
return { ok: false, code: "missing-caller", keys };
|
|
991
|
+
}
|
|
992
|
+
if (caller.trim().length === 0) {
|
|
993
|
+
if (hasDefault)
|
|
994
|
+
return { ok: true, key: "default" };
|
|
995
|
+
return { ok: false, code: "unknown-caller", caller, keys };
|
|
996
|
+
}
|
|
997
|
+
if (keys.includes(caller)) {
|
|
998
|
+
return { ok: true, key: caller };
|
|
999
|
+
}
|
|
1000
|
+
const matches = matchVariantPatterns(keys, caller);
|
|
1001
|
+
if (matches.length === 1) {
|
|
1002
|
+
return { ok: true, key: matches[0] };
|
|
1003
|
+
}
|
|
975
1004
|
if (matches.length > 1) {
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1005
|
+
return {
|
|
1006
|
+
ok: false,
|
|
1007
|
+
code: "ambiguous-caller",
|
|
1008
|
+
caller,
|
|
1009
|
+
keys,
|
|
1010
|
+
matches
|
|
1011
|
+
};
|
|
979
1012
|
}
|
|
980
|
-
|
|
1013
|
+
if (hasDefault)
|
|
1014
|
+
return { ok: true, key: "default" };
|
|
1015
|
+
return { ok: false, code: "unknown-caller", caller, keys };
|
|
981
1016
|
}
|
|
982
1017
|
|
|
983
1018
|
// src/runtime/policy.ts
|
|
@@ -1451,12 +1486,15 @@ function applyForcedCountWhere(container, forcedCountWhere) {
|
|
|
1451
1486
|
}
|
|
1452
1487
|
function resolvedWhereCoversConstraint(where, constraint) {
|
|
1453
1488
|
if (constraint.fields.length === 1) {
|
|
1454
|
-
|
|
1489
|
+
const value2 = where[constraint.fields[0]];
|
|
1490
|
+
return value2 !== null && value2 !== void 0;
|
|
1455
1491
|
}
|
|
1456
1492
|
const value = where[constraint.selector];
|
|
1457
1493
|
if (!isPlainObject(value))
|
|
1458
1494
|
return false;
|
|
1459
|
-
return constraint.fields.every(
|
|
1495
|
+
return constraint.fields.every(
|
|
1496
|
+
(field) => value[field] !== null && value[field] !== void 0
|
|
1497
|
+
);
|
|
1460
1498
|
}
|
|
1461
1499
|
function validateResolvedUniqueWhere(model, where, method, uniqueMap) {
|
|
1462
1500
|
const constraints = uniqueMap[model];
|
|
@@ -1483,17 +1521,16 @@ function assertDirectUniqueShapeValue(model, field, value, typeMap) {
|
|
|
1483
1521
|
);
|
|
1484
1522
|
}
|
|
1485
1523
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
const keys = Object.keys(value);
|
|
1524
|
+
const actual = isForcedValue(value) ? value.value : value;
|
|
1525
|
+
if (isPlainObject(actual)) {
|
|
1526
|
+
const keys = Object.keys(actual);
|
|
1490
1527
|
throw new ShapeError(
|
|
1491
1528
|
`Invalid unique where shape for "${model ?? "unknown"}.${field}". Prisma WhereUniqueInput does not accept filter operator objects${keys.length ? `: ${keys.join(", ")}` : ""}. Use { ${field}: true } in guard shape and { ${field}: value } in request args.`
|
|
1492
1529
|
);
|
|
1493
1530
|
}
|
|
1494
|
-
if (
|
|
1531
|
+
if (actual === null || actual === void 0) {
|
|
1495
1532
|
throw new ShapeError(
|
|
1496
|
-
`Invalid unique where shape for "${model ?? "unknown"}.${field}". Unique fields must use true or a forced value.`
|
|
1533
|
+
`Invalid unique where shape for "${model ?? "unknown"}.${field}". Unique fields must use true or a forced non-null value.`
|
|
1497
1534
|
);
|
|
1498
1535
|
}
|
|
1499
1536
|
}
|
|
@@ -1507,9 +1544,8 @@ function shapeCoversConstraint(where, constraint, model, typeMap) {
|
|
|
1507
1544
|
}
|
|
1508
1545
|
if (!(constraint.selector in where))
|
|
1509
1546
|
return false;
|
|
1510
|
-
const
|
|
1511
|
-
|
|
1512
|
-
return true;
|
|
1547
|
+
const rawSelectorValue = where[constraint.selector];
|
|
1548
|
+
const selectorValue = isForcedValue(rawSelectorValue) ? rawSelectorValue.value : rawSelectorValue;
|
|
1513
1549
|
if (!isPlainObject(selectorValue)) {
|
|
1514
1550
|
throw new ShapeError(
|
|
1515
1551
|
`Compound unique selector "${model}.${constraint.selector}" must be an object with fields: ${constraint.fields.join(", ")}`
|
|
@@ -2012,8 +2048,18 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2012
2048
|
(constraint) => constraint.selector === selector
|
|
2013
2049
|
) ?? null;
|
|
2014
2050
|
}
|
|
2015
|
-
function
|
|
2051
|
+
function buildUniqueWhereFieldSchema(fieldMeta, allowNullableFilter) {
|
|
2016
2052
|
const schema = buildDirectScalarSchema(fieldMeta, enumMap, scalarBase);
|
|
2053
|
+
if (allowNullableFilter && !fieldMeta.isRequired) {
|
|
2054
|
+
return schema.nullable();
|
|
2055
|
+
}
|
|
2056
|
+
return schema;
|
|
2057
|
+
}
|
|
2058
|
+
function parseForcedUniqueValue(model, fieldName, fieldMeta, value, allowNullableFilter) {
|
|
2059
|
+
const schema = buildUniqueWhereFieldSchema(
|
|
2060
|
+
fieldMeta,
|
|
2061
|
+
allowNullableFilter
|
|
2062
|
+
);
|
|
2017
2063
|
const actual = isForcedValue(value) ? value.value : value;
|
|
2018
2064
|
try {
|
|
2019
2065
|
return schema.parse(actual);
|
|
@@ -2091,10 +2137,9 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2091
2137
|
`Invalid compound unique where shape for "${model}.${key}.${fieldName}". Prisma compound unique selectors do not accept filter operator objects${operators.length ? `: ${operators.join(", ")}` : ""}. Use direct values only.`
|
|
2092
2138
|
);
|
|
2093
2139
|
}
|
|
2094
|
-
const directSchema2 =
|
|
2140
|
+
const directSchema2 = buildUniqueWhereFieldSchema(
|
|
2095
2141
|
fieldMeta2,
|
|
2096
|
-
|
|
2097
|
-
scalarBase
|
|
2142
|
+
false
|
|
2098
2143
|
);
|
|
2099
2144
|
if (fieldValue === true) {
|
|
2100
2145
|
nestedSchemas[fieldName] = directSchema2;
|
|
@@ -2103,7 +2148,8 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2103
2148
|
model,
|
|
2104
2149
|
fieldName,
|
|
2105
2150
|
fieldMeta2,
|
|
2106
|
-
fieldValue
|
|
2151
|
+
fieldValue,
|
|
2152
|
+
false
|
|
2107
2153
|
);
|
|
2108
2154
|
}
|
|
2109
2155
|
}
|
|
@@ -2142,7 +2188,7 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2142
2188
|
`Invalid unique where shape for "${model}.${key}". Prisma WhereUniqueInput does not accept operators: ${keys.join(", ")}. Use a direct unique value shape, for example { ${key}: true }.`
|
|
2143
2189
|
);
|
|
2144
2190
|
}
|
|
2145
|
-
const directSchema =
|
|
2191
|
+
const directSchema = buildUniqueWhereFieldSchema(fieldMeta, true);
|
|
2146
2192
|
if (value === true) {
|
|
2147
2193
|
fieldSchemas[key] = directSchema;
|
|
2148
2194
|
continue;
|
|
@@ -2151,7 +2197,8 @@ function createWhereBuilder(typeMap, enumMap, scalarBase, uniqueMap = {}) {
|
|
|
2151
2197
|
model,
|
|
2152
2198
|
key,
|
|
2153
2199
|
fieldMeta,
|
|
2154
|
-
value
|
|
2200
|
+
value,
|
|
2201
|
+
true
|
|
2155
2202
|
);
|
|
2156
2203
|
forcedOnlyKeys.add(key);
|
|
2157
2204
|
}
|
|
@@ -3410,22 +3457,27 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3410
3457
|
forcedSelectCountWhere
|
|
3411
3458
|
};
|
|
3412
3459
|
}
|
|
3413
|
-
function
|
|
3414
|
-
const
|
|
3415
|
-
if (
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
built = buildShapeZodSchema(model, method, resolved);
|
|
3425
|
-
} else {
|
|
3426
|
-
built = builtCache.get("default");
|
|
3460
|
+
function throwQueryVariantResolution(resolution) {
|
|
3461
|
+
const keys = resolution.keys.map((key) => `"${key}"`).join(", ");
|
|
3462
|
+
if (resolution.code === "reserved-key") {
|
|
3463
|
+
throw new ShapeError(
|
|
3464
|
+
`Caller key "${resolution.key}" collides with reserved shape config key. Rename the caller path.`
|
|
3465
|
+
);
|
|
3466
|
+
}
|
|
3467
|
+
if (resolution.code === "missing-caller") {
|
|
3468
|
+
throw new CallerError(
|
|
3469
|
+
`Missing caller. This query uses named shape routing with keys: ${keys}. Provide caller via opts.caller.`
|
|
3470
|
+
);
|
|
3427
3471
|
}
|
|
3428
|
-
|
|
3472
|
+
if (resolution.code === "ambiguous-caller") {
|
|
3473
|
+
const matches = (resolution.matches ?? []).map((pattern) => `"${pattern}"`).join(", ");
|
|
3474
|
+
throw new CallerError(
|
|
3475
|
+
`Ambiguous caller "${resolution.caller}" matches multiple patterns: ${matches}`
|
|
3476
|
+
);
|
|
3477
|
+
}
|
|
3478
|
+
throw new CallerError(
|
|
3479
|
+
`Unknown caller: "${resolution.caller}". Allowed: ${keys}`
|
|
3480
|
+
);
|
|
3429
3481
|
}
|
|
3430
3482
|
function buildQuerySchema(model, method, config) {
|
|
3431
3483
|
const isSingle = typeof config === "function" || isShapeConfig(config);
|
|
@@ -3437,12 +3489,15 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3437
3489
|
);
|
|
3438
3490
|
}
|
|
3439
3491
|
if (!isSingle) {
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3492
|
+
const keys = Object.keys(config);
|
|
3493
|
+
const validation = resolveGuardVariantKey({
|
|
3494
|
+
kind: "named",
|
|
3495
|
+
keys,
|
|
3496
|
+
caller: void 0,
|
|
3497
|
+
reservedKeys: SHAPE_CONFIG_KEYS
|
|
3498
|
+
});
|
|
3499
|
+
if (!validation.ok && validation.code === "reserved-key") {
|
|
3500
|
+
throwQueryVariantResolution(validation);
|
|
3446
3501
|
}
|
|
3447
3502
|
for (const [key, shapeOrFn] of Object.entries(
|
|
3448
3503
|
config
|
|
@@ -3480,50 +3535,37 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
|
3480
3535
|
);
|
|
3481
3536
|
}
|
|
3482
3537
|
const namedConfig = config;
|
|
3483
|
-
const
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
);
|
|
3495
|
-
}
|
|
3496
|
-
const allowed = Object.keys(namedConfig);
|
|
3497
|
-
throw new CallerError(
|
|
3498
|
-
`Missing caller. This query uses named shape routing with keys: ${allowed.map((k) => `"${k}"`).join(", ")}. Provide caller via opts.caller.`
|
|
3499
|
-
);
|
|
3500
|
-
}
|
|
3501
|
-
const matched = matchCaller(namedConfig, caller);
|
|
3502
|
-
if (!matched) {
|
|
3503
|
-
if ("default" in namedConfig) {
|
|
3504
|
-
return resolveDefaultShape(
|
|
3505
|
-
namedConfig,
|
|
3506
|
-
model,
|
|
3507
|
-
method,
|
|
3508
|
-
normalizedBody,
|
|
3509
|
-
opts,
|
|
3510
|
-
builtCache,
|
|
3511
|
-
isUnique
|
|
3512
|
-
);
|
|
3513
|
-
}
|
|
3514
|
-
const allowed = Object.keys(namedConfig);
|
|
3515
|
-
throw new CallerError(
|
|
3516
|
-
`Unknown caller: "${caller}". Allowed: ${allowed.map((k) => `"${k}"`).join(", ")}`
|
|
3517
|
-
);
|
|
3518
|
-
}
|
|
3519
|
-
if (typeof matched.shape === "function") {
|
|
3520
|
-
const resolved = resolveAndValidateShape(matched.shape, opts?.ctx);
|
|
3538
|
+
const resolution = resolveGuardVariantKey({
|
|
3539
|
+
kind: "named",
|
|
3540
|
+
keys: Object.keys(namedConfig),
|
|
3541
|
+
caller: opts?.caller,
|
|
3542
|
+
reservedKeys: SHAPE_CONFIG_KEYS
|
|
3543
|
+
});
|
|
3544
|
+
if (!resolution.ok)
|
|
3545
|
+
throwQueryVariantResolution(resolution);
|
|
3546
|
+
const shapeOrFn = namedConfig[resolution.key];
|
|
3547
|
+
if (typeof shapeOrFn === "function") {
|
|
3548
|
+
const resolved = resolveAndValidateShape(shapeOrFn, opts?.ctx);
|
|
3521
3549
|
built = buildShapeZodSchema(model, method, resolved);
|
|
3522
3550
|
} else {
|
|
3523
|
-
built = builtCache.get(
|
|
3551
|
+
built = builtCache.get(resolution.key);
|
|
3524
3552
|
}
|
|
3525
3553
|
}
|
|
3526
|
-
|
|
3554
|
+
const result = applyBuiltShape(
|
|
3555
|
+
built,
|
|
3556
|
+
normalizedBody,
|
|
3557
|
+
isUnique,
|
|
3558
|
+
model
|
|
3559
|
+
);
|
|
3560
|
+
if (isUnique && result.where) {
|
|
3561
|
+
validateResolvedUniqueWhere(
|
|
3562
|
+
model,
|
|
3563
|
+
result.where,
|
|
3564
|
+
method,
|
|
3565
|
+
uniqueMap
|
|
3566
|
+
);
|
|
3567
|
+
}
|
|
3568
|
+
return result;
|
|
3527
3569
|
}
|
|
3528
3570
|
};
|
|
3529
3571
|
}
|
|
@@ -4755,34 +4797,43 @@ function resolveDynamicShape(shapeFn, ctx, context) {
|
|
|
4755
4797
|
}
|
|
4756
4798
|
return result;
|
|
4757
4799
|
}
|
|
4758
|
-
function
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
throw new ShapeError(
|
|
4765
|
-
`Caller key "${key}" collides with reserved guard shape key. Rename the caller path.`
|
|
4766
|
-
);
|
|
4767
|
-
}
|
|
4800
|
+
function throwVariantResolution(resolution) {
|
|
4801
|
+
const keys = resolution.keys.map((key) => `"${key}"`).join(", ");
|
|
4802
|
+
if (resolution.code === "reserved-key") {
|
|
4803
|
+
throw new ShapeError(
|
|
4804
|
+
`Caller key "${resolution.key}" collides with reserved guard shape key. Rename the caller path.`
|
|
4805
|
+
);
|
|
4768
4806
|
}
|
|
4769
|
-
if (
|
|
4770
|
-
if ("default" in input) {
|
|
4771
|
-
return resolveShapeEntry(input.default, body, contextFn, "default");
|
|
4772
|
-
}
|
|
4807
|
+
if (resolution.code === "missing-caller") {
|
|
4773
4808
|
throw new CallerError(
|
|
4774
|
-
`Missing caller. This guard uses named shape routing with keys: ${keys
|
|
4809
|
+
`Missing caller. This guard uses named shape routing with keys: ${keys}. Provide caller via guard(input, caller).`
|
|
4775
4810
|
);
|
|
4776
4811
|
}
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
return resolveShapeEntry(input.default, body, contextFn, "default");
|
|
4812
|
+
if (resolution.code === "ambiguous-caller") {
|
|
4813
|
+
const matches = (resolution.matches ?? []).map((pattern) => `"${pattern}"`).join(", ");
|
|
4814
|
+
throw new CallerError(
|
|
4815
|
+
`Ambiguous caller "${resolution.caller}" matches multiple patterns: ${matches}`
|
|
4816
|
+
);
|
|
4783
4817
|
}
|
|
4784
4818
|
throw new CallerError(
|
|
4785
|
-
`Unknown caller: "${caller}". Allowed: ${keys
|
|
4819
|
+
`Unknown caller: "${resolution.caller}". Allowed: ${keys}`
|
|
4820
|
+
);
|
|
4821
|
+
}
|
|
4822
|
+
function resolveNamedShape(input, body, contextFn, explicitCaller) {
|
|
4823
|
+
assertNoCallerInBody(body);
|
|
4824
|
+
const resolution = resolveGuardVariantKey({
|
|
4825
|
+
kind: "named",
|
|
4826
|
+
keys: Object.keys(input),
|
|
4827
|
+
caller: explicitCaller,
|
|
4828
|
+
reservedKeys: GUARD_SHAPE_KEYS
|
|
4829
|
+
});
|
|
4830
|
+
if (!resolution.ok)
|
|
4831
|
+
throwVariantResolution(resolution);
|
|
4832
|
+
return resolveShapeEntry(
|
|
4833
|
+
input[resolution.key],
|
|
4834
|
+
body,
|
|
4835
|
+
contextFn,
|
|
4836
|
+
resolution.key
|
|
4786
4837
|
);
|
|
4787
4838
|
}
|
|
4788
4839
|
function resolveShapeEntry(entry, body, contextFn, matchedKey) {
|