@prisma-next/sql-contract-psl 0.14.0-dev.5 → 0.14.0-dev.7
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 +11 -7
- package/dist/index.d.mts +7 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{interpreter-B0BsCLKT.mjs → interpreter-kDkm5opL.mjs} +260 -226
- package/dist/interpreter-kDkm5opL.mjs.map +1 -0
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +21 -7
- package/dist/provider.mjs.map +1 -1
- package/package.json +12 -12
- package/src/interpreter.ts +139 -322
- package/src/provider.ts +37 -9
- package/src/psl-attribute-parsing.ts +18 -14
- package/src/psl-authoring-arguments.ts +2 -2
- package/src/psl-column-resolution.ts +17 -15
- package/src/psl-field-resolution.ts +28 -20
- package/src/psl-named-type-resolution.ts +250 -0
- package/src/psl-relation-resolution.ts +5 -4
- package/dist/interpreter-B0BsCLKT.mjs.map +0 -1
package/src/provider.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import type { ContractConfig } from '@prisma-next/config/config-types';
|
|
2
|
+
import type { ContractConfig, ContractSourceDiagnostic } from '@prisma-next/config/config-types';
|
|
3
3
|
import { applySpecifierDefaultControlPolicy } from '@prisma-next/contract/apply-specifier-default-control-policy';
|
|
4
4
|
import type { ControlPolicy } from '@prisma-next/contract/types';
|
|
5
5
|
import type { CodecLookup } from '@prisma-next/framework-components/codec';
|
|
6
6
|
import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';
|
|
7
7
|
import type { Namespace } from '@prisma-next/framework-components/ir';
|
|
8
|
-
import {
|
|
8
|
+
import { buildSymbolTable, rangeToPslSpan } from '@prisma-next/psl-parser';
|
|
9
|
+
import type { ParseDiagnostic, SourceFile } from '@prisma-next/psl-parser/syntax';
|
|
10
|
+
import { parse } from '@prisma-next/psl-parser/syntax';
|
|
9
11
|
import type { SqlNamespaceTablesInput } from '@prisma-next/sql-contract/types';
|
|
10
12
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
11
13
|
import { notOk, ok } from '@prisma-next/utils/result';
|
|
12
14
|
import { basename, extname } from 'pathe';
|
|
15
|
+
|
|
13
16
|
import { interpretPslDocumentToSqlContract } from './interpreter';
|
|
14
17
|
import type { ColumnDescriptor } from './psl-column-resolution';
|
|
15
18
|
|
|
@@ -42,6 +45,19 @@ function defaultOutputFromSchemaPath(schemaPath: string): string {
|
|
|
42
45
|
return `${base}.json`;
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
function mapParseDiagnostics(
|
|
49
|
+
diagnostics: readonly ParseDiagnostic[],
|
|
50
|
+
sourceFile: SourceFile,
|
|
51
|
+
sourceId: string,
|
|
52
|
+
): ContractSourceDiagnostic[] {
|
|
53
|
+
return diagnostics.map((diagnostic) => ({
|
|
54
|
+
code: diagnostic.code,
|
|
55
|
+
message: diagnostic.message,
|
|
56
|
+
sourceId,
|
|
57
|
+
span: rangeToPslSpan(diagnostic.range, sourceFile),
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
|
|
45
61
|
function buildColumnDescriptorMap(
|
|
46
62
|
scalarTypeDescriptors: ReadonlyMap<string, string>,
|
|
47
63
|
codecLookup: CodecLookup,
|
|
@@ -85,19 +101,31 @@ export function prismaContract(schemaPath: string, options: PrismaContractOption
|
|
|
85
101
|
});
|
|
86
102
|
}
|
|
87
103
|
|
|
88
|
-
const document = parsePslDocument({
|
|
89
|
-
schema,
|
|
90
|
-
sourceId: schemaPath,
|
|
91
|
-
pslBlockDescriptors: context.authoringContributions.pslBlockDescriptors,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
104
|
const scalarTypeDescriptors = buildColumnDescriptorMap(
|
|
95
105
|
context.scalarTypeDescriptors,
|
|
96
106
|
context.codecLookup,
|
|
97
107
|
);
|
|
98
108
|
|
|
99
|
-
const
|
|
109
|
+
const { document, sourceFile, diagnostics: parseDiagnostics } = parse(schema);
|
|
110
|
+
const { table: symbolTable, diagnostics: symbolTableDiagnostics } = buildSymbolTable({
|
|
100
111
|
document,
|
|
112
|
+
sourceFile,
|
|
113
|
+
scalarTypes: [...context.scalarTypeDescriptors.keys()],
|
|
114
|
+
pslBlockDescriptors: context.authoringContributions.pslBlockDescriptors,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Do not short-circuit on provider-level diagnostics; recovered CST can
|
|
118
|
+
// still produce interpreter diagnostics in the same response.
|
|
119
|
+
const seedDiagnostics = [
|
|
120
|
+
...mapParseDiagnostics(parseDiagnostics, sourceFile, schemaPath),
|
|
121
|
+
...mapParseDiagnostics(symbolTableDiagnostics, sourceFile, schemaPath),
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
const interpreted = interpretPslDocumentToSqlContract({
|
|
125
|
+
symbolTable,
|
|
126
|
+
sourceFile,
|
|
127
|
+
sourceId: schemaPath,
|
|
128
|
+
seedDiagnostics,
|
|
101
129
|
target: options.target,
|
|
102
130
|
authoringContributions: context.authoringContributions,
|
|
103
131
|
scalarTypeDescriptors,
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { ContractSourceDiagnostic } from '@prisma-next/config/config-types';
|
|
2
2
|
import type { ControlPolicy } from '@prisma-next/contract/types';
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
3
|
+
import type { PslSpan, ResolvedAttribute } from '@prisma-next/psl-parser';
|
|
4
|
+
import { parseQuotedStringLiteral } from '@prisma-next/psl-parser';
|
|
5
5
|
|
|
6
|
-
export {
|
|
6
|
+
export { parseQuotedStringLiteral };
|
|
7
|
+
|
|
8
|
+
export function getPositionalArgument(attribute: ResolvedAttribute, index = 0): string | undefined {
|
|
9
|
+
return attribute.args.filter((arg) => arg.kind === 'positional')[index]?.value;
|
|
10
|
+
}
|
|
7
11
|
|
|
8
12
|
export function lowerFirst(value: string): string {
|
|
9
13
|
if (value.length === 0) return value;
|
|
@@ -11,13 +15,13 @@ export function lowerFirst(value: string): string {
|
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
export function getAttribute(
|
|
14
|
-
attributes: readonly
|
|
18
|
+
attributes: readonly ResolvedAttribute[] | undefined,
|
|
15
19
|
name: string,
|
|
16
|
-
):
|
|
20
|
+
): ResolvedAttribute | undefined {
|
|
17
21
|
return attributes?.find((attribute) => attribute.name === name);
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
export function getNamedArgument(attribute:
|
|
24
|
+
export function getNamedArgument(attribute: ResolvedAttribute, name: string): string | undefined {
|
|
21
25
|
const entry = attribute.args.find((arg) => arg.kind === 'named' && arg.name === name);
|
|
22
26
|
if (!entry || entry.kind !== 'named') {
|
|
23
27
|
return undefined;
|
|
@@ -26,7 +30,7 @@ export function getNamedArgument(attribute: PslAttribute, name: string): string
|
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
export function getPositionalArgumentEntry(
|
|
29
|
-
attribute:
|
|
33
|
+
attribute: ResolvedAttribute,
|
|
30
34
|
index = 0,
|
|
31
35
|
): { value: string; span: PslSpan } | undefined {
|
|
32
36
|
const entries = attribute.args.filter((arg) => arg.kind === 'positional');
|
|
@@ -63,7 +67,7 @@ export function parseFieldList(value: string): readonly string[] | undefined {
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
export function parseMapName(input: {
|
|
66
|
-
readonly attribute:
|
|
70
|
+
readonly attribute: ResolvedAttribute | undefined;
|
|
67
71
|
readonly defaultValue: string;
|
|
68
72
|
readonly sourceId: string;
|
|
69
73
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
@@ -98,7 +102,7 @@ export function parseMapName(input: {
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
export function parseConstraintMapArgument(input: {
|
|
101
|
-
readonly attribute:
|
|
105
|
+
readonly attribute: ResolvedAttribute | undefined;
|
|
102
106
|
readonly sourceId: string;
|
|
103
107
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
104
108
|
readonly entityLabel: string;
|
|
@@ -128,7 +132,7 @@ export function parseConstraintMapArgument(input: {
|
|
|
128
132
|
return undefined;
|
|
129
133
|
}
|
|
130
134
|
|
|
131
|
-
export function getPositionalArguments(attribute:
|
|
135
|
+
export function getPositionalArguments(attribute: ResolvedAttribute): readonly string[] {
|
|
132
136
|
return attribute.args
|
|
133
137
|
.filter((arg) => arg.kind === 'positional')
|
|
134
138
|
.map((arg) => (arg.kind === 'positional' ? arg.value : ''));
|
|
@@ -276,7 +280,7 @@ export function pushInvalidAttributeArgument(input: {
|
|
|
276
280
|
}
|
|
277
281
|
|
|
278
282
|
export function parseOptionalSingleIntegerArgument(input: {
|
|
279
|
-
readonly attribute:
|
|
283
|
+
readonly attribute: ResolvedAttribute;
|
|
280
284
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
281
285
|
readonly sourceId: string;
|
|
282
286
|
readonly entityLabel: string;
|
|
@@ -319,7 +323,7 @@ export function parseOptionalSingleIntegerArgument(input: {
|
|
|
319
323
|
}
|
|
320
324
|
|
|
321
325
|
export function parseOptionalNumericArguments(input: {
|
|
322
|
-
readonly attribute:
|
|
326
|
+
readonly attribute: ResolvedAttribute;
|
|
323
327
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
324
328
|
readonly sourceId: string;
|
|
325
329
|
readonly entityLabel: string;
|
|
@@ -374,7 +378,7 @@ export function parseOptionalNumericArguments(input: {
|
|
|
374
378
|
}
|
|
375
379
|
|
|
376
380
|
export function parseAttributeFieldList(input: {
|
|
377
|
-
readonly attribute:
|
|
381
|
+
readonly attribute: ResolvedAttribute;
|
|
378
382
|
readonly sourceId: string;
|
|
379
383
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
380
384
|
readonly code: string;
|
|
@@ -426,7 +430,7 @@ function isControlPolicyLiteral(value: string): value is ControlPolicy {
|
|
|
426
430
|
}
|
|
427
431
|
|
|
428
432
|
export function parseControlPolicyAttribute(input: {
|
|
429
|
-
readonly attribute:
|
|
433
|
+
readonly attribute: ResolvedAttribute;
|
|
430
434
|
readonly sourceId: string;
|
|
431
435
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
432
436
|
}): ControlPolicy | undefined {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ContractSourceDiagnostic } from '@prisma-next/config/config-types';
|
|
2
2
|
import type { AuthoringArgumentDescriptor } from '@prisma-next/framework-components/authoring';
|
|
3
|
-
import type {
|
|
3
|
+
import type { PslSpan, ResolvedAttributeArg } from '@prisma-next/psl-parser';
|
|
4
4
|
import { unquoteStringLiteral } from './psl-attribute-parsing';
|
|
5
5
|
|
|
6
6
|
const INVALID_AUTHORING_ARGUMENT = Symbol('invalidAuthoringArgument');
|
|
@@ -351,7 +351,7 @@ function pushInvalidPslHelperArgument(input: {
|
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
export function mapPslHelperArgs(input: {
|
|
354
|
-
readonly args: readonly
|
|
354
|
+
readonly args: readonly ResolvedAttributeArg[];
|
|
355
355
|
readonly descriptors: readonly AuthoringArgumentDescriptor[];
|
|
356
356
|
readonly helperLabel: string;
|
|
357
357
|
readonly span: PslSpan;
|
|
@@ -20,12 +20,13 @@ import type {
|
|
|
20
20
|
MutationDefaultGeneratorDescriptor,
|
|
21
21
|
} from '@prisma-next/framework-components/control';
|
|
22
22
|
import type {
|
|
23
|
-
|
|
24
|
-
PslField,
|
|
23
|
+
FieldSymbol,
|
|
25
24
|
PslSpan,
|
|
26
|
-
|
|
25
|
+
ResolvedAttribute,
|
|
26
|
+
ResolvedTypeConstructorCall,
|
|
27
27
|
} from '@prisma-next/psl-parser';
|
|
28
28
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
29
|
+
|
|
29
30
|
import {
|
|
30
31
|
lowerDefaultFunctionWithRegistry,
|
|
31
32
|
parseDefaultFunctionCall,
|
|
@@ -207,7 +208,7 @@ export function reportUnknownFieldPreset(input: {
|
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
export function instantiatePslTypeConstructor(input: {
|
|
210
|
-
readonly call:
|
|
211
|
+
readonly call: ResolvedTypeConstructorCall;
|
|
211
212
|
readonly descriptor: AuthoringTypeConstructorDescriptor;
|
|
212
213
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
213
214
|
readonly sourceId: string;
|
|
@@ -265,7 +266,7 @@ function pushUnsupportedTypeConstructorDiagnostic(input: {
|
|
|
265
266
|
}
|
|
266
267
|
|
|
267
268
|
export function resolvePslTypeConstructorDescriptor(input: {
|
|
268
|
-
readonly call:
|
|
269
|
+
readonly call: ResolvedTypeConstructorCall;
|
|
269
270
|
readonly authoringContributions: AuthoringContributions | undefined;
|
|
270
271
|
readonly composedExtensions: ReadonlySet<string>;
|
|
271
272
|
readonly familyId: string;
|
|
@@ -314,8 +315,8 @@ export function resolvePslTypeConstructorDescriptor(input: {
|
|
|
314
315
|
*
|
|
315
316
|
* Symmetric with `instantiatePslTypeConstructor` but richer: a field preset can contribute `default`, `executionDefaults`, `id`, `unique`, and `nullable` in addition to the storage-type triple. PSL → typed-args coercion happens here (via `mapPslHelperArgs`) so that `instantiateAuthoringFieldPreset` itself stays typed-input-only and TS keeps its zero-runtime-validation cost.
|
|
316
317
|
*/
|
|
317
|
-
export function
|
|
318
|
-
readonly call:
|
|
318
|
+
export function instantiateFieldPreset(input: {
|
|
319
|
+
readonly call: ResolvedTypeConstructorCall;
|
|
319
320
|
readonly descriptor: AuthoringFieldPresetDescriptor;
|
|
320
321
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
321
322
|
readonly sourceId: string;
|
|
@@ -395,7 +396,7 @@ export type ResolveFieldTypeResult =
|
|
|
395
396
|
| { readonly ok: false; readonly alreadyReported: boolean };
|
|
396
397
|
|
|
397
398
|
export function resolveFieldTypeDescriptor(input: {
|
|
398
|
-
readonly field:
|
|
399
|
+
readonly field: FieldSymbol;
|
|
399
400
|
readonly enumTypeDescriptors: ReadonlyMap<string, ColumnDescriptor>;
|
|
400
401
|
readonly namedTypeDescriptors: ReadonlyMap<string, ColumnDescriptor>;
|
|
401
402
|
readonly scalarTypeDescriptors: ReadonlyMap<string, ColumnDescriptor>;
|
|
@@ -407,6 +408,10 @@ export function resolveFieldTypeDescriptor(input: {
|
|
|
407
408
|
readonly sourceId: string;
|
|
408
409
|
readonly entityLabel: string;
|
|
409
410
|
}): ResolveFieldTypeResult {
|
|
411
|
+
// Avoid cascading unsupported-type diagnostics after invalid qualification.
|
|
412
|
+
if (input.field.malformedType) {
|
|
413
|
+
return { ok: false, alreadyReported: true };
|
|
414
|
+
}
|
|
410
415
|
if (input.field.typeConstructor) {
|
|
411
416
|
// Field presets carry richer semantics than type constructors, so a field preset match is the complete answer. Shared composition rejects exact cross-registry collisions before PSL resolution can observe them.
|
|
412
417
|
const presetDescriptor = getAuthoringFieldPreset(
|
|
@@ -414,7 +419,7 @@ export function resolveFieldTypeDescriptor(input: {
|
|
|
414
419
|
input.field.typeConstructor.path,
|
|
415
420
|
);
|
|
416
421
|
if (presetDescriptor) {
|
|
417
|
-
const instantiated =
|
|
422
|
+
const instantiated = instantiateFieldPreset({
|
|
418
423
|
call: input.field.typeConstructor,
|
|
419
424
|
descriptor: presetDescriptor,
|
|
420
425
|
diagnostics: input.diagnostics,
|
|
@@ -588,7 +593,7 @@ export const NATIVE_TYPE_SPECS: Readonly<Record<string, NativeTypeSpec>> = {
|
|
|
588
593
|
};
|
|
589
594
|
|
|
590
595
|
export function resolveDbNativeTypeAttribute(input: {
|
|
591
|
-
readonly attribute:
|
|
596
|
+
readonly attribute: ResolvedAttribute;
|
|
592
597
|
readonly baseType: string;
|
|
593
598
|
readonly baseDescriptor: ColumnDescriptor;
|
|
594
599
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
@@ -703,7 +708,7 @@ export function parseDefaultLiteralValue(expression: string): ColumnDefault | un
|
|
|
703
708
|
export function lowerDefaultForField(input: {
|
|
704
709
|
readonly modelName: string;
|
|
705
710
|
readonly fieldName: string;
|
|
706
|
-
readonly defaultAttribute:
|
|
711
|
+
readonly defaultAttribute: ResolvedAttribute;
|
|
707
712
|
readonly columnDescriptor: ColumnDescriptor;
|
|
708
713
|
readonly generatorDescriptorById: ReadonlyMap<string, MutationDefaultGeneratorDescriptor>;
|
|
709
714
|
readonly sourceId: string;
|
|
@@ -809,14 +814,11 @@ export function lowerDefaultForField(input: {
|
|
|
809
814
|
}
|
|
810
815
|
|
|
811
816
|
export function resolveColumnDescriptor(
|
|
812
|
-
field:
|
|
817
|
+
field: FieldSymbol,
|
|
813
818
|
enumTypeDescriptors: ReadonlyMap<string, ColumnDescriptor>,
|
|
814
819
|
namedTypeDescriptors: ReadonlyMap<string, ColumnDescriptor>,
|
|
815
820
|
scalarTypeDescriptors: ReadonlyMap<string, ColumnDescriptor>,
|
|
816
821
|
): ColumnDescriptor | undefined {
|
|
817
|
-
if (field.typeRef && namedTypeDescriptors.has(field.typeRef)) {
|
|
818
|
-
return namedTypeDescriptors.get(field.typeRef);
|
|
819
|
-
}
|
|
820
822
|
if (namedTypeDescriptors.has(field.typeName)) {
|
|
821
823
|
return namedTypeDescriptors.get(field.typeName);
|
|
822
824
|
}
|
|
@@ -9,13 +9,12 @@ import type {
|
|
|
9
9
|
ControlMutationDefaultRegistry,
|
|
10
10
|
MutationDefaultGeneratorDescriptor,
|
|
11
11
|
} from '@prisma-next/framework-components/control';
|
|
12
|
-
import type {
|
|
12
|
+
import type { FieldSymbol, ModelSymbol, ResolvedAttribute } from '@prisma-next/psl-parser';
|
|
13
13
|
import type { EnumTypeHandle } from '@prisma-next/sql-contract-ts/contract-builder';
|
|
14
14
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
15
15
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
16
16
|
import {
|
|
17
17
|
getAttribute,
|
|
18
|
-
getPositionalArgumentEntry,
|
|
19
18
|
lowerFirst,
|
|
20
19
|
parseConstraintMapArgument,
|
|
21
20
|
parseMapName,
|
|
@@ -36,13 +35,21 @@ type LoweredFieldDefault = {
|
|
|
36
35
|
function lowerEnumDefaultForField(input: {
|
|
37
36
|
readonly modelName: string;
|
|
38
37
|
readonly fieldName: string;
|
|
39
|
-
readonly defaultAttribute:
|
|
38
|
+
readonly defaultAttribute: ResolvedAttribute;
|
|
40
39
|
readonly enumHandle: EnumTypeHandle;
|
|
41
40
|
readonly sourceId: string;
|
|
42
41
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
43
42
|
}): LoweredFieldDefault {
|
|
44
|
-
const
|
|
45
|
-
|
|
43
|
+
const positionalEntries = input.defaultAttribute.args.filter((arg) => arg.kind === 'positional');
|
|
44
|
+
const hasNamedEntries = input.defaultAttribute.args.some((arg) => arg.kind === 'named');
|
|
45
|
+
const expressionEntry = positionalEntries[0];
|
|
46
|
+
if (hasNamedEntries || positionalEntries.length !== 1 || expressionEntry === undefined) {
|
|
47
|
+
input.diagnostics.push({
|
|
48
|
+
code: 'PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT',
|
|
49
|
+
message: `Field "${input.modelName}.${input.fieldName}" @default on an enum field expects exactly one positional enum member argument.`,
|
|
50
|
+
sourceId: input.sourceId,
|
|
51
|
+
span: input.defaultAttribute.span,
|
|
52
|
+
});
|
|
46
53
|
return {};
|
|
47
54
|
}
|
|
48
55
|
|
|
@@ -84,9 +91,10 @@ function lowerEnumDefaultForField(input: {
|
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
export type ResolvedField = {
|
|
87
|
-
readonly field:
|
|
94
|
+
readonly field: FieldSymbol;
|
|
88
95
|
readonly columnName: string;
|
|
89
96
|
readonly descriptor: ColumnDescriptor;
|
|
97
|
+
readonly nullable: boolean;
|
|
90
98
|
readonly defaultValue?: ColumnDefault;
|
|
91
99
|
readonly executionDefaults?: ExecutionMutationDefaultPhases;
|
|
92
100
|
readonly isId: boolean;
|
|
@@ -99,7 +107,7 @@ export type ResolvedField = {
|
|
|
99
107
|
};
|
|
100
108
|
|
|
101
109
|
export type ModelNameMapping = {
|
|
102
|
-
readonly model:
|
|
110
|
+
readonly model: ModelSymbol;
|
|
103
111
|
readonly tableName: string;
|
|
104
112
|
readonly fieldColumns: Map<string, string>;
|
|
105
113
|
};
|
|
@@ -112,7 +120,7 @@ export type ModelNameMapping = {
|
|
|
112
120
|
* {@link modelCoordinateKey} rather than the bare model name.
|
|
113
121
|
*/
|
|
114
122
|
export type ModelNamespaceEntry = {
|
|
115
|
-
readonly model:
|
|
123
|
+
readonly model: ModelSymbol;
|
|
116
124
|
readonly namespaceId: string | undefined;
|
|
117
125
|
};
|
|
118
126
|
|
|
@@ -123,7 +131,7 @@ export function modelCoordinateKey(namespaceId: string, modelName: string): stri
|
|
|
123
131
|
}
|
|
124
132
|
|
|
125
133
|
export interface CollectResolvedFieldsInput {
|
|
126
|
-
readonly model:
|
|
134
|
+
readonly model: ModelSymbol;
|
|
127
135
|
readonly mapping: ModelNameMapping;
|
|
128
136
|
readonly enumTypeDescriptors: Map<string, ColumnDescriptor>;
|
|
129
137
|
readonly namedTypeDescriptors: Map<string, ColumnDescriptor>;
|
|
@@ -163,7 +171,7 @@ const BUILTIN_FIELD_ATTRIBUTE_NAMES: ReadonlySet<string> = new Set([
|
|
|
163
171
|
*/
|
|
164
172
|
interface RemovedAttributeRule {
|
|
165
173
|
readonly hint: string;
|
|
166
|
-
readonly suppressWhen: (field:
|
|
174
|
+
readonly suppressWhen: (field: FieldSymbol) => boolean;
|
|
167
175
|
}
|
|
168
176
|
|
|
169
177
|
const REMOVED_ATTRIBUTE_RULES: ReadonlyMap<string, RemovedAttributeRule> = new Map([
|
|
@@ -193,8 +201,8 @@ const REMOVED_ATTRIBUTE_RULES: ReadonlyMap<string, RemovedAttributeRule> = new M
|
|
|
193
201
|
}
|
|
194
202
|
|
|
195
203
|
function validateFieldAttributes(input: {
|
|
196
|
-
readonly model:
|
|
197
|
-
readonly field:
|
|
204
|
+
readonly model: ModelSymbol;
|
|
205
|
+
readonly field: FieldSymbol;
|
|
198
206
|
readonly composedExtensions: ReadonlySet<string>;
|
|
199
207
|
readonly authoringContributions: AuthoringContributions | undefined;
|
|
200
208
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
@@ -240,13 +248,13 @@ function validateFieldAttributes(input: {
|
|
|
240
248
|
}
|
|
241
249
|
|
|
242
250
|
function extractFieldConstraintNames(input: {
|
|
243
|
-
readonly model:
|
|
244
|
-
readonly field:
|
|
251
|
+
readonly model: ModelSymbol;
|
|
252
|
+
readonly field: FieldSymbol;
|
|
245
253
|
readonly sourceId: string;
|
|
246
254
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
247
255
|
}): {
|
|
248
|
-
readonly idAttribute:
|
|
249
|
-
readonly uniqueAttribute:
|
|
256
|
+
readonly idAttribute: ResolvedAttribute | undefined;
|
|
257
|
+
readonly uniqueAttribute: ResolvedAttribute | undefined;
|
|
250
258
|
readonly idName: string | undefined;
|
|
251
259
|
readonly uniqueName: string | undefined;
|
|
252
260
|
} {
|
|
@@ -292,7 +300,7 @@ export function collectResolvedFields(input: CollectResolvedFieldsInput): Resolv
|
|
|
292
300
|
} = input;
|
|
293
301
|
const resolvedFields: ResolvedField[] = [];
|
|
294
302
|
|
|
295
|
-
for (const field of model.fields) {
|
|
303
|
+
for (const field of Object.values(model.fields)) {
|
|
296
304
|
const isModelField = modelNames.has(field.typeName);
|
|
297
305
|
|
|
298
306
|
if (field.list && isModelField) {
|
|
@@ -448,8 +456,7 @@ export function collectResolvedFields(input: CollectResolvedFieldsInput): Resolv
|
|
|
448
456
|
});
|
|
449
457
|
continue;
|
|
450
458
|
}
|
|
451
|
-
const fieldUsesNamedType =
|
|
452
|
-
field.typeRef !== undefined || namedTypeDescriptors.has(field.typeName);
|
|
459
|
+
const fieldUsesNamedType = namedTypeDescriptors.has(field.typeName);
|
|
453
460
|
if (loweredOnCreate && !fieldUsesNamedType) {
|
|
454
461
|
const generatorDescriptor = generatorDescriptorById.get(loweredOnCreate.id);
|
|
455
462
|
const generatedDescriptor = generatorDescriptor?.resolveGeneratedColumnDescriptor?.({
|
|
@@ -502,6 +509,7 @@ export function collectResolvedFields(input: CollectResolvedFieldsInput): Resolv
|
|
|
502
509
|
field,
|
|
503
510
|
columnName: mappedColumnName,
|
|
504
511
|
descriptor,
|
|
512
|
+
nullable: presetContributions?.nullable ?? field.optional,
|
|
505
513
|
...ifDefined('defaultValue', fieldDefaultValue),
|
|
506
514
|
...ifDefined('executionDefaults', fieldExecutionDefaults),
|
|
507
515
|
isId: isIdField || Boolean(presetContributions?.id),
|
|
@@ -535,7 +543,7 @@ export function buildModelMappings(
|
|
|
535
543
|
span: model.span,
|
|
536
544
|
});
|
|
537
545
|
const fieldColumns = new Map<string, string>();
|
|
538
|
-
for (const field of model.fields) {
|
|
546
|
+
for (const field of Object.values(model.fields)) {
|
|
539
547
|
const fieldMapAttribute = getAttribute(field.attributes, 'map');
|
|
540
548
|
const columnName = parseMapName({
|
|
541
549
|
attribute: fieldMapAttribute,
|