@nxgt/mongo-kit 0.1.5 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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: the clients are the root kit's.
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**, where its type is
269
- already `never`: the message names the databases to read instead.
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;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"}
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":"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"}
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"}
@@ -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;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"}
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
@@ -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 = (where, said) => {
12
- throw new TypeError(`defineConfig: ${where} ${said}`);
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(where, what, options) {
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(where, `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");
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(where, "is not a configuration object");
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(where, hasUri ? "has both a uri and a client: pass the one it should use" : "has neither a uri nor a client");
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(where, "has a uri that is not a string");
48
+ refuse(name, "has a uri that is not a string");
36
49
  }
37
50
  if (hasClient && typeof config.client?.db !== "function") {
38
- refuse(where, "has a client that is not a MongoClient");
51
+ refuse(name, "has a client that is not a MongoClient");
39
52
  }
40
53
  if (hasClient && config.clientOptions !== undefined) {
41
- refuse(where, "has client options beside a client it did not open: pass them where the client is made");
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(where, "has an empty database name");
57
+ refuse(name, "has an empty database name");
45
58
  }
46
59
  if (typeof config.collections !== "object" || config.collections === null) {
47
- refuse(where, "has no collections object");
60
+ refuse(name, "has no collections object");
48
61
  }
49
62
  const definitions = definitionsOf(config.collections);
50
63
  if (definitions.length === 0) {
51
- refuse(where, "has a collections object with no definition in it: pass the module, as in `import * as collections`");
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(where, `wires "${seen}" and "${key}" to the same collection, "${definition.name}"`);
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(where, `has options for "${key}", which it does not wire`);
77
+ refuse(name, `has options for "${key}", which it does not wire`, key);
65
78
  }
66
79
  }
67
- checkOwned(where, "options", config.options);
80
+ checkOwned(name, "options", config.options);
68
81
  for (const [key, options] of Object.entries(config.optionsFor ?? {})) {
69
- checkOwned(where, `the options of "${key}"`, options);
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 TypeError("defineConfig: a configuration object is required");
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 TypeError("defineConfig: databases is not an object");
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 TypeError("defineConfig: databases names none");
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 TypeError("discoverCollections: a glob is required");
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 TypeError(`discoverCollections: ${path} exports no definition named "${name}"`);
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 TypeError(`discoverCollections: ${seen} and ${path} both define the collection "${definition.name}"`);
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 TypeError(`This kit has no database "${name}": it has ${ctx.databases.map((database) => `"${database.name}"`).join(", ")}.`);
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 TypeError("transaction: this kit holds more than one client, and a transaction " + "lives on one. Name the database it runs on, as `{ on: 'main' }`.");
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 TypeError("transaction: this kit is already in a session, which this call " + "joins, so `on` has no client left to choose.");
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 TypeError("close: this kit came from `as`, `withSession` or a transaction. " + "Close the kit `createKit` returned — the clients are shared.");
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 TypeError("kit.db: this kit has several databases. Read the one you mean, " + `as \`kit.databases.${ctx.databases[0]?.name ?? "main"}\`.`);
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 TypeError(`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.");
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=00B9BBA5C20F9E8D64756E2164756E21
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
- "import type { AnyCollectionDefinition } from '@nxgt/mongo';\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 = (where: string, said: string): never => {\n\tthrow new TypeError(`defineConfig: ${where} ${said}`);\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(where: string, what: string, options: unknown): 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\twhere,\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);\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\tconst where = `database \"${name}\"`;\n\tif (typeof config !== 'object' || config === null) {\n\t\trefuse(where, '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\twhere,\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(where, 'has a uri that is not a string');\n\t}\n\tif (hasClient && typeof config.client?.db !== 'function') {\n\t\trefuse(where, 'has a client that is not a MongoClient');\n\t}\n\tif (hasClient && config.clientOptions !== undefined) {\n\t\trefuse(\n\t\t\twhere,\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(where, 'has an empty database name');\n\t}\n\tif (typeof config.collections !== 'object' || config.collections === null) {\n\t\trefuse(where, 'has no collections object');\n\t}\n\tconst definitions = definitionsOf(config.collections);\n\tif (definitions.length === 0) {\n\t\trefuse(\n\t\t\twhere,\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\twhere,\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(where, `has options for \"${key}\", which it does not wire`);\n\t\t}\n\t}\n\tcheckOwned(where, 'options', config.options);\n\tfor (const [key, options] of Object.entries(config.optionsFor ?? {})) {\n\t\tcheckOwned(where, `the options of \"${key}\"`, options);\n\t}\n\treturn definitions;\n}\n",
6
- "import { 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 TypeError('defineConfig: a configuration object is required');\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 TypeError('defineConfig: databases is not an object');\n\t}\n\tconst names = Object.keys(databases);\n\tif (names.length === 0) {\n\t\tthrow new TypeError('defineConfig: databases names none');\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",
7
- "import type { AnyCollectionDefinition } from '@nxgt/mongo';\nimport { definitionsOf, isDefinition } from './config/checks';\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 TypeError('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 TypeError(\n\t\t\t\t`discoverCollections: ${path} exports no definition named \"${name}\"`,\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 TypeError(\n\t\t\t\t\t`discoverCollections: ${seen} and ${path} both define the collection \"${definition.name}\"`,\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",
8
- "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 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 TypeError(\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);\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",
9
- "import type { AnyCollectionDefinition, MongoConnection } from '@nxgt/mongo';\nimport type { ClientSession, Db, MongoClient } from 'mongodb';\nimport type { KitCollectionOptions } from '../config/types';\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 TypeError(\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);\n\t}\n\treturn found;\n}\n",
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 TypeError(\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 TypeError(\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",
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 TypeError(\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 TypeError(\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"
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": "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",
16
- "debugId": "00B9BBA5C20F9E8D64756E2164756E21",
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;AAE5D,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,CAUzE"}
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;AAGjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAyCxC;;;;;;;;;;;;;GAaG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAuC7E"}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/kit/derive.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAIjE,OAAO,KAAK,EAAyB,QAAQ,EAAE,MAAM,SAAS,CAAC;AAkB/D;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,CAsErD"}
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;AAC/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,CASb;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACtB,GAAG,EAAE,UAAU,EACf,EAAE,EAAE,MAAM,GAAG,SAAS,GACpB,eAAe,CASjB;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"}
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 `TypeError` naming
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 `TypeError`, thrown from `defineConfig`, before anything
173
- connects. The message names the database it is about:
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
- // TypeError: defineConfig: database "default" has neither a uri nor a client
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;
@@ -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 `TypeError` naming the databases to read instead: with
77
- two of them there is no "the" database.
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 `TypeError`:
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
@@ -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-is-not-an-object) · [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) |
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 throws where the
135
- configuration is written, before the application starts.
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
- ### `defineConfig: databases is not an object`
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
- ### `defineConfig: databases names none`
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. It fires when the types
328
- were bypassed, and when a driver release adds a member your key already uses.
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. (Reading `kit.databases.<name>` for a
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.1.5",
3
+ "version": "0.2.1",
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.15.1",
51
+ "@nxgt/mongo": "^0.17.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.15.1",
58
+ "@nxgt/mongo": "^0.17.0",
59
59
  "mongodb": ">=7.0.0 <8",
60
60
  "typescript": "^6.0.3"
61
61
  }