@prisma-next/mongo-contract-ts 0.0.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 +90 -0
- package/package.json +46 -0
- package/src/config-types.ts +13 -0
- package/src/contract-builder.ts +1403 -0
- package/src/exports/config-types.ts +2 -0
- package/src/exports/contract-builder.ts +19 -0
|
@@ -0,0 +1,1403 @@
|
|
|
1
|
+
import { computeProfileHash, computeStorageHash } from '@prisma-next/contract/hashing';
|
|
2
|
+
import type {
|
|
3
|
+
ContractEmbedRelation,
|
|
4
|
+
ContractField,
|
|
5
|
+
ContractFieldType,
|
|
6
|
+
ContractReferenceRelation,
|
|
7
|
+
ContractValueObject,
|
|
8
|
+
ProfileHashBase,
|
|
9
|
+
StorageHashBase,
|
|
10
|
+
} from '@prisma-next/contract/types';
|
|
11
|
+
import type {
|
|
12
|
+
ExtensionPackRef,
|
|
13
|
+
FamilyPackRef,
|
|
14
|
+
TargetPackRef,
|
|
15
|
+
} from '@prisma-next/framework-components/components';
|
|
16
|
+
import {
|
|
17
|
+
type MongoCollectionOptions,
|
|
18
|
+
type MongoContract,
|
|
19
|
+
type MongoContractWithTypeMaps,
|
|
20
|
+
type MongoIndex,
|
|
21
|
+
type MongoIndexFields,
|
|
22
|
+
type MongoIndexOptions,
|
|
23
|
+
type MongoStorage,
|
|
24
|
+
type MongoStorageCollection,
|
|
25
|
+
type MongoStorageCollectionOptions,
|
|
26
|
+
type MongoStorageIndex,
|
|
27
|
+
type MongoTypeMaps,
|
|
28
|
+
validateMongoContract,
|
|
29
|
+
} from '@prisma-next/mongo-contract';
|
|
30
|
+
|
|
31
|
+
type VariantSpec = {
|
|
32
|
+
readonly value: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type StorageRelationSpec = {
|
|
36
|
+
readonly field: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type ContractCapabilities = Record<string, Record<string, boolean>>;
|
|
40
|
+
type StringListInput = string | readonly string[];
|
|
41
|
+
type Present<T> = Exclude<T, undefined>;
|
|
42
|
+
type EmptyObject = Record<never, never>;
|
|
43
|
+
type Simplify<T> = { [K in keyof T]: T[K] } & EmptyObject;
|
|
44
|
+
type StrictShape<Actual, Shape> = Actual &
|
|
45
|
+
Shape &
|
|
46
|
+
Record<Exclude<keyof Actual, keyof Shape>, never>;
|
|
47
|
+
|
|
48
|
+
type UnionToIntersection<Union> = (Union extends unknown ? (value: Union) => void : never) extends (
|
|
49
|
+
value: infer Intersection,
|
|
50
|
+
) => void
|
|
51
|
+
? Intersection
|
|
52
|
+
: never;
|
|
53
|
+
|
|
54
|
+
export type ExtractCodecTypesFromPack<P> = P extends { __codecTypes?: infer CodecTypes }
|
|
55
|
+
? CodecTypes extends Record<string, { output: unknown }>
|
|
56
|
+
? CodecTypes
|
|
57
|
+
: Record<string, never>
|
|
58
|
+
: Record<string, never>;
|
|
59
|
+
|
|
60
|
+
// This mirrors @prisma-next/target-mongo/codec-types because authoring must stay decoupled from
|
|
61
|
+
// the target layer while still exposing the built-in Mongo codec registry to type inference.
|
|
62
|
+
type MongoCodecTypes = {
|
|
63
|
+
readonly 'mongo/objectId@1': { readonly input: string; readonly output: string };
|
|
64
|
+
readonly 'mongo/string@1': { readonly input: string; readonly output: string };
|
|
65
|
+
readonly 'mongo/double@1': { readonly input: number; readonly output: number };
|
|
66
|
+
readonly 'mongo/int32@1': { readonly input: number; readonly output: number };
|
|
67
|
+
readonly 'mongo/bool@1': { readonly input: boolean; readonly output: boolean };
|
|
68
|
+
readonly 'mongo/date@1': { readonly input: Date; readonly output: Date };
|
|
69
|
+
readonly 'mongo/vector@1': {
|
|
70
|
+
readonly input: readonly number[];
|
|
71
|
+
readonly output: readonly number[];
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
type MergeExtensionCodecTypes<Packs extends Record<string, unknown>> = UnionToIntersection<
|
|
76
|
+
{
|
|
77
|
+
[K in keyof Packs]: ExtractCodecTypesFromPack<Packs[K]>;
|
|
78
|
+
}[keyof Packs]
|
|
79
|
+
>;
|
|
80
|
+
|
|
81
|
+
type MergeExtensionCodecTypesSafe<Packs> =
|
|
82
|
+
Packs extends Record<string, unknown>
|
|
83
|
+
? keyof Packs extends never
|
|
84
|
+
? Record<string, never>
|
|
85
|
+
: MergeExtensionCodecTypes<Packs>
|
|
86
|
+
: Record<string, never>;
|
|
87
|
+
|
|
88
|
+
export interface FieldBuilder<
|
|
89
|
+
Type extends ContractFieldType = ContractFieldType,
|
|
90
|
+
Nullable extends boolean = boolean,
|
|
91
|
+
Many extends boolean = boolean,
|
|
92
|
+
> {
|
|
93
|
+
readonly __kind: 'field';
|
|
94
|
+
readonly __type: Type;
|
|
95
|
+
readonly __nullable: Nullable;
|
|
96
|
+
readonly __many: Many;
|
|
97
|
+
optional(): FieldBuilder<Type, true, Many>;
|
|
98
|
+
many(): FieldBuilder<Type, Nullable, true>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface ValueObjectBuilder<
|
|
102
|
+
Name extends string = string,
|
|
103
|
+
Fields extends Record<string, FieldBuilder> = Record<string, FieldBuilder>,
|
|
104
|
+
> {
|
|
105
|
+
readonly __kind: 'valueObject';
|
|
106
|
+
readonly __name: Name;
|
|
107
|
+
readonly __fields: Fields;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface FieldReference<
|
|
111
|
+
ModelName extends string = string,
|
|
112
|
+
FieldName extends string = string,
|
|
113
|
+
> {
|
|
114
|
+
readonly __kind: 'fieldRef';
|
|
115
|
+
readonly modelName: ModelName;
|
|
116
|
+
readonly fieldName: FieldName;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface RelationOn<
|
|
120
|
+
LocalFields extends readonly string[] = readonly string[],
|
|
121
|
+
TargetFields extends readonly string[] = readonly string[],
|
|
122
|
+
> {
|
|
123
|
+
readonly localFields: LocalFields;
|
|
124
|
+
readonly targetFields: TargetFields;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface RelationBuilder<
|
|
128
|
+
To extends string = string,
|
|
129
|
+
Cardinality extends '1:1' | '1:N' | 'N:1' = '1:1' | '1:N' | 'N:1',
|
|
130
|
+
On extends RelationOn | undefined = RelationOn | undefined,
|
|
131
|
+
> {
|
|
132
|
+
readonly __kind: 'relation';
|
|
133
|
+
readonly __to: To;
|
|
134
|
+
readonly __cardinality: Cardinality;
|
|
135
|
+
readonly __on: On;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface ModelBuilder<
|
|
139
|
+
Name extends string = string,
|
|
140
|
+
Fields extends Record<string, FieldBuilder> = Record<string, FieldBuilder>,
|
|
141
|
+
Relations extends Record<string, RelationBuilder> = Record<string, RelationBuilder>,
|
|
142
|
+
Collection extends string | undefined = string | undefined,
|
|
143
|
+
Owner extends string | undefined = string | undefined,
|
|
144
|
+
Base extends string | undefined = string | undefined,
|
|
145
|
+
StorageRelations extends Record<string, StorageRelationSpec> | undefined =
|
|
146
|
+
| Record<string, StorageRelationSpec>
|
|
147
|
+
| undefined,
|
|
148
|
+
Discriminator extends { readonly field: string } | undefined =
|
|
149
|
+
| { readonly field: string }
|
|
150
|
+
| undefined,
|
|
151
|
+
Variants extends Record<string, VariantSpec> | undefined =
|
|
152
|
+
| Record<string, VariantSpec>
|
|
153
|
+
| undefined,
|
|
154
|
+
> {
|
|
155
|
+
readonly __kind: 'model';
|
|
156
|
+
readonly __name: Name;
|
|
157
|
+
readonly __fields: Fields;
|
|
158
|
+
readonly __relations: Relations;
|
|
159
|
+
readonly __indexes: readonly MongoIndex[] | undefined;
|
|
160
|
+
readonly __collectionOptions: MongoCollectionOptions | undefined;
|
|
161
|
+
readonly __collection: Collection;
|
|
162
|
+
readonly __owner: Owner;
|
|
163
|
+
readonly __base: Base;
|
|
164
|
+
readonly __storageRelations: StorageRelations;
|
|
165
|
+
readonly __discriminator: Discriminator;
|
|
166
|
+
readonly __variants: Variants;
|
|
167
|
+
ref<const FieldName extends keyof Fields & string>(
|
|
168
|
+
fieldName: FieldName,
|
|
169
|
+
): FieldReference<Name, FieldName>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
type AnyFieldBuilder = FieldBuilder<ContractFieldType, boolean, boolean>;
|
|
173
|
+
type AnyReferenceRelationBuilder = RelationBuilder<string, '1:1' | '1:N' | 'N:1', RelationOn>;
|
|
174
|
+
type AnyEmbedRelationBuilder = RelationBuilder<string, '1:1' | '1:N', undefined>;
|
|
175
|
+
type AnyRelationBuilder = AnyReferenceRelationBuilder | AnyEmbedRelationBuilder;
|
|
176
|
+
type AnyFieldReference = FieldReference<string, string>;
|
|
177
|
+
type NamedValueObjectBuilder<
|
|
178
|
+
Name extends string = string,
|
|
179
|
+
Fields extends Record<string, AnyFieldBuilder> = Record<string, AnyFieldBuilder>,
|
|
180
|
+
> = ValueObjectBuilder<Name, Fields>;
|
|
181
|
+
type AnyValueObjectBuilder = NamedValueObjectBuilder;
|
|
182
|
+
type NamedModelBuilder<
|
|
183
|
+
Name extends string = string,
|
|
184
|
+
Fields extends Record<string, AnyFieldBuilder> = Record<string, AnyFieldBuilder>,
|
|
185
|
+
Relations extends Record<string, AnyRelationBuilder> = Record<string, AnyRelationBuilder>,
|
|
186
|
+
Collection extends string | undefined = string | undefined,
|
|
187
|
+
Owner extends string | undefined = string | undefined,
|
|
188
|
+
Base extends string | undefined = string | undefined,
|
|
189
|
+
StorageRelations extends Record<string, StorageRelationSpec> | undefined =
|
|
190
|
+
| Record<string, StorageRelationSpec>
|
|
191
|
+
| undefined,
|
|
192
|
+
Discriminator extends { readonly field: string } | undefined =
|
|
193
|
+
| { readonly field: string }
|
|
194
|
+
| undefined,
|
|
195
|
+
Variants extends Record<string, VariantSpec> | undefined =
|
|
196
|
+
| Record<string, VariantSpec>
|
|
197
|
+
| undefined,
|
|
198
|
+
> = ModelBuilder<
|
|
199
|
+
Name,
|
|
200
|
+
Fields,
|
|
201
|
+
Relations,
|
|
202
|
+
Collection,
|
|
203
|
+
Owner,
|
|
204
|
+
Base,
|
|
205
|
+
StorageRelations,
|
|
206
|
+
Discriminator,
|
|
207
|
+
Variants
|
|
208
|
+
>;
|
|
209
|
+
type AnyModelBuilder = NamedModelBuilder;
|
|
210
|
+
|
|
211
|
+
type ExtractFieldReferenceName<T> =
|
|
212
|
+
T extends FieldReference<string, infer FieldName extends string> ? FieldName : never;
|
|
213
|
+
type ExtractModelName<T> = T extends NamedModelBuilder<infer Name> ? Name : never;
|
|
214
|
+
type ExtractValueObjectName<T> = T extends NamedValueObjectBuilder<infer Name> ? Name : never;
|
|
215
|
+
type ExtractModelCollection<T> =
|
|
216
|
+
T extends NamedModelBuilder<
|
|
217
|
+
string,
|
|
218
|
+
Record<string, AnyFieldBuilder>,
|
|
219
|
+
Record<string, AnyRelationBuilder>,
|
|
220
|
+
infer Collection
|
|
221
|
+
>
|
|
222
|
+
? Collection
|
|
223
|
+
: never;
|
|
224
|
+
type ExtractModelOwner<T> =
|
|
225
|
+
T extends NamedModelBuilder<
|
|
226
|
+
string,
|
|
227
|
+
Record<string, AnyFieldBuilder>,
|
|
228
|
+
Record<string, AnyRelationBuilder>,
|
|
229
|
+
string | undefined,
|
|
230
|
+
infer Owner
|
|
231
|
+
>
|
|
232
|
+
? Owner
|
|
233
|
+
: never;
|
|
234
|
+
type ExtractModelBase<T> =
|
|
235
|
+
T extends NamedModelBuilder<
|
|
236
|
+
string,
|
|
237
|
+
Record<string, AnyFieldBuilder>,
|
|
238
|
+
Record<string, AnyRelationBuilder>,
|
|
239
|
+
string | undefined,
|
|
240
|
+
string | undefined,
|
|
241
|
+
infer Base
|
|
242
|
+
>
|
|
243
|
+
? Base
|
|
244
|
+
: never;
|
|
245
|
+
type ExtractModelStorageRelations<T> =
|
|
246
|
+
T extends NamedModelBuilder<
|
|
247
|
+
string,
|
|
248
|
+
Record<string, AnyFieldBuilder>,
|
|
249
|
+
Record<string, AnyRelationBuilder>,
|
|
250
|
+
string | undefined,
|
|
251
|
+
string | undefined,
|
|
252
|
+
string | undefined,
|
|
253
|
+
infer StorageRelations
|
|
254
|
+
>
|
|
255
|
+
? StorageRelations
|
|
256
|
+
: never;
|
|
257
|
+
|
|
258
|
+
type ModelStorageSection<T> =
|
|
259
|
+
ExtractModelCollection<T> extends string
|
|
260
|
+
? { readonly collection: ExtractModelCollection<T> }
|
|
261
|
+
: EmptyObject;
|
|
262
|
+
type ModelStorageRelationsSection<T> =
|
|
263
|
+
ExtractModelStorageRelations<T> extends Record<string, StorageRelationSpec>
|
|
264
|
+
? keyof ExtractModelStorageRelations<T> extends never
|
|
265
|
+
? EmptyObject
|
|
266
|
+
: { readonly relations: ExtractModelStorageRelations<T> }
|
|
267
|
+
: EmptyObject;
|
|
268
|
+
type RootModelCollection<T> =
|
|
269
|
+
ExtractModelCollection<T> extends string
|
|
270
|
+
? ExtractModelOwner<T> extends undefined
|
|
271
|
+
? ExtractModelBase<T> extends undefined
|
|
272
|
+
? ExtractModelCollection<T>
|
|
273
|
+
: never
|
|
274
|
+
: never
|
|
275
|
+
: never;
|
|
276
|
+
type RootModelName<T> = RootModelCollection<T> extends never ? never : ExtractModelName<T>;
|
|
277
|
+
type CollectionName<T> =
|
|
278
|
+
ExtractModelCollection<T> extends string ? ExtractModelCollection<T> : never;
|
|
279
|
+
|
|
280
|
+
type ModelNameInput = string | AnyModelBuilder;
|
|
281
|
+
type ValueObjectNameInput = string | AnyValueObjectBuilder;
|
|
282
|
+
type RelationTargetFieldsInput<TargetName extends string> =
|
|
283
|
+
| StringListInput
|
|
284
|
+
| FieldReference<TargetName, string>
|
|
285
|
+
| readonly FieldReference<TargetName, string>[];
|
|
286
|
+
|
|
287
|
+
type NormalizeModelName<T> = T extends string ? T : ExtractModelName<T>;
|
|
288
|
+
|
|
289
|
+
type NormalizeModelNameOrUndefined<T> = [T] extends [undefined]
|
|
290
|
+
? undefined
|
|
291
|
+
: NormalizeModelName<Present<T>>;
|
|
292
|
+
|
|
293
|
+
type NormalizeValueObjectName<T> = T extends string ? T : ExtractValueObjectName<T>;
|
|
294
|
+
|
|
295
|
+
type NormalizeStringList<T> = T extends readonly string[]
|
|
296
|
+
? T
|
|
297
|
+
: T extends string
|
|
298
|
+
? readonly [T]
|
|
299
|
+
: readonly string[];
|
|
300
|
+
|
|
301
|
+
type NormalizeTargetFieldList<T> = T extends readonly AnyFieldReference[]
|
|
302
|
+
? {
|
|
303
|
+
readonly [K in keyof T]: ExtractFieldReferenceName<T[K]>;
|
|
304
|
+
}
|
|
305
|
+
: T extends AnyFieldReference
|
|
306
|
+
? readonly [ExtractFieldReferenceName<T>]
|
|
307
|
+
: NormalizeStringList<T>;
|
|
308
|
+
|
|
309
|
+
type ContractFieldFromBuilder<TBuilder> =
|
|
310
|
+
TBuilder extends FieldBuilder<
|
|
311
|
+
infer Type extends ContractFieldType,
|
|
312
|
+
infer Nullable extends boolean,
|
|
313
|
+
infer Many extends boolean
|
|
314
|
+
>
|
|
315
|
+
? Simplify<
|
|
316
|
+
{
|
|
317
|
+
readonly type: Type;
|
|
318
|
+
readonly nullable: Nullable;
|
|
319
|
+
} & (Many extends true ? { readonly many: true } : EmptyObject)
|
|
320
|
+
>
|
|
321
|
+
: never;
|
|
322
|
+
|
|
323
|
+
type ContractFieldsFromRecord<Fields extends Record<string, AnyFieldBuilder>> = Simplify<{
|
|
324
|
+
readonly [K in keyof Fields]: ContractFieldFromBuilder<Fields[K]>;
|
|
325
|
+
}>;
|
|
326
|
+
|
|
327
|
+
type ContractValueObjectFromBuilder<TBuilder> =
|
|
328
|
+
TBuilder extends ValueObjectBuilder<string, infer Fields extends Record<string, AnyFieldBuilder>>
|
|
329
|
+
? Simplify<{
|
|
330
|
+
readonly fields: ContractFieldsFromRecord<Fields>;
|
|
331
|
+
}>
|
|
332
|
+
: never;
|
|
333
|
+
|
|
334
|
+
type ContractValueObjectsFromRecord<ValueObjects extends Record<string, AnyValueObjectBuilder>> =
|
|
335
|
+
Simplify<{
|
|
336
|
+
readonly [K in keyof ValueObjects as ExtractValueObjectName<
|
|
337
|
+
ValueObjects[K]
|
|
338
|
+
>]: ContractValueObjectFromBuilder<ValueObjects[K]>;
|
|
339
|
+
}>;
|
|
340
|
+
|
|
341
|
+
type ContractRelationFromBuilder<TBuilder> =
|
|
342
|
+
TBuilder extends RelationBuilder<
|
|
343
|
+
infer To extends string,
|
|
344
|
+
infer Cardinality extends '1:1' | '1:N' | 'N:1',
|
|
345
|
+
infer On extends RelationOn | undefined
|
|
346
|
+
>
|
|
347
|
+
? On extends RelationOn
|
|
348
|
+
? {
|
|
349
|
+
readonly to: To;
|
|
350
|
+
readonly cardinality: Cardinality;
|
|
351
|
+
readonly on: On;
|
|
352
|
+
}
|
|
353
|
+
: {
|
|
354
|
+
readonly to: To;
|
|
355
|
+
readonly cardinality: Cardinality;
|
|
356
|
+
}
|
|
357
|
+
: never;
|
|
358
|
+
|
|
359
|
+
type ContractRelationsFromRecord<Relations extends Record<string, AnyRelationBuilder>> =
|
|
360
|
+
keyof Relations extends never
|
|
361
|
+
? Record<string, never>
|
|
362
|
+
: Simplify<{
|
|
363
|
+
readonly [K in keyof Relations]: ContractRelationFromBuilder<Relations[K]>;
|
|
364
|
+
}>;
|
|
365
|
+
|
|
366
|
+
type ContractModelStorageFromBuilder<TBuilder> = ModelStorageSection<TBuilder> &
|
|
367
|
+
ModelStorageRelationsSection<TBuilder>;
|
|
368
|
+
|
|
369
|
+
type MaybeOwner<Owner> = [Owner] extends [undefined]
|
|
370
|
+
? EmptyObject
|
|
371
|
+
: { readonly owner: Owner & string };
|
|
372
|
+
type MaybeBase<Base> = [Base] extends [undefined] ? EmptyObject : { readonly base: Base & string };
|
|
373
|
+
type MaybeDiscriminator<Discriminator> = [Discriminator] extends [undefined]
|
|
374
|
+
? EmptyObject
|
|
375
|
+
: { readonly discriminator: Discriminator & { readonly field: string } };
|
|
376
|
+
type MaybeVariants<Variants> = [Variants] extends [undefined]
|
|
377
|
+
? EmptyObject
|
|
378
|
+
: { readonly variants: Variants };
|
|
379
|
+
|
|
380
|
+
type ContractModelFromBuilder<TBuilder> =
|
|
381
|
+
TBuilder extends NamedModelBuilder<
|
|
382
|
+
string,
|
|
383
|
+
infer Fields extends Record<string, AnyFieldBuilder>,
|
|
384
|
+
infer Relations extends Record<string, AnyRelationBuilder>,
|
|
385
|
+
string | undefined,
|
|
386
|
+
infer Owner,
|
|
387
|
+
infer Base,
|
|
388
|
+
Record<string, StorageRelationSpec> | undefined,
|
|
389
|
+
infer Discriminator,
|
|
390
|
+
infer Variants
|
|
391
|
+
>
|
|
392
|
+
? Simplify<
|
|
393
|
+
{
|
|
394
|
+
readonly fields: ContractFieldsFromRecord<Fields>;
|
|
395
|
+
readonly relations: ContractRelationsFromRecord<Relations>;
|
|
396
|
+
readonly storage: ContractModelStorageFromBuilder<TBuilder>;
|
|
397
|
+
} & MaybeOwner<Owner> &
|
|
398
|
+
MaybeBase<Base> &
|
|
399
|
+
MaybeDiscriminator<Discriminator> &
|
|
400
|
+
MaybeVariants<Variants>
|
|
401
|
+
>
|
|
402
|
+
: never;
|
|
403
|
+
|
|
404
|
+
type ContractModelsFromRecord<Models extends Record<string, AnyModelBuilder>> = Simplify<{
|
|
405
|
+
readonly [K in keyof Models as ExtractModelName<Models[K]>]: ContractModelFromBuilder<Models[K]>;
|
|
406
|
+
}>;
|
|
407
|
+
|
|
408
|
+
type DerivedRootModels<Models extends Record<string, AnyModelBuilder>> = Simplify<{
|
|
409
|
+
readonly [K in keyof Models as RootModelCollection<Models[K]>]: RootModelName<Models[K]>;
|
|
410
|
+
}>;
|
|
411
|
+
|
|
412
|
+
type StorageCollectionsFromModels<Models extends Record<string, AnyModelBuilder>> = Simplify<{
|
|
413
|
+
readonly [K in keyof Models as CollectionName<Models[K]>]: MongoStorageCollection;
|
|
414
|
+
}>;
|
|
415
|
+
|
|
416
|
+
type NormalizeRoots<Roots extends Record<string, ModelNameInput>> = Simplify<{
|
|
417
|
+
readonly [K in keyof Roots]: NormalizeModelName<Roots[K]>;
|
|
418
|
+
}>;
|
|
419
|
+
|
|
420
|
+
type DefinitionModels<Definition> = Definition extends {
|
|
421
|
+
readonly models?: infer Models extends Record<string, AnyModelBuilder>;
|
|
422
|
+
}
|
|
423
|
+
? Models
|
|
424
|
+
: Record<never, never>;
|
|
425
|
+
|
|
426
|
+
type DefinitionValueObjects<Definition> = Definition extends {
|
|
427
|
+
readonly valueObjects?: infer ValueObjects extends Record<string, AnyValueObjectBuilder>;
|
|
428
|
+
}
|
|
429
|
+
? ValueObjects
|
|
430
|
+
: Record<never, never>;
|
|
431
|
+
|
|
432
|
+
type DefinitionRoots<Definition> = Definition extends {
|
|
433
|
+
readonly roots?: infer Roots extends Record<string, ModelNameInput>;
|
|
434
|
+
}
|
|
435
|
+
? NormalizeRoots<Roots>
|
|
436
|
+
: DerivedRootModels<DefinitionModels<Definition>>;
|
|
437
|
+
|
|
438
|
+
type DefinitionCapabilities<Definition> = Definition extends {
|
|
439
|
+
readonly capabilities?: infer Capabilities extends ContractCapabilities;
|
|
440
|
+
}
|
|
441
|
+
? Capabilities
|
|
442
|
+
: Record<never, never>;
|
|
443
|
+
|
|
444
|
+
type DefinitionExtensionPacks<Definition> = Definition extends {
|
|
445
|
+
readonly extensionPacks?: infer ExtensionPacks extends Record<
|
|
446
|
+
string,
|
|
447
|
+
ExtensionPackRef<string, string>
|
|
448
|
+
>;
|
|
449
|
+
}
|
|
450
|
+
? ExtensionPacks
|
|
451
|
+
: Record<never, never>;
|
|
452
|
+
|
|
453
|
+
type DefinitionFamilyId<Definition> = Definition extends {
|
|
454
|
+
readonly family: FamilyPackRef<infer FamilyId>;
|
|
455
|
+
}
|
|
456
|
+
? FamilyId
|
|
457
|
+
: string;
|
|
458
|
+
|
|
459
|
+
type DefinitionTargetId<Definition> = Definition extends {
|
|
460
|
+
readonly target: TargetPackRef<string, infer TargetId>;
|
|
461
|
+
}
|
|
462
|
+
? TargetId
|
|
463
|
+
: string;
|
|
464
|
+
|
|
465
|
+
type DefinitionStorage<Definition> = Simplify<
|
|
466
|
+
MongoStorage & {
|
|
467
|
+
readonly collections: StorageCollectionsFromModels<DefinitionModels<Definition>>;
|
|
468
|
+
readonly storageHash: StorageHashBase<string>;
|
|
469
|
+
}
|
|
470
|
+
>;
|
|
471
|
+
|
|
472
|
+
type MaybeValueObjectsSection<ValueObjects extends Record<string, AnyValueObjectBuilder>> =
|
|
473
|
+
keyof ValueObjects extends never
|
|
474
|
+
? EmptyObject
|
|
475
|
+
: {
|
|
476
|
+
readonly valueObjects: ContractValueObjectsFromRecord<ValueObjects>;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
type MongoContractBaseFromDefinition<Definition> = Simplify<
|
|
480
|
+
{
|
|
481
|
+
readonly target: DefinitionTargetId<Definition>;
|
|
482
|
+
readonly targetFamily: DefinitionFamilyId<Definition>;
|
|
483
|
+
readonly roots: DefinitionRoots<Definition>;
|
|
484
|
+
readonly models: ContractModelsFromRecord<DefinitionModels<Definition>>;
|
|
485
|
+
readonly storage: DefinitionStorage<Definition>;
|
|
486
|
+
readonly capabilities: DefinitionCapabilities<Definition>;
|
|
487
|
+
readonly extensionPacks: DefinitionExtensionPacks<Definition>;
|
|
488
|
+
readonly profileHash: ProfileHashBase<string>;
|
|
489
|
+
readonly meta: Record<string, never>;
|
|
490
|
+
} & MaybeValueObjectsSection<DefinitionValueObjects<Definition>>
|
|
491
|
+
>;
|
|
492
|
+
|
|
493
|
+
type CodecTypesFromDefinition<Definition> = MongoCodecTypes &
|
|
494
|
+
MergeExtensionCodecTypesSafe<DefinitionExtensionPacks<Definition>>;
|
|
495
|
+
|
|
496
|
+
export type MongoContractResult<Definition> = MongoContractWithTypeMaps<
|
|
497
|
+
MongoContractBaseFromDefinition<Definition>,
|
|
498
|
+
MongoTypeMaps<CodecTypesFromDefinition<Definition>>
|
|
499
|
+
>;
|
|
500
|
+
|
|
501
|
+
type ContractAuthoringHelpers = {
|
|
502
|
+
readonly field: typeof field;
|
|
503
|
+
readonly index: typeof index;
|
|
504
|
+
readonly model: typeof model;
|
|
505
|
+
readonly rel: typeof rel;
|
|
506
|
+
readonly valueObject: typeof valueObject;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
export type ContractScaffold<
|
|
510
|
+
Family extends FamilyPackRef<string>,
|
|
511
|
+
Target extends TargetPackRef<string, string>,
|
|
512
|
+
ExtensionPacks extends Record<string, ExtensionPackRef<string, string>> | undefined = undefined,
|
|
513
|
+
Capabilities extends ContractCapabilities | undefined = undefined,
|
|
514
|
+
Roots extends Record<string, ModelNameInput> | undefined = undefined,
|
|
515
|
+
> = {
|
|
516
|
+
readonly family: Family;
|
|
517
|
+
readonly target: Target;
|
|
518
|
+
readonly extensionPacks?: ExtensionPacks;
|
|
519
|
+
readonly capabilities?: Capabilities;
|
|
520
|
+
readonly roots?: Roots;
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
export type ContractDefinition<
|
|
524
|
+
Family extends FamilyPackRef<string>,
|
|
525
|
+
Target extends TargetPackRef<string, string>,
|
|
526
|
+
Models extends Record<string, AnyModelBuilder> = Record<never, never>,
|
|
527
|
+
ValueObjects extends Record<string, AnyValueObjectBuilder> = Record<never, never>,
|
|
528
|
+
ExtensionPacks extends Record<string, ExtensionPackRef<string, string>> | undefined = undefined,
|
|
529
|
+
Capabilities extends ContractCapabilities | undefined = undefined,
|
|
530
|
+
Roots extends Record<string, ModelNameInput> | undefined = undefined,
|
|
531
|
+
> = ContractScaffold<Family, Target, ExtensionPacks, Capabilities, Roots> & {
|
|
532
|
+
readonly models?: Models;
|
|
533
|
+
readonly valueObjects?: ValueObjects;
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
export type ContractFactory<
|
|
537
|
+
Models extends Record<string, AnyModelBuilder> = Record<never, never>,
|
|
538
|
+
ValueObjects extends Record<string, AnyValueObjectBuilder> = Record<never, never>,
|
|
539
|
+
Roots extends Record<string, ModelNameInput> | undefined = undefined,
|
|
540
|
+
> = (helpers: ContractAuthoringHelpers) => {
|
|
541
|
+
readonly models?: Models;
|
|
542
|
+
readonly valueObjects?: ValueObjects;
|
|
543
|
+
readonly roots?: Roots;
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
type FieldBuilderSpec<
|
|
547
|
+
Type extends ContractFieldType,
|
|
548
|
+
Nullable extends boolean,
|
|
549
|
+
Many extends boolean,
|
|
550
|
+
> = {
|
|
551
|
+
readonly type: Type;
|
|
552
|
+
readonly nullable: Nullable;
|
|
553
|
+
readonly many: Many;
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
function createFieldBuilder<
|
|
557
|
+
Type extends ContractFieldType,
|
|
558
|
+
Nullable extends boolean,
|
|
559
|
+
Many extends boolean,
|
|
560
|
+
>(spec: FieldBuilderSpec<Type, Nullable, Many>): FieldBuilder<Type, Nullable, Many> {
|
|
561
|
+
return {
|
|
562
|
+
__kind: 'field',
|
|
563
|
+
__type: spec.type,
|
|
564
|
+
__nullable: spec.nullable,
|
|
565
|
+
__many: spec.many,
|
|
566
|
+
optional() {
|
|
567
|
+
return createFieldBuilder<Type, true, Many>({
|
|
568
|
+
type: spec.type,
|
|
569
|
+
nullable: true,
|
|
570
|
+
many: spec.many,
|
|
571
|
+
});
|
|
572
|
+
},
|
|
573
|
+
many() {
|
|
574
|
+
return createFieldBuilder<Type, Nullable, true>({
|
|
575
|
+
type: spec.type,
|
|
576
|
+
nullable: spec.nullable,
|
|
577
|
+
many: true,
|
|
578
|
+
});
|
|
579
|
+
},
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function normalizeOptionalTypeParams(
|
|
584
|
+
typeParams: Record<string, unknown> | undefined,
|
|
585
|
+
): { readonly typeParams: Record<string, unknown> } | Record<never, never> {
|
|
586
|
+
if (!typeParams) {
|
|
587
|
+
return {};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return { typeParams };
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function createScalarFieldBuilder<
|
|
594
|
+
CodecId extends string,
|
|
595
|
+
TypeParams extends Record<string, unknown> | undefined = undefined,
|
|
596
|
+
>(
|
|
597
|
+
codecId: CodecId,
|
|
598
|
+
options?: { readonly typeParams?: TypeParams },
|
|
599
|
+
): FieldBuilder<
|
|
600
|
+
{
|
|
601
|
+
readonly kind: 'scalar';
|
|
602
|
+
readonly codecId: CodecId;
|
|
603
|
+
} & ([TypeParams] extends [undefined] ? EmptyObject : { readonly typeParams: TypeParams }),
|
|
604
|
+
false,
|
|
605
|
+
false
|
|
606
|
+
> {
|
|
607
|
+
return createFieldBuilder({
|
|
608
|
+
type: {
|
|
609
|
+
kind: 'scalar',
|
|
610
|
+
codecId,
|
|
611
|
+
...normalizeOptionalTypeParams(options?.typeParams),
|
|
612
|
+
} as {
|
|
613
|
+
readonly kind: 'scalar';
|
|
614
|
+
readonly codecId: CodecId;
|
|
615
|
+
} & ([TypeParams] extends [undefined] ? EmptyObject : { readonly typeParams: TypeParams }),
|
|
616
|
+
nullable: false,
|
|
617
|
+
many: false,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export const field = {
|
|
622
|
+
scalar: createScalarFieldBuilder,
|
|
623
|
+
objectId() {
|
|
624
|
+
return createScalarFieldBuilder('mongo/objectId@1');
|
|
625
|
+
},
|
|
626
|
+
string() {
|
|
627
|
+
return createScalarFieldBuilder('mongo/string@1');
|
|
628
|
+
},
|
|
629
|
+
double() {
|
|
630
|
+
return createScalarFieldBuilder('mongo/double@1');
|
|
631
|
+
},
|
|
632
|
+
int32() {
|
|
633
|
+
return createScalarFieldBuilder('mongo/int32@1');
|
|
634
|
+
},
|
|
635
|
+
bool() {
|
|
636
|
+
return createScalarFieldBuilder('mongo/bool@1');
|
|
637
|
+
},
|
|
638
|
+
date() {
|
|
639
|
+
return createScalarFieldBuilder('mongo/date@1');
|
|
640
|
+
},
|
|
641
|
+
vector<const TypeParams extends Record<string, unknown> | undefined = undefined>(options?: {
|
|
642
|
+
readonly typeParams?: TypeParams;
|
|
643
|
+
}) {
|
|
644
|
+
return createScalarFieldBuilder('mongo/vector@1', options);
|
|
645
|
+
},
|
|
646
|
+
valueObject<const ValueObject extends ValueObjectNameInput>(valueObjectName: ValueObject) {
|
|
647
|
+
return createFieldBuilder({
|
|
648
|
+
type: {
|
|
649
|
+
kind: 'valueObject',
|
|
650
|
+
name: resolveValueObjectName(valueObjectName),
|
|
651
|
+
} as {
|
|
652
|
+
readonly kind: 'valueObject';
|
|
653
|
+
readonly name: NormalizeValueObjectName<ValueObject>;
|
|
654
|
+
},
|
|
655
|
+
nullable: false,
|
|
656
|
+
many: false,
|
|
657
|
+
});
|
|
658
|
+
},
|
|
659
|
+
} as const;
|
|
660
|
+
|
|
661
|
+
export function index<const Fields extends MongoIndexFields>(
|
|
662
|
+
fields: Fields,
|
|
663
|
+
): {
|
|
664
|
+
readonly fields: Fields;
|
|
665
|
+
};
|
|
666
|
+
export function index<const Fields extends MongoIndexFields, const Options>(
|
|
667
|
+
fields: Fields,
|
|
668
|
+
options: StrictShape<Options, MongoIndexOptions>,
|
|
669
|
+
): {
|
|
670
|
+
readonly fields: Fields;
|
|
671
|
+
readonly options: Options & MongoIndexOptions;
|
|
672
|
+
};
|
|
673
|
+
export function index(
|
|
674
|
+
fields: MongoIndexFields,
|
|
675
|
+
options?: MongoIndexOptions,
|
|
676
|
+
): {
|
|
677
|
+
readonly fields: MongoIndexFields;
|
|
678
|
+
readonly options?: MongoIndexOptions;
|
|
679
|
+
} {
|
|
680
|
+
return {
|
|
681
|
+
fields,
|
|
682
|
+
...(options ? { options } : {}),
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function createFieldReference<const ModelName extends string, const FieldName extends string>(
|
|
687
|
+
modelName: ModelName,
|
|
688
|
+
fieldName: FieldName,
|
|
689
|
+
): FieldReference<ModelName, FieldName> {
|
|
690
|
+
return {
|
|
691
|
+
__kind: 'fieldRef',
|
|
692
|
+
modelName,
|
|
693
|
+
fieldName,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function isFieldReference(value: unknown): value is FieldReference<string, string> {
|
|
698
|
+
return (
|
|
699
|
+
typeof value === 'object' && value !== null && '__kind' in value && value.__kind === 'fieldRef'
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function resolveModelName(value: ModelNameInput): string {
|
|
704
|
+
return typeof value === 'string' ? value : value.__name;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function resolveValueObjectName(value: ValueObjectNameInput): string {
|
|
708
|
+
return typeof value === 'string' ? value : value.__name;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function normalizeStringList(value: StringListInput): readonly string[] {
|
|
712
|
+
return typeof value === 'string' ? [value] : [...value];
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function normalizeTargetField(
|
|
716
|
+
targetModelName: string,
|
|
717
|
+
value: string | FieldReference<string, string>,
|
|
718
|
+
): string {
|
|
719
|
+
if (!isFieldReference(value)) {
|
|
720
|
+
return value;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (value.modelName !== targetModelName) {
|
|
724
|
+
throw new Error(
|
|
725
|
+
`Relation target "${targetModelName}" cannot reference field "${value.modelName}.${value.fieldName}".`,
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return value.fieldName;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function normalizeTargetFields(
|
|
733
|
+
targetModelName: string,
|
|
734
|
+
value: RelationTargetFieldsInput<string>,
|
|
735
|
+
): readonly string[] {
|
|
736
|
+
if (typeof value === 'string') {
|
|
737
|
+
return [value];
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (isFieldReference(value)) {
|
|
741
|
+
return [normalizeTargetField(targetModelName, value)];
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
return value.map((entry) => normalizeTargetField(targetModelName, entry));
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
type ReferenceOptions<
|
|
748
|
+
Target extends ModelNameInput,
|
|
749
|
+
From extends StringListInput,
|
|
750
|
+
To extends RelationTargetFieldsInput<NormalizeModelName<Target>>,
|
|
751
|
+
> = {
|
|
752
|
+
readonly from: From;
|
|
753
|
+
readonly to: To;
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
type RelationOnFromOptions<
|
|
757
|
+
From extends StringListInput,
|
|
758
|
+
To extends RelationTargetFieldsInput<string>,
|
|
759
|
+
> = {
|
|
760
|
+
readonly localFields: NormalizeStringList<From>;
|
|
761
|
+
readonly targetFields: NormalizeTargetFieldList<To>;
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
function createRelationBuilder<
|
|
765
|
+
To extends string,
|
|
766
|
+
Cardinality extends '1:1' | '1:N' | 'N:1',
|
|
767
|
+
On extends RelationOn | undefined,
|
|
768
|
+
>(spec: {
|
|
769
|
+
readonly to: To;
|
|
770
|
+
readonly cardinality: Cardinality;
|
|
771
|
+
readonly on: On;
|
|
772
|
+
}): RelationBuilder<To, Cardinality, On> {
|
|
773
|
+
return {
|
|
774
|
+
__kind: 'relation',
|
|
775
|
+
__to: spec.to,
|
|
776
|
+
__cardinality: spec.cardinality,
|
|
777
|
+
__on: spec.on,
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function createReferenceRelationBuilder<
|
|
782
|
+
Target extends ModelNameInput,
|
|
783
|
+
Cardinality extends '1:1' | '1:N' | 'N:1',
|
|
784
|
+
From extends StringListInput,
|
|
785
|
+
To extends RelationTargetFieldsInput<NormalizeModelName<Target>>,
|
|
786
|
+
>(
|
|
787
|
+
target: Target,
|
|
788
|
+
cardinality: Cardinality,
|
|
789
|
+
options: ReferenceOptions<Target, From, To>,
|
|
790
|
+
): RelationBuilder<NormalizeModelName<Target>, Cardinality, RelationOnFromOptions<From, To>> {
|
|
791
|
+
const targetModelName = resolveModelName(target);
|
|
792
|
+
|
|
793
|
+
return createRelationBuilder({
|
|
794
|
+
to: targetModelName as NormalizeModelName<Target>,
|
|
795
|
+
cardinality,
|
|
796
|
+
on: {
|
|
797
|
+
localFields: normalizeStringList(options.from) as NormalizeStringList<From>,
|
|
798
|
+
targetFields: normalizeTargetFields(
|
|
799
|
+
targetModelName,
|
|
800
|
+
options.to,
|
|
801
|
+
) as NormalizeTargetFieldList<To>,
|
|
802
|
+
},
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function createEmbedRelationBuilder<
|
|
807
|
+
Target extends ModelNameInput,
|
|
808
|
+
Cardinality extends '1:1' | '1:N',
|
|
809
|
+
>(
|
|
810
|
+
target: Target,
|
|
811
|
+
cardinality: Cardinality,
|
|
812
|
+
): RelationBuilder<NormalizeModelName<Target>, Cardinality, undefined> {
|
|
813
|
+
return createRelationBuilder({
|
|
814
|
+
to: resolveModelName(target) as NormalizeModelName<Target>,
|
|
815
|
+
cardinality,
|
|
816
|
+
on: undefined,
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
function hasOne<const Target extends ModelNameInput>(
|
|
821
|
+
target: Target,
|
|
822
|
+
): RelationBuilder<NormalizeModelName<Target>, '1:1', undefined>;
|
|
823
|
+
function hasOne<
|
|
824
|
+
const Target extends ModelNameInput,
|
|
825
|
+
const From extends StringListInput,
|
|
826
|
+
const To extends RelationTargetFieldsInput<NormalizeModelName<Target>>,
|
|
827
|
+
>(
|
|
828
|
+
target: Target,
|
|
829
|
+
options: ReferenceOptions<Target, From, To>,
|
|
830
|
+
): RelationBuilder<NormalizeModelName<Target>, '1:1', RelationOnFromOptions<From, To>>;
|
|
831
|
+
function hasOne(
|
|
832
|
+
target: ModelNameInput,
|
|
833
|
+
options?: ReferenceOptions<ModelNameInput, StringListInput, RelationTargetFieldsInput<string>>,
|
|
834
|
+
) {
|
|
835
|
+
if (!options) {
|
|
836
|
+
return createEmbedRelationBuilder(target, '1:1');
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return createReferenceRelationBuilder(target, '1:1', options);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function hasMany<const Target extends ModelNameInput>(
|
|
843
|
+
target: Target,
|
|
844
|
+
): RelationBuilder<NormalizeModelName<Target>, '1:N', undefined>;
|
|
845
|
+
function hasMany<
|
|
846
|
+
const Target extends ModelNameInput,
|
|
847
|
+
const From extends StringListInput,
|
|
848
|
+
const To extends RelationTargetFieldsInput<NormalizeModelName<Target>>,
|
|
849
|
+
>(
|
|
850
|
+
target: Target,
|
|
851
|
+
options: ReferenceOptions<Target, From, To>,
|
|
852
|
+
): RelationBuilder<NormalizeModelName<Target>, '1:N', RelationOnFromOptions<From, To>>;
|
|
853
|
+
function hasMany(
|
|
854
|
+
target: ModelNameInput,
|
|
855
|
+
options?: ReferenceOptions<ModelNameInput, StringListInput, RelationTargetFieldsInput<string>>,
|
|
856
|
+
) {
|
|
857
|
+
if (!options) {
|
|
858
|
+
return createEmbedRelationBuilder(target, '1:N');
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
return createReferenceRelationBuilder(target, '1:N', options);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function belongsTo<
|
|
865
|
+
const Target extends ModelNameInput,
|
|
866
|
+
const From extends StringListInput,
|
|
867
|
+
const To extends RelationTargetFieldsInput<NormalizeModelName<Target>>,
|
|
868
|
+
>(
|
|
869
|
+
target: Target,
|
|
870
|
+
options: ReferenceOptions<Target, From, To>,
|
|
871
|
+
): RelationBuilder<NormalizeModelName<Target>, 'N:1', RelationOnFromOptions<From, To>> {
|
|
872
|
+
return createReferenceRelationBuilder(target, 'N:1', options);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
export const rel = {
|
|
876
|
+
belongsTo,
|
|
877
|
+
hasMany,
|
|
878
|
+
hasOne,
|
|
879
|
+
} as const;
|
|
880
|
+
|
|
881
|
+
type ValueObjectInput<Fields extends Record<string, AnyFieldBuilder>> = {
|
|
882
|
+
readonly fields: Fields;
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
export function valueObject<
|
|
886
|
+
const Name extends string,
|
|
887
|
+
const Fields extends Record<string, AnyFieldBuilder>,
|
|
888
|
+
>(name: Name, input: ValueObjectInput<Fields>): ValueObjectBuilder<Name, Fields> {
|
|
889
|
+
return {
|
|
890
|
+
__kind: 'valueObject',
|
|
891
|
+
__name: name,
|
|
892
|
+
__fields: input.fields,
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
type ModelDiscriminatorInput<Variants extends Record<string, VariantSpec>> = {
|
|
897
|
+
readonly field: string;
|
|
898
|
+
readonly variants: Variants;
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
type ModelInput<
|
|
902
|
+
Fields extends Record<string, AnyFieldBuilder>,
|
|
903
|
+
Relations extends Record<string, AnyRelationBuilder> | undefined,
|
|
904
|
+
Collection extends string | undefined,
|
|
905
|
+
Indexes extends readonly MongoIndex[] | undefined,
|
|
906
|
+
CollectionOptions,
|
|
907
|
+
Owner extends ModelNameInput | undefined,
|
|
908
|
+
Base extends ModelNameInput | undefined,
|
|
909
|
+
StorageRelations extends Record<string, StorageRelationSpec> | undefined,
|
|
910
|
+
Discriminator extends ModelDiscriminatorInput<Record<string, VariantSpec>> | undefined,
|
|
911
|
+
> = {
|
|
912
|
+
readonly collection?: Collection;
|
|
913
|
+
readonly indexes?: Indexes;
|
|
914
|
+
readonly collectionOptions?: StrictShape<CollectionOptions, MongoCollectionOptions>;
|
|
915
|
+
readonly storageRelations?: StorageRelations;
|
|
916
|
+
readonly fields: Fields;
|
|
917
|
+
readonly relations?: Relations;
|
|
918
|
+
readonly owner?: Owner;
|
|
919
|
+
readonly base?: Base;
|
|
920
|
+
readonly discriminator?: Discriminator;
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
export function model<
|
|
924
|
+
const Name extends string,
|
|
925
|
+
const Fields extends Record<string, AnyFieldBuilder>,
|
|
926
|
+
const Relations extends Record<string, AnyRelationBuilder> | undefined = undefined,
|
|
927
|
+
const Collection extends string | undefined = undefined,
|
|
928
|
+
const Indexes extends readonly MongoIndex[] | undefined = undefined,
|
|
929
|
+
const CollectionOptions = undefined,
|
|
930
|
+
const Owner extends ModelNameInput | undefined = undefined,
|
|
931
|
+
const Base extends ModelNameInput | undefined = undefined,
|
|
932
|
+
const StorageRelations extends Record<string, StorageRelationSpec> | undefined = undefined,
|
|
933
|
+
const Discriminator extends
|
|
934
|
+
| ModelDiscriminatorInput<Record<string, VariantSpec>>
|
|
935
|
+
| undefined = undefined,
|
|
936
|
+
>(
|
|
937
|
+
name: Name,
|
|
938
|
+
input: ModelInput<
|
|
939
|
+
Fields,
|
|
940
|
+
Relations,
|
|
941
|
+
Collection,
|
|
942
|
+
Indexes,
|
|
943
|
+
CollectionOptions,
|
|
944
|
+
Owner,
|
|
945
|
+
Base,
|
|
946
|
+
StorageRelations,
|
|
947
|
+
Discriminator
|
|
948
|
+
>,
|
|
949
|
+
): ModelBuilder<
|
|
950
|
+
Name,
|
|
951
|
+
Fields,
|
|
952
|
+
Relations extends Record<string, AnyRelationBuilder> ? Relations : Record<never, never>,
|
|
953
|
+
Collection,
|
|
954
|
+
NormalizeModelNameOrUndefined<Owner>,
|
|
955
|
+
NormalizeModelNameOrUndefined<Base>,
|
|
956
|
+
StorageRelations,
|
|
957
|
+
Discriminator extends { readonly field: infer Field extends string }
|
|
958
|
+
? { readonly field: Field }
|
|
959
|
+
: undefined,
|
|
960
|
+
Discriminator extends { readonly variants: infer Variants extends Record<string, VariantSpec> }
|
|
961
|
+
? Variants
|
|
962
|
+
: undefined
|
|
963
|
+
> {
|
|
964
|
+
return {
|
|
965
|
+
__kind: 'model',
|
|
966
|
+
__name: name,
|
|
967
|
+
__fields: input.fields,
|
|
968
|
+
__relations: (input.relations ?? {}) as Relations extends Record<string, AnyRelationBuilder>
|
|
969
|
+
? Relations
|
|
970
|
+
: Record<never, never>,
|
|
971
|
+
__indexes: input.indexes,
|
|
972
|
+
__collectionOptions: input.collectionOptions,
|
|
973
|
+
__collection: input.collection as Collection,
|
|
974
|
+
__owner: (input.owner
|
|
975
|
+
? resolveModelName(input.owner)
|
|
976
|
+
: undefined) as NormalizeModelNameOrUndefined<Owner>,
|
|
977
|
+
__base: (input.base
|
|
978
|
+
? resolveModelName(input.base)
|
|
979
|
+
: undefined) as NormalizeModelNameOrUndefined<Base>,
|
|
980
|
+
__storageRelations: input.storageRelations as StorageRelations,
|
|
981
|
+
__discriminator: (input.discriminator
|
|
982
|
+
? { field: input.discriminator.field }
|
|
983
|
+
: undefined) as Discriminator extends { readonly field: infer Field extends string }
|
|
984
|
+
? { readonly field: Field }
|
|
985
|
+
: undefined,
|
|
986
|
+
__variants: input.discriminator?.variants as Discriminator extends {
|
|
987
|
+
readonly variants: infer Variants extends Record<string, VariantSpec>;
|
|
988
|
+
}
|
|
989
|
+
? Variants
|
|
990
|
+
: undefined,
|
|
991
|
+
ref(fieldName) {
|
|
992
|
+
return createFieldReference(name, fieldName);
|
|
993
|
+
},
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function validateTargetPackRef(
|
|
998
|
+
family: FamilyPackRef<string>,
|
|
999
|
+
target: TargetPackRef<string, string>,
|
|
1000
|
+
): void {
|
|
1001
|
+
if (family.familyId !== 'mongo') {
|
|
1002
|
+
throw new Error(
|
|
1003
|
+
`defineContract only accepts Mongo family packs. Received family "${family.familyId}".`,
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
if (target.familyId !== family.familyId) {
|
|
1008
|
+
throw new Error(
|
|
1009
|
+
`target pack "${target.id}" targets family "${target.familyId}" but contract family is "${family.familyId}".`,
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
function validateExtensionPackRefs(
|
|
1015
|
+
target: TargetPackRef<string, string>,
|
|
1016
|
+
extensionPacks?: Record<string, ExtensionPackRef<string, string>>,
|
|
1017
|
+
): void {
|
|
1018
|
+
if (!extensionPacks) {
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
for (const packRef of Object.values(extensionPacks)) {
|
|
1023
|
+
if (packRef.kind !== 'extension') {
|
|
1024
|
+
throw new Error(
|
|
1025
|
+
`defineContract only accepts extension pack refs in extensionPacks. Received kind "${packRef.kind}".`,
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
if (packRef.familyId !== target.familyId) {
|
|
1030
|
+
throw new Error(
|
|
1031
|
+
`extension pack "${packRef.id}" targets family "${packRef.familyId}" but contract target family is "${target.familyId}".`,
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (packRef.targetId && packRef.targetId !== target.targetId) {
|
|
1036
|
+
throw new Error(
|
|
1037
|
+
`extension pack "${packRef.id}" targets "${packRef.targetId}" but contract target is "${target.targetId}".`,
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
function isContractScaffold(
|
|
1044
|
+
value: unknown,
|
|
1045
|
+
): value is ContractScaffold<
|
|
1046
|
+
FamilyPackRef<string>,
|
|
1047
|
+
TargetPackRef<string, string>,
|
|
1048
|
+
Record<string, ExtensionPackRef<string, string>> | undefined,
|
|
1049
|
+
ContractCapabilities | undefined,
|
|
1050
|
+
Record<string, ModelNameInput> | undefined
|
|
1051
|
+
> {
|
|
1052
|
+
if (typeof value !== 'object' || value === null) {
|
|
1053
|
+
return false;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
return 'family' in value && 'target' in value;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
function buildContractField(builder: AnyFieldBuilder): ContractField {
|
|
1060
|
+
return builder.__many
|
|
1061
|
+
? {
|
|
1062
|
+
type: builder.__type,
|
|
1063
|
+
nullable: builder.__nullable,
|
|
1064
|
+
many: true,
|
|
1065
|
+
}
|
|
1066
|
+
: {
|
|
1067
|
+
type: builder.__type,
|
|
1068
|
+
nullable: builder.__nullable,
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
function buildFields(fields: Record<string, AnyFieldBuilder>): Record<string, ContractField> {
|
|
1073
|
+
const builtFields: Record<string, ContractField> = {};
|
|
1074
|
+
|
|
1075
|
+
for (const [fieldName, fieldBuilder] of Object.entries(fields)) {
|
|
1076
|
+
builtFields[fieldName] = buildContractField(fieldBuilder);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
return builtFields;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
function buildRelation(
|
|
1083
|
+
relationBuilder: AnyRelationBuilder,
|
|
1084
|
+
): ContractEmbedRelation | ContractReferenceRelation {
|
|
1085
|
+
return relationBuilder.__on
|
|
1086
|
+
? {
|
|
1087
|
+
to: relationBuilder.__to,
|
|
1088
|
+
cardinality: relationBuilder.__cardinality,
|
|
1089
|
+
on: relationBuilder.__on,
|
|
1090
|
+
}
|
|
1091
|
+
: {
|
|
1092
|
+
to: relationBuilder.__to,
|
|
1093
|
+
cardinality: relationBuilder.__cardinality,
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
function buildRelations(
|
|
1098
|
+
relations: Record<string, AnyRelationBuilder>,
|
|
1099
|
+
): Record<string, ContractEmbedRelation | ContractReferenceRelation> {
|
|
1100
|
+
const builtRelations: Record<string, ContractEmbedRelation | ContractReferenceRelation> = {};
|
|
1101
|
+
|
|
1102
|
+
for (const [relationName, relationBuilder] of Object.entries(relations)) {
|
|
1103
|
+
builtRelations[relationName] = buildRelation(relationBuilder);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
return builtRelations;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
function buildValueObjects(
|
|
1110
|
+
valueObjects: Record<string, AnyValueObjectBuilder> | undefined,
|
|
1111
|
+
): Record<string, ContractValueObject> {
|
|
1112
|
+
const builtValueObjects: Record<string, ContractValueObject> = {};
|
|
1113
|
+
|
|
1114
|
+
for (const valueObjectBuilder of Object.values(valueObjects ?? {})) {
|
|
1115
|
+
if (valueObjectBuilder.__name in builtValueObjects) {
|
|
1116
|
+
throw new Error(
|
|
1117
|
+
`Duplicate value object name "${valueObjectBuilder.__name}" in defineContract().`,
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
builtValueObjects[valueObjectBuilder.__name] = {
|
|
1122
|
+
fields: buildFields(valueObjectBuilder.__fields),
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
return builtValueObjects;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function buildModels(
|
|
1130
|
+
models: Record<string, AnyModelBuilder> | undefined,
|
|
1131
|
+
): Record<string, MongoContract['models'][string]> {
|
|
1132
|
+
const builtModels: Record<string, MongoContract['models'][string]> = {};
|
|
1133
|
+
|
|
1134
|
+
for (const modelBuilder of Object.values(models ?? {})) {
|
|
1135
|
+
if (modelBuilder.__name in builtModels) {
|
|
1136
|
+
throw new Error(`Duplicate model name "${modelBuilder.__name}" in defineContract().`);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
const storage = {
|
|
1140
|
+
...(modelBuilder.__collection ? { collection: modelBuilder.__collection } : {}),
|
|
1141
|
+
...(modelBuilder.__storageRelations ? { relations: modelBuilder.__storageRelations } : {}),
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
builtModels[modelBuilder.__name] = {
|
|
1145
|
+
fields: buildFields(modelBuilder.__fields),
|
|
1146
|
+
relations: buildRelations(modelBuilder.__relations),
|
|
1147
|
+
storage,
|
|
1148
|
+
...(modelBuilder.__owner ? { owner: modelBuilder.__owner } : {}),
|
|
1149
|
+
...(modelBuilder.__base ? { base: modelBuilder.__base } : {}),
|
|
1150
|
+
...(modelBuilder.__discriminator ? { discriminator: modelBuilder.__discriminator } : {}),
|
|
1151
|
+
...(modelBuilder.__variants ? { variants: modelBuilder.__variants } : {}),
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
return builtModels;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
function deriveRoots(models: Record<string, AnyModelBuilder> | undefined): Record<string, string> {
|
|
1159
|
+
const roots: Record<string, string> = {};
|
|
1160
|
+
|
|
1161
|
+
for (const modelBuilder of Object.values(models ?? {})) {
|
|
1162
|
+
if (!modelBuilder.__collection || modelBuilder.__owner || modelBuilder.__base) {
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
roots[modelBuilder.__collection] = modelBuilder.__name;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
return roots;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
function normalizeRoots(roots: Record<string, ModelNameInput> | undefined): Record<string, string> {
|
|
1173
|
+
const normalizedRoots: Record<string, string> = {};
|
|
1174
|
+
|
|
1175
|
+
for (const [rootName, rootValue] of Object.entries(roots ?? {})) {
|
|
1176
|
+
normalizedRoots[rootName] = resolveModelName(rootValue);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
return normalizedRoots;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function stableStringify(value: unknown): string {
|
|
1183
|
+
if (Array.isArray(value)) {
|
|
1184
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
if (value && typeof value === 'object') {
|
|
1188
|
+
return `{${Object.entries(value as Record<string, unknown>)
|
|
1189
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
1190
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`)
|
|
1191
|
+
.join(',')}}`;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
return JSON.stringify(value);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function toStorageIndex(index: MongoIndex): MongoStorageIndex {
|
|
1198
|
+
const keys = Object.entries(index.fields).map(([field, direction]) => ({
|
|
1199
|
+
field,
|
|
1200
|
+
direction,
|
|
1201
|
+
}));
|
|
1202
|
+
const result: Record<string, unknown> = { keys };
|
|
1203
|
+
if (index.options) {
|
|
1204
|
+
for (const [key, value] of Object.entries(index.options)) {
|
|
1205
|
+
if (value !== undefined) {
|
|
1206
|
+
result[key] = value;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
return result as unknown as MongoStorageIndex;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
function toStorageCollectionOptions(opts: MongoCollectionOptions): MongoStorageCollectionOptions {
|
|
1214
|
+
const result: Record<string, unknown> = {};
|
|
1215
|
+
if (opts.capped) {
|
|
1216
|
+
result['capped'] = { size: opts.size ?? 0, ...(opts.max != null ? { max: opts.max } : {}) };
|
|
1217
|
+
}
|
|
1218
|
+
if (opts.timeseries) result['timeseries'] = opts.timeseries;
|
|
1219
|
+
if (opts.collation) result['collation'] = opts.collation;
|
|
1220
|
+
if (opts.changeStreamPreAndPostImages)
|
|
1221
|
+
result['changeStreamPreAndPostImages'] = opts.changeStreamPreAndPostImages;
|
|
1222
|
+
if (opts.clusteredIndex) result['clusteredIndex'] = { name: opts.clusteredIndex.name };
|
|
1223
|
+
return result as unknown as MongoStorageCollectionOptions;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
function buildCollections(
|
|
1227
|
+
models: Record<string, AnyModelBuilder> | undefined,
|
|
1228
|
+
): Record<string, MongoStorageCollection> {
|
|
1229
|
+
const collections: Record<string, MongoStorageCollection> = {};
|
|
1230
|
+
const declaredIndexOwners = new Map<string, string>();
|
|
1231
|
+
|
|
1232
|
+
for (const modelBuilder of Object.values(models ?? {})) {
|
|
1233
|
+
if (!modelBuilder.__collection) {
|
|
1234
|
+
if (modelBuilder.__indexes && modelBuilder.__indexes.length > 0) {
|
|
1235
|
+
throw new Error(
|
|
1236
|
+
`Model "${modelBuilder.__name}" defines indexes but has no collection to attach them to.`,
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
if (modelBuilder.__collectionOptions) {
|
|
1241
|
+
throw new Error(
|
|
1242
|
+
`Model "${modelBuilder.__name}" defines collectionOptions but has no collection to attach them to.`,
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
const existingCollection = collections[modelBuilder.__collection] ?? {};
|
|
1250
|
+
const existingIndexes = existingCollection.indexes ?? [];
|
|
1251
|
+
|
|
1252
|
+
if (existingCollection.options && modelBuilder.__collectionOptions) {
|
|
1253
|
+
throw new Error(
|
|
1254
|
+
`Collection "${modelBuilder.__collection}" has collectionOptions declared by multiple models. Author collectionOptions on a single model per collection.`,
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
for (const collectionIndex of modelBuilder.__indexes ?? []) {
|
|
1259
|
+
const indexSignature = stableStringify(collectionIndex);
|
|
1260
|
+
const collectionIndexKey = `${modelBuilder.__collection}:${indexSignature}`;
|
|
1261
|
+
const firstOwner = declaredIndexOwners.get(collectionIndexKey);
|
|
1262
|
+
if (firstOwner) {
|
|
1263
|
+
throw new Error(
|
|
1264
|
+
`Collection "${modelBuilder.__collection}" defines duplicate index ${indexSignature}. First declared on model "${firstOwner}" and duplicated on model "${modelBuilder.__name}".`,
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
declaredIndexOwners.set(collectionIndexKey, modelBuilder.__name);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
const storageIndexes = (modelBuilder.__indexes ?? []).map(toStorageIndex);
|
|
1271
|
+
const storageOptions = modelBuilder.__collectionOptions
|
|
1272
|
+
? toStorageCollectionOptions(modelBuilder.__collectionOptions)
|
|
1273
|
+
: undefined;
|
|
1274
|
+
|
|
1275
|
+
collections[modelBuilder.__collection] =
|
|
1276
|
+
storageIndexes.length > 0
|
|
1277
|
+
? {
|
|
1278
|
+
...existingCollection,
|
|
1279
|
+
indexes: [...existingIndexes, ...storageIndexes],
|
|
1280
|
+
...(storageOptions ? { options: storageOptions } : {}),
|
|
1281
|
+
}
|
|
1282
|
+
: storageOptions
|
|
1283
|
+
? {
|
|
1284
|
+
...existingCollection,
|
|
1285
|
+
options: storageOptions,
|
|
1286
|
+
}
|
|
1287
|
+
: existingCollection;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
return collections;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
function buildContractFromDefinition<
|
|
1294
|
+
const Definition extends ContractDefinition<
|
|
1295
|
+
FamilyPackRef<string>,
|
|
1296
|
+
TargetPackRef<string, string>,
|
|
1297
|
+
Record<string, AnyModelBuilder>,
|
|
1298
|
+
Record<string, AnyValueObjectBuilder>,
|
|
1299
|
+
Record<string, ExtensionPackRef<string, string>> | undefined,
|
|
1300
|
+
ContractCapabilities | undefined,
|
|
1301
|
+
Record<string, ModelNameInput> | undefined
|
|
1302
|
+
>,
|
|
1303
|
+
>(definition: Definition): MongoContractResult<Definition> {
|
|
1304
|
+
validateTargetPackRef(definition.family, definition.target);
|
|
1305
|
+
validateExtensionPackRefs(definition.target, definition.extensionPacks);
|
|
1306
|
+
|
|
1307
|
+
const builtModels = buildModels(definition.models);
|
|
1308
|
+
const builtValueObjects = buildValueObjects(definition.valueObjects);
|
|
1309
|
+
const roots = definition.roots
|
|
1310
|
+
? normalizeRoots(definition.roots)
|
|
1311
|
+
: deriveRoots(definition.models);
|
|
1312
|
+
const capabilities = definition.capabilities ?? {};
|
|
1313
|
+
const collections = buildCollections(definition.models);
|
|
1314
|
+
const storageBody = {
|
|
1315
|
+
collections,
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
const builtContract = {
|
|
1319
|
+
target: definition.target.targetId,
|
|
1320
|
+
targetFamily: definition.family.familyId,
|
|
1321
|
+
roots,
|
|
1322
|
+
models: builtModels,
|
|
1323
|
+
...(Object.keys(builtValueObjects).length > 0 ? { valueObjects: builtValueObjects } : {}),
|
|
1324
|
+
storage: {
|
|
1325
|
+
...storageBody,
|
|
1326
|
+
storageHash: computeStorageHash({
|
|
1327
|
+
target: definition.target.targetId,
|
|
1328
|
+
targetFamily: definition.family.familyId,
|
|
1329
|
+
storage: storageBody,
|
|
1330
|
+
}),
|
|
1331
|
+
},
|
|
1332
|
+
capabilities,
|
|
1333
|
+
extensionPacks: definition.extensionPacks ?? {},
|
|
1334
|
+
profileHash: computeProfileHash({
|
|
1335
|
+
target: definition.target.targetId,
|
|
1336
|
+
targetFamily: definition.family.familyId,
|
|
1337
|
+
capabilities,
|
|
1338
|
+
}),
|
|
1339
|
+
meta: {},
|
|
1340
|
+
} satisfies MongoContract;
|
|
1341
|
+
|
|
1342
|
+
validateMongoContract(builtContract);
|
|
1343
|
+
|
|
1344
|
+
return builtContract as MongoContractResult<Definition>;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
export function defineContract<
|
|
1348
|
+
const Definition extends ContractDefinition<
|
|
1349
|
+
FamilyPackRef<string>,
|
|
1350
|
+
TargetPackRef<string, string>,
|
|
1351
|
+
Record<string, AnyModelBuilder>,
|
|
1352
|
+
Record<string, AnyValueObjectBuilder>,
|
|
1353
|
+
Record<string, ExtensionPackRef<string, string>> | undefined,
|
|
1354
|
+
ContractCapabilities | undefined,
|
|
1355
|
+
Record<string, ModelNameInput> | undefined
|
|
1356
|
+
>,
|
|
1357
|
+
>(definition: Definition): MongoContractResult<Definition>;
|
|
1358
|
+
export function defineContract<
|
|
1359
|
+
const Definition extends ContractScaffold<
|
|
1360
|
+
FamilyPackRef<string>,
|
|
1361
|
+
TargetPackRef<string, string>,
|
|
1362
|
+
Record<string, ExtensionPackRef<string, string>> | undefined,
|
|
1363
|
+
ContractCapabilities | undefined,
|
|
1364
|
+
Record<string, ModelNameInput> | undefined
|
|
1365
|
+
>,
|
|
1366
|
+
const Built extends {
|
|
1367
|
+
readonly models?: Record<string, AnyModelBuilder>;
|
|
1368
|
+
readonly valueObjects?: Record<string, AnyValueObjectBuilder>;
|
|
1369
|
+
readonly roots?: Record<string, ModelNameInput>;
|
|
1370
|
+
},
|
|
1371
|
+
>(
|
|
1372
|
+
definition: Definition,
|
|
1373
|
+
factory: (_helpers: ContractAuthoringHelpers) => Built,
|
|
1374
|
+
): MongoContractResult<Definition & Built>;
|
|
1375
|
+
export function defineContract(
|
|
1376
|
+
definition: ContractScaffold<
|
|
1377
|
+
FamilyPackRef<string>,
|
|
1378
|
+
TargetPackRef<string, string>,
|
|
1379
|
+
Record<string, ExtensionPackRef<string, string>> | undefined,
|
|
1380
|
+
ContractCapabilities | undefined,
|
|
1381
|
+
Record<string, ModelNameInput> | undefined
|
|
1382
|
+
>,
|
|
1383
|
+
factory?: ContractFactory<
|
|
1384
|
+
Record<string, AnyModelBuilder>,
|
|
1385
|
+
Record<string, AnyValueObjectBuilder>,
|
|
1386
|
+
Record<string, ModelNameInput> | undefined
|
|
1387
|
+
>,
|
|
1388
|
+
) {
|
|
1389
|
+
if (!isContractScaffold(definition)) {
|
|
1390
|
+
throw new TypeError(
|
|
1391
|
+
'defineContract expects a contract definition object. Define your contract with defineContract({ family, target, models, ... }).',
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if (!factory) {
|
|
1396
|
+
return buildContractFromDefinition(definition);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
return buildContractFromDefinition({
|
|
1400
|
+
...definition,
|
|
1401
|
+
...factory({ field, index, model, rel, valueObject }),
|
|
1402
|
+
});
|
|
1403
|
+
}
|