@frogfish/k2db 1.0.5 → 1.0.7

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/data.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { DB, BaseDocument } from "./db";
2
- export declare class Data {
1
+ import { K2DB, BaseDocument } from "./db";
2
+ export declare class K2Data {
3
3
  private db;
4
4
  private owner;
5
- constructor(db: DB, owner: string);
5
+ constructor(db: K2DB, owner: string);
6
6
  /**
7
7
  * Retrieves a single document by UUID.
8
8
  * @param collectionName - Name of the collection.
package/data.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  // src/Data.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.Data = void 0;
5
- class Data {
4
+ exports.K2Data = void 0;
5
+ class K2Data {
6
6
  constructor(db, owner) {
7
7
  this.db = db;
8
8
  this.owner = owner;
@@ -115,4 +115,4 @@ class Data {
115
115
  return this.db.isHealthy();
116
116
  }
117
117
  }
118
- exports.Data = Data;
118
+ exports.K2Data = K2Data;
package/db.d.ts CHANGED
@@ -19,7 +19,7 @@ export interface BaseDocument {
19
19
  _deleted?: boolean;
20
20
  [key: string]: any;
21
21
  }
22
- export declare class DB {
22
+ export declare class K2DB {
23
23
  private conf;
24
24
  private db;
25
25
  private connection;
package/db.js ADDED
@@ -0,0 +1,566 @@
1
+ "use strict";
2
+ // src/db.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.K2DB = void 0;
8
+ const k2error_1 = require("@frogfish/k2error"); // Keep the existing error structure
9
+ const mongodb_1 = require("mongodb");
10
+ const uuid_1 = require("uuid");
11
+ const debug_1 = __importDefault(require("debug"));
12
+ const debug = (0, debug_1.default)("k2:db");
13
+ class K2DB {
14
+ constructor(conf) {
15
+ this.conf = conf;
16
+ }
17
+ /**
18
+ * Initializes the MongoDB connection.
19
+ */
20
+ async init() {
21
+ const dbName = this.conf.name;
22
+ let connectUrl = "mongodb://";
23
+ // Add user and password if available
24
+ if (this.conf.user && this.conf.password) {
25
+ connectUrl += `${encodeURIComponent(this.conf.user)}:${encodeURIComponent(this.conf.password)}@`;
26
+ }
27
+ // Handle single host (non-replicaset) or multiple hosts (replicaset)
28
+ if (!this.conf.hosts || this.conf.hosts.length === 0) {
29
+ throw new k2error_1.K2Error(k2error_1.ServiceError.CONFIGURATION_ERROR, "No valid hosts provided in configuration", "sys_mdb_no_hosts");
30
+ }
31
+ connectUrl += this.conf.hosts
32
+ .map((host) => `${host.host}:${host.port || 27017}`)
33
+ .join(",");
34
+ // Append database name
35
+ connectUrl += `/${dbName}`;
36
+ // Append replicaset and options if it's a replicaset
37
+ if (this.conf.hosts.length > 1 && this.conf.replicaset) {
38
+ connectUrl += `?replicaSet=${this.conf.replicaset}&keepAlive=true&autoReconnect=true&socketTimeoutMS=0`;
39
+ }
40
+ // Mask sensitive information in logs
41
+ const safeConnectUrl = connectUrl.replace(/\/\/.*?:.*?@/, "//*****:*****@");
42
+ debug(`Connecting to MongoDB: ${safeConnectUrl}`);
43
+ // Define connection options with timeouts
44
+ const options = {
45
+ connectTimeoutMS: 2000, // 2 seconds
46
+ serverSelectionTimeoutMS: 2000, // 2 seconds
47
+ // Additional options can be added here
48
+ };
49
+ try {
50
+ // Establish MongoDB connection
51
+ this.connection = await mongodb_1.MongoClient.connect(connectUrl, options);
52
+ this.db = this.connection.db(dbName);
53
+ debug("Successfully connected to MongoDB");
54
+ }
55
+ catch (err) {
56
+ // Handle connection error
57
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Failed to connect to MongoDB", "sys_mdb_init", this.normalizeError(err));
58
+ }
59
+ }
60
+ /**
61
+ * Retrieves a collection from the database.
62
+ * @param collectionName - Name of the collection.
63
+ */
64
+ async getCollection(collectionName) {
65
+ try {
66
+ this.validateCollectionName(collectionName); // Validate the collection name
67
+ const collection = this.db.collection(collectionName);
68
+ return collection;
69
+ }
70
+ catch (err) {
71
+ // If the error is already an K2Error, rethrow it
72
+ if (err instanceof k2error_1.K2Error) {
73
+ throw err;
74
+ }
75
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, `Error getting collection: ${collectionName}`, "sys_mdb_gc", this.normalizeError(err));
76
+ }
77
+ }
78
+ async get(collectionName, uuid) {
79
+ const res = await this.findOne(collectionName, { _uuid: uuid });
80
+ if (!res) {
81
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Error getting the document with provided identity", "sys_mdb_get");
82
+ }
83
+ return res;
84
+ }
85
+ /**
86
+ * Retrieves a single document by UUID.
87
+ * @param collectionName - Name of the collection.
88
+ * @param uuid - UUID of the document.
89
+ * @param objectTypeName - Optional object type name.
90
+ * @param fields - Optional array of fields to include.
91
+ */
92
+ async findOne(collectionName, criteria, fields) {
93
+ const collection = await this.getCollection(collectionName);
94
+ const projection = {};
95
+ if (fields && fields.length > 0) {
96
+ fields.forEach((field) => {
97
+ projection[field] = 1;
98
+ });
99
+ }
100
+ try {
101
+ const item = await collection.findOne(criteria, { projection });
102
+ if (item) {
103
+ const { _id, ...rest } = item;
104
+ return rest;
105
+ }
106
+ return null;
107
+ }
108
+ catch (err) {
109
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Error finding document", "sys_mdb_fo", this.normalizeError(err));
110
+ }
111
+ }
112
+ /**
113
+ * Finds documents based on parameters with pagination support.
114
+ * @param collectionName - Name of the collection.
115
+ * @param filter - Criteria to filter the documents.
116
+ * @param params - Optional search parameters (for sorting, including/excluding fields).
117
+ * @param skip - Number of documents to skip (for pagination).
118
+ * @param limit - Maximum number of documents to return.
119
+ */
120
+ async find(collectionName, filter, params = {}, skip = 0, limit = 100) {
121
+ const collection = await this.getCollection(collectionName);
122
+ // Ensure filter is valid, defaulting to an empty object
123
+ const criteria = filter || {};
124
+ // Handle the _deleted field if params specify not to include deleted documents
125
+ if (params.includeDeleted) {
126
+ // No _deleted filter, include all documents
127
+ }
128
+ else if (params.deleted === true) {
129
+ criteria._deleted = true; // Explicitly search for deleted documents
130
+ }
131
+ else {
132
+ criteria._deleted = { $ne: true }; // Exclude deleted by default
133
+ }
134
+ // Build projection (fields to include or exclude)
135
+ let projection = { _id: 0 }; // Exclude _id by default
136
+ if (typeof params.filter === "string" && params.filter === "all") {
137
+ projection = {}; // Include all fields
138
+ }
139
+ else if (Array.isArray(params.filter)) {
140
+ params.filter.forEach((field) => {
141
+ projection[field] = 1; // Only include the specified fields
142
+ });
143
+ }
144
+ if (Array.isArray(params.exclude)) {
145
+ params.exclude.forEach((field) => {
146
+ projection[field] = 0; // Exclude the specified fields
147
+ });
148
+ }
149
+ // Build sorting options
150
+ let sort = undefined;
151
+ if (params.order) {
152
+ sort = {};
153
+ for (const [key, value] of Object.entries(params.order)) {
154
+ sort[key] = value === "asc" ? 1 : -1;
155
+ }
156
+ }
157
+ try {
158
+ let cursor = collection.find(criteria, { projection });
159
+ // Apply pagination
160
+ cursor = cursor.skip(skip).limit(limit);
161
+ if (sort) {
162
+ cursor = cursor.sort(sort);
163
+ }
164
+ const data = await cursor.toArray();
165
+ // Remove _id safely from each document
166
+ const result = data.map((doc) => {
167
+ const { _id, ...rest } = doc;
168
+ return rest;
169
+ });
170
+ return result;
171
+ }
172
+ catch (err) {
173
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Error executing find query", "sys_mdb_find_error", this.normalizeError(err));
174
+ }
175
+ }
176
+ /**
177
+ * Aggregates documents based on criteria with pagination support.
178
+ * @param collectionName - Name of the collection.
179
+ * @param criteria - Aggregation pipeline criteria.
180
+ * @param skip - Number of documents to skip (for pagination).
181
+ * @param limit - Maximum number of documents to return.
182
+ */
183
+ async aggregate(collectionName, criteria, skip = 0, limit = 100) {
184
+ if (criteria.length === 0) {
185
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Aggregation criteria cannot be empty", "sys_mdb_ag_empty");
186
+ }
187
+ // Ensure we always exclude soft-deleted documents
188
+ if (criteria[0].$match) {
189
+ criteria[0].$match = { ...criteria[0].$match, _deleted: { $ne: true } };
190
+ }
191
+ else {
192
+ criteria.unshift({ $match: { _deleted: { $ne: true } } });
193
+ }
194
+ // Add pagination stages to the aggregation pipeline
195
+ if (skip > 0) {
196
+ criteria.push({ $skip: skip });
197
+ }
198
+ if (limit > 0) {
199
+ criteria.push({ $limit: limit });
200
+ }
201
+ debug(`Aggregating with criteria: ${JSON.stringify(criteria, null, 2)}`);
202
+ const collection = await this.getCollection(collectionName);
203
+ // Sanitize criteria
204
+ const sanitizedCriteria = criteria.map((stage) => {
205
+ if (stage.$match) {
206
+ return K2DB.sanitiseCriteria(stage);
207
+ }
208
+ return stage;
209
+ });
210
+ try {
211
+ const data = await collection.aggregate(sanitizedCriteria).toArray();
212
+ // Enforce BaseDocument type on each document
213
+ return data.map((doc) => doc);
214
+ }
215
+ catch (err) {
216
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Aggregation failed", "sys_mdb_ag", this.normalizeError(err));
217
+ }
218
+ }
219
+ /**
220
+ * Creates a new document in the collection.
221
+ * @param collectionName - Name of the collection.
222
+ * @param owner - Owner of the document.
223
+ * @param data - Data to insert.
224
+ */
225
+ async create(collectionName, owner, data) {
226
+ if (!collectionName || !owner || !data) {
227
+ throw new k2error_1.K2Error(k2error_1.ServiceError.BAD_REQUEST, "Invalid method usage, parameters not defined", "sys_mdb_crv1");
228
+ }
229
+ if (typeof owner !== "string") {
230
+ throw new k2error_1.K2Error(k2error_1.ServiceError.BAD_REQUEST, "Owner must be of a string type", "sys_mdb_crv2");
231
+ }
232
+ const collection = await this.getCollection(collectionName);
233
+ const timestamp = Date.now();
234
+ // Generate a new UUID
235
+ const newUuid = (0, uuid_1.v4)();
236
+ // Spread `data` first, then set internal fields to prevent overwriting
237
+ const document = {
238
+ ...data,
239
+ _created: timestamp,
240
+ _updated: timestamp,
241
+ _owner: owner,
242
+ _uuid: newUuid,
243
+ };
244
+ try {
245
+ const result = await collection.insertOne(document);
246
+ return { id: document._uuid };
247
+ }
248
+ catch (err) {
249
+ // Use appropriate error typing
250
+ // Check if the error is a duplicate key error
251
+ if (err.code === 11000 && err.keyPattern && err.keyPattern._uuid) {
252
+ throw new k2error_1.K2Error(k2error_1.ServiceError.ALREADY_EXISTS, `A document with _uuid ${document._uuid} already exists.`, "sys_mdb_crv3");
253
+ }
254
+ // Log the error details for debugging
255
+ debug(`Was trying to insert into collection ${collectionName}, data: ${JSON.stringify(document)}`);
256
+ debug(err);
257
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Error saving object to database", "sys_mdb_sav", this.normalizeError(err));
258
+ }
259
+ }
260
+ /**
261
+ * Updates multiple documents based on criteria.
262
+ * Can either replace the documents or patch them.
263
+ * @param collectionName - Name of the collection.
264
+ * @param criteria - Update criteria.
265
+ * @param values - Values to update or replace with.
266
+ * @param replace - If true, replaces the entire document (PUT), otherwise patches (PATCH).
267
+ */
268
+ async updateAll(collectionName, criteria, values, replace = false) {
269
+ const collection = await this.getCollection(collectionName);
270
+ debug(`Updating ${collectionName} with criteria: ${JSON.stringify(criteria)}`);
271
+ values._updated = Date.now();
272
+ // Exclude soft-deleted documents unless _deleted is explicitly specified in criteria
273
+ if (!("_deleted" in criteria)) {
274
+ criteria = {
275
+ ...criteria,
276
+ _deleted: { $ne: true },
277
+ };
278
+ }
279
+ // Determine update operation based on the replace flag
280
+ const updateOperation = replace ? values : { $set: values };
281
+ try {
282
+ const res = await collection.updateMany(criteria, updateOperation);
283
+ return {
284
+ found: res.matchedCount,
285
+ modified: res.modifiedCount,
286
+ updated: res.acknowledged,
287
+ };
288
+ }
289
+ catch (err) {
290
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, `Error updating ${collectionName}`, "sys_mdb_update1", this.normalizeError(err));
291
+ }
292
+ }
293
+ /**
294
+ * Updates a single document by UUID.
295
+ * Can either replace the document or patch it.
296
+ * @param collectionName - Name of the collection.
297
+ * @param id - UUID string to identify the document.
298
+ * @param data - Data to update or replace with.
299
+ * @param replace - If true, replaces the entire document (PUT), otherwise patches (PATCH).
300
+ * @param objectTypeName - Optional object type name.
301
+ */
302
+ async update(collectionName, id, data, replace = false, objectTypeName) {
303
+ const collection = await this.getCollection(collectionName);
304
+ data._updated = Date.now();
305
+ try {
306
+ // Call updateAll with the UUID criteria and replace flag
307
+ const res = await this.updateAll(collectionName, { _uuid: id }, data, replace);
308
+ if (res.modified === 1) {
309
+ return { id: id };
310
+ }
311
+ if (res.modified === 0) {
312
+ throw new k2error_1.K2Error(k2error_1.ServiceError.NOT_FOUND, `Object in ${collectionName} not found`, "sys_mdb_update2");
313
+ }
314
+ }
315
+ catch (err) {
316
+ if (err instanceof k2error_1.K2Error) {
317
+ throw err;
318
+ }
319
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, `Error updating ${collectionName}`, "sys_mdb_update1", this.normalizeError(err));
320
+ }
321
+ }
322
+ /**
323
+ * Removes (soft deletes) multiple documents based on criteria.
324
+ * @param collectionName - Name of the collection.
325
+ * @param criteria - Removal criteria.
326
+ */
327
+ async deleteAll(collectionName, criteria) {
328
+ const collection = await this.getCollection(collectionName);
329
+ let foundCount;
330
+ try {
331
+ // Step 1: Count documents matching the original criteria
332
+ foundCount = await collection.countDocuments(criteria);
333
+ }
334
+ catch (err) {
335
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, `Error updating ${collectionName}`, "sys_mdb_deleteall_count", this.normalizeError(err));
336
+ }
337
+ // Step 2: Modify criteria to exclude already deleted documents
338
+ const modifiedCriteria = {
339
+ ...criteria,
340
+ _deleted: { $ne: true },
341
+ };
342
+ let result;
343
+ try {
344
+ // Perform the update
345
+ result = await this.updateAll(collectionName, modifiedCriteria, {
346
+ _deleted: true,
347
+ });
348
+ }
349
+ catch (err) {
350
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, `Error updating ${collectionName}`, "sys_mdb_deleteall_update", this.normalizeError(err));
351
+ }
352
+ // Step 3: Return the result, adjusting the 'found' count
353
+ return {
354
+ ...result,
355
+ found: foundCount, // Use the original count here
356
+ };
357
+ }
358
+ /**
359
+ * Removes (soft deletes) a single document by UUID.
360
+ * @param collectionName - Name of the collection.
361
+ * @param id - UUID of the document.
362
+ */
363
+ async delete(collectionName, id) {
364
+ try {
365
+ await this.deleteAll(collectionName, { _uuid: id });
366
+ return { id };
367
+ }
368
+ catch (err) {
369
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Error removing object from collection", "sys_mdb_remove_upd", this.normalizeError(err));
370
+ }
371
+ }
372
+ /**
373
+ * Permanently deletes a document that has been soft-deleted.
374
+ * @param collectionName - Name of the collection.
375
+ * @param id - UUID of the document.
376
+ */
377
+ async purge(collectionName, id) {
378
+ const collection = await this.getCollection(collectionName);
379
+ try {
380
+ const item = await collection.findOne({
381
+ _uuid: id,
382
+ _deleted: true,
383
+ });
384
+ if (!item) {
385
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Cannot purge item that is not deleted", "sys_mdb_gcol_pg2");
386
+ }
387
+ await collection.deleteMany({ _uuid: id });
388
+ return { id };
389
+ }
390
+ catch (err) {
391
+ if (err instanceof k2error_1.K2Error) {
392
+ throw err;
393
+ }
394
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, `Error purging item with id: ${id}`, "sys_mdb_pg", this.normalizeError(err));
395
+ }
396
+ }
397
+ /**
398
+ * Restores a soft-deleted document.
399
+ * @param collectionName - Name of the collection.
400
+ * @param criteria - Criteria to identify the document.
401
+ */
402
+ async restore(collectionName, criteria) {
403
+ const collection = await this.getCollection(collectionName);
404
+ criteria._deleted = true;
405
+ try {
406
+ const res = await collection.updateMany(criteria, {
407
+ $set: { _deleted: false },
408
+ });
409
+ return { status: "restored", modified: res.modifiedCount };
410
+ }
411
+ catch (err) {
412
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Error restoring a deleted item", "sys_mdb_pres", this.normalizeError(err));
413
+ }
414
+ }
415
+ /**
416
+ * Counts documents based on criteria.
417
+ * @param collectionName - Name of the collection.
418
+ * @param criteria - Counting criteria.
419
+ */
420
+ async count(collectionName, criteria) {
421
+ const collection = await this.getCollection(collectionName);
422
+ try {
423
+ const cnt = await collection.countDocuments(criteria);
424
+ return { count: cnt };
425
+ }
426
+ catch (err) {
427
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Error counting objects with given criteria", "sys_mdb_cn", this.normalizeError(err));
428
+ }
429
+ }
430
+ /**
431
+ * Drops an entire collection.
432
+ * @param collectionName - Name of the collection.
433
+ */
434
+ async drop(collectionName) {
435
+ const collection = await this.getCollection(collectionName);
436
+ try {
437
+ await collection.drop();
438
+ return { status: "ok" };
439
+ }
440
+ catch (err) {
441
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Error dropping collection", "sys_mdb_drop", this.normalizeError(err));
442
+ }
443
+ }
444
+ /**
445
+ * Sanitizes aggregation criteria.
446
+ * @param criteria - Aggregation stage criteria.
447
+ */
448
+ static sanitiseCriteria(criteria) {
449
+ if (criteria.$match) {
450
+ for (const key of Object.keys(criteria.$match)) {
451
+ if (typeof criteria.$match[key] !== "string") {
452
+ criteria.$match[key] = K2DB.sanitiseCriteria({
453
+ [key]: criteria.$match[key],
454
+ })[key];
455
+ }
456
+ else {
457
+ if (key === "$exists") {
458
+ criteria.$match[key] = criteria.$match[key] === "true";
459
+ }
460
+ }
461
+ }
462
+ }
463
+ return criteria;
464
+ }
465
+ /**
466
+ * Optional: Executes a transaction with the provided operations.
467
+ * @param operations - A function that performs operations within a transaction session.
468
+ */
469
+ async executeTransaction(operations) {
470
+ const session = this.connection.startSession();
471
+ session.startTransaction();
472
+ try {
473
+ await operations(session);
474
+ await session.commitTransaction();
475
+ }
476
+ catch (error) {
477
+ await session.abortTransaction();
478
+ throw this.normalizeError(error);
479
+ }
480
+ finally {
481
+ session.endSession();
482
+ }
483
+ }
484
+ /**
485
+ * Optional: Creates an index on the specified collection.
486
+ * @param collectionName - Name of the collection.
487
+ * @param indexSpec - Specification of the index.
488
+ * @param options - Optional index options.
489
+ */
490
+ async createIndex(collectionName, indexSpec, options) {
491
+ const collection = await this.getCollection(collectionName);
492
+ try {
493
+ await collection.createIndex(indexSpec, options);
494
+ debug(`Index created on ${collectionName}: ${JSON.stringify(indexSpec)}`);
495
+ }
496
+ catch (err) {
497
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, `Error creating index on ${collectionName}`, "sys_mdb_idx", this.normalizeError(err));
498
+ }
499
+ }
500
+ /**
501
+ * Releases the MongoDB connection.
502
+ */
503
+ async release() {
504
+ await this.connection.close();
505
+ debug("MongoDB connection released");
506
+ }
507
+ /**
508
+ * Closes the MongoDB connection.
509
+ */
510
+ close() {
511
+ this.connection.close();
512
+ }
513
+ /**
514
+ * Drops the entire database.
515
+ */
516
+ async dropDatabase() {
517
+ try {
518
+ await this.db.dropDatabase();
519
+ debug("Database dropped successfully");
520
+ }
521
+ catch (err) {
522
+ throw new k2error_1.K2Error(k2error_1.ServiceError.SYSTEM_ERROR, "Error dropping database", "sys_mdb_drop_db", this.normalizeError(err));
523
+ }
524
+ }
525
+ /**
526
+ * Validates the MongoDB collection name.
527
+ * @param collectionName - The name of the collection to validate.
528
+ * @throws {K2Error} - If the collection name is invalid.
529
+ */
530
+ validateCollectionName(collectionName) {
531
+ // Check for null character
532
+ if (collectionName.includes("\0")) {
533
+ throw new k2error_1.K2Error(k2error_1.ServiceError.BAD_REQUEST, "Collection name cannot contain null characters", "sys_mdb_invalid_collection_name");
534
+ }
535
+ // Check if it starts with 'system.'
536
+ if (collectionName.startsWith("system.")) {
537
+ throw new k2error_1.K2Error(k2error_1.ServiceError.BAD_REQUEST, "Collection name cannot start with 'system.'", "sys_mdb_invalid_collection_name");
538
+ }
539
+ // Check for invalid characters (e.g., '$')
540
+ if (collectionName.includes("$")) {
541
+ throw new k2error_1.K2Error(k2error_1.ServiceError.BAD_REQUEST, "Collection name cannot contain the '$' character", "sys_mdb_invalid_collection_name");
542
+ }
543
+ // Additional checks can be added here as needed
544
+ }
545
+ /**
546
+ * Optional: Checks the health of the database connection.
547
+ */
548
+ async isHealthy() {
549
+ try {
550
+ await this.db.command({ ping: 1 });
551
+ return true;
552
+ }
553
+ catch {
554
+ return false;
555
+ }
556
+ }
557
+ /**
558
+ * Utility to normalize the error type.
559
+ * @param err - The caught error of type `unknown`.
560
+ * @returns A normalized error of type `Error`.
561
+ */
562
+ normalizeError(err) {
563
+ return err instanceof Error ? err : new Error(String(err));
564
+ }
565
+ }
566
+ exports.K2DB = K2DB;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frogfish/k2db",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "A data handling library for K2 applications.",
5
5
  "main": "data.js",
6
6
  "types": "data.d.ts",
@@ -14,6 +14,8 @@
14
14
  },
15
15
  "files": [
16
16
  "data.d.ts",
17
- "db.d.ts"
17
+ "data.js",
18
+ "db.d.ts",
19
+ "db.js"
18
20
  ]
19
21
  }