@frogfish/k2db 2.0.6 → 3.0.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
@@ -23,9 +23,10 @@ Where it fits in the stack
23
23
 
24
24
  Deployment tips (Nomad, Lambda, etc.)
25
25
  - Environments: Targets Node.js runtimes (Node 18/20). Not suitable for non‑TCP “edge JS” (e.g., Cloudflare Workers) that cannot open Mongo sockets.
26
- - Connection reuse: Create one `K2DB` instance per process and reuse it.
27
- - Lambda: instantiate outside the handler and `await init()` lazily once.
28
- - Nomad: init on service start; reuse for the lifetime of the task.
26
+ - Connection reuse: Create and reuse `K2DB` instances.
27
+ - The underlying MongoDB connection pool is shared across `K2DB` instances created with the same cluster/auth settings (hosts/user/password/authSource/replicaset).
28
+ - This means you can safely keep one `K2DB` instance per logical database name (`name`) without creating a new TCP pool per database.
29
+ - `release()` is ref-counted: it only closes the shared pool when the last instance releases it.
29
30
  - Example (AWS Lambda):
30
31
  ```ts
31
32
  import { K2DB } from "@frogfish/k2db";
@@ -38,7 +39,24 @@ Deployment tips (Nomad, Lambda, etc.)
38
39
  return { statusCode: 200, body: JSON.stringify(res) };
39
40
  };
40
41
  ```
41
- - Pooling and timeouts: The driver manages a small pool by default.
42
+ If you serve multiple logical databases (multi-project / multi-tenant), cache `K2DB` by database name. Instances will still share a single underlying connection pool:
43
+
44
+ ```ts
45
+ import { K2DB } from "@frogfish/k2db";
46
+
47
+ const base = { hosts: [{ host: "cluster0.example.mongodb.net" }], user: process.env.DB_USER, password: process.env.DB_PASS };
48
+ const byName = new Map<string, K2DB>();
49
+
50
+ function dbFor(name: string) {
51
+ let db = byName.get(name);
52
+ if (!db) {
53
+ db = new K2DB({ ...base, name });
54
+ byName.set(name, db);
55
+ }
56
+ return db;
57
+ }
58
+ ```
59
+ - Pooling and timeouts: The MongoDB driver manages a small pool by default, and k2db reuses that pool across `K2DB` instances that share cluster/auth config.
42
60
  - Serverless: keep `minPoolSize=0` (default), consider `maxIdleTimeMS` to drop idle sockets faster.
43
61
  - Long‑lived services (Nomad): you can tune pool sizing if needed.
44
62
  - You can adjust `connectTimeoutMS/serverSelectionTimeoutMS` in the code if your environment needs higher values.
@@ -90,10 +108,11 @@ Config
90
108
  import { K2DB } from "@frogfish/k2db";
91
109
 
