@prisma/client-engine-runtime 6.8.0-dev.4 → 6.8.0-dev.40

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.
@@ -14,7 +14,12 @@ export type PrismaValueGenerator = {
14
14
  };
15
15
  };
16
16
  export declare function isPrismaValueGenerator(value: unknown): value is PrismaValueGenerator;
17
- export type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator;
17
+ export type PrismaValueBytes = {
18
+ prisma__type: 'bytes';
19
+ prisma__value: string;
20
+ };
21
+ export declare function isPrismaValueBytes(value: unknown): value is PrismaValueBytes;
22
+ export type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator | PrismaValueBytes;
18
23
  export type PrismaValueType = {
19
24
  type: 'Any';
20
25
  } | {
@@ -141,4 +146,63 @@ export type QueryPlanNode = {
141
146
  expr: QueryPlanNode;
142
147
  structure: ResultNode;
143
148
  };
149
+ } | {
150
+ type: 'validate';
151
+ args: {
152
+ expr: QueryPlanNode;
153
+ rules: DataRule[];
154
+ } & ValidationError;
155
+ } | {
156
+ type: 'if';
157
+ args: {
158
+ value: QueryPlanNode;
159
+ rule: DataRule;
160
+ then: QueryPlanNode;
161
+ else: QueryPlanNode;
162
+ };
163
+ } | {
164
+ type: 'unit';
165
+ };
166
+ export type DataRule = {
167
+ type: 'rowCountEq';
168
+ args: number;
169
+ } | {
170
+ type: 'rowCountNeq';
171
+ args: number;
172
+ } | {
173
+ type: 'never';
174
+ };
175
+ export type ValidationError = {
176
+ error_identifier: 'RELATION_VIOLATION';
177
+ context: {
178
+ relation: string;
179
+ modelA: string;
180
+ modelB: string;
181
+ };
182
+ } | {
183
+ error_identifier: 'MISSING_RELATED_RECORD';
184
+ context: {
185
+ model: string;
186
+ relation: string;
187
+ relationType: string;
188
+ operation: string;
189
+ neededFor?: string;
190
+ };
191
+ } | {
192
+ error_identifier: 'MISSING_RECORD';
193
+ context: {
194
+ operation: string;
195
+ };
196
+ } | {
197
+ error_identifier: 'INCOMPLETE_CONNECT_INPUT';
198
+ context: {
199
+ expectedRows: number;
200
+ };
201
+ } | {
202
+ error_identifier: 'RECORDS_NOT_CONNECTED';
203
+ context: {
204
+ relation: string;
205
+ parent: string;
206
+ child: string;
207
+ };
144
208
  };
package/dist/index.d.mts CHANGED
@@ -7,6 +7,16 @@ import { SqlQueryable } from '@prisma/driver-adapter-utils';
7
7
  import { SqlResultSet } from '@prisma/driver-adapter-utils';
8
8
  import { Transaction } from '@prisma/driver-adapter-utils';
9
9
 
10
+ export declare type DataRule = {
11
+ type: 'rowCountEq';
12
+ args: number;
13
+ } | {
14
+ type: 'rowCountNeq';
15
+ args: number;
16
+ } | {
17
+ type: 'never';
18
+ };
19
+
10
20
  declare type ExtendedSpanOptions = SpanOptions & {
11
21
  name: string;
12
22
  };
@@ -22,6 +32,8 @@ export declare type Fragment = {
22
32
  type: 'parameterTupleList';
23
33
  };
24
34
 
35
+ export declare function isPrismaValueBytes(value: unknown): value is PrismaValueBytes;
36
+
25
37
  export declare function isPrismaValueGenerator(value: unknown): value is PrismaValueGenerator;
26
38
 
27
39
  export declare function isPrismaValuePlaceholder(value: unknown): value is PrismaValuePlaceholder;
@@ -39,7 +51,12 @@ export declare interface PlaceholderFormat {
39
51
  hasNumbering: boolean;
40
52
  }
