@prisma-next/sql-contract-psl 0.12.0-dev.6 → 0.12.0-dev.60
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 +10 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{interpreter-QE6eZZof.mjs → interpreter-1VmrYYbi.mjs} +373 -23
- package/dist/interpreter-1VmrYYbi.mjs.map +1 -0
- package/dist/provider.d.mts +5 -0
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +5 -3
- package/dist/provider.mjs.map +1 -1
- package/package.json +14 -14
- package/src/interpreter.ts +442 -20
- package/src/provider.ts +10 -1
- package/src/psl-attribute-parsing.ts +66 -0
- package/src/psl-column-resolution.ts +10 -3
- package/src/psl-field-resolution.ts +6 -0
- package/src/psl-relation-resolution.ts +3 -4
- package/dist/interpreter-QE6eZZof.mjs.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContractSourceDiagnostic } from '@prisma-next/config/config-types';
|
|
2
|
+
import type { ControlPolicy } from '@prisma-next/contract/types';
|
|
2
3
|
import type { PslAttribute, PslSpan } from '@prisma-next/psl-parser';
|
|
3
4
|
import { getPositionalArgument, parseQuotedStringLiteral } from '@prisma-next/psl-parser';
|
|
4
5
|
|
|
@@ -411,6 +412,71 @@ export function findDuplicateFieldName(fieldNames: readonly string[]): string |
|
|
|
411
412
|
return undefined;
|
|
412
413
|
}
|
|
413
414
|
|
|
415
|
+
const CONTROL_POLICY_LITERALS = [
|
|
416
|
+
'managed',
|
|
417
|
+
'tolerated',
|
|
418
|
+
'external',
|
|
419
|
+
'observed',
|
|
420
|
+
] as const satisfies readonly ControlPolicy[];
|
|
421
|
+
|
|
422
|
+
const CONTROL_POLICY_LITERAL_SET = new Set<string>(CONTROL_POLICY_LITERALS);
|
|
423
|
+
|
|
424
|
+
function isControlPolicyLiteral(value: string): value is ControlPolicy {
|
|
425
|
+
return CONTROL_POLICY_LITERAL_SET.has(value);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function parseControlPolicyAttribute(input: {
|
|
429
|
+
readonly attribute: PslAttribute;
|
|
430
|
+
readonly sourceId: string;
|
|
431
|
+
readonly diagnostics: ContractSourceDiagnostic[];
|
|
432
|
+
}): ControlPolicy | undefined {
|
|
433
|
+
const namedArgs = input.attribute.args.filter((arg) => arg.kind === 'named');
|
|
434
|
+
if (namedArgs.length > 0) {
|
|
435
|
+
input.diagnostics.push({
|
|
436
|
+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
|
|
437
|
+
message:
|
|
438
|
+
'`@@control` does not accept named arguments; pass the policy positionally as `@@control(external)`.',
|
|
439
|
+
sourceId: input.sourceId,
|
|
440
|
+
span: input.attribute.span,
|
|
441
|
+
});
|
|
442
|
+
return undefined;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const positionalArgs = getPositionalArguments(input.attribute);
|
|
446
|
+
if (positionalArgs.length === 0) {
|
|
447
|
+
input.diagnostics.push({
|
|
448
|
+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
|
|
449
|
+
message:
|
|
450
|
+
'`@@control` requires exactly one positional argument: `managed`, `tolerated`, `external`, or `observed`.',
|
|
451
|
+
sourceId: input.sourceId,
|
|
452
|
+
span: input.attribute.span,
|
|
453
|
+
});
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
if (positionalArgs.length > 1) {
|
|
457
|
+
input.diagnostics.push({
|
|
458
|
+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
|
|
459
|
+
message: `\`@@control\` accepts exactly one positional argument; got ${positionalArgs.length}.`,
|
|
460
|
+
sourceId: input.sourceId,
|
|
461
|
+
span: input.attribute.span,
|
|
462
|
+
});
|
|
463
|
+
return undefined;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const token = unquoteStringLiteral(positionalArgs[0] ?? '').trim();
|
|
467
|
+
if (!isControlPolicyLiteral(token)) {
|
|
468
|
+
input.diagnostics.push({
|
|
469
|
+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
|
|
470
|
+
message: `\`@@control\` argument \`${token}\` is not a known policy. Allowed: \`managed\`, \`tolerated\`, \`external\`, \`observed\`.`,
|
|
471
|
+
sourceId: input.sourceId,
|
|
472
|
+
span: input.attribute.span,
|
|
473
|
+
});
|
|
474
|
+
return undefined;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return token;
|
|
478
|
+
}
|
|
479
|
+
|
|
414
480
|
export function mapFieldNamesToColumns(input: {
|
|
415
481
|
readonly modelName: string;
|
|
416
482
|
readonly fieldNames: readonly string[];
|
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
PslSpan,
|
|
26
26
|
PslTypeConstructorCall,
|
|
27
27
|
} from '@prisma-next/psl-parser';
|
|
28
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
28
29
|
import {
|
|
29
30
|
lowerDefaultFunctionWithRegistry,
|
|
30
31
|
parseDefaultFunctionCall,
|
|
@@ -67,7 +68,9 @@ export function getAuthoringTypeConstructor(
|
|
|
67
68
|
if (typeof current !== 'object' || current === null || Array.isArray(current)) {
|
|
68
69
|
return undefined;
|
|
69
70
|
}
|
|
70
|
-
current =
|
|
71
|
+
current = blindCast<Record<string, unknown>, 'narrowed by preceding typeof/null/array guards'>(
|
|
72
|
+
current,
|
|
73
|
+
)[segment];
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
return isAuthoringTypeConstructorDescriptor(current) ? current : undefined;
|
|
@@ -94,7 +97,9 @@ export function getAuthoringEntity(
|
|
|
94
97
|
if (typeof current !== 'object' || current === null || Array.isArray(current)) {
|
|
95
98
|
return undefined;
|
|
96
99
|
}
|
|
97
|
-
current =
|
|
100
|
+
current = blindCast<Record<string, unknown>, 'narrowed by preceding typeof/null/array guards'>(
|
|
101
|
+
current,
|
|
102
|
+
)[segment];
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
return isAuthoringEntityTypeDescriptor(current) ? current : undefined;
|
|
@@ -115,7 +120,9 @@ export function getAuthoringFieldPreset(
|
|
|
115
120
|
if (typeof current !== 'object' || current === null || Array.isArray(current)) {
|
|
116
121
|
return undefined;
|
|
117
122
|
}
|
|
118
|
-
current =
|
|
123
|
+
current = blindCast<Record<string, unknown>, 'narrowed by preceding typeof/null/array guards'>(
|
|
124
|
+
current,
|
|
125
|
+
)[segment];
|
|
119
126
|
}
|
|
120
127
|
|
|
121
128
|
return isAuthoringFieldPresetDescriptor(current) ? current : undefined;
|
|
@@ -232,6 +232,12 @@ export function collectResolvedFields(input: CollectResolvedFieldsInput): Resolv
|
|
|
232
232
|
if (isModelField && relationAttribute) {
|
|
233
233
|
continue;
|
|
234
234
|
}
|
|
235
|
+
// Cross-contract-space relation fields (e.g. `supabase:auth.User @relation(...)`) are not
|
|
236
|
+
// local model fields, but they carry a @relation attribute and should be skipped here.
|
|
237
|
+
// Their FK and RelationNode lowering is handled separately in the interpreter.
|
|
238
|
+
if (field.typeContractSpaceId !== undefined && relationAttribute) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
235
241
|
|
|
236
242
|
const isValueObjectField = compositeTypeNames.has(field.typeName);
|
|
237
243
|
const isListField = field.list;
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from './psl-attribute-parsing';
|
|
15
15
|
import { checkUncomposedNamespace, reportUncomposedNamespace } from './psl-column-resolution';
|
|
16
16
|
|
|
17
|
-
export const REFERENTIAL_ACTION_MAP = {
|
|
17
|
+
export const REFERENTIAL_ACTION_MAP: Record<string, ReferentialAction | undefined> = {
|
|
18
18
|
NoAction: 'noAction',
|
|
19
19
|
Restrict: 'restrict',
|
|
20
20
|
Cascade: 'cascade',
|
|
@@ -25,7 +25,7 @@ export const REFERENTIAL_ACTION_MAP = {
|
|
|
25
25
|
cascade: 'cascade',
|
|
26
26
|
setNull: 'setNull',
|
|
27
27
|
setDefault: 'setDefault',
|
|
28
|
-
}
|
|
28
|
+
};
|
|
29
29
|
|
|
30
30
|
export type ParsedRelationAttribute = {
|
|
31
31
|
readonly relationName?: string;
|
|
@@ -71,8 +71,7 @@ export function normalizeReferentialAction(input: {
|
|
|
71
71
|
readonly span: PslSpan;
|
|
72
72
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
73
73
|
}): ReferentialAction | undefined {
|
|
74
|
-
const normalized =
|
|
75
|
-
REFERENTIAL_ACTION_MAP[input.actionToken as keyof typeof REFERENTIAL_ACTION_MAP];
|
|
74
|
+
const normalized = REFERENTIAL_ACTION_MAP[input.actionToken];
|
|
76
75
|
if (normalized) {
|
|
77
76
|
return normalized;
|
|
78
77
|
}
|