@prisma/client-engine-runtime 6.8.0-dev.11 → 6.8.0-dev.12

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.
@@ -141,4 +141,51 @@ export type QueryPlanNode = {
141
141
  expr: QueryPlanNode;
142
142
  structure: ResultNode;
143
143
  };
144
+ } | {
145
+ type: 'validate';
146
+ args: {
147
+ expr: QueryPlanNode;
148
+ rules: DataRule[];
149
+ } & ValidationError;
150
+ };
151
+ export type DataRule = {
152
+ type: 'rowCountEq';
153
+ args: number;
154
+ } | {
155
+ type: 'rowCountNeq';
156
+ args: number;
157
+ };
158
+ export type ValidationError = {
159
+ error_identifier: 'RELATION_VIOLATION';
160
+ context: {
161
+ relation: string;
162
+ modelA: string;
163
+ modelB: string;
164
+ };
165
+ } | {
166
+ error_identifier: 'MISSING_RELATED_RECORD';
167
+ context: {
168
+ model: string;
169
+ relation: string;
170
+ relationType: string;
171
+ operation: string;
172
+ neededFor?: string;
173
+ };
174
+ } | {
175
+ error_identifier: 'MISSING_RECORD';
176
+ context: {
177
+ operation: string;
178
+ };
179
+ } | {
180
+ error_identifier: 'INCOMPLETE_CONNECT_INPUT';
181
+ context: {
182
+ expectedRows: number;
183
+ };
184
+ } | {
185
+ error_identifier: 'RECORDS_NOT_CONNECTED';
186
+ context: {
187
+ relation: string;
188
+ parent: string;
189
+ child: string;
190
+ };
144
191
  };
package/dist/index.d.mts CHANGED
@@ -7,6 +7,14 @@ 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
+
10
18
  declare type ExtendedSpanOptions = SpanOptions & {
11
19
  name: string;
12
20
  };
@@ -194,6 +202,12 @@ export declare type QueryPlanNode = {
194
202
  expr: QueryPlanNode;
195
203
  structure: ResultNode;
196
204
  };
205
+ } | {
206
+ type: 'validate';
207
+ args: {
208
+ expr: QueryPlanNode;
209
+ rules: DataRule[];
210
+ } & ValidationError;
197
211
  };
198
212
 
199
213
  export declare type ResultNode = {
@@ -266,6 +280,41 @@ export declare class UserFacingError extends Error {
266
280
  };
267
281
  }
268
282
 
283
+ export declare type ValidationError = {
284
+ error_identifier: 'RELATION_VIOLATION';
285
+ context: {
286
+ relation: string;
287
+ modelA: string;
288
+ modelB: string;
289
+ };
290
+ } | {
291
+ error_identifier: 'MISSING_RELATED_RECORD';
292
+ context: {
293
+ model: string;
294
+ relation: string;
295
+ relationType: string;
296
+ operation: string;
297
+ neededFor?: string;
298
+ };
299
+ } | {
300
+ error_identifier: 'MISSING_RECORD';
301
+ context: {
302
+ operation: string;
303
+ };
304
+ } | {
305
+ error_identifier: 'INCOMPLETE_CONNECT_INPUT';
306
+ context: {
307
+ expectedRows: number;
308
+ };
309
+ } | {
310
+ error_identifier: 'RECORDS_NOT_CONNECTED';
311
+ context: {
312
+ relation: string;
313
+ parent: string;
314
+ child: string;
315
+ };
316
+ };
317
+
269
318
  /**
270
319
  * The general type of values each node can evaluate to.
271
320
  */
package/dist/index.d.ts CHANGED
@@ -7,6 +7,14 @@ 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
+
10
18
  declare type ExtendedSpanOptions = SpanOptions & {
11
19
  name: string;
12
20
  };
@@ -194,6 +202,12 @@ export declare type QueryPlanNode = {
194
202
  expr: QueryPlanNode;
195
203
  structure: ResultNode;
196
204
  };
205
+ } | {
206
+ type: 'validate';
207
+ args: {
208
+ expr: QueryPlanNode;
209
+ rules: DataRule[];
210
+ } & ValidationError;
197
211
  };
198
212
 
