@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/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 { parsePslDocument } from '@prisma-next/psl-parser';
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 interpreted = interpretPslDocumentToSqlContract({
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 { PslAttribute, PslSpan } from '@prisma-next/psl-parser';
4
- import { getPositionalArgument, parseQuotedStringLiteral } from '@prisma-next/psl-parser';
3
+ import type { PslSpan, ResolvedAttribute } from '@prisma-next/psl-parser';
4
+ import { parseQuotedStringLiteral } from '@prisma-next/psl-parser';
5
5
 
6
- export { getPositionalArgument, parseQuotedStringLiteral };
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 PslAttribute[] | undefined,
18
+ attributes: readonly ResolvedAttribute[] | undefined,
15
19
  name: string,
16
- ): PslAttribute | undefined {
20
+ ): ResolvedAttribute | undefined {
17
21
  return attributes?.find((attribute) => attribute.name === name);
18
22
  }
19
23
 
20
- export function getNamedArgument(attribute: PslAttribute, name: string): string | undefined {
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: PslAttribute,
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: PslAttribute | undefined;
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: PslAttribute | undefined;
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: PslAttribute): readonly string[] {
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: PslAttribute;
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: PslAttribute;
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: PslAttribute;
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: PslAttribute;
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 { PslAttributeArgument, PslSpan } from '@prisma-next/psl-parser';
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 PslAttributeArgument[];
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
- PslAttribute,
24
- PslField,
23
+ FieldSymbol,
25
24
  PslSpan,
26
- PslTypeConstructorCall,
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: PslTypeConstructorCall;
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: PslTypeConstructorCall;
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 instantiatePslFieldPreset(input: {
318
- readonly call: PslTypeConstructorCall;
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: PslField;
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 = instantiatePslFieldPreset({
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: PslAttribute;
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: PslAttribute;
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: PslField,
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 { PslAttribute, PslField, PslModel } from '@prisma-next/psl-parser';
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: PslAttribute;
38
+ readonly defaultAttribute: ResolvedAttribute;
40
39
  readonly enumHandle: EnumTypeHandle;
41
40
  readonly sourceId: string;
42
41
  readonly diagnostics: ContractSourceDiagnostic[];
43
42
  }): LoweredFieldDefault {
44
- const expressionEntry = getPositionalArgumentEntry(input.defaultAttribute);
45
- if (!expressionEntry) {
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: PslField;
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: PslModel;
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: PslModel;
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: PslModel;
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: PslField) => boolean;
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: PslModel;
197
- readonly field: PslField;
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: PslModel;
244
- readonly field: PslField;
251
+ readonly model: ModelSymbol;
252
+ readonly field: FieldSymbol;
245
253
  readonly sourceId: string;
246
254
  readonly diagnostics: ContractSourceDiagnostic[];
247
255
  }): {
248
- readonly idAttribute: PslAttribute | undefined;
249
- readonly uniqueAttribute: PslAttribute | undefined;
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,