@prisma-next/sql-contract-psl 0.8.0 → 0.9.0-dev.1

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.
@@ -11,9 +11,10 @@ import type {
11
11
  } from '@prisma-next/contract/types';
12
12
  import type {
13
13
  AuthoringContributions,
14
- AuthoringTypeConstructorDescriptor,
14
+ AuthoringEntityContext,
15
+ AuthoringEntityTypeDescriptor,
15
16
  } from '@prisma-next/framework-components/authoring';
16
- import { instantiateAuthoringTypeConstructor } from '@prisma-next/framework-components/authoring';
17
+ import { instantiateAuthoringEntityType } from '@prisma-next/framework-components/authoring';
17
18
  import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';
18
19
  import type {
19
20
  ControlMutationDefaultRegistry,
@@ -29,7 +30,11 @@ import type {
29
30
  PslModel,
30
31
  PslNamedTypeDeclaration,
31
32
  } from '@prisma-next/psl-parser';
32
- import type { StorageTypeInstance } from '@prisma-next/sql-contract/types';
33
+ import {
34
+ isPostgresEnumStorageEntry,
35
+ type PostgresEnumStorageEntry,
36
+ type StorageTypeInstance,
37
+ } from '@prisma-next/sql-contract/types';
33
38
  import {
34
39
  buildSqlContractFromDefinition,
35
40
  type ForeignKeyNode,
@@ -55,7 +60,7 @@ import {
55
60
  import type { ColumnDescriptor } from './psl-column-resolution';
56
61
  import {
57
62
  checkUncomposedNamespace,
58
- getAuthoringTypeConstructor,
63
+ getAuthoringEntity,
59
64
  instantiatePslTypeConstructor,
60
65
  reportUncomposedNamespace,
61
66
  resolveDbNativeTypeAttribute,
@@ -158,17 +163,40 @@ function mapParserDiagnostics(document: ParsePslDocumentResult): ContractSourceD
158
163
  interface ProcessEnumDeclarationsInput {
159
164
  readonly enums: readonly PslEnum[];
160
165
  readonly sourceId: string;
161
- readonly enumTypeConstructor: AuthoringTypeConstructorDescriptor | undefined;
166
+ readonly enumEntityDescriptor: AuthoringEntityTypeDescriptor | undefined;
167
+ readonly entityContext: AuthoringEntityContext;
162
168
  readonly diagnostics: ContractSourceDiagnostic[];
163
169
  }
164
170
 
165
171
  function processEnumDeclarations(input: ProcessEnumDeclarationsInput): {
166
- readonly storageTypes: Record<string, StorageTypeInstance>;
172
+ readonly storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry>;
167
173
  readonly enumTypeDescriptors: Map<string, ColumnDescriptor>;
168
174
  } {
169
- const storageTypes: Record<string, StorageTypeInstance> = {};
175
+ const storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {};
170
176
  const enumTypeDescriptors = new Map<string, ColumnDescriptor>();
171
177
 
178
+ if (input.enums.length === 0) {
179
+ return { storageTypes, enumTypeDescriptors };
180
+ }
181
+
182
+ if (!input.enumEntityDescriptor) {
183
+ // The PSL `enum X { … }` syntax only resolves when the active
184
+ // pack composition contributes an `enum` entity-type factory (the
185
+ // Postgres target pack does so today via
186
+ // `authoring.entityTypes.enum`). Without the contribution we
187
+ // surface a diagnostic per declaration rather than silently
188
+ // swallowing the syntax.
189
+ for (const enumDeclaration of input.enums) {
190
+ input.diagnostics.push({
191
+ code: 'PSL_UNSUPPORTED_NAMED_TYPE_BASE',
192
+ message: `Enum "${enumDeclaration.name}" requires the active target pack to contribute an enum entity-type helper`,
193
+ sourceId: input.sourceId,
194
+ span: enumDeclaration.span,
195
+ });
196
+ }
197
+ return { storageTypes, enumTypeDescriptors };
198
+ }
199
+
172
200
  for (const enumDeclaration of input.enums) {
173
201
  const nativeType = parseMapName({
174
202
  attribute: getAttribute(enumDeclaration.attributes, 'map'),
@@ -178,29 +206,29 @@ function processEnumDeclarations(input: ProcessEnumDeclarationsInput): {
178
206
  entityLabel: `Enum "${enumDeclaration.name}"`,
179
207
  span: enumDeclaration.span,
180
208
  });
181
- const enumStorageType = input.enumTypeConstructor
182
- ? instantiateAuthoringTypeConstructor(input.enumTypeConstructor, [
183
- nativeType,
184
- enumDeclaration.values.map((value) => value.name),
185
- ])
186
- : {
187
- codecId: 'pg/enum@1',
188
- nativeType,
189
- typeParams: { values: enumDeclaration.values.map((value) => value.name) },
190
- };
209
+ const values = enumDeclaration.values.map((value) => value.name);
210
+ const constructed = instantiateAuthoringEntityType(
211
+ 'enum',
212
+ input.enumEntityDescriptor,
213
+ [{ name: enumDeclaration.name, nativeType, values }],
214
+ input.entityContext,
215
+ );
216
+ if (!isPostgresEnumStorageEntry(constructed)) {
217
+ input.diagnostics.push({
218
+ code: 'PSL_UNSUPPORTED_NAMED_TYPE_BASE',
219
+ message: `Enum "${enumDeclaration.name}": enum entity-type factory must return a PostgresEnumStorageEntry-shaped value (kind: 'postgres-enum')`,
220
+ sourceId: input.sourceId,
221
+ span: enumDeclaration.span,
222
+ });
223
+ continue;
224
+ }
191
225
  const descriptor: ColumnDescriptor = {
192
- codecId: enumStorageType.codecId,
193
- nativeType: enumStorageType.nativeType,
226
+ codecId: constructed.codecId,
227
+ nativeType: constructed.nativeType,
194
228
  typeRef: enumDeclaration.name,
195
229
  };
196
230
  enumTypeDescriptors.set(enumDeclaration.name, descriptor);
197
- storageTypes[enumDeclaration.name] = {
198
- codecId: enumStorageType.codecId,
199
- nativeType: enumStorageType.nativeType,
200
- typeParams: enumStorageType.typeParams ?? {
201
- values: enumDeclaration.values.map((value) => value.name),
202
- },
203
- };
231
+ storageTypes[enumDeclaration.name] = constructed;
204
232
  }
205
233
 
206
234
  return { storageTypes, enumTypeDescriptors };
@@ -282,10 +310,10 @@ function validateNamedTypeAttributes(input: {
282
310
  }
283
311
 
284
312
  function resolveNamedTypeDeclarations(input: ResolveNamedTypeDeclarationsInput): {
285
- readonly storageTypes: Record<string, StorageTypeInstance>;
313
+ readonly storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry>;
286
314
  readonly namedTypeDescriptors: Map<string, ColumnDescriptor>;
287
315
  } {
288
- const storageTypes: Record<string, StorageTypeInstance> = {};
316
+ const storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {};
289
317
  const namedTypeDescriptors = new Map<string, ColumnDescriptor>();
290
318
 
291
319
  for (const declaration of input.declarations) {
@@ -336,6 +364,7 @@ function resolveNamedTypeDeclarations(input: ResolveNamedTypeDeclarationsInput):
336
364
  toNamedTypeFieldDescriptor(declaration.name, storageType),
337
365
  );
338
366
  storageTypes[declaration.name] = {
367
+ kind: 'codec-instance',
339
368
  codecId: storageType.codecId,
340
369
  nativeType: storageType.nativeType,
341
370
  typeParams: storageType.typeParams ?? {},
@@ -401,6 +430,7 @@ function resolveNamedTypeDeclarations(input: ResolveNamedTypeDeclarationsInput):
401
430
  toNamedTypeFieldDescriptor(declaration.name, descriptor),
402
431
  );
403
432
  storageTypes[declaration.name] = {
433
+ kind: 'codec-instance',
404
434
  codecId: descriptor.codecId,
405
435
  nativeType: descriptor.nativeType,
406
436
  typeParams: descriptor.typeParams ?? {},
@@ -411,6 +441,7 @@ function resolveNamedTypeDeclarations(input: ResolveNamedTypeDeclarationsInput):
411
441
  const descriptor = toNamedTypeFieldDescriptor(declaration.name, baseDescriptor);
412
442
  namedTypeDescriptors.set(declaration.name, descriptor);
413
443
  storageTypes[declaration.name] = {
444
+ kind: 'codec-instance',
414
445
  codecId: baseDescriptor.codecId,
415
446
  nativeType: baseDescriptor.nativeType,
416
447
  typeParams: {},
@@ -1284,7 +1315,11 @@ export function interpretPslDocumentToSqlContract(
1284
1315
  const enumResult = processEnumDeclarations({
1285
1316
  enums,
1286
1317
  sourceId,
1287
- enumTypeConstructor: getAuthoringTypeConstructor(input.authoringContributions, ['enum']),
1318
+ enumEntityDescriptor: getAuthoringEntity(input.authoringContributions, ['enum']),
1319
+ entityContext: {
1320
+ family: input.target.familyId,
1321
+ target: input.target.targetId,
1322
+ },
1288
1323
  diagnostics,
1289
1324
  });
1290
1325
 
@@ -2,6 +2,7 @@ import type { ContractSourceDiagnostic } from '@prisma-next/config/config-types'
2
2
  import type { ColumnDefault, ExecutionMutationDefaultPhases } from '@prisma-next/contract/types';
3
3
  import type {
4
4
  AuthoringContributions,
5
+ AuthoringEntityTypeDescriptor,
5
6
  AuthoringFieldPresetDescriptor,
6
7
  AuthoringTypeConstructorDescriptor,
7
8
  } from '@prisma-next/framework-components/authoring';
@@ -9,6 +10,7 @@ import {
9
10
  hasRegisteredFieldNamespace,
10
11
  instantiateAuthoringFieldPreset,
11
12
  instantiateAuthoringTypeConstructor,
13
+ isAuthoringEntityTypeDescriptor,
12
14
  isAuthoringFieldPresetDescriptor,
13
15
  isAuthoringTypeConstructorDescriptor,
14
16
  validateAuthoringHelperArguments,
@@ -71,6 +73,33 @@ export function getAuthoringTypeConstructor(
71
73
  return isAuthoringTypeConstructorDescriptor(current) ? current : undefined;
72
74
  }
73
75
 
76
+ /**
77
+ * Walks `authoringContributions.entityTypes` segment-by-segment and returns
78
+ * the entity type descriptor at the resolved path, or `undefined` if no
79
+ * descriptor is registered.
80
+ *
81
+ * Used by the PSL interpreter to dispatch declarative entity-shaped
82
+ * declarations (`enum`, future `namespace { … }`, …) through the
83
+ * pack entity-type mechanism — the descriptor's `factory` (or
84
+ * `template`) materialises the IR-class instance without the
85
+ * interpreter knowing target-specific construction.
86
+ */
87
+ export function getAuthoringEntity(
88
+ contributions: AuthoringContributions | undefined,
89
+ path: readonly string[],
90
+ ): AuthoringEntityTypeDescriptor | undefined {
91
+ let current: unknown = contributions?.entityTypes;
92
+
93
+ for (const segment of path) {
94
+ if (typeof current !== 'object' || current === null || Array.isArray(current)) {
95
+ return undefined;
96
+ }
97
+ current = (current as Record<string, unknown>)[segment];
98
+ }
99
+
100
+ return isAuthoringEntityTypeDescriptor(current) ? current : undefined;
101
+ }
102
+
74
103
  /**
75
104
  * Walks `authoringContributions.field` segment-by-segment and returns the field-preset descriptor at the resolved path, or `undefined` if no descriptor is registered.
76
105
  *