199
213
  export declare type ResultNode = {
@@ -266,6 +280,41 @@ export declare class UserFacingError extends Error {
266
280
  };
267
281
  }
268
282
 
283
+ export declare type ValidationError = {
284
+ error_identifier: 'RELATION_VIOLATION';
285
+ context: {
286
+ relation: string;
287
+ modelA: string;
288
+ modelB: string;
289
+ };
290
+ } | {
291
+ error_identifier: 'MISSING_RELATED_RECORD';
292
+ context: {
293
+ model: string;
294
+ relation: string;
295
+ relationType: string;
296
+ operation: string;
297
+ neededFor?: string;
298
+ };
299
+ } | {
300
+ error_identifier: 'MISSING_RECORD';
301
+ context: {
302
+ operation: string;
303
+ };
304
+ } | {
305
+ error_identifier: 'INCOMPLETE_CONNECT_INPUT';
306
+ context: {
307
+ expectedRows: number;
308
+ };
309
+ } | {
310
+ error_identifier: 'RECORDS_NOT_CONNECTED';
311
+ context: {
312
+ relation: string;
313
+ parent: string;
314
+ child: string;
315
+ };
316
+ };
317
+
269
318
  /**
270
319
  * The general type of values each node can evaluate to.
271
320
  */
package/dist/index.js CHANGED
@@ -581,6 +581,67 @@ function serializeSql(resultSet) {
581
581
  );
582
582
  }
583
583
 
