@nxgt/mongo-kit 0.1.5 → 0.2.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 +41 -4
- package/dist/config/checks.d.ts.map +1 -1
- package/dist/config/define-config.d.ts.map +1 -1
- package/dist/discover.d.ts.map +1 -1
- package/dist/errors/kit-error.d.ts +41 -0
- package/dist/errors/kit-error.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -30
- package/dist/index.js.map +11 -10
- package/dist/kit/context.d.ts.map +1 -1
- package/dist/kit/create-kit.d.ts.map +1 -1
- package/dist/kit/derive.d.ts.map +1 -1
- package/dist/kit/transaction.d.ts.map +1 -1
- package/docs/README.md +1 -0
- package/docs/guide/actor-and-transactions.md +2 -2
- package/docs/guide/configuration.md +4 -3
- package/docs/guide/db-scope.md +3 -2
- package/docs/guide/discover-collections.md +3 -1
- package/docs/guide/errors.md +198 -0
- package/docs/roadmap.md +6 -0
- package/docs/troubleshooting.md +42 -12
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -218,6 +218,40 @@ definition under it; without `export`, every export that is a definition is
|
|
|
218
218
|
taken. Two files defining the same server collection are refused. It needs
|
|
219
219
|
the Bun runtime, and it is for scripts.
|
|
220
220
|
|
|
221
|
+
## Errors
|
|
222
|
+
|
|
223
|
+
`KitError` is what this package refuses: a configuration, a name or a call
|
|
224
|
+
that cannot work. It carries a `code`, and the `database` and `key` it is
|
|
225
|
+
about — never a URI, which may hold a password.
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
import { KitError } from '@nxgt/mongo-kit';
|
|
229
|
+
|
|
230
|
+
if (error instanceof KitError && error.code === 'CONFIG') {
|
|
231
|
+
console.error(`mongo: "${error.database}" is misconfigured`, error.message);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
| `KitErrorCode` | |
|
|
236
|
+
| --- | --- |
|
|
237
|
+
| `CONFIG` | `defineConfig` refused the configuration |
|
|
238
|
+
| `COLLISION` | a collection is wired under a name the driver's `Db` has |
|
|
239
|
+
| `NO_DATABASE` | `transaction(fn, { on })` named a database this kit does not hold |
|
|
240
|
+
| `SEVERAL_DATABASES` | `kit.db` was read on a kit that holds more than one |
|
|
241
|
+
| `TRANSACTION` | no client named where one is needed, or `{ on }` inside a session |
|
|
242
|
+
| `DERIVED` | `close()` on a kit `as`, `withSession` or a transaction derived |
|
|
243
|
+
| `DISCOVERY` | `discoverCollections` could not make a set of definitions |
|
|
244
|
+
|
|
245
|
+
It extends **`TypeError`**, not `Error`: each of these is a call or a
|
|
246
|
+
configuration written wrong, and this package threw bare `TypeError`s before
|
|
247
|
+
the class existed, so a `catch` that tests for `TypeError` still matches.
|
|
248
|
+
|
|
249
|
+
The collections are `@nxgt/mongo`'s, so what a *query* throws is its
|
|
250
|
+
`DataError` and its subclasses, unchanged. MongoDB's own refusal to connect
|
|
251
|
+
reaches the caller from `createKit` as the driver's error. Every code, with
|
|
252
|
+
the call that raises it, is in
|
|
253
|
+
[docs/guide/errors.md](docs/guide/errors.md).
|
|
254
|
+
|
|
221
255
|
## What does not compile
|
|
222
256
|
|
|
223
257
|
Each is a `@ts-expect-error` case in this package's type tests.
|
|
@@ -250,7 +284,7 @@ Each is a `@ts-expect-error` case in this package's type tests.
|
|
|
250
284
|
deployment step: it needs `dbAdmin`, and an index build is not in a
|
|
251
285
|
transaction.
|
|
252
286
|
- **A kit from `as` or `withSession` cannot be closed**, and `close()` on it
|
|
253
|
-
throws
|
|
287
|
+
throws `KitError` with the code `DERIVED`: the clients are the root kit's.
|
|
254
288
|
- **`discoverCollections` runs under Bun**, has no types, and does not
|
|
255
289
|
survive bundling. It is for scripts run from the repository; a Node script
|
|
256
290
|
calling it gets `Bun is not defined`.
|
|
@@ -258,15 +292,16 @@ Each is a `@ts-expect-error` case in this package's type tests.
|
|
|
258
292
|
uncallable: one call could not stamp both.
|
|
259
293
|
- **`{ on }` is required at run time, not by the types**, and cannot be: two
|
|
260
294
|
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
|
|
295
|
+
number of *clients*. Without it, a kit holding two throws `TRANSACTION`.
|
|
262
296
|
- **A transaction body may run twice.** The driver retries it from the start
|
|
263
297
|
on a transient error, so it must hold nothing that MongoDB would not roll
|
|
264
298
|
back.
|
|
265
299
|
- **A transaction reaches one client's databases.** With `{ on: 'main' }`,
|
|
266
300
|
an operation on a database of another client carries a session that client
|
|
267
301
|
does not own, and the driver refuses it.
|
|
268
|
-
- **`kit.db` throws on a kit with several databases**,
|
|
269
|
-
already `never`: the message names the databases to read
|
|
302
|
+
- **`kit.db` throws `SEVERAL_DATABASES` on a kit with several databases**,
|
|
303
|
+
where its type is already `never`: the message names the databases to read
|
|
304
|
+
instead.
|
|
270
305
|
|
|
271
306
|
## Documentation
|
|
272
307
|
|
|
@@ -278,6 +313,8 @@ Each is a `@ts-expect-error` case in this package's type tests.
|
|
|
278
313
|
- [The actor, sessions and transactions](docs/guide/actor-and-transactions.md)
|
|
279
314
|
— `as`, `withSession` and `transaction`.
|
|
280
315
|
- [Syncing](docs/guide/sync.md) — the deployment step, and `dryRun`.
|
|
316
|
+
- [Errors](docs/guide/errors.md) — `KitError`, its codes, and what each one
|
|
317
|
+
is thrown by.
|
|
281
318
|
- [`discoverCollections`](docs/guide/discover-collections.md) — definitions
|
|
282
319
|
from a glob, for scripts.
|
|
283
320
|
- [Troubleshooting](docs/troubleshooting.md) — the errors, by their message.
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/config/checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,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;AAsCD,6EAA6E;AAC7E,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,GAC5B,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAAE,CA6DrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-config.d.ts","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"define-config.d.ts","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,OAAO,EAEP,SAAS,EACT,cAAc,EACd,MAAM,SAAS,CAAC;AAmCjB;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
|
package/dist/discover.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAI3D,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,CAqCpC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** What the kit refused, as a string a caller can switch on. */
|
|
2
|
+
export type KitErrorCode =
|
|
3
|
+
/** The configuration object itself is wrong, and nothing connected. */
|
|
4
|
+
'CONFIG'
|
|
5
|
+
/** A collection is wired under a name that is a member of the driver's `Db`. */
|
|
6
|
+
| 'COLLISION'
|
|
7
|
+
/** A database was read by a name this kit does not hold. */
|
|
8
|
+
| 'NO_DATABASE'
|
|
9
|
+
/** `kit.db` was read on a kit that holds more than one database. */
|
|
10
|
+
| 'SEVERAL_DATABASES'
|
|
11
|
+
/** A transaction that cannot be opened: no client named, or already in one. */
|
|
12
|
+
| 'TRANSACTION'
|
|
13
|
+
/** `close()` on a kit that `as`, `withSession` or a transaction derived. */
|
|
14
|
+
| 'DERIVED'
|
|
15
|
+
/** `discoverCollections` could not make a set of definitions from a glob. */
|
|
16
|
+
| 'DISCOVERY';
|
|
17
|
+
export interface KitErrorOptions {
|
|
18
|
+
/** The database it is about, when one is named. */
|
|
19
|
+
database?: string | undefined;
|
|
20
|
+
/** The config key, the collection key or the path it is about. */
|
|
21
|
+
key?: string | undefined;
|
|
22
|
+
cause?: unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* What this package refuses, with a code beside the sentence.
|
|
26
|
+
*
|
|
27
|
+
* It extends `TypeError` rather than `Error`, unlike `DataError`,
|
|
28
|
+
* `RedisError` and `S3Error`: every one of these is a call or a
|
|
29
|
+
* configuration written wrong, which is what `TypeError` means, and this
|
|
30
|
+
* package threw bare `TypeError`s before it existed. Extending one keeps
|
|
31
|
+
* every `catch` that tests for `TypeError` working, and adds a `code` to
|
|
32
|
+
* switch on instead of matching the message text.
|
|
33
|
+
*/
|
|
34
|
+
export declare class KitError extends TypeError {
|
|
35
|
+
name: string;
|
|
36
|
+
readonly code: KitErrorCode;
|
|
37
|
+
readonly database: string | undefined;
|
|
38
|
+
readonly key: string | undefined;
|
|
39
|
+
constructor(code: KitErrorCode, message: string, options?: KitErrorOptions);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=kit-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kit-error.d.ts","sourceRoot":"","sources":["../../src/errors/kit-error.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,MAAM,MAAM,YAAY;AACvB,uEAAuE;AACrE,QAAQ;AACV,gFAAgF;GAC9E,WAAW;AACb,4DAA4D;GAC1D,aAAa;AACf,oEAAoE;GAClE,mBAAmB;AACrB,+EAA+E;GAC7E,aAAa;AACf,4EAA4E;GAC1E,SAAS;AACX,6EAA6E;GAC3E,WAAW,CAAC;AAEf,MAAM,WAAW,eAAe;IAC/B,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,qBAAa,QAAS,SAAQ,SAAS;IAC7B,IAAI,SAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;gBAErB,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe;CAM1E"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { defineConfig } from './config/define-config';
|
|
2
2
|
export type { CollectionsIn, CollectionsOf, DatabaseConfig, DbName, KitCollectionOptions, KitConfig, KitConfigInput, NoCollision, ReservedName, Unwired, } from './config/types';
|
|
3
3
|
export { type DiscoverOptions, discoverCollections } from './discover';
|
|
4
|
+
export { KitError, type KitErrorCode, type KitErrorOptions, } from './errors/kit-error';
|
|
4
5
|
export { createKit } from './kit/create-kit';
|
|
5
6
|
export type { DbScope, KitActor, KitOf, KitTransactionOptions, MongoKit, SoleScope, } from './kit/types';
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
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,EACN,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,eAAe,GACpB,MAAM,oBAAoB,CAAC;AAC5B,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"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
// src/errors/kit-error.ts
|
|
2
|
+
class KitError extends TypeError {
|
|
3
|
+
constructor(code, message, options) {
|
|
4
|
+
super(message, { cause: options?.cause });
|
|
5
|
+
this.name = "KitError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.database = options?.database;
|
|
8
|
+
this.key = options?.key;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
// src/config/checks.ts
|
|
2
13
|
function isDefinition(value) {
|
|
3
14
|
if (typeof value !== "object" || value === null)
|
|
@@ -8,65 +19,67 @@ function isDefinition(value) {
|
|
|
8
19
|
function definitionsOf(collections) {
|
|
9
20
|
return Object.entries(collections).filter((entry) => isDefinition(entry[1]));
|
|
10
21
|
}
|
|
11
|
-
var refuse = (
|
|
12
|
-
throw new
|
|
22
|
+
var refuse = (name, said, key) => {
|
|
23
|
+
throw new KitError("CONFIG", `defineConfig: database "${name}" ${said}`, {
|
|
24
|
+
database: name,
|
|
25
|
+
key
|
|
26
|
+
});
|
|
13
27
|
};
|
|
14
28
|
var OWNED = ["db", "session", "actor", "autoSync"];
|
|
15
|
-
function checkOwned(
|
|
29
|
+
function checkOwned(name, what, options, forKey) {
|
|
16
30
|
if (typeof options !== "object" || options === null)
|
|
17
31
|
return;
|
|
18
32
|
for (const key of OWNED) {
|
|
19
33
|
if (key in options) {
|
|
20
|
-
refuse(
|
|
34
|
+
refuse(name, `has "${key}" in ${what}, which the kit decides: ` + "a database is named by its key, `as` and `withSession` carry the " + "actor and the session, and `autoSync` is the database's", forKey);
|
|
21
35
|
}
|
|
22
36
|
}
|
|
23
37
|
}
|
|
24
38
|
function checkDatabase(name, config) {
|
|
25
|
-
const where = `database "${name}"`;
|
|
26
39
|
if (typeof config !== "object" || config === null) {
|
|
27
|
-
refuse(
|
|
40
|
+
refuse(name, "is not a configuration object");
|
|
28
41
|
}
|
|
29
42
|
const hasUri = config.uri !== undefined;
|
|
30
43
|
const hasClient = config.client !== undefined;
|
|
31
44
|
if (hasUri === hasClient) {
|
|
32
|
-
refuse(
|
|
45
|
+
refuse(name, hasUri ? "has both a uri and a client: pass the one it should use" : "has neither a uri nor a client");
|
|
33
46
|
}
|
|
34
47
|
if (hasUri && (typeof config.uri !== "string" || config.uri === "")) {
|
|
35
|
-
refuse(
|
|
48
|
+
refuse(name, "has a uri that is not a string");
|
|
36
49
|
}
|
|
37
50
|
if (hasClient && typeof config.client?.db !== "function") {
|
|
38
|
-
refuse(
|
|
51
|
+
refuse(name, "has a client that is not a MongoClient");
|
|
39
52
|
}
|
|
40
53
|
if (hasClient && config.clientOptions !== undefined) {
|
|
41
|
-
refuse(
|
|
54
|
+
refuse(name, "has client options beside a client it did not open: pass them where the client is made");
|
|
42
55
|
}
|
|
43
56
|
if (config.database !== undefined && config.database === "") {
|
|
44
|
-
refuse(
|
|
57
|
+
refuse(name, "has an empty database name");
|
|
45
58
|
}
|
|
46
59
|
if (typeof config.collections !== "object" || config.collections === null) {
|
|
47
|
-
refuse(
|
|
60
|
+
refuse(name, "has no collections object");
|
|
48
61
|
}
|
|
49
62
|
const definitions = definitionsOf(config.collections);
|
|
50
63
|
if (definitions.length === 0) {
|
|
51
|
-
refuse(
|
|
64
|
+
refuse(name, "has a collections object with no definition in it: pass the module, as in `import * as collections`");
|
|
52
65
|
}
|
|
53
66
|
const byName = new Map;
|
|
54
67
|
for (const [key, definition] of definitions) {
|
|
55
68
|
const seen = byName.get(definition.name);
|
|
56
69
|
if (seen !== undefined) {
|
|
57
|
-
refuse(
|
|
70
|
+
refuse(name, `wires "${seen}" and "${key}" to the same collection, "${definition.name}"`);
|
|
58
71
|
}
|
|
59
72
|
byName.set(definition.name, key);
|
|
60
73
|
}
|
|
61
74
|
const keys = new Set(definitions.map(([key]) => key));
|
|
62
75
|
for (const key of Object.keys(config.optionsFor ?? {})) {
|
|
63
76
|
if (!keys.has(key)) {
|
|
64
|
-
refuse(
|
|
77
|
+
refuse(name, `has options for "${key}", which it does not wire`, key);
|
|
65
78
|
}
|
|
66
79
|
}
|
|
67
|
-
checkOwned(
|
|
80
|
+
checkOwned(name, "options", config.options);
|
|
68
81
|
for (const [key, options] of Object.entries(config.optionsFor ?? {})) {
|
|
69
|
-
checkOwned(
|
|
82
|
+
checkOwned(name, `the options of "${key}"`, options, key);
|
|
70
83
|
}
|
|
71
84
|
return definitions;
|
|
72
85
|
}
|
|
@@ -74,18 +87,18 @@ function checkDatabase(name, config) {
|
|
|
74
87
|
// src/config/define-config.ts
|
|
75
88
|
function databasesOf(config) {
|
|
76
89
|
if (typeof config !== "object" || config === null) {
|
|
77
|
-
throw new
|
|
90
|
+
throw new KitError("CONFIG", "defineConfig: a configuration object is required");
|
|
78
91
|
}
|
|
79
92
|
if (!("databases" in config)) {
|
|
80
93
|
return { default: config };
|
|
81
94
|
}
|
|
82
95
|
const { databases } = config;
|
|
83
96
|
if (typeof databases !== "object" || databases === null) {
|
|
84
|
-
throw new
|
|
97
|
+
throw new KitError("CONFIG", "defineConfig: databases must be an object of databases by name, " + "as `{ databases: { main: … } }`. One database is the " + "configuration itself, and names itself with `database`.");
|
|
85
98
|
}
|
|
86
99
|
const names = Object.keys(databases);
|
|
87
100
|
if (names.length === 0) {
|
|
88
|
-
throw new
|
|
101
|
+
throw new KitError("CONFIG", "defineConfig: databases names none. Give it at least one, " + "as `{ databases: { main: … } }`.");
|
|
89
102
|
}
|
|
90
103
|
return databases;
|
|
91
104
|
}
|
|
@@ -102,7 +115,7 @@ function defineConfig(config) {
|
|
|
102
115
|
async function discoverCollections(options) {
|
|
103
116
|
const { glob, cwd = process.cwd(), export: name } = options;
|
|
104
117
|
if (typeof glob !== "string" || glob === "") {
|
|
105
|
-
throw new
|
|
118
|
+
throw new KitError("DISCOVERY", "discoverCollections: a glob is required");
|
|
106
119
|
}
|
|
107
120
|
const paths = await Array.fromAsync(new Bun.Glob(glob).scan({ cwd }));
|
|
108
121
|
const found = [];
|
|
@@ -111,12 +124,12 @@ async function discoverCollections(options) {
|
|
|
111
124
|
const module = await import(`${cwd}/${path}`);
|
|
112
125
|
const definitions = name === undefined ? definitionsOf(module) : isDefinition(module[name]) ? [[name, module[name]]] : [];
|
|
113
126
|
if (name !== undefined && definitions.length === 0) {
|
|
114
|
-
throw new
|
|
127
|
+
throw new KitError("DISCOVERY", `discoverCollections: ${path} exports no definition named "${name}"`, { key: path });
|
|
115
128
|
}
|
|
116
129
|
for (const [, definition] of definitions) {
|
|
117
130
|
const seen = byName.get(definition.name);
|
|
118
131
|
if (seen !== undefined && seen !== path) {
|
|
119
|
-
throw new
|
|
132
|
+
throw new KitError("DISCOVERY", `discoverCollections: ${seen} and ${path} both define the collection "${definition.name}"`, { key: path });
|
|
120
133
|
}
|
|
121
134
|
byName.set(definition.name, path);
|
|
122
135
|
found.push(definition);
|
|
@@ -140,7 +153,7 @@ function derived(ctx, change) {
|
|
|
140
153
|
function databaseOf(ctx, name) {
|
|
141
154
|
const found = ctx.databases.find((database) => database.name === name);
|
|
142
155
|
if (!found) {
|
|
143
|
-
throw new
|
|
156
|
+
throw new KitError("NO_DATABASE", `This kit has no database "${name}": it has ${ctx.databases.map((database) => `"${database.name}"`).join(", ")}.`, { database: name });
|
|
144
157
|
}
|
|
145
158
|
return found;
|
|
146
159
|
}
|
|
@@ -208,13 +221,13 @@ function clientFor(ctx, on) {
|
|
|
208
221
|
const [only] = clients;
|
|
209
222
|
if (clients.size === 1 && only)
|
|
210
223
|
return only;
|
|
211
|
-
throw new
|
|
224
|
+
throw new KitError("TRANSACTION", "transaction: this kit holds more than one client, and a transaction " + "lives on one. Name the database it runs on, as `{ on: 'main' }`.");
|
|
212
225
|
}
|
|
213
226
|
function hostFor(ctx, on) {
|
|
214
227
|
if (!ctx.session)
|
|
215
228
|
return clientFor(ctx, on);
|
|
216
229
|
if (on !== undefined) {
|
|
217
|
-
throw new
|
|
230
|
+
throw new KitError("TRANSACTION", "transaction: this kit is already in a session, which this call " + "joins, so `on` has no client left to choose.");
|
|
218
231
|
}
|
|
219
232
|
return ctx.session;
|
|
220
233
|
}
|
|
@@ -228,7 +241,7 @@ async function transact(ctx, build, fn, options) {
|
|
|
228
241
|
// src/kit/derive.ts
|
|
229
242
|
async function closeKit(ctx) {
|
|
230
243
|
if (!ctx.root) {
|
|
231
|
-
throw new
|
|
244
|
+
throw new KitError("DERIVED", "close: this kit came from `as`, `withSession` or a transaction. " + "Close the kit `createKit` returned — the clients are shared.");
|
|
232
245
|
}
|
|
233
246
|
for (const database of ctx.databases) {
|
|
234
247
|
await database.connection?.close();
|
|
@@ -260,7 +273,7 @@ function kitOf(ctx) {
|
|
|
260
273
|
get db() {
|
|
261
274
|
const [only] = ctx.databases;
|
|
262
275
|
if (ctx.databases.length !== 1 || !only) {
|
|
263
|
-
throw new
|
|
276
|
+
throw new KitError("SEVERAL_DATABASES", "kit.db: this kit has several databases. Read the one you mean, " + `as \`kit.databases.${ctx.databases[0]?.name ?? "main"}\`.`);
|
|
264
277
|
}
|
|
265
278
|
return scopeFor(only.name);
|
|
266
279
|
},
|
|
@@ -312,7 +325,7 @@ async function open(config) {
|
|
|
312
325
|
function checkCollisions(name, db, keys) {
|
|
313
326
|
for (const key of keys) {
|
|
314
327
|
if (key in db) {
|
|
315
|
-
throw new
|
|
328
|
+
throw new KitError("COLLISION", `createKit: database "${name}" wires a collection under "${key}", ` + "which is a member of the driver's Db: it would be unreachable. " + "Export that definition under another name.", { database: name, key });
|
|
316
329
|
}
|
|
317
330
|
}
|
|
318
331
|
}
|
|
@@ -350,10 +363,11 @@ async function createKit(config) {
|
|
|
350
363
|
return kitOf(ctx);
|
|
351
364
|
}
|
|
352
365
|
export {
|
|
366
|
+
KitError,
|
|
353
367
|
createKit,
|
|
354
368
|
defineConfig,
|
|
355
369
|
discoverCollections
|
|
356
370
|
};
|
|
357
371
|
|
|
358
|
-
//# debugId=
|
|
372
|
+
//# debugId=D45C9A203331820F64756E2164756E21
|
|
359
373
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/config/checks.ts", "../src/config/define-config.ts", "../src/discover.ts", "../src/kit/create-kit.ts", "../src/kit/context.ts", "../src/kit/scope.ts", "../src/kit/sync.ts", "../src/kit/transaction.ts", "../src/kit/derive.ts"],
|
|
3
|
+
"sources": ["../src/errors/kit-error.ts", "../src/config/checks.ts", "../src/config/define-config.ts", "../src/discover.ts", "../src/kit/create-kit.ts", "../src/kit/context.ts", "../src/kit/scope.ts", "../src/kit/sync.ts", "../src/kit/transaction.ts", "../src/kit/derive.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"
|
|
6
|
-
"import {
|
|
7
|
-
"import
|
|
8
|
-
"import {
|
|
9
|
-
"import
|
|
5
|
+
"/** What the kit refused, as a string a caller can switch on. */\nexport type KitErrorCode =\n\t/** The configuration object itself is wrong, and nothing connected. */\n\t| 'CONFIG'\n\t/** A collection is wired under a name that is a member of the driver's `Db`. */\n\t| 'COLLISION'\n\t/** A database was read by a name this kit does not hold. */\n\t| 'NO_DATABASE'\n\t/** `kit.db` was read on a kit that holds more than one database. */\n\t| 'SEVERAL_DATABASES'\n\t/** A transaction that cannot be opened: no client named, or already in one. */\n\t| 'TRANSACTION'\n\t/** `close()` on a kit that `as`, `withSession` or a transaction derived. */\n\t| 'DERIVED'\n\t/** `discoverCollections` could not make a set of definitions from a glob. */\n\t| 'DISCOVERY';\n\nexport interface KitErrorOptions {\n\t/** The database it is about, when one is named. */\n\tdatabase?: string | undefined;\n\t/** The config key, the collection key or the path it is about. */\n\tkey?: string | undefined;\n\tcause?: unknown;\n}\n\n/**\n * What this package refuses, with a code beside the sentence.\n *\n * It extends `TypeError` rather than `Error`, unlike `DataError`,\n * `RedisError` and `S3Error`: every one of these is a call or a\n * configuration written wrong, which is what `TypeError` means, and this\n * package threw bare `TypeError`s before it existed. Extending one keeps\n * every `catch` that tests for `TypeError` working, and adds a `code` to\n * switch on instead of matching the message text.\n */\nexport class KitError extends TypeError {\n\toverride name = 'KitError';\n\treadonly code: KitErrorCode;\n\treadonly database: string | undefined;\n\treadonly key: string | undefined;\n\n\tconstructor(code: KitErrorCode, message: string, options?: KitErrorOptions) {\n\t\tsuper(message, { cause: options?.cause });\n\t\tthis.code = code;\n\t\tthis.database = options?.database;\n\t\tthis.key = options?.key;\n\t}\n}\n",
|
|
6
|
+
"import type { AnyCollectionDefinition } from '@nxgt/mongo';\nimport { KitError } from '../errors/kit-error';\nimport type { DatabaseConfig } from './types';\n\n/** A definition, told by its shape: `instanceof` has no class to ask. */\nexport function isDefinition(value: unknown): value is AnyCollectionDefinition {\n\tif (typeof value !== 'object' || value === null) return false;\n\tconst candidate = value as Partial<AnyCollectionDefinition>;\n\treturn (\n\t\ttypeof candidate.name === 'string' &&\n\t\ttypeof candidate.schema === 'object' &&\n\t\tcandidate.schema !== null &&\n\t\tArray.isArray(candidate.indexes) &&\n\t\ttypeof candidate.stamps === 'object'\n\t);\n}\n\n/** The definitions of a module object, under the keys they are exported by. */\nexport function definitionsOf(\n\tcollections: object,\n): [string, AnyCollectionDefinition][] {\n\treturn Object.entries(collections).filter(\n\t\t(entry): entry is [string, AnyCollectionDefinition] =>\n\t\t\tisDefinition(entry[1]),\n\t);\n}\n\nconst refuse = (name: string, said: string, key?: string): never => {\n\tthrow new KitError('CONFIG', `defineConfig: database \"${name}\" ${said}`, {\n\t\tdatabase: name,\n\t\tkey,\n\t});\n};\n\n/**\n * The collection options the kit decides itself: the database each collection\n * is on, the session and the actor a derived kit carries, and the sync the\n * database's `autoSync` asks for. The types refuse them in `options`, where\n * the shape is `KitCollectionOptions`; under `optionsFor` they are only\n * refused here, and one of them there would quietly outrank the kit.\n */\nconst OWNED = ['db', 'session', 'actor', 'autoSync'] as const;\n\nfunction checkOwned(\n\tname: string,\n\twhat: string,\n\toptions: unknown,\n\tforKey?: string,\n): void {\n\tif (typeof options !== 'object' || options === null) return;\n\tfor (const key of OWNED) {\n\t\tif (key in options) {\n\t\t\trefuse(\n\t\t\t\tname,\n\t\t\t\t`has \"${key}\" in ${what}, which the kit decides: ` +\n\t\t\t\t\t'a database is named by its key, `as` and `withSession` carry the ' +\n\t\t\t\t\t\"actor and the session, and `autoSync` is the database's\",\n\t\t\t\tforKey,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/** Everything one database's config must answer before anything connects. */\nexport function checkDatabase(\n\tname: string,\n\tconfig: DatabaseConfig<object>,\n): [string, AnyCollectionDefinition][] {\n\tif (typeof config !== 'object' || config === null) {\n\t\trefuse(name, 'is not a configuration object');\n\t}\n\tconst hasUri = config.uri !== undefined;\n\tconst hasClient = config.client !== undefined;\n\tif (hasUri === hasClient) {\n\t\trefuse(\n\t\t\tname,\n\t\t\thasUri\n\t\t\t\t? 'has both a uri and a client: pass the one it should use'\n\t\t\t\t: 'has neither a uri nor a client',\n\t\t);\n\t}\n\tif (hasUri && (typeof config.uri !== 'string' || config.uri === '')) {\n\t\trefuse(name, 'has a uri that is not a string');\n\t}\n\tif (hasClient && typeof config.client?.db !== 'function') {\n\t\trefuse(name, 'has a client that is not a MongoClient');\n\t}\n\tif (hasClient && config.clientOptions !== undefined) {\n\t\trefuse(\n\t\t\tname,\n\t\t\t'has client options beside a client it did not open: pass them where the client is made',\n\t\t);\n\t}\n\tif (config.database !== undefined && config.database === '') {\n\t\trefuse(name, 'has an empty database name');\n\t}\n\tif (typeof config.collections !== 'object' || config.collections === null) {\n\t\trefuse(name, 'has no collections object');\n\t}\n\tconst definitions = definitionsOf(config.collections);\n\tif (definitions.length === 0) {\n\t\trefuse(\n\t\t\tname,\n\t\t\t'has a collections object with no definition in it: pass the module, as in `import * as collections`',\n\t\t);\n\t}\n\tconst byName = new Map<string, string>();\n\tfor (const [key, definition] of definitions) {\n\t\tconst seen = byName.get(definition.name);\n\t\tif (seen !== undefined) {\n\t\t\trefuse(\n\t\t\t\tname,\n\t\t\t\t`wires \"${seen}\" and \"${key}\" to the same collection, \"${definition.name}\"`,\n\t\t\t);\n\t\t}\n\t\tbyName.set(definition.name, key);\n\t}\n\tconst keys = new Set(definitions.map(([key]) => key));\n\tfor (const key of Object.keys(config.optionsFor ?? {})) {\n\t\tif (!keys.has(key)) {\n\t\t\trefuse(name, `has options for \"${key}\", which it does not wire`, key);\n\t\t}\n\t}\n\tcheckOwned(name, 'options', config.options);\n\tfor (const [key, options] of Object.entries(config.optionsFor ?? {})) {\n\t\tcheckOwned(name, `the options of \"${key}\"`, options, key);\n\t}\n\treturn definitions;\n}\n",
|
|
7
|
+
"import { KitError } from '../errors/kit-error';\nimport { checkDatabase } from './checks';\nimport type {\n\tChecked,\n\tDatabaseConfig,\n\tKitConfig,\n\tKitConfigInput,\n} from './types';\n\n/** Whether the config named its databases, or is one database itself. */\nfunction databasesOf(\n\tconfig: KitConfigInput,\n): Record<string, DatabaseConfig<object>> {\n\tif (typeof config !== 'object' || config === null) {\n\t\tthrow new KitError(\n\t\t\t'CONFIG',\n\t\t\t'defineConfig: a configuration object is required',\n\t\t);\n\t}\n\tif (!('databases' in config)) {\n\t\treturn { default: config as DatabaseConfig<object> };\n\t}\n\tconst { databases } = config;\n\tif (typeof databases !== 'object' || databases === null) {\n\t\tthrow new KitError(\n\t\t\t'CONFIG',\n\t\t\t'defineConfig: databases must be an object of databases by name, ' +\n\t\t\t\t'as `{ databases: { main: … } }`. One database is the ' +\n\t\t\t\t'configuration itself, and names itself with `database`.',\n\t\t);\n\t}\n\tconst names = Object.keys(databases);\n\tif (names.length === 0) {\n\t\tthrow new KitError(\n\t\t\t'CONFIG',\n\t\t\t'defineConfig: databases names none. Give it at least one, ' +\n\t\t\t\t'as `{ databases: { main: … } }`.',\n\t\t);\n\t}\n\treturn databases as Record<string, DatabaseConfig<object>>;\n}\n\n/**\n * The configuration of an application's MongoDB, checked once and frozen.\n *\n * ```ts\n * import * as collections from './models';\n *\n * export const config = defineConfig({\n * \turi: process.env.MONGO_URI!,\n * \tcollections,\n * });\n * ```\n *\n * Several databases name themselves:\n *\n * ```ts\n * defineConfig({\n * \tdatabases: {\n * \t\tmain: { uri: process.env.MONGO_URI!, collections },\n * \t\tanalytics: { uri: process.env.ANALYTICS_URI!, collections: events },\n * \t},\n * });\n * ```\n *\n * It connects to nothing and reads no environment variable: what is wrong\n * with the configuration throws here, where the application starts, and the\n * variables are the application's to read.\n */\nexport function defineConfig<const C extends KitConfigInput>(\n\tconfig: C & Checked<C>,\n): KitConfig<C> {\n\tconst databases = databasesOf(config);\n\tfor (const [name, database] of Object.entries(databases)) {\n\t\tcheckDatabase(name, database);\n\t}\n\treturn Object.freeze({\n\t\tdatabases: Object.freeze({ ...databases }),\n\t}) as KitConfig<C>;\n}\n",
|
|
8
|
+
"import type { AnyCollectionDefinition } from '@nxgt/mongo';\nimport { definitionsOf, isDefinition } from './config/checks';\nimport { KitError } from './errors/kit-error';\n\n/** What to scan, and what to read in each file it finds. */\nexport interface DiscoverOptions {\n\t/** A glob, relative to `cwd`: `'src/models/*.model.ts'`. */\n\tglob: string;\n\t/** Where the glob starts. Default: the process's working directory. */\n\tcwd?: string;\n\t/**\n\t * The export to read in each file. Default: every export that is a\n\t * definition, which is what `import * as collections` gives.\n\t */\n\texport?: string;\n}\n\n/**\n * The definitions of the files a glob matches, read at run time.\n *\n * For scripts — a sync or a migration run from the repository — and for\n * nothing else: a glob is read from the file system, so it finds nothing\n * once the application is bundled, and it produces **no types**. An\n * application wires its collections with `import * as collections from\n * './models'`, which a bundler follows and the compiler sees.\n *\n * ```ts\n * import { connectMongo, syncCollections } from '@nxgt/mongo';\n * import { discoverCollections } from '@nxgt/mongo-kit';\n *\n * const mongo = await connectMongo(process.env.MONGO_URI!);\n * const definitions = await discoverCollections({ glob: 'src/**\\/*.model.ts' });\n * await syncCollections(mongo.db, definitions);\n * await mongo.close();\n * ```\n *\n * The files are imported, so their top level runs, and the glob is\n * `Bun.Glob`: this one function needs the Bun runtime.\n */\nexport async function discoverCollections(\n\toptions: DiscoverOptions,\n): Promise<AnyCollectionDefinition[]> {\n\tconst { glob, cwd = process.cwd(), export: name } = options;\n\tif (typeof glob !== 'string' || glob === '') {\n\t\tthrow new KitError('DISCOVERY', 'discoverCollections: a glob is required');\n\t}\n\tconst paths = await Array.fromAsync(new Bun.Glob(glob).scan({ cwd }));\n\tconst found: AnyCollectionDefinition[] = [];\n\tconst byName = new Map<string, string>();\n\tfor (const path of paths.sort()) {\n\t\tconst module = (await import(`${cwd}/${path}`)) as Record<string, unknown>;\n\t\tconst definitions =\n\t\t\tname === undefined\n\t\t\t\t? definitionsOf(module)\n\t\t\t\t: isDefinition(module[name])\n\t\t\t\t\t? ([[name, module[name]]] as [string, AnyCollectionDefinition][])\n\t\t\t\t\t: [];\n\t\tif (name !== undefined && definitions.length === 0) {\n\t\t\tthrow new KitError(\n\t\t\t\t'DISCOVERY',\n\t\t\t\t`discoverCollections: ${path} exports no definition named \"${name}\"`,\n\t\t\t\t{ key: path },\n\t\t\t);\n\t\t}\n\t\tfor (const [, definition] of definitions) {\n\t\t\tconst seen = byName.get(definition.name);\n\t\t\tif (seen !== undefined && seen !== path) {\n\t\t\t\tthrow new KitError(\n\t\t\t\t\t'DISCOVERY',\n\t\t\t\t\t`discoverCollections: ${seen} and ${path} both define the collection \"${definition.name}\"`,\n\t\t\t\t\t{ key: path },\n\t\t\t\t);\n\t\t\t}\n\t\t\tbyName.set(definition.name, path);\n\t\t\tfound.push(definition);\n\t\t}\n\t}\n\treturn found;\n}\n",
|
|
9
|
+
"import { connectMongo, type MongoConnection } from '@nxgt/mongo';\nimport type { Db } from 'mongodb';\nimport { checkDatabase } from '../config/checks';\nimport type { DatabaseConfig, KitConfig } from '../config/types';\nimport { KitError } from '../errors/kit-error';\nimport type { DatabaseContext, KitContext } from './context';\nimport { kitOf } from './derive';\nimport type { MongoKit } from './types';\n\n/** Where one database is, and whether the kit opened it itself. */\nasync function open(\n\tconfig: DatabaseConfig<object>,\n): Promise<{ db: Db; connection: MongoConnection | undefined }> {\n\tif (config.client) {\n\t\tconst client = config.client;\n\t\treturn {\n\t\t\tdb: config.database ? client.db(config.database) : client.db(),\n\t\t\tconnection: undefined,\n\t\t};\n\t}\n\tconst connection = await connectMongo(\n\t\tconfig.uri as string,\n\t\tconfig.clientOptions,\n\t);\n\treturn {\n\t\tdb: config.database ? connection.client.db(config.database) : connection.db,\n\t\tconnection,\n\t};\n}\n\n/**\n * A key the driver's `Db` already answers to would be unreachable on the\n * scope. The types refuse it where the config is written; this asks the\n * object itself, so a member the driver adds in a later release is caught\n * here rather than silently shadowed.\n */\nfunction checkCollisions(name: string, db: Db, keys: readonly string[]): void {\n\tfor (const key of keys) {\n\t\tif (key in db) {\n\t\t\tthrow new KitError(\n\t\t\t\t'COLLISION',\n\t\t\t\t`createKit: database \"${name}\" wires a collection under \"${key}\", ` +\n\t\t\t\t\t\"which is a member of the driver's Db: it would be unreachable. \" +\n\t\t\t\t\t'Export that definition under another name.',\n\t\t\t\t{ database: name, key },\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Opens what the configuration describes, and gives the application its\n * collections on their database.\n *\n * ```ts\n * await using kit = await createKit(config);\n * const user = await kit.db.users.create({ email: 'ada@example.com' });\n * ```\n *\n * A database with a `uri` takes a hold on the client `connectMongo` shares\n * for that URI, and `close()` gives it back; a database given a `client` uses\n * it and never closes it. Nothing is built ahead of the connections: a\n * collection is built the first time it is read.\n */\nexport async function createKit<C>(config: KitConfig<C>): Promise<MongoKit<C>> {\n\tconst entries = Object.entries(config.databases) as [\n\t\tstring,\n\t\tDatabaseConfig<object>,\n\t][];\n\tconst databases: DatabaseContext[] = [];\n\ttry {\n\t\tfor (const [name, database] of entries) {\n\t\t\tconst wired = checkDatabase(name, database);\n\t\t\tconst { db, connection } = await open(database);\n\t\t\tdatabases.push({\n\t\t\t\tname,\n\t\t\t\tdb,\n\t\t\t\tclient: connection?.client ?? (database.client as never),\n\t\t\t\twired,\n\t\t\t\toptions: (database.options ?? {}) as never,\n\t\t\t\toptionsFor: (database.optionsFor ?? {}) as never,\n\t\t\t\tautoSync: database.autoSync === true,\n\t\t\t\tconnection,\n\t\t\t});\n\t\t\tcheckCollisions(\n\t\t\t\tname,\n\t\t\t\tdb,\n\t\t\t\twired.map(([key]) => key),\n\t\t\t);\n\t\t}\n\t} catch (error) {\n\t\t// Whatever opened before the failure is this call's to give back.\n\t\tfor (const database of databases) await database.connection?.close();\n\t\tthrow error;\n\t}\n\tconst ctx: KitContext = {\n\t\tdatabases,\n\t\tsession: undefined,\n\t\tactor: undefined,\n\t\tcache: new Map(),\n\t\troot: true,\n\t};\n\treturn kitOf<C>(ctx);\n}\n",
|
|
10
|
+
"import type { AnyCollectionDefinition, MongoConnection } from '@nxgt/mongo';\nimport type { ClientSession, Db, MongoClient } from 'mongodb';\nimport type { KitCollectionOptions } from '../config/types';\nimport { KitError } from '../errors/kit-error';\n\n/** A collection as the kit holds it: the key it is reached by, and its definition. */\nexport type Wired = readonly [key: string, definition: AnyCollectionDefinition];\n\n/** One database of a kit, resolved once: data, like `@nxgt/mongo`'s own context. */\nexport interface DatabaseContext {\n\t/** The name the config gave it, which is the key on `kit.databases`. */\n\treadonly name: string;\n\treadonly db: Db;\n\treadonly client: MongoClient;\n\treadonly wired: readonly Wired[];\n\treadonly options: KitCollectionOptions<never>;\n\treadonly optionsFor: Readonly<Record<string, KitCollectionOptions<never>>>;\n\treadonly autoSync: boolean;\n\t/**\n\t * The connection the kit opened, or `undefined` when the config gave a\n\t * client: what it did not open is not its to close.\n\t */\n\treadonly connection: MongoConnection | undefined;\n}\n\n/** What one kit works from. A derived kit shares the databases, not the cache. */\nexport interface KitContext {\n\treadonly databases: readonly DatabaseContext[];\n\treadonly session: ClientSession | undefined;\n\treadonly actor: unknown;\n\t/** The collections already built, per database name then per key. */\n\treadonly cache: Map<string, Map<string, unknown>>;\n\t/** Whether this is the kit `createKit` returned, the only one to close. */\n\treadonly root: boolean;\n}\n\n/** The same kit over another session or actor: a fresh cache, the same databases. */\nexport function derived(\n\tctx: KitContext,\n\tchange: { session?: ClientSession | undefined; actor?: unknown },\n): KitContext {\n\treturn {\n\t\tdatabases: ctx.databases,\n\t\tsession: 'session' in change ? change.session : ctx.session,\n\t\tactor: 'actor' in change ? change.actor : ctx.actor,\n\t\tcache: new Map(),\n\t\troot: false,\n\t};\n}\n\n/** The database under this name, or the one there is. */\nexport function databaseOf(ctx: KitContext, name: string): DatabaseContext {\n\tconst found = ctx.databases.find((database) => database.name === name);\n\tif (!found) {\n\t\tthrow new KitError(\n\t\t\t'NO_DATABASE',\n\t\t\t`This kit has no database \"${name}\": it has ${ctx.databases\n\t\t\t\t.map((database) => `\"${database.name}\"`)\n\t\t\t\t.join(', ')}.`,\n\t\t\t{ database: name },\n\t\t);\n\t}\n\treturn found;\n}\n",
|
|
10
11
|
"import { getCollection } from '@nxgt/mongo';\nimport type { DatabaseContext, KitContext } from './context';\n\n/**\n * The collection under `key`, built the first time it is read and kept:\n * `getCollection` caches nothing, so a scope that built them all would pay\n * for every collection on every request that derives a kit.\n */\nexport function collectionAt(\n\tctx: KitContext,\n\tdatabase: DatabaseContext,\n\tkey: string,\n\tdefinition: DatabaseContext['wired'][number][1],\n): unknown {\n\tlet built = ctx.cache.get(database.name);\n\tif (!built) {\n\t\tbuilt = new Map();\n\t\tctx.cache.set(database.name, built);\n\t}\n\tconst found = built.get(key);\n\tif (found) return found;\n\tconst collection = getCollection(database.db, definition, {\n\t\t...database.options,\n\t\t...database.optionsFor[key],\n\t\t...(database.autoSync ? { autoSync: true } : {}),\n\t\t...(ctx.session ? { session: ctx.session } : {}),\n\t\t...(ctx.actor === undefined ? {} : { actor: ctx.actor }),\n\t} as never);\n\tbuilt.set(key, collection);\n\treturn collection;\n}\n\n/**\n * A database with its collections on it. The collections are own properties,\n * so `Object.keys` lists them; everything else is the driver's `Db`, read\n * through a proxy — the shape `getCollection` already uses to put this\n * package's methods over the driver's collection.\n *\n * A key the `Db` already answers to never reaches here: `createKit` refuses\n * it, and the types refuse it before that.\n */\nexport function scopeOf(ctx: KitContext, database: DatabaseContext): object {\n\tconst collections: Record<string, unknown> = {};\n\tfor (const [key, definition] of database.wired) {\n\t\tObject.defineProperty(collections, key, {\n\t\t\tenumerable: true,\n\t\t\tget: () => collectionAt(ctx, database, key, definition),\n\t\t});\n\t}\n\treturn new Proxy(collections, {\n\t\tget(target, key, receiver) {\n\t\t\tif (Reflect.has(target, key)) return Reflect.get(target, key, receiver);\n\t\t\tconst value = Reflect.get(database.db, key) as unknown;\n\t\t\treturn typeof value === 'function' ? value.bind(database.db) : value;\n\t\t},\n\t\thas(target, key) {\n\t\t\treturn Reflect.has(target, key) || Reflect.has(database.db, key);\n\t\t},\n\t});\n}\n",
|
|
11
12
|
"import {\n\ttype SyncOptions,\n\ttype SyncReport,\n\tsyncCollections,\n} from '@nxgt/mongo';\nimport type { KitContext } from './context';\n\n/**\n * Syncs exactly the collections the kit wires, database by database — which\n * `syncAll` cannot do, since the registry knows no database.\n *\n * A deployment step: `collMod` needs the `dbAdmin` role, and neither it nor\n * an index build runs in a transaction. The first database that throws stops\n * the rest, so a `dryRun` is the way to see everything at once.\n */\nexport async function syncKit(\n\tctx: KitContext,\n\toptions: SyncOptions = {},\n): Promise<Record<string, SyncReport[]>> {\n\tconst reports: Record<string, SyncReport[]> = {};\n\tfor (const database of ctx.databases) {\n\t\treports[database.name] = await syncCollections(\n\t\t\tdatabase.db,\n\t\t\tdatabase.wired.map(([, definition]) => definition),\n\t\t\toptions,\n\t\t);\n\t}\n\treturn reports;\n}\n",
|
|
12
|
-
"import { type TransactionHost, withTransaction } from '@nxgt/mongo';\nimport type { MongoClient, TransactionOptions } from 'mongodb';\nimport { databaseOf, derived, type KitContext } from './context';\n\n/**\n * The client a transaction runs on. One transaction lives on one client, so\n * a kit holding several has to be told which — there is no transaction\n * across clients to give.\n */\nexport function clientFor(\n\tctx: KitContext,\n\ton: string | undefined,\n): MongoClient {\n\tif (on !== undefined) return databaseOf(ctx, on).client;\n\tconst clients = new Set(ctx.databases.map((database) => database.client));\n\tconst [only] = clients;\n\tif (clients.size === 1 && only) return only;\n\tthrow new
|
|
13
|
-
"import type { SyncOptions } from '@nxgt/mongo';\nimport { databaseOf, derived, type KitContext } from './context';\nimport { scopeOf } from './scope';\nimport { syncKit } from './sync';\nimport { transact } from './transaction';\nimport type { KitTransactionOptions, MongoKit } from './types';\n\n/**\n * Closes what this kit's context opened. Idempotent, because each\n * `MongoConnection` is: a second call awaits the first one's work.\n */\nasync function closeKit(ctx: KitContext): Promise<void> {\n\tif (!ctx.root) {\n\t\tthrow new
|
|
13
|
+
"import { type TransactionHost, withTransaction } from '@nxgt/mongo';\nimport type { MongoClient, TransactionOptions } from 'mongodb';\nimport { KitError } from '../errors/kit-error';\nimport { databaseOf, derived, type KitContext } from './context';\n\n/**\n * The client a transaction runs on. One transaction lives on one client, so\n * a kit holding several has to be told which — there is no transaction\n * across clients to give.\n */\nexport function clientFor(\n\tctx: KitContext,\n\ton: string | undefined,\n): MongoClient {\n\tif (on !== undefined) return databaseOf(ctx, on).client;\n\tconst clients = new Set(ctx.databases.map((database) => database.client));\n\tconst [only] = clients;\n\tif (clients.size === 1 && only) return only;\n\tthrow new KitError(\n\t\t'TRANSACTION',\n\t\t'transaction: this kit holds more than one client, and a transaction ' +\n\t\t\t\"lives on one. Name the database it runs on, as `{ on: 'main' }`.\",\n\t);\n}\n\n/**\n * What the transaction runs on: the kit's own session when it has one, so\n * that a transaction inside a transaction **joins** the outer one rather than\n * opening a second, independent one beside it; a client otherwise.\n */\nexport function hostFor(\n\tctx: KitContext,\n\ton: string | undefined,\n): TransactionHost {\n\tif (!ctx.session) return clientFor(ctx, on);\n\tif (on !== undefined) {\n\t\tthrow new KitError(\n\t\t\t'TRANSACTION',\n\t\t\t'transaction: this kit is already in a session, which this call ' +\n\t\t\t\t'joins, so `on` has no client left to choose.',\n\t\t);\n\t}\n\treturn ctx.session;\n}\n\n/**\n * Runs `fn` in a transaction, with a kit whose collections are all in it.\n * The driver retries `fn` from the start on a transient error, so `fn` must\n * be safe to run twice — `@nxgt/mongo`'s `withTransaction` says the rest.\n */\nexport async function transact<T>(\n\tctx: KitContext,\n\tbuild: (ctx: KitContext) => unknown,\n\tfn: (kit: never) => Promise<T>,\n\toptions: (TransactionOptions & { on?: string }) | undefined,\n): Promise<T> {\n\tconst { on, ...rest } = options ?? {};\n\tconst host = hostFor(ctx, on);\n\tconst transactionOptions =\n\t\tObject.keys(rest).length > 0 ? (rest as TransactionOptions) : undefined;\n\treturn withTransaction(\n\t\thost,\n\t\t(session) => fn(build(derived(ctx, { session })) as never),\n\t\ttransactionOptions,\n\t);\n}\n",
|
|
14
|
+
"import type { SyncOptions } from '@nxgt/mongo';\nimport { KitError } from '../errors/kit-error';\nimport { databaseOf, derived, type KitContext } from './context';\nimport { scopeOf } from './scope';\nimport { syncKit } from './sync';\nimport { transact } from './transaction';\nimport type { KitTransactionOptions, MongoKit } from './types';\n\n/**\n * Closes what this kit's context opened. Idempotent, because each\n * `MongoConnection` is: a second call awaits the first one's work.\n */\nasync function closeKit(ctx: KitContext): Promise<void> {\n\tif (!ctx.root) {\n\t\tthrow new KitError(\n\t\t\t'DERIVED',\n\t\t\t'close: this kit came from `as`, `withSession` or a transaction. ' +\n\t\t\t\t'Close the kit `createKit` returned — the clients are shared.',\n\t\t);\n\t}\n\tfor (const database of ctx.databases) {\n\t\tawait database.connection?.close();\n\t}\n}\n\n/**\n * A kit over one context. `as` and `withSession` build another over a new\n * context, sharing the databases and the clients: only the collections are\n * built again, and only the ones a caller reads.\n */\nexport function kitOf<C>(ctx: KitContext): MongoKit<C> {\n\tconst scopes = new Map<string, object>();\n\tconst scopeFor = (name: string): object => {\n\t\tconst found = scopes.get(name);\n\t\tif (found) return found;\n\t\tconst scope = scopeOf(ctx, databaseOf(ctx, name));\n\t\tscopes.set(name, scope);\n\t\treturn scope;\n\t};\n\n\tconst databases = {} as Record<string, object>;\n\tconst clients = {} as Record<string, unknown>;\n\tfor (const database of ctx.databases) {\n\t\tObject.defineProperty(databases, database.name, {\n\t\t\tenumerable: true,\n\t\t\tget: () => scopeFor(database.name),\n\t\t});\n\t\tObject.defineProperty(clients, database.name, {\n\t\t\tenumerable: true,\n\t\t\tvalue: database.client,\n\t\t});\n\t}\n\n\tconst kit: MongoKit<C> = {\n\t\tget db() {\n\t\t\tconst [only] = ctx.databases;\n\t\t\tif (ctx.databases.length !== 1 || !only) {\n\t\t\t\tthrow new KitError(\n\t\t\t\t\t'SEVERAL_DATABASES',\n\t\t\t\t\t'kit.db: this kit has several databases. Read the one you mean, ' +\n\t\t\t\t\t\t`as \\`kit.databases.${ctx.databases[0]?.name ?? 'main'}\\`.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn scopeFor(only.name) as never;\n\t\t},\n\t\tdatabases: databases as never,\n\t\tclients: clients as never,\n\t\tget actor() {\n\t\t\treturn ctx.actor as never;\n\t\t},\n\t\tget session() {\n\t\t\treturn ctx.session;\n\t\t},\n\t\tas(actor) {\n\t\t\treturn kitOf<C>(derived(ctx, { actor }));\n\t\t},\n\t\twithSession(session) {\n\t\t\treturn kitOf<C>(derived(ctx, { session }));\n\t\t},\n\t\ttransaction<T>(\n\t\t\tfn: (kit: MongoKit<C>) => Promise<T>,\n\t\t\toptions?: KitTransactionOptions<C>,\n\t\t): Promise<T> {\n\t\t\treturn transact(\n\t\t\t\tctx,\n\t\t\t\t(next) => kitOf<C>(next),\n\t\t\t\tfn as never,\n\t\t\t\toptions as never,\n\t\t\t);\n\t\t},\n\t\tsync(options?: SyncOptions) {\n\t\t\treturn syncKit(ctx, options) as never;\n\t\t},\n\t\tclose() {\n\t\t\treturn closeKit(ctx);\n\t\t},\n\t\t[Symbol.asyncDispose]() {\n\t\t\treturn closeKit(ctx);\n\t\t},\n\t};\n\treturn kit;\n}\n"
|
|
14
15
|
],
|
|
15
|
-
"mappings": ";
|
|
16
|
-
"debugId": "
|
|
16
|
+
"mappings": "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",
|
|
17
|
+
"debugId": "D45C9A203331820F64756E2164756E21",
|
|
17
18
|
"names": []
|
|
18
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/kit/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/kit/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAG5D,sFAAsF;AACtF,MAAM,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,uBAAuB,CAAC,CAAC;AAEhF,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,KAAK,EAAE,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC;CACjD;AAED,kFAAkF;AAClF,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACvB;AAED,qFAAqF;AACrF,wBAAgB,OAAO,CACtB,GAAG,EAAE,UAAU,EACf,MAAM,EAAE;IAAE,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9D,UAAU,CAQZ;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAYzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-kit.d.ts","sourceRoot":"","sources":["../../src/kit/create-kit.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAkB,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"create-kit.d.ts","sourceRoot":"","sources":["../../src/kit/create-kit.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAkB,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA2CxC;;;;;;;;;;;;;GAaG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAuC7E"}
|
package/dist/kit/derive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/kit/derive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/kit/derive.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAIjE,OAAO,KAAK,EAAyB,QAAQ,EAAE,MAAM,SAAS,CAAC;AAmB/D;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,CAuErD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/kit/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAmB,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/kit/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAmB,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,EAAuB,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,SAAS,CACxB,GAAG,EAAE,UAAU,EACf,EAAE,EAAE,MAAM,GAAG,SAAS,GACpB,WAAW,CAUb;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACtB,GAAG,EAAE,UAAU,EACf,EAAE,EAAE,MAAM,GAAG,SAAS,GACpB,eAAe,CAUjB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC/B,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,EACnC,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAC9B,OAAO,EAAE,CAAC,kBAAkB,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,GACzD,OAAO,CAAC,CAAC,CAAC,CAUZ"}
|
package/docs/README.md
CHANGED
|
@@ -10,5 +10,6 @@ example per area. These pages are the long one.
|
|
|
10
10
|
| [The actor, sessions and transactions](guide/actor-and-transactions.md) | a write has to be stamped with who made it, or several writes have to commit together |
|
|
11
11
|
| [Syncing](guide/sync.md) | the collections, their validators and their indexes have to exist on the server |
|
|
12
12
|
| [`discoverCollections`](guide/discover-collections.md) | a script has to find the definitions of a repository without importing each one |
|
|
13
|
+
| [Errors](guide/errors.md) | something was refused — a configuration, a name, a transaction, a close — and you want the code to switch on |
|
|
13
14
|
| [Troubleshooting](troubleshooting.md) | something threw, and you have the message |
|
|
14
15
|
| [Roadmap](roadmap.md) | you want to know what is coming, and what will not |
|
|
@@ -124,8 +124,8 @@ await kit.transaction(async (outer) => {
|
|
|
124
124
|
|
|
125
125
|
`{ on }` is required at **run time**, not by the types, and cannot be: two
|
|
126
126
|
databases on one URI share a client and need none, so what decides is the
|
|
127
|
-
number of clients. A kit holding two without it throws a
|
|
128
|
-
what to write.
|
|
127
|
+
number of clients. A kit holding two without it throws a
|
|
128
|
+
[`KitError`](errors.md) with `code: 'TRANSACTION'`, naming what to write.
|
|
129
129
|
|
|
130
130
|
## In a request
|
|
131
131
|
|
|
@@ -169,12 +169,13 @@ export class UserService {
|
|
|
169
169
|
|
|
170
170
|
## What it throws
|
|
171
171
|
|
|
172
|
-
Every check is a `
|
|
173
|
-
connects. The message names the database it
|
|
172
|
+
Every check is a [`KitError`](errors.md) with `code: 'CONFIG'`, thrown from
|
|
173
|
+
`defineConfig`, before anything connects. The message names the database it
|
|
174
|
+
is about, and so does `error.database`:
|
|
174
175
|
|
|
175
176
|
```ts
|
|
176
177
|
defineConfig({ collections });
|
|
177
|
-
//
|
|
178
|
+
// KitError: defineConfig: database "default" has neither a uri nor a client
|
|
178
179
|
```
|
|
179
180
|
|
|
180
181
|
- a database with both a `uri` and a `client`, or neither;
|
package/docs/guide/db-scope.md
CHANGED
|
@@ -73,8 +73,9 @@ kit.clients.main === kit.clients.analytics; // true when one URI wires both
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
`kit.db` is then `never`, and reading it anyway — from JavaScript, or across
|
|
76
|
-
an `any` — throws a `
|
|
77
|
-
|
|
76
|
+
an `any` — throws a [`KitError`](errors.md) with
|
|
77
|
+
`code: 'SEVERAL_DATABASES'`, naming the databases to read instead: with two
|
|
78
|
+
of them there is no "the" database.
|
|
78
79
|
|
|
79
80
|
## Closing
|
|
80
81
|
|
|
@@ -55,7 +55,9 @@ A glob that matches nothing gives `[]`.
|
|
|
55
55
|
|
|
56
56
|
## What it throws
|
|
57
57
|
|
|
58
|
-
All three are a `
|
|
58
|
+
All three are a [`KitError`](errors.md) with `code: 'DISCOVERY'`. The two
|
|
59
|
+
that are about a file carry its path on `key`; the missing-glob one has no
|
|
60
|
+
path yet, so its `key` is `undefined`:
|
|
59
61
|
|
|
60
62
|
- `discoverCollections: a glob is required` — `glob` missing or empty.
|
|
61
63
|
- `discoverCollections: <path> exports no definition named "<name>"` — with
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Errors
|
|
2
|
+
|
|
3
|
+
`KitError` is what this package refuses — a configuration, a name or a call
|
|
4
|
+
that cannot work — with a `code` beside the sentence, so nothing has to match
|
|
5
|
+
the message text.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { defineConfig, KitError } from '@nxgt/mongo-kit';
|
|
9
|
+
import * as collections from './models';
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
defineConfig({ uri: process.env.MONGO_URI!, collections });
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (error instanceof KitError) {
|
|
15
|
+
error.code; // 'CONFIG'
|
|
16
|
+
error.database; // 'default' — the database it is about, when one is named
|
|
17
|
+
error.key; // the config key, collection key or path, when one is
|
|
18
|
+
}
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Errors from the collections themselves — a duplicate key, a failed
|
|
24
|
+
validation, a missing document — are `@nxgt/mongo`'s `DataError` and its
|
|
25
|
+
subclasses, unchanged: `kit.db.users` *is* one of its collections. `KitError`
|
|
26
|
+
is only about the wiring.
|
|
27
|
+
|
|
28
|
+
## The codes
|
|
29
|
+
|
|
30
|
+
| `code` | Thrown by | When |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| `CONFIG` | `defineConfig` | the configuration cannot work: no `uri` and no `client`, both at once, `clientOptions` beside a `client`, a `collections` with no definition in it, two keys on one server collection, `optionsFor` under a key nothing is wired under, or one of the four options the kit decides |
|
|
33
|
+
| `COLLISION` | `createKit` | a collection is wired under a name the driver's `Db` already has — `command`, `watch`, `collection`… — so it would be unreachable |
|
|
34
|
+
| `NO_DATABASE` | `transaction(fn, { on: '<name>' })` | this kit holds no database under that name; the message lists the ones it has. Reading `kit.databases.<name>` does **not** throw — an unknown key is plain `undefined` |
|
|
35
|
+
| `SEVERAL_DATABASES` | reading `kit.db` | the kit holds more than one database, so there is no "the" database to give |
|
|
36
|
+
| `TRANSACTION` | `transaction` | the kit holds several clients and the call named none, or it is already in a session and still passed `{ on }` |
|
|
37
|
+
| `DERIVED` | `close` | the kit came from `as`, `withSession` or a transaction: the clients are the root kit's |
|
|
38
|
+
| `DISCOVERY` | `discoverCollections` | the glob is missing, a matched file has no definition under the `export` asked for, or two files define the same server collection |
|
|
39
|
+
|
|
40
|
+
`code` is the field to switch on: it survives a build that ends up with two
|
|
41
|
+
copies of the package, which `instanceof` does not.
|
|
42
|
+
|
|
43
|
+
## What it carries
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
class KitError extends TypeError {
|
|
47
|
+
readonly code: KitErrorCode;
|
|
48
|
+
/** The database it is about, when one is named. */
|
|
49
|
+
readonly database: string | undefined;
|
|
50
|
+
/** The config key, the collection key or the path it is about. */
|
|
51
|
+
readonly key: string | undefined;
|
|
52
|
+
|
|
53
|
+
constructor(code: KitErrorCode, message: string, options?: KitErrorOptions);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type KitErrorCode =
|
|
57
|
+
| 'CONFIG'
|
|
58
|
+
| 'COLLISION'
|
|
59
|
+
| 'NO_DATABASE'
|
|
60
|
+
| 'SEVERAL_DATABASES'
|
|
61
|
+
| 'TRANSACTION'
|
|
62
|
+
| 'DERIVED'
|
|
63
|
+
| 'DISCOVERY';
|
|
64
|
+
|
|
65
|
+
interface KitErrorOptions {
|
|
66
|
+
database?: string | undefined;
|
|
67
|
+
key?: string | undefined;
|
|
68
|
+
cause?: unknown;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`database` is the key the database is named by in the configuration —
|
|
73
|
+
`default` for a lone one — and `key` is the collection key, the config key or
|
|
74
|
+
the file path the refusal is about. Neither is ever a URI: a connection
|
|
75
|
+
string holds the password, and this package prints none.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { createKit, KitError } from '@nxgt/mongo-kit';
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await createKit(config);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error instanceof KitError && error.code === 'COLLISION') {
|
|
84
|
+
error.database; // 'main'
|
|
85
|
+
error.key; // 'command' — the export to rename
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## It is a `TypeError`
|
|
92
|
+
|
|
93
|
+
`KitError` extends **`TypeError`**, not `Error`, unlike `@nxgt/mongo`'s
|
|
94
|
+
`DataError` or `@nxgt/redis`'s `RedisError`. Every one of these is a call or
|
|
95
|
+
a configuration written wrong, which is what `TypeError` means — and this
|
|
96
|
+
package threw bare `TypeError`s before the class existed, so nothing that
|
|
97
|
+
already catches one stopped matching:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
try {
|
|
101
|
+
defineConfig({ databases: {} } as never);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
error instanceof KitError; // true
|
|
104
|
+
error instanceof TypeError; // true — still what it always was
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
What is new is the `code`, which a `catch` can switch on instead of reading
|
|
109
|
+
the sentence.
|
|
110
|
+
|
|
111
|
+
## Where each one comes from
|
|
112
|
+
|
|
113
|
+
Nothing below reaches a request handler in a working application: they are
|
|
114
|
+
start-up and wiring failures, and `defineConfig` is deliberately the earliest
|
|
115
|
+
of them.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { createKit, defineConfig, KitError } from '@nxgt/mongo-kit';
|
|
119
|
+
import * as collections from './models';
|
|
120
|
+
|
|
121
|
+
// CONFIG — before anything connects.
|
|
122
|
+
defineConfig({ collections } as never);
|
|
123
|
+
// KitError: defineConfig: database "default" has neither a uri nor a client
|
|
124
|
+
|
|
125
|
+
// COLLISION — at createKit, against the driver's own Db.
|
|
126
|
+
await createKit(
|
|
127
|
+
defineConfig({ uri: process.env.MONGO_URI!, collections: { command: users } as never }),
|
|
128
|
+
);
|
|
129
|
+
// KitError: createKit: database "default" wires a collection under "command", …
|
|
130
|
+
|
|
131
|
+
// NO_DATABASE — a transaction named on a database this kit does not hold.
|
|
132
|
+
// The types refuse the name, so this is the call that came through an `any`,
|
|
133
|
+
// or from JavaScript. Reading `kit.databases.nowhere` gives `undefined`
|
|
134
|
+
// instead: only `on` looks a name up.
|
|
135
|
+
await kit.transaction(async () => {}, { on: 'nowhere' as never });
|
|
136
|
+
// KitError: This kit has no database "nowhere": it has "main", "analytics".
|
|
137
|
+
|
|
138
|
+
// SEVERAL_DATABASES — `kit.db` with more than one. Its type is `never`.
|
|
139
|
+
kit.db;
|
|
140
|
+
// KitError: kit.db: this kit has several databases. Read the one you mean, as `kit.databases.main`.
|
|
141
|
+
|
|
142
|
+
// TRANSACTION — several clients, and no `{ on }`.
|
|
143
|
+
await kit.transaction(async (tx) => { /* … */ });
|
|
144
|
+
// KitError: transaction: this kit holds more than one client, …
|
|
145
|
+
|
|
146
|
+
// DERIVED — closing a kit that `as` derived.
|
|
147
|
+
await kit.as(userId).close();
|
|
148
|
+
// KitError: close: this kit came from `as`, `withSession` or a transaction. …
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
A name no database has, and `{ on: 'nowhere' }` with it, do not compile
|
|
152
|
+
either: the types refuse them where they are written. The run-time refusal is
|
|
153
|
+
what catches the call that arrived through an `any`, or from JavaScript — and
|
|
154
|
+
`kit.db` on a kit with several databases, whose type is already `never`.
|
|
155
|
+
|
|
156
|
+
## A start-up that reports instead of crashing
|
|
157
|
+
|
|
158
|
+
The useful thing to do with a `KitError` is to say which database and which
|
|
159
|
+
key, because that is what the fix needs:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { createKit, defineConfig, KitError } from '@nxgt/mongo-kit';
|
|
163
|
+
import * as collections from './models';
|
|
164
|
+
|
|
165
|
+
export async function startDatabase() {
|
|
166
|
+
try {
|
|
167
|
+
return await createKit(
|
|
168
|
+
defineConfig({ uri: process.env.MONGO_URI!, collections }),
|
|
169
|
+
);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (error instanceof KitError) {
|
|
172
|
+
console.error(
|
|
173
|
+
`mongo: ${error.code}` +
|
|
174
|
+
(error.database ? ` on "${error.database}"` : '') +
|
|
175
|
+
(error.key ? ` at "${error.key}"` : ''),
|
|
176
|
+
error.message,
|
|
177
|
+
);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
throw error; // a driver error: a host that does not answer, a bad password
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
MongoDB's own refusal to connect is **not** a `KitError`: a host that does not
|
|
186
|
+
answer, a wrong password, a replica set with no primary are the driver's
|
|
187
|
+
errors, and they reach the caller unchanged from `createKit`. A database that
|
|
188
|
+
fails to open gives back every connection opened before it.
|
|
189
|
+
|
|
190
|
+
## Next
|
|
191
|
+
|
|
192
|
+
- [Configuration](configuration.md) — what `defineConfig` checks, key by key.
|
|
193
|
+
- [The `db` scope](db-scope.md) — `kit.db`, `kit.databases` and the names
|
|
194
|
+
that are refused.
|
|
195
|
+
- [The actor, sessions and transactions](actor-and-transactions.md) — `{ on }`,
|
|
196
|
+
and the kits that cannot be closed.
|
|
197
|
+
- [Troubleshooting](../troubleshooting.md) — the same errors, indexed by the
|
|
198
|
+
message you are staring at.
|
package/docs/roadmap.md
CHANGED
|
@@ -42,6 +42,12 @@ _Nothing queued._
|
|
|
42
42
|
|
|
43
43
|
## Shipped
|
|
44
44
|
|
|
45
|
+
- **Every refusal is a `KitError`, with a code** — `CONFIG`, `COLLISION`,
|
|
46
|
+
`NO_DATABASE`, `SEVERAL_DATABASES`, `TRANSACTION`, `DERIVED` or `DISCOVERY`,
|
|
47
|
+
beside the database and the key it is about, so a caller switches on the
|
|
48
|
+
code instead of matching the sentence; it extends `TypeError`, which these
|
|
49
|
+
were before, so a `catch` written against the old ones still catches them —
|
|
50
|
+
0.2.0.
|
|
45
51
|
- **Documentation that travels with the package** — a guide page for the
|
|
46
52
|
configuration, the `db` scope, actor and transactions, and `sync()`, a
|
|
47
53
|
troubleshooting page whose headings are the exact error text, and this
|
package/docs/troubleshooting.md
CHANGED
|
@@ -3,11 +3,33 @@
|
|
|
3
3
|
Every heading is the text the error prints, so the page can be searched with
|
|
4
4
|
what you have in front of you. Stacks, ids and paths are cut.
|
|
5
5
|
|
|
6
|
+
Everything this package refuses is a `KitError`, exported from
|
|
7
|
+
`@nxgt/mongo-kit`. It carries a `code` — `CONFIG`, `COLLISION`,
|
|
8
|
+
`NO_DATABASE`, `SEVERAL_DATABASES`, `TRANSACTION`, `DERIVED` or `DISCOVERY` —
|
|
9
|
+
beside the `database` and the `key` it is about, so a caller switches on the
|
|
10
|
+
code instead of matching the sentence. It extends `TypeError`, which these
|
|
11
|
+
were before 0.2.0, so a `catch` written against `TypeError` still catches
|
|
12
|
+
them. The driver's own errors, and `@nxgt/mongo`'s `DataError`s, reach you
|
|
13
|
+
unchanged.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { KitError } from '@nxgt/mongo-kit';
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
await kit.transaction(work, { on: 'main' });
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (error instanceof KitError) {
|
|
22
|
+
log.error({ code: error.code, database: error.database, key: error.key });
|
|
23
|
+
}
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
6
28
|
| Area | Entries |
|
|
7
29
|
| --- | --- |
|
|
8
30
|
| [Install](#install) | [ERESOLVE](#npm-error-eresolve-unable-to-resolve-dependency-tree) · [incorrect peer dependency](#warn-incorrect-peer-dependency-nxgtmongo0140) · [TS2307](#error-ts2307-cannot-find-module-nxgtmongo-or-its-corresponding-type-declarations) |
|
|
9
31
|
| [Types](#types) | [a key the `Db` has](#command-is-a-member-of-the-drivers-db-wire-this-collection-under-another-key) · [options for a key that is not wired](#posts-is-not-wired-by-this-database-there-are-no-options-for-it) |
|
|
10
|
-
| [Configuration](#configuration) | [no configuration at all](#defineconfig-a-configuration-object-is-required) · [`databases` is not an object](#defineconfig-databases-
|
|
32
|
+
| [Configuration](#configuration) | [no configuration at all](#defineconfig-a-configuration-object-is-required) · [`databases` is not an object of databases by name](#defineconfig-databases-must-be-an-object-of-databases-by-name-as--databases--main----one-database-is-the-configuration-itself-and-names-itself-with-database) · [neither a uri nor a client](#defineconfig-database-main-has-neither-a-uri-nor-a-client) · [both](#defineconfig-database-main-has-both-a-uri-and-a-client-pass-the-one-it-should-use) · [client options](#defineconfig-database-main-has-client-options-beside-a-client-it-did-not-open-pass-them-where-the-client-is-made) · [no definition in it](#defineconfig-database-main-has-a-collections-object-with-no-definition-in-it-pass-the-module-as-in-import--as-collections) · [two keys, one collection](#defineconfig-database-main-wires-users-and-people-to-the-same-collection-users) · [an option the kit decides](#defineconfig-database-main-has-session-in-options-which-the-kit-decides-) · [options for a key it does not wire](#defineconfig-database-main-has-options-for-posts-which-it-does-not-wire) · [no databases](#defineconfig-databases-names-none-give-it-at-least-one-as--databases--main---) |
|
|
11
33
|
| [Runtime](#runtime) | [a key the `Db` has, at creation](#createkit-database-main-wires-a-collection-under-command-which-is-a-member-of-the-drivers-db-it-would-be-unreachable-export-that-definition-under-another-name) · [ECONNREFUSED](#mongoserverselectionerror-connect-econnrefused-12700127017) · [one URI, two option sets](#connectmongo-this-uri-is-already-connected-with-other-options-pass-the-same-options-everywhere-or-close-the-first-connection) · [`kit.db` with several databases](#kitdb-this-kit-has-several-databases-read-the-one-you-mean-as-kitdatabasesmain) · [an unknown database](#this-kit-has-no-database-reporting-it-has-main-analytics) · [closing a derived kit](#close-this-kit-came-from-as-withsession-or-a-transaction-close-the-kit-createkit-returned--the-clients-are-shared) · [`sync()` and privileges](#not-authorized-on-app-to-execute-command--collmod-users--) |
|
|
12
34
|
| [Transactions](#transactions) | [more than one client](#transaction-this-kit-holds-more-than-one-client-and-a-transaction-lives-on-one-name-the-database-it-runs-on-as--on-main-) · [already in a session](#transaction-this-kit-is-already-in-a-session-which-this-call-joins-so-on-has-no-client-left-to-choose) · [a session from another client](#clientsession-must-be-from-the-same-mongoclient) · [no replica set](#this-mongodb-deployment-does-not-support-retryable-writes-please-add-retrywritesfalse-to-your-connection-string) |
|
|
13
35
|
| [Scripts](#scripts) | [`Bun is not defined`](#referenceerror-bun-is-not-defined) · [no glob](#discovercollections-a-glob-is-required) · [two files, one collection](#discovercollections-srcmodelsonemodelts-and-srcmodelstwomodelts-both-define-the-collection-twice) · [no definition of that name](#discovercollections-srcmodelsnotests-exports-no-definition-named-definition) |
|
|
@@ -131,8 +153,10 @@ defineConfig({
|
|
|
131
153
|
|
|
132
154
|
## Configuration
|
|
133
155
|
|
|
134
|
-
`defineConfig` connects to nothing: everything below
|
|
135
|
-
configuration is written, before the
|
|
156
|
+
`defineConfig` connects to nothing: everything below is a `KitError` with
|
|
157
|
+
`code: 'CONFIG'`, thrown where the configuration is written, before the
|
|
158
|
+
application starts. It names the database it is about as `error.database`,
|
|
159
|
+
and the collection key as `error.key` when one is at fault.
|
|
136
160
|
|
|
137
161
|
### `defineConfig: a configuration object is required`
|
|
138
162
|
|
|
@@ -154,7 +178,7 @@ import * as collections from './models';
|
|
|
154
178
|
export const config = defineConfig({ uri, collections });
|
|
155
179
|
```
|
|
156
180
|
|
|
157
|
-
###
|
|
181
|
+
### ``defineConfig: databases must be an object of databases by name, as `{ databases: { main: … } }`. One database is the configuration itself, and names itself with `database`.``
|
|
158
182
|
|
|
159
183
|
**When:** calling `defineConfig` with a `databases` that is a string, a number
|
|
160
184
|
or `null`.
|
|
@@ -302,7 +326,7 @@ a config built at run time, or JavaScript.
|
|
|
302
326
|
export const config = defineConfig({ uri, collections, optionsFor: { users: {} } });
|
|
303
327
|
```
|
|
304
328
|
|
|
305
|
-
###
|
|
329
|
+
### ``defineConfig: databases names none. Give it at least one, as `{ databases: { main: … } }`.``
|
|
306
330
|
|
|
307
331
|
**When:** calling `defineConfig` with `databases: {}`.
|
|
308
332
|
|
|
@@ -324,8 +348,10 @@ given back before it throws.
|
|
|
324
348
|
|
|
325
349
|
**Why:** the same collision as the
|
|
326
350
|
[type error](#command-is-a-member-of-the-drivers-db-wire-this-collection-under-another-key),
|
|
327
|
-
asked of the live `Db` object rather than of its type.
|
|
328
|
-
|
|
351
|
+
asked of the live `Db` object rather than of its type. A `KitError` with
|
|
352
|
+
`code: 'COLLISION'`, carrying the `database` and the `key` it refused. It
|
|
353
|
+
fires when the types were bypassed, and when a driver release adds a member
|
|
354
|
+
your key already uses.
|
|
329
355
|
|
|
330
356
|
**Fix:** rename the export, as above. If the driver added the member, raising
|
|
331
357
|
`mongodb` is what surfaced it — the check is deliberate, not a regression.
|
|
@@ -373,7 +399,7 @@ Both databases then share the one client, which is the point.
|
|
|
373
399
|
|
|
374
400
|
**Why:** `db` is the sole database's scope. With several there is no sole one,
|
|
375
401
|
so its type is already `never` — this is what a cast or a JavaScript call-site
|
|
376
|
-
gets at run time.
|
|
402
|
+
gets at run time. `code: 'SEVERAL_DATABASES'`.
|
|
377
403
|
|
|
378
404
|
**Fix:**
|
|
379
405
|
|
|
@@ -386,7 +412,8 @@ await kit.databases.main.users.create({ email: 'ada@example.com' });
|
|
|
386
412
|
**When:** `transaction(fn, { on })` with a name the config does not hold.
|
|
387
413
|
|
|
388
414
|
**Why:** the names are the keys of `databases` in the config, nothing else —
|
|
389
|
-
not the database names on the server.
|
|
415
|
+
not the database names on the server. `code: 'NO_DATABASE'`, with the name
|
|
416
|
+
that was asked for as `error.database`. (Reading `kit.databases.<name>` for a
|
|
390
417
|
name that is not there does not throw: it does not compile, and gives
|
|
391
418
|
`undefined` where the types were bypassed.)
|
|
392
419
|
|
|
@@ -403,7 +430,7 @@ that came from `as`, `withSession`, or the one handed to a transaction body.
|
|
|
403
430
|
|
|
404
431
|
**Why:** a derived kit shares the databases and the clients of the kit
|
|
405
432
|
`createKit` returned. Closing it would take the connections from every other
|
|
406
|
-
kit derived from the same root.
|
|
433
|
+
kit derived from the same root. `code: 'DERIVED'`.
|
|
407
434
|
|
|
408
435
|
**Fix:** keep `await using` for the root, and let the derived ones fall away:
|
|
409
436
|
|
|
@@ -442,6 +469,7 @@ client.
|
|
|
442
469
|
**Why:** a transaction lives on a single client, and the types cannot decide:
|
|
443
470
|
two databases on one URI share a client and need no `on`, so what matters is
|
|
444
471
|
the number of *clients*, which is known only once they are open.
|
|
472
|
+
`code: 'TRANSACTION'`.
|
|
445
473
|
|
|
446
474
|
**Fix:**
|
|
447
475
|
|
|
@@ -456,7 +484,7 @@ await kit.transaction((tx) => tx.databases.main.users.create(user), { on: 'main'
|
|
|
456
484
|
|
|
457
485
|
**Why:** a nested transaction **joins** the outer one rather than opening a
|
|
458
486
|
second beside it, so it runs on the session that is already open — there is no
|
|
459
|
-
client left to pick.
|
|
487
|
+
client left to pick. `code: 'TRANSACTION'`, as above.
|
|
460
488
|
|
|
461
489
|
**Fix:**
|
|
462
490
|
|
|
@@ -503,7 +531,9 @@ mongod --replSet rs0 --dbpath ./data # then, once: rs.initiate()
|
|
|
503
531
|
## Scripts
|
|
504
532
|
|
|
505
533
|
`discoverCollections` is for scripts run from the repository: it reads a glob
|
|
506
|
-
from the file system, gives no types, and does not survive bundling.
|
|
534
|
+
from the file system, gives no types, and does not survive bundling. What it
|
|
535
|
+
refuses is a `KitError` with `code: 'DISCOVERY'`, and the path it was reading
|
|
536
|
+
as `error.key`.
|
|
507
537
|
|
|
508
538
|
### `ReferenceError: Bun is not defined`
|
|
509
539
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxgt/mongo-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "An application's MongoDB wiring in one object: a validated config, the clients it opens from it, and every collection typed on the database it lives in",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -48,14 +48,14 @@
|
|
|
48
48
|
]
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@nxgt/mongo": "^0.
|
|
51
|
+
"@nxgt/mongo": "^0.16.0",
|
|
52
52
|
"@types/bun": "^1.4.0",
|
|
53
53
|
"mongodb": "7.6.0",
|
|
54
54
|
"mongodb-memory-server-core": "11.2.0",
|
|
55
55
|
"zod": "4.6.5"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@nxgt/mongo": "^0.
|
|
58
|
+
"@nxgt/mongo": "^0.16.0",
|
|
59
59
|
"mongodb": ">=7.0.0 <8",
|
|
60
60
|
"typescript": "^6.0.3"
|
|
61
61
|
}
|