@prisma-next/framework-components 0.5.0-dev.9 → 0.5.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.
- package/README.md +51 -4
- package/dist/authoring.d.mts +2 -2
- package/dist/authoring.mjs +2 -122
- package/dist/codec-m_-FAyQn.d.mts +168 -0
- package/dist/codec-m_-FAyQn.d.mts.map +1 -0
- package/dist/codec.d.mts +48 -2
- package/dist/codec.d.mts.map +1 -0
- package/dist/codec.mjs +67 -4
- package/dist/codec.mjs.map +1 -1
- package/dist/components.d.mts +1 -1
- package/dist/components.mjs +2 -3
- package/dist/control.d.mts +421 -78
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +83 -58
- package/dist/control.mjs.map +1 -1
- package/dist/emission-types-CMv_053d.d.mts +38 -0
- package/dist/emission-types-CMv_053d.d.mts.map +1 -0
- package/dist/emission.d.mts +2 -2
- package/dist/emission.mjs +1 -1
- package/dist/execution.d.mts +5 -5
- package/dist/execution.d.mts.map +1 -1
- package/dist/execution.mjs +4 -6
- package/dist/execution.mjs.map +1 -1
- package/dist/{framework-authoring-D1-JZ37B.d.mts → framework-authoring-DGIQbNPt.d.mts} +43 -12
- package/dist/framework-authoring-DGIQbNPt.d.mts.map +1 -0
- package/dist/framework-authoring-DxXcjyJX.mjs +209 -0
- package/dist/framework-authoring-DxXcjyJX.mjs.map +1 -0
- package/dist/{framework-components-DFZMi2h7.d.mts → framework-components-CHuBHXQN.d.mts} +45 -58
- package/dist/framework-components-CHuBHXQN.d.mts.map +1 -0
- package/dist/{framework-components-C8ZhSwXe.mjs → framework-components-FdqmlGUj.mjs} +3 -3
- package/dist/framework-components-FdqmlGUj.mjs.map +1 -0
- package/dist/psl-ast-Ckn_G-jv.d.mts +159 -0
- package/dist/psl-ast-Ckn_G-jv.d.mts.map +1 -0
- package/dist/psl-ast.d.mts +2 -0
- package/dist/psl-ast.mjs +1 -0
- package/dist/runtime.d.mts +273 -37
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +172 -30
- package/dist/runtime.mjs.map +1 -1
- package/dist/{types-import-spec-C4sc7wbb.d.mts → types-import-spec-BxI5cSQy.d.mts} +2 -2
- package/dist/types-import-spec-BxI5cSQy.d.mts.map +1 -0
- package/package.json +12 -8
- package/src/control/control-capabilities.ts +96 -0
- package/src/{control-descriptors.ts → control/control-descriptors.ts} +7 -7
- package/src/{control-instances.ts → control/control-instances.ts} +52 -6
- package/src/{control-migration-types.ts → control/control-migration-types.ts} +251 -63
- package/src/control/control-operation-preview.ts +23 -0
- package/src/{control-result-types.ts → control/control-result-types.ts} +0 -2
- package/src/control/control-spaces.ts +82 -0
- package/src/{control-stack.ts → control/control-stack.ts} +77 -111
- package/src/control/emission-types.ts +48 -0
- package/src/control/psl-ast.ts +193 -0
- package/src/{execution-descriptors.ts → execution/execution-descriptors.ts} +7 -7
- package/src/{execution-instances.ts → execution/execution-instances.ts} +1 -1
- package/src/{execution-requirements.ts → execution/execution-requirements.ts} +1 -1
- package/src/execution/race-against-abort.ts +89 -0
- package/src/execution/run-with-middleware.ts +153 -0
- package/src/{runtime-core.ts → execution/runtime-core.ts} +27 -3
- package/src/execution/runtime-error.ts +94 -0
- package/src/execution/runtime-middleware.ts +235 -0
- package/src/exports/authoring.ts +5 -2
- package/src/exports/codec.ts +27 -2
- package/src/exports/components.ts +2 -2
- package/src/exports/control.ts +41 -14
- package/src/exports/emission.ts +2 -2
- package/src/exports/execution.ts +5 -5
- package/src/exports/psl-ast.ts +1 -0
- package/src/exports/runtime.ts +18 -9
- package/src/shared/codec-descriptor.ts +87 -0
- package/src/shared/codec-types.ts +79 -0
- package/src/shared/codec.ts +80 -0
- package/src/shared/column-spec.ts +83 -0
- package/src/{framework-authoring.ts → shared/framework-authoring.ts} +210 -23
- package/src/{framework-components.ts → shared/framework-components.ts} +22 -49
- package/src/{mutation-default-types.ts → shared/mutation-default-types.ts} +22 -2
- package/dist/authoring.mjs.map +0 -1
- package/dist/codec-types-DQ1Agjom.d.mts +0 -58
- package/dist/codec-types-DQ1Agjom.d.mts.map +0 -1
- package/dist/emission-types-BPAALJbF.d.mts +0 -24
- package/dist/emission-types-BPAALJbF.d.mts.map +0 -1
- package/dist/framework-authoring-D1-JZ37B.d.mts.map +0 -1
- package/dist/framework-components-C8ZhSwXe.mjs.map +0 -1
- package/dist/framework-components-DFZMi2h7.d.mts.map +0 -1
- package/dist/types-import-spec-C4sc7wbb.d.mts.map +0 -1
- package/src/codec-types.ts +0 -64
- package/src/control-capabilities.ts +0 -34
- package/src/emission-types.ts +0 -28
- package/src/run-with-middleware.ts +0 -77
- package/src/runtime-error.ts +0 -55
- package/src/runtime-middleware.ts +0 -87
- /package/src/{control-schema-view.ts → control/control-schema-view.ts} +0 -0
- /package/src/{async-iterable-result.ts → execution/async-iterable-result.ts} +0 -0
- /package/src/{execution-stack.ts → execution/execution-stack.ts} +0 -0
- /package/src/{query-plan.ts → execution/query-plan.ts} +0 -0
- /package/src/{types-import-spec.ts → shared/types-import-spec.ts} +0 -0
|
@@ -1,25 +1,30 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
ControlAdapterDescriptor,
|
|
4
|
-
ControlDriverDescriptor,
|
|
5
|
-
ControlExtensionDescriptor,
|
|
6
|
-
ControlFamilyDescriptor,
|
|
7
|
-
ControlTargetDescriptor,
|
|
8
|
-
} from './control-descriptors';
|
|
1
|
+
import type { Codec } from '../shared/codec';
|
|
2
|
+
import type { CodecLookup, CodecMeta } from '../shared/codec-types';
|
|
9
3
|
import type {
|
|
10
4
|
AuthoringContributions,
|
|
11
5
|
AuthoringFieldNamespace,
|
|
12
|
-
AuthoringFieldPresetDescriptor,
|
|
13
|
-
AuthoringTypeConstructorDescriptor,
|
|
14
6
|
AuthoringTypeNamespace,
|
|
15
|
-
} from '
|
|
16
|
-
import
|
|
7
|
+
} from '../shared/framework-authoring';
|
|
8
|
+
import {
|
|
9
|
+
assertNoCrossRegistryCollisions,
|
|
10
|
+
isAuthoringFieldPresetDescriptor,
|
|
11
|
+
isAuthoringTypeConstructorDescriptor,
|
|
12
|
+
mergeAuthoringNamespaces,
|
|
13
|
+
} from '../shared/framework-authoring';
|
|
14
|
+
import type { ComponentMetadata } from '../shared/framework-components';
|
|
17
15
|
import type {
|
|
18
16
|
ControlMutationDefaultEntry,
|
|
19
17
|
ControlMutationDefaults,
|
|
20
18
|
MutationDefaultGeneratorDescriptor,
|
|
21
|
-
} from '
|
|
22
|
-
import type { TypesImportSpec } from '
|
|
19
|
+
} from '../shared/mutation-default-types';
|
|
20
|
+
import type { TypesImportSpec } from '../shared/types-import-spec';
|
|
21
|
+
import type {
|
|
22
|
+
ControlAdapterDescriptor,
|
|
23
|
+
ControlDriverDescriptor,
|
|
24
|
+
ControlExtensionDescriptor,
|
|
25
|
+
ControlFamilyDescriptor,
|
|
26
|
+
ControlTargetDescriptor,
|
|
27
|
+
} from './control-descriptors';
|
|
23
28
|
|
|
24
29
|
export interface AssembledAuthoringContributions {
|
|
25
30
|
readonly field: AuthoringFieldNamespace;
|
|
@@ -37,7 +42,6 @@ export interface ControlStack<
|
|
|
37
42
|
readonly extensionPacks: readonly ControlExtensionDescriptor<TFamilyId, TTargetId>[];
|
|
38
43
|
|
|
39
44
|
readonly codecTypeImports: ReadonlyArray<TypesImportSpec>;
|
|
40
|
-
readonly operationTypeImports: ReadonlyArray<TypesImportSpec>;
|
|
41
45
|
readonly queryOperationTypeImports: ReadonlyArray<TypesImportSpec>;
|
|
42
46
|
readonly extensionIds: ReadonlyArray<string>;
|
|
43
47
|
readonly codecLookup: CodecLookup;
|
|
@@ -101,21 +105,6 @@ export function extractCodecTypeImports(
|
|
|
101
105
|
return imports;
|
|
102
106
|
}
|
|
103
107
|
|
|
104
|
-
export function extractOperationTypeImports(
|
|
105
|
-
descriptors: ReadonlyArray<Pick<ComponentMetadata, 'types'>>,
|
|
106
|
-
): ReadonlyArray<TypesImportSpec> {
|
|
107
|
-
const imports: TypesImportSpec[] = [];
|
|
108
|
-
|
|
109
|
-
for (const descriptor of descriptors) {
|
|
110
|
-
const operationTypes = descriptor.types?.operationTypes;
|
|
111
|
-
if (operationTypes?.import) {
|
|
112
|
-
imports.push(operationTypes.import);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return imports;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
108
|
export function extractQueryOperationTypeImports(
|
|
120
109
|
descriptors: ReadonlyArray<Pick<ComponentMetadata, 'types'>>,
|
|
121
110
|
): ReadonlyArray<TypesImportSpec> {
|
|
@@ -153,70 +142,6 @@ export function extractComponentIds(
|
|
|
153
142
|
return ids;
|
|
154
143
|
}
|
|
155
144
|
|
|
156
|
-
function isTypeConstructorDescriptor(value: unknown): value is AuthoringTypeConstructorDescriptor {
|
|
157
|
-
return (
|
|
158
|
-
typeof value === 'object' &&
|
|
159
|
-
value !== null &&
|
|
160
|
-
(value as { kind?: unknown }).kind === 'typeConstructor'
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function isFieldPresetDescriptor(value: unknown): value is AuthoringFieldPresetDescriptor {
|
|
165
|
-
return (
|
|
166
|
-
typeof value === 'object' &&
|
|
167
|
-
value !== null &&
|
|
168
|
-
(value as { kind?: unknown }).kind === 'fieldPreset'
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function mergeAuthoringNamespaces(
|
|
173
|
-
target: Record<string, unknown>,
|
|
174
|
-
source: Record<string, unknown>,
|
|
175
|
-
path: readonly string[],
|
|
176
|
-
leafGuard: (value: unknown) => boolean,
|
|
177
|
-
label: string,
|
|
178
|
-
): void {
|
|
179
|
-
const assertSafePath = (currentPath: readonly string[]) => {
|
|
180
|
-
const blockedSegment = currentPath.find(
|
|
181
|
-
(segment) => segment === '__proto__' || segment === 'constructor' || segment === 'prototype',
|
|
182
|
-
);
|
|
183
|
-
if (blockedSegment) {
|
|
184
|
-
throw new Error(
|
|
185
|
-
`Invalid authoring ${label} helper "${currentPath.join('.')}". Helper path segments must not use "${blockedSegment}".`,
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
for (const [key, sourceValue] of Object.entries(source)) {
|
|
191
|
-
const currentPath = [...path, key];
|
|
192
|
-
assertSafePath(currentPath);
|
|
193
|
-
const hasExistingValue = Object.hasOwn(target, key);
|
|
194
|
-
const existingValue = hasExistingValue ? target[key] : undefined;
|
|
195
|
-
|
|
196
|
-
if (!hasExistingValue) {
|
|
197
|
-
target[key] = sourceValue;
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const existingIsLeaf = leafGuard(existingValue);
|
|
202
|
-
const sourceIsLeaf = leafGuard(sourceValue);
|
|
203
|
-
|
|
204
|
-
if (existingIsLeaf || sourceIsLeaf) {
|
|
205
|
-
throw new Error(
|
|
206
|
-
`Duplicate authoring ${label} helper "${currentPath.join('.')}". Descriptor contributions must be unique across composed components.`,
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
mergeAuthoringNamespaces(
|
|
211
|
-
existingValue as Record<string, unknown>,
|
|
212
|
-
sourceValue as Record<string, unknown>,
|
|
213
|
-
currentPath,
|
|
214
|
-
leafGuard,
|
|
215
|
-
label,
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
145
|
export function assembleAuthoringContributions(
|
|
221
146
|
descriptors: ReadonlyArray<{ readonly authoring?: AuthoringContributions }>,
|
|
222
147
|
): AssembledAuthoringContributions {
|
|
@@ -229,7 +154,7 @@ export function assembleAuthoringContributions(
|
|
|
229
154
|
field,
|
|
230
155
|
descriptor.authoring.field,
|
|
231
156
|
[],
|
|
232
|
-
|
|
157
|
+
isAuthoringFieldPresetDescriptor,
|
|
233
158
|
'field',
|
|
234
159
|
);
|
|
235
160
|
}
|
|
@@ -240,14 +165,18 @@ export function assembleAuthoringContributions(
|
|
|
240
165
|
type,
|
|
241
166
|
descriptor.authoring.type,
|
|
242
167
|
[],
|
|
243
|
-
|
|
168
|
+
isAuthoringTypeConstructorDescriptor,
|
|
244
169
|
'type',
|
|
245
170
|
);
|
|
246
171
|
}
|
|
247
172
|
|
|
173
|
+
const fieldNamespace = field as AuthoringFieldNamespace;
|
|
174
|
+
const typeNamespace = type as AuthoringTypeNamespace;
|
|
175
|
+
assertNoCrossRegistryCollisions(typeNamespace, fieldNamespace);
|
|
176
|
+
|
|
248
177
|
return {
|
|
249
|
-
field:
|
|
250
|
-
type:
|
|
178
|
+
field: fieldNamespace,
|
|
179
|
+
type: typeNamespace,
|
|
251
180
|
};
|
|
252
181
|
}
|
|
253
182
|
|
|
@@ -326,27 +255,65 @@ export function assembleControlMutationDefaults(
|
|
|
326
255
|
}
|
|
327
256
|
|
|
328
257
|
export function extractCodecLookup(
|
|
329
|
-
descriptors: ReadonlyArray<Pick<ComponentMetadata & { id
|
|
258
|
+
descriptors: ReadonlyArray<Pick<ComponentMetadata & { id: string }, 'types' | 'id'>>,
|
|
330
259
|
): CodecLookup {
|
|
331
|
-
const byId = new Map<string,
|
|
260
|
+
const byId = new Map<string, Codec>();
|
|
261
|
+
const targetTypesById = new Map<string, readonly string[]>();
|
|
262
|
+
const metaById = new Map<string, CodecMeta>();
|
|
263
|
+
const renderersById = new Map<string, (params: Record<string, unknown>) => string | undefined>();
|
|
332
264
|
const owners = new Map<string, string>();
|
|
333
265
|
for (const descriptor of descriptors) {
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
for (const
|
|
266
|
+
const codecTypes = descriptor.types?.codecTypes;
|
|
267
|
+
const descriptorId = descriptor.id;
|
|
268
|
+
// Descriptor-side metadata is the single source of truth for `targetTypes` / `meta` / `renderOutputType`. Every contributor ships a `codecDescriptors` list on `types.codecTypes`.
|
|
269
|
+
for (const codecDescriptor of codecTypes?.codecDescriptors ?? []) {
|
|
338
270
|
assertUniqueCodecOwner({
|
|
339
|
-
codecId:
|
|
271
|
+
codecId: codecDescriptor.codecId,
|
|
340
272
|
owners,
|
|
341
273
|
descriptorId,
|
|
342
|
-
entityLabel: 'codec
|
|
343
|
-
entityOwnershipLabel: 'codec
|
|
274
|
+
entityLabel: 'codec descriptor',
|
|
275
|
+
entityOwnershipLabel: 'codec descriptor provider',
|
|
344
276
|
});
|
|
345
|
-
owners.set(
|
|
346
|
-
|
|
277
|
+
owners.set(codecDescriptor.codecId, descriptorId);
|
|
278
|
+
if (Array.isArray(codecDescriptor.targetTypes)) {
|
|
279
|
+
targetTypesById.set(codecDescriptor.codecId, codecDescriptor.targetTypes);
|
|
280
|
+
}
|
|
281
|
+
if (codecDescriptor.meta !== undefined) {
|
|
282
|
+
metaById.set(codecDescriptor.codecId, codecDescriptor.meta);
|
|
283
|
+
}
|
|
284
|
+
if (typeof codecDescriptor.renderOutputType === 'function') {
|
|
285
|
+
renderersById.set(codecDescriptor.codecId, codecDescriptor.renderOutputType);
|
|
286
|
+
}
|
|
287
|
+
// Materialize a representative `Codec` instance for `byId.get()` so consumers reading the lookup's instance side (e.g. SQL renderer's cast-policy lookup, or the contract emitter's literal-default `encodeJson` resolver) keep finding the codec.
|
|
288
|
+
//
|
|
289
|
+
// Two cohorts:
|
|
290
|
+
// - Non-parameterized descriptors: factory must succeed; any throw is a real bug and we let it propagate (no silent try/catch).
|
|
291
|
+
// - Parameterized descriptors: try with empty params. Many parameterized codecs treat params as advisory (e.g. `pg/timestamptz@1` whose precision is rendered into the `nativeType` only and never read by the runtime codec), so an empty-params construction yields a usable representative for id-keyed lookups (e.g. emit-time literal-default encoding). Codecs whose factory genuinely requires params (e.g. `pg/vector@1` threading `length` into the runtime codec) will throw; for those, per-column instances are materialized at runtime by `buildContractCodecRegistry` and the id-keyed lookup miss is correct (the column-aware path resolves them).
|
|
292
|
+
if (!byId.has(codecDescriptor.codecId)) {
|
|
293
|
+
if (codecDescriptor.isParameterized) {
|
|
294
|
+
try {
|
|
295
|
+
const representative = codecDescriptor.factory({} as never)({
|
|
296
|
+
name: `<lookup:${codecDescriptor.codecId}>`,
|
|
297
|
+
} as Parameters<ReturnType<typeof codecDescriptor.factory>>[0]);
|
|
298
|
+
byId.set(codecDescriptor.codecId, representative);
|
|
299
|
+
} catch {
|
|
300
|
+
// Factory requires concrete params; skip representative materialization. Per-column instances are built at runtime; id-keyed lookup miss is the correct outcome here.
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
const representative = codecDescriptor.factory(undefined as never)({
|
|
304
|
+
name: `<lookup:${codecDescriptor.codecId}>`,
|
|
305
|
+
} as Parameters<ReturnType<typeof codecDescriptor.factory>>[0]);
|
|
306
|
+
byId.set(codecDescriptor.codecId, representative);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
347
309
|
}
|
|
348
310
|
}
|
|
349
|
-
return {
|
|
311
|
+
return {
|
|
312
|
+
get: (id) => byId.get(id),
|
|
313
|
+
targetTypesFor: (id) => targetTypesById.get(id),
|
|
314
|
+
metaFor: (id) => metaById.get(id),
|
|
315
|
+
renderOutputTypeFor: (id, params) => renderersById.get(id)?.(params),
|
|
316
|
+
};
|
|
350
317
|
}
|
|
351
318
|
|
|
352
319
|
export function validateScalarTypeCodecIds(
|
|
@@ -382,7 +349,6 @@ export function createControlStack<TFamilyId extends string, TTargetId extends s
|
|
|
382
349
|
extensionPacks: extensionPacks as readonly ControlExtensionDescriptor<TFamilyId, TTargetId>[],
|
|
383
350
|
|
|
384
351
|
codecTypeImports: extractCodecTypeImports(allDescriptors),
|
|
385
|
-
operationTypeImports: extractOperationTypeImports(allDescriptors),
|
|
386
352
|
queryOperationTypeImports: extractQueryOperationTypeImports(allDescriptors),
|
|
387
353
|
extensionIds: extractComponentIds(family, target, adapter, extensionPacks),
|
|
388
354
|
codecLookup,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Contract, ContractModel } from '@prisma-next/contract/types';
|
|
2
|
+
import type { TypesImportSpec } from '../shared/types-import-spec';
|
|
3
|
+
|
|
4
|
+
export interface GenerateContractTypesOptions {
|
|
5
|
+
readonly queryOperationTypeImports?: ReadonlyArray<TypesImportSpec>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ValidationContext {
|
|
9
|
+
readonly codecTypeImports?: ReadonlyArray<TypesImportSpec>;
|
|
10
|
+
readonly extensionIds?: ReadonlyArray<string>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface EmissionSpi {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
|
|
16
|
+
generateStorageType(contract: Contract, storageHashTypeName: string): string;
|
|
17
|
+
|
|
18
|
+
generateModelStorageType(modelName: string, model: ContractModel): string;
|
|
19
|
+
|
|
20
|
+
getFamilyImports(): string[];
|
|
21
|
+
|
|
22
|
+
getFamilyTypeAliases(options?: GenerateContractTypesOptions): string;
|
|
23
|
+
|
|
24
|
+
getTypeMapsExpression(): string;
|
|
25
|
+
|
|
26
|
+
getContractWrapper(contractBaseName: string, typeMapsName: string): string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Per-family resolver for typeParams that don't live inline on the
|
|
30
|
+
* framework-domain `ContractField`. Some families (notably SQL) let columns
|
|
31
|
+
* reference a named entry in `storage.types` via `typeRef`; the typeParams
|
|
32
|
+
* live on that named entry rather than on the domain field. The framework
|
|
33
|
+
* emit path consults this hook so the codec's `renderOutputType` can run
|
|
34
|
+
* for typeRef-shaped columns.
|
|
35
|
+
*
|
|
36
|
+
* Inline `field.type.typeParams` always takes precedence; the hook is only
|
|
37
|
+
* consulted when the domain field has no typeParams. Families without
|
|
38
|
+
* named storage types (e.g. mongo) don't implement this hook.
|
|
39
|
+
*
|
|
40
|
+
* Returns `undefined` when the field has no resolvable typeParams.
|
|
41
|
+
*/
|
|
42
|
+
resolveFieldTypeParams?(
|
|
43
|
+
modelName: string,
|
|
44
|
+
fieldName: string,
|
|
45
|
+
model: ContractModel,
|
|
46
|
+
contract: Contract,
|
|
47
|
+
): Record<string, unknown> | undefined;
|
|
48
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
export interface PslPosition {
|
|
2
|
+
readonly offset: number;
|
|
3
|
+
readonly line: number;
|
|
4
|
+
readonly column: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface PslSpan {
|
|
8
|
+
readonly start: PslPosition;
|
|
9
|
+
readonly end: PslPosition;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type PslDiagnosticCode =
|
|
13
|
+
| 'PSL_UNTERMINATED_BLOCK'
|
|
14
|
+
| 'PSL_UNSUPPORTED_TOP_LEVEL_BLOCK'
|
|
15
|
+
| 'PSL_INVALID_ATTRIBUTE_SYNTAX'
|
|
16
|
+
| 'PSL_INVALID_MODEL_MEMBER'
|
|
17
|
+
| 'PSL_UNSUPPORTED_MODEL_ATTRIBUTE'
|
|
18
|
+
| 'PSL_UNSUPPORTED_FIELD_ATTRIBUTE'
|
|
19
|
+
| 'PSL_INVALID_RELATION_ATTRIBUTE'
|
|
20
|
+
| 'PSL_INVALID_REFERENTIAL_ACTION'
|
|
21
|
+
| 'PSL_INVALID_DEFAULT_VALUE'
|
|
22
|
+
| 'PSL_INVALID_ENUM_MEMBER'
|
|
23
|
+
| 'PSL_INVALID_TYPES_MEMBER';
|
|
24
|
+
|
|
25
|
+
export interface PslDiagnostic {
|
|
26
|
+
readonly code: PslDiagnosticCode;
|
|
27
|
+
readonly message: string;
|
|
28
|
+
readonly sourceId: string;
|
|
29
|
+
readonly span: PslSpan;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PslDefaultFunctionValue {
|
|
33
|
+
readonly kind: 'function';
|
|
34
|
+
readonly name: 'autoincrement' | 'now';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface PslDefaultLiteralValue {
|
|
38
|
+
readonly kind: 'literal';
|
|
39
|
+
readonly value: string | number | boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type PslDefaultValue = PslDefaultFunctionValue | PslDefaultLiteralValue;
|
|
43
|
+
|
|
44
|
+
export type PslAttributeTarget = 'field' | 'model' | 'enum' | 'namedType';
|
|
45
|
+
|
|
46
|
+
export interface PslAttributePositionalArgument {
|
|
47
|
+
readonly kind: 'positional';
|
|
48
|
+
readonly value: string;
|
|
49
|
+
readonly span: PslSpan;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface PslAttributeNamedArgument {
|
|
53
|
+
readonly kind: 'named';
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly value: string;
|
|
56
|
+
readonly span: PslSpan;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type PslAttributeArgument = PslAttributePositionalArgument | PslAttributeNamedArgument;
|
|
60
|
+
|
|
61
|
+
export interface PslTypeConstructorCall {
|
|
62
|
+
readonly kind: 'typeConstructor';
|
|
63
|
+
readonly path: readonly string[];
|
|
64
|
+
readonly args: readonly PslAttributeArgument[];
|
|
65
|
+
readonly span: PslSpan;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface PslAttribute {
|
|
69
|
+
readonly kind: 'attribute';
|
|
70
|
+
readonly target: PslAttributeTarget;
|
|
71
|
+
readonly name: string;
|
|
72
|
+
readonly args: readonly PslAttributeArgument[];
|
|
73
|
+
readonly span: PslSpan;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type PslReferentialAction = string;
|
|
77
|
+
|
|
78
|
+
export type PslFieldAttribute = PslAttribute;
|
|
79
|
+
|
|
80
|
+
export interface PslField {
|
|
81
|
+
readonly kind: 'field';
|
|
82
|
+
readonly name: string;
|
|
83
|
+
readonly typeName: string;
|
|
84
|
+
readonly typeConstructor?: PslTypeConstructorCall;
|
|
85
|
+
readonly optional: boolean;
|
|
86
|
+
readonly list: boolean;
|
|
87
|
+
readonly typeRef?: string;
|
|
88
|
+
readonly attributes: readonly PslFieldAttribute[];
|
|
89
|
+
readonly span: PslSpan;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface PslUniqueConstraint {
|
|
93
|
+
readonly kind: 'unique';
|
|
94
|
+
readonly fields: readonly string[];
|
|
95
|
+
readonly span: PslSpan;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface PslIndexConstraint {
|
|
99
|
+
readonly kind: 'index';
|
|
100
|
+
readonly fields: readonly string[];
|
|
101
|
+
readonly span: PslSpan;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type PslModelAttribute = PslAttribute;
|
|
105
|
+
|
|
106
|
+
export interface PslModel {
|
|
107
|
+
readonly kind: 'model';
|
|
108
|
+
readonly name: string;
|
|
109
|
+
readonly fields: readonly PslField[];
|
|
110
|
+
readonly attributes: readonly PslModelAttribute[];
|
|
111
|
+
readonly span: PslSpan;
|
|
112
|
+
/**
|
|
113
|
+
* Optional leading comment line emitted above the `model` keyword by the
|
|
114
|
+
* printer. Producers (e.g. `sqlSchemaIrToPslAst`) attach introspection
|
|
115
|
+
* advisories such as "// WARNING: This table has no primary key in the
|
|
116
|
+
* database" here. The parser leaves this field unset; round-tripping a
|
|
117
|
+
* parsed schema does not re-attach comments.
|
|
118
|
+
*/
|
|
119
|
+
readonly comment?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface PslEnumValue {
|
|
123
|
+
readonly kind: 'enumValue';
|
|
124
|
+
readonly name: string;
|
|
125
|
+
/**
|
|
126
|
+
* Optional storage label for the enum member, captured from a trailing
|
|
127
|
+
* `@map("...")` attribute on the member line. The parser populates this
|
|
128
|
+
* when the source PSL carries an explicit `@map`. Producers (e.g.
|
|
129
|
+
* `sqlSchemaIrToPslAst`) leave it unset; the printer emits `@map(...)`
|
|
130
|
+
* automatically when normalisation would change the printed member name
|
|
131
|
+
* (so an enum value `'in-progress'` becomes `inProgress @map("in-progress")`
|
|
132
|
+
* in PSL, preserving the round-trip).
|
|
133
|
+
*/
|
|
134
|
+
readonly mapName?: string;
|
|
135
|
+
readonly span: PslSpan;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface PslEnum {
|
|
139
|
+
readonly kind: 'enum';
|
|
140
|
+
readonly name: string;
|
|
141
|
+
readonly values: readonly PslEnumValue[];
|
|
142
|
+
readonly attributes: readonly PslAttribute[];
|
|
143
|
+
readonly span: PslSpan;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface PslCompositeType {
|
|
147
|
+
readonly kind: 'compositeType';
|
|
148
|
+
readonly name: string;
|
|
149
|
+
readonly fields: readonly PslField[];
|
|
150
|
+
readonly attributes: readonly PslAttribute[];
|
|
151
|
+
readonly span: PslSpan;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface PslNamedTypeDeclaration {
|
|
155
|
+
readonly kind: 'namedType';
|
|
156
|
+
readonly name: string;
|
|
157
|
+
/**
|
|
158
|
+
* Parser invariant: exactly one of `baseType` and `typeConstructor` is set.
|
|
159
|
+
* Expressing this as a discriminated union trips TypeScript narrowing when
|
|
160
|
+
* the declaration flows through helpers that accept the full union.
|
|
161
|
+
*/
|
|
162
|
+
readonly baseType?: string;
|
|
163
|
+
readonly typeConstructor?: PslTypeConstructorCall;
|
|
164
|
+
readonly attributes: readonly PslAttribute[];
|
|
165
|
+
readonly span: PslSpan;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface PslTypesBlock {
|
|
169
|
+
readonly kind: 'types';
|
|
170
|
+
readonly declarations: readonly PslNamedTypeDeclaration[];
|
|
171
|
+
readonly span: PslSpan;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface PslDocumentAst {
|
|
175
|
+
readonly kind: 'document';
|
|
176
|
+
readonly sourceId: string;
|
|
177
|
+
readonly models: readonly PslModel[];
|
|
178
|
+
readonly enums: readonly PslEnum[];
|
|
179
|
+
readonly compositeTypes: readonly PslCompositeType[];
|
|
180
|
+
readonly types?: PslTypesBlock;
|
|
181
|
+
readonly span: PslSpan;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface ParsePslDocumentInput {
|
|
185
|
+
readonly schema: string;
|
|
186
|
+
readonly sourceId: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface ParsePslDocumentResult {
|
|
190
|
+
readonly ast: PslDocumentAst;
|
|
191
|
+
readonly diagnostics: readonly PslDiagnostic[];
|
|
192
|
+
readonly ok: boolean;
|
|
193
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AdapterDescriptor,
|
|
3
|
+
DriverDescriptor,
|
|
4
|
+
ExtensionDescriptor,
|
|
5
|
+
FamilyDescriptor,
|
|
6
|
+
TargetDescriptor,
|
|
7
|
+
} from '../shared/framework-components';
|
|
1
8
|
import type {
|
|
2
9
|
RuntimeAdapterInstance,
|
|
3
10
|
RuntimeDriverInstance,
|
|
@@ -6,13 +13,6 @@ import type {
|
|
|
6
13
|
RuntimeTargetInstance,
|
|
7
14
|
} from './execution-instances';
|
|
8
15
|
import type { ExecutionStack } from './execution-stack';
|
|
9
|
-
import type {
|
|
10
|
-
AdapterDescriptor,
|
|
11
|
-
DriverDescriptor,
|
|
12
|
-
ExtensionDescriptor,
|
|
13
|
-
FamilyDescriptor,
|
|
14
|
-
TargetDescriptor,
|
|
15
|
-
} from './framework-components';
|
|
16
16
|
|
|
17
17
|
export interface RuntimeFamilyDescriptor<
|
|
18
18
|
TFamilyId extends string,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { checkContractComponentRequirements } from '../shared/framework-components';
|
|
1
2
|
import type {
|
|
2
3
|
RuntimeAdapterDescriptor,
|
|
3
4
|
RuntimeExtensionDescriptor,
|
|
4
5
|
RuntimeFamilyDescriptor,
|
|
5
6
|
RuntimeTargetDescriptor,
|
|
6
7
|
} from './execution-descriptors';
|
|
7
|
-
import { checkContractComponentRequirements } from './framework-components';
|
|
8
8
|
|
|
9
9
|
export function assertRuntimeContractRequirementsSatisfied<
|
|
10
10
|
TFamilyId extends string,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { RuntimeAbortedPhase } from './runtime-error';
|
|
2
|
+
import { runtimeAborted } from './runtime-error';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Throw a phase-tagged `RUNTIME.ABORTED` envelope if the supplied
|
|
6
|
+
* context is already aborted at the precheck site. Centralises the
|
|
7
|
+
* `if (ctx.signal?.aborted) throw runtimeAborted(...)` pattern that
|
|
8
|
+
* every codec dispatch site (and the `beforeExecute` middleware phase)
|
|
9
|
+
* repeats. Accepts both the framework `CodecCallContext` and the
|
|
10
|
+
* `RuntimeMiddlewareContext`; both expose `signal?: AbortSignal`.
|
|
11
|
+
*/
|
|
12
|
+
export function checkAborted(
|
|
13
|
+
ctx: { readonly signal?: AbortSignal },
|
|
14
|
+
phase: RuntimeAbortedPhase,
|
|
15
|
+
): void {
|
|
16
|
+
if (ctx.signal?.aborted) {
|
|
17
|
+
throw runtimeAborted(phase, ctx.signal.reason);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Race a per-cell `Promise.all` (or any other in-flight work promise) against
|
|
23
|
+
* the supplied abort signal so the runtime returns `RUNTIME.ABORTED` promptly
|
|
24
|
+
* even when codec bodies ignore the signal. In-flight bodies that ignore the
|
|
25
|
+
* signal are abandoned and run to completion in the background — the
|
|
26
|
+
* cooperative-cancellation contract documented in ADR 204.
|
|
27
|
+
*
|
|
28
|
+
* Call sites still SHOULD pre-check `signal.aborted` and short-circuit with
|
|
29
|
+
* a phase-tagged `RUNTIME.ABORTED` envelope before invoking this helper —
|
|
30
|
+
* that path is the canonical "aborted at entry" surface and avoids
|
|
31
|
+
* scheduling the work promise. As a defensive belt-and-braces, this helper
|
|
32
|
+
* also handles the already-aborted case internally: `AbortSignal` does not
|
|
33
|
+
* replay past abort events to listeners registered after the abort, so we
|
|
34
|
+
* inspect `signal.aborted` synchronously and reject with the sentinel
|
|
35
|
+
* before installing the listener. The rejection is still attributed to the
|
|
36
|
+
* abort path via the sentinel-identity check.
|
|
37
|
+
*
|
|
38
|
+
* Distinguishing the rejection source is load-bearing for AC-ERR4
|
|
39
|
+
* (`RUNTIME.ENCODE_FAILED` / `RUNTIME.DECODE_FAILED` pass through unchanged).
|
|
40
|
+
* The semantically equivalent `abortable(signal)` helper in
|
|
41
|
+
* `@prisma-next/utils` rejects with `signal.reason ?? new DOMException(...)`,
|
|
42
|
+
* which is not stably distinguishable from a codec-thrown error by identity
|
|
43
|
+
* alone (a fresh fallback DOMException is allocated per call). We instead
|
|
44
|
+
* track abort attribution with a unique sentinel: only the `onAbort` listener
|
|
45
|
+
* installed here ever rejects with the sentinel, so an `error === sentinel`
|
|
46
|
+
* identity check after the race is unambiguous.
|
|
47
|
+
*
|
|
48
|
+
* Lives in `framework-components` (rather than the SQL family, where it
|
|
49
|
+
* originated in m2) so every family runtime that needs cooperative
|
|
50
|
+
* cancellation around a codec-dispatch `Promise.all` (SQL encode + decode
|
|
51
|
+
* today, Mongo encode in m3) shares the same attribution logic.
|
|
52
|
+
*/
|
|
53
|
+
export async function raceAgainstAbort<T>(
|
|
54
|
+
work: Promise<T>,
|
|
55
|
+
signal: AbortSignal | undefined,
|
|
56
|
+
phase: RuntimeAbortedPhase,
|
|
57
|
+
): Promise<T> {
|
|
58
|
+
if (signal === undefined) {
|
|
59
|
+
return await work;
|
|
60
|
+
}
|
|
61
|
+
const sentinel: { reason: unknown } = { reason: undefined };
|
|
62
|
+
let onAbort: (() => void) | undefined;
|
|
63
|
+
|
|
64
|
+
const abortPromise = new Promise<never>((_, reject) => {
|
|
65
|
+
if (signal.aborted) {
|
|
66
|
+
sentinel.reason = signal.reason;
|
|
67
|
+
reject(sentinel);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
onAbort = () => {
|
|
71
|
+
sentinel.reason = signal.reason;
|
|
72
|
+
reject(sentinel);
|
|
73
|
+
};
|
|
74
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
return await Promise.race([work, abortPromise]);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error === sentinel) {
|
|
81
|
+
throw runtimeAborted(phase, sentinel.reason);
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
} finally {
|
|
85
|
+
if (onAbort) {
|
|
86
|
+
signal.removeEventListener('abort', onAbort);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|