prisma-guard 1.21.0 → 1.22.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.
@@ -25,6 +25,7 @@ __export(runtime_exports, {
25
25
  ShapeError: () => ShapeError,
26
26
  createGuard: () => createGuard,
27
27
  force: () => force,
28
+ namedShapes: () => namedShapes,
28
29
  unsupported: () => unsupported
29
30
  });
30
31
  module.exports = __toCommonJS(runtime_exports);
@@ -222,7 +223,13 @@ function createScalarBase(strictDecimal) {
222
223
  import_zod.z.string().regex(/^-?\d+$/).transform((v) => BigInt(v))
223
224
  ]),
224
225
  Boolean: () => import_zod.z.boolean(),
225
- DateTime: () => import_zod.z.union([import_zod.z.date(), import_zod.z.string().datetime({ offset: true })]).pipe(import_zod.z.coerce.date()),
226
+ DateTime: () => import_zod.z.union([
227
+ import_zod.z.date(),
228
+ import_zod.z.string().refine(
229
+ (s) => !isNaN(Date.parse(s)),
230
+ "Invalid date string"
231
+ )
232
+ ]).pipe(import_zod.z.coerce.date()),
226
233
  Json: () => import_zod.z.unknown().refine(
227
234
  isJsonSafe,
228
235
  "Value must be JSON-serializable (no undefined, functions, symbols, class instances, NaN, Infinity, or circular references)"
@@ -239,8 +246,8 @@ function wrapWithInputCoercion(fieldType, isList, schema) {
239
246
  break;
240
247
  case "Int":
241
248
  itemCoercion = import_zod.z.union([
242
- import_zod.z.number().int(),
243
- import_zod.z.string().regex(/^-?\d+$/).transform(Number)
249
+ import_zod.z.number().transform((v) => Math.trunc(v)).pipe(import_zod.z.number().int()),
250
+ import_zod.z.string().regex(/^-?\d+(\.\d+)?$/).transform((v) => Math.trunc(Number(v)))
244
251
  ]);
245
252
  break;
246
253
  case "Float":
@@ -818,7 +825,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefault
818
825
  var import_zod7 = require("zod");
819
826
 
820
827
  // src/shared/constants.ts
821
- var SHAPE_CONFIG_KEYS = /* @__PURE__ */ new Set([
828
+ var SHAPE_CONFIG_KEY_LIST = [
822
829
  "where",
823
830
  "include",
824
831
  "select",
@@ -834,13 +841,15 @@ var SHAPE_CONFIG_KEYS = /* @__PURE__ */ new Set([
834
841
  "_min",
835
842
  "_max",
836
843
  "by"
837
- ]);
838
- var GUARD_SHAPE_KEYS = /* @__PURE__ */ new Set([
844
+ ];
845
+ var SHAPE_CONFIG_KEYS = new Set(SHAPE_CONFIG_KEY_LIST);
846
+ var GUARD_SHAPE_KEY_LIST = [
839
847
  "data",
840
848
  "create",
841
849
  "update",
842
- ...SHAPE_CONFIG_KEYS
843
- ]);
850
+ ...SHAPE_CONFIG_KEY_LIST
851
+ ];
852
+ var GUARD_SHAPE_KEYS = new Set(GUARD_SHAPE_KEY_LIST);
844
853
  var COMBINATOR_KEYS = /* @__PURE__ */ new Set(["AND", "OR", "NOT"]);
845
854
  var TO_MANY_RELATION_OPS = /* @__PURE__ */ new Set(["some", "every", "none"]);
846
855
  var TO_ONE_RELATION_OPS = /* @__PURE__ */ new Set(["is", "isNot"]);
@@ -2478,67 +2487,51 @@ function createProjectionBuilder(typeMap, enumMap, deps) {
2478
2487
  return { buildIncludeSchema, buildSelectSchema, buildIncludeCountSchema };
2479
2488
  }
2480
2489
 
2481
- // src/runtime/query-builder.ts
2482
- var METHOD_ALLOWED_ARGS = {
2483
- findMany: /* @__PURE__ */ new Set([
2484
- "where",
2485
- "include",
2486
- "select",
2487
- "orderBy",
2488
- "cursor",
2489
- "take",
2490
- "skip",
2491
- "distinct"
2492
- ]),
2493
- findFirst: /* @__PURE__ */ new Set([
2494
- "where",
2495
- "include",
2496
- "select",
2497
- "orderBy",
2498
- "cursor",
2499
- "take",
2500
- "skip",
2501
- "distinct"
2502
- ]),
2503
- findFirstOrThrow: /* @__PURE__ */ new Set([
2504
- "where",
2505
- "include",
2506
- "select",
2507
- "orderBy",
2508
- "cursor",
2509
- "take",
2510
- "skip",
2511
- "distinct"
2512
- ]),
2513
- findUnique: /* @__PURE__ */ new Set(["where", "include", "select"]),
2514
- findUniqueOrThrow: /* @__PURE__ */ new Set(["where", "include", "select"]),
2515
- count: /* @__PURE__ */ new Set(["where", "select", "cursor", "orderBy", "skip", "take"]),
2516
- aggregate: /* @__PURE__ */ new Set([
2517
- "where",
2518
- "orderBy",
2519
- "cursor",
2520
- "take",
2521
- "skip",
2522
- "_count",
2523
- "_avg",
2524
- "_sum",
2525
- "_min",
2526
- "_max"
2527
- ]),
2528
- groupBy: /* @__PURE__ */ new Set([
2529
- "where",
2530
- "by",
2531
- "having",
2532
- "_count",
2533
- "_avg",
2534
- "_sum",
2535
- "_min",
2536
- "_max",
2537
- "orderBy",
2538
- "take",
2539
- "skip"
2540
- ])
2490
+ // src/shared/operation-shape-keys.ts
2491
+ var OPERATION_SHAPE_KEYS = {
2492
+ findMany: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
2493
+ findFirst: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
2494
+ findFirstOrThrow: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
2495
+ findUnique: ["where", "include", "select"],
2496
+ findUniqueOrThrow: ["where", "include", "select"],
2497
+ findManyPaginated: ["where", "include", "select", "orderBy", "cursor", "take", "skip", "distinct"],
2498
+ count: ["where", "select", "cursor", "orderBy", "skip", "take"],
2499
+ aggregate: ["where", "orderBy", "cursor", "take", "skip", "_count", "_avg", "_sum", "_min", "_max"],
2500
+ groupBy: ["where", "by", "having", "_count", "_avg", "_sum", "_min", "_max", "orderBy", "take", "skip"],
2501
+ create: ["data", "select", "include"],
2502
+ createMany: ["data"],
2503
+ createManyAndReturn: ["data", "select", "include"],
2504
+ update: ["data", "where", "select", "include"],
2505
+ updateMany: ["data", "where"],
2506
+ updateManyAndReturn: ["data", "where", "select", "include"],
2507
+ upsert: ["where", "create", "update", "select", "include"],
2508
+ delete: ["where", "select", "include"],
2509
+ deleteMany: ["where"]
2541
2510
  };
2511
+ var READ_METHOD_ALLOWED_ARGS = {
2512
+ findMany: new Set(OPERATION_SHAPE_KEYS.findMany),
2513
+ findFirst: new Set(OPERATION_SHAPE_KEYS.findFirst),
2514
+ findFirstOrThrow: new Set(OPERATION_SHAPE_KEYS.findFirstOrThrow),
2515
+ findUnique: new Set(OPERATION_SHAPE_KEYS.findUnique),
2516
+ findUniqueOrThrow: new Set(OPERATION_SHAPE_KEYS.findUniqueOrThrow),
2517
+ count: new Set(OPERATION_SHAPE_KEYS.count),
2518
+ aggregate: new Set(OPERATION_SHAPE_KEYS.aggregate),
2519
+ groupBy: new Set(OPERATION_SHAPE_KEYS.groupBy)
2520
+ };
2521
+ var MUTATION_SHAPE_KEYS = {
2522
+ create: new Set(OPERATION_SHAPE_KEYS.create),
2523
+ createMany: new Set(OPERATION_SHAPE_KEYS.createMany),
2524
+ createManyAndReturn: new Set(OPERATION_SHAPE_KEYS.createManyAndReturn),
2525
+ update: new Set(OPERATION_SHAPE_KEYS.update),
2526
+ updateMany: new Set(OPERATION_SHAPE_KEYS.updateMany),
2527
+ updateManyAndReturn: new Set(OPERATION_SHAPE_KEYS.updateManyAndReturn),
2528
+ upsert: new Set(OPERATION_SHAPE_KEYS.upsert),
2529
+ delete: new Set(OPERATION_SHAPE_KEYS.delete),
2530
+ deleteMany: new Set(OPERATION_SHAPE_KEYS.deleteMany)
2531
+ };
2532
+
2533
+ // src/runtime/query-builder.ts
2534
+ var METHOD_ALLOWED_ARGS = READ_METHOD_ALLOWED_ARGS;
2542
2535
  var UNIQUE_WHERE_METHODS = /* @__PURE__ */ new Set([
2543
2536
  "findUnique",
2544
2537
  "findUniqueOrThrow"
@@ -2949,11 +2942,29 @@ function buildAndConditions(existingWhere, conditions) {
2949
2942
  return conditions[0];
2950
2943
  return { AND: conditions };
2951
2944
  }
2952
- function buildScopedUniqueWhere(existingWhere, conditions) {
2953
- if (!existingWhere) {
2945
+ function stripScopeFksFromWhere(where, scopeFks, log, model) {
2946
+ let result = where;
2947
+ for (const fk of scopeFks) {
2948
+ if (fk in result) {
2949
+ if (result === where)
2950
+ result = { ...where };
2951
+ log.warn(
2952
+ `prisma-guard: Scope FK "${fk}" found in where for model "${model}". Stripped in favor of scope context.`
2953
+ );
2954
+ delete result[fk];
2955
+ }
2956
+ }
2957
+ return result;
2958
+ }
2959
+ function buildScopedUniqueWhere(existingWhere, conditions, scopeFks, log, model) {
2960
+ let cleaned = existingWhere;
2961
+ if (cleaned) {
2962
+ cleaned = stripScopeFksFromWhere(cleaned, scopeFks, log, model);
2963
+ }
2964
+ if (!cleaned || Object.keys(cleaned).length === 0) {
2954
2965
  return conditions.length === 1 ? conditions[0] : { AND: conditions };
2955
2966
  }
2956
- const { AND: existingAnd, ...topLevel } = existingWhere;
2967
+ const { AND: existingAnd, ...topLevel } = cleaned;
2957
2968
  const allConditions = [];
2958
2969
  if (existingAnd !== void 0) {
2959
2970
  if (Array.isArray(existingAnd)) {
@@ -3053,6 +3064,13 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
3053
3064
  `prisma-guard: Invalid onScopeRelationWrite "${onScopeRelationWrite}". Allowed: ${[...VALID_ON_SCOPE_RELATION_WRITES].join(", ")}`
3054
3065
  );
3055
3066
  }
3067
+ const scopeFkSets = /* @__PURE__ */ new Map();
3068
+ for (const [model, entries] of Object.entries(scopeMap)) {
3069
+ const fks = /* @__PURE__ */ new Set();
3070
+ for (const entry of entries)
3071
+ fks.add(entry.fk);
3072
+ scopeFkSets.set(model, fks);
3073
+ }
3056
3074
  return {
3057
3075
  name: "prisma-guard-scope",
3058
3076
  query: {
@@ -3073,7 +3091,7 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
3073
3091
  if (missingRoots.length > 0) {
3074
3092
  if (isMutation || guardConfig.onMissingScopeContext === "error") {
3075
3093
  throw new PolicyError(
3076
- `Missing scope context for model "${model}": roots ${missingRoots.map((r) => `"${r}"`).join(", ")} not provided. All scope roots must be present.`
3094
+ `prisma-guard: Missing scope context for model "${model}": roots ${missingRoots.map((r) => `"${r}"`).join(", ")} not provided. All scope roots must be present.`
3077
3095
  );
3078
3096
  }
3079
3097
  if (guardConfig.onMissingScopeContext === "warn") {
@@ -3089,9 +3107,10 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
3089
3107
  const overrides = Object.fromEntries(
3090
3108
  presentScopes.map((s) => [s.fk, ctx[s.root]])
3091
3109
  );
3110
+ const modelFks = scopeFkSets.get(model) ?? /* @__PURE__ */ new Set();
3092
3111
  const nextArgs = { ...args };
3093
3112
  if (operation === "upsert") {
3094
- nextArgs.where = buildScopedUniqueWhere(args.where, conditions);
3113
+ nextArgs.where = buildScopedUniqueWhere(args.where, conditions, modelFks, log, model);
3095
3114
  if (args.create !== void 0 && args.create !== null) {
3096
3115
  if (typeof args.create !== "object" || Array.isArray(args.create)) {
3097
3116
  throw new ShapeError(`upsert expects create to be an object`);
@@ -3129,7 +3148,7 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
3129
3148
  if (FIND_UNIQUE_OPS.has(operation)) {
3130
3149
  if (findUniqueMode === "reject") {
3131
3150
  throw new PolicyError(
3132
- `Scoped model "${model}" does not allow ${operation} via scope extension (findUniqueMode is "reject"). Use findFirst with explicit where conditions instead.`
3151
+ `prisma-guard: Scoped model "${model}" does not allow ${operation} via scope extension (findUniqueMode is "reject"). Use findFirst with explicit where conditions instead.`
3133
3152
  );
3134
3153
  }
3135
3154
  return handleFindUnique(
@@ -3199,7 +3218,7 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
3199
3218
  return query(nextArgs);
3200
3219
  }
3201
3220
  if (UNIQUE_MUTATION_OPS.has(operation)) {
3202
- nextArgs.where = buildScopedUniqueWhere(args.where, conditions);
3221
+ nextArgs.where = buildScopedUniqueWhere(args.where, conditions, modelFks, log, model);
3203
3222
  if (args.data !== void 0 && args.data !== null) {
3204
3223
  if (typeof args.data !== "object" || Array.isArray(args.data)) {
3205
3224
  throw new ShapeError(`${operation} expects data to be an object`);
@@ -3239,7 +3258,7 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
3239
3258
  return query(nextArgs);
3240
3259
  }
3241
3260
  throw new ShapeError(
3242
- `Unknown operation "${operation}" on scoped model "${model}". Update prisma-guard to handle this operation.`
3261
+ `prisma-guard: Unknown operation "${operation}" on scoped model "${model}". Update prisma-guard to handle this operation.`
3243
3262
  );
3244
3263
  }
3245
3264
  }
@@ -3310,7 +3329,7 @@ async function handleFindUnique(args, query, conditions, scopes, operation, log,
3310
3329
  if (!looseEqual(verifyObj[fk], value, log, fk)) {
3311
3330
  if (operation === "findUniqueOrThrow") {
3312
3331
  throw new PolicyError(
3313
- `Record on model "${model}" not accessible in current scope`
3332
+ `prisma-guard: Record on model "${model}" not accessible in current scope`
3314
3333
  );
3315
3334
  }
3316
3335
  return null;
@@ -4382,8 +4401,10 @@ function createModelGuardExtension(config) {
4382
4401
  equalityFields.add(key);
4383
4402
  continue;
4384
4403
  }
4385
- if (isPlainObject(value) && "equals" in value) {
4386
- equalityFields.add(key);
4404
+ if (isPlainObject(value)) {
4405
+ if ("equals" in value) {
4406
+ equalityFields.add(key);
4407
+ }
4387
4408
  continue;
4388
4409
  }
4389
4410
  if (value !== null && value !== void 0) {
@@ -5258,6 +5279,11 @@ function createGuard(config) {
5258
5279
  }
5259
5280
  };
5260
5281
  }
5282
+
5283
+ // src/shared/typed-shape.ts
5284
+ function namedShapes(shapes) {
5285
+ return shapes;
5286
+ }
5261
5287
  // Annotate the CommonJS export names for ESM import in node:
5262
5288
  0 && (module.exports = {
5263
5289
  CallerError,
@@ -5265,6 +5291,7 @@ function createGuard(config) {
5265
5291
  ShapeError,
5266
5292
  createGuard,
5267
5293
  force,
5294
+ namedShapes,
5268
5295
  unsupported
5269
5296
  });
5270
5297
  //# sourceMappingURL=index.cjs.map