@mongorm/orm 0.1.1-beta.3 → 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 -185
- package/dist/index.mjs +166 -105
- 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,72 +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<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 ModelFilter<Shape extends SchemaShape> = ModelFilterForDocument<StoredDocument<Shape>, Infer<Schema<Shape>>>;
|
|
136
|
-
type SortDirection = 'asc' | 'desc';
|
|
137
|
-
type ModelSort<Shape extends SchemaShape> = Partial<Record<Extract<keyof Infer<Schema<Shape>>, string>, SortDirection>>;
|
|
138
|
-
type ModelDocument<Shape extends SchemaShape> = Infer<Schema<Shape>>;
|
|
139
|
-
type HiddenKey<Shape extends SchemaShape> = { [Key in keyof Shape]: Shape[Key] extends {
|
|
140
|
-
readonly __hidden: true;
|
|
141
|
-
} ? Key : never; }[keyof Shape];
|
|
142
|
-
type HiddenDocumentKey<Shape extends SchemaShape> = Extract<HiddenKey<Shape>, keyof ModelDocument<Shape>> & string;
|
|
143
|
-
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;
|
|
144
|
-
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>];
|
|
145
|
-
type CursorMethod<Shape extends SchemaShape, Result extends object, Ready extends boolean> = Ready extends true ? (after?: ObjectId$1) => ModelCursor<Shape, Result> : undefined;
|
|
146
|
-
type SelectableKey<Shape extends SchemaShape> = Exclude<Extract<keyof ModelDocument<Shape>, string>, '_id' | HiddenDocumentKey<Shape>> | NestedSelectableKey<Shape>;
|
|
147
|
-
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;
|
|
148
|
-
type UnionToIntersection<Value> = (Value extends unknown ? (input: Value) => void : never) extends ((input: infer Intersection) => void) ? Intersection : never;
|
|
149
|
-
type Simplify<Value> = { [Key in keyof Value]: Value[Key]; };
|
|
150
|
-
type VisibleDocument<Shape extends SchemaShape> = Omit<ModelDocument<Shape>, Extract<HiddenKey<Shape>, keyof ModelDocument<Shape>>>;
|
|
151
|
-
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>>>>;
|
|
152
|
-
type RelationTarget<Relation> = Relation extends {
|
|
153
|
-
resolve: () => infer Target;
|
|
154
|
-
} ? Target : never;
|
|
155
|
-
type RelationDocument<Relation> = RelationTarget<Relation> extends Schema<infer TargetShape, any> ? Infer<Schema<TargetShape>> : never;
|
|
156
|
-
type RelationMapOf$1<Relation> = RelationTarget<Relation> extends {
|
|
157
|
-
readonly relationMap: infer TargetRelations;
|
|
158
|
-
} ? TargetRelations extends SchemaRelationMap ? TargetRelations : {} : {};
|
|
159
|
-
type ScopeName<Scopes> = Extract<keyof Scopes, string>;
|
|
160
|
-
type PopulationMode = 'none' | 'populate' | 'scope';
|
|
161
|
-
type RelationSelect<Relation> = RelationTarget<Relation> extends Schema<infer TargetShape, any> ? Exclude<SelectableKey<TargetShape>, '_id'> : never;
|
|
162
|
-
type PopulateSpec<Relations extends SchemaRelationMap> = { [Name in Extract<keyof Relations, string>]: {
|
|
163
|
-
ref: Name;
|
|
164
|
-
select?: readonly RelationSelect<Relations[Name]>[];
|
|
165
|
-
populate?: PopulateSpecs<RelationMapOf$1<Relations[Name]>>;
|
|
166
|
-
}; }[Extract<keyof Relations, string>];
|
|
167
|
-
type PopulateSpecs<Relations extends SchemaRelationMap> = readonly PopulateSpec<Relations>[];
|
|
168
|
-
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; };
|
|
169
|
-
type PopulatedRelation<Relation, Spec> = Spec extends {
|
|
170
|
-
populate: infer Nested extends PopulateSpecs<RelationMapOf$1<Relation>>;
|
|
171
|
-
} ? RelationDocument<Relation> extends (infer PopulatedDocument extends object) ? PopulatedResult<PopulatedDocument, RelationMapOf$1<Relation>, Nested> : never : RelationDocument<Relation>;
|
|
172
|
-
//#endregion
|
|
173
|
-
//#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>>>;
|
|
174
308
|
/** A typed, awaitable MongoDB find query. */
|
|
175
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[]> {
|
|
176
310
|
private readonly collection;
|
|
@@ -196,6 +330,7 @@ export declare class ModelQuery<Shape extends SchemaShape, Result extends object
|
|
|
196
330
|
/** Include both active and soft-deleted documents in this query. */
|
|
197
331
|
private deletedMode;
|
|
198
332
|
private effectiveFilter;
|
|
333
|
+
private executionContext;
|
|
199
334
|
/** Sort results by one or more schema fields. */
|
|
200
335
|
sort(spec: ModelSort<Shape>): ModelQuery<Shape, Result, false, Relations, Scopes, Mode, SoftDelete>;
|
|
201
336
|
/** Skip a non-negative number of matching documents. */
|
|
@@ -209,65 +344,18 @@ export declare class ModelQuery<Shape extends SchemaShape, Result extends object
|
|
|
209
344
|
/** Populate declared one-way relations, including nested relation arrays. */
|
|
210
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>;
|
|
211
346
|
/** Apply a named population scope. */
|
|
212
|
-
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>;
|
|
213
348
|
/** Count matching documents, optionally using MongoDB's collection estimate. */
|
|
214
349
|
count(estimate?: boolean): Promise<number>;
|
|
215
|
-
/** Return one `_id`-ordered page and
|
|
350
|
+
/** Return one bounded `_id`-ordered page and its continuation cursor. */
|
|
216
351
|
private createCursor;
|
|
217
352
|
private execute;
|
|
218
353
|
first(): Promise<Result | null>;
|
|
219
|
-
private createQueryCursor;
|
|
220
354
|
then<TResult1 = Result[], TResult2 = never>(onfulfilled?: ((value: Result[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
221
355
|
}
|
|
222
356
|
//#endregion
|
|
223
357
|
//#region src/schema/schema.d.ts
|
|
224
|
-
/** Built-in persistence behavior applied by a schema. */
|
|
225
|
-
interface SchemaOptions {
|
|
226
|
-
readonly timestamps?: boolean;
|
|
227
|
-
readonly softdelete?: boolean;
|
|
228
|
-
}
|
|
229
|
-
type IndexFieldMap<Shape extends SchemaShape> = Record<Extract<keyof Shape, string>, IndexDirection>;
|
|
230
|
-
/** A non-empty, autocomplete-friendly MongoDB index key definition. */
|
|
231
|
-
type SchemaIndexFields<Shape extends SchemaShape> = { [Key in keyof IndexFieldMap<Shape>]: Pick<IndexFieldMap<Shape>, Key> & Partial<Omit<IndexFieldMap<Shape>, Key>>; }[keyof IndexFieldMap<Shape>];
|
|
232
|
-
/** A schema-aware MongoDB partial-index filter. */
|
|
233
|
-
type SchemaPartialFilter<Shape extends SchemaShape> = ModelFilter<Shape>;
|
|
234
|
-
type SchemaIndexOptions<Shape extends SchemaShape> = Omit<IndexDescription, 'key' | 'partialFilterExpression'> & {
|
|
235
|
-
/** Restrict indexed documents using schema-aware MongoDB filter operators. */
|
|
236
|
-
readonly partialFilterExpression?: SchemaPartialFilter<Shape>;
|
|
237
|
-
};
|
|
238
|
-
type SchemaIndex<Shape extends SchemaShape> = {
|
|
239
|
-
readonly fields: SchemaIndexFields<Shape>;
|
|
240
|
-
readonly options?: SchemaIndexOptions<Shape>;
|
|
241
|
-
};
|
|
242
|
-
type ExactPartialFilter<Shape extends SchemaShape, Filter> = Filter & Record<Exclude<keyof Filter, keyof SchemaPartialFilter<Shape>>, never>;
|
|
243
|
-
type ValidateIndexDefinition<Shape extends SchemaShape, Definition> = Definition extends {
|
|
244
|
-
readonly options?: infer Options;
|
|
245
|
-
} ? Definition & {
|
|
246
|
-
readonly options?: Options extends {
|
|
247
|
-
readonly partialFilterExpression?: infer Filter;
|
|
248
|
-
} ? Options & {
|
|
249
|
-
readonly partialFilterExpression?: ExactPartialFilter<Shape, Filter>;
|
|
250
|
-
} : Options;
|
|
251
|
-
} : Definition;
|
|
252
|
-
type ValidateIndexDefinitions<Shape extends SchemaShape, Definitions extends readonly unknown[]> = { [Key in keyof Definitions]: ValidateIndexDefinition<Shape, Definitions[Key]>; };
|
|
253
|
-
type SchemaIndexNames<Indexes extends readonly SchemaIndex<any>[]> = Extract<Indexes[number] extends (infer Index) ? Index extends {
|
|
254
|
-
readonly options?: {
|
|
255
|
-
readonly name?: infer Name;
|
|
256
|
-
};
|
|
257
|
-
} ? Name : never : never, string>;
|
|
258
|
-
type TimestampShape = {
|
|
259
|
-
createdAt: z.ZodDefault<z.ZodDate>;
|
|
260
|
-
updatedAt: z.ZodDefault<z.ZodDate>;
|
|
261
|
-
};
|
|
262
|
-
type SoftDeleteShape = {
|
|
263
|
-
deletedAt: z.ZodDefault<z.ZodNullable<z.ZodDate>>;
|
|
264
|
-
};
|
|
265
|
-
type ManagedShape<Options extends SchemaOptions> = (Options['timestamps'] extends true ? TimestampShape : {}) & (Options['softdelete'] extends true ? SoftDeleteShape : {});
|
|
266
|
-
type ManagedField<Options extends SchemaOptions> = (Options['timestamps'] extends true ? 'createdAt' | 'updatedAt' : never) | (Options['softdelete'] extends true ? 'deletedAt' : never);
|
|
267
|
-
type SoftDeleteEnabled<Options extends SchemaOptions> = Options['softdelete'] extends true ? true : false;
|
|
268
|
-
export declare const hasSoftDelete: (options: SchemaOptions) => boolean;
|
|
269
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>>];
|
|
270
|
-
type ScopeDefinitions = Record<string, readonly object[]>;
|
|
271
359
|
/** A typed, runtime-validated schema definition. */
|
|
272
360
|
export declare class Schema<Shape extends SchemaShape, Relations extends SchemaRelationMap = {}, Scopes extends ScopeDefinitions = {}, Options extends SchemaOptions = {}, Indexes extends readonly SchemaIndex<any>[] = []> {
|
|
273
361
|
/** The underlying Zod object for advanced validation use cases. */
|
|
@@ -309,93 +397,52 @@ export declare class Schema<Shape extends SchemaShape, Relations extends SchemaR
|
|
|
309
397
|
}
|
|
310
398
|
//#endregion
|
|
311
399
|
//#region src/relations/definitions.d.ts
|
|
312
|
-
/**
|
|
313
|
-
type SchemaLike = Schema<
|
|
314
|
-
/** 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. */
|
|
315
403
|
interface RefDefinition<Target extends SchemaLike = SchemaLike> {
|
|
316
|
-
/** Resolve the related schema after module initialization completes. */
|
|
317
404
|
resolve: () => Target;
|
|
318
405
|
}
|
|
319
|
-
/** Metadata for a one-way relation between two
|
|
320
|
-
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 : {}> {
|
|
321
410
|
readonly resolve: () => Target;
|
|
322
411
|
readonly localField: LocalField;
|
|
323
412
|
readonly foreignField: ForeignField;
|
|
413
|
+
/** Target relations are carried separately to avoid recursively wrapping its schema type. */
|
|
414
|
+
readonly __targetRelations?: TargetRelations;
|
|
324
415
|
}
|
|
325
|
-
/** Relation metadata attached to a schema. */
|
|
326
416
|
type SchemaRelationMap = Record<string, SchemaRelation>;
|
|
327
|
-
/**
|
|
417
|
+
/** Relation target declaration accepted by `Schema.relations()`. */
|
|
328
418
|
type RelationInput<Target extends SchemaLike = SchemaLike> = (() => Target) | {
|
|
329
419
|
target: () => Target;
|
|
330
420
|
foreignField?: string;
|
|
331
421
|
};
|
|
332
|
-
/** Extract a relation target from a relation declaration. */
|
|
333
422
|
type RelationInputTarget<Input> = Input extends (() => infer Target) ? Target : Input extends {
|
|
334
423
|
target: () => infer Target;
|
|
335
424
|
} ? Target : never;
|
|
336
|
-
/** A string identifier field carrying a typed relation target. */
|
|
337
|
-
type RefField<Target extends SchemaLike = SchemaLike> = z.ZodType<ObjectId$1> & {
|
|
338
|
-
readonly __ref?: RefDefinition<Target>;
|
|
339
|
-
optional(): OptionalRefField<Target>;
|
|
340
|
-
nullable(): NullableRefField<Target>;
|
|
341
|
-
nullish(): NullishRefField<Target>;
|
|
342
|
-
};
|
|
343
|
-
/** An optional relation field that retains its target metadata. */
|
|
344
|
-
type OptionalRefField<Target extends SchemaLike = SchemaLike> = z.ZodOptional<RefField<Target>> & {
|
|
345
|
-
readonly __ref?: RefDefinition<Target>;
|
|
346
|
-
};
|
|
347
|
-
/** A nullable relation field that retains its target metadata. */
|
|
348
|
-
type NullableRefField<Target extends SchemaLike = SchemaLike> = z.ZodNullable<RefField<Target>> & {
|
|
349
|
-
readonly __ref?: RefDefinition<Target>;
|
|
350
|
-
};
|
|
351
|
-
/** An optional and nullable relation field that retains its target metadata. */
|
|
352
|
-
type NullishRefField<Target extends SchemaLike = SchemaLike> = z.ZodOptional<NullableRefField<Target>> & {
|
|
353
|
-
readonly __ref?: RefDefinition<Target>;
|
|
354
|
-
};
|
|
355
|
-
type RelationDefinition<Field> = Field extends {
|
|
356
|
-
readonly __ref?: infer Definition;
|
|
357
|
-
} ? NonNullable<Definition> : Field extends z.ZodOptional<infer Inner> ? RelationDefinition<Inner> : Field extends z.ZodNullable<infer Inner> ? RelationDefinition<Inner> : never;
|
|
358
|
-
/** The relation metadata inferred from a schema shape. */
|
|
359
|
-
type RelationMap<Shape extends SchemaShape> = { [Key in keyof Shape as RelationDefinition<Shape[Key]> extends never ? never : Key]: RelationDefinition<Shape[Key]>; };
|
|
360
425
|
//#endregion
|
|
361
|
-
//#region src/relations/registry.d.ts
|
|
426
|
+
//#region src/relations/registry-types.d.ts
|
|
362
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`. */
|
|
363
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; };
|
|
364
|
-
type
|
|
365
|
-
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; };
|
|
366
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]; };
|
|
367
|
-
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. */
|
|
368
434
|
type ScopeDefinitionsBySchema<Registry extends Record<string, SchemaLike>> = { [Name in keyof Registry]?: Record<string, PopulateSpecs<RelationMapOf<Registry[Name]>>>; };
|
|
369
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. */
|
|
370
437
|
type SchemaRegistryBuilder<Registry extends Record<string, SchemaLike>> = Registry & {
|
|
371
438
|
readonly __registry?: Registry;
|
|
372
439
|
defineRelations<const Definitions extends RelationDefinitions<Registry>>(definitions: Definitions): SchemaRegistryBuilder<RegistryWithRelations<Registry, Definitions>>;
|
|
373
440
|
defineScopes<const Definitions extends ScopeDefinitionsBySchema<Registry>>(definitions: Definitions): SchemaRegistryBuilder<RegistryWithScopes<Registry, Definitions>>;
|
|
374
441
|
};
|
|
375
|
-
declare const createSchemaRegistry: <const Registry extends Record<string, SchemaLike>>(registry: Registry) => SchemaRegistryBuilder<Registry>;
|
|
376
442
|
//#endregion
|
|
377
|
-
//#region src/
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
hidden(): this & {
|
|
381
|
-
readonly __hidden: true;
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
/** A schema field that can be marked as hidden from default query results. */
|
|
386
|
-
type HiddenCapable<T extends z.ZodType> = Omit<T, 'optional' | 'nullable' | 'nullish'> & HiddenMethods<T>;
|
|
387
|
-
/** A schema field marked as hidden from default query results. */
|
|
388
|
-
type HiddenSchema<T extends z.ZodType> = Omit<T, 'optional' | 'nullable' | 'nullish'> & HiddenMethods<T> & {
|
|
389
|
-
readonly __hidden: true;
|
|
390
|
-
};
|
|
391
|
-
type HiddenMethods<T extends z.ZodType> = {
|
|
392
|
-
hidden(): HiddenSchema<T>;
|
|
393
|
-
optional(): HiddenCapable<z.ZodOptional<T>>;
|
|
394
|
-
nullable(): HiddenCapable<z.ZodNullable<T>>;
|
|
395
|
-
nullish(): HiddenCapable<z.ZodOptional<z.ZodNullable<T>>>;
|
|
396
|
-
};
|
|
397
|
-
/** Create a MongoDB ObjectId schema. */
|
|
398
|
-
declare const objectId: () => HiddenCapable<z.ZodInstanceOf<ObjectId$1>>;
|
|
443
|
+
//#region src/relations/registry.d.ts
|
|
444
|
+
/** Create a registry of named schemas with typed relation and scope builders. */
|
|
445
|
+
declare const createSchemaRegistry: <const Registry extends Record<string, SchemaLike>>(registry: Registry) => SchemaRegistryBuilder<Registry>;
|
|
399
446
|
//#endregion
|
|
400
447
|
//#region src/api.d.ts
|
|
401
448
|
type ZodConstructorKey = { [Key in keyof typeof z]: Key extends string ? Key extends Lowercase<Key> ? (typeof z)[Key] extends ((...args: any[]) => any) ? Key : never : never : never; }[keyof typeof z];
|
|
@@ -444,4 +491,4 @@ export declare class EstimatedCountError extends InvalidQueryError {
|
|
|
444
491
|
constructor();
|
|
445
492
|
}
|
|
446
493
|
//#endregion
|
|
447
|
-
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,10 +174,10 @@ 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: z.date().default(() => /* @__PURE__ */ new Date()),
|
|
175
|
-
updatedAt: z.date().default(() => /* @__PURE__ */ new Date())
|
|
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: z.date().nullable().default(null) } : {}
|
|
180
|
+
...options.softdelete ? { deletedAt: managedField(z.date().nullable().default(null), options) } : {}
|
|
178
181
|
};
|
|
179
182
|
return new Schema({
|
|
180
183
|
...this.definition.shape,
|
|
@@ -271,40 +274,8 @@ var EstimatedCountError = class extends InvalidQueryError {
|
|
|
271
274
|
}
|
|
272
275
|
};
|
|
273
276
|
//#endregion
|
|
274
|
-
//#region src/query/
|
|
275
|
-
/**
|
|
276
|
-
var ModelCursor = class {
|
|
277
|
-
open;
|
|
278
|
-
pageSize;
|
|
279
|
-
transform;
|
|
280
|
-
next = null;
|
|
281
|
-
constructor(open, pageSize, transform) {
|
|
282
|
-
this.open = open;
|
|
283
|
-
this.pageSize = pageSize;
|
|
284
|
-
this.transform = transform;
|
|
285
|
-
}
|
|
286
|
-
async *[Symbol.asyncIterator]() {
|
|
287
|
-
const cursor = this.open();
|
|
288
|
-
try {
|
|
289
|
-
const documents = [];
|
|
290
|
-
for (let index = 0; index < this.pageSize && await cursor.hasNext(); index += 1) documents.push(await cursor.next());
|
|
291
|
-
const transformed = this.transform ? await this.transform(documents) : documents;
|
|
292
|
-
for (const document of transformed) yield document;
|
|
293
|
-
const last = transformed.at(-1);
|
|
294
|
-
this.next = await cursor.hasNext() && last ? last._id : null;
|
|
295
|
-
} finally {
|
|
296
|
-
await cursor.close();
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
//#endregion
|
|
301
|
-
//#region src/model/soft-delete.ts
|
|
302
|
-
const applySoftDeleteFilter = (filter, mode) => {
|
|
303
|
-
if (mode === "all") return filter;
|
|
304
|
-
return { $and: [mode === "deleted" ? { deletedAt: { $ne: null } } : { deletedAt: null }, filter] };
|
|
305
|
-
};
|
|
306
|
-
//#endregion
|
|
307
|
-
//#region src/query/runtime.ts
|
|
277
|
+
//#region src/query/projection/runtime.ts
|
|
278
|
+
/** Remove duplicate and ancestor-overlapping MongoDB projection paths. */
|
|
308
279
|
const normalizeProjectionFields = (fields) => {
|
|
309
280
|
const unique = new Set(fields);
|
|
310
281
|
return [...unique].filter((field) => {
|
|
@@ -316,6 +287,7 @@ const normalizeProjectionFields = (fields) => {
|
|
|
316
287
|
return true;
|
|
317
288
|
});
|
|
318
289
|
};
|
|
290
|
+
/** Build the MongoDB projection from schema visibility and query selections. */
|
|
319
291
|
const projectionFor = (fields, hiddenFields, selectedFields, shownFields) => {
|
|
320
292
|
const effectiveSelectedFields = selectedFields?.length ? selectedFields : void 0;
|
|
321
293
|
if (!effectiveSelectedFields && hiddenFields.length === 0 && shownFields.length === 0) return;
|
|
@@ -323,25 +295,9 @@ const projectionFor = (fields, hiddenFields, selectedFields, shownFields) => {
|
|
|
323
295
|
const visibleFields = effectiveSelectedFields ?? fields.filter((field) => !hidden.has(field));
|
|
324
296
|
return Object.fromEntries(normalizeProjectionFields([...visibleFields, ...shownFields]).map((field) => [field, 1]));
|
|
325
297
|
};
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
constructor(enabled) {
|
|
330
|
-
this.enabled = enabled;
|
|
331
|
-
}
|
|
332
|
-
includeDeleted() {
|
|
333
|
-
this.mode = "all";
|
|
334
|
-
}
|
|
335
|
-
deleted(mode) {
|
|
336
|
-
this.mode = mode === "include" ? "all" : "deleted";
|
|
337
|
-
}
|
|
338
|
-
isFiltered() {
|
|
339
|
-
return this.enabled && this.mode !== "all";
|
|
340
|
-
}
|
|
341
|
-
effectiveFilter(filter) {
|
|
342
|
-
return applySoftDeleteFilter(filter, this.mode);
|
|
343
|
-
}
|
|
344
|
-
};
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/query/population/executor.ts
|
|
300
|
+
/** Executes already-validated population instructions against related collections. */
|
|
345
301
|
var PopulationExecutor = class {
|
|
346
302
|
db;
|
|
347
303
|
relations;
|
|
@@ -372,9 +328,111 @@ var PopulationExecutor = class {
|
|
|
372
328
|
document[spec.ref] = related;
|
|
373
329
|
}
|
|
374
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. */
|
|
375
389
|
const createCursorFilter = (filter, after) => after ? { $and: [filter, { _id: { $gt: after } }] } : filter;
|
|
376
390
|
//#endregion
|
|
377
|
-
//#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
|
|
378
436
|
/** A typed, awaitable MongoDB find query. */
|
|
379
437
|
var ModelQuery = class {
|
|
380
438
|
collection;
|
|
@@ -420,6 +478,34 @@ var ModelQuery = class {
|
|
|
420
478
|
effectiveFilter() {
|
|
421
479
|
return this.softDelete.effectiveFilter(this.filterSpec);
|
|
422
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
|
+
}
|
|
423
509
|
/** Sort results by one or more schema fields. */
|
|
424
510
|
sort(spec) {
|
|
425
511
|
this.sortSpec = spec;
|
|
@@ -462,43 +548,18 @@ var ModelQuery = class {
|
|
|
462
548
|
return this;
|
|
463
549
|
}
|
|
464
550
|
/** Count matching documents, optionally using MongoDB's collection estimate. */
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if (Object.keys(this.filterSpec).length > 0 || this.softDelete.isFiltered()) throw new EstimatedCountError();
|
|
468
|
-
return this.collection.estimatedDocumentCount();
|
|
469
|
-
}
|
|
470
|
-
return this.collection.countDocuments(this.effectiveFilter());
|
|
551
|
+
count(estimate = false) {
|
|
552
|
+
return countQuery(this.executionContext(), estimate);
|
|
471
553
|
}
|
|
472
|
-
/** Return one `_id`-ordered page and
|
|
554
|
+
/** Return one bounded `_id`-ordered page and its continuation cursor. */
|
|
473
555
|
createCursor(after) {
|
|
474
|
-
|
|
475
|
-
if (this.skipCount !== void 0) throw new CursorQueryError("Cursor queries do not support skip");
|
|
476
|
-
if (this.sortSpec) {
|
|
477
|
-
if (Object.keys(this.sortSpec).length !== 1 || this.sortSpec._id !== "asc") throw new CursorQueryError("Cursor queries require the default _id ascending sort");
|
|
478
|
-
}
|
|
479
|
-
const filter = createCursorFilter(this.effectiveFilter(), after);
|
|
480
|
-
return new ModelCursor(() => {
|
|
481
|
-
let cursor = this.collection.find(filter).sort({ _id: 1 }).limit(this.limitCount + 1);
|
|
482
|
-
const projection = projectionFor(this.fields, this.hiddenFields, this.selectedFields, this.shownFields);
|
|
483
|
-
if (projection) cursor = cursor.project(projection);
|
|
484
|
-
return cursor;
|
|
485
|
-
}, this.limitCount, (documents) => this.population.apply(documents, this.populateSpecs));
|
|
556
|
+
return createCursorPage(this.executionContext(), after);
|
|
486
557
|
}
|
|
487
558
|
execute() {
|
|
488
|
-
return
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
return (await this.population.apply(documents, this.populateSpecs))[0] ?? null;
|
|
493
|
-
}
|
|
494
|
-
createQueryCursor() {
|
|
495
|
-
let cursor = this.collection.find(this.effectiveFilter());
|
|
496
|
-
if (this.sortSpec) cursor = cursor.sort(this.sortSpec);
|
|
497
|
-
if (this.skipCount !== void 0) cursor = cursor.skip(this.skipCount);
|
|
498
|
-
if (this.limitCount !== void 0) cursor = cursor.limit(this.limitCount);
|
|
499
|
-
const projection = projectionFor(this.fields, this.hiddenFields, this.selectedFields, this.shownFields);
|
|
500
|
-
if (projection) cursor = cursor.project(projection);
|
|
501
|
-
return cursor;
|
|
559
|
+
return executeQuery(this.executionContext());
|
|
560
|
+
}
|
|
561
|
+
first() {
|
|
562
|
+
return executeFirst(this.executionContext());
|
|
502
563
|
}
|
|
503
564
|
then(onfulfilled, onrejected) {
|
|
504
565
|
return this.execute().then(onfulfilled, onrejected);
|
|
@@ -626,7 +687,7 @@ var Db = class {
|
|
|
626
687
|
constructor(options) {
|
|
627
688
|
this.options = options;
|
|
628
689
|
this.client = new MongoClient(options.uri, options.clientOptions);
|
|
629
|
-
for (const [name, schema] of Object.entries(options.
|
|
690
|
+
for (const [name, schema] of Object.entries(options.schemas ?? {})) {
|
|
630
691
|
this.registerSchema(schema, name);
|
|
631
692
|
let model;
|
|
632
693
|
Object.defineProperty(this, name, {
|