@prisma-next/sql-contract-psl 0.5.0-dev.50 → 0.5.0-dev.52
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 +9 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{interpreter-iFCRN9nb.mjs → interpreter-DJrrH8Ee.mjs} +234 -27
- package/dist/interpreter-DJrrH8Ee.mjs.map +1 -0
- package/dist/provider.d.mts +2 -2
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +1 -1
- package/dist/provider.mjs.map +1 -1
- package/package.json +11 -11
- package/src/interpreter.ts +17 -8
- package/src/provider.ts +2 -2
- package/src/psl-column-resolution.ts +253 -28
- package/src/psl-field-resolution.ts +128 -17
- package/src/psl-relation-resolution.ts +3 -0
- package/dist/interpreter-iFCRN9nb.mjs.map +0 -1
package/src/interpreter.ts
CHANGED
|
@@ -77,19 +77,19 @@ import {
|
|
|
77
77
|
|
|
78
78
|
export interface InterpretPslDocumentToSqlContractInput {
|
|
79
79
|
readonly document: ParsePslDocumentResult;
|
|
80
|
-
readonly target: TargetPackRef<'sql',
|
|
80
|
+
readonly target: TargetPackRef<'sql', string>;
|
|
81
81
|
readonly scalarTypeDescriptors: ReadonlyMap<string, ColumnDescriptor>;
|
|
82
82
|
readonly composedExtensionPacks?: readonly string[];
|
|
83
|
-
readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql',
|
|
83
|
+
readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql', string>[];
|
|
84
84
|
readonly controlMutationDefaults?: ControlMutationDefaults;
|
|
85
85
|
readonly authoringContributions?: AuthoringContributions;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function buildComposedExtensionPackRefs(
|
|
89
|
-
target: TargetPackRef<'sql',
|
|
89
|
+
target: TargetPackRef<'sql', string>,
|
|
90
90
|
extensionIds: readonly string[],
|
|
91
|
-
extensionPackRefs: readonly ExtensionPackRef<'sql',
|
|
92
|
-
): Record<string, ExtensionPackRef<'sql',
|
|
91
|
+
extensionPackRefs: readonly ExtensionPackRef<'sql', string>[] = [],
|
|
92
|
+
): Record<string, ExtensionPackRef<'sql', string>> | undefined {
|
|
93
93
|
if (extensionIds.length === 0) {
|
|
94
94
|
return undefined;
|
|
95
95
|
}
|
|
@@ -106,7 +106,7 @@ function buildComposedExtensionPackRefs(
|
|
|
106
106
|
familyId: target.familyId,
|
|
107
107
|
targetId: target.targetId,
|
|
108
108
|
version: '0.0.1',
|
|
109
|
-
} satisfies ExtensionPackRef<'sql',
|
|
109
|
+
} satisfies ExtensionPackRef<'sql', string>),
|
|
110
110
|
]),
|
|
111
111
|
);
|
|
112
112
|
}
|
|
@@ -219,6 +219,7 @@ function validateNamedTypeAttributes(input: {
|
|
|
219
219
|
readonly sourceId: string;
|
|
220
220
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
221
221
|
readonly composedExtensions: ReadonlySet<string>;
|
|
222
|
+
readonly authoringContributions: AuthoringContributions | undefined;
|
|
222
223
|
readonly allowDbNativeType: boolean;
|
|
223
224
|
readonly familyId: string;
|
|
224
225
|
readonly targetId: string;
|
|
@@ -250,6 +251,7 @@ function validateNamedTypeAttributes(input: {
|
|
|
250
251
|
const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
|
|
251
252
|
familyId: input.familyId,
|
|
252
253
|
targetId: input.targetId,
|
|
254
|
+
authoringContributions: input.authoringContributions,
|
|
253
255
|
});
|
|
254
256
|
if (uncomposedNamespace) {
|
|
255
257
|
reportUncomposedNamespace({
|
|
@@ -289,6 +291,7 @@ function resolveNamedTypeDeclarations(input: ResolveNamedTypeDeclarationsInput):
|
|
|
289
291
|
sourceId: input.sourceId,
|
|
290
292
|
diagnostics: input.diagnostics,
|
|
291
293
|
composedExtensions: input.composedExtensions,
|
|
294
|
+
authoringContributions: input.authoringContributions,
|
|
292
295
|
allowDbNativeType: false,
|
|
293
296
|
familyId: input.familyId,
|
|
294
297
|
targetId: input.targetId,
|
|
@@ -367,6 +370,7 @@ function resolveNamedTypeDeclarations(input: ResolveNamedTypeDeclarationsInput):
|
|
|
367
370
|
sourceId: input.sourceId,
|
|
368
371
|
diagnostics: input.diagnostics,
|
|
369
372
|
composedExtensions: input.composedExtensions,
|
|
373
|
+
authoringContributions: input.authoringContributions,
|
|
370
374
|
allowDbNativeType: true,
|
|
371
375
|
familyId: input.familyId,
|
|
372
376
|
targetId: input.targetId,
|
|
@@ -483,6 +487,7 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
483
487
|
field,
|
|
484
488
|
sourceId,
|
|
485
489
|
composedExtensions: input.composedExtensions,
|
|
490
|
+
authoringContributions: input.authoringContributions,
|
|
486
491
|
diagnostics,
|
|
487
492
|
familyId: input.familyId,
|
|
488
493
|
targetId: input.targetId,
|
|
@@ -604,7 +609,11 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
604
609
|
const uncomposedNamespace = checkUncomposedNamespace(
|
|
605
610
|
modelAttribute.name,
|
|
606
611
|
input.composedExtensions,
|
|
607
|
-
{
|
|
612
|
+
{
|
|
613
|
+
familyId: input.familyId,
|
|
614
|
+
targetId: input.targetId,
|
|
615
|
+
authoringContributions: input.authoringContributions,
|
|
616
|
+
},
|
|
608
617
|
);
|
|
609
618
|
if (uncomposedNamespace) {
|
|
610
619
|
reportUncomposedNamespace({
|
|
@@ -762,7 +771,7 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
762
771
|
descriptor: resolvedField.descriptor,
|
|
763
772
|
nullable: resolvedField.field.optional,
|
|
764
773
|
...ifDefined('default', resolvedField.defaultValue),
|
|
765
|
-
...ifDefined('
|
|
774
|
+
...ifDefined('executionDefaults', resolvedField.executionDefaults),
|
|
766
775
|
})),
|
|
767
776
|
...(primaryKeyColumns.length > 0
|
|
768
777
|
? {
|
package/src/provider.ts
CHANGED
|
@@ -10,8 +10,8 @@ import type { ColumnDescriptor } from './psl-column-resolution';
|
|
|
10
10
|
|
|
11
11
|
export interface PrismaContractOptions {
|
|
12
12
|
readonly output?: string;
|
|
13
|
-
readonly target: TargetPackRef<'sql',
|
|
14
|
-
readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql',
|
|
13
|
+
readonly target: TargetPackRef<'sql', string>;
|
|
14
|
+
readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql', string>[];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function buildColumnDescriptorMap(
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { ContractSourceDiagnostic } from '@prisma-next/config/config-types';
|
|
2
|
-
import type { ColumnDefault,
|
|
2
|
+
import type { ColumnDefault, ExecutionMutationDefaultPhases } from '@prisma-next/contract/types';
|
|
3
3
|
import type {
|
|
4
4
|
AuthoringContributions,
|
|
5
|
+
AuthoringFieldPresetDescriptor,
|
|
5
6
|
AuthoringTypeConstructorDescriptor,
|
|
6
7
|
} from '@prisma-next/framework-components/authoring';
|
|
7
8
|
import {
|
|
9
|
+
hasRegisteredFieldNamespace,
|
|
10
|
+
instantiateAuthoringFieldPreset,
|
|
8
11
|
instantiateAuthoringTypeConstructor,
|
|
12
|
+
isAuthoringFieldPresetDescriptor,
|
|
9
13
|
isAuthoringTypeConstructorDescriptor,
|
|
10
14
|
validateAuthoringHelperArguments,
|
|
11
15
|
} from '@prisma-next/framework-components/authoring';
|
|
@@ -67,6 +71,34 @@ export function getAuthoringTypeConstructor(
|
|
|
67
71
|
return isAuthoringTypeConstructorDescriptor(current) ? current : undefined;
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Walks `authoringContributions.field` segment-by-segment and returns the
|
|
76
|
+
* field-preset descriptor at the resolved path, or `undefined` if no descriptor
|
|
77
|
+
* is registered.
|
|
78
|
+
*
|
|
79
|
+
* Symmetric with `getAuthoringTypeConstructor`. Field presets are strictly
|
|
80
|
+
* richer than type constructors — they can contribute `default` /
|
|
81
|
+
* `executionDefaults` / `id` / `unique` / `nullable` in addition to the
|
|
82
|
+
* `codecId` / `nativeType` / `typeParams` triple. PSL resolution tries field
|
|
83
|
+
* presets first, then falls back to type constructors on miss (see
|
|
84
|
+
* `resolveFieldTypeDescriptor`).
|
|
85
|
+
*/
|
|
86
|
+
export function getAuthoringFieldPreset(
|
|
87
|
+
contributions: AuthoringContributions | undefined,
|
|
88
|
+
path: readonly string[],
|
|
89
|
+
): AuthoringFieldPresetDescriptor | undefined {
|
|
90
|
+
let current: unknown = contributions?.field;
|
|
91
|
+
|
|
92
|
+
for (const segment of path) {
|
|
93
|
+
if (typeof current !== 'object' || current === null || Array.isArray(current)) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
current = (current as Record<string, unknown>)[segment];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return isAuthoringFieldPresetDescriptor(current) ? current : undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
70
102
|
/**
|
|
71
103
|
* Returns the namespace prefix of `attributeName` if it references an
|
|
72
104
|
* unrecognized extension namespace, otherwise `undefined`. A namespace is
|
|
@@ -75,16 +107,22 @@ export function getAuthoringTypeConstructor(
|
|
|
75
107
|
* - `db` (native-type spec, always allowed),
|
|
76
108
|
* - the active family id (e.g. `sql`),
|
|
77
109
|
* - the active target id (e.g. `postgres`),
|
|
110
|
+
* - a registered field-preset namespace (e.g. `temporal`),
|
|
78
111
|
* - present in `composedExtensions`.
|
|
79
112
|
*
|
|
80
|
-
* Family/target namespaces are exempted so that e.g. `@sql.foo`
|
|
81
|
-
* PSL_UNSUPPORTED_*_ATTRIBUTE (the attribute isn't defined)
|
|
82
|
-
* PSL_EXTENSION_NAMESPACE_NOT_COMPOSED (the namespace is already
|
|
113
|
+
* Family/target/field-preset namespaces are exempted so that e.g. `@sql.foo`
|
|
114
|
+
* surfaces as PSL_UNSUPPORTED_*_ATTRIBUTE (the attribute isn't defined)
|
|
115
|
+
* rather than PSL_EXTENSION_NAMESPACE_NOT_COMPOSED (the namespace is already
|
|
116
|
+
* composed).
|
|
83
117
|
*/
|
|
84
118
|
export function checkUncomposedNamespace(
|
|
85
119
|
attributeName: string,
|
|
86
120
|
composedExtensions: ReadonlySet<string>,
|
|
87
|
-
context?: {
|
|
121
|
+
context?: {
|
|
122
|
+
readonly familyId?: string;
|
|
123
|
+
readonly targetId?: string;
|
|
124
|
+
readonly authoringContributions?: AuthoringContributions | undefined;
|
|
125
|
+
},
|
|
88
126
|
): string | undefined {
|
|
89
127
|
const dotIndex = attributeName.indexOf('.');
|
|
90
128
|
if (dotIndex <= 0 || dotIndex === attributeName.length - 1) {
|
|
@@ -95,6 +133,7 @@ export function checkUncomposedNamespace(
|
|
|
95
133
|
namespace === 'db' ||
|
|
96
134
|
namespace === context?.familyId ||
|
|
97
135
|
namespace === context?.targetId ||
|
|
136
|
+
hasRegisteredFieldNamespace(context?.authoringContributions, namespace) ||
|
|
98
137
|
composedExtensions.has(namespace)
|
|
99
138
|
) {
|
|
100
139
|
return undefined;
|
|
@@ -126,6 +165,29 @@ export function reportUncomposedNamespace(input: {
|
|
|
126
165
|
});
|
|
127
166
|
}
|
|
128
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Pushes the canonical `PSL_UNKNOWN_FIELD_PRESET` diagnostic when a typoed
|
|
170
|
+
* preset name is referenced inside a registered field-preset namespace. The
|
|
171
|
+
* `data` payload exposes the namespace and full helper path so machine
|
|
172
|
+
* consumers (agents, IDE extensions) don't have to parse the prose.
|
|
173
|
+
*/
|
|
174
|
+
export function reportUnknownFieldPreset(input: {
|
|
175
|
+
readonly entityLabel: string;
|
|
176
|
+
readonly namespace: string;
|
|
177
|
+
readonly helperPath: string;
|
|
178
|
+
readonly sourceId: string;
|
|
179
|
+
readonly span: PslSpan;
|
|
180
|
+
readonly diagnostics: ContractSourceDiagnostic[];
|
|
181
|
+
}): void {
|
|
182
|
+
input.diagnostics.push({
|
|
183
|
+
code: 'PSL_UNKNOWN_FIELD_PRESET',
|
|
184
|
+
message: `${input.entityLabel} references unknown field preset "${input.helperPath}". Check the spelling against the available presets in the "${input.namespace}" namespace.`,
|
|
185
|
+
sourceId: input.sourceId,
|
|
186
|
+
span: input.span,
|
|
187
|
+
data: { namespace: input.namespace, helperPath: input.helperPath },
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
129
191
|
export function instantiatePslTypeConstructor(input: {
|
|
130
192
|
readonly call: PslTypeConstructorCall;
|
|
131
193
|
readonly descriptor: AuthoringTypeConstructorDescriptor;
|
|
@@ -200,17 +262,19 @@ export function resolvePslTypeConstructorDescriptor(input: {
|
|
|
200
262
|
return descriptor;
|
|
201
263
|
}
|
|
202
264
|
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
265
|
+
const uncomposedNamespace = checkUncomposedNamespace(
|
|
266
|
+
input.call.path.join('.'),
|
|
267
|
+
input.composedExtensions,
|
|
268
|
+
{
|
|
269
|
+
familyId: input.familyId,
|
|
270
|
+
targetId: input.targetId,
|
|
271
|
+
authoringContributions: input.authoringContributions,
|
|
272
|
+
},
|
|
273
|
+
);
|
|
274
|
+
if (uncomposedNamespace) {
|
|
211
275
|
reportUncomposedNamespace({
|
|
212
276
|
subjectLabel: `Type constructor "${input.call.path.join('.')}"`,
|
|
213
|
-
namespace,
|
|
277
|
+
namespace: uncomposedNamespace,
|
|
214
278
|
sourceId: input.sourceId,
|
|
215
279
|
span: input.call.span,
|
|
216
280
|
diagnostics: input.diagnostics,
|
|
@@ -227,8 +291,99 @@ export function resolvePslTypeConstructorDescriptor(input: {
|
|
|
227
291
|
});
|
|
228
292
|
}
|
|
229
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Instantiates a field-preset call against its descriptor, coercing PSL AST
|
|
296
|
+
* arguments into the descriptor's typed argument shape and returning the
|
|
297
|
+
* preset's full set of contract contributions.
|
|
298
|
+
*
|
|
299
|
+
* Symmetric with `instantiatePslTypeConstructor` but richer: a field preset
|
|
300
|
+
* can contribute `default`, `executionDefaults`, `id`, `unique`, and
|
|
301
|
+
* `nullable` in addition to the storage-type triple. PSL → typed-args
|
|
302
|
+
* coercion happens here (via `mapPslHelperArgs`) so that
|
|
303
|
+
* `instantiateAuthoringFieldPreset` itself stays typed-input-only and TS
|
|
304
|
+
* keeps its zero-runtime-validation cost.
|
|
305
|
+
*/
|
|
306
|
+
export function instantiatePslFieldPreset(input: {
|
|
307
|
+
readonly call: PslTypeConstructorCall;
|
|
308
|
+
readonly descriptor: AuthoringFieldPresetDescriptor;
|
|
309
|
+
readonly diagnostics: ContractSourceDiagnostic[];
|
|
310
|
+
readonly sourceId: string;
|
|
311
|
+
readonly entityLabel: string;
|
|
312
|
+
}):
|
|
313
|
+
| {
|
|
314
|
+
readonly descriptor: ColumnDescriptor;
|
|
315
|
+
readonly nullable: boolean;
|
|
316
|
+
readonly default?: ColumnDefault;
|
|
317
|
+
readonly executionDefaults?: ExecutionMutationDefaultPhases;
|
|
318
|
+
readonly id: boolean;
|
|
319
|
+
readonly unique: boolean;
|
|
320
|
+
}
|
|
321
|
+
| undefined {
|
|
322
|
+
const helperPath = input.call.path.join('.');
|
|
323
|
+
const args = mapPslHelperArgs({
|
|
324
|
+
args: input.call.args,
|
|
325
|
+
descriptors: input.descriptor.args ?? [],
|
|
326
|
+
helperLabel: `preset "${helperPath}"`,
|
|
327
|
+
span: input.call.span,
|
|
328
|
+
diagnostics: input.diagnostics,
|
|
329
|
+
sourceId: input.sourceId,
|
|
330
|
+
entityLabel: input.entityLabel,
|
|
331
|
+
});
|
|
332
|
+
if (!args) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
validateAuthoringHelperArguments(helperPath, input.descriptor.args, args);
|
|
338
|
+
const instantiated = instantiateAuthoringFieldPreset(input.descriptor, args);
|
|
339
|
+
return {
|
|
340
|
+
descriptor: {
|
|
341
|
+
codecId: instantiated.descriptor.codecId,
|
|
342
|
+
nativeType: instantiated.descriptor.nativeType,
|
|
343
|
+
...(instantiated.descriptor.typeParams !== undefined
|
|
344
|
+
? { typeParams: instantiated.descriptor.typeParams }
|
|
345
|
+
: {}),
|
|
346
|
+
},
|
|
347
|
+
nullable: instantiated.nullable,
|
|
348
|
+
...(instantiated.default !== undefined ? { default: instantiated.default } : {}),
|
|
349
|
+
...(instantiated.executionDefaults !== undefined
|
|
350
|
+
? { executionDefaults: instantiated.executionDefaults }
|
|
351
|
+
: {}),
|
|
352
|
+
id: instantiated.id,
|
|
353
|
+
unique: instantiated.unique,
|
|
354
|
+
};
|
|
355
|
+
} catch (error) {
|
|
356
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
357
|
+
input.diagnostics.push({
|
|
358
|
+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
|
|
359
|
+
message: `${input.entityLabel} preset "${helperPath}" ${message}`,
|
|
360
|
+
sourceId: input.sourceId,
|
|
361
|
+
span: input.call.span,
|
|
362
|
+
});
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Contract contributions a field preset adds beyond the bare storage-type
|
|
369
|
+
* triple. Set when a field is resolved through the field-preset dispatch
|
|
370
|
+
* path; absent when resolved through the type-constructor path or as a
|
|
371
|
+
* scalar/enum/named-type lookup.
|
|
372
|
+
*/
|
|
373
|
+
export type FieldPresetContributions = {
|
|
374
|
+
readonly nullable: boolean;
|
|
375
|
+
readonly id: boolean;
|
|
376
|
+
readonly unique: boolean;
|
|
377
|
+
readonly default?: ColumnDefault;
|
|
378
|
+
readonly executionDefaults?: ExecutionMutationDefaultPhases;
|
|
379
|
+
};
|
|
380
|
+
|
|
230
381
|
export type ResolveFieldTypeResult =
|
|
231
|
-
| {
|
|
382
|
+
| {
|
|
383
|
+
readonly ok: true;
|
|
384
|
+
readonly descriptor: ColumnDescriptor;
|
|
385
|
+
readonly presetContributions?: FieldPresetContributions;
|
|
386
|
+
}
|
|
232
387
|
| { readonly ok: false; readonly alreadyReported: boolean };
|
|
233
388
|
|
|
234
389
|
export function resolveFieldTypeDescriptor(input: {
|
|
@@ -245,18 +400,73 @@ export function resolveFieldTypeDescriptor(input: {
|
|
|
245
400
|
readonly entityLabel: string;
|
|
246
401
|
}): ResolveFieldTypeResult {
|
|
247
402
|
if (input.field.typeConstructor) {
|
|
403
|
+
// Field presets carry richer semantics than type constructors, so a field
|
|
404
|
+
// preset match is the complete answer. Shared composition rejects exact
|
|
405
|
+
// cross-registry collisions before PSL resolution can observe them.
|
|
406
|
+
const presetDescriptor = getAuthoringFieldPreset(
|
|
407
|
+
input.authoringContributions,
|
|
408
|
+
input.field.typeConstructor.path,
|
|
409
|
+
);
|
|
410
|
+
if (presetDescriptor) {
|
|
411
|
+
const instantiated = instantiatePslFieldPreset({
|
|
412
|
+
call: input.field.typeConstructor,
|
|
413
|
+
descriptor: presetDescriptor,
|
|
414
|
+
diagnostics: input.diagnostics,
|
|
415
|
+
sourceId: input.sourceId,
|
|
416
|
+
entityLabel: input.entityLabel,
|
|
417
|
+
});
|
|
418
|
+
if (!instantiated) {
|
|
419
|
+
return { ok: false, alreadyReported: true };
|
|
420
|
+
}
|
|
421
|
+
const presetContributions: FieldPresetContributions = {
|
|
422
|
+
nullable: instantiated.nullable,
|
|
423
|
+
id: instantiated.id,
|
|
424
|
+
unique: instantiated.unique,
|
|
425
|
+
...(instantiated.default !== undefined ? { default: instantiated.default } : {}),
|
|
426
|
+
...(instantiated.executionDefaults !== undefined
|
|
427
|
+
? { executionDefaults: instantiated.executionDefaults }
|
|
428
|
+
: {}),
|
|
429
|
+
};
|
|
430
|
+
return { ok: true, descriptor: instantiated.descriptor, presetContributions };
|
|
431
|
+
}
|
|
432
|
+
|
|
248
433
|
const helperPath = input.field.typeConstructor.path.join('.');
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
434
|
+
const namespacePrefix =
|
|
435
|
+
input.field.typeConstructor.path.length > 1 ? input.field.typeConstructor.path[0] : undefined;
|
|
436
|
+
const typeDescriptor = getAuthoringTypeConstructor(
|
|
437
|
+
input.authoringContributions,
|
|
438
|
+
input.field.typeConstructor.path,
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
if (
|
|
442
|
+
!typeDescriptor &&
|
|
443
|
+
namespacePrefix &&
|
|
444
|
+
hasRegisteredFieldNamespace(input.authoringContributions, namespacePrefix)
|
|
445
|
+
) {
|
|
446
|
+
reportUnknownFieldPreset({
|
|
447
|
+
entityLabel: input.entityLabel,
|
|
448
|
+
namespace: namespacePrefix,
|
|
449
|
+
helperPath,
|
|
450
|
+
sourceId: input.sourceId,
|
|
451
|
+
span: input.field.typeConstructor.span,
|
|
452
|
+
diagnostics: input.diagnostics,
|
|
453
|
+
});
|
|
454
|
+
return { ok: false, alreadyReported: true };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const descriptor =
|
|
458
|
+
typeDescriptor ??
|
|
459
|
+
resolvePslTypeConstructorDescriptor({
|
|
460
|
+
call: input.field.typeConstructor,
|
|
461
|
+
authoringContributions: input.authoringContributions,
|
|
462
|
+
composedExtensions: input.composedExtensions,
|
|
463
|
+
familyId: input.familyId,
|
|
464
|
+
targetId: input.targetId,
|
|
465
|
+
diagnostics: input.diagnostics,
|
|
466
|
+
sourceId: input.sourceId,
|
|
467
|
+
unsupportedCode: 'PSL_UNSUPPORTED_FIELD_TYPE',
|
|
468
|
+
unsupportedMessage: `${input.entityLabel} type constructor "${helperPath}" is not supported in SQL PSL provider v1`,
|
|
469
|
+
});
|
|
260
470
|
if (!descriptor) {
|
|
261
471
|
return { ok: false, alreadyReported: true };
|
|
262
472
|
}
|
|
@@ -495,7 +705,7 @@ export function lowerDefaultForField(input: {
|
|
|
495
705
|
readonly diagnostics: ContractSourceDiagnostic[];
|
|
496
706
|
}): {
|
|
497
707
|
readonly defaultValue?: ColumnDefault;
|
|
498
|
-
readonly
|
|
708
|
+
readonly executionDefaults?: ExecutionMutationDefaultPhases;
|
|
499
709
|
} {
|
|
500
710
|
const positionalEntries = input.defaultAttribute.args.filter((arg) => arg.kind === 'positional');
|
|
501
711
|
const namedEntries = input.defaultAttribute.args.filter((arg) => arg.kind === 'named');
|
|
@@ -568,6 +778,21 @@ export function lowerDefaultForField(input: {
|
|
|
568
778
|
return {};
|
|
569
779
|
}
|
|
570
780
|
|
|
781
|
+
// Preset-only generators (e.g. `timestampNow`) co-register their codec
|
|
782
|
+
// through the preset descriptor, so they don't carry an
|
|
783
|
+
// `applicableCodecIds` list. Such a generator surfacing on the
|
|
784
|
+
// `@default(...)` lowering path is itself the bug — emit a diagnostic
|
|
785
|
+
// pointing the user at the correct authoring surface.
|
|
786
|
+
if (generatorDescriptor.applicableCodecIds === undefined) {
|
|
787
|
+
input.diagnostics.push({
|
|
788
|
+
code: 'PSL_INVALID_DEFAULT_APPLICABILITY',
|
|
789
|
+
message: `Default generator "${generatorDescriptor.id}" is not applicable to "@default(...)" lowering. Use the corresponding field preset (e.g. \`temporal.${generatorDescriptor.id === 'timestampNow' ? 'updatedAt' : generatorDescriptor.id}()\`) instead.`,
|
|
790
|
+
sourceId: input.sourceId,
|
|
791
|
+
span: expressionEntry.span,
|
|
792
|
+
});
|
|
793
|
+
return {};
|
|
794
|
+
}
|
|
795
|
+
|
|
571
796
|
if (!generatorDescriptor.applicableCodecIds.includes(input.columnDescriptor.codecId)) {
|
|
572
797
|
input.diagnostics.push({
|
|
573
798
|
code: 'PSL_INVALID_DEFAULT_APPLICABILITY',
|
|
@@ -578,7 +803,7 @@ export function lowerDefaultForField(input: {
|
|
|
578
803
|
return {};
|
|
579
804
|
}
|
|
580
805
|
|
|
581
|
-
return {
|
|
806
|
+
return { executionDefaults: { onCreate: lowered.value.generated } };
|
|
582
807
|
}
|
|
583
808
|
|
|
584
809
|
export function resolveColumnDescriptor(
|