@mongorm/orm 0.1.1-beta.4 → 0.1.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/dist/index.d.mts +232 -191
- package/dist/index.mjs +166 -108
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,171 @@
|
|
|
1
1
|
import { Collection, Condition, Db as Db$1, DeleteResult, Document, FindCursor, IndexDescription, IndexDirection, MongoClientOptions, ObjectId, ObjectId as ObjectId$1, RootFilterOperators, WithId } from "mongodb";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
//#region src/schema/scalars.d.ts
|
|
4
|
+
declare module 'zod' {
|
|
5
|
+
interface ZodType {
|
|
6
|
+
hidden(): this & {
|
|
7
|
+
readonly __hidden: true;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** A schema field that can be marked as hidden from default query results. */
|
|
12
|
+
type HiddenCapable<T extends z.ZodType> = Omit<T, 'optional' | 'nullable' | 'nullish'> & HiddenMethods<T>;
|
|
13
|
+
/** A schema field marked as hidden from default query results. */
|
|
14
|
+
type HiddenSchema<T extends z.ZodType> = Omit<T, 'optional' | 'nullable' | 'nullish'> & HiddenMethods<T> & {
|
|
15
|
+
readonly __hidden: true;
|
|
16
|
+
};
|
|
17
|
+
type HiddenMethods<T extends z.ZodType> = {
|
|
18
|
+
hidden(): HiddenSchema<T>;
|
|
19
|
+
optional(): HiddenCapable<z.ZodOptional<T>>;
|
|
20
|
+
nullable(): HiddenCapable<z.ZodNullable<T>>;
|
|
21
|
+
nullish(): HiddenCapable<z.ZodOptional<z.ZodNullable<T>>>;
|
|
22
|
+
};
|
|
23
|
+
/** Create a MongoDB ObjectId schema. */
|
|
24
|
+
declare const objectId: () => HiddenCapable<z.ZodInstanceOf<ObjectId$1>>;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/schema/options.d.ts
|
|
27
|
+
/** Built-in persistence behavior applied by a schema. */
|
|
28
|
+
interface SchemaOptions {
|
|
29
|
+
readonly timestamps?: boolean;
|
|
30
|
+
readonly softdelete?: boolean;
|
|
31
|
+
/** Hide Mongorm-managed fields from default query results. */
|
|
32
|
+
readonly hideManaged?: boolean;
|
|
33
|
+
}
|
|
34
|
+
type ManagedSchema<Field extends z.ZodType, Options extends SchemaOptions> = Options['hideManaged'] extends true ? HiddenSchema<Field> : Field;
|
|
35
|
+
type TimestampShape<Options extends SchemaOptions> = {
|
|
36
|
+
createdAt: ManagedSchema<z.ZodDefault<z.ZodDate>, Options>;
|
|
37
|
+
updatedAt: ManagedSchema<z.ZodDefault<z.ZodDate>, Options>;
|
|
38
|
+
};
|
|
39
|
+
type SoftDeleteShape<Options extends SchemaOptions> = {
|
|
40
|
+
deletedAt: ManagedSchema<z.ZodDefault<z.ZodNullable<z.ZodDate>>, Options>;
|
|
41
|
+
};
|
|
42
|
+
type ManagedShape<Options extends SchemaOptions> = (Options['timestamps'] extends true ? TimestampShape<Options> : {}) & (Options['softdelete'] extends true ? SoftDeleteShape<Options> : {});
|
|
43
|
+
type ManagedField<Options extends SchemaOptions> = (Options['timestamps'] extends true ? 'createdAt' | 'updatedAt' : never) | (Options['softdelete'] extends true ? 'deletedAt' : never);
|
|
44
|
+
type SoftDeleteEnabled<Options extends SchemaOptions> = Options['softdelete'] extends true ? true : false;
|
|
45
|
+
export declare const hasSoftDelete: (options: SchemaOptions) => boolean;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/schema/contracts.d.ts
|
|
48
|
+
/** A Zod object shape used as the source of truth for a model. */
|
|
49
|
+
type SchemaShape = z.ZodRawShape;
|
|
50
|
+
/** The runtime Zod object generated from a schema shape. */
|
|
51
|
+
type SchemaDefinition<Shape extends SchemaShape> = z.ZodObject<Shape>;
|
|
52
|
+
/** Population scopes keyed by scope name. */
|
|
53
|
+
type ScopeDefinitions = Record<string, readonly object[]>;
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/query/cursor/cursor.d.ts
|
|
56
|
+
/** A lazy async iterable for one cursor-pagination page. */
|
|
57
|
+
export declare class ModelCursor<Shape extends SchemaShape, Result extends object> implements AsyncIterable<Result> {
|
|
58
|
+
private readonly open;
|
|
59
|
+
private readonly pageSize;
|
|
60
|
+
private readonly transform?;
|
|
61
|
+
next: ObjectId$1 | null;
|
|
62
|
+
constructor(open: () => FindCursor<WithId<StoredDocument<Shape>>>, pageSize: number, transform?: ((documents: Result[]) => Promise<Result[]>) | undefined);
|
|
63
|
+
[Symbol.asyncIterator](): AsyncGenerator<Result>;
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/query/types/document.d.ts
|
|
67
|
+
/** Persisted data inferred from a schema, including MongoDB's generated identifier. */
|
|
68
|
+
type ModelDocument<Shape extends SchemaShape> = z.output<SchemaDefinition<Shape>> & {
|
|
69
|
+
_id: ObjectId$1;
|
|
70
|
+
};
|
|
71
|
+
/** Document type passed to MongoDB's collection APIs. */
|
|
72
|
+
type StoredDocument<Shape extends SchemaShape> = ModelDocument<Shape> & Document;
|
|
73
|
+
type HiddenKey<Shape extends SchemaShape> = { [Key in keyof Shape]: Shape[Key] extends {
|
|
74
|
+
readonly __hidden: true;
|
|
75
|
+
} ? Key : never; }[keyof Shape];
|
|
76
|
+
type HiddenDocumentKey<Shape extends SchemaShape> = Extract<HiddenKey<Shape>, keyof ModelDocument<Shape>> & string;
|
|
77
|
+
/** Fields exposed by an unprojected model query. */
|
|
78
|
+
type VisibleDocument<Shape extends SchemaShape> = Omit<ModelDocument<Shape>, Extract<HiddenKey<Shape>, keyof ModelDocument<Shape>>>;
|
|
79
|
+
type CursorMethod<Shape extends SchemaShape, Result extends object, Ready extends boolean> = Ready extends true ? (after?: ObjectId$1) => ModelCursor<Shape, Result> : undefined;
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/query/types/filter.d.ts
|
|
82
|
+
type NestedFilterKey<Value, Prefix extends string = ''> = Value extends object ? Value extends ObjectId$1 | Date | readonly unknown[] ? never : { [Key in Extract<keyof Value, string>]: NonNullable<Value[Key]> extends object ? `${Prefix}${Key}` | `${Prefix}${Key}.${NestedFilterKey<NonNullable<Value[Key]>>}` : `${Prefix}${Key}`; }[Extract<keyof Value, string>] : never;
|
|
83
|
+
type NestedFilterValue<Value, Path extends string> = Path extends `${infer Head}.${infer Tail}` ? Head extends keyof Value ? NestedFilterValue<NonNullable<Value[Head]>, Tail> : never : Path extends keyof Value ? Value[Path] : never;
|
|
84
|
+
type FilterForDocument<DocumentShape extends Document, FieldShape extends object> = Partial<{ [Key in keyof FieldShape]: Condition<FieldShape[Key]>; }> & Partial<{ [Path in NestedFilterKey<FieldShape>]: Condition<NestedFilterValue<FieldShape, Path>>; }> & Partial<Pick<RootFilterOperators<DocumentShape>, '$comment' | '$expr' | '$jsonSchema' | '$text' | '$where'>> & {
|
|
85
|
+
$and?: FilterForDocument<DocumentShape, FieldShape>[];
|
|
86
|
+
$nor?: FilterForDocument<DocumentShape, FieldShape>[];
|
|
87
|
+
$or?: FilterForDocument<DocumentShape, FieldShape>[];
|
|
88
|
+
};
|
|
89
|
+
/** Schema-checked MongoDB filters, including nested dot-notation paths. */
|
|
90
|
+
type ModelFilter<Shape extends SchemaShape> = FilterForDocument<StoredDocument<Shape>, ModelDocument<Shape>>;
|
|
91
|
+
/** MongoDB sort directions supported by the model query API. */
|
|
92
|
+
type SortDirection = 'asc' | 'desc';
|
|
93
|
+
/** Schema-checked sort specifications. */
|
|
94
|
+
type ModelSort<Shape extends SchemaShape> = Partial<Record<Extract<keyof ModelDocument<Shape>, string>, SortDirection>>;
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/query/types/utils.d.ts
|
|
97
|
+
/** Flatten mapped and intersection types to make editor hints easier to read. */
|
|
98
|
+
type Simplify<Value> = { [Key in keyof Value]: Value[Key]; };
|
|
99
|
+
/** Convert a union into an intersection when merging selected field paths. */
|
|
100
|
+
type UnionToIntersection<Value> = (Value extends unknown ? (input: Value) => void : never) extends ((input: infer Intersection) => void) ? Intersection : never;
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/query/types/selection.d.ts
|
|
103
|
+
type NestedDocumentKeys<Value> = Value extends object ? Value extends ObjectId$1 | Date ? never : { [Key in Extract<keyof Value, string>]: NonNullable<Value[Key]> extends object ? Key | `${Key}.${NestedDocumentKeys<NonNullable<Value[Key]>>}` : Key; }[Extract<keyof Value, string>] : never;
|
|
104
|
+
type NestedSelectableKey<Shape extends SchemaShape> = { [Key in Extract<keyof Shape, string>]: Key extends keyof ModelDocument<Shape> ? NonNullable<ModelDocument<Shape>[Key]> extends object ? `${Key}.${NestedDocumentKeys<NonNullable<ModelDocument<Shape>[Key]>>}` : never : never; }[Extract<keyof Shape, string>];
|
|
105
|
+
/** Field paths accepted by `.select()`. */
|
|
106
|
+
type SelectableKey<Shape extends SchemaShape> = Exclude<Extract<keyof ModelDocument<Shape>, string>, '_id' | HiddenDocumentKey<Shape>> | NestedSelectableKey<Shape>;
|
|
107
|
+
type PathSelection<Value, Path extends string> = Path extends `${infer Head}.${infer Tail}` ? Head extends keyof Value ? { [Key in Head]: PathSelection<NonNullable<Value[Head]>, Tail>; } : never : Path extends keyof Value ? Pick<Value, Path> : never;
|
|
108
|
+
/** Projected result type that preserves the structure of selected nested paths. */
|
|
109
|
+
type SelectedDocument<Shape extends SchemaShape, Key extends SelectableKey<Shape>> = [Key] extends [never] ? VisibleDocument<Shape> : Simplify<Pick<ModelDocument<Shape>, '_id'> & UnionToIntersection<PathSelection<ModelDocument<Shape>, Extract<Key, string>>>>;
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/query/population/types.d.ts
|
|
112
|
+
type RelationTarget<Relation> = Relation extends {
|
|
113
|
+
resolve: () => infer Target;
|
|
114
|
+
} ? Target : never;
|
|
115
|
+
type RelationDocument<Relation> = Infer<RelationTarget<Relation>>;
|
|
116
|
+
type RelationMapOf$1<Relation> = Relation extends {
|
|
117
|
+
readonly __targetRelations?: infer Relations;
|
|
118
|
+
} ? NonNullable<Relations> extends SchemaRelationMap ? NonNullable<Relations> : {} : RelationTarget<Relation> extends {
|
|
119
|
+
readonly relationMap: infer TargetRelations;
|
|
120
|
+
} ? TargetRelations extends SchemaRelationMap ? TargetRelations : {} : {};
|
|
121
|
+
type RelationSelect<Relation> = RelationTarget<Relation> extends Schema<infer TargetShape, any> ? Exclude<SelectableKey<TargetShape>, '_id'> : never;
|
|
122
|
+
type ScopeName<Scopes> = Extract<keyof Scopes, string>;
|
|
123
|
+
type PopulationMode = 'none' | 'populate' | 'scope';
|
|
124
|
+
/** A typed description of one declared relation to load. */
|
|
125
|
+
type PopulateSpec<Relations extends SchemaRelationMap> = { [Name in Extract<keyof Relations, string>]: {
|
|
126
|
+
ref: Name;
|
|
127
|
+
select?: readonly RelationSelect<Relations[Name]>[];
|
|
128
|
+
populate?: PopulateSpecs<RelationMapOf$1<Relations[Name]>>;
|
|
129
|
+
}; }[Extract<keyof Relations, string>];
|
|
130
|
+
/** A list of relation-population specifications. */
|
|
131
|
+
type PopulateSpecs<Relations extends SchemaRelationMap> = readonly PopulateSpec<Relations>[];
|
|
132
|
+
/** Query result after replacing relation IDs with populated documents. */
|
|
133
|
+
type PopulatedResult<Result extends object, Relations extends SchemaRelationMap, Specs extends PopulateSpecs<Relations>> = Simplify<Omit<Result, Extract<Specs[number]['ref'], keyof Result>> & { [Spec in Specs[number] as Spec['ref']]: PopulatedRelation<Relations[Spec['ref']], Spec> | null; }>;
|
|
134
|
+
type PopulatedRelation<Relation, Spec> = Spec extends {
|
|
135
|
+
populate: infer Nested extends PopulateSpecs<RelationMapOf$1<Relation>>;
|
|
136
|
+
} ? RelationDocument<Relation> extends (infer PopulatedDocument extends object) ? PopulatedResult<PopulatedDocument, RelationMapOf$1<Relation>, Nested> : never : RelationDocument<Relation>;
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/schema/indexes.d.ts
|
|
139
|
+
type IndexFieldMap<Shape extends SchemaShape> = Record<Extract<keyof Shape, string>, IndexDirection>;
|
|
140
|
+
/** Non-empty, schema-checked MongoDB index keys. */
|
|
141
|
+
type SchemaIndexFields<Shape extends SchemaShape> = { [Key in keyof IndexFieldMap<Shape>]: Pick<IndexFieldMap<Shape>, Key> & Partial<Omit<IndexFieldMap<Shape>, Key>>; }[keyof IndexFieldMap<Shape>];
|
|
142
|
+
/** Partial-index filter restricted to this schema's fields and operators. */
|
|
143
|
+
type SchemaPartialFilter<Shape extends SchemaShape> = ModelFilter<Shape>;
|
|
144
|
+
type SchemaIndexOptions<Shape extends SchemaShape> = Omit<IndexDescription, 'key' | 'partialFilterExpression'> & {
|
|
145
|
+
readonly partialFilterExpression?: SchemaPartialFilter<Shape>;
|
|
146
|
+
};
|
|
147
|
+
type SchemaIndex<Shape extends SchemaShape> = {
|
|
148
|
+
readonly fields: SchemaIndexFields<Shape>;
|
|
149
|
+
readonly options?: SchemaIndexOptions<Shape>;
|
|
150
|
+
};
|
|
151
|
+
type ExactPartialFilter<Shape extends SchemaShape, Filter> = Filter & Record<Exclude<keyof Filter, keyof SchemaPartialFilter<Shape>>, never>;
|
|
152
|
+
type ValidateIndexDefinition<Shape extends SchemaShape, Definition> = Definition extends {
|
|
153
|
+
readonly options?: infer Options;
|
|
154
|
+
} ? Definition & {
|
|
155
|
+
readonly options?: Options extends {
|
|
156
|
+
readonly partialFilterExpression?: infer Filter;
|
|
157
|
+
} ? Options & {
|
|
158
|
+
readonly partialFilterExpression?: ExactPartialFilter<Shape, Filter>;
|
|
159
|
+
} : Options;
|
|
160
|
+
} : Definition;
|
|
161
|
+
type ValidateIndexDefinitions<Shape extends SchemaShape, Definitions extends readonly unknown[]> = { [Key in keyof Definitions]: ValidateIndexDefinition<Shape, Definitions[Key]>; };
|
|
162
|
+
/** Explicit names declared for typed per-model index management. */
|
|
163
|
+
type SchemaIndexNames<Indexes extends readonly SchemaIndex<any>[]> = Extract<Indexes[number] extends (infer Index) ? Index extends {
|
|
164
|
+
readonly options?: {
|
|
165
|
+
readonly name?: infer Name;
|
|
166
|
+
};
|
|
167
|
+
} ? Name : never : never, string>;
|
|
168
|
+
//#endregion
|
|
3
169
|
//#region src/schema/inference.d.ts
|
|
4
170
|
/** The raw parsed shape produced by a schema before persistence fields are added. */
|
|
5
171
|
type InferShape<T> = T extends {
|
|
@@ -14,21 +180,40 @@ type Infer<T> = InferShape<T> & {
|
|
|
14
180
|
_id: ObjectId$1;
|
|
15
181
|
};
|
|
16
182
|
//#endregion
|
|
17
|
-
//#region src/
|
|
18
|
-
/**
|
|
19
|
-
type
|
|
20
|
-
|
|
21
|
-
|
|
183
|
+
//#region src/relations/ref-fields.d.ts
|
|
184
|
+
/** ObjectId Zod field carrying a lazily resolved relation target. */
|
|
185
|
+
type RefField<Target extends SchemaLike = SchemaLike> = z.ZodType<ObjectId$1> & {
|
|
186
|
+
readonly __ref?: RefDefinition<Target>;
|
|
187
|
+
optional(): OptionalRefField<Target>;
|
|
188
|
+
nullable(): NullableRefField<Target>;
|
|
189
|
+
nullish(): NullishRefField<Target>;
|
|
190
|
+
};
|
|
191
|
+
type OptionalRefField<Target extends SchemaLike = SchemaLike> = z.ZodOptional<RefField<Target>> & {
|
|
192
|
+
readonly __ref?: RefDefinition<Target>;
|
|
193
|
+
};
|
|
194
|
+
type NullableRefField<Target extends SchemaLike = SchemaLike> = z.ZodNullable<RefField<Target>> & {
|
|
195
|
+
readonly __ref?: RefDefinition<Target>;
|
|
196
|
+
};
|
|
197
|
+
type NullishRefField<Target extends SchemaLike = SchemaLike> = z.ZodOptional<NullableRefField<Target>> & {
|
|
198
|
+
readonly __ref?: RefDefinition<Target>;
|
|
199
|
+
};
|
|
200
|
+
type RelationDefinition<Field> = Field extends {
|
|
201
|
+
readonly __ref?: infer Definition;
|
|
202
|
+
} ? NonNullable<Definition> : Field extends z.ZodOptional<infer Inner> ? RelationDefinition<Inner> : Field extends z.ZodNullable<infer Inner> ? RelationDefinition<Inner> : never;
|
|
203
|
+
/** Relation fields inferred from a schema shape. */
|
|
204
|
+
type RelationMap<Shape extends SchemaShape> = { [Key in keyof Shape as RelationDefinition<Shape[Key]> extends never ? never : Key]: RelationDefinition<Shape[Key]>; };
|
|
22
205
|
//#endregion
|
|
23
|
-
//#region src/model/
|
|
206
|
+
//#region src/model/types.d.ts
|
|
24
207
|
type CreateInput<Shape extends SchemaShape, Options extends SchemaOptions> = Omit<InferInput<Schema<Shape, {}, {}, Options>>, '_id'>;
|
|
25
208
|
type UpdateInput<Shape extends SchemaShape, Options extends SchemaOptions> = Partial<Omit<InferInput<Schema<Shape, {}, {}, Options>>, '_id'>>;
|
|
26
|
-
type
|
|
209
|
+
type ModelResult<Shape extends SchemaShape, Relations extends SchemaRelationMap, Scopes extends ScopeDefinitions, Options extends SchemaOptions> = Infer<Schema<Shape, Relations, Scopes, Options>>;
|
|
27
210
|
type IndexNames<Indexes extends readonly SchemaIndex<any>[]> = SchemaIndexNames<Indexes>;
|
|
28
211
|
type IndexManager<Indexes extends readonly SchemaIndex<any>[]> = {
|
|
29
212
|
readonly drop: (names: readonly IndexNames<Indexes>[]) => Promise<void>;
|
|
30
213
|
readonly purge: () => Promise<void>;
|
|
31
214
|
};
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/model/model.d.ts
|
|
32
217
|
/** A MongoDB collection with CRUD operations derived from a schema. */
|
|
33
218
|
export declare class Model<Shape extends SchemaShape, Relations extends SchemaRelationMap = {}, Scopes extends ScopeDefinitions = {}, Options extends SchemaOptions = {}, Indexes extends readonly SchemaIndex<any>[] = []> {
|
|
34
219
|
private readonly db;
|
|
@@ -36,9 +221,9 @@ export declare class Model<Shape extends SchemaShape, Relations extends SchemaRe
|
|
|
36
221
|
private readonly schema;
|
|
37
222
|
readonly index: IndexManager<Indexes>;
|
|
38
223
|
readonly bulk: {
|
|
39
|
-
create: (inputs: readonly CreateInput<Shape, Options>[]) => Promise<
|
|
224
|
+
create: (inputs: readonly CreateInput<Shape, Options>[]) => Promise<ModelResult<Shape, Relations, Scopes, Options>[]>;
|
|
40
225
|
};
|
|
41
|
-
readonly restore: SoftDeleteEnabled<Options> extends true ? (filter: ModelFilter<Shape>) => Promise<
|
|
226
|
+
readonly restore: SoftDeleteEnabled<Options> extends true ? (filter: ModelFilter<Shape>) => Promise<ModelResult<Shape, Relations, Scopes, Options> | null> : never;
|
|
42
227
|
readonly purge: SoftDeleteEnabled<Options> extends true ? (filter: ModelFilter<Shape>) => Promise<DeleteResult> : never;
|
|
43
228
|
/** Create a model bound to a database collection and schema. */
|
|
44
229
|
constructor(db: Db, name: string, schema: Schema<Shape, Relations, Scopes, Options, Indexes>);
|
|
@@ -46,13 +231,13 @@ export declare class Model<Shape extends SchemaShape, Relations extends SchemaRe
|
|
|
46
231
|
private dropIndexes;
|
|
47
232
|
private activeFilter;
|
|
48
233
|
/** Validate and insert one document, generating its ObjectId. */
|
|
49
|
-
create(input: CreateInput<Shape, Options>): Promise<
|
|
234
|
+
create(input: CreateInput<Shape, Options>): Promise<ModelResult<Shape, Relations, Scopes, Options>>;
|
|
50
235
|
private prepareDocument;
|
|
51
236
|
private bulkCreate;
|
|
52
237
|
/** Build a query for all documents matching a MongoDB filter. */
|
|
53
238
|
find(filter?: ModelFilter<Shape>): ModelQuery<Shape, VisibleDocument<Shape>, true, Relations, Scopes, 'none', SoftDeleteEnabled<Options>>;
|
|
54
239
|
/** Validate and apply a partial update to the first matching document. */
|
|
55
|
-
update(filter: ModelFilter<Shape>, patch: UpdateInput<Shape, Options>): Promise<
|
|
240
|
+
update(filter: ModelFilter<Shape>, patch: UpdateInput<Shape, Options>): Promise<ModelResult<Shape, Relations, Scopes, Options> | null>;
|
|
56
241
|
/** Restore matching soft-deleted documents. */
|
|
57
242
|
private restoreDocument;
|
|
58
243
|
/** Delete every document matching a MongoDB filter. */
|
|
@@ -61,9 +246,10 @@ export declare class Model<Shape extends SchemaShape, Relations extends SchemaRe
|
|
|
61
246
|
private purgeDocuments;
|
|
62
247
|
}
|
|
63
248
|
//#endregion
|
|
64
|
-
//#region src/connection/
|
|
249
|
+
//#region src/connection/types.d.ts
|
|
250
|
+
/** Schema registry supplied to a database handle. */
|
|
65
251
|
type SchemaRegistry = Record<string, SchemaLike>;
|
|
66
|
-
/** Configuration for a MongoDB connection. */
|
|
252
|
+
/** Configuration for a MongoDB connection and its registered collections. */
|
|
67
253
|
interface DbOptions<Registry extends SchemaRegistry = SchemaRegistry> {
|
|
68
254
|
/** MongoDB connection string. */
|
|
69
255
|
uri: string;
|
|
@@ -71,14 +257,17 @@ interface DbOptions<Registry extends SchemaRegistry = SchemaRegistry> {
|
|
|
71
257
|
database: string;
|
|
72
258
|
/** Optional native MongoDB client options. */
|
|
73
259
|
clientOptions?: MongoClientOptions;
|
|
74
|
-
/**
|
|
75
|
-
|
|
260
|
+
/** Registered schemas, exposed as matching model properties and collection names. */
|
|
261
|
+
schemas?: Registry;
|
|
76
262
|
}
|
|
77
263
|
type ModelForSchema<SchemaType> = SchemaType extends Schema<infer Shape, infer Relations, infer Scopes, infer Options, infer Indexes extends readonly SchemaIndex<any>[]> ? Model<Shape, Relations, Scopes, Options, Indexes> : never;
|
|
264
|
+
/** Model properties generated from a schema registry. */
|
|
78
265
|
type DatabaseModels<Registry extends SchemaRegistry> = { readonly [Name in keyof Registry]: ModelForSchema<Registry[Name]>; };
|
|
79
266
|
type RegistryOfBuilder<Builder extends {
|
|
80
267
|
readonly __registry?: SchemaRegistry;
|
|
81
268
|
}> = NonNullable<Builder['__registry']>;
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/connection/database.d.ts
|
|
82
271
|
/** Owns a MongoDB client and creates schema-bound models. */
|
|
83
272
|
export declare class Db<Registry extends SchemaRegistry = SchemaRegistry> {
|
|
84
273
|
private readonly options;
|
|
@@ -105,74 +294,17 @@ export declare class Db<Registry extends SchemaRegistry = SchemaRegistry> {
|
|
|
105
294
|
}
|
|
106
295
|
/** Create a disconnected MongoDB database handle. */
|
|
107
296
|
export declare function createDatabase<const Registry extends SchemaRegistry>(options: DbOptions<Registry> & {
|
|
108
|
-
|
|
297
|
+
schemas: Registry;
|
|
109
298
|
}): Db<Registry> & DatabaseModels<Registry>;
|
|
110
299
|
export declare function createDatabase<const Builder extends {
|
|
111
300
|
readonly __registry?: SchemaRegistry;
|
|
112
|
-
}>(options: Omit<DbOptions<RegistryOfBuilder<Builder>>, '
|
|
113
|
-
|
|
301
|
+
}>(options: Omit<DbOptions<RegistryOfBuilder<Builder>>, 'schemas'> & {
|
|
302
|
+
schemas: Builder;
|
|
114
303
|
}): Db<RegistryOfBuilder<Builder>> & DatabaseModels<RegistryOfBuilder<Builder>>;
|
|
115
304
|
export declare function createDatabase(options: DbOptions): Db;
|
|
116
305
|
//#endregion
|
|
117
|
-
//#region src/query/
|
|
118
|
-
|
|
119
|
-
export declare class ModelCursor<Shape extends SchemaShape, Result extends object> implements AsyncIterable<Result> {
|
|
120
|
-
private readonly open;
|
|
121
|
-
private readonly pageSize;
|
|
122
|
-
private readonly transform?;
|
|
123
|
-
next: ObjectId$1 | null;
|
|
124
|
-
constructor(open: () => FindCursor<WithId<StoredDocument<Shape>>>, pageSize: number, transform?: ((documents: Result[]) => Promise<Result[]>) | undefined);
|
|
125
|
-
[Symbol.asyncIterator](): AsyncGenerator<Result>;
|
|
126
|
-
}
|
|
127
|
-
//#endregion
|
|
128
|
-
//#region src/query/types.d.ts
|
|
129
|
-
type StoredDocument<Shape extends SchemaShape> = Infer<Schema<Shape>> & Document;
|
|
130
|
-
type ModelFilterForDocument<DocumentShape extends Document, FieldShape extends object = DocumentShape> = Partial<{ [Key in keyof FieldShape]: Condition<FieldShape[Key]>; }> & Partial<{ [Path in NestedFilterKey<FieldShape>]: Condition<NestedFilterValue<FieldShape, Path>>; }> & Partial<Pick<RootFilterOperators<DocumentShape>, '$comment' | '$expr' | '$jsonSchema' | '$text' | '$where'>> & {
|
|
131
|
-
$and?: ModelFilterForDocument<DocumentShape, FieldShape>[];
|
|
132
|
-
$nor?: ModelFilterForDocument<DocumentShape, FieldShape>[];
|
|
133
|
-
$or?: ModelFilterForDocument<DocumentShape, FieldShape>[];
|
|
134
|
-
};
|
|
135
|
-
type NestedFilterKey<Value, Prefix extends string = ''> = Value extends object ? Value extends ObjectId$1 | Date | readonly unknown[] ? never : { [Key in Extract<keyof Value, string>]: NonNullable<Value[Key]> extends object ? `${Prefix}${Key}` | `${Prefix}${Key}.${NestedFilterKey<NonNullable<Value[Key]>>}` : `${Prefix}${Key}`; }[Extract<keyof Value, string>] : never;
|
|
136
|
-
type NestedFilterValue<Value, Path extends string> = Path extends `${infer Head}.${infer Tail}` ? Head extends keyof Value ? NestedFilterValue<NonNullable<Value[Head]>, Tail> : never : Path extends keyof Value ? Value[Path] : never;
|
|
137
|
-
type ModelFilter<Shape extends SchemaShape> = ModelFilterForDocument<StoredDocument<Shape>, Infer<Schema<Shape>>>;
|
|
138
|
-
type SortDirection = 'asc' | 'desc';
|
|
139
|
-
type ModelSort<Shape extends SchemaShape> = Partial<Record<Extract<keyof Infer<Schema<Shape>>, string>, SortDirection>>;
|
|
140
|
-
type ModelDocument<Shape extends SchemaShape> = Infer<Schema<Shape>>;
|
|
141
|
-
type HiddenKey<Shape extends SchemaShape> = { [Key in keyof Shape]: Shape[Key] extends {
|
|
142
|
-
readonly __hidden: true;
|
|
143
|
-
} ? Key : never; }[keyof Shape];
|
|
144
|
-
type HiddenDocumentKey<Shape extends SchemaShape> = Extract<HiddenKey<Shape>, keyof ModelDocument<Shape>> & string;
|
|
145
|
-
type NestedDocumentKeys<Value, Prefix extends string = ''> = Value extends object ? Value extends ObjectId$1 | Date ? never : { [Key in Extract<keyof Value, string>]: NonNullable<Value[Key]> extends object ? `${Prefix}${Key}` | `${Prefix}${Key}.${NestedDocumentKeys<NonNullable<Value[Key]>>}` : `${Prefix}${Key}`; }[Extract<keyof Value, string>] : never;
|
|
146
|
-
type NestedSelectableKey<Shape extends SchemaShape> = { [Key in Extract<keyof Shape, string>]: Key extends keyof ModelDocument<Shape> ? NonNullable<ModelDocument<Shape>[Key]> extends object ? `${Key}.${NestedDocumentKeys<NonNullable<ModelDocument<Shape>[Key]>>}` : never : never; }[Extract<keyof Shape, string>];
|
|
147
|
-
type CursorMethod<Shape extends SchemaShape, Result extends object, Ready extends boolean> = Ready extends true ? (after?: ObjectId$1) => ModelCursor<Shape, Result> : undefined;
|
|
148
|
-
type SelectableKey<Shape extends SchemaShape> = Exclude<Extract<keyof ModelDocument<Shape>, string>, '_id' | HiddenDocumentKey<Shape>> | NestedSelectableKey<Shape>;
|
|
149
|
-
type PathSelection<Value, Path extends string> = Path extends `${infer Head}.${infer Tail}` ? Head extends keyof Value ? { [Key in Head]: PathSelection<NonNullable<Value[Key]>, Tail>; } : never : Path extends keyof Value ? Pick<Value, Path> : never;
|
|
150
|
-
type UnionToIntersection<Value> = (Value extends unknown ? (input: Value) => void : never) extends ((input: infer Intersection) => void) ? Intersection : never;
|
|
151
|
-
type Simplify<Value> = { [Key in keyof Value]: Value[Key]; };
|
|
152
|
-
type VisibleDocument<Shape extends SchemaShape> = Omit<ModelDocument<Shape>, Extract<HiddenKey<Shape>, keyof ModelDocument<Shape>>>;
|
|
153
|
-
type SelectedDocument<Shape extends SchemaShape, Key extends SelectableKey<Shape>> = [Key] extends [never] ? VisibleDocument<Shape> : Simplify<Pick<ModelDocument<Shape>, '_id'> & UnionToIntersection<PathSelection<ModelDocument<Shape>, Extract<Key, string>>>>;
|
|
154
|
-
type RelationTarget<Relation> = Relation extends {
|
|
155
|
-
resolve: () => infer Target;
|
|
156
|
-
} ? Target : never;
|
|
157
|
-
type RelationDocument<Relation> = RelationTarget<Relation> extends Schema<infer TargetShape, any> ? Infer<Schema<TargetShape>> : never;
|
|
158
|
-
type RelationMapOf$1<Relation> = RelationTarget<Relation> extends {
|
|
159
|
-
readonly relationMap: infer TargetRelations;
|
|
160
|
-
} ? TargetRelations extends SchemaRelationMap ? TargetRelations : {} : {};
|
|
161
|
-
type ScopeName<Scopes> = Extract<keyof Scopes, string>;
|
|
162
|
-
type PopulationMode = 'none' | 'populate' | 'scope';
|
|
163
|
-
type RelationSelect<Relation> = RelationTarget<Relation> extends Schema<infer TargetShape, any> ? Exclude<SelectableKey<TargetShape>, '_id'> : never;
|
|
164
|
-
type PopulateSpec<Relations extends SchemaRelationMap> = { [Name in Extract<keyof Relations, string>]: {
|
|
165
|
-
ref: Name;
|
|
166
|
-
select?: readonly RelationSelect<Relations[Name]>[];
|
|
167
|
-
populate?: PopulateSpecs<RelationMapOf$1<Relations[Name]>>;
|
|
168
|
-
}; }[Extract<keyof Relations, string>];
|
|
169
|
-
type PopulateSpecs<Relations extends SchemaRelationMap> = readonly PopulateSpec<Relations>[];
|
|
170
|
-
type PopulatedResult<Result extends object, Relations extends SchemaRelationMap, Specs extends PopulateSpecs<Relations>> = Omit<Result, Extract<Specs[number]['ref'], keyof Result>> & { [Spec in Specs[number] as Spec['ref']]: PopulatedRelation<Relations[Spec['ref']], Spec> | null; };
|
|
171
|
-
type PopulatedRelation<Relation, Spec> = Spec extends {
|
|
172
|
-
populate: infer Nested extends PopulateSpecs<RelationMapOf$1<Relation>>;
|
|
173
|
-
} ? RelationDocument<Relation> extends (infer PopulatedDocument extends object) ? PopulatedResult<PopulatedDocument, RelationMapOf$1<Relation>, Nested> : never : RelationDocument<Relation>;
|
|
174
|
-
//#endregion
|
|
175
|
-
//#region src/query/many-query.d.ts
|
|
306
|
+
//#region src/query/builder/find-query.d.ts
|
|
307
|
+
type ScopeResult<Result extends object, Relations extends SchemaRelationMap, Scopes extends ScopeDefinitions, Name extends ScopeName<Scopes>> = PopulatedResult<Result, Relations, Extract<Scopes[Name], PopulateSpecs<Relations>>>;
|
|
176
308
|
/** A typed, awaitable MongoDB find query. */
|
|
177
309
|
export declare class ModelQuery<Shape extends SchemaShape, Result extends object = VisibleDocument<Shape>, CursorReady extends boolean = true, Relations extends SchemaRelationMap = {}, Scopes extends ScopeDefinitions = {}, Mode extends PopulationMode = 'none', SoftDelete extends boolean = false> implements PromiseLike<Result[]> {
|
|
178
310
|
private readonly collection;
|
|
@@ -198,6 +330,7 @@ export declare class ModelQuery<Shape extends SchemaShape, Result extends object
|
|
|
198
330
|
/** Include both active and soft-deleted documents in this query. */
|
|
199
331
|
private deletedMode;
|
|
200
332
|
private effectiveFilter;
|
|
333
|
+
private executionContext;
|
|
201
334
|
/** Sort results by one or more schema fields. */
|
|
202
335
|
sort(spec: ModelSort<Shape>): ModelQuery<Shape, Result, false, Relations, Scopes, Mode, SoftDelete>;
|
|
203
336
|
/** Skip a non-negative number of matching documents. */
|
|
@@ -211,91 +344,18 @@ export declare class ModelQuery<Shape extends SchemaShape, Result extends object
|
|
|
211
344
|
/** Populate declared one-way relations, including nested relation arrays. */
|
|
212
345
|
populate<Specs extends PopulateSpecs<Relations>>(this: Mode extends 'scope' ? never : ModelQuery<Shape, Result, CursorReady, Relations, Scopes, Mode, SoftDelete>, specs: Specs): ModelQuery<Shape, PopulatedResult<Result, Relations, Specs>, CursorReady, Relations, Scopes, 'populate', SoftDelete>;
|
|
213
346
|
/** Apply a named population scope. */
|
|
214
|
-
with<Name extends ScopeName<Scopes>>(this: Mode extends 'populate' ? never : ModelQuery<Shape, Result, CursorReady, Relations, Scopes, Mode, SoftDelete>, name: Name): ModelQuery<Shape,
|
|
347
|
+
with<Name extends ScopeName<Scopes>>(this: Mode extends 'populate' ? never : ModelQuery<Shape, Result, CursorReady, Relations, Scopes, Mode, SoftDelete>, name: Name): ModelQuery<Shape, ScopeResult<Result, Relations, Scopes, Name>, CursorReady, Relations, Scopes, 'scope', SoftDelete>;
|
|
215
348
|
/** Count matching documents, optionally using MongoDB's collection estimate. */
|
|
216
349
|
count(estimate?: boolean): Promise<number>;
|
|
217
|
-
/** Return one `_id`-ordered page and
|
|
350
|
+
/** Return one bounded `_id`-ordered page and its continuation cursor. */
|
|
218
351
|
private createCursor;
|
|
219
352
|
private execute;
|
|
220
353
|
first(): Promise<Result | null>;
|
|
221
|
-
private createQueryCursor;
|
|
222
354
|
then<TResult1 = Result[], TResult2 = never>(onfulfilled?: ((value: Result[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
223
355
|
}
|
|
224
356
|
//#endregion
|
|
225
|
-
//#region src/schema/scalars.d.ts
|
|
226
|
-
declare module 'zod' {
|
|
227
|
-
interface ZodType {
|
|
228
|
-
hidden(): this & {
|
|
229
|
-
readonly __hidden: true;
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
/** A schema field that can be marked as hidden from default query results. */
|
|
234
|
-
type HiddenCapable<T extends z.ZodType> = Omit<T, 'optional' | 'nullable' | 'nullish'> & HiddenMethods<T>;
|
|
235
|
-
/** A schema field marked as hidden from default query results. */
|
|
236
|
-
type HiddenSchema<T extends z.ZodType> = Omit<T, 'optional' | 'nullable' | 'nullish'> & HiddenMethods<T> & {
|
|
237
|
-
readonly __hidden: true;
|
|
238
|
-
};
|
|
239
|
-
type HiddenMethods<T extends z.ZodType> = {
|
|
240
|
-
hidden(): HiddenSchema<T>;
|
|
241
|
-
optional(): HiddenCapable<z.ZodOptional<T>>;
|
|
242
|
-
nullable(): HiddenCapable<z.ZodNullable<T>>;
|
|
243
|
-
nullish(): HiddenCapable<z.ZodOptional<z.ZodNullable<T>>>;
|
|
244
|
-
};
|
|
245
|
-
/** Create a MongoDB ObjectId schema. */
|
|
246
|
-
declare const objectId: () => HiddenCapable<z.ZodInstanceOf<ObjectId$1>>;
|
|
247
|
-
//#endregion
|
|
248
357
|
//#region src/schema/schema.d.ts
|
|
249
|
-
/** Built-in persistence behavior applied by a schema. */
|
|
250
|
-
interface SchemaOptions {
|
|
251
|
-
readonly timestamps?: boolean;
|
|
252
|
-
readonly softdelete?: boolean;
|
|
253
|
-
/** Hide Mongorm-managed fields from default query results. */
|
|
254
|
-
readonly hideManaged?: boolean;
|
|
255
|
-
}
|
|
256
|
-
type IndexFieldMap<Shape extends SchemaShape> = Record<Extract<keyof Shape, string>, IndexDirection>;
|
|
257
|
-
/** A non-empty, autocomplete-friendly MongoDB index key definition. */
|
|
258
|
-
type SchemaIndexFields<Shape extends SchemaShape> = { [Key in keyof IndexFieldMap<Shape>]: Pick<IndexFieldMap<Shape>, Key> & Partial<Omit<IndexFieldMap<Shape>, Key>>; }[keyof IndexFieldMap<Shape>];
|
|
259
|
-
/** A schema-aware MongoDB partial-index filter. */
|
|
260
|
-
type SchemaPartialFilter<Shape extends SchemaShape> = ModelFilter<Shape>;
|
|
261
|
-
type SchemaIndexOptions<Shape extends SchemaShape> = Omit<IndexDescription, 'key' | 'partialFilterExpression'> & {
|
|
262
|
-
/** Restrict indexed documents using schema-aware MongoDB filter operators. */
|
|
263
|
-
readonly partialFilterExpression?: SchemaPartialFilter<Shape>;
|
|
264
|
-
};
|
|
265
|
-
type SchemaIndex<Shape extends SchemaShape> = {
|
|
266
|
-
readonly fields: SchemaIndexFields<Shape>;
|
|
267
|
-
readonly options?: SchemaIndexOptions<Shape>;
|
|
268
|
-
};
|
|
269
|
-
type ExactPartialFilter<Shape extends SchemaShape, Filter> = Filter & Record<Exclude<keyof Filter, keyof SchemaPartialFilter<Shape>>, never>;
|
|
270
|
-
type ValidateIndexDefinition<Shape extends SchemaShape, Definition> = Definition extends {
|
|
271
|
-
readonly options?: infer Options;
|
|
272
|
-
} ? Definition & {
|
|
273
|
-
readonly options?: Options extends {
|
|
274
|
-
readonly partialFilterExpression?: infer Filter;
|
|
275
|
-
} ? Options & {
|
|
276
|
-
readonly partialFilterExpression?: ExactPartialFilter<Shape, Filter>;
|
|
277
|
-
} : Options;
|
|
278
|
-
} : Definition;
|
|
279
|
-
type ValidateIndexDefinitions<Shape extends SchemaShape, Definitions extends readonly unknown[]> = { [Key in keyof Definitions]: ValidateIndexDefinition<Shape, Definitions[Key]>; };
|
|
280
|
-
type SchemaIndexNames<Indexes extends readonly SchemaIndex<any>[]> = Extract<Indexes[number] extends (infer Index) ? Index extends {
|
|
281
|
-
readonly options?: {
|
|
282
|
-
readonly name?: infer Name;
|
|
283
|
-
};
|
|
284
|
-
} ? Name : never : never, string>;
|
|
285
|
-
type ManagedSchema<T extends z.ZodType, Options extends SchemaOptions> = Options['hideManaged'] extends true ? HiddenSchema<T> : T;
|
|
286
|
-
type TimestampShape<Options extends SchemaOptions> = {
|
|
287
|
-
createdAt: ManagedSchema<z.ZodDefault<z.ZodDate>, Options>;
|
|
288
|
-
updatedAt: ManagedSchema<z.ZodDefault<z.ZodDate>, Options>;
|
|
289
|
-
};
|
|
290
|
-
type SoftDeleteShape<Options extends SchemaOptions> = {
|
|
291
|
-
deletedAt: ManagedSchema<z.ZodDefault<z.ZodNullable<z.ZodDate>>, Options>;
|
|
292
|
-
};
|
|
293
|
-
type ManagedShape<Options extends SchemaOptions> = (Options['timestamps'] extends true ? TimestampShape<Options> : {}) & (Options['softdelete'] extends true ? SoftDeleteShape<Options> : {});
|
|
294
|
-
type ManagedField<Options extends SchemaOptions> = (Options['timestamps'] extends true ? 'createdAt' | 'updatedAt' : never) | (Options['softdelete'] extends true ? 'deletedAt' : never);
|
|
295
|
-
type SoftDeleteEnabled<Options extends SchemaOptions> = Options['softdelete'] extends true ? true : false;
|
|
296
|
-
export declare const hasSoftDelete: (options: SchemaOptions) => boolean;
|
|
297
358
|
type ObjectIdFieldKeys<Shape extends SchemaShape> = { [Key in keyof InferShape<Schema<Shape>>]-?: NonNullable<InferShape<Schema<Shape>>[Key]> extends ObjectId$1 ? Key : never; }[keyof InferShape<Schema<Shape>>];
|
|
298
|
-
type ScopeDefinitions = Record<string, readonly object[]>;
|
|
299
359
|
/** A typed, runtime-validated schema definition. */
|
|
300
360
|
export declare class Schema<Shape extends SchemaShape, Relations extends SchemaRelationMap = {}, Scopes extends ScopeDefinitions = {}, Options extends SchemaOptions = {}, Indexes extends readonly SchemaIndex<any>[] = []> {
|
|
301
361
|
/** The underlying Zod object for advanced validation use cases. */
|
|
@@ -320,7 +380,6 @@ export declare class Schema<Shape extends SchemaShape, Relations extends SchemaR
|
|
|
320
380
|
constructor(shape: Shape, relations?: Relations, scopeMap?: Scopes, optionsConfig?: Options, indexDefinitions?: Indexes);
|
|
321
381
|
/** Enable managed timestamps and/or soft deletion for this schema. */
|
|
322
382
|
options<const Enabled extends SchemaOptions>(options: Enabled): Schema<Shape & ManagedShape<Enabled>, Relations, Scopes, Enabled, Indexes>;
|
|
323
|
-
private managedField;
|
|
324
383
|
/** Declare MongoDB indexes for explicit synchronization with the database. */
|
|
325
384
|
indexes<const Definitions extends readonly SchemaIndex<Shape>[]>(definitions: readonly SchemaIndex<Shape>[] & ValidateIndexDefinitions<Shape, Definitions>): Schema<Shape, Relations, Scopes, Options, Definitions>;
|
|
326
385
|
/** Add one or more one-way relations without requiring circular schema declarations. */
|
|
@@ -338,69 +397,51 @@ export declare class Schema<Shape extends SchemaShape, Relations extends SchemaR
|
|
|
338
397
|
}
|
|
339
398
|
//#endregion
|
|
340
399
|
//#region src/relations/definitions.d.ts
|
|
341
|
-
/**
|
|
342
|
-
type SchemaLike = Schema<
|
|
343
|
-
/** Metadata
|
|
400
|
+
/** Schema object accepted as a lazy relation target or registry entry. */
|
|
401
|
+
type SchemaLike = Schema<any, any, any, any, any>;
|
|
402
|
+
/** Metadata for a schema target resolved lazily to support circular imports. */
|
|
344
403
|
interface RefDefinition<Target extends SchemaLike = SchemaLike> {
|
|
345
|
-
/** Resolve the related schema after module initialization completes. */
|
|
346
404
|
resolve: () => Target;
|
|
347
405
|
}
|
|
348
|
-
/** Metadata for a one-way relation between two
|
|
349
|
-
interface SchemaRelation<Target extends SchemaLike = SchemaLike, LocalField extends string = string, ForeignField extends string = string
|
|
406
|
+
/** Metadata for a one-way relation between two registered schemas. */
|
|
407
|
+
interface SchemaRelation<Target extends SchemaLike = SchemaLike, LocalField extends string = string, ForeignField extends string = string, TargetRelations = Target extends {
|
|
408
|
+
readonly relationMap: infer Relations;
|
|
409
|
+
} ? Relations : {}> {
|
|
350
410
|
readonly resolve: () => Target;
|
|
351
411
|
readonly localField: LocalField;
|
|
352
412
|
readonly foreignField: ForeignField;
|
|
413
|
+
/** Target relations are carried separately to avoid recursively wrapping its schema type. */
|
|
414
|
+
readonly __targetRelations?: TargetRelations;
|
|
353
415
|
}
|
|
354
|
-
/** Relation metadata attached to a schema. */
|
|
355
416
|
type SchemaRelationMap = Record<string, SchemaRelation>;
|
|
356
|
-
/**
|
|
417
|
+
/** Relation target declaration accepted by `Schema.relations()`. */
|
|
357
418
|
type RelationInput<Target extends SchemaLike = SchemaLike> = (() => Target) | {
|
|
358
419
|
target: () => Target;
|
|
359
420
|
foreignField?: string;
|
|
360
421
|
};
|
|
361
|
-
/** Extract a relation target from a relation declaration. */
|
|
362
422
|
type RelationInputTarget<Input> = Input extends (() => infer Target) ? Target : Input extends {
|
|
363
423
|
target: () => infer Target;
|
|
364
424
|
} ? Target : never;
|
|
365
|
-
/** A string identifier field carrying a typed relation target. */
|
|
366
|
-
type RefField<Target extends SchemaLike = SchemaLike> = z.ZodType<ObjectId$1> & {
|
|
367
|
-
readonly __ref?: RefDefinition<Target>;
|
|
368
|
-
optional(): OptionalRefField<Target>;
|
|
369
|
-
nullable(): NullableRefField<Target>;
|
|
370
|
-
nullish(): NullishRefField<Target>;
|
|
371
|
-
};
|
|
372
|
-
/** An optional relation field that retains its target metadata. */
|
|
373
|
-
type OptionalRefField<Target extends SchemaLike = SchemaLike> = z.ZodOptional<RefField<Target>> & {
|
|
374
|
-
readonly __ref?: RefDefinition<Target>;
|
|
375
|
-
};
|
|
376
|
-
/** A nullable relation field that retains its target metadata. */
|
|
377
|
-
type NullableRefField<Target extends SchemaLike = SchemaLike> = z.ZodNullable<RefField<Target>> & {
|
|
378
|
-
readonly __ref?: RefDefinition<Target>;
|
|
379
|
-
};
|
|
380
|
-
/** An optional and nullable relation field that retains its target metadata. */
|
|
381
|
-
type NullishRefField<Target extends SchemaLike = SchemaLike> = z.ZodOptional<NullableRefField<Target>> & {
|
|
382
|
-
readonly __ref?: RefDefinition<Target>;
|
|
383
|
-
};
|
|
384
|
-
type RelationDefinition<Field> = Field extends {
|
|
385
|
-
readonly __ref?: infer Definition;
|
|
386
|
-
} ? NonNullable<Definition> : Field extends z.ZodOptional<infer Inner> ? RelationDefinition<Inner> : Field extends z.ZodNullable<infer Inner> ? RelationDefinition<Inner> : never;
|
|
387
|
-
/** The relation metadata inferred from a schema shape. */
|
|
388
|
-
type RelationMap<Shape extends SchemaShape> = { [Key in keyof Shape as RelationDefinition<Shape[Key]> extends never ? never : Key]: RelationDefinition<Shape[Key]>; };
|
|
389
425
|
//#endregion
|
|
390
|
-
//#region src/relations/registry.d.ts
|
|
426
|
+
//#region src/relations/registry-types.d.ts
|
|
391
427
|
type ObjectIdKeys<Shape extends SchemaShape> = { [Key in keyof InferShape<Schema<Shape>>]-?: NonNullable<InferShape<Schema<Shape>>[Key]> extends ObjectId$1 ? Key : never; }[keyof InferShape<Schema<Shape>>];
|
|
428
|
+
/** Valid local ObjectId fields and targets accepted by `defineRelations`. */
|
|
392
429
|
type RelationDefinitions<Registry extends Record<string, SchemaLike>> = { [Name in keyof Registry]?: Registry[Name] extends Schema<infer Shape, any, any, any> ? Partial<Record<Extract<ObjectIdKeys<Shape>, string>, Extract<keyof Registry, string>>> : never; };
|
|
393
|
-
type
|
|
394
|
-
type RelationsFor<Registry extends Record<string, SchemaLike>, AllDefinitions extends RelationDefinitions<Registry>, Definitions> = { [Field in keyof Definitions & string]: Definitions[Field] extends keyof Registry ? SchemaRelation<EnrichedSchema<Registry, AllDefinitions, Definitions[Field]>, Field, '_id'> : never; };
|
|
430
|
+
type RelationsFor<Registry extends Record<string, SchemaLike>, AllDefinitions extends RelationDefinitions<Registry>, Definitions> = { [Field in keyof Definitions & string]: Definitions[Field] extends keyof Registry ? SchemaRelation<Registry[Definitions[Field]], Field, '_id', RelationsFor<Registry, AllDefinitions, NonNullable<AllDefinitions[Definitions[Field]]>>> : never; };
|
|
395
431
|
type RegistryWithRelations<Registry extends Record<string, SchemaLike>, Definitions extends RelationDefinitions<Registry>> = { [Name in keyof Registry]: Registry[Name] extends Schema<infer Shape, infer Relations, infer Scopes, infer Options> ? Schema<Shape, Relations & RelationsFor<Registry, Definitions, NonNullable<Definitions[Name]>>, Scopes, Options, Registry[Name] extends Schema<any, any, any, any, infer Indexes extends readonly SchemaIndex<any>[]> ? Indexes : []> : Registry[Name]; };
|
|
396
|
-
type RelationMapOf<Value> = Value extends Schema<any, infer Relations, any, any> ? Relations : {};
|
|
432
|
+
type RelationMapOf<Value> = Value extends SchemaRelation<any, any, any, infer Relations> ? Relations : Value extends Schema<any, infer Relations, any, any> ? Relations : {};
|
|
433
|
+
/** Scope declarations accepted for each registered model. */
|
|
397
434
|
type ScopeDefinitionsBySchema<Registry extends Record<string, SchemaLike>> = { [Name in keyof Registry]?: Record<string, PopulateSpecs<RelationMapOf<Registry[Name]>>>; };
|
|
398
435
|
type RegistryWithScopes<Registry extends Record<string, SchemaLike>, Definitions extends ScopeDefinitionsBySchema<Registry>> = { [Name in keyof Registry]: Registry[Name] extends Schema<infer Shape, infer Relations, infer Scopes, infer Options> ? Schema<Shape, Relations, Scopes & (Definitions[Name] extends ScopeDefinitions ? Definitions[Name] : {}), Options, Registry[Name] extends Schema<any, any, any, any, infer Indexes extends readonly SchemaIndex<any>[]> ? Indexes : []> : Registry[Name]; };
|
|
436
|
+
/** Typed registry builder API, with relations and scopes reflected in model types. */
|
|
399
437
|
type SchemaRegistryBuilder<Registry extends Record<string, SchemaLike>> = Registry & {
|
|
400
438
|
readonly __registry?: Registry;
|
|
401
439
|
defineRelations<const Definitions extends RelationDefinitions<Registry>>(definitions: Definitions): SchemaRegistryBuilder<RegistryWithRelations<Registry, Definitions>>;
|
|
402
440
|
defineScopes<const Definitions extends ScopeDefinitionsBySchema<Registry>>(definitions: Definitions): SchemaRegistryBuilder<RegistryWithScopes<Registry, Definitions>>;
|
|
403
441
|
};
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region src/relations/registry.d.ts
|
|
444
|
+
/** Create a registry of named schemas with typed relation and scope builders. */
|
|
404
445
|
declare const createSchemaRegistry: <const Registry extends Record<string, SchemaLike>>(registry: Registry) => SchemaRegistryBuilder<Registry>;
|
|
405
446
|
//#endregion
|
|
406
447
|
//#region src/api.d.ts
|
|
@@ -450,4 +491,4 @@ export declare class EstimatedCountError extends InvalidQueryError {
|
|
|
450
491
|
constructor();
|
|
451
492
|
}
|
|
452
493
|
//#endregion
|
|
453
|
-
export { type DatabaseModels, type DbOptions, type HiddenDocumentKey, type Infer, type InferInput, type InferShape, type ManagedField, type ModelFilter, type ModelSort, ObjectId, type OrmApi, type OrmErrorCode, type PopulateSpec, type PopulateSpecs, type PopulatedResult, type RefDefinition, type RefField, type RelationDefinitions, type RelationMap, type SchemaDefinition, type SchemaIndex, type SchemaIndexFields, type SchemaLike, type SchemaOptions, type SchemaRegistry, type SchemaRegistryBuilder, type SchemaRelation, type SchemaRelationMap, type SchemaShape, type ScopeDefinitionsBySchema, type SelectableKey, type SelectedDocument, type SoftDeleteEnabled, type StoredDocument, type VisibleDocument };
|
|
494
|
+
export { type DatabaseModels, type DbOptions, type HiddenDocumentKey, type Infer, type InferInput, type InferShape, type ManagedField, type ModelFilter, type ModelSort, type NullableRefField, type NullishRefField, ObjectId, type OptionalRefField, type OrmApi, type OrmErrorCode, type PopulateSpec, type PopulateSpecs, type PopulatedResult, type RefDefinition, type RefField, type RelationDefinitions, type RelationMap, type SchemaDefinition, type SchemaIndex, type SchemaIndexFields, type SchemaLike, type SchemaOptions, type SchemaRegistry, type SchemaRegistryBuilder, type SchemaRelation, type SchemaRelationMap, type SchemaShape, type ScopeDefinitionsBySchema, type SelectableKey, type SelectedDocument, type SoftDeleteEnabled, type StoredDocument, type VisibleDocument };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MongoClient, ObjectId, ObjectId as ObjectId$1 } from "mongodb";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
//#region src/relations/
|
|
4
|
-
/** Create a
|
|
3
|
+
//#region src/relations/ref-fields.ts
|
|
4
|
+
/** Create a typed ObjectId field linked to a lazily resolved schema. */
|
|
5
5
|
const createRef = (resolve) => {
|
|
6
6
|
const field = z.instanceof(ObjectId$1);
|
|
7
7
|
const createOptional = field.optional.bind(field);
|
|
@@ -37,22 +37,21 @@ const attachRef = (field, resolve) => {
|
|
|
37
37
|
});
|
|
38
38
|
return field;
|
|
39
39
|
};
|
|
40
|
-
/** Collect
|
|
41
|
-
const collectRefs = (shape) =>
|
|
42
|
-
return Object.fromEntries(Object.entries(shape).filter(([, field]) => "__ref" in field).map(([name, field]) => [name, field.__ref]));
|
|
43
|
-
};
|
|
40
|
+
/** Collect ref field metadata without evaluating target schemas. */
|
|
41
|
+
const collectRefs = (shape) => Object.fromEntries(Object.entries(shape).filter(([, field]) => "__ref" in field).map(([name, field]) => [name, field.__ref]));
|
|
44
42
|
//#endregion
|
|
45
43
|
//#region src/relations/registry.ts
|
|
44
|
+
/** Attach relation/scope builder methods and apply definitions to schema metadata. */
|
|
46
45
|
const attachMethods = (registry) => {
|
|
47
46
|
const defineRelations = (definitions) => {
|
|
48
47
|
for (const [name, relations] of Object.entries(definitions)) {
|
|
49
|
-
const
|
|
50
|
-
if (!
|
|
48
|
+
const source = registry[name];
|
|
49
|
+
if (!source) throw new Error(`Unknown schema "${name}" in relation definitions`);
|
|
51
50
|
for (const [field, targetName] of Object.entries(relations ?? {})) {
|
|
52
51
|
const target = registry[targetName];
|
|
53
52
|
if (!target) throw new Error(`Unknown relation target "${targetName}"`);
|
|
54
|
-
if (!(field in
|
|
55
|
-
|
|
53
|
+
if (!(field in source.definition.shape)) throw new Error(`Unknown relation field "${name}.${field}"`);
|
|
54
|
+
source.relationMap[field] = {
|
|
56
55
|
resolve: () => target,
|
|
57
56
|
localField: field,
|
|
58
57
|
foreignField: "_id"
|
|
@@ -63,9 +62,9 @@ const attachMethods = (registry) => {
|
|
|
63
62
|
};
|
|
64
63
|
const defineScopes = (definitions) => {
|
|
65
64
|
for (const [name, scopes] of Object.entries(definitions)) {
|
|
66
|
-
const
|
|
67
|
-
if (!
|
|
68
|
-
Object.assign(
|
|
65
|
+
const schema = registry[name];
|
|
66
|
+
if (!schema) throw new Error(`Unknown schema "${name}" in scope definitions`);
|
|
67
|
+
Object.assign(schema.scopeMap, scopes);
|
|
69
68
|
}
|
|
70
69
|
return attachMethods(registry);
|
|
71
70
|
};
|
|
@@ -88,6 +87,7 @@ const attachMethods = (registry) => {
|
|
|
88
87
|
});
|
|
89
88
|
return registry;
|
|
90
89
|
};
|
|
90
|
+
/** Create a registry of named schemas with typed relation and scope builders. */
|
|
91
91
|
const createSchemaRegistry = (registry) => attachMethods(registry);
|
|
92
92
|
//#endregion
|
|
93
93
|
//#region src/schema/scalars.ts
|
|
@@ -133,8 +133,11 @@ const withZodNamespace = (namespace) => new Proxy(namespace, { get(target, prope
|
|
|
133
133
|
/** Create a MongoDB ObjectId schema. */
|
|
134
134
|
const objectId = () => withHidden(z.instanceof(ObjectId$1));
|
|
135
135
|
//#endregion
|
|
136
|
-
//#region src/schema/
|
|
136
|
+
//#region src/schema/options.ts
|
|
137
137
|
const hasSoftDelete = (options) => options.softdelete === true;
|
|
138
|
+
const managedField = (field, options) => options.hideManaged ? withHidden(field).hidden() : field;
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/schema/schema.ts
|
|
138
141
|
/** A typed, runtime-validated schema definition. */
|
|
139
142
|
var Schema = class Schema {
|
|
140
143
|
/** The underlying Zod object for advanced validation use cases. */
|
|
@@ -171,19 +174,16 @@ var Schema = class Schema {
|
|
|
171
174
|
if (options.softdelete && "deletedAt" in this.definition.shape) throw new Error("The deletedAt field is managed by Mongorm");
|
|
172
175
|
const managedShape = {
|
|
173
176
|
...options.timestamps ? {
|
|
174
|
-
createdAt:
|
|
175
|
-
updatedAt:
|
|
177
|
+
createdAt: managedField(z.date().default(() => /* @__PURE__ */ new Date()), options),
|
|
178
|
+
updatedAt: managedField(z.date().default(() => /* @__PURE__ */ new Date()), options)
|
|
176
179
|
} : {},
|
|
177
|
-
...options.softdelete ? { deletedAt:
|
|
180
|
+
...options.softdelete ? { deletedAt: managedField(z.date().nullable().default(null), options) } : {}
|
|
178
181
|
};
|
|
179
182
|
return new Schema({
|
|
180
183
|
...this.definition.shape,
|
|
181
184
|
...managedShape
|
|
182
185
|
}, this.relationMap, this.scopeMap, options, this.indexDefinitions);
|
|
183
186
|
}
|
|
184
|
-
managedField(field, options) {
|
|
185
|
-
return options.hideManaged ? withHidden(field).hidden() : field;
|
|
186
|
-
}
|
|
187
187
|
/** Declare MongoDB indexes for explicit synchronization with the database. */
|
|
188
188
|
indexes(definitions) {
|
|
189
189
|
if (definitions.some(({ fields }) => Object.keys(fields).length === 0)) throw new Error("Index definitions must include at least one field");
|
|
@@ -274,40 +274,8 @@ var EstimatedCountError = class extends InvalidQueryError {
|
|
|
274
274
|
}
|
|
275
275
|
};
|
|
276
276
|
//#endregion
|
|
277
|
-
//#region src/query/
|
|
278
|
-
/**
|
|
279
|
-
var ModelCursor = class {
|
|
280
|
-
open;
|
|
281
|
-
pageSize;
|
|
282
|
-
transform;
|
|
283
|
-
next = null;
|
|
284
|
-
constructor(open, pageSize, transform) {
|
|
285
|
-
this.open = open;
|
|
286
|
-
this.pageSize = pageSize;
|
|
287
|
-
this.transform = transform;
|
|
288
|
-
}
|
|
289
|
-
async *[Symbol.asyncIterator]() {
|
|
290
|
-
const cursor = this.open();
|
|
291
|
-
try {
|
|
292
|
-
const documents = [];
|
|
293
|
-
for (let index = 0; index < this.pageSize && await cursor.hasNext(); index += 1) documents.push(await cursor.next());
|
|
294
|
-
const transformed = this.transform ? await this.transform(documents) : documents;
|
|
295
|
-
for (const document of transformed) yield document;
|
|
296
|
-
const last = transformed.at(-1);
|
|
297
|
-
this.next = await cursor.hasNext() && last ? last._id : null;
|
|
298
|
-
} finally {
|
|
299
|
-
await cursor.close();
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
//#endregion
|
|
304
|
-
//#region src/model/soft-delete.ts
|
|
305
|
-
const applySoftDeleteFilter = (filter, mode) => {
|
|
306
|
-
if (mode === "all") return filter;
|
|
307
|
-
return { $and: [mode === "deleted" ? { deletedAt: { $ne: null } } : { deletedAt: null }, filter] };
|
|
308
|
-
};
|
|
309
|
-
//#endregion
|
|
310
|
-
//#region src/query/runtime.ts
|
|
277
|
+
//#region src/query/projection/runtime.ts
|
|
278
|
+
/** Remove duplicate and ancestor-overlapping MongoDB projection paths. */
|
|
311
279
|
const normalizeProjectionFields = (fields) => {
|
|
312
280
|
const unique = new Set(fields);
|
|
313
281
|
return [...unique].filter((field) => {
|
|
@@ -319,6 +287,7 @@ const normalizeProjectionFields = (fields) => {
|
|
|
319
287
|
return true;
|
|
320
288
|
});
|
|
321
289
|
};
|
|
290
|
+
/** Build the MongoDB projection from schema visibility and query selections. */
|
|
322
291
|
const projectionFor = (fields, hiddenFields, selectedFields, shownFields) => {
|
|
323
292
|
const effectiveSelectedFields = selectedFields?.length ? selectedFields : void 0;
|
|
324
293
|
if (!effectiveSelectedFields && hiddenFields.length === 0 && shownFields.length === 0) return;
|
|
@@ -326,25 +295,9 @@ const projectionFor = (fields, hiddenFields, selectedFields, shownFields) => {
|
|
|
326
295
|
const visibleFields = effectiveSelectedFields ?? fields.filter((field) => !hidden.has(field));
|
|
327
296
|
return Object.fromEntries(normalizeProjectionFields([...visibleFields, ...shownFields]).map((field) => [field, 1]));
|
|
328
297
|
};
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
constructor(enabled) {
|
|
333
|
-
this.enabled = enabled;
|
|
334
|
-
}
|
|
335
|
-
includeDeleted() {
|
|
336
|
-
this.mode = "all";
|
|
337
|
-
}
|
|
338
|
-
deleted(mode) {
|
|
339
|
-
this.mode = mode === "include" ? "all" : "deleted";
|
|
340
|
-
}
|
|
341
|
-
isFiltered() {
|
|
342
|
-
return this.enabled && this.mode !== "all";
|
|
343
|
-
}
|
|
344
|
-
effectiveFilter(filter) {
|
|
345
|
-
return applySoftDeleteFilter(filter, this.mode);
|
|
346
|
-
}
|
|
347
|
-
};
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/query/population/executor.ts
|
|
300
|
+
/** Executes already-validated population instructions against related collections. */
|
|
348
301
|
var PopulationExecutor = class {
|
|
349
302
|
db;
|
|
350
303
|
relations;
|
|
@@ -375,9 +328,111 @@ var PopulationExecutor = class {
|
|
|
375
328
|
document[spec.ref] = related;
|
|
376
329
|
}
|
|
377
330
|
};
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/model/soft-delete.ts
|
|
333
|
+
const applySoftDeleteFilter = (filter, mode) => {
|
|
334
|
+
if (mode === "all") return filter;
|
|
335
|
+
return { $and: [mode === "deleted" ? { deletedAt: { $ne: null } } : { deletedAt: null }, filter] };
|
|
336
|
+
};
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/query/soft-delete/state.ts
|
|
339
|
+
/** Query-local state for composing a schema's default soft-delete filter. */
|
|
340
|
+
var SoftDeleteState = class {
|
|
341
|
+
enabled;
|
|
342
|
+
mode = "active";
|
|
343
|
+
constructor(enabled) {
|
|
344
|
+
this.enabled = enabled;
|
|
345
|
+
}
|
|
346
|
+
includeDeleted() {
|
|
347
|
+
this.mode = "all";
|
|
348
|
+
}
|
|
349
|
+
deleted(mode) {
|
|
350
|
+
this.mode = mode === "include" ? "all" : "deleted";
|
|
351
|
+
}
|
|
352
|
+
isFiltered() {
|
|
353
|
+
return this.enabled && this.mode !== "all";
|
|
354
|
+
}
|
|
355
|
+
effectiveFilter(filter) {
|
|
356
|
+
return applySoftDeleteFilter(filter, this.mode);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region src/query/cursor/cursor.ts
|
|
361
|
+
/** A lazy async iterable for one cursor-pagination page. */
|
|
362
|
+
var ModelCursor = class {
|
|
363
|
+
open;
|
|
364
|
+
pageSize;
|
|
365
|
+
transform;
|
|
366
|
+
next = null;
|
|
367
|
+
constructor(open, pageSize, transform) {
|
|
368
|
+
this.open = open;
|
|
369
|
+
this.pageSize = pageSize;
|
|
370
|
+
this.transform = transform;
|
|
371
|
+
}
|
|
372
|
+
async *[Symbol.asyncIterator]() {
|
|
373
|
+
const cursor = this.open();
|
|
374
|
+
try {
|
|
375
|
+
const documents = [];
|
|
376
|
+
for (let index = 0; index < this.pageSize && await cursor.hasNext(); index += 1) documents.push(await cursor.next());
|
|
377
|
+
const transformed = this.transform ? await this.transform(documents) : documents;
|
|
378
|
+
for (const document of transformed) yield document;
|
|
379
|
+
const last = transformed.at(-1);
|
|
380
|
+
this.next = await cursor.hasNext() && last ? last._id : null;
|
|
381
|
+
} finally {
|
|
382
|
+
await cursor.close();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/query/cursor/filter.ts
|
|
388
|
+
/** Add the `_id` continuation boundary used by cursor pagination. */
|
|
378
389
|
const createCursorFilter = (filter, after) => after ? { $and: [filter, { _id: { $gt: after } }] } : filter;
|
|
379
390
|
//#endregion
|
|
380
|
-
//#region src/query/
|
|
391
|
+
//#region src/query/builder/executor.ts
|
|
392
|
+
/** Execute a list query or MongoDB's estimated collection count. */
|
|
393
|
+
const countQuery = async (context, estimate) => {
|
|
394
|
+
if (estimate) {
|
|
395
|
+
if (Object.keys(context.filter).length > 0 || context.softDelete.isFiltered()) throw new EstimatedCountError();
|
|
396
|
+
return context.collection.estimatedDocumentCount();
|
|
397
|
+
}
|
|
398
|
+
return context.collection.countDocuments(context.effectiveFilter);
|
|
399
|
+
};
|
|
400
|
+
/** Open an `_id`-ordered, bounded cursor page. */
|
|
401
|
+
const createCursorPage = (context, after) => {
|
|
402
|
+
if (context.limitCount === void 0 || context.limitCount === 0) throw new CursorQueryError("Cursor queries require a positive limit");
|
|
403
|
+
if (context.skipCount !== void 0) throw new CursorQueryError("Cursor queries do not support skip");
|
|
404
|
+
if (context.sortSpec) {
|
|
405
|
+
if (Object.keys(context.sortSpec).length !== 1 || context.sortSpec._id !== "asc") throw new CursorQueryError("Cursor queries require the default _id ascending sort");
|
|
406
|
+
}
|
|
407
|
+
const filter = createCursorFilter(context.effectiveFilter, after);
|
|
408
|
+
return new ModelCursor(() => {
|
|
409
|
+
let cursor = context.collection.find(filter).sort({ _id: 1 }).limit(context.limitCount + 1);
|
|
410
|
+
const projection = projectionFor(context.fields, context.hiddenFields, context.selectedFields, context.shownFields);
|
|
411
|
+
if (projection) cursor = cursor.project(projection);
|
|
412
|
+
return cursor;
|
|
413
|
+
}, context.limitCount, (documents) => context.population.apply(documents, context.populateSpecs));
|
|
414
|
+
};
|
|
415
|
+
/** Execute a list query and populate its results. */
|
|
416
|
+
const executeQuery = async (context) => {
|
|
417
|
+
const documents = await createFindCursor(context).toArray();
|
|
418
|
+
return context.population.apply(documents, context.populateSpecs);
|
|
419
|
+
};
|
|
420
|
+
/** Execute a query for its first matching document. */
|
|
421
|
+
const executeFirst = async (context) => {
|
|
422
|
+
const documents = await createFindCursor(context).limit(1).toArray();
|
|
423
|
+
return (await context.population.apply(documents, context.populateSpecs))[0] ?? null;
|
|
424
|
+
};
|
|
425
|
+
const createFindCursor = (context) => {
|
|
426
|
+
let cursor = context.collection.find(context.effectiveFilter);
|
|
427
|
+
if (context.sortSpec) cursor = cursor.sort(context.sortSpec);
|
|
428
|
+
if (context.skipCount !== void 0) cursor = cursor.skip(context.skipCount);
|
|
429
|
+
if (context.limitCount !== void 0) cursor = cursor.limit(context.limitCount);
|
|
430
|
+
const projection = projectionFor(context.fields, context.hiddenFields, context.selectedFields, context.shownFields);
|
|
431
|
+
if (projection) cursor = cursor.project(projection);
|
|
432
|
+
return cursor;
|
|
433
|
+
};
|
|
434
|
+
//#endregion
|
|
435
|
+
//#region src/query/builder/find-query.ts
|
|
381
436
|
/** A typed, awaitable MongoDB find query. */
|
|
382
437
|
var ModelQuery = class {
|
|
383
438
|
collection;
|
|
@@ -423,6 +478,34 @@ var ModelQuery = class {
|
|
|
423
478
|
effectiveFilter() {
|
|
424
479
|
return this.softDelete.effectiveFilter(this.filterSpec);
|
|
425
480
|
}
|
|
481
|
+
executionContext() {
|
|
482
|
+
const context = {
|
|
483
|
+
collection: this.collection,
|
|
484
|
+
filter: this.filterSpec,
|
|
485
|
+
effectiveFilter: this.effectiveFilter(),
|
|
486
|
+
fields: this.fields,
|
|
487
|
+
hiddenFields: this.hiddenFields,
|
|
488
|
+
selectedFields: this.selectedFields,
|
|
489
|
+
shownFields: this.shownFields,
|
|
490
|
+
sortSpec: this.sortSpec,
|
|
491
|
+
skipCount: this.skipCount,
|
|
492
|
+
limitCount: this.limitCount,
|
|
493
|
+
softDelete: this.softDelete,
|
|
494
|
+
population: this.population,
|
|
495
|
+
populateSpecs: this.populateSpecs
|
|
496
|
+
};
|
|
497
|
+
Object.defineProperties(context, {
|
|
498
|
+
effectiveFilter: { get: () => this.effectiveFilter() },
|
|
499
|
+
selectedFields: { get: () => this.selectedFields },
|
|
500
|
+
shownFields: { get: () => this.shownFields },
|
|
501
|
+
sortSpec: { get: () => this.sortSpec },
|
|
502
|
+
skipCount: { get: () => this.skipCount },
|
|
503
|
+
limitCount: { get: () => this.limitCount },
|
|
504
|
+
softDelete: { get: () => this.softDelete },
|
|
505
|
+
populateSpecs: { get: () => this.populateSpecs }
|
|
506
|
+
});
|
|
507
|
+
return context;
|
|
508
|
+
}
|
|
426
509
|
/** Sort results by one or more schema fields. */
|
|
427
510
|
sort(spec) {
|
|
428
511
|
this.sortSpec = spec;
|
|
@@ -465,43 +548,18 @@ var ModelQuery = class {
|
|
|
465
548
|
return this;
|
|
466
549
|
}
|
|
467
550
|
/** Count matching documents, optionally using MongoDB's collection estimate. */
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
if (Object.keys(this.filterSpec).length > 0 || this.softDelete.isFiltered()) throw new EstimatedCountError();
|
|
471
|
-
return this.collection.estimatedDocumentCount();
|
|
472
|
-
}
|
|
473
|
-
return this.collection.countDocuments(this.effectiveFilter());
|
|
551
|
+
count(estimate = false) {
|
|
552
|
+
return countQuery(this.executionContext(), estimate);
|
|
474
553
|
}
|
|
475
|
-
/** Return one `_id`-ordered page and
|
|
554
|
+
/** Return one bounded `_id`-ordered page and its continuation cursor. */
|
|
476
555
|
createCursor(after) {
|
|
477
|
-
|
|
478
|
-
if (this.skipCount !== void 0) throw new CursorQueryError("Cursor queries do not support skip");
|
|
479
|
-
if (this.sortSpec) {
|
|
480
|
-
if (Object.keys(this.sortSpec).length !== 1 || this.sortSpec._id !== "asc") throw new CursorQueryError("Cursor queries require the default _id ascending sort");
|
|
481
|
-
}
|
|
482
|
-
const filter = createCursorFilter(this.effectiveFilter(), after);
|
|
483
|
-
return new ModelCursor(() => {
|
|
484
|
-
let cursor = this.collection.find(filter).sort({ _id: 1 }).limit(this.limitCount + 1);
|
|
485
|
-
const projection = projectionFor(this.fields, this.hiddenFields, this.selectedFields, this.shownFields);
|
|
486
|
-
if (projection) cursor = cursor.project(projection);
|
|
487
|
-
return cursor;
|
|
488
|
-
}, this.limitCount, (documents) => this.population.apply(documents, this.populateSpecs));
|
|
556
|
+
return createCursorPage(this.executionContext(), after);
|
|
489
557
|
}
|
|
490
558
|
execute() {
|
|
491
|
-
return
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
return (await this.population.apply(documents, this.populateSpecs))[0] ?? null;
|
|
496
|
-
}
|
|
497
|
-
createQueryCursor() {
|
|
498
|
-
let cursor = this.collection.find(this.effectiveFilter());
|
|
499
|
-
if (this.sortSpec) cursor = cursor.sort(this.sortSpec);
|
|
500
|
-
if (this.skipCount !== void 0) cursor = cursor.skip(this.skipCount);
|
|
501
|
-
if (this.limitCount !== void 0) cursor = cursor.limit(this.limitCount);
|
|
502
|
-
const projection = projectionFor(this.fields, this.hiddenFields, this.selectedFields, this.shownFields);
|
|
503
|
-
if (projection) cursor = cursor.project(projection);
|
|
504
|
-
return cursor;
|
|
559
|
+
return executeQuery(this.executionContext());
|
|
560
|
+
}
|
|
561
|
+
first() {
|
|
562
|
+
return executeFirst(this.executionContext());
|
|
505
563
|
}
|
|
506
564
|
then(onfulfilled, onrejected) {
|
|
507
565
|
return this.execute().then(onfulfilled, onrejected);
|
|
@@ -629,7 +687,7 @@ var Db = class {
|
|
|
629
687
|
constructor(options) {
|
|
630
688
|
this.options = options;
|
|
631
689
|
this.client = new MongoClient(options.uri, options.clientOptions);
|
|
632
|
-
for (const [name, schema] of Object.entries(options.
|
|
690
|
+
for (const [name, schema] of Object.entries(options.schemas ?? {})) {
|
|
633
691
|
this.registerSchema(schema, name);
|
|
634
692
|
let model;
|
|
635
693
|
Object.defineProperty(this, name, {
|