@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 +57 -7
- package/{dist/data.d.ts → data.d.ts} +3 -1
- package/{dist/data.js → data.js} +2 -0
- package/{dist/db.d.ts → db.d.ts} +19 -6
- package/{dist/db.js → db.js} +297 -74
- package/package.json +13 -35
- package/dist/LICENSE +0 -674
- package/dist/README.md +0 -297
- package/dist/package.json +0 -32
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
|
|
27
|
-
-
|
|
28
|
-
-
|
|
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
|
-
-
|
|
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
|
|
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";
|
package/{dist/data.js → data.js}
RENAMED
|
@@ -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);
|
package/{dist/db.d.ts → db.d.ts}
RENAMED
|
@@ -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. */
|