mongoose-killer 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ben Thayer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # Viper
2
+
3
+ In a fight of Mongoose vs. Viper, only 1 in 10 Vipers come out on top. This is the 10th Viper.
4
+
5
+ Viper is a drop-in Mongoose replacement built for speed.
6
+
7
+ Under the hood, it uses the native MongoDB driver without any fluff for optimal performance while maintaining the same convenient API as Mongoose.
8
+
9
+ ## Quickstart
10
+
11
+ ```bash
12
+ npm install mongoose-killer mongodb
13
+ ```
14
+ ```ts
15
+ import { createGetModel } from "mongoose-killer";
16
+ import { MongoClient } from "mongodb";
17
+
18
+ const client = new MongoClient(process.env.MONGO_URI!);
19
+ await client.connect();
20
+ const db = client.db("app");
21
+
22
+ const getModel = createGetModel({
23
+ db,
24
+ models: {
25
+ User: "users",
26
+ Post: "posts",
27
+ },
28
+ populates: {
29
+ Post: {
30
+ authorID: { collection: "users" },
31
+ },
32
+ },
33
+ });
34
+
35
+ const User = getModel("User");
36
+ const Post = getModel("Post");
37
+
38
+ await User.create({ name: "Alice" });
39
+
40
+ const posts = await Post.find({ published: true })
41
+ .populate("authorID")
42
+ .sort({ createdAt: -1 })
43
+ .limit(10)
44
+ .lean();
45
+ ```
46
+
47
+ ## What it is
48
+
49
+ - **A `Model` API, not an ODM.** No schemas, no validation, no hooks,
50
+ no virtuals. The native MongoDB driver does the work; Viper just
51
+ exposes it through the chainable, thenable, `.populate()`-aware
52
+ interface mongoose users already think in.
53
+ - **Drop-in equivalence is enforced.** The test suite runs every test
54
+ against real Mongoose *and* Viper, asserting identical results. If a
55
+ call shape isn't covered by `tests/`, assume it isn't supported.
56
+ - **TypeScript-first.** Source is TS, types ship with the package, ESM
57
+ and CJS both work.
58
+
59
+ ## What it isn't
60
+
61
+ - A general-purpose ODM. The supported surface is whatever the test
62
+ suite covers — see "What's supported" below.
63
+ - A schema layer. Documents are plain objects in and out. If you want
64
+ validation, do it before you call Viper.
65
+
66
+ ## Installing
67
+
68
+ ```bash
69
+ npm install mongoose-killer mongodb
70
+ # or
71
+ yarn add mongoose-killer mongodb
72
+ ```
73
+
74
+ `mongodb` is a peer dependency — Viper doesn't bundle a driver, so the
75
+ client and Viper agree on driver version.
76
+
77
+ ## What's supported
78
+
79
+ - **Standard calls**: `find`, `findOne`, `findById`, `findOneAndUpdate`,
80
+ `findByIdAndUpdate`, `findOneAndDelete`, `findByIdAndDelete`,
81
+ `updateOne`, `updateMany`, `deleteOne`, `deleteMany`, `create`,
82
+ `insertMany`, `bulkWrite`, `aggregate`, `countDocuments`, `distinct`,
83
+ `exists`.
84
+ - **Populate**: string path, object spec with `select`/`match`, nested,
85
+ array-of-refs (`tags._id`), array of specs, static
86
+ `Model.populate(docs, spec)`. Always `$in`-batched, never N+1.
87
+ - **Method chaining**: `lean`, `sort`, `limit`, `skip`, `select`, `hint`,
88
+ `comment`, `session`, `read`, `populate`, `where`, `or`, `and`,
89
+ `nor`, `option`.
90
+ - **Sessions & Transactions**: `{ session }` threads through every op for transactions.
91
+ - **Thenable API**: `await`, `.then`, `.catch`, `.finally`, `.exec()`.
92
+
93
+ ## What's *not* supported
94
+
95
+ - Schemas, virtuals, hooks (`pre`/`post`), getters, setters, casting,
96
+ validation, defaults.
97
+ - `new Model(doc)` / `doc.save()`. Use `Model.create(doc)`.
98
+ - Mongoose-specific connection/event API. You own the `MongoClient`
99
+ and pass us a `Db`.
100
+ - Mongoose's `Types` namespace — import `ObjectId` / `Decimal128`
101
+ straight from `bson`.
102
+
103
+ ## Compatibility Features
104
+
105
+ ### ObjectId Auto-casting
106
+
107
+ Like Mongoose, Viper automatically casts 24-char hex strings to
108
+ `ObjectId` in filter positions. **On by default** for drop-in parity.
109
+
110
+ Since Viper doesn't know your schemas, this cast is applied to *every*
111
+ 24-hex string in a filter, not just the ones that should be IDs. If
112
+ you have a field that stores 24-character hex strings that aren't IDs,
113
+ this behavior will be incorrect for your use case.
114
+
115
+ You can easily opt out of this behavior by changing the global default
116
+ or overriding per query.
117
+
118
+
119
+ ```ts
120
+ // Default — on
121
+ const getModel = createGetModel({ db, models });
122
+ const getModel = createGetModel({ db, models, autoCastIds: true }); // Same but explicit
123
+
124
+ // Opt out globally
125
+ const getModel = createGetModel({ db, models, autoCastIds: false });
126
+
127
+ // Override per query
128
+ await User.findOne({ _id: hexId }).castIds(); // force on
129
+ await User.findOne({ slug: hexLookingSlug }).skipCastIds(); // force off
130
+ ```
131
+
132
+ If both `.castIds()` and `.skipCastIds()` get called on the same query
133
+ (probably a bug), Viper throws by default. You can change that via
134
+ `castIdsConflictPolicy: 'firstWins' | 'lastWins' | 'defaultWins'`.
135
+
136
+ ### `.id` field
137
+
138
+ Like Mongoose, Viper exposes `.id` on non-lean documents as the
139
+ stringified `_id`.
140
+
141
+ ```ts
142
+ const post = await Post.findOne({ slug: "hello-world" });
143
+ // post._id is an ObjectId, post.id is its string form
144
+
145
+ console.log(post._id); // ObjectId("507f1f77bcf86cd799439011")
146
+ console.log(post.id); // "507f1f77bcf86cd799439011"
147
+ ```
148
+
149
+ If a document has no `_id`, `.id` returns `null` (matching Mongoose).
150
+ The `.id` field will not be detected by `JSON.stringify`, `Object.keys`
151
+ and other similar functions, so you won't end up with an id field when
152
+ storing in Redis, for example.
153
+
154
+ `.lean()` returns plain driver objects with no `.id` virtual, also
155
+ matching Mongoose.
156
+
157
+ ## TypeScript and ESM
158
+
159
+ Both TypeScript and ESM import syntax are supported by default.
160
+
161
+
162
+ ## Running the tests
163
+
164
+ Requires a local MongoDB at `mongodb://localhost:27017`
165
+
166
+ With docker-compose like this:
167
+ ```docker-compose
168
+ services:
169
+ mongo:
170
+ image: mongo:7
171
+ ports:
172
+ - "27017:27017"
173
+ ulimits:
174
+ nofile:
175
+ soft: 64000
176
+ hard: 64000
177
+ ```
178
+
179
+ With docker like this:
180
+ ```bash
181
+ docker run --ulimit nofile=64000:64000 -p 27017:27017 mongo:7
182
+ ```
183
+
184
+ If using replica set mode, you will need to include directConnection=true
185
+
186
+ Setting ulimits is required because the internal socket limit default is low and running the tests create a lot of sockets.
187
+
188
+
189
+ ```bash
190
+ yarn test # default — runs against Viper
191
+ yarn test:real # against real mongoose
192
+ yarn test:both # back-to-back; the drop-in proof
193
+ yarn typecheck # tsc --noEmit
194
+ ```
195
+
196
+ ## License
197
+
198
+ MIT.
@@ -0,0 +1,11 @@
1
+ export declare class CastIdsConflictError extends Error {
2
+ readonly name = "CastIdsConflictError";
3
+ readonly castIdsCallCount: number;
4
+ readonly skipCastIdsCallCount: number;
5
+ constructor(opts: {
6
+ castIdsCallCount: number;
7
+ skipCastIdsCallCount: number;
8
+ });
9
+ static is(err: unknown): err is CastIdsConflictError;
10
+ }
11
+ //# sourceMappingURL=CastIdsConflictError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CastIdsConflictError.d.ts","sourceRoot":"","sources":["../../src/CastIdsConflictError.ts"],"names":[],"mappings":"AASA,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,0BAA0B;IACvC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;gBAE1B,IAAI,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,oBAAoB,EAAE,MAAM,CAAA;KAAE;IAS5E,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,oBAAoB;CAMrD"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ // Thrown at query exec time when a single Query has had both
3
+ // `.castIds()` and `.skipCastIds()` called on it AND the configured
4
+ // conflict policy is "throw".
5
+ //
6
+ // Exported as a named class so callers can do `instanceof` in error
7
+ // handlers. The static `is(err)` helper is here for callers that
8
+ // receive errors across module-boundaries (different bundles can
9
+ // produce instanceof-incompatible classes with the same name).
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.CastIdsConflictError = void 0;
12
+ class CastIdsConflictError extends Error {
13
+ name = "CastIdsConflictError";
14
+ castIdsCallCount;
15
+ skipCastIdsCallCount;
16
+ constructor(opts) {
17
+ super(`Conflicting calls on the same Query: ${opts.castIdsCallCount}× .castIds() and ${opts.skipCastIdsCallCount}× .skipCastIds(). ` +
18
+ `The configured castIdsConflictPolicy is "throw" — pass createGetModel({ castIdsConflictPolicy: "lastWins" | "firstWins" | "defaultWins" }) to resolve conflicts automatically, or remove one of the calls.`);
19
+ this.castIdsCallCount = opts.castIdsCallCount;
20
+ this.skipCastIdsCallCount = opts.skipCastIdsCallCount;
21
+ }
22
+ static is(err) {
23
+ return (err instanceof Error &&
24
+ err.name === "CastIdsConflictError");
25
+ }
26
+ }
27
+ exports.CastIdsConflictError = CastIdsConflictError;
@@ -0,0 +1,51 @@
1
+ import type { Collection, Db } from "mongodb";
2
+ import { Query } from "./Query.js";
3
+ import type { CastIdsConflictPolicy } from "./types.js";
4
+ type ModelInit = {
5
+ name: string;
6
+ collection: Collection;
7
+ db: Db;
8
+ populates: Record<string, {
9
+ collection: string;
10
+ }>;
11
+ collectionToModelName: Map<string, string>;
12
+ getModelByName: (name: string) => any;
13
+ autoCastIds: boolean;
14
+ castIdsConflictPolicy: CastIdsConflictPolicy;
15
+ };
16
+ export declare class Model {
17
+ name: string;
18
+ collection: Collection;
19
+ populates: Record<string, {
20
+ collection: string;
21
+ }>;
22
+ collectionToModelName: Map<string, string>;
23
+ getModelByName: (name: string) => any;
24
+ autoCastIds: boolean;
25
+ castIdsConflictPolicy: CastIdsConflictPolicy;
26
+ db: {
27
+ db: Db;
28
+ };
29
+ constructor(init: ModelInit);
30
+ find(filter?: any, projection?: any, options?: any): Query;
31
+ findOne(filter?: any, projection?: any, options?: any): Query;
32
+ findById(id: any, projection?: any, options?: any): Query;
33
+ findOneAndUpdate(filter: any, update: any, options?: any): Query;
34
+ findByIdAndUpdate(id: any, update: any, options?: any): Query;
35
+ findOneAndDelete(filter: any, options?: any): Query;
36
+ findByIdAndDelete(id: any, options?: any): Query;
37
+ updateOne(filter: any, update: any, options?: any): Query;
38
+ updateMany(filter: any, update: any, options?: any): Query;
39
+ deleteOne(filter: any, options?: any): Query;
40
+ deleteMany(filter: any, options?: any): Query;
41
+ countDocuments(filter?: any, options?: any): Query;
42
+ distinct(field: string, filter?: any, options?: any): Query;
43
+ exists(filter: any, options?: any): Query;
44
+ aggregate(pipeline?: any[], options?: any): Query;
45
+ create(input: any, options?: any): Promise<any>;
46
+ insertMany(docs: any[], options?: any): Promise<any[]>;
47
+ bulkWrite(ops: any[], options?: any): Promise<any>;
48
+ populate(docs: any, arg: any): Promise<any>;
49
+ }
50
+ export {};
51
+ //# sourceMappingURL=Model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Model.d.ts","sourceRoot":"","sources":["../../src/Model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Model = void 0;
4
+ const bson_1 = require("bson");
5
+ const Query_js_1 = require("./Query.js");
6
+ const populate_js_1 = require("./populate.js");
7
+ // Mongoose `Model` is half class, half magic. We mimic the surface the
8
+ // codebase actually uses (per inventory): the static entry verbs.
9
+ // Instances are not a thing — call sites never `new Model()`.
10
+ class Model {
11
+ name;
12
+ collection;
13
+ populates;
14
+ collectionToModelName;
15
+ getModelByName;
16
+ autoCastIds;
17
+ castIdsConflictPolicy;
18
+ // Mongoose exposes Model.db as a Connection-like object, and
19
+ // Connection.db is the native Db handle. Some libraries reach
20
+ // through `Model.db.db` to grab the native Db and issue a raw
21
+ // `db.collection(name).findOne({})` — bypassing the ODM (often
22
+ // to avoid lossy type handling like mongoose's historical
23
+ // Decimal128 quirks).
24
+ //
25
+ // We mimic that two-hop shape: `model.db` is a wrapper object whose
26
+ // `.db` is the native Db. Anything else hanging off Connection is
27
+ // not currently used by any call site, so we leave it unset.
28
+ db;
29
+ constructor(init) {
30
+ this.name = init.name;
31
+ this.collection = init.collection;
32
+ this.populates = init.populates;
33
+ this.collectionToModelName = init.collectionToModelName;
34
+ this.getModelByName = init.getModelByName;
35
+ this.autoCastIds = init.autoCastIds;
36
+ this.castIdsConflictPolicy = init.castIdsConflictPolicy;
37
+ this.db = { db: init.db };
38
+ }
39
+ // ---- query entry verbs --------------------------------------------
40
+ find(filter = {}, projection, options) {
41
+ const q = new Query_js_1.Query({ model: this, op: "find", filter, options });
42
+ if (projection)
43
+ q.select(projection);
44
+ if (options?.sort)
45
+ q.sort(options.sort);
46
+ if (options?.limit !== undefined)
47
+ q.limit(options.limit);
48
+ if (options?.skip !== undefined)
49
+ q.skip(options.skip);
50
+ return q;
51
+ }
52
+ findOne(filter = {}, projection, options) {
53
+ const q = new Query_js_1.Query({ model: this, op: "findOne", filter, options });
54
+ if (projection)
55
+ q.select(projection);
56
+ return q;
57
+ }
58
+ findById(id, projection, options) {
59
+ return this.findOne({ _id: coerceId(id) }, projection, options);
60
+ }
61
+ findOneAndUpdate(filter, update, options) {
62
+ return new Query_js_1.Query({
63
+ model: this,
64
+ op: "findOneAndUpdate",
65
+ filter,
66
+ update,
67
+ options,
68
+ });
69
+ }
70
+ findByIdAndUpdate(id, update, options) {
71
+ return this.findOneAndUpdate({ _id: coerceId(id) }, update, options);
72
+ }
73
+ findOneAndDelete(filter, options) {
74
+ return new Query_js_1.Query({
75
+ model: this,
76
+ op: "findOneAndDelete",
77
+ filter,
78
+ options,
79
+ });
80
+ }
81
+ findByIdAndDelete(id, options) {
82
+ return this.findOneAndDelete({ _id: coerceId(id) }, options);
83
+ }
84
+ updateOne(filter, update, options) {
85
+ return new Query_js_1.Query({
86
+ model: this,
87
+ op: "updateOne",
88
+ filter,
89
+ update,
90
+ options,
91
+ });
92
+ }
93
+ updateMany(filter, update, options) {
94
+ return new Query_js_1.Query({
95
+ model: this,
96
+ op: "updateMany",
97
+ filter,
98
+ update,
99
+ options,
100
+ });
101
+ }
102
+ deleteOne(filter, options) {
103
+ return new Query_js_1.Query({
104
+ model: this,
105
+ op: "deleteOne",
106
+ filter,
107
+ options,
108
+ });
109
+ }
110
+ deleteMany(filter, options) {
111
+ return new Query_js_1.Query({
112
+ model: this,
113
+ op: "deleteMany",
114
+ filter,
115
+ options,
116
+ });
117
+ }
118
+ countDocuments(filter = {}, options) {
119
+ return new Query_js_1.Query({
120
+ model: this,
121
+ op: "countDocuments",
122
+ filter,
123
+ options,
124
+ });
125
+ }
126
+ distinct(field, filter = {}, options) {
127
+ return new Query_js_1.Query({
128
+ model: this,
129
+ op: "distinct",
130
+ filter,
131
+ distinctField: field,
132
+ options,
133
+ });
134
+ }
135
+ exists(filter, options) {
136
+ return new Query_js_1.Query({
137
+ model: this,
138
+ op: "exists",
139
+ filter,
140
+ options,
141
+ });
142
+ }
143
+ aggregate(pipeline = [], options) {
144
+ return new Query_js_1.Query({
145
+ model: this,
146
+ op: "aggregate",
147
+ pipeline,
148
+ options,
149
+ });
150
+ }
151
+ // ---- writes that aren't chains ------------------------------------
152
+ // Mongoose's create returns the inserted doc(s) with _id set.
153
+ // Inventory uses both forms: create(doc) and create([docs], options).
154
+ async create(input, options) {
155
+ if (Array.isArray(input)) {
156
+ const docs = input.map(prepInsert);
157
+ if (docs.length === 0)
158
+ return [];
159
+ await this.collection.insertMany(docs, options ?? {});
160
+ return docs;
161
+ }
162
+ const doc = prepInsert(input);
163
+ await this.collection.insertOne(doc, options ?? {});
164
+ return doc;
165
+ }
166
+ async insertMany(docs, options) {
167
+ const prepared = docs.map(prepInsert);
168
+ if (prepared.length === 0)
169
+ return [];
170
+ await this.collection.insertMany(prepared, options ?? {});
171
+ return prepared;
172
+ }
173
+ async bulkWrite(ops, options) {
174
+ return this.collection.bulkWrite(ops, options ?? {});
175
+ }
176
+ // Static populate: takes pre-fetched doc(s) and decorates them.
177
+ // Used in a handful of inventory sites for ad-hoc populate after
178
+ // a custom aggregation.
179
+ async populate(docs, arg) {
180
+ const isArr = Array.isArray(docs);
181
+ const arr = isArr ? docs : [docs];
182
+ if (arr.length === 0)
183
+ return docs;
184
+ const specs = (0, populate_js_1.normalizePopulateArg)(arg);
185
+ const populated = await (0, populate_js_1.runPopulate)(arr, specs, {
186
+ ownerModel: this,
187
+ session: undefined,
188
+ });
189
+ return isArr ? populated : populated[0];
190
+ }
191
+ }
192
+ exports.Model = Model;
193
+ // Mongoose auto-assigns _id on insert; the driver does the same, but
194
+ // only if _id is missing. We let the driver handle it — no work here.
195
+ // Kept as a hook in case we ever need to deep-clone or strip Mongoose-
196
+ // only fields.
197
+ const prepInsert = (doc) => doc;
198
+ const coerceId = (id) => {
199
+ if (id instanceof bson_1.ObjectId)
200
+ return id;
201
+ if (typeof id === "string" && /^[0-9a-fA-F]{24}$/.test(id)) {
202
+ return new bson_1.ObjectId(id);
203
+ }
204
+ return id;
205
+ };
@@ -0,0 +1,85 @@
1
+ import type { Collection, ClientSession } from "mongodb";
2
+ import type { CastIdsConflictPolicy } from "./types.js";
3
+ type Op = "find" | "findOne" | "findOneAndUpdate" | "findOneAndDelete" | "updateOne" | "updateMany" | "deleteOne" | "deleteMany" | "countDocuments" | "distinct" | "exists" | "aggregate";
4
+ type ModelLike = {
5
+ name: string;
6
+ collection: Collection;
7
+ populates: Record<string, {
8
+ collection: string;
9
+ }>;
10
+ collectionToModelName: Map<string, string>;
11
+ getModelByName: (name: string) => any;
12
+ autoCastIds: boolean;
13
+ castIdsConflictPolicy: CastIdsConflictPolicy;
14
+ };
15
+ type QueryInit = {
16
+ model: ModelLike;
17
+ op: Op;
18
+ filter?: any;
19
+ update?: any;
20
+ pipeline?: any[];
21
+ distinctField?: string;
22
+ options?: any;
23
+ };
24
+ export declare class Query implements PromiseLike<any> {
25
+ private model;
26
+ private op;
27
+ private filter;
28
+ private update;
29
+ private pipeline;
30
+ private distinctField;
31
+ private _sort;
32
+ private _limit;
33
+ private _skip;
34
+ private _select;
35
+ private _hint;
36
+ private _comment;
37
+ private _session;
38
+ private _readPref;
39
+ private _populates;
40
+ private _lean;
41
+ private _castIdsOps;
42
+ private opOptions;
43
+ private _executed;
44
+ private _execPromise;
45
+ constructor(init: QueryInit);
46
+ lean(on?: boolean): this;
47
+ sort(spec: any): this;
48
+ limit(n: number): this;
49
+ skip(n: number): this;
50
+ select(spec: any): this;
51
+ hint(h: any): this;
52
+ comment(c: string): this;
53
+ session(s: ClientSession | null | undefined): this;
54
+ read(pref: any): this;
55
+ castIds(): this;
56
+ skipCastIds(): this;
57
+ populate(arg?: any): this;
58
+ where(field: string, value?: any): this;
59
+ or(conditions: any[]): this;
60
+ and(conditions: any[]): this;
61
+ nor(conditions: any[]): this;
62
+ option(o: any): this;
63
+ exec(): Promise<any>;
64
+ then<TResolve = any, TReject = never>(onResolve?: ((v: any) => TResolve | PromiseLike<TResolve>) | null, onReject?: ((e: any) => TReject | PromiseLike<TReject>) | null): Promise<TResolve | TReject>;
65
+ catch<T = never>(onReject?: ((e: any) => T | PromiseLike<T>) | null): Promise<any | T>;
66
+ finally(onFinally?: (() => void) | null): Promise<any>;
67
+ private run;
68
+ private castedFilter;
69
+ private resolveCastIds;
70
+ private findOptions;
71
+ private writeOptions;
72
+ private runFind;
73
+ private runFindOne;
74
+ private runFindOneAndUpdate;
75
+ private runFindOneAndDelete;
76
+ private runUpdate;
77
+ private runDelete;
78
+ private runCount;
79
+ private runDistinct;
80
+ private runExists;
81
+ private runAggregate;
82
+ }
83
+ export declare const addIdVirtual: (doc: any) => void;
84
+ export {};
85
+ //# sourceMappingURL=Query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../../src/Query.ts"],"names":[],"mappings":"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"}