@nxgt/mongo-kit 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 +21 -0
- package/README.md +273 -0
- package/dist/config/checks.d.ts +9 -0
- package/dist/config/checks.d.ts.map +1 -0
- package/dist/config/define-config.d.ts +30 -0
- package/dist/config/define-config.d.ts.map +1 -0
- package/dist/config/types.d.ts +124 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/discover.d.ts +37 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +359 -0
- package/dist/index.js.map +18 -0
- package/dist/kit/context.d.ts +39 -0
- package/dist/kit/context.d.ts.map +1 -0
- package/dist/kit/create-kit.d.ts +18 -0
- package/dist/kit/create-kit.d.ts.map +1 -0
- package/dist/kit/derive.d.ts +9 -0
- package/dist/kit/derive.d.ts.map +1 -0
- package/dist/kit/scope.d.ts +18 -0
- package/dist/kit/scope.d.ts.map +1 -0
- package/dist/kit/sync.d.ts +12 -0
- package/dist/kit/sync.d.ts.map +1 -0
- package/dist/kit/transaction.d.ts +24 -0
- package/dist/kit/transaction.d.ts.map +1 -0
- package/dist/kit/types.d.ts +92 -0
- package/dist/kit/types.d.ts.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Steve Tsala
|
|
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,273 @@
|
|
|
1
|
+
# @nxgt/mongo-kit
|
|
2
|
+
|
|
3
|
+
An application's MongoDB in one object: a configuration checked once, the
|
|
4
|
+
clients it needs opened from it, and every collection of
|
|
5
|
+
[`@nxgt/mongo`](https://www.npmjs.com/package/@nxgt/mongo) typed on the
|
|
6
|
+
database it lives in.
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { createKit, defineConfig } from '@nxgt/mongo-kit';
|
|
10
|
+
import * as collections from './models'; // every `defineCollection` of the app
|
|
11
|
+
|
|
12
|
+
export const kit = await createKit(
|
|
13
|
+
defineConfig({ uri: process.env.MONGO_URI!, collections }),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const user = await kit.db.users.create({ email: 'ada@example.com' });
|
|
17
|
+
const posts = await kit.db.posts.findMany({ filter: { authorId: user._id } });
|
|
18
|
+
await kit.db.command({ ping: 1 }); // the driver's Db, untouched
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`kit.db` is the driver's `Db` with the collections on it: `db.users` is the
|
|
22
|
+
typed collection `getCollection(db, users)` gives, and everything a `Db`
|
|
23
|
+
answers to is still there. Nothing else has to be wired: no `getCollection`
|
|
24
|
+
at each call site, no client to pass around, no session to thread by hand.
|
|
25
|
+
|
|
26
|
+
> **0.x, on `@nxgt/mongo`.** The API is still settling.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
bun add @nxgt/mongo-kit @nxgt/mongo mongodb zod
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- `@nxgt/mongo`: required peer. The collections, their options and their
|
|
35
|
+
behaviour are its; this package wires them.
|
|
36
|
+
- `mongodb` `>=7.0.0 <8`: required peer, as `@nxgt/mongo` needs it. `zod` is
|
|
37
|
+
`@nxgt/mongo`'s.
|
|
38
|
+
- `typescript` 6: required peer, the version every `@nxgt` package pins.
|
|
39
|
+
- Tested against MongoDB 8.2. A **replica set** only for transactions, which
|
|
40
|
+
is MongoDB's own rule.
|
|
41
|
+
|
|
42
|
+
## The collections
|
|
43
|
+
|
|
44
|
+
They come from a module object — one import, and every type follows:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
// src/models/index.ts
|
|
48
|
+
export * from './users.model';
|
|
49
|
+
export * from './posts.model';
|
|
50
|
+
|
|
51
|
+
// src/db.ts
|
|
52
|
+
import * as collections from './models';
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Each export that is a `defineCollection` becomes a key on the scope, under
|
|
56
|
+
the name it is exported by; anything else in the module — a function, a
|
|
57
|
+
constant, a type — is left where it is. The key is the name the application
|
|
58
|
+
reads (`db.users`), and the collection's own `name` is the one on the server,
|
|
59
|
+
so `export const users = defineCollection({ name: 'app_users', … })` is
|
|
60
|
+
`db.users` here and `app_users` there. Two exports on one server collection
|
|
61
|
+
are refused: two keys writing to the same place is a mistake, not a feature.
|
|
62
|
+
|
|
63
|
+
For a **script** — a sync or a migration run from the repository — the files
|
|
64
|
+
can be read from disk instead:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { syncCollections } from '@nxgt/mongo';
|
|
68
|
+
import { discoverCollections } from '@nxgt/mongo-kit';
|
|
69
|
+
|
|
70
|
+
const definitions = await discoverCollections({ glob: 'src/**/*.model.ts' });
|
|
71
|
+
await syncCollections(db, definitions); // `db` from connectMongo, say
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
It gives `@nxgt/mongo`'s `AnyCollectionDefinition[]`, **with no types**: a
|
|
75
|
+
glob is read at run time, so a bundler cannot follow it and the compiler sees
|
|
76
|
+
nothing. It **runs under Bun** — the glob is `Bun.Glob` — and it imports each
|
|
77
|
+
file it finds, so their top level runs. It is for scripts, never for the
|
|
78
|
+
wiring of an application.
|
|
79
|
+
|
|
80
|
+
## Several databases
|
|
81
|
+
|
|
82
|
+
They name themselves, and `kit.databases` reads them:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
export const kit = await createKit(
|
|
86
|
+
defineConfig({
|
|
87
|
+
databases: {
|
|
88
|
+
main: { uri: process.env.MONGO_URI!, collections },
|
|
89
|
+
analytics: { uri: process.env.ANALYTICS_URI!, collections: events },
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
await kit.databases.main.users.create({ email: 'ada@example.com' });
|
|
95
|
+
await kit.databases.analytics.events.create({ kind: 'signup' });
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`kit.db` is then `never`: with two databases there is no “the” database, and
|
|
99
|
+
the name is what says which — and reading it anyway, from JavaScript or
|
|
100
|
+
across an `any`, throws. A single database is named `default`, so
|
|
101
|
+
`kit.databases.default` and `kit.clients.default` are the long way of writing
|
|
102
|
+
the same thing, and `sync()` reports under that key.
|
|
103
|
+
|
|
104
|
+
Two databases on one URI share one client, which is what `connectMongo`
|
|
105
|
+
already does; their `clientOptions` must then be identical, since the second
|
|
106
|
+
hold on a client opened with other options is refused.
|
|
107
|
+
|
|
108
|
+
## The actor and the session
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
await kit.as(userId).db.posts.create({ title: 'a' }); // stamps createdBy
|
|
112
|
+
|
|
113
|
+
await kit.as(userId).transaction(async (tx) => {
|
|
114
|
+
const team = await tx.db.teams.create({ name: 'Core' });
|
|
115
|
+
await tx.db.users.update(userId, { teamId: team._id });
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`as` and `withSession` give back **another kit** over the same clients: the
|
|
120
|
+
one they came from is unchanged, so a request's kit never leaks into the
|
|
121
|
+
next. The collections are built on the first read and kept, so a request
|
|
122
|
+
pays for the collections it touches and no others.
|
|
123
|
+
|
|
124
|
+
`transaction` runs the body with a kit whose collections are all in the
|
|
125
|
+
session — nothing has to be passed. **The driver retries the body from the
|
|
126
|
+
start** on a transient error, so it must be safe to run twice: keep side
|
|
127
|
+
effects that are not MongoDB's out of it. A transaction inside a transaction
|
|
128
|
+
**joins** the outer one, and takes no `{ on }`, since the session already
|
|
129
|
+
decided; MongoDB has no savepoints, so an inner failure takes the whole
|
|
130
|
+
transaction with it.
|
|
131
|
+
|
|
132
|
+
With several clients, `{ on: 'main' }` says whose, since a transaction lives
|
|
133
|
+
on one client — and inside that body only the databases on that client can be
|
|
134
|
+
used: an operation on another one carries a session its client does not own,
|
|
135
|
+
and the driver refuses it.
|
|
136
|
+
|
|
137
|
+
The actor's type is the one the collections agree on: a kit whose collections
|
|
138
|
+
stamp an `ObjectId` takes an `ObjectId`, and one whose collections stamp
|
|
139
|
+
nothing has no `as` to call.
|
|
140
|
+
|
|
141
|
+
## Sync
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
const reports = await kit.sync(); // { main: [ … ], analytics: [ … ] }
|
|
145
|
+
await kit.sync({ dryRun: true }); // what it would change
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The first database that throws stops the rest, which is what `dryRun` is for:
|
|
149
|
+
it reports everything at once. It syncs exactly the collections the kit
|
|
150
|
+
wires, database by database —
|
|
151
|
+
`@nxgt/mongo`'s `syncAll` cannot, since its registry knows no database. It is
|
|
152
|
+
a **deployment step**: `collMod` needs the `dbAdmin` role, and an index build
|
|
153
|
+
runs outside any transaction. For tests and development, `autoSync: true` in
|
|
154
|
+
the config syncs each collection before its first operation instead.
|
|
155
|
+
|
|
156
|
+
## Closing
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
await kit.close(); // or `await using kit = await createKit(…)`
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
It gives back the clients it opened, and leaves alone a `client` the config
|
|
163
|
+
gave it: what it did not open is not its to close. Only the kit `createKit`
|
|
164
|
+
returned can be closed — one from `as`, `withSession` or a transaction shares
|
|
165
|
+
those clients and refuses.
|
|
166
|
+
|
|
167
|
+
## API
|
|
168
|
+
|
|
169
|
+
### `defineConfig(config)`
|
|
170
|
+
|
|
171
|
+
Checks the configuration and freezes it. It connects to nothing and reads no
|
|
172
|
+
environment variable: the application writes `uri: process.env.MONGO_URI!`,
|
|
173
|
+
and what is wrong throws here, where the application starts.
|
|
174
|
+
|
|
175
|
+
| Key | Default | |
|
|
176
|
+
| --- | --- | --- |
|
|
177
|
+
| `uri` | — | One of `uri` and `client`, never both. |
|
|
178
|
+
| `client` | — | A client the application opened. Never closed by the kit. |
|
|
179
|
+
| `clientOptions` | `{}` | Passed to the driver with `uri`. Refused with `client`. |
|
|
180
|
+
| `database` | the URI's, else `test` | The database's name. |
|
|
181
|
+
| `collections` | — | `import * as collections from './models'`. |
|
|
182
|
+
| `options` | `{}` | `@nxgt/mongo`'s collection options, for every collection. |
|
|
183
|
+
| `optionsFor` | `{}` | The same, per key, merged over `options`. |
|
|
184
|
+
| `autoSync` | `false` | Sync each collection before its first operation. |
|
|
185
|
+
|
|
186
|
+
`db`, `session`, `actor` and `autoSync` are not collection options here: the
|
|
187
|
+
kit decides them, and one of them under `options` does not compile, while one
|
|
188
|
+
under `optionsFor` is refused by `defineConfig` — the types cannot see that
|
|
189
|
+
deep. Several databases go under `databases: { main: …, … }`, each one taking
|
|
190
|
+
the same keys; a lone database is named `default`.
|
|
191
|
+
|
|
192
|
+
### `createKit(config)`
|
|
193
|
+
|
|
194
|
+
Opens what the configuration describes, and gives a `MongoKit`:
|
|
195
|
+
|
|
196
|
+
| Member | |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| `db` | The only database's scope; `never` with several, and it throws if read anyway. |
|
|
199
|
+
| `databases` | Every scope, under its name — `default` when the config named none. |
|
|
200
|
+
| `clients` | The `MongoClient` of each database, under the same names. |
|
|
201
|
+
| `actor`, `session` | What this kit stamps and runs in, if anything. |
|
|
202
|
+
| `as(actor)` | The same kit, stamping that actor. |
|
|
203
|
+
| `withSession(session)` | The same kit, in that session; `undefined` takes it away. |
|
|
204
|
+
| `transaction(fn, options?)` | `fn` with a kit in a transaction. May run twice. |
|
|
205
|
+
| `sync(options?)` | `SyncReport[]` per database, under its name. |
|
|
206
|
+
| `close()` | Gives back what it opened. Idempotent. |
|
|
207
|
+
|
|
208
|
+
`KitOf<typeof config>` is that kit's type, for an application that declares
|
|
209
|
+
it — a service holding the kit, say — rather than reading it off `await
|
|
210
|
+
createKit(…)`.
|
|
211
|
+
|
|
212
|
+
### `discoverCollections({ glob, cwd?, export? })`
|
|
213
|
+
|
|
214
|
+
The definitions of the files a glob matches, read at run time and untyped.
|
|
215
|
+
`cwd` is where the glob starts, `process.cwd()` by default. `export` reads
|
|
216
|
+
one export by name in each file, and throws for a matched file that has no
|
|
217
|
+
definition under it; without `export`, every export that is a definition is
|
|
218
|
+
taken. Two files defining the same server collection are refused. It needs
|
|
219
|
+
the Bun runtime, and it is for scripts.
|
|
220
|
+
|
|
221
|
+
## What does not compile
|
|
222
|
+
|
|
223
|
+
Each is a `@ts-expect-error` case in this package's type tests.
|
|
224
|
+
|
|
225
|
+
- A collection wired under a name the driver's `Db` already has
|
|
226
|
+
(`command`, `watch`, `collection`, …): it would be unreachable.
|
|
227
|
+
- `db.usrs`, or a field no schema has in a `create`.
|
|
228
|
+
- `kit.db` when the kit holds several databases, `kit.databases.nowhere`,
|
|
229
|
+
or `{ on: 'nowhere' }`.
|
|
230
|
+
- `optionsFor` under a key no collection is wired under.
|
|
231
|
+
- `session`, `db`, `actor` or `autoSync` under `options`. Under
|
|
232
|
+
`optionsFor`, the same four are refused by `defineConfig` instead.
|
|
233
|
+
- `as` with an actor of the wrong type, and `as` at all when the
|
|
234
|
+
collections stamp none or disagree.
|
|
235
|
+
|
|
236
|
+
## Traps
|
|
237
|
+
|
|
238
|
+
- **A key the driver's `Db` has is refused twice**: by the types where the
|
|
239
|
+
config is written, and by `createKit` against the object itself — which is
|
|
240
|
+
what catches a member a later driver release adds.
|
|
241
|
+
- **The scope is a `Db` underneath.** `Object.keys(kit.db)` lists the
|
|
242
|
+
collections, not the driver's members; a driver method read off it is
|
|
243
|
+
bound, so `const { command } = kit.db` works.
|
|
244
|
+
- **`createKit` connects.** `defineConfig` does not, so a wrong URI throws
|
|
245
|
+
where the kit is created, and a database that fails gives back every
|
|
246
|
+
connection opened before it.
|
|
247
|
+
- **A client the config gave is never closed**, including by `await using`.
|
|
248
|
+
Close it where it was opened.
|
|
249
|
+
- **`autoSync` is for tests and development.** In production `sync()` is a
|
|
250
|
+
deployment step: it needs `dbAdmin`, and an index build is not in a
|
|
251
|
+
transaction.
|
|
252
|
+
- **A kit from `as` or `withSession` cannot be closed**, and `close()` on it
|
|
253
|
+
throws: the clients are the root kit's.
|
|
254
|
+
- **`discoverCollections` runs under Bun**, has no types, and does not
|
|
255
|
+
survive bundling. It is for scripts run from the repository; a Node script
|
|
256
|
+
calling it gets `Bun is not defined`.
|
|
257
|
+
- **Two collections that stamp actors of different types** leave `as`
|
|
258
|
+
uncallable: one call could not stamp both.
|
|
259
|
+
- **`{ on }` is required at run time, not by the types**, and cannot be: two
|
|
260
|
+
databases on one URI share a client and need none, so what decides is the
|
|
261
|
+
number of *clients*. Without it, a kit holding two throws.
|
|
262
|
+
- **A transaction body may run twice.** The driver retries it from the start
|
|
263
|
+
on a transient error, so it must hold nothing that MongoDB would not roll
|
|
264
|
+
back.
|
|
265
|
+
- **A transaction reaches one client's databases.** With `{ on: 'main' }`,
|
|
266
|
+
an operation on a database of another client carries a session that client
|
|
267
|
+
does not own, and the driver refuses it.
|
|
268
|
+
- **`kit.db` throws on a kit with several databases**, where its type is
|
|
269
|
+
already `never`: the message names the databases to read instead.
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AnyCollectionDefinition } from '@nxgt/mongo';
|
|
2
|
+
import type { DatabaseConfig } from './types';
|
|
3
|
+
/** A definition, told by its shape: `instanceof` has no class to ask. */
|
|
4
|
+
export declare function isDefinition(value: unknown): value is AnyCollectionDefinition;
|
|
5
|
+
/** The definitions of a module object, under the keys they are exported by. */
|
|
6
|
+
export declare function definitionsOf(collections: object): [string, AnyCollectionDefinition][];
|
|
7
|
+
/** Everything one database's config must answer before anything connects. */
|
|
8
|
+
export declare function checkDatabase(name: string, config: DatabaseConfig<object>): [string, AnyCollectionDefinition][];
|
|
9
|
+
//# sourceMappingURL=checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/config/checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,uBAAuB,CAU7E;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAC5B,WAAW,EAAE,MAAM,GACjB,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAAE,CAKrC;AA6BD,6EAA6E;AAC7E,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,GAC5B,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAAE,CA8DrC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Checked, KitConfig, KitConfigInput } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The configuration of an application's MongoDB, checked once and frozen.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import * as collections from './models';
|
|
7
|
+
*
|
|
8
|
+
* export const config = defineConfig({
|
|
9
|
+
* uri: process.env.MONGO_URI!,
|
|
10
|
+
* collections,
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Several databases name themselves:
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* defineConfig({
|
|
18
|
+
* databases: {
|
|
19
|
+
* main: { uri: process.env.MONGO_URI!, collections },
|
|
20
|
+
* analytics: { uri: process.env.ANALYTICS_URI!, collections: events },
|
|
21
|
+
* },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* It connects to nothing and reads no environment variable: what is wrong
|
|
26
|
+
* with the configuration throws here, where the application starts, and the
|
|
27
|
+
* variables are the application's to read.
|
|
28
|
+
*/
|
|
29
|
+
export declare function defineConfig<const C extends KitConfigInput>(config: C & Checked<C>): KitConfig<C>;
|
|
30
|
+
//# sourceMappingURL=define-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-config.d.ts","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,OAAO,EAEP,SAAS,EACT,cAAc,EACd,MAAM,SAAS,CAAC;AAuBjB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,CAAC,SAAS,cAAc,EAC1D,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACpB,SAAS,CAAC,CAAC,CAAC,CAQd"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { AnyCollectionDefinition, CollectionOptions } from '@nxgt/mongo';
|
|
2
|
+
import type { Db, MongoClient, MongoClientOptions } from 'mongodb';
|
|
3
|
+
/**
|
|
4
|
+
* The collections of a module, as `import * as collections` gives them: its
|
|
5
|
+
* other exports — types are gone already, and a function or a constant is no
|
|
6
|
+
* definition — are left out of the scope rather than refused.
|
|
7
|
+
*/
|
|
8
|
+
export type CollectionsOf<C> = {
|
|
9
|
+
[K in keyof C as C[K] extends AnyCollectionDefinition ? K : never]: C[K];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* A name the driver's `Db` already uses. The scope carries the collections
|
|
13
|
+
* over a `Db`, so a collection under one of these names would be unreachable
|
|
14
|
+
* — and `db.watch` would answer something other than what a caller expects.
|
|
15
|
+
*
|
|
16
|
+
* Read from the driver's own type, never from a list of ours: a member the
|
|
17
|
+
* driver adds is covered the day the pin moves. At run time the same question
|
|
18
|
+
* is asked of the object itself, with `in`.
|
|
19
|
+
*/
|
|
20
|
+
export type ReservedName = keyof Db;
|
|
21
|
+
/** The keys of `C` that a `Db` already answers to. */
|
|
22
|
+
export type Collides<C> = Extract<keyof CollectionsOf<C>, ReservedName>;
|
|
23
|
+
/**
|
|
24
|
+
* Makes a colliding key unassignable, and says why where the developer is
|
|
25
|
+
* looking: under that key, the value would have to be a string no definition
|
|
26
|
+
* is.
|
|
27
|
+
*/
|
|
28
|
+
export type NoCollision<C> = [Collides<C>] extends [never] ? unknown : {
|
|
29
|
+
[K in Collides<C>]: `"${K & string}" is a member of the driver's Db: wire this collection under another key`;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Makes options written for a key no collection is wired under unassignable,
|
|
33
|
+
* and says so under that key — the same shape as `NoCollision`.
|
|
34
|
+
*/
|
|
35
|
+
export type Unwired<Cols, OF> = [
|
|
36
|
+
Exclude<keyof OF, keyof CollectionsOf<Cols>>
|
|
37
|
+
] extends [never] ? unknown : {
|
|
38
|
+
[K in Exclude<keyof OF, keyof CollectionsOf<Cols>>]: `"${K & string}" is not wired by this database: there are no options for it`;
|
|
39
|
+
};
|
|
40
|
+
/** The options of one collection, minus what the kit decides itself. */
|
|
41
|
+
export type KitCollectionOptions<Def> = Omit<CollectionOptions<Def>, 'db' | 'session' | 'actor' | 'autoSync'>;
|
|
42
|
+
/** One database: where it is, and what it holds. */
|
|
43
|
+
export interface DatabaseConfig<C> {
|
|
44
|
+
/**
|
|
45
|
+
* Where to connect. One of `uri` and `client`, never both. Databases on
|
|
46
|
+
* one URI share a client, which the kit closes with its last holder.
|
|
47
|
+
*/
|
|
48
|
+
uri?: string;
|
|
49
|
+
/**
|
|
50
|
+
* A client the application opened. The kit uses it and **never closes
|
|
51
|
+
* it**: what it did not open is not its to close.
|
|
52
|
+
*/
|
|
53
|
+
client?: MongoClient;
|
|
54
|
+
/** Passed to the driver with `uri`. Refused with `client`, which has its own. */
|
|
55
|
+
clientOptions?: MongoClientOptions;
|
|
56
|
+
/** The database's name. Default: the one the URI names, or `test`. */
|
|
57
|
+
database?: string;
|
|
58
|
+
/** `import * as collections from './models'`, passed as it is. */
|
|
59
|
+
collections: C;
|
|
60
|
+
/** For every collection of this database. */
|
|
61
|
+
options?: KitCollectionOptions<AnyCollectionDefinition>;
|
|
62
|
+
/** For one collection, merged over `options`. */
|
|
63
|
+
optionsFor?: {
|
|
64
|
+
[K in keyof CollectionsOf<C>]?: KitCollectionOptions<CollectionsOf<C>[K]>;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Sync each collection before its first operation, once per database:
|
|
68
|
+
* `@nxgt/mongo`'s `autoSync`. For tests and development, never for
|
|
69
|
+
* production, where `sync()` is a deployment step.
|
|
70
|
+
*/
|
|
71
|
+
autoSync?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** One database, or several under their names. */
|
|
74
|
+
export type KitConfigInput = DatabaseConfig<object> | {
|
|
75
|
+
databases: Record<string, DatabaseConfig<object>>;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* The config with every key it has to refuse — one the driver's `Db` already
|
|
79
|
+
* answers to, or options for a collection that is not wired — turned into the
|
|
80
|
+
* message above. `defineConfig` takes its argument as `C & Checked<C>`, and a
|
|
81
|
+
* constraint written that way is what makes the refusal land on the key the
|
|
82
|
+
* application wrote, rather than on the whole object.
|
|
83
|
+
*/
|
|
84
|
+
export type Checked<C> = C extends {
|
|
85
|
+
databases: infer D;
|
|
86
|
+
} ? {
|
|
87
|
+
databases: {
|
|
88
|
+
[N in keyof D]: CheckedDatabase<D[N]>;
|
|
89
|
+
};
|
|
90
|
+
} : CheckedDatabase<C>;
|
|
91
|
+
/** One database's collections, and the options written for them. */
|
|
92
|
+
type CheckedDatabase<D> = D extends {
|
|
93
|
+
collections: infer Cols;
|
|
94
|
+
} ? {
|
|
95
|
+
collections: Cols & NoCollision<Cols>;
|
|
96
|
+
} & (D extends {
|
|
97
|
+
optionsFor: infer OF;
|
|
98
|
+
} ? {
|
|
99
|
+
optionsFor: OF & Unwired<Cols, OF>;
|
|
100
|
+
} : unknown) : D;
|
|
101
|
+
/** The databases of a config, whichever of the two shapes it was written in. */
|
|
102
|
+
export type DatabasesOf<C> = C extends {
|
|
103
|
+
databases: infer D;
|
|
104
|
+
} ? D : {
|
|
105
|
+
default: C;
|
|
106
|
+
};
|
|
107
|
+
/** The name of every database. */
|
|
108
|
+
export type DbName<C> = keyof DatabasesOf<C> & string;
|
|
109
|
+
/** The collections of one database, as they were passed. */
|
|
110
|
+
export type CollectionsIn<C, N extends DbName<C>> = DatabasesOf<C>[N] extends {
|
|
111
|
+
collections: infer Cols;
|
|
112
|
+
} ? Cols : never;
|
|
113
|
+
/**
|
|
114
|
+
* What `defineConfig` gives back: the databases under their names, checked
|
|
115
|
+
* and frozen, carrying the shape it was written in — which is what decides
|
|
116
|
+
* whether the kit has a `db` of its own.
|
|
117
|
+
*/
|
|
118
|
+
export interface KitConfig<C> {
|
|
119
|
+
readonly databases: {
|
|
120
|
+
readonly [N in DbName<C>]: DatabaseConfig<CollectionsIn<C, N>>;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
export {};
|
|
124
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AnyCollectionDefinition } from '@nxgt/mongo';
|
|
2
|
+
/** What to scan, and what to read in each file it finds. */
|
|
3
|
+
export interface DiscoverOptions {
|
|
4
|
+
/** A glob, relative to `cwd`: `'src/models/*.model.ts'`. */
|
|
5
|
+
glob: string;
|
|
6
|
+
/** Where the glob starts. Default: the process's working directory. */
|
|
7
|
+
cwd?: string;
|
|
8
|
+
/**
|
|
9
|
+
* The export to read in each file. Default: every export that is a
|
|
10
|
+
* definition, which is what `import * as collections` gives.
|
|
11
|
+
*/
|
|
12
|
+
export?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The definitions of the files a glob matches, read at run time.
|
|
16
|
+
*
|
|
17
|
+
* For scripts — a sync or a migration run from the repository — and for
|
|
18
|
+
* nothing else: a glob is read from the file system, so it finds nothing
|
|
19
|
+
* once the application is bundled, and it produces **no types**. An
|
|
20
|
+
* application wires its collections with `import * as collections from
|
|
21
|
+
* './models'`, which a bundler follows and the compiler sees.
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { connectMongo, syncCollections } from '@nxgt/mongo';
|
|
25
|
+
* import { discoverCollections } from '@nxgt/mongo-kit';
|
|
26
|
+
*
|
|
27
|
+
* const mongo = await connectMongo(process.env.MONGO_URI!);
|
|
28
|
+
* const definitions = await discoverCollections({ glob: 'src/**\/*.model.ts' });
|
|
29
|
+
* await syncCollections(mongo.db, definitions);
|
|
30
|
+
* await mongo.close();
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* The files are imported, so their top level runs, and the glob is
|
|
34
|
+
* `Bun.Glob`: this one function needs the Bun runtime.
|
|
35
|
+
*/
|
|
36
|
+
export declare function discoverCollections(options: DiscoverOptions): Promise<AnyCollectionDefinition[]>;
|
|
37
|
+
//# sourceMappingURL=discover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAG3D,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC/B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAiCpC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { defineConfig } from './config/define-config';
|
|
2
|
+
export type { CollectionsIn, CollectionsOf, DatabaseConfig, DbName, KitCollectionOptions, KitConfig, KitConfigInput, NoCollision, ReservedName, Unwired, } from './config/types';
|
|
3
|
+
export { type DiscoverOptions, discoverCollections } from './discover';
|
|
4
|
+
export { createKit } from './kit/create-kit';
|
|
5
|
+
export type { DbScope, KitActor, KitOf, KitTransactionOptions, MongoKit, SoleScope, } from './kit/types';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,YAAY,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,MAAM,EACN,oBAAoB,EACpB,SAAS,EACT,cAAc,EACd,WAAW,EACX,YAAY,EACZ,OAAO,GACP,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,KAAK,eAAe,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,YAAY,EACX,OAAO,EACP,QAAQ,EACR,KAAK,EACL,qBAAqB,EACrB,QAAQ,EACR,SAAS,GACT,MAAM,aAAa,CAAC"}
|