@prisma-next/sql-contract-ts 0.14.0-dev.7 → 0.14.0-dev.71
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/dist/{build-contract-CQ4u83jx.mjs → build-contract-C07F2P2K.mjs} +238 -28
- package/dist/build-contract-C07F2P2K.mjs.map +1 -0
- package/dist/config-types.d.mts +2 -0
- package/dist/config-types.d.mts.map +1 -1
- package/dist/config-types.mjs +2 -1
- package/dist/config-types.mjs.map +1 -1
- package/dist/contract-builder.d.mts +248 -340
- package/dist/contract-builder.d.mts.map +1 -1
- package/dist/contract-builder.mjs +54 -86
- package/dist/contract-builder.mjs.map +1 -1
- package/package.json +14 -14
- package/src/authoring-helper-runtime.ts +2 -6
- package/src/authoring-type-utils.ts +6 -3
- package/src/build-contract.ts +402 -49
- package/src/composed-authoring-helpers.ts +3 -6
- package/src/config-types.ts +7 -1
- package/src/contract-builder.ts +71 -5
- package/src/contract-definition.ts +27 -4
- package/src/contract-dsl.ts +214 -107
- package/src/contract-lowering.ts +5 -1
- package/src/contract-types.ts +70 -13
- package/src/enum-type.ts +14 -306
- package/dist/build-contract-CQ4u83jx.mjs.map +0 -1
|
@@ -13,9 +13,6 @@ import type {
|
|
|
13
13
|
} from '@prisma-next/framework-components/authoring';
|
|
14
14
|
import {
|
|
15
15
|
assertNoCrossRegistryCollisions,
|
|
16
|
-
isAuthoringEntityTypeDescriptor,
|
|
17
|
-
isAuthoringFieldPresetDescriptor,
|
|
18
|
-
isAuthoringTypeConstructorDescriptor,
|
|
19
16
|
mergeAuthoringNamespaces,
|
|
20
17
|
} from '@prisma-next/framework-components/authoring';
|
|
21
18
|
import type {
|
|
@@ -183,7 +180,7 @@ function composeTypeNamespace(components: readonly AuthoringComponent[]): Author
|
|
|
183
180
|
for (const component of components) {
|
|
184
181
|
const ns = extractTypeNamespace(component);
|
|
185
182
|
if (Object.keys(ns).length > 0) {
|
|
186
|
-
mergeAuthoringNamespaces(merged, ns, [],
|
|
183
|
+
mergeAuthoringNamespaces(merged, ns, [], 'typeConstructor', 'type');
|
|
187
184
|
}
|
|
188
185
|
}
|
|
189
186
|
return merged as AuthoringTypeNamespace;
|
|
@@ -194,7 +191,7 @@ function composeFieldNamespace(components: readonly AuthoringComponent[]): Autho
|
|
|
194
191
|
for (const component of components) {
|
|
195
192
|
const ns = extractFieldNamespace(component);
|
|
196
193
|
if (Object.keys(ns).length > 0) {
|
|
197
|
-
mergeAuthoringNamespaces(merged, ns, [],
|
|
194
|
+
mergeAuthoringNamespaces(merged, ns, [], 'fieldPreset', 'field');
|
|
198
195
|
}
|
|
199
196
|
}
|
|
200
197
|
return merged as AuthoringFieldNamespace;
|
|
@@ -207,7 +204,7 @@ function composeEntityNamespace(
|
|
|
207
204
|
for (const component of components) {
|
|
208
205
|
const ns = extractEntitiesNamespace(component);
|
|
209
206
|
if (Object.keys(ns).length > 0) {
|
|
210
|
-
mergeAuthoringNamespaces(merged, ns, [],
|
|
207
|
+
mergeAuthoringNamespaces(merged, ns, [], 'entity', 'entity');
|
|
211
208
|
}
|
|
212
209
|
}
|
|
213
210
|
return merged as AuthoringEntityTypeNamespace;
|
package/src/config-types.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { ContractConfig } from '@prisma-next/config/config-types';
|
|
|
3
3
|
import { applySpecifierDefaultControlPolicy } from '@prisma-next/contract/apply-specifier-default-control-policy';
|
|
4
4
|
import type { Contract, ControlPolicy } from '@prisma-next/contract/types';
|
|
5
5
|
import type { TargetPackRef } from '@prisma-next/framework-components/components';
|
|
6
|
+
import type { SqlNamespaceBase, SqlNamespaceInput } from '@prisma-next/sql-contract/types';
|
|
6
7
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
7
8
|
import { ok } from '@prisma-next/utils/result';
|
|
8
9
|
import { extname } from 'pathe';
|
|
@@ -27,13 +28,18 @@ export interface TypeScriptContractSpecifierOptions {
|
|
|
27
28
|
export function emptyContract(options: {
|
|
28
29
|
readonly output?: string;
|
|
29
30
|
readonly target: TargetPackRef<'sql', string>;
|
|
31
|
+
readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
|
|
30
32
|
readonly defaultControlPolicy?: ControlPolicy;
|
|
31
33
|
}): ContractConfig {
|
|
32
34
|
return {
|
|
33
35
|
source: {
|
|
34
36
|
sourceFormat: 'typescript',
|
|
35
37
|
load: async () => {
|
|
36
|
-
const built = buildSqlContractFromDefinition({
|
|
38
|
+
const built = buildSqlContractFromDefinition({
|
|
39
|
+
target: options.target,
|
|
40
|
+
createNamespace: options.createNamespace,
|
|
41
|
+
models: [],
|
|
42
|
+
});
|
|
37
43
|
return ok(applySpecifierDefaultControlPolicy(built, options.defaultControlPolicy));
|
|
38
44
|
},
|
|
39
45
|
},
|
package/src/contract-builder.ts
CHANGED
|
@@ -6,8 +6,11 @@ import type {
|
|
|
6
6
|
FamilyPackRef,
|
|
7
7
|
TargetPackRef,
|
|
8
8
|
} from '@prisma-next/framework-components/components';
|
|
9
|
-
import type {
|
|
10
|
-
|
|
9
|
+
import type {
|
|
10
|
+
SqlNamespaceBase,
|
|
11
|
+
SqlNamespaceInput,
|
|
12
|
+
StorageTypeInstance,
|
|
13
|
+
} from '@prisma-next/sql-contract/types';
|
|
11
14
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
12
15
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
13
16
|
import { buildSqlContractFromDefinition } from './build-contract';
|
|
@@ -15,6 +18,7 @@ import {
|
|
|
15
18
|
type ComposedAuthoringHelpers,
|
|
16
19
|
createComposedAuthoringHelpers,
|
|
17
20
|
} from './composed-authoring-helpers';
|
|
21
|
+
import type { PackEntitiesInput } from './contract-definition';
|
|
18
22
|
import {
|
|
19
23
|
type ContractInput,
|
|
20
24
|
type ContractModelBuilder,
|
|
@@ -68,11 +72,12 @@ type ContractDefinition<
|
|
|
68
72
|
readonly foreignKeyDefaults?: ForeignKeyDefaults;
|
|
69
73
|
readonly defaultControlPolicy?: ControlPolicy;
|
|
70
74
|
readonly namespaces?: Namespaces;
|
|
71
|
-
readonly createNamespace
|
|
75
|
+
readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
|
|
72
76
|
readonly types?: Types;
|
|
73
77
|
readonly models?: Models;
|
|
74
78
|
readonly codecLookup?: CodecLookup;
|
|
75
79
|
readonly enums?: Enums;
|
|
80
|
+
readonly packEntities?: PackEntitiesInput;
|
|
76
81
|
};
|
|
77
82
|
|
|
78
83
|
type ContractScaffold<
|
|
@@ -93,11 +98,12 @@ type ContractScaffold<
|
|
|
93
98
|
readonly foreignKeyDefaults?: ForeignKeyDefaults;
|
|
94
99
|
readonly defaultControlPolicy?: ControlPolicy;
|
|
95
100
|
readonly namespaces?: Namespaces;
|
|
96
|
-
readonly createNamespace
|
|
101
|
+
readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
|
|
97
102
|
readonly types?: never;
|
|
98
103
|
readonly models?: never;
|
|
99
104
|
readonly codecLookup?: CodecLookup;
|
|
100
105
|
readonly enums?: Enums;
|
|
106
|
+
readonly packEntities?: PackEntitiesInput;
|
|
101
107
|
};
|
|
102
108
|
|
|
103
109
|
type ContractFactory<
|
|
@@ -111,6 +117,7 @@ type ContractFactory<
|
|
|
111
117
|
readonly types?: Types;
|
|
112
118
|
readonly models?: Models;
|
|
113
119
|
readonly enums?: Enums;
|
|
120
|
+
readonly packEntities?: PackEntitiesInput;
|
|
114
121
|
};
|
|
115
122
|
|
|
116
123
|
function validateTargetPackRef(
|
|
@@ -314,11 +321,12 @@ type BoundDefinitionInput<
|
|
|
314
321
|
readonly foreignKeyDefaults?: ForeignKeyDefaults;
|
|
315
322
|
readonly defaultControlPolicy?: ControlPolicy;
|
|
316
323
|
readonly namespaces?: Namespaces;
|
|
317
|
-
readonly createNamespace
|
|
324
|
+
readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
|
|
318
325
|
readonly types?: Types;
|
|
319
326
|
readonly models?: Models;
|
|
320
327
|
readonly codecLookup?: CodecLookup;
|
|
321
328
|
readonly enums?: Record<string, EnumTypeHandle>;
|
|
329
|
+
readonly packEntities?: PackEntitiesInput;
|
|
322
330
|
};
|
|
323
331
|
|
|
324
332
|
// A bare `Record<string, EnumTypeHandle>` (no literal keys) is the widened
|
|
@@ -342,6 +350,60 @@ type WithFamilyTarget<
|
|
|
342
350
|
T extends TargetPackRef<'sql', string>,
|
|
343
351
|
> = Input & { readonly family: F; readonly target: T };
|
|
344
352
|
|
|
353
|
+
// Deep-merges packEntities authored on the scaffold definition with those
|
|
354
|
+
// returned from the factory callback — three levels deep (namespace, kind,
|
|
355
|
+
// name), unlike `enums`' flat shallow merge, since a factory-declared
|
|
356
|
+
// namespace/kind must not silently drop a scaffold-declared sibling entry
|
|
357
|
+
// under the same namespace or kind. A same-name key present on both sides is
|
|
358
|
+
// only merged when it is the identical entity instance; two *different*
|
|
359
|
+
// entities of the same namespace/kind/name is a collision (the emitted
|
|
360
|
+
// entry could reflect only one), rejected here the same way
|
|
361
|
+
// `mergeCollectedPackEntities` in `build-contract.ts` rejects it.
|
|
362
|
+
function mergePackEntityNames(
|
|
363
|
+
namespaceId: string,
|
|
364
|
+
kind: string,
|
|
365
|
+
a: Readonly<Record<string, unknown>> | undefined,
|
|
366
|
+
b: Readonly<Record<string, unknown>> | undefined,
|
|
367
|
+
): Readonly<Record<string, unknown>> {
|
|
368
|
+
if (a !== undefined && b !== undefined) {
|
|
369
|
+
for (const [name, entity] of Object.entries(b)) {
|
|
370
|
+
const existing = a[name];
|
|
371
|
+
if (existing !== undefined && existing !== entity) {
|
|
372
|
+
throw new Error(
|
|
373
|
+
`defineContract: two different "${kind}" entities named "${name}" in namespace "${namespaceId}" — a factory-returned pack entity conflicts with a scaffold-declared one; pack-entity names must be unique per namespace.`,
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return { ...(a ?? {}), ...(b ?? {}) };
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function mergePackEntityKinds(
|
|
382
|
+
namespaceId: string,
|
|
383
|
+
a: Readonly<Record<string, Readonly<Record<string, unknown>>>> | undefined,
|
|
384
|
+
b: Readonly<Record<string, Readonly<Record<string, unknown>>>> | undefined,
|
|
385
|
+
): Readonly<Record<string, Readonly<Record<string, unknown>>>> {
|
|
386
|
+
const kinds = new Set([...Object.keys(a ?? {}), ...Object.keys(b ?? {})]);
|
|
387
|
+
const result: Record<string, Readonly<Record<string, unknown>>> = {};
|
|
388
|
+
for (const kind of kinds) {
|
|
389
|
+
result[kind] = mergePackEntityNames(namespaceId, kind, a?.[kind], b?.[kind]);
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function mergePackEntities(
|
|
395
|
+
a: PackEntitiesInput | undefined,
|
|
396
|
+
b: PackEntitiesInput | undefined,
|
|
397
|
+
): PackEntitiesInput | undefined {
|
|
398
|
+
if (a === undefined && b === undefined) return undefined;
|
|
399
|
+
const namespaces = new Set([...Object.keys(a ?? {}), ...Object.keys(b ?? {})]);
|
|
400
|
+
const result: Record<string, Readonly<Record<string, Readonly<Record<string, unknown>>>>> = {};
|
|
401
|
+
for (const namespaceId of namespaces) {
|
|
402
|
+
result[namespaceId] = mergePackEntityKinds(namespaceId, a?.[namespaceId], b?.[namespaceId]);
|
|
403
|
+
}
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
|
|
345
407
|
/**
|
|
346
408
|
* Shared builder that assembles a SqlContract with pre-bound family and target.
|
|
347
409
|
* Extension wrappers keep their own public overloads and delegate their impl body here;
|
|
@@ -387,6 +449,7 @@ export function buildBoundContract<
|
|
|
387
449
|
readonly types?: Record<string, StorageTypeInstance>;
|
|
388
450
|
readonly models?: Record<string, ModelLike>;
|
|
389
451
|
readonly enums?: Record<string, EnumTypeHandle>;
|
|
452
|
+
readonly packEntities?: PackEntitiesInput;
|
|
390
453
|
},
|
|
391
454
|
>(
|
|
392
455
|
family: F,
|
|
@@ -412,6 +475,7 @@ export function buildBoundContract(
|
|
|
412
475
|
readonly types?: Record<string, StorageTypeInstance>;
|
|
413
476
|
readonly models?: Record<string, ModelLike>;
|
|
414
477
|
readonly enums?: Record<string, EnumTypeHandle>;
|
|
478
|
+
readonly packEntities?: PackEntitiesInput;
|
|
415
479
|
})
|
|
416
480
|
| undefined,
|
|
417
481
|
) {
|
|
@@ -426,11 +490,13 @@ export function buildBoundContract(
|
|
|
426
490
|
}),
|
|
427
491
|
);
|
|
428
492
|
const mergedEnums = { ...(definition.enums ?? {}), ...built.enums };
|
|
493
|
+
const mergedPackEntities = mergePackEntities(definition.packEntities, built.packEntities);
|
|
429
494
|
return buildContractFromDsl({
|
|
430
495
|
...full,
|
|
431
496
|
...ifDefined('types', built.types),
|
|
432
497
|
...ifDefined('models', built.models),
|
|
433
498
|
...ifDefined('enums', Object.keys(mergedEnums).length > 0 ? mergedEnums : undefined),
|
|
499
|
+
...ifDefined('packEntities', mergedPackEntities),
|
|
434
500
|
});
|
|
435
501
|
}
|
|
436
502
|
|
|
@@ -6,16 +6,30 @@ import type {
|
|
|
6
6
|
import type { ForeignKeyDefaultsState } from '@prisma-next/contract-authoring';
|
|
7
7
|
import type { ColumnTypeDescriptor } from '@prisma-next/framework-components/codec';
|
|
8
8
|
import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';
|
|
9
|
-
import type { Namespace } from '@prisma-next/framework-components/ir';
|
|
10
9
|
import type {
|
|
11
10
|
ReferentialAction,
|
|
12
|
-
|
|
11
|
+
SqlNamespaceBase,
|
|
12
|
+
SqlNamespaceInput,
|
|
13
13
|
StorageTypeInstance,
|
|
14
14
|
} from '@prisma-next/sql-contract/types';
|
|
15
15
|
import type { EnumTypeHandle } from './enum-type';
|
|
16
16
|
|
|
17
17
|
export type { ExecutionMutationDefaultPhases };
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* An author-declared, namespace-scoped pack entity attachment: namespace id →
|
|
21
|
+
* entity kind (the discriminator the target/extension pack registered its
|
|
22
|
+
* `AuthoringContributions.entityTypes` descriptor under, e.g. `native_enum`)
|
|
23
|
+
* → entity name → the lowered entity instance. Generic on purpose — neither
|
|
24
|
+
* the framework nor `contract-ts` names a specific entity kind here; the
|
|
25
|
+
* shape mirrors `SqlNamespaceInput.entries` (`entries.<kind>[name]`), just
|
|
26
|
+
* namespace-nested so an author can target any declared namespace (default
|
|
27
|
+
* or named), not only the contract's default namespace.
|
|
28
|
+
*/
|
|
29
|
+
export type PackEntitiesInput = Readonly<
|
|
30
|
+
Record<string, Readonly<Record<string, Readonly<Record<string, unknown>>>>>
|
|
31
|
+
>;
|
|
32
|
+
|
|
19
33
|
export interface FieldNode {
|
|
20
34
|
readonly fieldName: string;
|
|
21
35
|
readonly columnName: string;
|
|
@@ -175,8 +189,8 @@ export interface ContractDefinition {
|
|
|
175
189
|
* `SqlStorage.namespaces` together with `createNamespace`.
|
|
176
190
|
*/
|
|
177
191
|
readonly namespaces?: readonly string[];
|
|
178
|
-
/** Target-supplied factory that materialises a `
|
|
179
|
-
readonly createNamespace
|
|
192
|
+
/** Target-supplied factory that materialises a `SqlNamespaceBase` concretion for a declared namespace coordinate. */
|
|
193
|
+
readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
|
|
180
194
|
readonly models: readonly ModelNode[];
|
|
181
195
|
readonly valueObjects?: readonly ValueObjectNode[];
|
|
182
196
|
/**
|
|
@@ -185,4 +199,13 @@ export interface ContractDefinition {
|
|
|
185
199
|
* default namespace.
|
|
186
200
|
*/
|
|
187
201
|
readonly enums?: Record<string, EnumTypeHandle>;
|
|
202
|
+
/**
|
|
203
|
+
* Author-declared pack entities, keyed by namespace then entity kind then
|
|
204
|
+
* name. Each entity lowers into `storage.namespaces[ns].entries.<kind>`;
|
|
205
|
+
* when the registered entity-type descriptor's factory output implements
|
|
206
|
+
* the `SqlValueSetDerivingEntityTypeOutput.deriveValueSet` hook, the
|
|
207
|
+
* derived value-set also folds into `entries.valueSet`, mirroring how
|
|
208
|
+
* `enums` flows there.
|
|
209
|
+
*/
|
|
210
|
+
readonly packEntities?: PackEntitiesInput;
|
|
188
211
|
}
|