41
53
 
42
- export declare type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator;
54
+ export declare type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator | PrismaValueBytes;
55
+
56
+ export declare type PrismaValueBytes = {
57
+ prisma__type: 'bytes';
58
+ prisma__value: string;
59
+ };
43
60
 
44
61
  export declare type PrismaValueGenerator = {
45
62
  prisma__type: 'generatorCall';
@@ -194,6 +211,22 @@ export declare type QueryPlanNode = {
194
211
  expr: QueryPlanNode;
195
212
  structure: ResultNode;
196
213
  };
214
+ } | {
215
+ type: 'validate';
216
+ args: {
217
+ expr: QueryPlanNode;
218
+ rules: DataRule[];
219
+ } & ValidationError;
220
+ } | {
221
+ type: 'if';
222
+ args: {
223
+ value: QueryPlanNode;
224
+ rule: DataRule;
225
+ then: QueryPlanNode;
226
+ else: QueryPlanNode;
227
+ };
228
+ } | {
229
+ type: 'unit';
197
230
  };
198
231
 
199
232
  export declare type ResultNode = {
@@ -266,6 +299,41 @@ export declare class UserFacingError extends Error {
266
299
  };
267
300
  }
268
301
 
302
+ export declare type ValidationError = {
303
+ error_identifier: 'RELATION_VIOLATION';
304
+ context: {
305
+ relation: string;
306
+ modelA: string;
307
+ modelB: string;
308
+ };
309
+ } | {
310
+ error_identifier: 'MISSING_RELATED_RECORD';
311
+ context: {
312
+ model: string;
313
+ relation: string;
314
+ relationType: string;
315
+ operation: string;
316
+ neededFor?: string;
317
+ };
318
+ } | {
319
+ error_identifier: 'MISSING_RECORD';
320
+ context: {
321
+ operation: string;
322
+ };
323
+ } | {
324
+ error_identifier: 'INCOMPLETE_CONNECT_INPUT';
325
+ context: {
326
+ expectedRows: number;
327
+ };
328
+ } | {
329
+ error_identifier: 'RECORDS_NOT_CONNECTED';
330
+ context: {
331
+ relation: string;
332
+ parent: string;
333
+ child: string;
334
+ };
335
+ };
336
+
269
337
  /**
270
338
  * The general type of values each node can evaluate to.
271
339
  */
package/dist/index.d.ts CHANGED
@@ -7,6 +7,16 @@ import { SqlQueryable } from '@prisma/driver-adapter-utils';
7
7
  import { SqlResultSet } from '@prisma/driver-adapter-utils';
8
8
  import { Transaction } from '@prisma/driver-adapter-utils';
9
9
 
10
+ export declare type DataRule = {
11
+ type: 'rowCountEq';
12
+ args: number;
13
+ } | {
14
+ type: 'rowCountNeq';
15
+ args: number;
16
+ } | {
17
+ type: 'never';
18
+ };
19
+
10
20
  declare type ExtendedSpanOptions = SpanOptions & {
11
21
  name: string;
12
22
  };
@@ -22,6 +32,8 @@ export declare type Fragment = {
22
32
  type: 'parameterTupleList';
23
33
  };
24
34
 
35
+ export declare function isPrismaValueBytes(value: unknown): value is PrismaValueBytes;
36
+
25
37
  export declare function isPrismaValueGenerator(value: unknown): value is PrismaValueGenerator;
26
38
 
27
39
  export declare function isPrismaValuePlaceholder(value: unknown): value is PrismaValuePlaceholder;
@@ -39,7 +51,12 @@ export declare interface PlaceholderFormat {
39
51
  hasNumbering: boolean;
40
52
  }
41
53
 