92
110
  const db = new K2DB({
93
- name: "mydb",
111
+ name: "mydb", // logical database name; instances with the same hosts/auth share one connection pool
94
112
  hosts: [{ host: "cluster0.example.mongodb.net" }], // SRV if single host without port
95
113
  user: process.env.DB_USER,
96
114
  password: process.env.DB_PASS,
115
+ authSource: process.env.DB_AUTH_SOURCE, // optional (defaults to "admin" when user+password provided)
97
116
  slowQueryMs: 300,
98
117
  hooks: {
99
118
  beforeQuery: (op, d) => {},
@@ -107,7 +126,19 @@ await db.ensureIndexes("myCollection");
107
126
 
108
127
  Environment loader
109
128
  ```ts
110
- const conf = K2DB.fromEnv(); // K2DB_NAME, K2DB_HOSTS, K2DB_USER, K2DB_PASSWORD, K2DB_REPLICASET, K2DB_SLOW_MS
129
+ const conf = K2DB.fromEnv(); // K2DB_NAME (logical db), K2DB_HOSTS, K2DB_USER, K2DB_PASSWORD, K2DB_AUTH_SOURCE, K2DB_REPLICASET, K2DB_SLOW_MS
130
+ ```
131
+
132
+ Testing
133
+
134
+ If you run many test suites in a single Node process and want to fully tear down shared MongoDB pools between suites, you can use the test helper:
135
+
136
+ ```ts
137
+ import { resetSharedMongoClientsForTests } from "@frogfish/k2db";
138
+
139
+ afterAll(async () => {
140
+ await resetSharedMongoClientsForTests();
141
+ });
111
142
  ```
112
143
 
113
144
  Tips
@@ -168,6 +199,7 @@ export K2DB_NAME=mydb
168
199
  export K2DB_HOSTS=cluster0.xxxxxx.mongodb.net
169
200
  export K2DB_USER=your_user
170
201
  export K2DB_PASSWORD=your_pass
202
+ export K2DB_AUTH_SOURCE=admin
171
203
  node hello.mjs
172
204
  ```
173
205
  ```ts
@@ -194,7 +226,7 @@ const db = new K2DB({
194
226
  password: process.env.DB_PASS,
195
227
  });
196
228
 
197
- await db.init();
229
+ await db.init(); // safe to call multiple times; concurrent calls are deduped
198
230
  await db.ensureIndexes("hello"); // unique _uuid among non-deleted, plus helpful indexes
199
231
 
200
232
  // Create a document (owner is required)
@@ -295,3 +327,21 @@ Returns by method
295
327
  - `setSchema(collection, zodSchema, { mode }?)`: `void`
296
328
  - `clearSchema(collection)`: `void`
297
329
  - `clearSchemas()`: `void`
330
+
331
+ ## UUID
332
+
333
+ _uuid = Crockford Base32 encoded UUID V7, Uppercase, with hyphens
334
+
335
+ 0J4F2-H6M8Q-7RX4V-9D3TN-8K2WZ
336
+
337
+ // Canonical uppercase form with hyphens Crockford 32
338
+ const CROCKFORD_ID_REGEX = /^[0-9A-HJKMNP-TV-Z]{5}-[0-9A-HJKMNP-TV-Z]{5}-[0-9A-HJKMNP-TV-Z]{5}-[0-9A-HJKMNP-TV-Z]{5}-[0-9A-HJKMNP-TV-Z]{6}$/;
339
+
340
+ // Example usage:
341
+ const id = "0J4F2-H6M8Q-7RX4V-9D3TN-8K2WZ";
342
+ console.log(CROCKFORD_ID_REGEX.test(id)); // true
343
+
344
+ Usage examples:
345
+
346
+ import { isK2ID, K2DB } from '@frogfish/k2db'
347
+ isK2ID('01HZY2AB-3JKM-4NPQ-5RST-6VWXYZ')
@@ -1,4 +1,5 @@
1
- import type { K2DB, BaseDocument, CreateResult, UpdateResult, DeleteResult, RestoreResult, CountResult, DropResult, VersionedUpdateResult, VersionInfo } from "./db.js";
1
+ import { K2DB } from "./db.js";
2
+ import type { BaseDocument, CreateResult, UpdateResult, DeleteResult, RestoreResult, CountResult, DropResult, VersionedUpdateResult, VersionInfo } from "./db.js";
2
3
  export declare class K2Data {
3
4
  private db;
4
5
  private owner;
@@ -100,4 +101,5 @@ export declare class K2Data {
100
101
  isHealthy(): Promise<boolean>;
101
102
  }
102
103
  export { K2DB } from "./db.js";
104
+ export declare const isK2ID: (id: string) => boolean;
103
105
  export type { DatabaseConfig, BaseDocument, CreateResult, UpdateResult, DeleteResult, RestoreResult, CountResult, DropResult, VersionedUpdateResult, VersionInfo, } from "./db.js";
@@ -1,3 +1,4 @@
1
+ import { K2DB } from "./db.js";
1
2
  export class K2Data {
2
3
  db;
3
4
  owner;
@@ -148,3 +149,4 @@ export class K2Data {
148
149
  }
149
150
  // Re-export K2DB (runtime) and types from the root entry
150
151
  export { K2DB } from "./db.js";
152
+ export const isK2ID = (id) => K2DB.isK2ID(id);
@@ -1,5 +1,12 @@
1
1
  import { ObjectId } from "mongodb";
2
2
  import { ZodTypeAny } from "zod";
3
+ /**
4
+ * Test helper: fully reset the shared MongoClient pool.
5
+ *
6
+ * Not for production usage; intended for test runners to clean up
7
+ * between suites without restarting the process.
8
+ */
9
+ export declare function resetSharedMongoClientsForTests(): Promise<void>;
3
10
  export interface HostConfig {
4
11
  host: string;
5
12
  port?: number;
@@ -8,6 +15,7 @@ export interface DatabaseConfig {
8
15
  name: string;
9
16
  user?: string;
10
17
  password?: string;
18
+ authSource?: string;
11
19
  hosts?: HostConfig[];
12
20
  replicaset?: string;
13
21
  slowQueryMs?: number;
@@ -63,6 +71,9 @@ export declare class K2DB {
63
71
  private conf;
64
72
  private db;
65
73
  private connection;
74
+ private clientKey?;
75
+ private initialized;
76
+ private initPromise?;
66
77
  private schemas;
67
78
  constructor(conf: DatabaseConfig);
68
79
  /**
@@ -183,8 +194,16 @@ export declare class K2DB {
183
194
  * @param criteria - Aggregation stage criteria.
184
195
  */
185
196
  private static sanitiseCriteria;
197
+ /** Recursively uppercases any values for fields named `_uuid` within a query object. */
198
+ private static normalizeCriteriaIds;
199
+ /** Uppercase helper for `_uuid` field supporting operators like $in/$nin/$eq/$ne and arrays. */
200
+ private static normalizeUuidField;
186
201
  /** Strip any user-provided fields that start with '_' (reserved). */
187
202
  private static stripReservedFields;
203
+ /** True if string matches K2 ID format (Crockford Base32, 8-4-4-4-6, uppercase). */
204
+ static isK2ID(id: string): boolean;
205
+ /** Uppercase incoming IDs for case-insensitive lookups. */
206
+ private static normalizeId;
188
207
  /**
189
208
  * Run an async DB operation with timing, slow logging, and hooks.
190
209
  */
@@ -232,12 +251,6 @@ export declare class K2DB {
232
251
  * Optional: Checks the health of the database connection.
233
252
  */
234
253
  isHealthy(): Promise<boolean>;
235
- /**
236
- * Utility to normalize the error type.
237
- * @param err - The caught error of type `unknown`.
238
- * @returns A normalized error of type `Error`.
239
- */
240
- private normalizeError;
241
254
  /** Name of the history collection for a given collection. */
242
255
  private historyName;
243
256
  /** Register a Zod schema for a collection. */