@prisma-next/mongo-contract-ts 0.14.0-dev.23 → 0.14.0-dev.24
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/contract-builder.d.mts +67 -16
- package/dist/contract-builder.d.mts.map +1 -1
- package/dist/contract-builder.mjs +50 -14
- package/dist/contract-builder.mjs.map +1 -1
- package/package.json +10 -10
- package/src/contract-builder.ts +271 -26
- package/src/enum-type.ts +14 -0
- package/src/exports/contract-builder.ts +3 -0
package/src/contract-builder.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { computeProfileHash, computeStorageHash } from '@prisma-next/contract/hashing';
|
|
2
2
|
import {
|
|
3
3
|
type ContractEmbedRelation,
|
|
4
|
+
type ContractEnum,
|
|
4
5
|
type ContractField,
|
|
5
6
|
type ContractFieldType,
|
|
6
7
|
type ContractModelBase,
|
|
@@ -9,8 +10,10 @@ import {
|
|
|
9
10
|
type ControlPolicy,
|
|
10
11
|
type CrossReference,
|
|
11
12
|
crossRef,
|
|
13
|
+
type JsonValue,
|
|
12
14
|
type ProfileHashBase,
|
|
13
15
|
type StorageHashBase,
|
|
16
|
+
type ValueSetRef,
|
|
14
17
|
} from '@prisma-next/contract/types';
|
|
15
18
|
import {
|
|
16
19
|
createEntityHelpersFromNamespace,
|
|
@@ -53,6 +56,7 @@ import { mongoContractCanonicalizationHooks } from '@prisma-next/mongo-contract/
|
|
|
53
56
|
import { canonicalStringify } from '@prisma-next/utils/canonical-stringify';
|
|
54
57
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
55
58
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
59
|
+
import type { EnumTypeHandle } from './enum-type';
|
|
56
60
|
|
|
57
61
|
// `canonicalStringify` rejects non-plain objects so a `Map` or class
|
|
58
62
|
// instance cannot silently collapse to `{}`. The storage-shape values
|
|
@@ -139,13 +143,15 @@ export interface FieldBuilder<
|
|
|
139
143
|
Type extends ContractFieldType = ContractFieldType,
|
|
140
144
|
Nullable extends boolean = boolean,
|
|
141
145
|
Many extends boolean = boolean,
|
|
146
|
+
Handle extends EnumTypeHandle | undefined = EnumTypeHandle | undefined,
|
|
142
147
|
> {
|
|
143
148
|
readonly __kind: 'field';
|
|
144
149
|
readonly __type: Type;
|
|
145
150
|
readonly __nullable: Nullable;
|
|
146
151
|
readonly __many: Many;
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
readonly __enumHandle: Handle;
|
|
153
|
+
optional(): FieldBuilder<Type, true, Many, Handle>;
|
|
154
|
+
many(): FieldBuilder<Type, Nullable, true, Handle>;
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
export interface ValueObjectBuilder<
|
|
@@ -220,7 +226,12 @@ export interface ModelBuilder<
|
|
|
220
226
|
): FieldReference<Name, FieldName>;
|
|
221
227
|
}
|
|
222
228
|
|
|
223
|
-
type AnyFieldBuilder = FieldBuilder<
|
|
229
|
+
type AnyFieldBuilder = FieldBuilder<
|
|
230
|
+
ContractFieldType,
|
|
231
|
+
boolean,
|
|
232
|
+
boolean,
|
|
233
|
+
EnumTypeHandle | undefined
|
|
234
|
+
>;
|
|
224
235
|
type AnyReferenceRelationBuilder = RelationBuilder<string, '1:1' | '1:N' | 'N:1', RelationOn>;
|
|
225
236
|
type AnyEmbedRelationBuilder = RelationBuilder<string, '1:1' | '1:N', undefined>;
|
|
226
237
|
type AnyRelationBuilder = AnyReferenceRelationBuilder | AnyEmbedRelationBuilder;
|
|
@@ -486,6 +497,34 @@ type DefinitionValueObjects<Definition> = Definition extends {
|
|
|
486
497
|
? ValueObjects
|
|
487
498
|
: Record<never, never>;
|
|
488
499
|
|
|
500
|
+
type DefinitionEnums<Definition> = Definition extends {
|
|
501
|
+
readonly enums?: infer E;
|
|
502
|
+
}
|
|
503
|
+
? Present<E> extends Record<string, EnumTypeHandle>
|
|
504
|
+
? string extends keyof Present<E>
|
|
505
|
+
? Record<never, never>
|
|
506
|
+
: Present<E>
|
|
507
|
+
: Record<never, never>
|
|
508
|
+
: Record<never, never>;
|
|
509
|
+
|
|
510
|
+
type EnumHandleAccessorType<Handle> =
|
|
511
|
+
Handle extends EnumTypeHandle<infer _Name, infer Values, infer Names, infer MembersMap>
|
|
512
|
+
? {
|
|
513
|
+
readonly values: Values;
|
|
514
|
+
readonly names: Names;
|
|
515
|
+
readonly members: MembersMap;
|
|
516
|
+
has(v: Values[number]): boolean;
|
|
517
|
+
nameOf(v: Values[number]): string | undefined;
|
|
518
|
+
ordinalOf(v: Values[number]): number;
|
|
519
|
+
}
|
|
520
|
+
: never;
|
|
521
|
+
|
|
522
|
+
type BuiltEnumAccessors<Definition> = {
|
|
523
|
+
readonly [K in keyof DefinitionEnums<Definition>]: EnumHandleAccessorType<
|
|
524
|
+
DefinitionEnums<Definition>[K]
|
|
525
|
+
>;
|
|
526
|
+
};
|
|
527
|
+
|
|
489
528
|
type DefinitionRoots<Definition> = Definition extends {
|
|
490
529
|
readonly roots?: infer Roots extends Record<string, ModelNameInput>;
|
|
491
530
|
}
|
|
@@ -527,10 +566,34 @@ type MaybeValueObjectsSection<ValueObjects extends Record<string, AnyValueObject
|
|
|
527
566
|
readonly valueObjects: ContractValueObjectsFromRecord<ValueObjects>;
|
|
528
567
|
};
|
|
529
568
|
|
|
569
|
+
// Project EnumTypeHandle to the namespace enum-entry shape.
|
|
570
|
+
// Uses enumMembers (which carries Values[number] literals) rather than
|
|
571
|
+
// ContractEnum.members (which uses JsonValue and erases literals).
|
|
572
|
+
type EnumHandleToEntry<Handle> =
|
|
573
|
+
Handle extends EnumTypeHandle<string, infer Values, infer _Names, infer _MembersMap>
|
|
574
|
+
? {
|
|
575
|
+
readonly codecId: string;
|
|
576
|
+
readonly members: readonly { readonly name: string; readonly value: Values[number] }[];
|
|
577
|
+
}
|
|
578
|
+
: never;
|
|
579
|
+
|
|
580
|
+
type ContractEnumsFromRecord<Enums extends Record<string, EnumTypeHandle>> = {
|
|
581
|
+
readonly [K in keyof Enums as Enums[K] extends EnumTypeHandle<infer Name>
|
|
582
|
+
? Name
|
|
583
|
+
: never]: EnumHandleToEntry<Enums[K]>;
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
type MaybeEnumsSection<Enums extends Record<string, EnumTypeHandle>> = keyof Enums extends never
|
|
587
|
+
? EmptyObject
|
|
588
|
+
: {
|
|
589
|
+
readonly enum: ContractEnumsFromRecord<Enums>;
|
|
590
|
+
};
|
|
591
|
+
|
|
530
592
|
type MongoDomainNamespaceFromDefinition<Definition> = Simplify<
|
|
531
593
|
{
|
|
532
594
|
readonly models: ContractModelsFromRecord<DefinitionModels<Definition>>;
|
|
533
|
-
} & MaybeValueObjectsSection<DefinitionValueObjects<Definition>>
|
|
595
|
+
} & MaybeValueObjectsSection<DefinitionValueObjects<Definition>> &
|
|
596
|
+
MaybeEnumsSection<DefinitionEnums<Definition>>
|
|
534
597
|
>;
|
|
535
598
|
|
|
536
599
|
type MongoContractBaseFromDefinition<Definition> = Simplify<{
|
|
@@ -548,14 +611,133 @@ type MongoContractBaseFromDefinition<Definition> = Simplify<{
|
|
|
548
611
|
readonly profileHash: ProfileHashBase<string>;
|
|
549
612
|
readonly meta: Record<string, never>;
|
|
550
613
|
readonly defaultControlPolicy?: ControlPolicy;
|
|
614
|
+
readonly enumAccessors?: BuiltEnumAccessors<Definition>;
|
|
551
615
|
}>;
|
|
552
616
|
|
|
553
617
|
type CodecTypesFromDefinition<Definition> = MongoCodecTypes &
|
|
554
618
|
MergeExtensionCodecTypesSafe<DefinitionExtensionPacks<Definition>>;
|
|
555
619
|
|
|
620
|
+
// The enum value union for a field builder — `EnumTypeHandle['values'][number]`
|
|
621
|
+
// when the builder carries an enum handle, `never` otherwise.
|
|
622
|
+
type BuilderEnumValueUnion<TBuilder> =
|
|
623
|
+
TBuilder extends FieldBuilder<
|
|
624
|
+
ContractFieldType,
|
|
625
|
+
boolean,
|
|
626
|
+
boolean,
|
|
627
|
+
infer Handle extends EnumTypeHandle | undefined
|
|
628
|
+
>
|
|
629
|
+
? [Handle] extends [EnumTypeHandle<string, infer Values>]
|
|
630
|
+
? readonly unknown[] extends Values
|
|
631
|
+
? never
|
|
632
|
+
: Values[number]
|
|
633
|
+
: never
|
|
634
|
+
: never;
|
|
635
|
+
|
|
636
|
+
// The base codec/enum/value-object type for a builder field on a given channel,
|
|
637
|
+
// before nullable/many modifiers. Enum fields resolve to the value union on both
|
|
638
|
+
// channels; scalar fields resolve to the codec's channel-specific type.
|
|
639
|
+
type BuilderBaseChannelType<
|
|
640
|
+
TBuilder,
|
|
641
|
+
TValueObjects extends Record<string, AnyValueObjectBuilder>,
|
|
642
|
+
TCodecTypes extends Record<string, { output: unknown; input: unknown }>,
|
|
643
|
+
Channel extends 'output' | 'input',
|
|
644
|
+
> =
|
|
645
|
+
TBuilder extends FieldBuilder<
|
|
646
|
+
infer Type extends ContractFieldType,
|
|
647
|
+
boolean,
|
|
648
|
+
boolean,
|
|
649
|
+
EnumTypeHandle | undefined
|
|
650
|
+
>
|
|
651
|
+
? [BuilderEnumValueUnion<TBuilder>] extends [never]
|
|
652
|
+
? Type extends {
|
|
653
|
+
readonly kind: 'scalar';
|
|
654
|
+
readonly codecId: infer CId extends keyof TCodecTypes;
|
|
655
|
+
}
|
|
656
|
+
? TCodecTypes[CId][Channel]
|
|
657
|
+
: Type extends { readonly kind: 'valueObject'; readonly name: infer VOName extends string }
|
|
658
|
+
? VOName extends keyof TValueObjects
|
|
659
|
+
? {
|
|
660
|
+
-readonly [K in keyof ExtractValueObjectFields<
|
|
661
|
+
TValueObjects[VOName]
|
|
662
|
+
>]: BuilderFieldChannelType<
|
|
663
|
+
ExtractValueObjectFields<TValueObjects[VOName]>[K],
|
|
664
|
+
TValueObjects,
|
|
665
|
+
TCodecTypes,
|
|
666
|
+
Channel
|
|
667
|
+
>;
|
|
668
|
+
}
|
|
669
|
+
: unknown
|
|
670
|
+
: unknown
|
|
671
|
+
: BuilderEnumValueUnion<TBuilder>
|
|
672
|
+
: never;
|
|
673
|
+
|
|
674
|
+
type ExtractValueObjectFields<TBuilder> =
|
|
675
|
+
TBuilder extends NamedValueObjectBuilder<string, infer Fields> ? Fields : Record<never, never>;
|
|
676
|
+
|
|
677
|
+
// Runs once per `defineContract` call to build the precomputed `FieldOutputTypes`/`FieldInputTypes`
|
|
678
|
+
// maps. Consumers index those maps in O(1) via `InferModelRow` — this is NOT re-evaluated per query.
|
|
679
|
+
// Recursion is bounded to value-object nesting depth (each level resolves its fields exactly once).
|
|
680
|
+
//
|
|
681
|
+
// The JS type for one field builder on a given channel, with nullable/many applied.
|
|
682
|
+
// Compose many first (array wrapping), then add nullability. This avoids the
|
|
683
|
+
// TypeScript operator-precedence trap where `A | B extends infer X` infers X
|
|
684
|
+
// only from B, not from `A | B`.
|
|
685
|
+
type BuilderFieldChannelType<
|
|
686
|
+
TBuilder,
|
|
687
|
+
TValueObjects extends Record<string, AnyValueObjectBuilder>,
|
|
688
|
+
TCodecTypes extends Record<string, { output: unknown; input: unknown }>,
|
|
689
|
+
Channel extends 'output' | 'input',
|
|
690
|
+
> =
|
|
691
|
+
TBuilder extends FieldBuilder<
|
|
692
|
+
ContractFieldType,
|
|
693
|
+
infer Nullable extends boolean,
|
|
694
|
+
infer Many extends boolean,
|
|
695
|
+
EnumTypeHandle | undefined
|
|
696
|
+
>
|
|
697
|
+
?
|
|
698
|
+
| (Many extends true
|
|
699
|
+
? BuilderBaseChannelType<TBuilder, TValueObjects, TCodecTypes, Channel>[]
|
|
700
|
+
: BuilderBaseChannelType<TBuilder, TValueObjects, TCodecTypes, Channel>)
|
|
701
|
+
| (Nullable extends true ? null : never)
|
|
702
|
+
: never;
|
|
703
|
+
|
|
704
|
+
type ExtractModelFields<TBuilder> =
|
|
705
|
+
TBuilder extends NamedModelBuilder<string, infer Fields> ? Fields : Record<never, never>;
|
|
706
|
+
|
|
707
|
+
type FieldChannelTypesFromDefinition<Definition, Channel extends 'output' | 'input'> = {
|
|
708
|
+
readonly [K in typeof UNBOUND_NAMESPACE_ID]: {
|
|
709
|
+
readonly [ModelKey in keyof DefinitionModels<Definition> as ExtractModelName<
|
|
710
|
+
DefinitionModels<Definition>[ModelKey]
|
|
711
|
+
>]: {
|
|
712
|
+
readonly [FieldName in keyof ExtractModelFields<
|
|
713
|
+
DefinitionModels<Definition>[ModelKey]
|
|
714
|
+
>]: BuilderFieldChannelType<
|
|
715
|
+
ExtractModelFields<DefinitionModels<Definition>[ModelKey]>[FieldName],
|
|
716
|
+
DefinitionValueObjects<Definition>,
|
|
717
|
+
CodecTypesFromDefinition<Definition>,
|
|
718
|
+
Channel
|
|
719
|
+
>;
|
|
720
|
+
};
|
|
721
|
+
};
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
type FieldOutputTypesFromDefinition<Definition> = FieldChannelTypesFromDefinition<
|
|
725
|
+
Definition,
|
|
726
|
+
'output'
|
|
727
|
+
>;
|
|
728
|
+
|
|
729
|
+
type FieldInputTypesFromDefinition<Definition> = FieldChannelTypesFromDefinition<
|
|
730
|
+
Definition,
|
|
731
|
+
'input'
|
|
732
|
+
>;
|
|
733
|
+
|
|
556
734
|
export type MongoContractResult<Definition> = MongoContractWithTypeMaps<
|
|
557
735
|
MongoContractBaseFromDefinition<Definition>,
|
|
558
|
-
MongoTypeMaps<
|
|
736
|
+
MongoTypeMaps<
|
|
737
|
+
CodecTypesFromDefinition<Definition>,
|
|
738
|
+
FieldOutputTypesFromDefinition<Definition>,
|
|
739
|
+
FieldInputTypesFromDefinition<Definition>
|
|
740
|
+
>
|
|
559
741
|
>;
|
|
560
742
|
|
|
561
743
|
type ExtractEntitiesNamespaceFromPack<Pack> = ExtractAuthoringNamespaceFromPack<
|
|
@@ -663,6 +845,7 @@ export type ContractDefinition<
|
|
|
663
845
|
> = ContractScaffold<Family, Target, ExtensionPacks, Roots> & {
|
|
664
846
|
readonly models?: Models;
|
|
665
847
|
readonly valueObjects?: ValueObjects;
|
|
848
|
+
readonly enums?: Record<string, EnumTypeHandle>;
|
|
666
849
|
};
|
|
667
850
|
|
|
668
851
|
export type ContractFactory<
|
|
@@ -692,25 +875,31 @@ function createFieldBuilder<
|
|
|
692
875
|
Type extends ContractFieldType,
|
|
693
876
|
Nullable extends boolean,
|
|
694
877
|
Many extends boolean,
|
|
695
|
-
|
|
878
|
+
Handle extends EnumTypeHandle | undefined = undefined,
|
|
879
|
+
>(
|
|
880
|
+
spec: FieldBuilderSpec<Type, Nullable, Many>,
|
|
881
|
+
enumHandle?: Handle,
|
|
882
|
+
): FieldBuilder<Type, Nullable, Many, Handle> {
|
|
696
883
|
return {
|
|
697
884
|
__kind: 'field',
|
|
698
885
|
__type: spec.type,
|
|
699
886
|
__nullable: spec.nullable,
|
|
700
887
|
__many: spec.many,
|
|
888
|
+
__enumHandle: blindCast<
|
|
889
|
+
Handle,
|
|
890
|
+
'optional param widens to Handle | undefined; Handle defaults to undefined when no enum handle is passed'
|
|
891
|
+
>(enumHandle),
|
|
701
892
|
optional() {
|
|
702
|
-
return createFieldBuilder<Type, true, Many>(
|
|
703
|
-
type: spec.type,
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
});
|
|
893
|
+
return createFieldBuilder<Type, true, Many, Handle>(
|
|
894
|
+
{ type: spec.type, nullable: true, many: spec.many },
|
|
895
|
+
enumHandle,
|
|
896
|
+
);
|
|
707
897
|
},
|
|
708
898
|
many() {
|
|
709
|
-
return createFieldBuilder<Type, Nullable, true>(
|
|
710
|
-
type: spec.type,
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
});
|
|
899
|
+
return createFieldBuilder<Type, Nullable, true, Handle>(
|
|
900
|
+
{ type: spec.type, nullable: spec.nullable, many: true },
|
|
901
|
+
enumHandle,
|
|
902
|
+
);
|
|
714
903
|
},
|
|
715
904
|
};
|
|
716
905
|
}
|
|
@@ -791,6 +980,22 @@ export const field = {
|
|
|
791
980
|
many: false,
|
|
792
981
|
});
|
|
793
982
|
},
|
|
983
|
+
namedType<const Handle extends EnumTypeHandle>(handle: Handle) {
|
|
984
|
+
return createFieldBuilder(
|
|
985
|
+
{
|
|
986
|
+
type: blindCast<
|
|
987
|
+
{ readonly kind: 'scalar'; readonly codecId: Handle['codecId'] },
|
|
988
|
+
'literal narrowing: kind is inferred as string without the cast'
|
|
989
|
+
>({
|
|
990
|
+
kind: 'scalar',
|
|
991
|
+
codecId: handle.codecId,
|
|
992
|
+
}),
|
|
993
|
+
nullable: false,
|
|
994
|
+
many: false,
|
|
995
|
+
},
|
|
996
|
+
handle,
|
|
997
|
+
);
|
|
998
|
+
},
|
|
794
999
|
} as const;
|
|
795
1000
|
|
|
796
1001
|
export function index<const Fields extends MongoIndexFields>(
|
|
@@ -1193,15 +1398,26 @@ function isContractScaffold(
|
|
|
1193
1398
|
}
|
|
1194
1399
|
|
|
1195
1400
|
function buildContractField(builder: AnyFieldBuilder): ContractField {
|
|
1401
|
+
const valueSet: ValueSetRef | undefined = builder.__enumHandle
|
|
1402
|
+
? {
|
|
1403
|
+
plane: 'domain',
|
|
1404
|
+
entityKind: 'enum',
|
|
1405
|
+
namespaceId: UNBOUND_NAMESPACE_ID,
|
|
1406
|
+
entityName: builder.__enumHandle.enumName,
|
|
1407
|
+
}
|
|
1408
|
+
: undefined;
|
|
1409
|
+
|
|
1196
1410
|
return builder.__many
|
|
1197
1411
|
? {
|
|
1198
1412
|
type: builder.__type,
|
|
1199
1413
|
nullable: builder.__nullable,
|
|
1200
1414
|
many: true,
|
|
1415
|
+
...ifDefined('valueSet', valueSet),
|
|
1201
1416
|
}
|
|
1202
1417
|
: {
|
|
1203
1418
|
type: builder.__type,
|
|
1204
1419
|
nullable: builder.__nullable,
|
|
1420
|
+
...ifDefined('valueSet', valueSet),
|
|
1205
1421
|
};
|
|
1206
1422
|
}
|
|
1207
1423
|
|
|
@@ -1343,20 +1559,16 @@ function toStorageCollectionOptions(
|
|
|
1343
1559
|
...(opts.capped
|
|
1344
1560
|
? { capped: { size: opts.size ?? 0, ...(opts.max != null && { max: opts.max }) } }
|
|
1345
1561
|
: {}),
|
|
1346
|
-
...(
|
|
1347
|
-
...(opts.indexOptionDefaults
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
...(opts.collation !== undefined && { collation: opts.collation }),
|
|
1351
|
-
...(opts.timeseries !== undefined && { timeseries: opts.timeseries }),
|
|
1562
|
+
...ifDefined('storageEngine', opts.storageEngine),
|
|
1563
|
+
...ifDefined('indexOptionDefaults', opts.indexOptionDefaults),
|
|
1564
|
+
...ifDefined('collation', opts.collation),
|
|
1565
|
+
...ifDefined('timeseries', opts.timeseries),
|
|
1352
1566
|
...(opts.clusteredIndex !== undefined && {
|
|
1353
1567
|
clusteredIndex:
|
|
1354
1568
|
opts.clusteredIndex.name !== undefined ? { name: opts.clusteredIndex.name } : {},
|
|
1355
1569
|
}),
|
|
1356
|
-
...(
|
|
1357
|
-
...(opts.changeStreamPreAndPostImages
|
|
1358
|
-
changeStreamPreAndPostImages: opts.changeStreamPreAndPostImages,
|
|
1359
|
-
}),
|
|
1570
|
+
...ifDefined('expireAfterSeconds', opts.expireAfterSeconds),
|
|
1571
|
+
...ifDefined('changeStreamPreAndPostImages', opts.changeStreamPreAndPostImages),
|
|
1360
1572
|
};
|
|
1361
1573
|
return new MongoCollectionOptions(input);
|
|
1362
1574
|
}
|
|
@@ -1582,6 +1794,37 @@ function buildContractFromDefinition<
|
|
|
1582
1794
|
},
|
|
1583
1795
|
}) as unknown as MongoStorageShape<string>;
|
|
1584
1796
|
|
|
1797
|
+
const builtEnums: Record<string, ContractEnum> = {};
|
|
1798
|
+
for (const [enumName, handle] of Object.entries(definition.enums ?? {})) {
|
|
1799
|
+
if (enumName !== handle.enumName) {
|
|
1800
|
+
throw new Error(
|
|
1801
|
+
`enum declaration key "${enumName}" must match enumType name "${handle.enumName}". Aliases are not supported.`,
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
builtEnums[enumName] = {
|
|
1805
|
+
codecId: handle.codecId,
|
|
1806
|
+
members: handle.enumMembers.map((m) => ({
|
|
1807
|
+
name: m.name,
|
|
1808
|
+
value: blindCast<
|
|
1809
|
+
JsonValue,
|
|
1810
|
+
'enum member values are codec inputs (string/number/bool) and are always JsonValue-compatible'
|
|
1811
|
+
>(m.value),
|
|
1812
|
+
})),
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
const hasEnums = Object.keys(builtEnums).length > 0;
|
|
1816
|
+
|
|
1817
|
+
for (const [modelName, modelBuilder] of Object.entries(definition.models ?? {})) {
|
|
1818
|
+
for (const [fieldName, fieldBuilder] of Object.entries(modelBuilder.__fields)) {
|
|
1819
|
+
const handle = fieldBuilder.__enumHandle;
|
|
1820
|
+
if (handle && !(handle.enumName in builtEnums)) {
|
|
1821
|
+
throw new Error(
|
|
1822
|
+
`Model "${modelName}" field "${fieldName}" references enum "${handle.enumName}" which is not declared in defineContract({ enums: { ... } }).`,
|
|
1823
|
+
);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1585
1828
|
const builtContract = {
|
|
1586
1829
|
target: definition.target.targetId,
|
|
1587
1830
|
targetFamily: definition.family.familyId,
|
|
@@ -1592,6 +1835,7 @@ function buildContractFromDefinition<
|
|
|
1592
1835
|
[UNBOUND_NAMESPACE_ID]: {
|
|
1593
1836
|
models: builtModels,
|
|
1594
1837
|
...(Object.keys(builtValueObjects).length > 0 ? { valueObjects: builtValueObjects } : {}),
|
|
1838
|
+
...(hasEnums ? { enum: builtEnums } : {}),
|
|
1595
1839
|
},
|
|
1596
1840
|
},
|
|
1597
1841
|
},
|
|
@@ -1624,6 +1868,7 @@ type BoundDefinitionInput<
|
|
|
1624
1868
|
readonly defaultControlPolicy?: ControlPolicy;
|
|
1625
1869
|
readonly models?: Models;
|
|
1626
1870
|
readonly valueObjects?: ValueObjects;
|
|
1871
|
+
readonly enums?: Record<string, EnumTypeHandle>;
|
|
1627
1872
|
};
|
|
1628
1873
|
|
|
1629
1874
|
// Merges a bound input with the pre-bound family/target to produce a full ContractDefinition.
|
package/src/enum-type.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
BoundEnumType,
|
|
3
|
+
CodecInput,
|
|
4
|
+
CodecTypeMap,
|
|
5
|
+
EnumMember,
|
|
6
|
+
EnumTypeHandle,
|
|
7
|
+
} from '@prisma-next/contract-authoring';
|
|
8
|
+
export {
|
|
9
|
+
bindEnumType,
|
|
10
|
+
ENUM_TYPE_HANDLE_BRAND,
|
|
11
|
+
enumType,
|
|
12
|
+
isEnumTypeHandle,
|
|
13
|
+
member,
|
|
14
|
+
} from '@prisma-next/contract-authoring';
|
|
@@ -2,6 +2,7 @@ export type {
|
|
|
2
2
|
ContractDefinition,
|
|
3
3
|
ContractFactory,
|
|
4
4
|
ContractScaffold,
|
|
5
|
+
ExtractCodecTypesFromPack,
|
|
5
6
|
FieldBuilder,
|
|
6
7
|
FieldReference,
|
|
7
8
|
ModelBuilder,
|
|
@@ -18,3 +19,5 @@ export {
|
|
|
18
19
|
rel,
|
|
19
20
|
valueObject,
|
|
20
21
|
} from '../contract-builder';
|
|
22
|
+
export type { BoundEnumType, CodecTypeMap, EnumMember, EnumTypeHandle } from '../enum-type';
|
|
23
|
+
export { bindEnumType, enumType, isEnumTypeHandle, member } from '../enum-type';
|