@nxgt/mongo 0.1.0 → 0.3.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/README.md +149 -29
- package/dist/collection/get-collection.d.ts +26 -0
- package/dist/collection/get-collection.d.ts.map +1 -0
- package/dist/collection/types.d.ts +270 -0
- package/dist/collection/types.d.ts.map +1 -0
- package/dist/definition/define-collection.d.ts +37 -5
- package/dist/definition/define-collection.d.ts.map +1 -1
- package/dist/definition/fields.d.ts +5 -5
- package/dist/definition/fields.d.ts.map +1 -1
- package/dist/definition/object-id.d.ts +51 -0
- package/dist/definition/object-id.d.ts.map +1 -0
- package/dist/errors/data-error.d.ts +13 -1
- package/dist/errors/data-error.d.ts.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +292 -188
- package/dist/index.js.map +10 -9
- package/dist/transaction/with-transaction.d.ts +4 -4
- package/package.json +1 -1
- package/dist/repository/create-repository.d.ts +0 -21
- package/dist/repository/create-repository.d.ts.map +0 -1
- package/dist/repository/types.d.ts +0 -145
- package/dist/repository/types.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
A typed MongoDB collection, from one Zod schema: the schema types every read
|
|
4
4
|
and write, and the same schema becomes the collection's `$jsonSchema`
|
|
5
|
-
validator, applied idempotently. On top of it, a
|
|
5
|
+
validator, applied idempotently. On top of it, a collection with pagination,
|
|
6
6
|
transactions, optimistic locking, soft delete, audit stamps, and MongoDB's
|
|
7
7
|
errors turned into ones you can catch.
|
|
8
8
|
|
|
9
|
-
It wraps the official `mongodb` driver, which stays a peer dependency
|
|
10
|
-
driver's
|
|
9
|
+
It wraps the official `mongodb` driver, which stays a peer dependency — and it
|
|
10
|
+
does not hide it: the driver's own methods are on the very same object, so
|
|
11
|
+
`aggregate`, `watch` and `bulkWrite` are always at hand.
|
|
11
12
|
|
|
12
13
|
## Install
|
|
13
14
|
|
|
@@ -31,6 +32,7 @@ export const users = defineCollection({
|
|
|
31
32
|
_id: id(),
|
|
32
33
|
email: z.email(),
|
|
33
34
|
name: z.string().nullable().default(null),
|
|
35
|
+
loginCount: z.int().default(0),
|
|
34
36
|
...timestamps(),
|
|
35
37
|
...softDelete(),
|
|
36
38
|
...optimisticLock(),
|
|
@@ -46,8 +48,23 @@ const db = client.db('app');
|
|
|
46
48
|
## Definition
|
|
47
49
|
|
|
48
50
|
`defineCollection` takes the collection's name, the Zod schema of its
|
|
49
|
-
documents, its indexes
|
|
50
|
-
|
|
51
|
+
documents, its indexes, and how its validator is applied.
|
|
52
|
+
|
|
53
|
+
An index is keyed on the schema's own fields, so an editor completes them and a
|
|
54
|
+
typo does not compile. A path into a field is allowed too, since that is how
|
|
55
|
+
MongoDB indexes a nested key. Everything else is the driver's own
|
|
56
|
+
`IndexDescription` — `unique`, `name`, `collation`, `expireAfterSeconds`,
|
|
57
|
+
`partialFilterExpression`:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
indexes: [
|
|
61
|
+
{ key: { email: 1 }, unique: true, name: 'users_email_unique' },
|
|
62
|
+
{ key: { createdAt: -1 } },
|
|
63
|
+
{ key: { 'address.city': 1 } },
|
|
64
|
+
// @ts-expect-error there is no such field
|
|
65
|
+
{ key: { emial: 1 } },
|
|
66
|
+
]
|
|
67
|
+
```
|
|
51
68
|
|
|
52
69
|
The schema is the one source of truth. `z.output` is what a read gives back,
|
|
53
70
|
`z.input` what a write takes: a field with a default — `_id`, `createdAt`,
|
|
@@ -94,34 +111,87 @@ already full.
|
|
|
94
111
|
## Documents
|
|
95
112
|
|
|
96
113
|
```ts
|
|
97
|
-
import {
|
|
114
|
+
import { getCollection } from '@nxgt/mongo';
|
|
98
115
|
|
|
99
|
-
const
|
|
116
|
+
const collection = getCollection(db, users); // a Db, or a MongoClient
|
|
100
117
|
|
|
101
|
-
const ada = await
|
|
102
|
-
// → { _id: ObjectId, email, name: null, createdAt: Date, version: 0, … }
|
|
118
|
+
const ada = await collection.create({ email: 'ada@example.com' });
|
|
119
|
+
// → { _id: ObjectId, id: '507f…', email, name: null, createdAt: Date, version: 0, … }
|
|
103
120
|
|
|
104
|
-
await
|
|
105
|
-
await
|
|
106
|
-
await
|
|
107
|
-
await
|
|
108
|
-
await
|
|
109
|
-
await
|
|
121
|
+
await collection.findById(ada._id); // the document, or undefined
|
|
122
|
+
await collection.getById(ada._id); // or NotFoundError
|
|
123
|
+
await collection.findFirst({ email: 'ada@example.com' });
|
|
124
|
+
await collection.findMany({ filter: { name: null }, sort: { createdAt: -1 }, limit: 10 });
|
|
125
|
+
await collection.count({ name: null });
|
|
126
|
+
await collection.exists({ email: 'ada@example.com' });
|
|
110
127
|
|
|
111
|
-
await
|
|
112
|
-
await
|
|
113
|
-
await
|
|
128
|
+
await collection.update(ada._id, { name: 'Ada' }); // checked field by field
|
|
129
|
+
await collection.update(ada._id, { $inc: { loginCount: 1 } }); // MongoDB's operators too
|
|
130
|
+
await collection.updateMany({ name: null }, { name: 'unknown' });
|
|
114
131
|
|
|
115
|
-
await
|
|
116
|
-
await
|
|
117
|
-
await
|
|
132
|
+
await collection.delete(ada._id); // soft, on a schema with deletedAt
|
|
133
|
+
await collection.restore(ada._id);
|
|
134
|
+
await collection.hardDelete(ada._id); // really gone
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**The driver's collection is the same object.** Everything this package does
|
|
138
|
+
not wrap is on it directly — no `.collection` to go through:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
await collection.aggregate([{ $group: { _id: '$teamId', n: { $sum: 1 } } }]).toArray();
|
|
142
|
+
collection.watch();
|
|
143
|
+
await collection.distinct('email');
|
|
144
|
+
await collection.bulkWrite([…]);
|
|
145
|
+
collection.collectionName; // 'users'
|
|
118
146
|
```
|
|
119
147
|
|
|
148
|
+
Three names are defined by both, and this package's win, because a filter that
|
|
149
|
+
came out empty must not rewrite a collection: `count`, `updateMany` and
|
|
150
|
+
`deleteMany` return a number and require a filter. The driver's own are on
|
|
151
|
+
`raw`, which is its `Collection`, untouched:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
await collection.updateMany({ name: null }, { name: 'x' }); // → number
|
|
155
|
+
await collection.raw.updateMany({}, { $set: { name: 'x' } }); // → UpdateResult
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`raw` is also the way out for an update operator this package does not name.
|
|
159
|
+
|
|
120
160
|
`create` checks the document against the schema before sending it, which is
|
|
121
161
|
also what fills its defaults. `update` checks each field of a patch — the
|
|
122
162
|
driver's own `UpdateFilter` is intersected with `Document` and accepts any key
|
|
123
163
|
whatsoever, including a typo.
|
|
124
164
|
|
|
165
|
+
## Ids
|
|
166
|
+
|
|
167
|
+
Every document a repository gives back carries `id`: its `_id` as a string. It
|
|
168
|
+
is computed, never stored — the collection holds `_id` alone — and it is an
|
|
169
|
+
ordinary enumerable property, so `JSON.stringify` and a spread carry it and a
|
|
170
|
+
handler can return the document as it is.
|
|
171
|
+
|
|
172
|
+
Because it is not a stored field, nothing can be filtered or patched on it: the
|
|
173
|
+
server would match nothing, and TypeScript refuses it. To go the other way,
|
|
174
|
+
from a string that arrived over HTTP:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { toObjectId, tryObjectId, isValidObjectId, objectIdParam } from '@nxgt/mongo';
|
|
178
|
+
|
|
179
|
+
await repo.getById(toObjectId(params.id)); // an ObjectId, or InvalidIdError
|
|
180
|
+
tryObjectId(params.id); // an ObjectId, or undefined
|
|
181
|
+
isValidObjectId(params.id); // a boolean
|
|
182
|
+
toObjectIds(query.ids); // for a `$in` filter
|
|
183
|
+
|
|
184
|
+
// Or as part of a schema, where the parameters are parsed:
|
|
185
|
+
const route = z.object({ id: objectIdParam() });
|
|
186
|
+
const { id } = route.parse(params); // ObjectId
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Do not call `new ObjectId(value)` on input you did not produce.** Given
|
|
190
|
+
`null` or `undefined` the driver does not throw: it invents a fresh id, so a
|
|
191
|
+
parameter that never arrived becomes a perfectly valid id that matches nothing.
|
|
192
|
+
`toObjectId` throws `InvalidIdError`, which a handler can turn into a 400 or a
|
|
193
|
+
404.
|
|
194
|
+
|
|
125
195
|
## Pagination
|
|
126
196
|
|
|
127
197
|
```ts
|
|
@@ -147,15 +217,20 @@ document is repeated or skipped while the collection is written to, where
|
|
|
147
217
|
import { withTransaction } from '@nxgt/mongo';
|
|
148
218
|
|
|
149
219
|
await withTransaction(client, async (session) => {
|
|
150
|
-
const team = await teams.
|
|
151
|
-
await users.
|
|
220
|
+
const team = await teams.withSession(session).create({ name: 'Core' });
|
|
221
|
+
await users.withSession(session).update(userId, { teamId: team._id });
|
|
152
222
|
});
|
|
153
223
|
```
|
|
154
224
|
|
|
155
225
|
**Every operation has to be given the session.** MongoDB has no ambient
|
|
156
226
|
session: a write that was not given one runs outside the transaction and is not
|
|
157
|
-
rolled back with it. `
|
|
158
|
-
and it returns a new
|
|
227
|
+
rolled back with it. `collection.withSession(session)` is how a collection
|
|
228
|
+
takes it, and it returns a new collection rather than changing the one you
|
|
229
|
+
have.
|
|
230
|
+
|
|
231
|
+
`withSession` binds **this package's** methods. A driver method on the same
|
|
232
|
+
object — `aggregate`, `bulkWrite`, `countDocuments` — takes its session the
|
|
233
|
+
driver's way, in its options: `collection.aggregate(pipeline, { session })`.
|
|
159
234
|
|
|
160
235
|
Given a session that is already in a transaction, `withTransaction` joins it.
|
|
161
236
|
MongoDB has no savepoints, so an inner failure takes the whole transaction
|
|
@@ -190,6 +265,7 @@ application never reads a numeric code:
|
|
|
190
265
|
| `ValidationError` | `VALIDATION` | the collection's validator refused it (121) |
|
|
191
266
|
| `OptimisticLockError` | `OPTIMISTIC_LOCK` | `expectedVersion` no longer matches |
|
|
192
267
|
| `InvalidCursorError` | `INVALID_CURSOR` | a cursor this package did not write |
|
|
268
|
+
| `InvalidIdError` | `INVALID_ID` | a value that is no `ObjectId`, nor the string of one |
|
|
193
269
|
| `DataError` | `DATABASE` | any other server error, with its `serverCode` |
|
|
194
270
|
|
|
195
271
|
`ConflictError` carries `index`, `keys` and, when the server gives them,
|
|
@@ -211,7 +287,9 @@ server error reaches you untouched.
|
|
|
211
287
|
| --- | --- |
|
|
212
288
|
| `defineCollection(config)` | a collection: name, schema, indexes, validation |
|
|
213
289
|
| `id`, `objectId`, `timestamps`, `softDelete`, `optimisticLock`, `actors` | the field helpers |
|
|
214
|
-
| `
|
|
290
|
+
| `toObjectId`, `toObjectIds`, `tryObjectId`, `objectIdParam` | a string from outside as an `ObjectId` |
|
|
291
|
+
| `isValidObjectId`, `isObjectIdString`, `isObjectId` | the checks behind them |
|
|
292
|
+
| `getCollection(dbOrClient, definition, options?)` | the typed collection, driver methods included |
|
|
215
293
|
| `syncCollection`, `syncCollections` | create and bring in line, with `dryRun` |
|
|
216
294
|
| `withTransaction(clientOrSession, fn, options?)` | a transaction, joined when nested |
|
|
217
295
|
| `toMongoJsonSchema(schema)` | a Zod schema as a MongoDB `$jsonSchema` |
|
|
@@ -219,14 +297,47 @@ server error reaches you untouched.
|
|
|
219
297
|
| `DataError` and its subclasses, `toDataError` | the errors |
|
|
220
298
|
| `diffIndexes`, `normalizeIndex`, `validationMatches` | what `sync` compares with |
|
|
221
299
|
|
|
222
|
-
`
|
|
223
|
-
`touchUpdatedAt`, `optimisticLock`, `validate: 'off'`, `maxPageSize
|
|
300
|
+
`CollectionOptions` turns the behaviours off one by one: `softDelete`,
|
|
301
|
+
`touchUpdatedAt`, `optimisticLock`, `validate: 'off'`, `maxPageSize`, and it
|
|
302
|
+
names the database with `db` when you pass a client.
|
|
303
|
+
|
|
304
|
+
## What does not compile
|
|
305
|
+
|
|
306
|
+
The schema types more than the documents. These are compile errors, each one
|
|
307
|
+
kept as a test in `test/types/strictness.ts`:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
await collection.findMany({ sort: { nope: 1 } }); // no such field
|
|
311
|
+
await collection.findMany({ sort: { email: 'up' } }); // not a direction
|
|
312
|
+
await collection.findMany({ projection: { nope: 1 } }); // no such field
|
|
313
|
+
await collection.findMany({ projection: { email: 2 } }); // 0, 1 or an operator
|
|
314
|
+
await collection.update(id, { $set: { nope: 1 } }); // no such field
|
|
315
|
+
await collection.update(id, { $set: { email: 1 } }); // email is a string
|
|
316
|
+
await collection.update(id, { $inc: { email: 1 } }); // not a numeric field
|
|
317
|
+
await collection.update(id, { $push: { title: 'x' } }); // not an array field
|
|
318
|
+
await collection.update(id, { id: 'abc' }); // id is computed
|
|
319
|
+
collection.as('not-an-object-id'); // the schema types the actor
|
|
320
|
+
posts.as(someone); // posts stamp no actor
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
What is **not** checked: the tail of a dotted path, and a `filter`, which stays
|
|
324
|
+
the driver's `Filter` — rebuilding it would mean reimplementing every query
|
|
325
|
+
operator, and getting it subtly wrong is worse than being honest about it.
|
|
224
326
|
|
|
225
327
|
## Traps
|
|
226
328
|
|
|
227
329
|
- **There is no ambient session.** An operation inside `withTransaction` that
|
|
228
330
|
was not given the session is not part of the transaction. Use
|
|
229
|
-
`
|
|
331
|
+
`collection.withSession(session)` for every one of them.
|
|
332
|
+
- **A driver method does not take the collection's session.** `withSession`
|
|
333
|
+
binds this package's methods; `collection.aggregate(…)` is the driver's own,
|
|
334
|
+
so it wants `{ session }` in its options like anywhere else. The session is
|
|
335
|
+
on the collection as `collection.session` when you need to pass it along.
|
|
336
|
+
- **`estimatedDocumentCount` counts writes that have not committed.** It reads
|
|
337
|
+
the storage engine's metadata rather than the documents, so it is not
|
|
338
|
+
transactional and it is not exact — a document inserted by an open
|
|
339
|
+
transaction is already in its answer. `count` queries, and is the one to
|
|
340
|
+
assert on.
|
|
230
341
|
- **`$jsonSchema` is not JSON Schema.** MongoDB rejects `$ref`, `$schema`,
|
|
231
342
|
`default`, `format` and `id`, has no `integer` type, and treats a keyword it
|
|
232
343
|
does not know as an error rather than ignoring it. `toMongoJsonSchema`
|
|
@@ -255,6 +366,15 @@ server error reaches you untouched.
|
|
|
255
366
|
- **Rebuilding an index drops it first.** MongoDB cannot alter an index in
|
|
256
367
|
place, so `sync` drops and recreates one whose options changed: there is a
|
|
257
368
|
window with no index, and on a large collection the rebuild is not free.
|
|
369
|
+
- **`new ObjectId(undefined)` is a fresh id, not an error.** So is
|
|
370
|
+
`new ObjectId(null)`. A missing route parameter turns into a valid id that
|
|
371
|
+
matches nothing, and the bug surfaces as an empty result rather than as a
|
|
372
|
+
failure. Use `toObjectId`, which throws, or `tryObjectId`, which answers
|
|
373
|
+
`undefined`.
|
|
374
|
+
- **`id` is computed, not stored.** It is on every document a repository
|
|
375
|
+
returns, and on none in the collection: a filter or a patch keyed on it would
|
|
376
|
+
match nothing, so both are compile errors. Query on `_id`. A schema that
|
|
377
|
+
declares an `id` field of its own keeps it, untouched.
|
|
258
378
|
- **`validate: 'off'` also turns the defaults off.** Nothing fills `_id`,
|
|
259
379
|
`createdAt` or `version` any more, because filling them is what parsing does.
|
|
260
380
|
- **The driver retries a transaction's callback** on a transient error, for up
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Db, MongoClient } from 'mongodb';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import { type CollectionDefinition } from '../definition/define-collection';
|
|
4
|
+
import type { CollectionOptions, TypedCollection } from './types';
|
|
5
|
+
/** What a collection can be reached through: a database, or a client. */
|
|
6
|
+
export type CollectionSource = Db | MongoClient;
|
|
7
|
+
/**
|
|
8
|
+
* A collection: this package's methods and the driver's own, on one object.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const users = getCollection(db, usersDefinition);
|
|
12
|
+
*
|
|
13
|
+
* const ada = await users.create({ email: 'ada@example.com' });
|
|
14
|
+
* await users.update(ada._id, { name: 'Ada' }, { expectedVersion: ada.version });
|
|
15
|
+
* await users.aggregate([{ $group: { _id: '$teamId', n: { $sum: 1 } } }]);
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* It takes a `Db`, or a `MongoClient` — then the database is the URI's, or the
|
|
19
|
+
* one named in `{ db }`.
|
|
20
|
+
*
|
|
21
|
+
* Every operation runs in the collection's session, which `withSession` sets:
|
|
22
|
+
* MongoDB has no ambient session, so a write inside a transaction that was not
|
|
23
|
+
* given one is not part of it and is not rolled back.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getCollection<Schema extends z.ZodObject>(source: CollectionSource, definition: CollectionDefinition<Schema>, options?: CollectionOptions<CollectionDefinition<Schema>>): TypedCollection<CollectionDefinition<Schema>>;
|
|
26
|
+
//# sourceMappingURL=get-collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-collection.d.ts","sourceRoot":"","sources":["../../src/collection/get-collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,EAAE,EAAY,WAAW,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAEN,KAAK,oBAAoB,EAEzB,MAAM,iCAAiC,CAAC;AAiBzC,OAAO,KAAK,EACX,iBAAiB,EAEjB,eAAe,EACf,MAAM,SAAS,CAAC;AAsBjB,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG,EAAE,GAAG,WAAW,CAAC;AAmBhD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,MAAM,SAAS,CAAC,CAAC,SAAS,EACvD,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,oBAAoB,CAAC,MAAM,CAAC,EACxC,OAAO,GAAE,iBAAiB,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAM,GAC3D,eAAe,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAG/C"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import type { ClientSession, Db, Collection as DriverCollection, Filter } from 'mongodb';
|
|
2
|
+
import type { DocumentOf, FieldOf, IdOf, NewDocumentOf, ReadDocumentOf } from '../definition/define-collection';
|
|
3
|
+
import type { CursorPage, Page, PageOptions } from '../pagination/page';
|
|
4
|
+
import type { SyncOptions, SyncReport } from '../sync/sync-collection';
|
|
5
|
+
export type OrderDirection = 'asc' | 'desc';
|
|
6
|
+
/**
|
|
7
|
+
* A field of the documents, or a path into one. The tail of a path cannot be
|
|
8
|
+
* checked — a schema says nothing about what is under an arbitrary key — but
|
|
9
|
+
* its head must be a field, which is what catches a misspelt name.
|
|
10
|
+
*/
|
|
11
|
+
export type FieldPath<Def> = FieldOf<Def> | `${FieldOf<Def>}.${string}`;
|
|
12
|
+
/** The fields whose type is a number, for `$inc` and `$mul`. */
|
|
13
|
+
type NumericFieldsOf<Doc> = {
|
|
14
|
+
[K in keyof Doc]: NonNullable<Doc[K]> extends number ? K : never;
|
|
15
|
+
}[keyof Doc] & string;
|
|
16
|
+
/** The fields whose type is an array, for `$push` and `$addToSet`. */
|
|
17
|
+
type ArrayFieldsOf<Doc> = {
|
|
18
|
+
[K in keyof Doc]: NonNullable<Doc[K]> extends readonly unknown[] ? K : never;
|
|
19
|
+
}[keyof Doc] & string;
|
|
20
|
+
type ElementOf<T> = NonNullable<T> extends readonly (infer Element)[] ? Element : never;
|
|
21
|
+
/** A sort, keyed on the schema's fields. */
|
|
22
|
+
export type SortOf<Def> = {
|
|
23
|
+
[Field in FieldPath<Def>]?: 1 | -1 | 'asc' | 'desc';
|
|
24
|
+
};
|
|
25
|
+
/** What a projection may hold beside `0` and `1`. */
|
|
26
|
+
export type ProjectionOperator = {
|
|
27
|
+
$slice: number | [number, number];
|
|
28
|
+
} | {
|
|
29
|
+
$elemMatch: Record<string, unknown>;
|
|
30
|
+
} | {
|
|
31
|
+
$meta: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* A projection, keyed on the schema's fields. It says what is sent over the
|
|
35
|
+
* wire; it does not narrow the type of the documents, because the driver
|
|
36
|
+
* cannot either — a projected read is typed as the whole document.
|
|
37
|
+
*/
|
|
38
|
+
export type ProjectionOf<Def> = {
|
|
39
|
+
[Field in FieldPath<Def>]?: 0 | 1 | boolean | ProjectionOperator;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Who is writing: the type the schema gives `createdBy`, or `updatedBy`, or
|
|
43
|
+
* `deletedBy`. A collection with none of them has no actor to stamp, so `as`
|
|
44
|
+
* cannot be called on it at all.
|
|
45
|
+
*/
|
|
46
|
+
export type ActorOf<Def> = 'createdBy' extends keyof DocumentOf<Def> ? NonNullable<DocumentOf<Def>['createdBy']> : 'updatedBy' extends keyof DocumentOf<Def> ? NonNullable<DocumentOf<Def>['updatedBy']> : 'deletedBy' extends keyof DocumentOf<Def> ? NonNullable<DocumentOf<Def>['deletedBy']> : never;
|
|
47
|
+
/** What `$set` takes: a field's own type, or anything under a path. */
|
|
48
|
+
export type SetOf<Def> = {
|
|
49
|
+
[Field in FieldPath<Def>]?: Field extends keyof DocumentOf<Def> ? DocumentOf<Def>[Field] : unknown;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* MongoDB's update operators, keyed on the schema.
|
|
53
|
+
*
|
|
54
|
+
* The driver's own `UpdateFilter` is intersected with `Document`, whose index
|
|
55
|
+
* signature accepts every key — `$set: { nope: 1 }` included — and `Omit`
|
|
56
|
+
* cannot take that back, since omitting a literal key from an index signature
|
|
57
|
+
* leaves the index signature. So the operators are declared here instead. One
|
|
58
|
+
* this does not name is a reason to reach for `raw`, which is the driver's own
|
|
59
|
+
* collection, untouched.
|
|
60
|
+
*/
|
|
61
|
+
export interface UpdateOperators<Def> {
|
|
62
|
+
$set?: SetOf<Def>;
|
|
63
|
+
$setOnInsert?: SetOf<Def>;
|
|
64
|
+
$unset?: {
|
|
65
|
+
[Field in FieldPath<Def>]?: '' | 1 | true;
|
|
66
|
+
};
|
|
67
|
+
$inc?: {
|
|
68
|
+
[Field in NumericFieldsOf<DocumentOf<Def>> | `${FieldOf<Def>}.${string}`]?: number;
|
|
69
|
+
};
|
|
70
|
+
$mul?: {
|
|
71
|
+
[Field in NumericFieldsOf<DocumentOf<Def>> | `${FieldOf<Def>}.${string}`]?: number;
|
|
72
|
+
};
|
|
73
|
+
$min?: SetOf<Def>;
|
|
74
|
+
$max?: SetOf<Def>;
|
|
75
|
+
$rename?: {
|
|
76
|
+
[Field in FieldPath<Def>]?: string;
|
|
77
|
+
};
|
|
78
|
+
$currentDate?: {
|
|
79
|
+
[Field in FieldPath<Def>]?: true | {
|
|
80
|
+
$type: 'date' | 'timestamp';
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
$push?: PushOf<Def>;
|
|
84
|
+
$addToSet?: PushOf<Def>;
|
|
85
|
+
$pull?: {
|
|
86
|
+
[Field in FieldPath<Def>]?: unknown;
|
|
87
|
+
};
|
|
88
|
+
$pullAll?: {
|
|
89
|
+
[Field in FieldPath<Def>]?: readonly unknown[];
|
|
90
|
+
};
|
|
91
|
+
$pop?: {
|
|
92
|
+
[Field in FieldPath<Def>]?: 1 | -1;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** What `$push` and `$addToSet` take: an element of the array, or `$each`. */
|
|
96
|
+
export type PushOf<Def> = {
|
|
97
|
+
[Field in ArrayFieldsOf<DocumentOf<Def>>]?: ElementOf<DocumentOf<Def>[Field]> | {
|
|
98
|
+
$each: readonly ElementOf<DocumentOf<Def>[Field]>[];
|
|
99
|
+
$position?: number;
|
|
100
|
+
$slice?: number;
|
|
101
|
+
$sort?: 1 | -1 | Record<string, 1 | -1>;
|
|
102
|
+
};
|
|
103
|
+
} & {
|
|
104
|
+
[Path in `${string}.${string}`]?: unknown;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* What an update writes: the document's own fields, checked against the
|
|
108
|
+
* schema, or MongoDB's operators for what they cannot say.
|
|
109
|
+
*/
|
|
110
|
+
export type Patch<Def> = Partial<DocumentOf<Def>> | (UpdateOperators<Def> & {
|
|
111
|
+
[K in keyof DocumentOf<Def>]?: never;
|
|
112
|
+
} & {
|
|
113
|
+
id?: never;
|
|
114
|
+
});
|
|
115
|
+
export interface ReadOptions {
|
|
116
|
+
/** Include soft-deleted documents. Ignored without a `deletedAt` field. */
|
|
117
|
+
withDeleted?: boolean;
|
|
118
|
+
}
|
|
119
|
+
export interface FindFirstOptions<Def> extends ReadOptions {
|
|
120
|
+
sort?: SortOf<Def>;
|
|
121
|
+
projection?: ProjectionOf<Def>;
|
|
122
|
+
}
|
|
123
|
+
export interface FindManyOptions<Def> extends FindFirstOptions<Def> {
|
|
124
|
+
filter?: Filter<DocumentOf<Def>>;
|
|
125
|
+
limit?: number;
|
|
126
|
+
skip?: number;
|
|
127
|
+
}
|
|
128
|
+
export interface PaginateOptions<Def> extends PageOptions, ReadOptions {
|
|
129
|
+
filter?: Filter<DocumentOf<Def>>;
|
|
130
|
+
/** Default `{ _id: 1 }`, so that pages are stable. */
|
|
131
|
+
sort?: SortOf<Def>;
|
|
132
|
+
}
|
|
133
|
+
export interface CursorPaginateOptions<Def> extends ReadOptions {
|
|
134
|
+
/** The `nextCursor` of the previous page. Omit it for the first page. */
|
|
135
|
+
after?: string | null | undefined;
|
|
136
|
+
/** Documents per page. Default `20`, at most `maxPageSize`. */
|
|
137
|
+
limit?: number;
|
|
138
|
+
filter?: Filter<DocumentOf<Def>>;
|
|
139
|
+
/**
|
|
140
|
+
* The field to page along. Default `_id`. Any other is followed by `_id`,
|
|
141
|
+
* which breaks its ties, and must be set on every document.
|
|
142
|
+
*/
|
|
143
|
+
orderBy?: FieldOf<Def>;
|
|
144
|
+
/** Default `'asc'`. */
|
|
145
|
+
direction?: OrderDirection;
|
|
146
|
+
}
|
|
147
|
+
export interface UpdateOptions {
|
|
148
|
+
/**
|
|
149
|
+
* Only update the document while its `version` is still this one. When it
|
|
150
|
+
* is not, nothing is written and `OptimisticLockError` is thrown with the
|
|
151
|
+
* version the document has now.
|
|
152
|
+
*/
|
|
153
|
+
expectedVersion?: number;
|
|
154
|
+
}
|
|
155
|
+
export interface CollectionOptions<Def> {
|
|
156
|
+
/**
|
|
157
|
+
* Soft delete through the `deletedAt` field. Default: on when the schema
|
|
158
|
+
* has one. `false` makes `delete` a real delete.
|
|
159
|
+
*/
|
|
160
|
+
softDelete?: boolean;
|
|
161
|
+
/**
|
|
162
|
+
* Set `updatedAt` on every update that does not set it. Default: on when
|
|
163
|
+
* the schema has the field.
|
|
164
|
+
*/
|
|
165
|
+
touchUpdatedAt?: boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Raise `version` by one on every update. Default: on when the schema has
|
|
168
|
+
* the field. `expectedVersion` needs it.
|
|
169
|
+
*/
|
|
170
|
+
optimisticLock?: boolean;
|
|
171
|
+
/**
|
|
172
|
+
* Check documents against the schema before writing them, which is also
|
|
173
|
+
* what fills their defaults. Default `'parse'`. `'off'` sends them as they
|
|
174
|
+
* are — and then nothing fills `_id`, `createdAt` or `version`.
|
|
175
|
+
*/
|
|
176
|
+
validate?: 'parse' | 'off';
|
|
177
|
+
/** The largest `pageSize` or `limit` a page may ask for. Default `100`. */
|
|
178
|
+
maxPageSize?: number;
|
|
179
|
+
/** The session every operation runs in. `withSession` is how it is set. */
|
|
180
|
+
session?: ClientSession;
|
|
181
|
+
/** Who is writing, stamped into `createdBy`, `updatedBy` and `deletedBy`. */
|
|
182
|
+
actor?: ActorOf<Def>;
|
|
183
|
+
/** Which database, when `getCollection` is given a client rather than a `Db`. */
|
|
184
|
+
db?: string;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* What this package adds to a collection. The collection you are handed is
|
|
188
|
+
* this **and** the driver's own `Collection`, so `aggregate`, `watch`,
|
|
189
|
+
* `bulkWrite` and the rest are on it directly.
|
|
190
|
+
*/
|
|
191
|
+
export interface CollectionApi<Def> {
|
|
192
|
+
readonly definition: Def;
|
|
193
|
+
readonly db: Db;
|
|
194
|
+
/**
|
|
195
|
+
* The driver's collection, untouched. It is where the three methods this
|
|
196
|
+
* one redefines still live — `raw.updateMany`, `raw.deleteMany` and
|
|
197
|
+
* `raw.count` — and where an update operator this package does not name
|
|
198
|
+
* can still be sent.
|
|
199
|
+
*/
|
|
200
|
+
readonly raw: DriverCollection<DocumentOf<Def>>;
|
|
201
|
+
/** The session every operation of this collection runs in, if any. */
|
|
202
|
+
readonly session: ClientSession | undefined;
|
|
203
|
+
/**
|
|
204
|
+
* The same collection, bound to a session. MongoDB has no ambient session:
|
|
205
|
+
* without this, an operation inside a transaction runs outside it.
|
|
206
|
+
*
|
|
207
|
+
* It binds **this package's** methods. A driver method reached through the
|
|
208
|
+
* collection takes its session the driver's way, in its options.
|
|
209
|
+
*/
|
|
210
|
+
withSession(session: ClientSession | undefined): TypedCollection<Def>;
|
|
211
|
+
/** The same collection, stamping this actor into the `*By` fields. */
|
|
212
|
+
as(actor: ActorOf<Def>): TypedCollection<Def>;
|
|
213
|
+
/** Creates the collection, its validator and its indexes. See `syncCollection`. */
|
|
214
|
+
sync(options?: SyncOptions): Promise<SyncReport>;
|
|
215
|
+
/** The document with this `_id`, or `undefined`. */
|
|
216
|
+
findById(id: IdOf<Def>, options?: ReadOptions): Promise<ReadDocumentOf<Def> | undefined>;
|
|
217
|
+
/** The document with this `_id`. Throws `NotFoundError`. */
|
|
218
|
+
getById(id: IdOf<Def>, options?: ReadOptions): Promise<ReadDocumentOf<Def>>;
|
|
219
|
+
/** The first document that matches, or `undefined`. */
|
|
220
|
+
findFirst(filter?: Filter<DocumentOf<Def>>, options?: FindFirstOptions<Def>): Promise<ReadDocumentOf<Def> | undefined>;
|
|
221
|
+
/** Every document that matches. */
|
|
222
|
+
findMany(options?: FindManyOptions<Def>): Promise<ReadDocumentOf<Def>[]>;
|
|
223
|
+
/** Checks the document against the schema, fills its defaults, inserts it. */
|
|
224
|
+
create(values: NewDocumentOf<Def>): Promise<ReadDocumentOf<Def>>;
|
|
225
|
+
/** The same, in one insert. `[]` sends nothing. */
|
|
226
|
+
createMany(values: readonly NewDocumentOf<Def>[]): Promise<ReadDocumentOf<Def>[]>;
|
|
227
|
+
/** Updates the document with this `_id` and returns it. Throws `NotFoundError`. */
|
|
228
|
+
update(id: IdOf<Def>, patch: Patch<Def>, options?: UpdateOptions): Promise<ReadDocumentOf<Def>>;
|
|
229
|
+
/**
|
|
230
|
+
* Updates every document that matches, and returns how many changed. The
|
|
231
|
+
* driver's own `updateMany`, which returns an `UpdateResult` and takes no
|
|
232
|
+
* filter for granted, is `raw.updateMany`.
|
|
233
|
+
*/
|
|
234
|
+
updateMany(filter: Filter<DocumentOf<Def>>, patch: Patch<Def>): Promise<number>;
|
|
235
|
+
/**
|
|
236
|
+
* Deletes the document with this `_id` and returns it: a soft delete on a
|
|
237
|
+
* collection with `deletedAt`. Throws `NotFoundError`.
|
|
238
|
+
*/
|
|
239
|
+
delete(id: IdOf<Def>): Promise<ReadDocumentOf<Def>>;
|
|
240
|
+
/** Deletes every document that matches, and returns how many. `raw.deleteMany` is the driver's. */
|
|
241
|
+
deleteMany(filter: Filter<DocumentOf<Def>>): Promise<number>;
|
|
242
|
+
/** A real delete, of a live or a soft-deleted document. */
|
|
243
|
+
hardDelete(id: IdOf<Def>): Promise<ReadDocumentOf<Def>>;
|
|
244
|
+
/** A real delete of every document that matches, soft-deleted ones included. */
|
|
245
|
+
hardDeleteMany(filter: Filter<DocumentOf<Def>>): Promise<number>;
|
|
246
|
+
/** Clears `deletedAt` and returns the document. Throws `NotFoundError`. */
|
|
247
|
+
restore(id: IdOf<Def>): Promise<ReadDocumentOf<Def>>;
|
|
248
|
+
/**
|
|
249
|
+
* How many documents match, soft-deleted ones left out. The driver's
|
|
250
|
+
* `count` is `raw.count`, and `estimatedDocumentCount` is on this
|
|
251
|
+
* collection directly.
|
|
252
|
+
*/
|
|
253
|
+
count(filter?: Filter<DocumentOf<Def>>, options?: ReadOptions): Promise<number>;
|
|
254
|
+
/** Whether any document matches. */
|
|
255
|
+
exists(filter: Filter<DocumentOf<Def>>, options?: ReadOptions): Promise<boolean>;
|
|
256
|
+
/** One page of the documents that match, and how many there are. */
|
|
257
|
+
paginate(options?: PaginateOptions<Def>): Promise<Page<ReadDocumentOf<Def>>>;
|
|
258
|
+
/** One page of the documents that match, after a cursor. */
|
|
259
|
+
paginateByCursor(options?: CursorPaginateOptions<Def>): Promise<CursorPage<ReadDocumentOf<Def>>>;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* A collection: this package's methods, plus every method of the driver's own
|
|
263
|
+
* `Collection` that they do not redefine.
|
|
264
|
+
*
|
|
265
|
+
* Three names are defined by both, and this package's win: `count`,
|
|
266
|
+
* `updateMany` and `deleteMany`. The driver's are on `raw`.
|
|
267
|
+
*/
|
|
268
|
+
export type TypedCollection<Def> = CollectionApi<Def> & Omit<DriverCollection<DocumentOf<Def>>, keyof CollectionApi<Def>>;
|
|
269
|
+
export {};
|
|
270
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/collection/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IndexDescription, ObjectId } from 'mongodb';
|
|
1
|
+
import type { IndexDescription, IndexDirection, ObjectId } from 'mongodb';
|
|
2
2
|
import type { z } from 'zod';
|
|
3
3
|
/** What MongoDB does with a document that fails the validator. */
|
|
4
4
|
export type ValidationAction = 'error' | 'warn';
|
|
@@ -13,6 +13,23 @@ export interface ValidationConfig {
|
|
|
13
13
|
/** Default `'error'`. `'warn'` logs and lets the write through. */
|
|
14
14
|
action?: ValidationAction;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* What an index may be keyed on: a field of the documents, which an editor
|
|
18
|
+
* completes, or a path into one — `{ 'address.city': 1 }` is how MongoDB
|
|
19
|
+
* indexes a nested field, and there is no way to check the tail of a path
|
|
20
|
+
* against a schema without rejecting the paths Mongo allows.
|
|
21
|
+
*/
|
|
22
|
+
export type IndexKey<Doc> = {
|
|
23
|
+
[Field in (keyof Doc & string) | `${keyof Doc & string}.${string}`]?: IndexDirection;
|
|
24
|
+
} | Map<string, IndexDirection>;
|
|
25
|
+
/**
|
|
26
|
+
* An index, keyed on the schema's own fields. Everything else — `unique`,
|
|
27
|
+
* `name`, `collation`, `partialFilterExpression`, the TTL — is the driver's
|
|
28
|
+
* `IndexDescription`, unchanged.
|
|
29
|
+
*/
|
|
30
|
+
export interface CollectionIndex<Doc> extends Omit<IndexDescription, 'key'> {
|
|
31
|
+
key: IndexKey<Doc>;
|
|
32
|
+
}
|
|
16
33
|
/** What `defineCollection` takes. */
|
|
17
34
|
export interface CollectionConfig<Schema extends z.ZodObject> {
|
|
18
35
|
/** The collection's name on the server. */
|
|
@@ -22,13 +39,16 @@ export interface CollectionConfig<Schema extends z.ZodObject> {
|
|
|
22
39
|
* `z.input` what a write takes. It must have an `_id`.
|
|
23
40
|
*/
|
|
24
41
|
schema: Schema;
|
|
25
|
-
/** The indexes `sync` creates,
|
|
26
|
-
indexes?: readonly
|
|
42
|
+
/** The indexes `sync` creates, keyed on the schema's fields. */
|
|
43
|
+
indexes?: readonly CollectionIndex<z.output<Schema>>[];
|
|
27
44
|
/** The `$jsonSchema` validator `sync` writes from the schema. */
|
|
28
45
|
validation?: ValidationConfig;
|
|
29
46
|
}
|
|
30
47
|
/** A collection, as `defineCollection` returns it: frozen, with its defaults. */
|
|
31
|
-
export interface CollectionDefinition<Schema extends z.ZodObject = z.ZodObject>
|
|
48
|
+
export interface CollectionDefinition<Schema extends z.ZodObject = z.ZodObject> {
|
|
49
|
+
readonly name: string;
|
|
50
|
+
readonly schema: Schema;
|
|
51
|
+
/** As the driver takes them: `sync` hands these straight to MongoDB. */
|
|
32
52
|
readonly indexes: readonly IndexDescription[];
|
|
33
53
|
readonly validation: Required<ValidationConfig>;
|
|
34
54
|
}
|
|
@@ -38,6 +58,18 @@ export type AnyCollectionDefinition = CollectionDefinition<any>;
|
|
|
38
58
|
export type DocumentOf<Def> = Def extends {
|
|
39
59
|
schema: infer Schema;
|
|
40
60
|
} ? Schema extends z.ZodType ? z.output<Schema> : never : never;
|
|
61
|
+
/**
|
|
62
|
+
* A document as a collection gives it back: the stored document, plus `id`.
|
|
63
|
+
*
|
|
64
|
+
* `id` is `_id` as a string, computed rather than stored — the collection
|
|
65
|
+
* holds `_id` alone. It is enumerable, so `JSON.stringify` and a spread carry
|
|
66
|
+
* it, which is what makes a document ready to return from an API; it is not
|
|
67
|
+
* part of `DocumentOf`, so a filter or a patch cannot be keyed on it, because
|
|
68
|
+
* the server would match nothing.
|
|
69
|
+
*/
|
|
70
|
+
export type ReadDocumentOf<Def> = DocumentOf<Def> & {
|
|
71
|
+
readonly id: string;
|
|
72
|
+
};
|
|
41
73
|
/** What a write takes: the documents before their defaults are filled. */
|
|
42
74
|
export type NewDocumentOf<Def> = Def extends {
|
|
43
75
|
schema: infer Schema;
|
|
@@ -69,7 +101,7 @@ export type FieldOf<Def> = keyof DocumentOf<Def> & string;
|
|
|
69
101
|
* ```
|
|
70
102
|
*/
|
|
71
103
|
export declare function defineCollection<Schema extends z.ZodObject>(config: CollectionConfig<Schema>): CollectionDefinition<Schema>;
|
|
72
|
-
/** Which of the fields the
|
|
104
|
+
/** Which of the fields the collection knows about a definition's schema has. */
|
|
73
105
|
export declare function stampsOf(definition: AnyCollectionDefinition): {
|
|
74
106
|
createdAt: boolean;
|
|
75
107
|
updatedAt: boolean;
|