42
- export declare type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator;
54
+ export declare type PrismaValue = string | boolean | number | PrismaValue[] | null | Record<string, unknown> | PrismaValuePlaceholder | PrismaValueGenerator | PrismaValueBytes;
55
+
56
+ export declare type PrismaValueBytes = {
57
+ prisma__type: 'bytes';
58
+ prisma__value: string;
59
+ };
43
60
 
44
61
  export declare type PrismaValueGenerator = {
45
62
  prisma__type: 'generatorCall';
@@ -194,6 +211,22 @@ export declare type QueryPlanNode = {
194
211
  expr: QueryPlanNode;
195
212
  structure: ResultNode;
196
213
  };
214
+ } | {
215
+ type: 'validate';
216
+ args: {
217
+ expr: QueryPlanNode;
218
+ rules: DataRule[];
219
+ } & ValidationError;
220
+ } | {
221
+ type: 'if';
222
+ args: {
223
+ value: QueryPlanNode;
224
+ rule: DataRule;
225
+ then: QueryPlanNode;
226
+ else: QueryPlanNode;
227
+ };
228
+ } | {
229
+ type: 'unit';
197
230
  };
198
231
 
199
232
  export declare type ResultNode = {
@@ -266,6 +299,41 @@ export declare class UserFacingError extends Error {
266
299
  };
267
300
  }
268
301
 
302
+ export declare type ValidationError = {
303
+ error_identifier: 'RELATION_VIOLATION';
304
+ context: {
305
+ relation: string;
306
+ modelA: string;
307
+ modelB: string;
308
+ };
309
+ } | {
310
+ error_identifier: 'MISSING_RELATED_RECORD';
311
+ context: {
312
+ model: string;
313
+ relation: string;
314
+ relationType: string;
315
+ operation: string;
316
+ neededFor?: string;
317
+ };
318
+ } | {
319
+ error_identifier: 'MISSING_RECORD';
320
+ context: {
321
+ operation: string;
322
+ };
323
+ } | {
324
+ error_identifier: 'INCOMPLETE_CONNECT_INPUT';
325
+ context: {
326
+ expectedRows: number;
327
+ };
328
+ } | {
329
+ error_identifier: 'RECORDS_NOT_CONNECTED';
330
+ context: {
331
+ relation: string;
332
+ parent: string;
333
+ child: string;
334
+ };
335
+ };
336
+
269
337
  /**
270
338
  * The general type of values each node can evaluate to.
271
339
  */
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  TransactionManager: () => TransactionManager,
35
35
  TransactionManagerError: () => TransactionManagerError,
36
36
  UserFacingError: () => UserFacingError,
37
+ isPrismaValueBytes: () => isPrismaValueBytes,
37
38
  isPrismaValueGenerator: () => isPrismaValueGenerator,
38
39
  isPrismaValuePlaceholder: () => isPrismaValuePlaceholder,
39
40
  noopTracingHelper: () => noopTracingHelper
@@ -211,6 +212,7 @@ function renderConstraint(constraint) {
211
212
  }
212
213
 
213
214
  // src/interpreter/DataMapper.ts
