prisma-guard 1.2.0 → 1.3.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 +362 -42
- package/dist/generator/index.js +97 -38
- package/dist/generator/index.js.map +1 -1
- package/dist/runtime/index.cjs +3444 -0
- package/dist/runtime/index.cjs.map +1 -0
- package/dist/runtime/index.d.cts +188 -0
- package/dist/runtime/index.d.ts +10 -2
- package/dist/runtime/index.js +633 -172
- package/dist/runtime/index.js.map +1 -1
- package/package.json +9 -3
package/dist/runtime/index.js
CHANGED
|
@@ -33,12 +33,6 @@ function formatZodError(err) {
|
|
|
33
33
|
}).join("; ");
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// src/runtime/schema-builder.ts
|
|
37
|
-
import { z as z3 } from "zod";
|
|
38
|
-
|
|
39
|
-
// src/runtime/zod-type-map.ts
|
|
40
|
-
import { z as z2 } from "zod";
|
|
41
|
-
|
|
42
36
|
// src/shared/scalar-base.ts
|
|
43
37
|
import { z } from "zod";
|
|
44
38
|
function isJsonSafe(value) {
|
|
@@ -89,43 +83,54 @@ function isJsonSafe(value) {
|
|
|
89
83
|
}
|
|
90
84
|
return true;
|
|
91
85
|
}
|
|
92
|
-
var
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
86
|
+
var DECIMAL_REGEX = /^-?(\d+\.?\d*|\.\d+)([eE]-?\d+)?$/;
|
|
87
|
+
var decimalStringSchema = z.string().refine(
|
|
88
|
+
(s) => DECIMAL_REGEX.test(s),
|
|
89
|
+
"Invalid decimal string"
|
|
90
|
+
);
|
|
91
|
+
var decimalObjectSchema = z.custom(
|
|
92
|
+
(v) => v !== null && typeof v === "object" && typeof v.toFixed === "function" && typeof v.toNumber === "function",
|
|
93
|
+
"Expected Decimal-compatible object"
|
|
94
|
+
);
|
|
95
|
+
function createDecimalFactory(strict) {
|
|
96
|
+
if (strict) {
|
|
97
|
+
return () => z.union([decimalStringSchema, decimalObjectSchema]);
|
|
98
|
+
}
|
|
99
|
+
return () => z.union([z.number(), decimalStringSchema, decimalObjectSchema]);
|
|
100
|
+
}
|
|
101
|
+
function createScalarBase(strictDecimal) {
|
|
102
|
+
return {
|
|
103
|
+
String: () => z.string(),
|
|
104
|
+
Int: () => z.number().int(),
|
|
105
|
+
Float: () => z.number(),
|
|
106
|
+
Decimal: createDecimalFactory(strictDecimal),
|
|
107
|
+
BigInt: () => z.union([
|
|
108
|
+
z.bigint(),
|
|
109
|
+
z.number().int().refine(
|
|
110
|
+
(v) => v >= Number.MIN_SAFE_INTEGER && v <= Number.MAX_SAFE_INTEGER,
|
|
111
|
+
"Number exceeds safe integer range for BigInt conversion"
|
|
112
|
+
).transform((v) => BigInt(v)),
|
|
113
|
+
z.string().regex(/^-?\d+$/).transform((v) => BigInt(v))
|
|
114
|
+
]),
|
|
115
|
+
Boolean: () => z.boolean(),
|
|
116
|
+
DateTime: () => z.union([
|
|
117
|
+
z.date(),
|
|
118
|
+
z.string().datetime({ offset: true })
|
|
119
|
+
]).pipe(z.coerce.date()),
|
|
120
|
+
Json: () => z.unknown().refine(isJsonSafe, "Value must be JSON-serializable (no undefined, functions, symbols, class instances, NaN, Infinity, or circular references)"),
|
|
121
|
+
Bytes: () => z.union([
|
|
122
|
+
z.string(),
|
|
123
|
+
z.custom((v) => v instanceof Uint8Array)
|
|
124
|
+
])
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
var SCALAR_BASE = createScalarBase(false);
|
|
128
|
+
|
|
129
|
+
// src/runtime/schema-builder.ts
|
|
130
|
+
import { z as z3 } from "zod";
|
|
127
131
|
|
|
128
132
|
// src/runtime/zod-type-map.ts
|
|
133
|
+
import { z as z2 } from "zod";
|
|
129
134
|
var SCALAR_OPERATORS = {
|
|
130
135
|
String: /* @__PURE__ */ new Set(["equals", "not", "contains", "startsWith", "endsWith", "in", "notIn"]),
|
|
131
136
|
Int: /* @__PURE__ */ new Set(["equals", "not", "gt", "gte", "lt", "lte", "in", "notIn"]),
|
|
@@ -150,7 +155,7 @@ function getSupportedOperators(fieldMeta) {
|
|
|
150
155
|
return [];
|
|
151
156
|
return [...ops];
|
|
152
157
|
}
|
|
153
|
-
function createBaseType(fieldMeta, enumMap) {
|
|
158
|
+
function createBaseType(fieldMeta, enumMap, scalarBase) {
|
|
154
159
|
let base;
|
|
155
160
|
if (fieldMeta.isEnum) {
|
|
156
161
|
const values = enumMap[fieldMeta.type];
|
|
@@ -159,7 +164,7 @@ function createBaseType(fieldMeta, enumMap) {
|
|
|
159
164
|
}
|
|
160
165
|
base = z2.enum(values);
|
|
161
166
|
} else {
|
|
162
|
-
const factory =
|
|
167
|
+
const factory = scalarBase[fieldMeta.type];
|
|
163
168
|
if (!factory) {
|
|
164
169
|
throw new ShapeError(`Unknown scalar type: ${fieldMeta.type}`);
|
|
165
170
|
}
|
|
@@ -170,7 +175,7 @@ function createBaseType(fieldMeta, enumMap) {
|
|
|
170
175
|
}
|
|
171
176
|
return base;
|
|
172
177
|
}
|
|
173
|
-
function createScalarListOperatorSchema(fieldMeta, operator, enumMap) {
|
|
178
|
+
function createScalarListOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
|
|
174
179
|
if (!SCALAR_LIST_OPERATORS.has(operator)) {
|
|
175
180
|
throw new ShapeError(`Operator "${operator}" not supported for scalar list fields`);
|
|
176
181
|
}
|
|
@@ -179,19 +184,19 @@ function createScalarListOperatorSchema(fieldMeta, operator, enumMap) {
|
|
|
179
184
|
}
|
|
180
185
|
if (operator === "equals") {
|
|
181
186
|
const itemMeta2 = { ...fieldMeta, isList: false };
|
|
182
|
-
const itemBase2 = createBaseType(itemMeta2, enumMap);
|
|
187
|
+
const itemBase2 = createBaseType(itemMeta2, enumMap, scalarBase);
|
|
183
188
|
return fieldMeta.isRequired ? z2.array(itemBase2) : z2.union([z2.array(itemBase2), z2.null()]);
|
|
184
189
|
}
|
|
185
190
|
const itemMeta = { ...fieldMeta, isList: false };
|
|
186
|
-
const itemBase = createBaseType(itemMeta, enumMap);
|
|
191
|
+
const itemBase = createBaseType(itemMeta, enumMap, scalarBase);
|
|
187
192
|
if (operator === "has") {
|
|
188
193
|
return !fieldMeta.isRequired ? z2.union([itemBase, z2.null()]) : itemBase;
|
|
189
194
|
}
|
|
190
195
|
return z2.array(itemBase);
|
|
191
196
|
}
|
|
192
|
-
function createOperatorSchema(fieldMeta, operator, enumMap) {
|
|
197
|
+
function createOperatorSchema(fieldMeta, operator, enumMap, scalarBase) {
|
|
193
198
|
if (fieldMeta.isList) {
|
|
194
|
-
return createScalarListOperatorSchema(fieldMeta, operator, enumMap);
|
|
199
|
+
return createScalarListOperatorSchema(fieldMeta, operator, enumMap, scalarBase);
|
|
195
200
|
}
|
|
196
201
|
if (fieldMeta.isEnum) {
|
|
197
202
|
const values = enumMap[fieldMeta.type];
|
|
@@ -220,7 +225,7 @@ function createOperatorSchema(fieldMeta, operator, enumMap) {
|
|
|
220
225
|
if (!supportedOps.has(operator)) {
|
|
221
226
|
throw new ShapeError(`Operator "${operator}" not supported for type "${fieldMeta.type}"`);
|
|
222
227
|
}
|
|
223
|
-
const factory =
|
|
228
|
+
const factory = scalarBase[fieldMeta.type];
|
|
224
229
|
if (!factory) {
|
|
225
230
|
throw new ShapeError(`Unknown scalar type: ${fieldMeta.type}`);
|
|
226
231
|
}
|
|
@@ -237,6 +242,24 @@ function createOperatorSchema(fieldMeta, operator, enumMap) {
|
|
|
237
242
|
return scalar;
|
|
238
243
|
}
|
|
239
244
|
|
|
245
|
+
// src/shared/utils.ts
|
|
246
|
+
function isPlainObject(v) {
|
|
247
|
+
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
248
|
+
return false;
|
|
249
|
+
const proto = Object.getPrototypeOf(v);
|
|
250
|
+
return proto === Object.prototype || proto === null;
|
|
251
|
+
}
|
|
252
|
+
function schemaProducesValueForUndefined(schema) {
|
|
253
|
+
const result = schema.safeParse(void 0);
|
|
254
|
+
return result.success && result.data !== void 0;
|
|
255
|
+
}
|
|
256
|
+
function isZodSchema(value) {
|
|
257
|
+
if (value == null || typeof value !== "object")
|
|
258
|
+
return false;
|
|
259
|
+
const v = value;
|
|
260
|
+
return typeof v.parse === "function" && typeof v.optional === "function";
|
|
261
|
+
}
|
|
262
|
+
|
|
240
263
|
// src/runtime/schema-builder.ts
|
|
241
264
|
var DEFAULT_MAX_CACHE = 500;
|
|
242
265
|
var DEFAULT_MAX_DEPTH = 5;
|
|
@@ -258,13 +281,7 @@ function lruSet(cache, key, value, maxSize) {
|
|
|
258
281
|
cache.delete(oldest);
|
|
259
282
|
}
|
|
260
283
|
}
|
|
261
|
-
function
|
|
262
|
-
if (value == null || typeof value !== "object")
|
|
263
|
-
return false;
|
|
264
|
-
const v = value;
|
|
265
|
-
return typeof v.parse === "function" && typeof v.optional === "function";
|
|
266
|
-
}
|
|
267
|
-
function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
284
|
+
function createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefaults) {
|
|
268
285
|
const chainCache = /* @__PURE__ */ new Map();
|
|
269
286
|
function buildFieldSchema(model, field) {
|
|
270
287
|
const cacheKey = `${model}.${field}`;
|
|
@@ -277,7 +294,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
277
294
|
const fieldMeta = modelFields[field];
|
|
278
295
|
if (!fieldMeta)
|
|
279
296
|
throw new ShapeError(`Unknown field "${field}" on model "${model}"`);
|
|
280
|
-
const base = createBaseType(fieldMeta, enumMap);
|
|
297
|
+
const base = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
281
298
|
let result = base;
|
|
282
299
|
const chainFn = zodChains[model]?.[field];
|
|
283
300
|
if (chainFn) {
|
|
@@ -300,7 +317,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
300
317
|
const fieldMeta = modelFields[field];
|
|
301
318
|
if (!fieldMeta)
|
|
302
319
|
throw new ShapeError(`Unknown field "${field}" on model "${model}"`);
|
|
303
|
-
return createBaseType(fieldMeta, enumMap);
|
|
320
|
+
return createBaseType(fieldMeta, enumMap, scalarBase);
|
|
304
321
|
}
|
|
305
322
|
function buildInputSchema(model, opts) {
|
|
306
323
|
if (opts.pick && opts.omit) {
|
|
@@ -311,6 +328,8 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
311
328
|
const modelFields = typeMap[model];
|
|
312
329
|
if (!modelFields)
|
|
313
330
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
331
|
+
const zodDefaultFields = zodDefaults[model];
|
|
332
|
+
const zodDefaultSet = zodDefaultFields ? new Set(zodDefaultFields) : void 0;
|
|
314
333
|
let fieldNames = Object.keys(modelFields).filter((name) => {
|
|
315
334
|
const meta = modelFields[name];
|
|
316
335
|
return !meta.isRelation && !meta.isUpdatedAt;
|
|
@@ -336,6 +355,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
336
355
|
for (const name of fieldNames) {
|
|
337
356
|
const fieldMeta = modelFields[name];
|
|
338
357
|
let fieldSchema;
|
|
358
|
+
let handlesUndefined;
|
|
339
359
|
if (opts.refine?.[name]) {
|
|
340
360
|
let refined;
|
|
341
361
|
try {
|
|
@@ -352,14 +372,22 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
352
372
|
);
|
|
353
373
|
}
|
|
354
374
|
fieldSchema = refined;
|
|
375
|
+
handlesUndefined = schemaProducesValueForUndefined(fieldSchema);
|
|
355
376
|
} else {
|
|
356
377
|
fieldSchema = buildFieldSchema(model, name);
|
|
378
|
+
handlesUndefined = zodDefaultSet !== void 0 && zodDefaultSet.has(name);
|
|
357
379
|
}
|
|
358
380
|
if (mode === "create") {
|
|
359
381
|
if (!fieldMeta.isRequired) {
|
|
360
|
-
|
|
382
|
+
if (handlesUndefined) {
|
|
383
|
+
fieldSchema = allowNull ? fieldSchema.nullable() : fieldSchema;
|
|
384
|
+
} else {
|
|
385
|
+
fieldSchema = allowNull ? fieldSchema.nullable().optional() : fieldSchema.optional();
|
|
386
|
+
}
|
|
361
387
|
} else if (fieldMeta.hasDefault) {
|
|
362
|
-
|
|
388
|
+
if (!handlesUndefined) {
|
|
389
|
+
fieldSchema = fieldSchema.optional();
|
|
390
|
+
}
|
|
363
391
|
}
|
|
364
392
|
} else {
|
|
365
393
|
if (!fieldMeta.isRequired && allowNull) {
|
|
@@ -420,7 +448,7 @@ function createSchemaBuilder(typeMap, zodChains, enumMap) {
|
|
|
420
448
|
const schemaMap = {};
|
|
421
449
|
for (const name of scalarNames) {
|
|
422
450
|
const fieldMeta = modelFields[name];
|
|
423
|
-
let fieldSchema = createBaseType(fieldMeta, enumMap);
|
|
451
|
+
let fieldSchema = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
424
452
|
if (!fieldMeta.isRequired) {
|
|
425
453
|
fieldSchema = fieldSchema.nullable();
|
|
426
454
|
}
|
|
@@ -505,6 +533,8 @@ var SHAPE_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
505
533
|
]);
|
|
506
534
|
var GUARD_SHAPE_KEYS = /* @__PURE__ */ new Set([
|
|
507
535
|
"data",
|
|
536
|
+
"create",
|
|
537
|
+
"update",
|
|
508
538
|
...SHAPE_CONFIG_KEYS
|
|
509
539
|
]);
|
|
510
540
|
var COMBINATOR_KEYS = /* @__PURE__ */ new Set(["AND", "OR", "NOT"]);
|
|
@@ -514,6 +544,15 @@ var ALL_RELATION_OPS = /* @__PURE__ */ new Set([...TO_MANY_RELATION_OPS, ...TO_O
|
|
|
514
544
|
function toDelegateKey(modelName) {
|
|
515
545
|
return modelName[0].toLowerCase() + modelName.slice(1);
|
|
516
546
|
}
|
|
547
|
+
var FORCED_MARKER = Symbol.for("prisma-guard.forced");
|
|
548
|
+
function isForcedValue(v) {
|
|
549
|
+
return v !== null && typeof v === "object" && v[FORCED_MARKER] === true;
|
|
550
|
+
}
|
|
551
|
+
function force(value) {
|
|
552
|
+
const wrapper = { value };
|
|
553
|
+
wrapper[FORCED_MARKER] = true;
|
|
554
|
+
return wrapper;
|
|
555
|
+
}
|
|
517
556
|
|
|
518
557
|
// src/shared/match-caller.ts
|
|
519
558
|
function matchCallerPattern(patterns, caller) {
|
|
@@ -549,14 +588,6 @@ function matchCallerPattern(patterns, caller) {
|
|
|
549
588
|
return matches[0];
|
|
550
589
|
}
|
|
551
590
|
|
|
552
|
-
// src/shared/is-plain-object.ts
|
|
553
|
-
function isPlainObject(v) {
|
|
554
|
-
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
555
|
-
return false;
|
|
556
|
-
const proto = Object.getPrototypeOf(v);
|
|
557
|
-
return proto === Object.prototype || proto === null;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
591
|
// src/runtime/policy.ts
|
|
561
592
|
function requireContext(ctx, label) {
|
|
562
593
|
if (ctx === void 0 || ctx === null) {
|
|
@@ -580,6 +611,37 @@ function validateContext(ctx) {
|
|
|
580
611
|
// src/runtime/query-builder-where.ts
|
|
581
612
|
import { z as z4 } from "zod";
|
|
582
613
|
|
|
614
|
+
// src/shared/deep-clone.ts
|
|
615
|
+
function deepClone(value) {
|
|
616
|
+
if (value === null || value === void 0)
|
|
617
|
+
return value;
|
|
618
|
+
switch (typeof value) {
|
|
619
|
+
case "string":
|
|
620
|
+
case "number":
|
|
621
|
+
case "boolean":
|
|
622
|
+
case "bigint":
|
|
623
|
+
return value;
|
|
624
|
+
case "object": {
|
|
625
|
+
if (value instanceof Date)
|
|
626
|
+
return new Date(value.getTime());
|
|
627
|
+
if (value instanceof Uint8Array)
|
|
628
|
+
return value.slice();
|
|
629
|
+
if (Array.isArray(value))
|
|
630
|
+
return value.map(deepClone);
|
|
631
|
+
const proto = Object.getPrototypeOf(value);
|
|
632
|
+
if (proto !== Object.prototype && proto !== null)
|
|
633
|
+
return value;
|
|
634
|
+
const result = {};
|
|
635
|
+
for (const [k, v] of Object.entries(value)) {
|
|
636
|
+
result[k] = deepClone(v);
|
|
637
|
+
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
default:
|
|
641
|
+
return value;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
583
645
|
// src/runtime/query-builder-forced.ts
|
|
584
646
|
var EMPTY_WHERE_FORCED = { conditions: {}, relations: {} };
|
|
585
647
|
function hasWhereForced(f) {
|
|
@@ -588,7 +650,7 @@ function hasWhereForced(f) {
|
|
|
588
650
|
function mergeWhereForced(where, forced) {
|
|
589
651
|
if (!hasWhereForced(forced))
|
|
590
652
|
return where ?? {};
|
|
591
|
-
let result = where ?
|
|
653
|
+
let result = where ? deepClone(where) : {};
|
|
592
654
|
for (const [relName, opMap] of Object.entries(forced.relations)) {
|
|
593
655
|
if (!result[relName] || typeof result[relName] !== "object") {
|
|
594
656
|
result[relName] = {};
|
|
@@ -602,7 +664,7 @@ function mergeWhereForced(where, forced) {
|
|
|
602
664
|
}
|
|
603
665
|
}
|
|
604
666
|
if (Object.keys(forced.conditions).length > 0) {
|
|
605
|
-
const scalarClone =
|
|
667
|
+
const scalarClone = deepClone(forced.conditions);
|
|
606
668
|
if (Object.keys(result).length === 0) {
|
|
607
669
|
result = scalarClone;
|
|
608
670
|
} else {
|
|
@@ -628,7 +690,7 @@ function mergeUniqueWhereForced(where, forced) {
|
|
|
628
690
|
}
|
|
629
691
|
}
|
|
630
692
|
if (Object.keys(forced.conditions).length > 0) {
|
|
631
|
-
const conditions = [
|
|
693
|
+
const conditions = [deepClone(forced.conditions)];
|
|
632
694
|
const existing = result.AND;
|
|
633
695
|
if (existing) {
|
|
634
696
|
if (Array.isArray(existing)) {
|
|
@@ -670,6 +732,13 @@ function applyBuiltShape(built, body, isUniqueMethod) {
|
|
|
670
732
|
}
|
|
671
733
|
return validated;
|
|
672
734
|
}
|
|
735
|
+
function buildCountForPlacement(countWhere) {
|
|
736
|
+
const countSelect = {};
|
|
737
|
+
for (const [countRel, countForced] of Object.entries(countWhere)) {
|
|
738
|
+
countSelect[countRel] = { where: mergeWhereForced(void 0, countForced) };
|
|
739
|
+
}
|
|
740
|
+
return { _count: { select: countSelect } };
|
|
741
|
+
}
|
|
673
742
|
function applyForcedTree(validated, key, tree) {
|
|
674
743
|
const container = validated[key];
|
|
675
744
|
if (!container)
|
|
@@ -692,11 +761,11 @@ function applyForcedTree(validated, key, tree) {
|
|
|
692
761
|
applyForcedTree(expanded, "select", forced.select);
|
|
693
762
|
}
|
|
694
763
|
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
764
|
+
const placement = forced._countWherePlacement ?? "include";
|
|
765
|
+
if (!expanded[placement])
|
|
766
|
+
expanded[placement] = {};
|
|
767
|
+
const placementObj = expanded[placement];
|
|
768
|
+
Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
|
|
700
769
|
}
|
|
701
770
|
if (expanded.include && expanded.select) {
|
|
702
771
|
throw new ShapeError(
|
|
@@ -725,7 +794,11 @@ function applyForcedTree(validated, key, tree) {
|
|
|
725
794
|
applyForcedTree(relObj, "select", forced.select);
|
|
726
795
|
}
|
|
727
796
|
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
728
|
-
|
|
797
|
+
const placement = forced._countWherePlacement ?? "include";
|
|
798
|
+
const projContainer = relObj[placement];
|
|
799
|
+
if (projContainer) {
|
|
800
|
+
applyForcedCountWhere(projContainer, forced._countWhere);
|
|
801
|
+
}
|
|
729
802
|
}
|
|
730
803
|
if (relObj.include && relObj.select) {
|
|
731
804
|
throw new ShapeError(
|
|
@@ -747,11 +820,11 @@ function buildForcedOnlyContainer(tree) {
|
|
|
747
820
|
if (forced.select)
|
|
748
821
|
nested.select = buildForcedOnlyContainer(forced.select);
|
|
749
822
|
if (forced._countWhere && Object.keys(forced._countWhere).length > 0) {
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
823
|
+
const placement = forced._countWherePlacement ?? "include";
|
|
824
|
+
if (!nested[placement])
|
|
825
|
+
nested[placement] = {};
|
|
826
|
+
const placementObj = nested[placement];
|
|
827
|
+
Object.assign(placementObj, buildCountForPlacement(forced._countWhere));
|
|
755
828
|
}
|
|
756
829
|
result[relName] = Object.keys(nested).length > 0 ? nested : true;
|
|
757
830
|
}
|
|
@@ -813,29 +886,38 @@ function validateResolvedUniqueWhere(model, where, method, uniqueMap) {
|
|
|
813
886
|
);
|
|
814
887
|
}
|
|
815
888
|
}
|
|
816
|
-
function
|
|
817
|
-
const constraints = uniqueMap[model];
|
|
818
|
-
if (!constraints || constraints.length === 0)
|
|
819
|
-
return;
|
|
889
|
+
function collectEqualityFields(where, model, typeMap) {
|
|
820
890
|
const combinators = /* @__PURE__ */ new Set(["AND", "OR", "NOT"]);
|
|
821
|
-
const
|
|
822
|
-
for (const key of Object.
|
|
891
|
+
const fields = /* @__PURE__ */ new Set();
|
|
892
|
+
for (const [key, value] of Object.entries(where)) {
|
|
893
|
+
if (key === "AND") {
|
|
894
|
+
if (isPlainObject(value)) {
|
|
895
|
+
for (const f of collectEqualityFields(value, model, typeMap)) {
|
|
896
|
+
fields.add(f);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
823
901
|
if (combinators.has(key))
|
|
824
902
|
continue;
|
|
825
|
-
if (typeMap && typeMap[model]?.[key]?.isRelation)
|
|
903
|
+
if (typeMap && model && typeMap[model]?.[key]?.isRelation)
|
|
904
|
+
continue;
|
|
905
|
+
if (!value || !isPlainObject(value))
|
|
826
906
|
continue;
|
|
827
|
-
|
|
907
|
+
if (Object.keys(value).every((op) => op === "equals")) {
|
|
908
|
+
fields.add(key);
|
|
909
|
+
}
|
|
828
910
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
911
|
+
return fields;
|
|
912
|
+
}
|
|
913
|
+
function validateUniqueEquality(model, where, method, uniqueMap, typeMap) {
|
|
914
|
+
const constraints = uniqueMap[model];
|
|
915
|
+
if (!constraints || constraints.length === 0)
|
|
916
|
+
return;
|
|
917
|
+
const equalityFields = collectEqualityFields(where, model, typeMap);
|
|
918
|
+
const valid = constraints.some(
|
|
919
|
+
(constraint) => constraint.every((field) => equalityFields.has(field))
|
|
920
|
+
);
|
|
839
921
|
if (!valid) {
|
|
840
922
|
const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
|
|
841
923
|
throw new ShapeError(
|
|
@@ -847,6 +929,7 @@ function validateUniqueEquality(model, where, method, uniqueMap, typeMap) {
|
|
|
847
929
|
// src/runtime/query-builder-where.ts
|
|
848
930
|
var UNSUPPORTED_WHERE_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
|
|
849
931
|
var STRING_MODE_OPS = /* @__PURE__ */ new Set(["contains", "startsWith", "endsWith", "equals"]);
|
|
932
|
+
var MAX_WHERE_DEPTH = 10;
|
|
850
933
|
function safeStringify(v) {
|
|
851
934
|
if (typeof v === "bigint")
|
|
852
935
|
return `${v}n`;
|
|
@@ -937,8 +1020,14 @@ function mergeRelationForcedMaps(target, source) {
|
|
|
937
1020
|
}
|
|
938
1021
|
}
|
|
939
1022
|
}
|
|
940
|
-
function createWhereBuilder(typeMap, enumMap) {
|
|
941
|
-
function buildWhereSchema(model, whereConfig) {
|
|
1023
|
+
function createWhereBuilder(typeMap, enumMap, scalarBase) {
|
|
1024
|
+
function buildWhereSchema(model, whereConfig, depth) {
|
|
1025
|
+
const currentDepth = depth ?? 0;
|
|
1026
|
+
if (currentDepth > MAX_WHERE_DEPTH) {
|
|
1027
|
+
throw new ShapeError(
|
|
1028
|
+
`Where schema for model "${model}" exceeds maximum nesting depth (${MAX_WHERE_DEPTH}). Check for circular relation references in the shape.`
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
942
1031
|
const modelFields = typeMap[model];
|
|
943
1032
|
if (!modelFields)
|
|
944
1033
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -953,7 +1042,8 @@ function createWhereBuilder(typeMap, enumMap) {
|
|
|
953
1042
|
model,
|
|
954
1043
|
fieldSchemas,
|
|
955
1044
|
scalarConditions,
|
|
956
|
-
relationForced
|
|
1045
|
+
relationForced,
|
|
1046
|
+
currentDepth
|
|
957
1047
|
);
|
|
958
1048
|
continue;
|
|
959
1049
|
}
|
|
@@ -965,8 +1055,10 @@ function createWhereBuilder(typeMap, enumMap) {
|
|
|
965
1055
|
key,
|
|
966
1056
|
value,
|
|
967
1057
|
fieldMeta,
|
|
1058
|
+
model,
|
|
968
1059
|
fieldSchemas,
|
|
969
|
-
relationForced
|
|
1060
|
+
relationForced,
|
|
1061
|
+
currentDepth
|
|
970
1062
|
);
|
|
971
1063
|
continue;
|
|
972
1064
|
}
|
|
@@ -986,16 +1078,28 @@ function createWhereBuilder(typeMap, enumMap) {
|
|
|
986
1078
|
}
|
|
987
1079
|
};
|
|
988
1080
|
}
|
|
989
|
-
function processCombinator(key, value, model, fieldSchemas, parentConditions, parentRelations) {
|
|
1081
|
+
function processCombinator(key, value, model, fieldSchemas, parentConditions, parentRelations, depth) {
|
|
990
1082
|
if (!isPlainObject(value)) {
|
|
991
1083
|
throw new ShapeError(`"${key}" in where shape must be an object defining allowed fields`);
|
|
992
1084
|
}
|
|
993
|
-
const result = buildWhereSchema(model, value);
|
|
1085
|
+
const result = buildWhereSchema(model, value, depth + 1);
|
|
1086
|
+
if (!result.schema && !hasWhereForced(result.forced)) {
|
|
1087
|
+
throw new ShapeError(
|
|
1088
|
+
`Empty "${key}" combinator in where shape for model "${model}". Define at least one field.`
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
994
1091
|
if (result.schema) {
|
|
1092
|
+
let elementSchema = result.schema;
|
|
1093
|
+
if (!hasWhereForced(result.forced)) {
|
|
1094
|
+
elementSchema = result.schema.refine(
|
|
1095
|
+
(v) => v !== void 0 && v !== null && Object.keys(v).some((k) => v[k] !== void 0),
|
|
1096
|
+
{ message: `"${key}" member must specify at least one condition` }
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
995
1099
|
if (key === "NOT") {
|
|
996
|
-
fieldSchemas[key] = z4.union([
|
|
1100
|
+
fieldSchemas[key] = z4.union([elementSchema, z4.array(elementSchema).min(1)]).optional();
|
|
997
1101
|
} else {
|
|
998
|
-
fieldSchemas[key] = z4.array(
|
|
1102
|
+
fieldSchemas[key] = z4.array(elementSchema).min(1).optional();
|
|
999
1103
|
}
|
|
1000
1104
|
}
|
|
1001
1105
|
if (hasWhereForced(result.forced)) {
|
|
@@ -1015,11 +1119,16 @@ function createWhereBuilder(typeMap, enumMap) {
|
|
|
1015
1119
|
}
|
|
1016
1120
|
}
|
|
1017
1121
|
}
|
|
1018
|
-
function processRelationFilter(key, value, fieldMeta, fieldSchemas, parentRelations) {
|
|
1122
|
+
function processRelationFilter(key, value, fieldMeta, model, fieldSchemas, parentRelations, depth) {
|
|
1019
1123
|
if (!isPlainObject(value)) {
|
|
1020
1124
|
throw new ShapeError(`Relation filter for "${key}" must be an object with operators (some, every, none, is, isNot)`);
|
|
1021
1125
|
}
|
|
1022
1126
|
const allowedOps = fieldMeta.isList ? TO_MANY_RELATION_OPS : TO_ONE_RELATION_OPS;
|
|
1127
|
+
if (Object.keys(value).length === 0) {
|
|
1128
|
+
throw new ShapeError(
|
|
1129
|
+
`Empty relation filter for "${key}" on model "${model}". Define at least one operator: ${[...allowedOps].join(", ")}`
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1023
1132
|
const opSchemas = {};
|
|
1024
1133
|
const opForced = {};
|
|
1025
1134
|
let hasClientOps = false;
|
|
@@ -1035,7 +1144,12 @@ function createWhereBuilder(typeMap, enumMap) {
|
|
|
1035
1144
|
`Relation filter operator "${op}" on "${key}" must be an object defining nested where fields`
|
|
1036
1145
|
);
|
|
1037
1146
|
}
|
|
1038
|
-
const nested = buildWhereSchema(fieldMeta.type, opValue);
|
|
1147
|
+
const nested = buildWhereSchema(fieldMeta.type, opValue, depth + 1);
|
|
1148
|
+
if (!nested.schema && !hasWhereForced(nested.forced)) {
|
|
1149
|
+
throw new ShapeError(
|
|
1150
|
+
`Empty nested where for relation "${key}.${op}" on model "${model}". Define at least one field.`
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1039
1153
|
if (nested.schema) {
|
|
1040
1154
|
if (!hasWhereForced(nested.forced)) {
|
|
1041
1155
|
opSchemas[op] = nested.schema.refine(
|
|
@@ -1051,6 +1165,11 @@ function createWhereBuilder(typeMap, enumMap) {
|
|
|
1051
1165
|
opForced[op] = nested.forced;
|
|
1052
1166
|
}
|
|
1053
1167
|
}
|
|
1168
|
+
if (!hasClientOps && Object.keys(opForced).length === 0) {
|
|
1169
|
+
throw new ShapeError(
|
|
1170
|
+
`Relation filter for "${key}" on model "${model}" produced no conditions. Define at least one nested field in the operator shape.`
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1054
1173
|
if (hasClientOps) {
|
|
1055
1174
|
const clientOpKeys = Object.keys(opSchemas);
|
|
1056
1175
|
const opObjSchema = z4.object(opSchemas).strict();
|
|
@@ -1080,17 +1199,18 @@ function createWhereBuilder(typeMap, enumMap) {
|
|
|
1080
1199
|
const clientOpKeys = [];
|
|
1081
1200
|
for (const [op, opValue] of Object.entries(operators)) {
|
|
1082
1201
|
if (opValue === true) {
|
|
1083
|
-
opSchemas[op] = createOperatorSchema(fieldMeta, op, enumMap).optional();
|
|
1202
|
+
opSchemas[op] = createOperatorSchema(fieldMeta, op, enumMap, scalarBase).optional();
|
|
1084
1203
|
hasClientOps = true;
|
|
1085
1204
|
clientOpKeys.push(op);
|
|
1086
1205
|
if (fieldMeta.type === "String" && !fieldMeta.isList && STRING_MODE_OPS.has(op)) {
|
|
1087
1206
|
hasStringModeOp = true;
|
|
1088
1207
|
}
|
|
1089
1208
|
} else {
|
|
1090
|
-
const
|
|
1209
|
+
const actualOpValue = isForcedValue(opValue) ? opValue.value : opValue;
|
|
1210
|
+
const opSchema = createOperatorSchema(fieldMeta, op, enumMap, scalarBase);
|
|
1091
1211
|
let parsed;
|
|
1092
1212
|
try {
|
|
1093
|
-
parsed = opSchema.parse(
|
|
1213
|
+
parsed = opSchema.parse(actualOpValue);
|
|
1094
1214
|
} catch (err) {
|
|
1095
1215
|
throw new ShapeError(
|
|
1096
1216
|
`Invalid forced value for "${model}.${fieldName}.${op}": ${err.message}`
|
|
@@ -1124,11 +1244,21 @@ function createWhereBuilder(typeMap, enumMap) {
|
|
|
1124
1244
|
// src/runtime/query-builder-args.ts
|
|
1125
1245
|
import { z as z5 } from "zod";
|
|
1126
1246
|
var UNSUPPORTED_BY_TYPES = /* @__PURE__ */ new Set(["Json", "Bytes"]);
|
|
1127
|
-
function
|
|
1247
|
+
function requireConfigTrue(config, context) {
|
|
1248
|
+
for (const [key, value] of Object.entries(config)) {
|
|
1249
|
+
if (value !== true) {
|
|
1250
|
+
throw new ShapeError(
|
|
1251
|
+
`Config value for "${key}" in ${context} must be true, got ${typeof value}`
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
function createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
1128
1257
|
function buildOrderBySchema(model, orderByConfig) {
|
|
1129
1258
|
const modelFields = typeMap[model];
|
|
1130
1259
|
if (!modelFields)
|
|
1131
1260
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1261
|
+
requireConfigTrue(orderByConfig, `orderBy on model "${model}"`);
|
|
1132
1262
|
const fieldSchemas = {};
|
|
1133
1263
|
for (const fieldName of Object.keys(orderByConfig)) {
|
|
1134
1264
|
const fieldMeta = modelFields[fieldName];
|
|
@@ -1174,16 +1304,17 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap) {
|
|
|
1174
1304
|
const modelFields = typeMap[model];
|
|
1175
1305
|
if (!modelFields)
|
|
1176
1306
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1307
|
+
requireConfigTrue(cursorConfig, `cursor on model "${model}"`);
|
|
1177
1308
|
const cursorFields = new Set(Object.keys(cursorConfig));
|
|
1178
1309
|
const constraints = uniqueMap[model];
|
|
1179
1310
|
if (constraints && constraints.length > 0) {
|
|
1180
1311
|
const covered = constraints.some(
|
|
1181
|
-
(constraint) => constraint.every((field) => cursorFields.has(field))
|
|
1312
|
+
(constraint) => constraint.length === cursorFields.size && constraint.every((field) => cursorFields.has(field))
|
|
1182
1313
|
);
|
|
1183
1314
|
if (!covered) {
|
|
1184
1315
|
const constraintDesc = constraints.map((c) => `(${c.join(", ")})`).join(" | ");
|
|
1185
1316
|
throw new ShapeError(
|
|
1186
|
-
`cursor on model "${model}" must
|
|
1317
|
+
`cursor on model "${model}" must exactly match a unique constraint: ${constraintDesc}`
|
|
1187
1318
|
);
|
|
1188
1319
|
}
|
|
1189
1320
|
}
|
|
@@ -1196,7 +1327,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap) {
|
|
|
1196
1327
|
throw new ShapeError(`Relation field "${fieldName}" cannot be used in cursor`);
|
|
1197
1328
|
if (fieldMeta.isList)
|
|
1198
1329
|
throw new ShapeError(`List field "${fieldName}" cannot be used in cursor`);
|
|
1199
|
-
fieldSchemas[fieldName] = createBaseType(fieldMeta, enumMap);
|
|
1330
|
+
fieldSchemas[fieldName] = createBaseType(fieldMeta, enumMap, scalarBase);
|
|
1200
1331
|
}
|
|
1201
1332
|
return z5.object(fieldSchemas).strict().optional();
|
|
1202
1333
|
}
|
|
@@ -1245,6 +1376,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap) {
|
|
|
1245
1376
|
const modelFields = typeMap[model];
|
|
1246
1377
|
if (!modelFields)
|
|
1247
1378
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1379
|
+
requireConfigTrue(havingConfig, `having on model "${model}"`);
|
|
1248
1380
|
const fieldSchemas = {};
|
|
1249
1381
|
for (const fieldName of Object.keys(havingConfig)) {
|
|
1250
1382
|
const fieldMeta = modelFields[fieldName];
|
|
@@ -1261,7 +1393,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap) {
|
|
|
1261
1393
|
const opSchemas = {};
|
|
1262
1394
|
const opKeys = [];
|
|
1263
1395
|
for (const op of ops) {
|
|
1264
|
-
opSchemas[op] = createOperatorSchema(fieldMeta, op, enumMap).optional();
|
|
1396
|
+
opSchemas[op] = createOperatorSchema(fieldMeta, op, enumMap, scalarBase).optional();
|
|
1265
1397
|
opKeys.push(op);
|
|
1266
1398
|
}
|
|
1267
1399
|
if (fieldMeta.type === "String" && !fieldMeta.isList) {
|
|
@@ -1282,6 +1414,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap) {
|
|
|
1282
1414
|
const modelFields = typeMap[model];
|
|
1283
1415
|
if (!modelFields)
|
|
1284
1416
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1417
|
+
requireConfigTrue(fieldConfig, `${opName} on model "${model}"`);
|
|
1285
1418
|
const isNumericOnly = opName === "_avg" || opName === "_sum";
|
|
1286
1419
|
const isComparableOnly = opName === "_min" || opName === "_max";
|
|
1287
1420
|
const fieldSchemas = {};
|
|
@@ -1295,6 +1428,8 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap) {
|
|
|
1295
1428
|
throw new ShapeError(`Unknown field "${fieldName}" on model "${model}" in ${opName}`);
|
|
1296
1429
|
if (fieldMeta.isRelation)
|
|
1297
1430
|
throw new ShapeError(`Relation field "${fieldName}" cannot be used in ${opName}`);
|
|
1431
|
+
if (fieldMeta.isList)
|
|
1432
|
+
throw new ShapeError(`List field "${fieldName}" cannot be used in ${opName}`);
|
|
1298
1433
|
if (isNumericOnly && !NUMERIC_TYPES.has(fieldMeta.type)) {
|
|
1299
1434
|
throw new ShapeError(
|
|
1300
1435
|
`Field "${fieldName}" (${fieldMeta.type}) cannot be used in ${opName}. Only numeric types (Int, Float, Decimal, BigInt) are supported.`
|
|
@@ -1320,6 +1455,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap) {
|
|
|
1320
1455
|
const modelFields = typeMap[model];
|
|
1321
1456
|
if (!modelFields)
|
|
1322
1457
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1458
|
+
requireConfigTrue(config, `${context} on model "${model}"`);
|
|
1323
1459
|
const fieldSchemas = {};
|
|
1324
1460
|
for (const fieldName of Object.keys(config)) {
|
|
1325
1461
|
if (fieldName !== "_all") {
|
|
@@ -1341,6 +1477,7 @@ function createArgsBuilder(typeMap, enumMap, uniqueMap) {
|
|
|
1341
1477
|
const modelFields = typeMap[model];
|
|
1342
1478
|
if (!modelFields)
|
|
1343
1479
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
1480
|
+
requireConfigTrue(selectConfig, `count select on model "${model}"`);
|
|
1344
1481
|
const fieldSchemas = {};
|
|
1345
1482
|
for (const fieldName of Object.keys(selectConfig)) {
|
|
1346
1483
|
if (fieldName === "_all") {
|
|
@@ -1393,6 +1530,7 @@ var KNOWN_NESTED_SELECT_KEYS = /* @__PURE__ */ new Set([
|
|
|
1393
1530
|
"skip"
|
|
1394
1531
|
]);
|
|
1395
1532
|
var KNOWN_COUNT_SELECT_ENTRY_KEYS = /* @__PURE__ */ new Set(["where"]);
|
|
1533
|
+
var MAX_PROJECTION_DEPTH = 10;
|
|
1396
1534
|
function validateNestedKeys(keys, allowed, context) {
|
|
1397
1535
|
for (const key of keys) {
|
|
1398
1536
|
if (!allowed.has(key)) {
|
|
@@ -1446,6 +1584,11 @@ function createProjectionBuilder(typeMap, enumMap, deps) {
|
|
|
1446
1584
|
if (fieldConfig === true) {
|
|
1447
1585
|
countSelectFields[fieldName] = z6.literal(true).optional();
|
|
1448
1586
|
} else if (isPlainObject(fieldConfig)) {
|
|
1587
|
+
if (Object.keys(fieldConfig).length === 0) {
|
|
1588
|
+
throw new ShapeError(
|
|
1589
|
+
`Empty config for _count.select.${fieldName} on model "${model}". Use true or { where: { ... } }.`
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
1449
1592
|
validateNestedKeys(
|
|
1450
1593
|
Object.keys(fieldConfig),
|
|
1451
1594
|
KNOWN_COUNT_SELECT_ENTRY_KEYS,
|
|
@@ -1474,13 +1617,28 @@ function createProjectionBuilder(typeMap, enumMap, deps) {
|
|
|
1474
1617
|
);
|
|
1475
1618
|
}
|
|
1476
1619
|
}
|
|
1477
|
-
const
|
|
1620
|
+
const countSelectKeys = Object.keys(countSelectFields);
|
|
1621
|
+
const selectSchema = z6.object(countSelectFields).strict().refine(
|
|
1622
|
+
(v) => countSelectKeys.some((k) => v[k] !== void 0),
|
|
1623
|
+
{ message: "_count.select must specify at least one field" }
|
|
1624
|
+
);
|
|
1478
1625
|
return {
|
|
1479
1626
|
schema: z6.object({ select: selectSchema }).strict().optional(),
|
|
1480
1627
|
forcedCountWhere
|
|
1481
1628
|
};
|
|
1482
1629
|
}
|
|
1483
|
-
function buildIncludeSchema(model, includeConfig) {
|
|
1630
|
+
function buildIncludeSchema(model, includeConfig, depth) {
|
|
1631
|
+
const currentDepth = depth ?? 0;
|
|
1632
|
+
if (currentDepth > MAX_PROJECTION_DEPTH) {
|
|
1633
|
+
throw new ShapeError(
|
|
1634
|
+
`Include schema for model "${model}" exceeds maximum nesting depth (${MAX_PROJECTION_DEPTH}). Check for circular relation references in the shape.`
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1637
|
+
if (Object.keys(includeConfig).length === 0) {
|
|
1638
|
+
throw new ShapeError(
|
|
1639
|
+
`Empty include config on model "${model}". Define at least one relation.`
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1484
1642
|
const modelFields = typeMap[model];
|
|
1485
1643
|
if (!modelFields)
|
|
1486
1644
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -1530,20 +1688,24 @@ function createProjectionBuilder(typeMap, enumMap, deps) {
|
|
|
1530
1688
|
relForced.where = forced;
|
|
1531
1689
|
}
|
|
1532
1690
|
if (config.include) {
|
|
1533
|
-
const nested = buildIncludeSchema(fieldMeta.type, config.include);
|
|
1691
|
+
const nested = buildIncludeSchema(fieldMeta.type, config.include, currentDepth + 1);
|
|
1534
1692
|
nestedSchemas["include"] = nested.schema;
|
|
1535
1693
|
if (Object.keys(nested.forcedTree).length > 0)
|
|
1536
1694
|
relForced.include = nested.forcedTree;
|
|
1537
|
-
if (Object.keys(nested.forcedCountWhere).length > 0)
|
|
1695
|
+
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
1538
1696
|
relForced._countWhere = nested.forcedCountWhere;
|
|
1697
|
+
relForced._countWherePlacement = "include";
|
|
1698
|
+
}
|
|
1539
1699
|
}
|
|
1540
1700
|
if (config.select) {
|
|
1541
|
-
const nested = buildSelectSchema(fieldMeta.type, config.select);
|
|
1701
|
+
const nested = buildSelectSchema(fieldMeta.type, config.select, currentDepth + 1);
|
|
1542
1702
|
nestedSchemas["select"] = nested.schema;
|
|
1543
1703
|
if (Object.keys(nested.forcedTree).length > 0)
|
|
1544
1704
|
relForced.select = nested.forcedTree;
|
|
1545
|
-
if (Object.keys(nested.forcedCountWhere).length > 0)
|
|
1705
|
+
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
1546
1706
|
relForced._countWhere = nested.forcedCountWhere;
|
|
1707
|
+
relForced._countWherePlacement = "select";
|
|
1708
|
+
}
|
|
1547
1709
|
}
|
|
1548
1710
|
if (config.orderBy) {
|
|
1549
1711
|
nestedSchemas["orderBy"] = deps.buildOrderBySchema(fieldMeta.type, config.orderBy);
|
|
@@ -1563,13 +1725,30 @@ function createProjectionBuilder(typeMap, enumMap, deps) {
|
|
|
1563
1725
|
forcedTree[relName] = relForced;
|
|
1564
1726
|
}
|
|
1565
1727
|
}
|
|
1728
|
+
const includeFieldKeys = Object.keys(fieldSchemas);
|
|
1729
|
+
const baseSchema = z6.object(fieldSchemas).strict();
|
|
1730
|
+
const schema = includeFieldKeys.length > 0 ? baseSchema.refine(
|
|
1731
|
+
(v) => includeFieldKeys.some((k) => v[k] !== void 0),
|
|
1732
|
+
{ message: "include must specify at least one field" }
|
|
1733
|
+
).optional() : baseSchema.optional();
|
|
1566
1734
|
return {
|
|
1567
|
-
schema
|
|
1735
|
+
schema,
|
|
1568
1736
|
forcedTree,
|
|
1569
1737
|
forcedCountWhere: topLevelForcedCountWhere
|
|
1570
1738
|
};
|
|
1571
1739
|
}
|
|
1572
|
-
function buildSelectSchema(model, selectConfig) {
|
|
1740
|
+
function buildSelectSchema(model, selectConfig, depth) {
|
|
1741
|
+
const currentDepth = depth ?? 0;
|
|
1742
|
+
if (currentDepth > MAX_PROJECTION_DEPTH) {
|
|
1743
|
+
throw new ShapeError(
|
|
1744
|
+
`Select schema for model "${model}" exceeds maximum nesting depth (${MAX_PROJECTION_DEPTH}). Check for circular relation references in the shape.`
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
if (Object.keys(selectConfig).length === 0) {
|
|
1748
|
+
throw new ShapeError(
|
|
1749
|
+
`Empty select config on model "${model}". Define at least one field.`
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1573
1752
|
const modelFields = typeMap[model];
|
|
1574
1753
|
if (!modelFields)
|
|
1575
1754
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
@@ -1607,12 +1786,14 @@ function createProjectionBuilder(typeMap, enumMap, deps) {
|
|
|
1607
1786
|
const nestedSchemas = {};
|
|
1608
1787
|
const relForced = {};
|
|
1609
1788
|
if (config.select) {
|
|
1610
|
-
const nested = buildSelectSchema(fieldMeta.type, config.select);
|
|
1789
|
+
const nested = buildSelectSchema(fieldMeta.type, config.select, currentDepth + 1);
|
|
1611
1790
|
nestedSchemas["select"] = nested.schema;
|
|
1612
1791
|
if (Object.keys(nested.forcedTree).length > 0)
|
|
1613
1792
|
relForced.select = nested.forcedTree;
|
|
1614
|
-
if (Object.keys(nested.forcedCountWhere).length > 0)
|
|
1793
|
+
if (Object.keys(nested.forcedCountWhere).length > 0) {
|
|
1615
1794
|
relForced._countWhere = nested.forcedCountWhere;
|
|
1795
|
+
relForced._countWherePlacement = "select";
|
|
1796
|
+
}
|
|
1616
1797
|
}
|
|
1617
1798
|
if (config.where) {
|
|
1618
1799
|
const { schema: whereSchema, forced } = deps.buildWhereSchema(
|
|
@@ -1642,8 +1823,14 @@ function createProjectionBuilder(typeMap, enumMap, deps) {
|
|
|
1642
1823
|
forcedTree[fieldName] = relForced;
|
|
1643
1824
|
}
|
|
1644
1825
|
}
|
|
1826
|
+
const selectFieldKeys = Object.keys(fieldSchemas);
|
|
1827
|
+
const baseSchema = z6.object(fieldSchemas).strict();
|
|
1828
|
+
const schema = selectFieldKeys.length > 0 ? baseSchema.refine(
|
|
1829
|
+
(v) => selectFieldKeys.some((k) => v[k] !== void 0),
|
|
1830
|
+
{ message: "select must specify at least one field" }
|
|
1831
|
+
).optional() : baseSchema.optional();
|
|
1645
1832
|
return {
|
|
1646
|
-
schema
|
|
1833
|
+
schema,
|
|
1647
1834
|
forcedTree,
|
|
1648
1835
|
forcedCountWhere: topLevelForcedCountWhere
|
|
1649
1836
|
};
|
|
@@ -1663,9 +1850,9 @@ var METHOD_ALLOWED_ARGS = {
|
|
|
1663
1850
|
groupBy: /* @__PURE__ */ new Set(["where", "by", "having", "_count", "_avg", "_sum", "_min", "_max", "orderBy", "take", "skip"])
|
|
1664
1851
|
};
|
|
1665
1852
|
var UNIQUE_WHERE_METHODS = /* @__PURE__ */ new Set(["findUnique", "findUniqueOrThrow"]);
|
|
1666
|
-
function createQueryBuilder(typeMap, enumMap, uniqueMap
|
|
1667
|
-
const whereBuilder = createWhereBuilder(typeMap, enumMap);
|
|
1668
|
-
const argsBuilder = createArgsBuilder(typeMap, enumMap, uniqueMap);
|
|
1853
|
+
function createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase) {
|
|
1854
|
+
const whereBuilder = createWhereBuilder(typeMap, enumMap, scalarBase);
|
|
1855
|
+
const argsBuilder = createArgsBuilder(typeMap, enumMap, uniqueMap, scalarBase);
|
|
1669
1856
|
const projectionBuilder = createProjectionBuilder(typeMap, enumMap, {
|
|
1670
1857
|
buildWhereSchema: whereBuilder.buildWhereSchema,
|
|
1671
1858
|
buildOrderBySchema: argsBuilder.buildOrderBySchema,
|
|
@@ -1774,8 +1961,12 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1774
1961
|
schemaFields["cursor"] = argsBuilder.buildCursorSchema(model, shape.cursor);
|
|
1775
1962
|
if (shape.take)
|
|
1776
1963
|
schemaFields["take"] = argsBuilder.buildTakeSchema(shape.take);
|
|
1777
|
-
if (shape.skip)
|
|
1964
|
+
if (shape.skip !== void 0) {
|
|
1965
|
+
if (shape.skip !== true) {
|
|
1966
|
+
throw new ShapeError('Shape config "skip" must be true');
|
|
1967
|
+
}
|
|
1778
1968
|
schemaFields["skip"] = z7.number().int().min(0).optional();
|
|
1969
|
+
}
|
|
1779
1970
|
if (shape.distinct)
|
|
1780
1971
|
schemaFields["distinct"] = argsBuilder.buildDistinctSchema(model, shape.distinct);
|
|
1781
1972
|
if (shape._count)
|
|
@@ -1831,6 +2022,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1831
2022
|
[...builtCache.entries()].map(([k, v]) => [k, v.zodSchema])
|
|
1832
2023
|
),
|
|
1833
2024
|
parse(body, opts) {
|
|
2025
|
+
const normalizedBody = body === void 0 || body === null ? {} : body;
|
|
1834
2026
|
let built;
|
|
1835
2027
|
if (isSingle) {
|
|
1836
2028
|
if (typeof config === "function") {
|
|
@@ -1840,9 +2032,9 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1840
2032
|
built = builtCache.get("_default");
|
|
1841
2033
|
}
|
|
1842
2034
|
} else {
|
|
1843
|
-
if (!isPlainObject(
|
|
2035
|
+
if (!isPlainObject(normalizedBody))
|
|
1844
2036
|
throw new ShapeError("Request body must be an object");
|
|
1845
|
-
if ("caller" in
|
|
2037
|
+
if ("caller" in normalizedBody) {
|
|
1846
2038
|
throw new CallerError(
|
|
1847
2039
|
"Pass caller via opts.caller, not in the request body."
|
|
1848
2040
|
);
|
|
@@ -1868,7 +2060,7 @@ function createQueryBuilder(typeMap, enumMap, uniqueMap = {}) {
|
|
|
1868
2060
|
built = builtCache.get(matched.key);
|
|
1869
2061
|
}
|
|
1870
2062
|
}
|
|
1871
|
-
return applyBuiltShape(built,
|
|
2063
|
+
return applyBuiltShape(built, normalizedBody, isUnique);
|
|
1872
2064
|
}
|
|
1873
2065
|
};
|
|
1874
2066
|
}
|
|
@@ -2055,10 +2247,24 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
|
|
|
2055
2247
|
const overrides = Object.fromEntries(
|
|
2056
2248
|
presentScopes.map((s) => [s.fk, ctx[s.root]])
|
|
2057
2249
|
);
|
|
2250
|
+
const nextArgs = { ...args };
|
|
2058
2251
|
if (operation === "upsert") {
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2252
|
+
nextArgs.where = buildScopedUniqueWhere(args.where, conditions);
|
|
2253
|
+
if (args.create !== void 0 && args.create !== null) {
|
|
2254
|
+
if (typeof args.create !== "object" || Array.isArray(args.create)) {
|
|
2255
|
+
throw new ShapeError(`upsert expects create to be an object`);
|
|
2256
|
+
}
|
|
2257
|
+
nextArgs.create = { ...args.create };
|
|
2258
|
+
enforceDataScope(nextArgs.create, scopes, overrides, log, model, operation, onScopeRelationWrite, "create");
|
|
2259
|
+
}
|
|
2260
|
+
if (args.update !== void 0 && args.update !== null) {
|
|
2261
|
+
if (typeof args.update !== "object" || Array.isArray(args.update)) {
|
|
2262
|
+
throw new ShapeError(`upsert expects update to be an object`);
|
|
2263
|
+
}
|
|
2264
|
+
nextArgs.update = { ...args.update };
|
|
2265
|
+
enforceDataScope(nextArgs.update, scopes, overrides, log, model, operation, onScopeRelationWrite, "mutate");
|
|
2266
|
+
}
|
|
2267
|
+
return query(nextArgs);
|
|
2062
2268
|
}
|
|
2063
2269
|
if (FIND_UNIQUE_OPS.has(operation)) {
|
|
2064
2270
|
if (findUniqueMode === "reject") {
|
|
@@ -2068,7 +2274,6 @@ function createScopeExtension(scopeMap, contextFn, guardConfig, logger) {
|
|
|
2068
2274
|
}
|
|
2069
2275
|
return handleFindUnique(args, query, conditions, scopes, operation, log);
|
|
2070
2276
|
}
|
|
2071
|
-
const nextArgs = { ...args };
|
|
2072
2277
|
if (READ_OPS.has(operation)) {
|
|
2073
2278
|
nextArgs.where = buildAndConditions(args.where, conditions);
|
|
2074
2279
|
return query(nextArgs);
|
|
@@ -2219,18 +2424,14 @@ var ALLOWED_BODY_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
|
|
|
2219
2424
|
var ALLOWED_BODY_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set(["data", "where", "select", "include"]);
|
|
2220
2425
|
var ALLOWED_BODY_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
|
|
2221
2426
|
var ALLOWED_BODY_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set(["where", "select", "include"]);
|
|
2427
|
+
var ALLOWED_BODY_KEYS_UPSERT = /* @__PURE__ */ new Set(["where", "create", "update", "select", "include"]);
|
|
2222
2428
|
var VALID_SHAPE_KEYS_CREATE = /* @__PURE__ */ new Set(["data"]);
|
|
2223
2429
|
var VALID_SHAPE_KEYS_CREATE_PROJECTION = /* @__PURE__ */ new Set(["data", "select", "include"]);
|
|
2224
2430
|
var VALID_SHAPE_KEYS_UPDATE = /* @__PURE__ */ new Set(["data", "where"]);
|
|
2225
2431
|
var VALID_SHAPE_KEYS_UPDATE_PROJECTION = /* @__PURE__ */ new Set(["data", "where", "select", "include"]);
|
|
2226
2432
|
var VALID_SHAPE_KEYS_DELETE = /* @__PURE__ */ new Set(["where"]);
|
|
2227
2433
|
var VALID_SHAPE_KEYS_DELETE_PROJECTION = /* @__PURE__ */ new Set(["where", "select", "include"]);
|
|
2228
|
-
|
|
2229
|
-
if (value == null || typeof value !== "object")
|
|
2230
|
-
return false;
|
|
2231
|
-
const v = value;
|
|
2232
|
-
return typeof v.parse === "function" && typeof v.optional === "function";
|
|
2233
|
-
}
|
|
2434
|
+
var VALID_SHAPE_KEYS_UPSERT = /* @__PURE__ */ new Set(["where", "create", "update", "select", "include"]);
|
|
2234
2435
|
function validateMutationBodyKeys(body, allowed, method) {
|
|
2235
2436
|
for (const key of Object.keys(body)) {
|
|
2236
2437
|
if (!allowed.has(key)) {
|
|
@@ -2275,10 +2476,12 @@ function validateCreateCompleteness(modelName, dataConfig, typeMap, scopeFks, zo
|
|
|
2275
2476
|
);
|
|
2276
2477
|
}
|
|
2277
2478
|
}
|
|
2278
|
-
function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder) {
|
|
2479
|
+
function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder, zodDefaults) {
|
|
2279
2480
|
const modelFields = typeMap[model];
|
|
2280
2481
|
if (!modelFields)
|
|
2281
2482
|
throw new ShapeError(`Unknown model: ${model}`);
|
|
2483
|
+
const zodDefaultFields = zodDefaults[model];
|
|
2484
|
+
const zodDefaultSet = zodDefaultFields ? new Set(zodDefaultFields) : void 0;
|
|
2282
2485
|
const schemaMap = {};
|
|
2283
2486
|
const forced = {};
|
|
2284
2487
|
for (const [fieldName, value] of Object.entries(dataConfig)) {
|
|
@@ -2300,15 +2503,18 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder) {
|
|
|
2300
2503
|
{ cause: err }
|
|
2301
2504
|
);
|
|
2302
2505
|
}
|
|
2303
|
-
if (!
|
|
2506
|
+
if (!isZodSchema(refined)) {
|
|
2304
2507
|
throw new ShapeError(`Inline refine for "${model}.${fieldName}" must return a Zod schema`);
|
|
2305
2508
|
}
|
|
2306
2509
|
let fieldSchema = refined;
|
|
2510
|
+
const handlesUndefined = schemaProducesValueForUndefined(fieldSchema);
|
|
2307
2511
|
if (mode === "create") {
|
|
2308
2512
|
if (!fieldMeta.isRequired) {
|
|
2309
|
-
fieldSchema = fieldSchema.nullable().optional();
|
|
2513
|
+
fieldSchema = handlesUndefined ? fieldSchema.nullable() : fieldSchema.nullable().optional();
|
|
2310
2514
|
} else if (fieldMeta.hasDefault) {
|
|
2311
|
-
|
|
2515
|
+
if (!handlesUndefined) {
|
|
2516
|
+
fieldSchema = fieldSchema.optional();
|
|
2517
|
+
}
|
|
2312
2518
|
}
|
|
2313
2519
|
} else {
|
|
2314
2520
|
if (!fieldMeta.isRequired) {
|
|
@@ -2320,11 +2526,14 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder) {
|
|
|
2320
2526
|
schemaMap[fieldName] = fieldSchema;
|
|
2321
2527
|
} else if (value === true) {
|
|
2322
2528
|
let fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
|
|
2529
|
+
const isZodDefaultField = zodDefaultSet !== void 0 && zodDefaultSet.has(fieldName);
|
|
2323
2530
|
if (mode === "create") {
|
|
2324
2531
|
if (!fieldMeta.isRequired) {
|
|
2325
|
-
fieldSchema = fieldSchema.nullable().optional();
|
|
2532
|
+
fieldSchema = isZodDefaultField ? fieldSchema.nullable() : fieldSchema.nullable().optional();
|
|
2326
2533
|
} else if (fieldMeta.hasDefault) {
|
|
2327
|
-
|
|
2534
|
+
if (!isZodDefaultField) {
|
|
2535
|
+
fieldSchema = fieldSchema.optional();
|
|
2536
|
+
}
|
|
2328
2537
|
}
|
|
2329
2538
|
} else {
|
|
2330
2539
|
if (!fieldMeta.isRequired) {
|
|
@@ -2335,13 +2544,14 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder) {
|
|
|
2335
2544
|
}
|
|
2336
2545
|
schemaMap[fieldName] = fieldSchema;
|
|
2337
2546
|
} else {
|
|
2547
|
+
const actualValue = isForcedValue(value) ? value.value : value;
|
|
2338
2548
|
let fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
|
|
2339
2549
|
if (!fieldMeta.isRequired) {
|
|
2340
2550
|
fieldSchema = fieldSchema.nullable();
|
|
2341
2551
|
}
|
|
2342
2552
|
let parsed;
|
|
2343
2553
|
try {
|
|
2344
|
-
parsed = fieldSchema.parse(
|
|
2554
|
+
parsed = fieldSchema.parse(actualValue);
|
|
2345
2555
|
} catch (err) {
|
|
2346
2556
|
throw new ShapeError(
|
|
2347
2557
|
`Invalid forced data value for "${model}.${fieldName}": ${err.message}`
|
|
@@ -2350,6 +2560,28 @@ function buildDataSchema(model, dataConfig, mode, typeMap, schemaBuilder) {
|
|
|
2350
2560
|
forced[fieldName] = parsed;
|
|
2351
2561
|
}
|
|
2352
2562
|
}
|
|
2563
|
+
if (mode === "create" && zodDefaultFields) {
|
|
2564
|
+
for (const fieldName of zodDefaultFields) {
|
|
2565
|
+
if (fieldName in dataConfig)
|
|
2566
|
+
continue;
|
|
2567
|
+
const fieldMeta = modelFields[fieldName];
|
|
2568
|
+
if (!fieldMeta)
|
|
2569
|
+
continue;
|
|
2570
|
+
if (fieldMeta.isRelation)
|
|
2571
|
+
continue;
|
|
2572
|
+
if (fieldMeta.isUpdatedAt)
|
|
2573
|
+
continue;
|
|
2574
|
+
const fieldSchema = schemaBuilder.buildFieldSchema(model, fieldName);
|
|
2575
|
+
const result = fieldSchema.safeParse(void 0);
|
|
2576
|
+
if (result.success && result.data !== void 0) {
|
|
2577
|
+
forced[fieldName] = result.data;
|
|
2578
|
+
} else {
|
|
2579
|
+
throw new ShapeError(
|
|
2580
|
+
`Field "${fieldName}" on model "${model}" has @zod default/catch but its schema does not produce a value for undefined input`
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2353
2585
|
return {
|
|
2354
2586
|
schema: z8.object(schemaMap).strict(),
|
|
2355
2587
|
forced
|
|
@@ -2360,7 +2592,7 @@ function validateAndMergeData(bodyData, cached, method) {
|
|
|
2360
2592
|
throw new ShapeError(`${method} requires "data" in request body`);
|
|
2361
2593
|
}
|
|
2362
2594
|
const validated = cached.schema.parse(bodyData);
|
|
2363
|
-
return { ...validated, ...cached.forced };
|
|
2595
|
+
return { ...validated, ...deepClone(cached.forced) };
|
|
2364
2596
|
}
|
|
2365
2597
|
function hasDataRefines(dataConfig) {
|
|
2366
2598
|
for (const value of Object.values(dataConfig)) {
|
|
@@ -2422,7 +2654,7 @@ function resolveShape(input, body, contextFn, caller) {
|
|
|
2422
2654
|
);
|
|
2423
2655
|
}
|
|
2424
2656
|
}
|
|
2425
|
-
const parsed = requireBody(body);
|
|
2657
|
+
const parsed = body === void 0 || body === null ? {} : requireBody(body);
|
|
2426
2658
|
if ("caller" in parsed) {
|
|
2427
2659
|
throw new CallerError(
|
|
2428
2660
|
"Pass caller as second argument to .guard() or via context function, not in the request body."
|
|
@@ -2448,7 +2680,7 @@ function resolveShape(input, body, contextFn, caller) {
|
|
|
2448
2680
|
}
|
|
2449
2681
|
|
|
2450
2682
|
// src/runtime/model-guard.ts
|
|
2451
|
-
var UNIQUE_MUTATION_METHODS = /* @__PURE__ */ new Set(["update", "delete"]);
|
|
2683
|
+
var UNIQUE_MUTATION_METHODS = /* @__PURE__ */ new Set(["update", "delete", "upsert"]);
|
|
2452
2684
|
var UNIQUE_READ_METHODS = /* @__PURE__ */ new Set(["findUnique", "findUniqueOrThrow"]);
|
|
2453
2685
|
var BULK_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
2454
2686
|
"updateMany",
|
|
@@ -2458,6 +2690,7 @@ var BULK_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
|
2458
2690
|
var PROJECTION_MUTATION_METHODS = /* @__PURE__ */ new Set([
|
|
2459
2691
|
"create",
|
|
2460
2692
|
"update",
|
|
2693
|
+
"upsert",
|
|
2461
2694
|
"delete",
|
|
2462
2695
|
"createManyAndReturn",
|
|
2463
2696
|
"updateManyAndReturn"
|
|
@@ -2466,11 +2699,148 @@ var BATCH_CREATE_METHODS = /* @__PURE__ */ new Set([
|
|
|
2466
2699
|
"createMany",
|
|
2467
2700
|
"createManyAndReturn"
|
|
2468
2701
|
]);
|
|
2702
|
+
function buildDefaultSelectInput(config) {
|
|
2703
|
+
const result = {};
|
|
2704
|
+
for (const [key, value] of Object.entries(config)) {
|
|
2705
|
+
if (key === "_count") {
|
|
2706
|
+
result[key] = buildDefaultCountInput(value);
|
|
2707
|
+
continue;
|
|
2708
|
+
}
|
|
2709
|
+
if (value === true) {
|
|
2710
|
+
result[key] = true;
|
|
2711
|
+
} else {
|
|
2712
|
+
const nested = {};
|
|
2713
|
+
if (value.select)
|
|
2714
|
+
nested.select = buildDefaultSelectInput(value.select);
|
|
2715
|
+
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
return result;
|
|
2719
|
+
}
|
|
2720
|
+
function buildDefaultIncludeInput(config) {
|
|
2721
|
+
const result = {};
|
|
2722
|
+
for (const [key, value] of Object.entries(config)) {
|
|
2723
|
+
if (key === "_count") {
|
|
2724
|
+
result[key] = buildDefaultCountInput(value);
|
|
2725
|
+
continue;
|
|
2726
|
+
}
|
|
2727
|
+
if (value === true) {
|
|
2728
|
+
result[key] = true;
|
|
2729
|
+
} else {
|
|
2730
|
+
const nested = {};
|
|
2731
|
+
if (value.include)
|
|
2732
|
+
nested.include = buildDefaultIncludeInput(value.include);
|
|
2733
|
+
if (value.select)
|
|
2734
|
+
nested.select = buildDefaultSelectInput(value.select);
|
|
2735
|
+
result[key] = Object.keys(nested).length > 0 ? nested : true;
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
return result;
|
|
2739
|
+
}
|
|
2740
|
+
function buildDefaultCountInput(config) {
|
|
2741
|
+
if (config === true)
|
|
2742
|
+
return true;
|
|
2743
|
+
if (!isPlainObject(config) || !config.select || !isPlainObject(config.select))
|
|
2744
|
+
return true;
|
|
2745
|
+
const selectObj = config.select;
|
|
2746
|
+
const result = {};
|
|
2747
|
+
for (const key of Object.keys(selectObj)) {
|
|
2748
|
+
result[key] = true;
|
|
2749
|
+
}
|
|
2750
|
+
return { select: result };
|
|
2751
|
+
}
|
|
2752
|
+
function buildDefaultProjectionBody(shape) {
|
|
2753
|
+
if (shape.select) {
|
|
2754
|
+
return { select: buildDefaultSelectInput(shape.select) };
|
|
2755
|
+
}
|
|
2756
|
+
if (shape.include) {
|
|
2757
|
+
return { include: buildDefaultIncludeInput(shape.include) };
|
|
2758
|
+
}
|
|
2759
|
+
return {};
|
|
2760
|
+
}
|
|
2761
|
+
function hasClientControlledValues(obj) {
|
|
2762
|
+
for (const value of Object.values(obj)) {
|
|
2763
|
+
if (isForcedValue(value))
|
|
2764
|
+
continue;
|
|
2765
|
+
if (value === true)
|
|
2766
|
+
return true;
|
|
2767
|
+
if (isPlainObject(value) && hasClientControlledValues(value))
|
|
2768
|
+
return true;
|
|
2769
|
+
}
|
|
2770
|
+
return false;
|
|
2771
|
+
}
|
|
2772
|
+
function checkIncludeForClientArgs(config) {
|
|
2773
|
+
for (const [key, value] of Object.entries(config)) {
|
|
2774
|
+
if (key === "_count") {
|
|
2775
|
+
if (value !== true && isPlainObject(value) && isPlainObject(value.select)) {
|
|
2776
|
+
const selectObj = value.select;
|
|
2777
|
+
for (const entryVal of Object.values(selectObj)) {
|
|
2778
|
+
if (isPlainObject(entryVal) && entryVal.where) {
|
|
2779
|
+
const w = entryVal.where;
|
|
2780
|
+
if (isPlainObject(w) && hasClientControlledValues(w))
|
|
2781
|
+
return true;
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
continue;
|
|
2786
|
+
}
|
|
2787
|
+
if (value === true)
|
|
2788
|
+
continue;
|
|
2789
|
+
if (value.orderBy || value.cursor || value.take || value.skip)
|
|
2790
|
+
return true;
|
|
2791
|
+
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where))
|
|
2792
|
+
return true;
|
|
2793
|
+
if (value.include && checkIncludeForClientArgs(value.include))
|
|
2794
|
+
return true;
|
|
2795
|
+
if (value.select && checkSelectForClientArgs(value.select))
|
|
2796
|
+
return true;
|
|
2797
|
+
}
|
|
2798
|
+
return false;
|
|
2799
|
+
}
|
|
2800
|
+
function checkSelectForClientArgs(config) {
|
|
2801
|
+
for (const [key, value] of Object.entries(config)) {
|
|
2802
|
+
if (key === "_count") {
|
|
2803
|
+
if (value !== true && isPlainObject(value) && isPlainObject(value.select)) {
|
|
2804
|
+
const selectObj = value.select;
|
|
2805
|
+
for (const entryVal of Object.values(selectObj)) {
|
|
2806
|
+
if (isPlainObject(entryVal) && entryVal.where) {
|
|
2807
|
+
const w = entryVal.where;
|
|
2808
|
+
if (isPlainObject(w) && hasClientControlledValues(w))
|
|
2809
|
+
return true;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
continue;
|
|
2814
|
+
}
|
|
2815
|
+
if (value === true)
|
|
2816
|
+
continue;
|
|
2817
|
+
if (value.orderBy || value.cursor || value.take || value.skip)
|
|
2818
|
+
return true;
|
|
2819
|
+
if (value.where && isPlainObject(value.where) && hasClientControlledValues(value.where))
|
|
2820
|
+
return true;
|
|
2821
|
+
if (value.select && checkSelectForClientArgs(value.select))
|
|
2822
|
+
return true;
|
|
2823
|
+
}
|
|
2824
|
+
return false;
|
|
2825
|
+
}
|
|
2826
|
+
function hasNestedClientControlledArgs(shape) {
|
|
2827
|
+
if (shape.include) {
|
|
2828
|
+
if (checkIncludeForClientArgs(shape.include))
|
|
2829
|
+
return true;
|
|
2830
|
+
}
|
|
2831
|
+
if (shape.select) {
|
|
2832
|
+
if (checkSelectForClientArgs(shape.select))
|
|
2833
|
+
return true;
|
|
2834
|
+
}
|
|
2835
|
+
return false;
|
|
2836
|
+
}
|
|
2469
2837
|
function createModelGuardExtension(config) {
|
|
2470
|
-
const { typeMap, enumMap, zodChains, zodDefaults, uniqueMap, scopeMap, contextFn } = config;
|
|
2838
|
+
const { typeMap, enumMap, zodChains, zodDefaults, uniqueMap, scopeMap, guardConfig, contextFn } = config;
|
|
2471
2839
|
const wrapZodErrors = config.wrapZodErrors ?? false;
|
|
2472
|
-
const
|
|
2473
|
-
const
|
|
2840
|
+
const enforceProjection = guardConfig.enforceProjection ?? false;
|
|
2841
|
+
const scalarBase = createScalarBase(guardConfig.strictDecimal ?? false);
|
|
2842
|
+
const schemaBuilder = createSchemaBuilder(typeMap, zodChains, enumMap, scalarBase, zodDefaults);
|
|
2843
|
+
const queryBuilder = createQueryBuilder(typeMap, enumMap, uniqueMap, scalarBase);
|
|
2474
2844
|
const modelScopeFks = /* @__PURE__ */ new Map();
|
|
2475
2845
|
for (const [model, entries] of Object.entries(scopeMap)) {
|
|
2476
2846
|
const fks = /* @__PURE__ */ new Set();
|
|
@@ -2523,11 +2893,11 @@ function createModelGuardExtension(config) {
|
|
|
2523
2893
|
const cached = dataSchemaCache.get(cacheKey);
|
|
2524
2894
|
if (cached)
|
|
2525
2895
|
return cached;
|
|
2526
|
-
const built = buildDataSchema(modelName, dataConfig, mode, typeMap, schemaBuilder);
|
|
2896
|
+
const built = buildDataSchema(modelName, dataConfig, mode, typeMap, schemaBuilder, zodDefaults);
|
|
2527
2897
|
dataSchemaCache.set(cacheKey, built);
|
|
2528
2898
|
return built;
|
|
2529
2899
|
}
|
|
2530
|
-
return buildDataSchema(modelName, dataConfig, mode, typeMap, schemaBuilder);
|
|
2900
|
+
return buildDataSchema(modelName, dataConfig, mode, typeMap, schemaBuilder, zodDefaults);
|
|
2531
2901
|
}
|
|
2532
2902
|
function getWhereBuilt(whereConfig, matchedKey, wasDynamic) {
|
|
2533
2903
|
if (!wasDynamic) {
|
|
@@ -2597,12 +2967,26 @@ function createModelGuardExtension(config) {
|
|
|
2597
2967
|
}
|
|
2598
2968
|
if (!hasShapeProjection)
|
|
2599
2969
|
return {};
|
|
2970
|
+
if (!hasBodyProjection && !enforceProjection)
|
|
2971
|
+
return {};
|
|
2972
|
+
if (!hasBodyProjection && enforceProjection) {
|
|
2973
|
+
if (hasNestedClientControlledArgs(shape)) {
|
|
2974
|
+
throw new ShapeError(
|
|
2975
|
+
`Guard shape defines nested client-controlled projection args (orderBy/take/cursor/skip/where) but the client did not provide select/include in the ${method} body. With enforceProjection enabled, either always provide projection in the body or remove client-controlled nested args from the shape.`
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2600
2979
|
const projection = getProjection(shape, matchedKey, wasDynamic);
|
|
2601
|
-
|
|
2602
|
-
if (
|
|
2603
|
-
projectionBody
|
|
2604
|
-
|
|
2605
|
-
|
|
2980
|
+
let projectionBody;
|
|
2981
|
+
if (hasBodyProjection) {
|
|
2982
|
+
projectionBody = {};
|
|
2983
|
+
if ("select" in parsed)
|
|
2984
|
+
projectionBody.select = parsed.select;
|
|
2985
|
+
if ("include" in parsed)
|
|
2986
|
+
projectionBody.include = parsed.include;
|
|
2987
|
+
} else {
|
|
2988
|
+
projectionBody = buildDefaultProjectionBody(shape);
|
|
2989
|
+
}
|
|
2606
2990
|
const validated = projection.zodSchema.parse(projectionBody);
|
|
2607
2991
|
if (Object.keys(projection.forcedIncludeTree).length > 0) {
|
|
2608
2992
|
applyForcedTree(validated, "include", projection.forcedIncludeTree);
|
|
@@ -2633,6 +3017,12 @@ function createModelGuardExtension(config) {
|
|
|
2633
3017
|
let validatedWhere;
|
|
2634
3018
|
if (built.schema) {
|
|
2635
3019
|
validatedWhere = built.schema.parse(bodyWhere);
|
|
3020
|
+
} else if (bodyWhere !== void 0) {
|
|
3021
|
+
if (bodyWhere === null || !isPlainObject(bodyWhere) || Object.keys(bodyWhere).length > 0) {
|
|
3022
|
+
throw new ShapeError(
|
|
3023
|
+
"Guard shape where contains only forced conditions. Client where input is not accepted."
|
|
3024
|
+
);
|
|
3025
|
+
}
|
|
2636
3026
|
}
|
|
2637
3027
|
if (hasWhereForced(built.forced)) {
|
|
2638
3028
|
return preserveUnique ? mergeUniqueWhereForced(validatedWhere, built.forced) : mergeWhereForced(validatedWhere, built.forced);
|
|
@@ -2785,6 +3175,70 @@ function createModelGuardExtension(config) {
|
|
|
2785
3175
|
return callDelegate(method, args);
|
|
2786
3176
|
};
|
|
2787
3177
|
}
|
|
3178
|
+
function makeUpsertMethod() {
|
|
3179
|
+
return (body) => {
|
|
3180
|
+
const caller = resolveCaller();
|
|
3181
|
+
const resolved = resolveShape(input, body, contextFn, caller);
|
|
3182
|
+
if (resolved.shape.data) {
|
|
3183
|
+
throw new ShapeError('Guard shape "data" is not valid for upsert. Use "create" and "update" instead.');
|
|
3184
|
+
}
|
|
3185
|
+
if (!resolved.shape.create) {
|
|
3186
|
+
throw new ShapeError('Guard shape requires "create" for upsert');
|
|
3187
|
+
}
|
|
3188
|
+
if (!resolved.shape.update) {
|
|
3189
|
+
throw new ShapeError('Guard shape requires "update" for upsert');
|
|
3190
|
+
}
|
|
3191
|
+
if (!resolved.shape.where) {
|
|
3192
|
+
throw new ShapeError('Guard shape requires "where" for upsert');
|
|
3193
|
+
}
|
|
3194
|
+
validateMutationShapeKeys(
|
|
3195
|
+
resolved.shape,
|
|
3196
|
+
VALID_SHAPE_KEYS_UPSERT,
|
|
3197
|
+
"upsert"
|
|
3198
|
+
);
|
|
3199
|
+
validateMutationBodyKeys(resolved.body, ALLOWED_BODY_KEYS_UPSERT, "upsert");
|
|
3200
|
+
maybeValidateUniqueWhere(modelName, resolved.shape, "upsert");
|
|
3201
|
+
const fks = modelScopeFks.get(modelName) ?? /* @__PURE__ */ new Set();
|
|
3202
|
+
validateCreateCompleteness(modelName, resolved.shape.create, typeMap, fks, zodDefaults);
|
|
3203
|
+
const createDataSchema = getDataSchema(
|
|
3204
|
+
"create",
|
|
3205
|
+
resolved.shape.create,
|
|
3206
|
+
`upsert:create\0${resolved.matchedKey}`,
|
|
3207
|
+
resolved.wasDynamic
|
|
3208
|
+
);
|
|
3209
|
+
const createData = validateAndMergeData(resolved.body.create, createDataSchema, "upsert (create)");
|
|
3210
|
+
const updateDataSchema = getDataSchema(
|
|
3211
|
+
"update",
|
|
3212
|
+
resolved.shape.update,
|
|
3213
|
+
`upsert:update\0${resolved.matchedKey}`,
|
|
3214
|
+
resolved.wasDynamic
|
|
3215
|
+
);
|
|
3216
|
+
const updateData = validateAndMergeData(resolved.body.update, updateDataSchema, "upsert (update)");
|
|
3217
|
+
const where = requireWhere(
|
|
3218
|
+
resolved.shape,
|
|
3219
|
+
resolved.body.where,
|
|
3220
|
+
"upsert",
|
|
3221
|
+
true,
|
|
3222
|
+
resolved.matchedKey,
|
|
3223
|
+
resolved.wasDynamic
|
|
3224
|
+
);
|
|
3225
|
+
validateResolvedUniqueWhere(modelName, where, "upsert", uniqueMap);
|
|
3226
|
+
const args = {
|
|
3227
|
+
where,
|
|
3228
|
+
create: createData,
|
|
3229
|
+
update: updateData
|
|
3230
|
+
};
|
|
3231
|
+
const projectionArgs = resolveProjection(
|
|
3232
|
+
resolved.shape,
|
|
3233
|
+
resolved.body,
|
|
3234
|
+
"upsert",
|
|
3235
|
+
resolved.matchedKey,
|
|
3236
|
+
resolved.wasDynamic
|
|
3237
|
+
);
|
|
3238
|
+
Object.assign(args, projectionArgs);
|
|
3239
|
+
return callDelegate("upsert", args);
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
2788
3242
|
return {
|
|
2789
3243
|
findMany: makeReadMethod("findMany"),
|
|
2790
3244
|
findFirst: makeReadMethod("findFirst"),
|
|
@@ -2800,6 +3254,7 @@ function createModelGuardExtension(config) {
|
|
|
2800
3254
|
update: makeUpdateMethod("update"),
|
|
2801
3255
|
updateMany: makeUpdateMethod("updateMany"),
|
|
2802
3256
|
updateManyAndReturn: makeUpdateMethod("updateManyAndReturn"),
|
|
3257
|
+
upsert: makeUpsertMethod(),
|
|
2803
3258
|
delete: makeDeleteMethod("delete"),
|
|
2804
3259
|
deleteMany: makeDeleteMethod("deleteMany")
|
|
2805
3260
|
};
|
|
@@ -2845,15 +3300,19 @@ function createModelGuardExtension(config) {
|
|
|
2845
3300
|
|
|
2846
3301
|
// src/runtime/guard.ts
|
|
2847
3302
|
function createGuard(config) {
|
|
3303
|
+
const scalarBase = createScalarBase(config.guardConfig.strictDecimal ?? false);
|
|
2848
3304
|
const schemaBuilder = createSchemaBuilder(
|
|
2849
3305
|
config.typeMap,
|
|
2850
3306
|
config.zodChains,
|
|
2851
|
-
config.enumMap
|
|
3307
|
+
config.enumMap,
|
|
3308
|
+
scalarBase,
|
|
3309
|
+
config.zodDefaults ?? {}
|
|
2852
3310
|
);
|
|
2853
3311
|
const queryBuilder = createQueryBuilder(
|
|
2854
3312
|
config.typeMap,
|
|
2855
3313
|
config.enumMap,
|
|
2856
|
-
config.uniqueMap ?? {}
|
|
3314
|
+
config.uniqueMap ?? {},
|
|
3315
|
+
scalarBase
|
|
2857
3316
|
);
|
|
2858
3317
|
const log = config.logger ?? { warn: (msg) => console.warn(msg) };
|
|
2859
3318
|
const wrapZodErrors = config.wrapZodErrors ?? false;
|
|
@@ -2912,8 +3371,8 @@ function createGuard(config) {
|
|
|
2912
3371
|
if (typeof val === "string" || typeof val === "number" || typeof val === "bigint") {
|
|
2913
3372
|
scopeCtx[key] = val;
|
|
2914
3373
|
} else if (val !== null && val !== void 0) {
|
|
2915
|
-
|
|
2916
|
-
`prisma-guard: Scope root "${key}" has non-primitive value (${typeof val}). Only string, number, and bigint values are
|
|
3374
|
+
throw new PolicyError(
|
|
3375
|
+
`prisma-guard: Scope root "${key}" has non-primitive value (${typeof val}). Only string, number, and bigint values are accepted for scope context.`
|
|
2917
3376
|
);
|
|
2918
3377
|
}
|
|
2919
3378
|
}
|
|
@@ -2932,6 +3391,7 @@ function createGuard(config) {
|
|
|
2932
3391
|
zodDefaults: config.zodDefaults ?? {},
|
|
2933
3392
|
uniqueMap: config.uniqueMap ?? {},
|
|
2934
3393
|
scopeMap: config.scopeMap,
|
|
3394
|
+
guardConfig: config.guardConfig,
|
|
2935
3395
|
contextFn,
|
|
2936
3396
|
wrapZodErrors
|
|
2937
3397
|
});
|
|
@@ -2947,6 +3407,7 @@ export {
|
|
|
2947
3407
|
CallerError,
|
|
2948
3408
|
PolicyError,
|
|
2949
3409
|
ShapeError,
|
|
2950
|
-
createGuard
|
|
3410
|
+
createGuard,
|
|
3411
|
+
force
|
|
2951
3412
|
};
|
|
2952
3413
|
//# sourceMappingURL=index.js.map
|