584
+ // src/interpreter/validation.ts
585
+ function performValidation(data, rules, error) {
586
+ if (!rules.every((rule) => doesSatisfyRule(data, rule))) {
587
+ const message = renderMessage(data, error);
588
+ const code = getErrorCode2(error);
589
+ throw new UserFacingError(message, code, error.context);
590
+ }
591
+ }
592
+ function doesSatisfyRule(data, rule) {
593
+ switch (rule.type) {
594
+ case "rowCountEq":
595
+ if (Array.isArray(data)) {
596
+ return data.length === rule.args;
597
+ }
598
+ if (data === null) {
599
+ return rule.args === 0;
600
+ }
601
+ return rule.args === 1;
602
+ case "rowCountNeq":
603
+ if (Array.isArray(data)) {
604
+ return data.length !== rule.args;
605
+ }
606
+ if (data === null) {
607
+ return rule.args !== 0;
608
+ }
609
+ return rule.args !== 1;
610
+ }
611
+ }
612
+ function renderMessage(data, error) {
613
+ switch (error.error_identifier) {
614
+ case "RELATION_VIOLATION":
615
+ 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.`;
616
+ case "MISSING_RECORD":
617
+ return `No record was found for ${error.context.operation}.`;
618
+ case "MISSING_RELATED_RECORD": {
619
+ const hint = error.context.neededFor ? ` (needed to ${error.context.neededFor})` : "";
620
+ 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}'.`;
621
+ }
622
+ case "INCOMPLETE_CONNECT_INPUT":
623
+ 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}.`;
624
+ case "RECORDS_NOT_CONNECTED":
625
+ return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
626
+ default:
627
+ assertNever(error, `Unknown error identifier: ${error}`);
628
+ }
629
+ }
630
+ function getErrorCode2(error) {
631
+ switch (error.error_identifier) {
632
+ case "RELATION_VIOLATION":
633
+ return "P2014";
634
+ case "RECORDS_NOT_CONNECTED":
635
+ return "P2017";
636
+ case "MISSING_RECORD":
637
+ case "MISSING_RELATED_RECORD":
638
+ case "INCOMPLETE_CONNECT_INPUT":
639
+ return "P2025";
640
+ default:
641
+ assertNever(error, `Unknown error identifier: ${error}`);
642
+ }
643
+ }
644
+
584
645
  // src/interpreter/QueryInterpreter.ts
585
646
  var QueryInterpreter = class _QueryInterpreter {
586
647
  #transactionManager;
@@ -718,6 +779,11 @@ var QueryInterpreter = class _QueryInterpreter {
718
779
  const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
719
780
  return applyDataMap(data, node.args.structure);
720
781
  }
782
+ case "validate": {
783
+ const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
784
+ performValidation(data, node.args.rules, node.args);
785
+ return data;
786
+ }
721
787
  default:
722
788
  assertNever(node, `Unexpected node type: ${node.type}`);
723
789
  }
package/dist/index.mjs CHANGED
@@ -539,6 +539,67 @@ function serializeSql(resultSet) {
539
539
  );
540
540
  }
541
541
 
542
+ // src/interpreter/validation.ts
543
+ function performValidation(data, rules, error) {
544
+ if (!rules.every((rule) => doesSatisfyRule(data, rule))) {
545
+ const message = renderMessage(data, error);
546
+ const code = getErrorCode2(error);
547
+ throw new UserFacingError(message, code, error.context);
548
+ }
549
+ }
550
+ function doesSatisfyRule(data, rule) {
551
+ switch (rule.type) {
552
+ case "rowCountEq":
553
+ if (Array.isArray(data)) {
554
+ return data.length === rule.args;
555
+ }
556
+ if (data === null) {
557
+ return rule.args === 0;
558
+ }
559
+ return rule.args === 1;
560
+ case "rowCountNeq":
561
+ if (Array.isArray(data)) {
562
+ return data.length !== rule.args;
563
+ }
564
+ if (data === null) {
565
+ return rule.args !== 0;
566
+ }
567
+ return rule.args !== 1;
568
+ }
569
+ }
570
+ function renderMessage(data, error) {
571
+ switch (error.error_identifier) {
572
+ case "RELATION_VIOLATION":
573
+ 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.`;
574
+ case "MISSING_RECORD":
575
+ return `No record was found for ${error.context.operation}.`;
576
+ case "MISSING_RELATED_RECORD": {
577
+ const hint = error.context.neededFor ? ` (needed to ${error.context.neededFor})` : "";
578
+ 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}'.`;
579
+ }
580
+ case "INCOMPLETE_CONNECT_INPUT":
581
+ 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}.`;
582
+ case "RECORDS_NOT_CONNECTED":
583
+ return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
584
+ default:
585
+ assertNever(error, `Unknown error identifier: ${error}`);
586
+ }
587
+ }
588
+ function getErrorCode2(error) {
589
+ switch (error.error_identifier) {
590
+ case "RELATION_VIOLATION":
591
+ return "P2014";
592
+ case "RECORDS_NOT_CONNECTED":
593
+ return "P2017";
594
+ case "MISSING_RECORD":
595
+ case "MISSING_RELATED_RECORD":
596
+ case "INCOMPLETE_CONNECT_INPUT":
597
+ return "P2025";
598
+ default:
599
+ assertNever(error, `Unknown error identifier: ${error}`);
600
+ }
601
+ }
602
+
542
603
  // src/interpreter/QueryInterpreter.ts
543
604
  var QueryInterpreter = class _QueryInterpreter {
544
605
  #transactionManager;
@@ -676,6 +737,11 @@ var QueryInterpreter = class _QueryInterpreter {
676
737
  const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
677
738
  return applyDataMap(data, node.args.structure);
678
739
  }
740
+ case "validate": {
741
+ const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
742
+ performValidation(data, node.args.rules, node.args);
743
+ return data;
744
+ }
679
745
  default:
680
746
  assertNever(node, `Unexpected node type: ${node.type}`);
681
747
  }
@@ -0,0 +1,2 @@
1
+ import { DataRule, ValidationError } from '../QueryPlan';
2
+ export declare function performValidation(data: unknown, rules: DataRule[], error: ValidationError): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/client-engine-runtime",
3
- "version": "6.8.0-dev.11",
3
+ "version": "6.8.0-dev.12",
4
4
  "description": "This package is intended for Prisma's internal use",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -30,8 +30,8 @@
30
30
  "nanoid": "5.1.5",
31
31
  "ulid": "3.0.0",
32
32
  "uuid": "11.1.0",
33
- "@prisma/debug": "6.8.0-dev.11",
34
- "@prisma/driver-adapter-utils": "6.8.0-dev.11"
33
+ "@prisma/debug": "6.8.0-dev.12",
34
+ "@prisma/driver-adapter-utils": "6.8.0-dev.12"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/jest": "29.5.14",