215
+ var import_decimal = __toESM(require("decimal.js"));
214
216
  function applyDataMap(data, structure) {
215
217
  switch (structure.type) {
216
218
  case "Object":
@@ -283,22 +285,21 @@ function mapValue(value, resultType) {
283
285
  case "Boolean":
284
286
  return typeof value === "boolean" ? value : value !== "0";
285
287
  case "Decimal":
286
- return typeof value === "number" ? value : parseFloat(`${value}`);
288
+ return typeof value === "number" ? new import_decimal.default(value) : new import_decimal.default(`${value}`);
287
289
  case "Date":
288
290
  return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
289
291
  case "Array": {
290
292
  const values = value;
291
- return values.map((v) => {
292
- mapValue(v, resultType.inner);
293
- });
293
+ return values.map((v) => mapValue(v, resultType.inner));
294
294
  }
295
295
  case "Object":
296
- return typeof value === "object" ? value : { value };
297
- case "Bytes":
298
- if (typeof value !== "string") {
299
- throw new Error(`DataMapper: Bytes data is not a string, got: ${typeof value}`);
296
+ return typeof value === "string" ? value : JSON.stringify(value);
297
+ case "Bytes": {
298
+ if (!Array.isArray(value)) {
299
+ throw new Error(`DataMapper: Bytes data is invalid, got: ${typeof value}`);
300
300
  }
301
- return value;
301
+ return new Uint8Array(value);
302
+ }
302
303
  default:
303
304
  assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
304
305
  }
@@ -405,6 +406,9 @@ function isPrismaValuePlaceholder(value) {
405
406
  function isPrismaValueGenerator(value) {
406
407
  return typeof value === "object" && value !== null && value["prisma__type"] === "generatorCall";
407
408
  }
409
+ function isPrismaValueBytes(value) {
410
+ return typeof value === "object" && value !== null && value["prisma__type"] === "bytes";
411
+ }
408
412
 
409
413
  // src/interpreter/renderQuery.ts
410
414
  function renderQuery(dbQuery, scope, generators) {
@@ -448,6 +452,9 @@ function evaluateParam(param, scope, generators) {
448
452
  if (Array.isArray(value)) {
449
453
  value = value.map((el) => evaluateParam(el, scope, generators));
450
454
  }
455
+ if (isPrismaValueBytes(value)) {
456
+ value = Buffer.from(value.prisma__value, "base64");
457
+ }
451
458
  return value;
452
459
  }
453
460
  function renderTemplateSql(fragments, placeholderFormat, params) {
@@ -518,9 +525,6 @@ function renderRawSql(sql, params) {
518
525
  };
519
526
  }
520
527
  function toArgType(value) {
521
- if (value === null) {
522
- return "Int32";
523
- }
524
528
  if (typeof value === "string") {
525
529
  return "Text";
526
530
  }
@@ -533,30 +537,10 @@ function toArgType(value) {
533
537
  if (Array.isArray(value)) {
534
538
  return "Array";
535
539
  }
536
- if (isPrismaValuePlaceholder(value)) {
537
- return placeholderTypeToArgType(value.prisma__value.type);
538
- }
539
- return "Json";
540
- }
541
- function placeholderTypeToArgType(type) {
542
- const typeMap = {
543
- Any: "Json",
544
- String: "Text",
545
- Int: "Int32",
546
- BigInt: "Int64",
547
- Float: "Double",
548
- Boolean: "Boolean",
549
- Decimal: "Numeric",
550
- Date: "DateTime",
551
- Object: "Json",
552
- Bytes: "Bytes",
553
- Array: "Array"
554
- };
555
- const mappedType = typeMap[type];
556
- if (!mappedType) {
557
- throw new Error(`Unknown placeholder type: ${type}`);
540
+ if (Buffer.isBuffer(value)) {
541
+ return "Bytes";
558
542
  }
559
- return mappedType;
543
+ return "Unknown";
560
544
  }
561
545
  function doesRequireEvaluation(param) {
562
546
  return isPrismaValuePlaceholder(param) || isPrismaValueGenerator(param);
@@ -584,6 +568,71 @@ function serializeSql(resultSet) {
584
568
  );
585
569
  }
586
570
 
571
+ // src/interpreter/validation.ts
572
+ function performValidation(data, rules, error) {
573
+ if (!rules.every((rule) => doesSatisfyRule(data, rule))) {
574
+ const message = renderMessage(data, error);
575
+ const code = getErrorCode2(error);
576
+ throw new UserFacingError(message, code, error.context);
577
+ }
578
+ }
579
+ function doesSatisfyRule(data, rule) {
580
+ switch (rule.type) {
581
+ case "rowCountEq":
582
+ if (Array.isArray(data)) {
583
+ return data.length === rule.args;
584
+ }
585
+ if (data === null) {
586
+ return rule.args === 0;
587
+ }
588
+ return rule.args === 1;
589
+ case "rowCountNeq":
590
+ if (Array.isArray(data)) {
591
+ return data.length !== rule.args;
592
+ }
593
+ if (data === null) {
594
+ return rule.args !== 0;
595
+ }
596
+ return rule.args !== 1;
597
+ case "never":
598
+ return false;
599
+ default:
600
+ assertNever(rule, `Unknown rule type: ${rule.type}`);
601
+ }
602
+ }
603
+ function renderMessage(data, error) {
604
+ switch (error.error_identifier) {
605
+ case "RELATION_VIOLATION":
606
+ return `The change you are trying to make would violate the required relation '${error.context.relation}' between the \`${error.context.modelA}\` and \`${error.context.modelB}\` models.`;
607
+ case "MISSING_RECORD":
608
+ return `An operation failed because it depends on one or more records that were required but not found. No record was found for ${error.context.operation}.`;
609
+ case "MISSING_RELATED_RECORD": {
610
+ const hint = error.context.neededFor ? ` (needed to ${error.context.neededFor})` : "";
611
+ return `An operation failed because it depends on one or more records that were required but not found. No '${error.context.model}' record${hint} was found for ${error.context.operation} on ${error.context.relationType} relation '${error.context.relation}'.`;
612
+ }
613
+ case "INCOMPLETE_CONNECT_INPUT":
614
+ return `An operation failed because it depends on one or more records that were required but not found. Expected ${error.context.expectedRows} records to be connected, found only ${Array.isArray(data) ? data.length : 0}.`;
615
+ case "RECORDS_NOT_CONNECTED":
616
+ return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
617
+ default:
618
+ assertNever(error, `Unknown error identifier: ${error}`);
619
+ }
620
+ }
621
+ function getErrorCode2(error) {
622
+ switch (error.error_identifier) {
623
+ case "RELATION_VIOLATION":
624
+ return "P2014";
625
+ case "RECORDS_NOT_CONNECTED":
626
+ return "P2017";
627
+ case "MISSING_RECORD":
628
+ case "MISSING_RELATED_RECORD":
629
+ case "INCOMPLETE_CONNECT_INPUT":
630
+ return "P2025";
631
+ default:
632
+ assertNever(error, `Unknown error identifier: ${error}`);
633
+ }
634
+ }
635
+
587
636
  // src/interpreter/QueryInterpreter.ts
588
637
  var QueryInterpreter = class _QueryInterpreter {
589
638
  #transactionManager;
@@ -624,11 +673,9 @@ var QueryInterpreter = class _QueryInterpreter {
624
673
  }
625
674
  case "let": {
626
675
  const nestedScope = Object.create(scope);
627
- await Promise.all(
628
- node.args.bindings.map(async (binding) => {
629
- nestedScope[binding.name] = await this.interpretNode(binding.expr, queryable, scope, generators);
630
- })
631
- );
676
+ for (const binding of node.args.bindings) {
677
+ nestedScope[binding.name] = await this.interpretNode(binding.expr, queryable, nestedScope, generators);
678
+ }
632
679
  return this.interpretNode(node.args.expr, queryable, nestedScope, generators);
633
680
  }
634
681
  case "getFirstNonEmpty": {
@@ -721,6 +768,22 @@ var QueryInterpreter = class _QueryInterpreter {
721
768
  const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
722
769
  return applyDataMap(data, node.args.structure);
723
770
  }
771
+ case "validate": {
772
+ const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
773
+ performValidation(data, node.args.rules, node.args);
774
+ return data;
775
+ }
776
+ case "if": {
777
+ const value = await this.interpretNode(node.args.value, queryable, scope, generators);
778
+ if (doesSatisfyRule(value, node.args.rule)) {
779
+ return await this.interpretNode(node.args.then, queryable, scope, generators);
780
+ } else {
781
+ return await this.interpretNode(node.args.else, queryable, scope, generators);
782
+ }
783
+ }
784
+ case "unit": {
785
+ return void 0;
786
+ }
724
787
  default:
725
788
  assertNever(node, `Unexpected node type: ${node.type}`);
726
789
  }
@@ -1030,6 +1093,7 @@ var TransactionManager = class {
1030
1093
  TransactionManager,
1031
1094
  TransactionManagerError,
1032
1095
  UserFacingError,
1096
+ isPrismaValueBytes,
1033
1097
  isPrismaValueGenerator,
1034
1098
  isPrismaValuePlaceholder,
1035
1099
  noopTracingHelper
package/dist/index.mjs CHANGED
@@ -169,6 +169,7 @@ function renderConstraint(constraint) {
169
169
  }
170
170
 
171
171
  // src/interpreter/DataMapper.ts
172
+ import Decimal from "decimal.js";
172
173
  function applyDataMap(data, structure) {
173
174
  switch (structure.type) {
174
175
  case "Object":
@@ -241,22 +242,21 @@ function mapValue(value, resultType) {
241
242
  case "Boolean":
242
243
  return typeof value === "boolean" ? value : value !== "0";
243
244
  case "Decimal":
244
- return typeof value === "number" ? value : parseFloat(`${value}`);
245
+ return typeof value === "number" ? new Decimal(value) : new Decimal(`${value}`);
245
246
  case "Date":
246
247
  return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
247
248
  case "Array": {
248
249
  const values = value;
249
- return values.map((v) => {
250
- mapValue(v, resultType.inner);
251
- });
250
+ return values.map((v) => mapValue(v, resultType.inner));
252
251
  }
253
252
  case "Object":
254
- return typeof value === "object" ? value : { value };
255
- case "Bytes":
256
- if (typeof value !== "string") {
257
- throw new Error(`DataMapper: Bytes data is not a string, got: ${typeof value}`);
253
+ return typeof value === "string" ? value : JSON.stringify(value);
254
+ case "Bytes": {
255
+ if (!Array.isArray(value)) {
256
+ throw new Error(`DataMapper: Bytes data is invalid, got: ${typeof value}`);
258
257
  }
259
- return value;
258
+ return new Uint8Array(value);
259
+ }
260
260
  default:
261
261
  assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
262
262
  }
@@ -363,6 +363,9 @@ function isPrismaValuePlaceholder(value) {
363
363
  function isPrismaValueGenerator(value) {
364
364
  return typeof value === "object" && value !== null && value["prisma__type"] === "generatorCall";
365
365
  }
366
+ function isPrismaValueBytes(value) {
367
+ return typeof value === "object" && value !== null && value["prisma__type"] === "bytes";
368
+ }
366
369
 
367
370
  // src/interpreter/renderQuery.ts
368
371
  function renderQuery(dbQuery, scope, generators) {
@@ -406,6 +409,9 @@ function evaluateParam(param, scope, generators) {
406
409
  if (Array.isArray(value)) {
407
410
  value = value.map((el) => evaluateParam(el, scope, generators));
408
411
  }
412
+ if (isPrismaValueBytes(value)) {
413
+ value = Buffer.from(value.prisma__value, "base64");
414
+ }
409
415
  return value;
410
416
  }
411
417
  function renderTemplateSql(fragments, placeholderFormat, params) {
@@ -476,9 +482,6 @@ function renderRawSql(sql, params) {
476
482
  };
477
483
  }
478
484
  function toArgType(value) {
479
- if (value === null) {
480
- return "Int32";
481
- }
482
485
  if (typeof value === "string") {
483
486
  return "Text";
484
487
  }
@@ -491,30 +494,10 @@ function toArgType(value) {
491
494
  if (Array.isArray(value)) {
492
495
  return "Array";
493
496
  }
494
- if (isPrismaValuePlaceholder(value)) {
495
- return placeholderTypeToArgType(value.prisma__value.type);
496
- }
497
- return "Json";
498
- }
499
- function placeholderTypeToArgType(type) {
500
- const typeMap = {
501
- Any: "Json",
502
- String: "Text",
503
- Int: "Int32",
504
- BigInt: "Int64",
505
- Float: "Double",
506
- Boolean: "Boolean",
507
- Decimal: "Numeric",
508
- Date: "DateTime",
509
- Object: "Json",
510
- Bytes: "Bytes",
511
- Array: "Array"
512
- };
513
- const mappedType = typeMap[type];
514
- if (!mappedType) {
515
- throw new Error(`Unknown placeholder type: ${type}`);
497
+ if (Buffer.isBuffer(value)) {
498
+ return "Bytes";
516
499
  }
517
- return mappedType;
500
+ return "Unknown";
518
501
  }
519
502
  function doesRequireEvaluation(param) {
520
503
  return isPrismaValuePlaceholder(param) || isPrismaValueGenerator(param);
@@ -542,6 +525,71 @@ function serializeSql(resultSet) {
542
525
  );
543
526
  }
544
527
 
528
+ // src/interpreter/validation.ts
529
+ function performValidation(data, rules, error) {
530
+ if (!rules.every((rule) => doesSatisfyRule(data, rule))) {
531
+ const message = renderMessage(data, error);
532
+ const code = getErrorCode2(error);
533
+ throw new UserFacingError(message, code, error.context);
534
+ }
535
+ }
536
+ function doesSatisfyRule(data, rule) {
537
+ switch (rule.type) {
538
+ case "rowCountEq":
539
+ if (Array.isArray(data)) {
540
+ return data.length === rule.args;
541
+ }
542
+ if (data === null) {
543
+ return rule.args === 0;
544
+ }
545
+ return rule.args === 1;
546
+ case "rowCountNeq":
547
+ if (Array.isArray(data)) {
548
+ return data.length !== rule.args;
549
+ }
550
+ if (data === null) {
551
+ return rule.args !== 0;
552
+ }
553
+ return rule.args !== 1;
554
+ case "never":
555
+ return false;
556
+ default:
557
+ assertNever(rule, `Unknown rule type: ${rule.type}`);
558
+ }
559
+ }
560
+ function renderMessage(data, error) {
561
+ switch (error.error_identifier) {
562
+ case "RELATION_VIOLATION":
563
+ return `The change you are trying to make would violate the required relation '${error.context.relation}' between the \`${error.context.modelA}\` and \`${error.context.modelB}\` models.`;
564
+ case "MISSING_RECORD":
565
+ return `An operation failed because it depends on one or more records that were required but not found. No record was found for ${error.context.operation}.`;
566
+ case "MISSING_RELATED_RECORD": {
567
+ const hint = error.context.neededFor ? ` (needed to ${error.context.neededFor})` : "";
568
+ return `An operation failed because it depends on one or more records that were required but not found. No '${error.context.model}' record${hint} was found for ${error.context.operation} on ${error.context.relationType} relation '${error.context.relation}'.`;
569
+ }
570
+ case "INCOMPLETE_CONNECT_INPUT":
571
+ return `An operation failed because it depends on one or more records that were required but not found. Expected ${error.context.expectedRows} records to be connected, found only ${Array.isArray(data) ? data.length : 0}.`;
572
+ case "RECORDS_NOT_CONNECTED":
573
+ return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
574
+ default:
575
+ assertNever(error, `Unknown error identifier: ${error}`);
576
+ }
577
+ }
578
+ function getErrorCode2(error) {
579
+ switch (error.error_identifier) {
580
+ case "RELATION_VIOLATION":
581
+ return "P2014";
582
+ case "RECORDS_NOT_CONNECTED":
583
+ return "P2017";
584
+ case "MISSING_RECORD":
585
+ case "MISSING_RELATED_RECORD":
586
+ case "INCOMPLETE_CONNECT_INPUT":
587
+ return "P2025";
588
+ default:
589
+ assertNever(error, `Unknown error identifier: ${error}`);
590
+ }
591
+ }
592
+
545
593
  // src/interpreter/QueryInterpreter.ts
546
594
  var QueryInterpreter = class _QueryInterpreter {
547
595
  #transactionManager;
@@ -582,11 +630,9 @@ var QueryInterpreter = class _QueryInterpreter {
582
630
  }
583
631
  case "let": {
584
632
  const nestedScope = Object.create(scope);
585
- await Promise.all(
586
- node.args.bindings.map(async (binding) => {
587
- nestedScope[binding.name] = await this.interpretNode(binding.expr, queryable, scope, generators);
588
- })
589
- );
633
+ for (const binding of node.args.bindings) {
634
+ nestedScope[binding.name] = await this.interpretNode(binding.expr, queryable, nestedScope, generators);
635
+ }
590
636
  return this.interpretNode(node.args.expr, queryable, nestedScope, generators);
591
637
  }
592
638
  case "getFirstNonEmpty": {
@@ -679,6 +725,22 @@ var QueryInterpreter = class _QueryInterpreter {
679
725
  const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
680
726
  return applyDataMap(data, node.args.structure);
681
727
  }
728
+ case "validate": {
729
+ const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
730
+ performValidation(data, node.args.rules, node.args);
731
+ return data;
732
+ }
733
+ case "if": {
734
+ const value = await this.interpretNode(node.args.value, queryable, scope, generators);
735
+ if (doesSatisfyRule(value, node.args.rule)) {
736
+ return await this.interpretNode(node.args.then, queryable, scope, generators);
737
+ } else {
738
+ return await this.interpretNode(node.args.else, queryable, scope, generators);
739
+ }
740
+ }
741
+ case "unit": {
742
+ return void 0;
743
+ }
682
744
  default:
683
745
  assertNever(node, `Unexpected node type: ${node.type}`);
684
746
  }
@@ -987,6 +1049,7 @@ export {
987
1049
  TransactionManager,
988
1050
  TransactionManagerError,
989
1051
  UserFacingError,
1052
+ isPrismaValueBytes,
990
1053
  isPrismaValueGenerator,
991
1054
  isPrismaValuePlaceholder,
992
1055
  noopTracingHelper
@@ -1,4 +1,3 @@
1
- import { PrismaValue } from '../QueryPlan';
2
1
  export declare class GeneratorRegistry {
3
2
  #private;
4
3
  constructor();
@@ -17,5 +16,5 @@ export interface GeneratorRegistrySnapshot {
17
16
  [key: string]: ValueGenerator;
18
17
  }
19
18
  export interface ValueGenerator {
20
- generate(...args: PrismaValue[]): PrismaValue;
19
+ generate(...args: unknown[]): unknown;
21
20
  }
@@ -0,0 +1,3 @@
1
+ import { DataRule, ValidationError } from '../QueryPlan';
2
+ export declare function performValidation(data: unknown, rules: DataRule[], error: ValidationError): void;
3
+ export declare function doesSatisfyRule(data: unknown, rule: DataRule): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/client-engine-runtime",
3
- "version": "6.8.0-dev.4",
3
+ "version": "6.8.0-dev.40",
4
4
  "description": "This package is intended for Prisma's internal use",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -27,11 +27,12 @@
27
27
  "@bugsnag/cuid": "3.2.1",
28
28
  "@opentelemetry/api": "1.9.0",
29
29
  "@paralleldrive/cuid2": "2.2.2",
30
+ "decimal.js": "10.5.0",
30
31
  "nanoid": "5.1.5",
31
32
  "ulid": "3.0.0",
32
33
  "uuid": "11.1.0",
33
- "@prisma/debug": "6.8.0-dev.4",
34
- "@prisma/driver-adapter-utils": "6.8.0-dev.4"
34
+ "@prisma/debug": "6.8.0-dev.40",
35
+ "@prisma/driver-adapter-utils": "6.8.0-dev.40"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@types/jest": "29.5.14",