@prisma-next/sql-contract-psl 0.13.0 → 0.14.0-dev.2

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.
@@ -1,14 +1,21 @@
1
1
  import type { ContractSourceDiagnostic } from '@prisma-next/config/config-types';
2
- import type { ColumnDefault, ExecutionMutationDefaultPhases } from '@prisma-next/contract/types';
2
+ import type {
3
+ ColumnDefault,
4
+ ColumnDefaultLiteralInputValue,
5
+ ExecutionMutationDefaultPhases,
6
+ } from '@prisma-next/contract/types';
3
7
  import type { AuthoringContributions } from '@prisma-next/framework-components/authoring';
4
8
  import type {
5
9
  ControlMutationDefaultRegistry,
6
10
  MutationDefaultGeneratorDescriptor,
7
11
  } from '@prisma-next/framework-components/control';
8
12
  import type { PslAttribute, PslField, PslModel } from '@prisma-next/psl-parser';
13
+ import type { EnumTypeHandle } from '@prisma-next/sql-contract-ts/contract-builder';
14
+ import { blindCast } from '@prisma-next/utils/casts';
9
15
  import { ifDefined } from '@prisma-next/utils/defined';
10
16
  import {
11
17
  getAttribute,
18
+ getPositionalArgumentEntry,
12
19
  lowerFirst,
13
20
  parseConstraintMapArgument,
14
21
  parseMapName,
@@ -21,6 +28,61 @@ import {
21
28
  resolveFieldTypeDescriptor,
22
29
  } from './psl-column-resolution';
23
30
 
31
+ type LoweredFieldDefault = {
32
+ readonly defaultValue?: ColumnDefault;
33
+ readonly executionDefaults?: ExecutionMutationDefaultPhases;
34
+ };
35
+
36
+ function lowerEnumDefaultForField(input: {
37
+ readonly modelName: string;
38
+ readonly fieldName: string;
39
+ readonly defaultAttribute: PslAttribute;
40
+ readonly enumHandle: EnumTypeHandle;
41
+ readonly sourceId: string;
42
+ readonly diagnostics: ContractSourceDiagnostic[];
43
+ }): LoweredFieldDefault {
44
+ const expressionEntry = getPositionalArgumentEntry(input.defaultAttribute);
45
+ if (!expressionEntry) {
46
+ return {};
47
+ }
48
+
49
+ const raw = expressionEntry.value.trim();
50
+ const isQuotedString = /^(['"]).*\1$/.test(raw);
51
+ const isFunctionCall = raw.includes('(') && raw.endsWith(')');
52
+
53
+ if (isQuotedString || isFunctionCall) {
54
+ input.diagnostics.push({
55
+ code: 'PSL_ENUM_DEFAULT_MUST_BE_MEMBER_NAME',
56
+ message: `Field "${input.modelName}.${input.fieldName}" @default on an enum field must name a member (e.g. @default(Low)), not a raw value or function.`,
57
+ sourceId: input.sourceId,
58
+ span: input.defaultAttribute.span,
59
+ });
60
+ return {};
61
+ }
62
+
63
+ const match = input.enumHandle.enumMembers.find((m) => m.name === raw);
64
+ if (!match) {
65
+ const validNames = input.enumHandle.enumMembers.map((m) => m.name).join(', ');
66
+ input.diagnostics.push({
67
+ code: 'PSL_ENUM_UNKNOWN_DEFAULT_MEMBER',
68
+ message: `Field "${input.modelName}.${input.fieldName}" @default(${raw}) does not name a member of ${input.enumHandle.enumName}. Valid members: ${validNames}.`,
69
+ sourceId: input.sourceId,
70
+ span: input.defaultAttribute.span,
71
+ });
72
+ return {};
73
+ }
74
+
75
+ return {
76
+ defaultValue: {
77
+ kind: 'literal',
78
+ value: blindCast<
79
+ ColumnDefaultLiteralInputValue,
80
+ 'enum member values are codec-validated JsonValue-compatible scalars'
81
+ >(match.value),
82
+ },
83
+ };
84
+ }
85
+
24
86
  export type ResolvedField = {
25
87
  readonly field: PslField;
26
88
  readonly columnName: string;
@@ -76,6 +138,7 @@ export interface CollectResolvedFieldsInput {
76
138
  readonly diagnostics: ContractSourceDiagnostic[];
77
139
  readonly sourceId: string;
78
140
  readonly scalarTypeDescriptors: ReadonlyMap<string, ColumnDescriptor>;
141
+ readonly enumHandles?: ReadonlyMap<string, EnumTypeHandle>;
79
142
  }
80
143
 
81
144
  const BUILTIN_FIELD_ATTRIBUTE_NAMES: ReadonlySet<string> = new Set([
@@ -95,7 +158,7 @@ const BUILTIN_FIELD_ATTRIBUTE_NAMES: ReadonlySet<string> = new Set([
95
158
  * migrated (so they don't get told to do what they just did).
96
159
  *
97
160
  * Pairing the suppression predicate with the hint makes each entry
98
- * self-contained: a future entry for, say, `@id` ↔ `id.uuidv7()` cannot
161
+ * self-contained: a future entry for, say, `@id` ↔ `id.uuidv7String()` cannot
99
162
  * silently inherit the wrong predicate when added.
100
163
  */
101
164
  interface RemovedAttributeRule {
@@ -225,6 +288,7 @@ export function collectResolvedFields(input: CollectResolvedFieldsInput): Resolv
225
288
  diagnostics,
226
289
  sourceId,
227
290
  scalarTypeDescriptors,
291
+ enumHandles,
228
292
  } = input;
229
293
  const resolvedFields: ResolvedField[] = [];
230
294
 
@@ -350,17 +414,27 @@ export function collectResolvedFields(input: CollectResolvedFieldsInput): Resolv
350
414
  });
351
415
  continue;
352
416
  }
353
- const loweredDefault = defaultAttribute
354
- ? lowerDefaultForField({
355
- modelName: model.name,
356
- fieldName: field.name,
357
- defaultAttribute,
358
- columnDescriptor: descriptor,
359
- generatorDescriptorById,
360
- sourceId,
361
- defaultFunctionRegistry,
362
- diagnostics,
363
- })
417
+ const enumHandle = enumHandles?.get(field.typeName);
418
+ const loweredDefault: LoweredFieldDefault = defaultAttribute
419
+ ? enumHandle
420
+ ? lowerEnumDefaultForField({
421
+ modelName: model.name,
422
+ fieldName: field.name,
423
+ defaultAttribute,
424
+ enumHandle,
425
+ sourceId,
426
+ diagnostics,
427
+ })
428
+ : lowerDefaultForField({
429
+ modelName: model.name,
430
+ fieldName: field.name,
431
+ defaultAttribute,
432
+ columnDescriptor: descriptor,
433
+ generatorDescriptorById,
434
+ sourceId,
435
+ defaultFunctionRegistry,
436
+ diagnostics,
437
+ })
364
438
  : {};
365
439
  const loweredOnCreate = loweredDefault.executionDefaults?.onCreate;
366
440
  if (field.optional && loweredOnCreate) {
@@ -374,7 +448,9 @@ export function collectResolvedFields(input: CollectResolvedFieldsInput): Resolv
374
448
  });
375
449
  continue;
376
450
  }
377
- if (loweredOnCreate) {
451
+ const fieldUsesNamedType =
452
+ field.typeRef !== undefined || namedTypeDescriptors.has(field.typeName);
453
+ if (loweredOnCreate && !fieldUsesNamedType) {
378
454
  const generatorDescriptor = generatorDescriptorById.get(loweredOnCreate.id);
379
455
  const generatedDescriptor = generatorDescriptor?.resolveGeneratedColumnDescriptor?.({
380
456
  generated: loweredOnCreate,