@prisma/client-engine-runtime 6.8.0-dev.3 → 6.8.0-dev.31
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/dist/QueryPlan.d.ts +47 -0
- package/dist/index.d.mts +49 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.js +67 -4
- package/dist/index.mjs +67 -4
- package/dist/interpreter/validation.d.ts +2 -0
- package/package.json +3 -3
package/dist/QueryPlan.d.ts
CHANGED
|
@@ -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 "
|
|
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 "
|
|
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
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/client-engine-runtime",
|
|
3
|
-
"version": "6.8.0-dev.
|
|
3
|
+
"version": "6.8.0-dev.31",
|
|
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/
|
|
34
|
-
"@prisma/
|
|
33
|
+
"@prisma/driver-adapter-utils": "6.8.0-dev.31",
|
|
34
|
+
"@prisma/debug": "6.8.0-dev.31"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/jest": "29.5.14",
|