@prisma/client-engine-runtime 6.8.0-dev.3 → 6.8.0-dev.30

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
@@ -518,9 +518,6 @@ function renderRawSql(sql, params) {
518
518
  };
519
519
  }
520
520
  function toArgType(value) {
521
- if (value === null) {
522
- return "Int32";
523
- }
524
521
  if (typeof value === "string") {
525
522
  return "Text";
526
523
  }
@@ -536,7 +533,7 @@ function toArgType(value) {
536
533
  if (isPrismaValuePlaceholder(value)) {
537
534
  return placeholderTypeToArgType(value.prisma__value.type);
538
535
  }
539
- return "Json";
536
+ return "Unknown";
540
537
  }
541
538
  function placeholderTypeToArgType(type) {
542
539
  const typeMap = {
@@ -584,6 +581,67 @@ function serializeSql(resultSet) {
584
581
  );
585
582
  }
586
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
+
587
645
  // src/interpreter/QueryInterpreter.ts
588
646
  var QueryInterpreter = class _QueryInterpreter {
589
647
  #transactionManager;
@@ -721,6 +779,11 @@ var QueryInterpreter = class _QueryInterpreter {
721
779
  const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
722
780
  return applyDataMap(data, node.args.structure);
723
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
+ }
724
787
  default:
725
788
  assertNever(node, `Unexpected node type: ${node.type}`);
726
789
  }
package/dist/index.mjs CHANGED
@@ -476,9 +476,6 @@ function renderRawSql(sql, params) {
476
476
  };
477
477
  }
478
478
  function toArgType(value) {
479
- if (value === null) {
480
- return "Int32";
481
- }
482
479
  if (typeof value === "string") {
483
480
  return "Text";
484
481
  }
@@ -494,7 +491,7 @@ function toArgType(value) {
494
491
  if (isPrismaValuePlaceholder(value)) {
495
492
  return placeholderTypeToArgType(value.prisma__value.type);
496
493
  }
497
- return "Json";
494
+ return "Unknown";
498
495
  }
499
496
  function placeholderTypeToArgType(type) {
500
497
  const typeMap = {
@@ -542,6 +539,67 @@ function serializeSql(resultSet) {
542
539
  );
543
540
  }
544
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
+
545
603
  // src/interpreter/QueryInterpreter.ts
546
604
  var QueryInterpreter = class _QueryInterpreter {
547
605
  #transactionManager;
@@ -679,6 +737,11 @@ var QueryInterpreter = class _QueryInterpreter {
679
737
  const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
680
738
  return applyDataMap(data, node.args.structure);
681
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
+ }
682
745
  default:
683
746
  assertNever(node, `Unexpected node type: ${node.type}`);
684
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.3",
3
+ "version": "6.8.0-dev.30",
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.3",
34
- "@prisma/driver-adapter-utils": "6.8.0-dev.3"
33
+ "@prisma/debug": "6.8.0-dev.30",
34
+ "@prisma/driver-adapter-utils": "6.8.0-dev.30"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/jest": "29.5.14",