prisma-guard 1.31.0 → 1.32.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.
@@ -944,40 +944,75 @@ function unsupported() {
944
944
  return marker;
945
945
  }
946
946
 
947
- // src/shared/match-caller.ts
948
- function matchCallerPattern(patterns, caller) {
949
- if (typeof caller !== "string" || caller.trim().length === 0)
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 patterns) {
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 ok = true;
962
- for (let i = 0; i < patternParts.length; i++) {
963
- if (patternParts[i].startsWith(":"))
957
+ let matchesCaller = true;
958
+ for (let index = 0; index < patternParts.length; index++) {
959
+ if (patternParts[index].startsWith(":"))
964
960
  continue;
965
- if (patternParts[i] !== callerParts[i]) {
966
- ok = false;
961
+ if (patternParts[index] !== callerParts[index]) {
962
+ matchesCaller = false;
967
963
  break;
968
964
  }
969
965
  }
970
- if (ok)
966
+ if (matchesCaller)
971
967
  matches.push(pattern);
972
968
  }
973
- if (matches.length === 0)
974
- return null;
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
- throw new CallerError(
977
- `Ambiguous caller "${caller}" matches multiple patterns: ${matches.map((p) => `"${p}"`).join(", ")}`
978
- );
1005
+ return {
1006
+ ok: false,
1007
+ code: "ambiguous-caller",
1008
+ caller,
1009
+ keys,
1010
+ matches
1011
+ };
979
1012
  }
980
- return matches[0];
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
@@ -3410,22 +3445,27 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
3410
3445
  forcedSelectCountWhere
3411
3446
  };
3412
3447
  }
3413
- function matchCaller(shapes, caller) {
3414
- const matched = matchCallerPattern(Object.keys(shapes), caller);
3415
- if (!matched)
3416
- return null;
3417
- return { key: matched, shape: shapes[matched] };
3418
- }
3419
- function resolveDefaultShape(config, model, method, normalizedBody, opts, builtCache, isUnique) {
3420
- const shapeOrFn = config.default;
3421
- let built;
3422
- if (typeof shapeOrFn === "function") {
3423
- const resolved = resolveAndValidateShape(shapeOrFn, opts?.ctx);
3424
- built = buildShapeZodSchema(model, method, resolved);
3425
- } else {
3426
- built = builtCache.get("default");
3448
+ function throwQueryVariantResolution(resolution) {
3449
+ const keys = resolution.keys.map((key) => `"${key}"`).join(", ");
3450
+ if (resolution.code === "reserved-key") {
3451
+ throw new ShapeError(
3452
+ `Caller key "${resolution.key}" collides with reserved shape config key. Rename the caller path.`
3453
+ );
3454
+ }
3455
+ if (resolution.code === "missing-caller") {
3456
+ throw new CallerError(
3457
+ `Missing caller. This query uses named shape routing with keys: ${keys}. Provide caller via opts.caller.`
3458
+ );
3427
3459
  }
3428
- return applyBuiltShape(built, normalizedBody, isUnique);
3460
+ if (resolution.code === "ambiguous-caller") {
3461
+ const matches = (resolution.matches ?? []).map((pattern) => `"${pattern}"`).join(", ");
3462
+ throw new CallerError(
3463
+ `Ambiguous caller "${resolution.caller}" matches multiple patterns: ${matches}`
3464
+ );
3465
+ }
3466
+ throw new CallerError(
3467
+ `Unknown caller: "${resolution.caller}". Allowed: ${keys}`
3468
+ );
3429
3469
  }
3430
3470
  function buildQuerySchema(model, method, config) {
3431
3471
  const isSingle = typeof config === "function" || isShapeConfig(config);
@@ -3437,12 +3477,15 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
3437
3477
  );
3438
3478
  }
3439
3479
  if (!isSingle) {
3440
- for (const key of Object.keys(config)) {
3441
- if (SHAPE_CONFIG_KEYS.has(key)) {
3442
- throw new ShapeError(
3443
- `Caller key "${key}" collides with reserved shape config key. Rename the caller path.`
3444
- );
3445
- }
3480
+ const keys = Object.keys(config);
3481
+ const validation = resolveGuardVariantKey({
3482
+ kind: "named",
3483
+ keys,
3484
+ caller: void 0,
3485
+ reservedKeys: SHAPE_CONFIG_KEYS
3486
+ });
3487
+ if (!validation.ok && validation.code === "reserved-key") {
3488
+ throwQueryVariantResolution(validation);
3446
3489
  }
3447
3490
  for (const [key, shapeOrFn] of Object.entries(
3448
3491
  config
@@ -3480,47 +3523,20 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
3480
3523
  );
3481
3524
  }
3482
3525
  const namedConfig = config;
3483
- const caller = opts?.caller;
3484
- if (typeof caller !== "string") {
3485
- if ("default" in namedConfig) {
3486
- return resolveDefaultShape(
3487
- namedConfig,
3488
- model,
3489
- method,
3490
- normalizedBody,
3491
- opts,
3492
- builtCache,
3493
- isUnique
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);
3526
+ const resolution = resolveGuardVariantKey({
3527
+ kind: "named",
3528
+ keys: Object.keys(namedConfig),
3529
+ caller: opts?.caller,
3530
+ reservedKeys: SHAPE_CONFIG_KEYS
3531
+ });
3532
+ if (!resolution.ok)
3533
+ throwQueryVariantResolution(resolution);
3534
+ const shapeOrFn = namedConfig[resolution.key];
3535
+ if (typeof shapeOrFn === "function") {
3536
+ const resolved = resolveAndValidateShape(shapeOrFn, opts?.ctx);
3521
3537
  built = buildShapeZodSchema(model, method, resolved);
3522
3538
  } else {
3523
- built = builtCache.get(matched.key);
3539
+ built = builtCache.get(resolution.key);
3524
3540
  }
3525
3541
  }
3526
3542
  return applyBuiltShape(built, normalizedBody, isUnique);
@@ -4755,34 +4771,43 @@ function resolveDynamicShape(shapeFn, ctx, context) {
4755
4771
  }
4756
4772
  return result;
4757
4773
  }
4758
- function resolveNamedShape(input, body, contextFn, explicitCaller) {
4759
- assertNoCallerInBody(body);
4760
- const caller = explicitCaller;
4761
- const keys = Object.keys(input);
4762
- for (const key of keys) {
4763
- if (GUARD_SHAPE_KEYS.has(key)) {
4764
- throw new ShapeError(
4765
- `Caller key "${key}" collides with reserved guard shape key. Rename the caller path.`
4766
- );
4767
- }
4774
+ function throwVariantResolution(resolution) {
4775
+ const keys = resolution.keys.map((key) => `"${key}"`).join(", ");
4776
+ if (resolution.code === "reserved-key") {
4777
+ throw new ShapeError(
4778
+ `Caller key "${resolution.key}" collides with reserved guard shape key. Rename the caller path.`
4779
+ );
4768
4780
  }
4769
- if (typeof caller !== "string") {
4770
- if ("default" in input) {
4771
- return resolveShapeEntry(input.default, body, contextFn, "default");
4772
- }
4781
+ if (resolution.code === "missing-caller") {
4773
4782
  throw new CallerError(
4774
- `Missing caller. This guard uses named shape routing with keys: ${keys.map((k) => `"${k}"`).join(", ")}. Provide caller via guard(input, caller).`
4783
+ `Missing caller. This guard uses named shape routing with keys: ${keys}. Provide caller via guard(input, caller).`
4775
4784
  );
4776
4785
  }
4777
- const matched = matchCallerPattern(keys, caller);
4778
- if (matched) {
4779
- return resolveShapeEntry(input[matched], body, contextFn, matched);
4780
- }
4781
- if ("default" in input) {
4782
- return resolveShapeEntry(input.default, body, contextFn, "default");
4786
+ if (resolution.code === "ambiguous-caller") {
4787
+ const matches = (resolution.matches ?? []).map((pattern) => `"${pattern}"`).join(", ");
4788
+ throw new CallerError(
4789
+ `Ambiguous caller "${resolution.caller}" matches multiple patterns: ${matches}`
4790
+ );
4783
4791
  }
4784
4792
  throw new CallerError(
4785
- `Unknown caller: "${caller}". Allowed: ${keys.map((k) => `"${k}"`).join(", ")}`
4793
+ `Unknown caller: "${resolution.caller}". Allowed: ${keys}`
4794
+ );
4795
+ }
4796
+ function resolveNamedShape(input, body, contextFn, explicitCaller) {
4797
+ assertNoCallerInBody(body);
4798
+ const resolution = resolveGuardVariantKey({
4799
+ kind: "named",
4800
+ keys: Object.keys(input),
4801
+ caller: explicitCaller,
4802
+ reservedKeys: GUARD_SHAPE_KEYS
4803
+ });
4804
+ if (!resolution.ok)
4805
+ throwVariantResolution(resolution);
4806
+ return resolveShapeEntry(
4807
+ input[resolution.key],
4808
+ body,
4809
+ contextFn,
4810
+ resolution.key
4786
4811
  );
4787
4812
  }
4788
4813
  function resolveShapeEntry(entry, body, contextFn, matchedKey) {