@push.rocks/smartmongo 2.0.14 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist_ts/00_commitinfo_data.js +2 -2
  2. package/dist_ts/congodb/congodb.plugins.d.ts +10 -0
  3. package/dist_ts/congodb/congodb.plugins.js +14 -0
  4. package/dist_ts/congodb/engine/AggregationEngine.d.ts +66 -0
  5. package/dist_ts/congodb/engine/AggregationEngine.js +189 -0
  6. package/dist_ts/congodb/engine/IndexEngine.d.ts +77 -0
  7. package/dist_ts/congodb/engine/IndexEngine.js +376 -0
  8. package/dist_ts/congodb/engine/QueryEngine.d.ts +54 -0
  9. package/dist_ts/congodb/engine/QueryEngine.js +271 -0
  10. package/dist_ts/congodb/engine/TransactionEngine.d.ts +85 -0
  11. package/dist_ts/congodb/engine/TransactionEngine.js +287 -0
  12. package/dist_ts/congodb/engine/UpdateEngine.d.ts +47 -0
  13. package/dist_ts/congodb/engine/UpdateEngine.js +461 -0
  14. package/dist_ts/congodb/errors/CongoErrors.d.ts +100 -0
  15. package/dist_ts/congodb/errors/CongoErrors.js +155 -0
  16. package/dist_ts/congodb/index.d.ts +19 -0
  17. package/dist_ts/congodb/index.js +26 -0
  18. package/dist_ts/congodb/server/CommandRouter.d.ts +51 -0
  19. package/dist_ts/congodb/server/CommandRouter.js +132 -0
  20. package/dist_ts/congodb/server/CongoServer.d.ts +95 -0
  21. package/dist_ts/congodb/server/CongoServer.js +227 -0
  22. package/dist_ts/congodb/server/WireProtocol.d.ts +117 -0
  23. package/dist_ts/congodb/server/WireProtocol.js +298 -0
  24. package/dist_ts/congodb/server/handlers/AdminHandler.d.ts +100 -0
  25. package/dist_ts/congodb/server/handlers/AdminHandler.js +568 -0
  26. package/dist_ts/congodb/server/handlers/AggregateHandler.d.ts +31 -0
  27. package/dist_ts/congodb/server/handlers/AggregateHandler.js +277 -0
  28. package/dist_ts/congodb/server/handlers/DeleteHandler.d.ts +8 -0
  29. package/dist_ts/congodb/server/handlers/DeleteHandler.js +83 -0
  30. package/dist_ts/congodb/server/handlers/FindHandler.d.ts +31 -0
  31. package/dist_ts/congodb/server/handlers/FindHandler.js +261 -0
  32. package/dist_ts/congodb/server/handlers/HelloHandler.d.ts +11 -0
  33. package/dist_ts/congodb/server/handlers/HelloHandler.js +62 -0
  34. package/dist_ts/congodb/server/handlers/IndexHandler.d.ts +20 -0
  35. package/dist_ts/congodb/server/handlers/IndexHandler.js +183 -0
  36. package/dist_ts/congodb/server/handlers/InsertHandler.d.ts +8 -0
  37. package/dist_ts/congodb/server/handlers/InsertHandler.js +76 -0
  38. package/dist_ts/congodb/server/handlers/UpdateHandler.d.ts +24 -0
  39. package/dist_ts/congodb/server/handlers/UpdateHandler.js +270 -0
  40. package/dist_ts/congodb/server/handlers/index.d.ts +8 -0
  41. package/dist_ts/congodb/server/handlers/index.js +10 -0
  42. package/dist_ts/congodb/server/index.d.ts +6 -0
  43. package/dist_ts/congodb/server/index.js +7 -0
  44. package/dist_ts/congodb/storage/FileStorageAdapter.d.ts +61 -0
  45. package/dist_ts/congodb/storage/FileStorageAdapter.js +396 -0
  46. package/dist_ts/congodb/storage/IStorageAdapter.d.ts +140 -0
  47. package/dist_ts/congodb/storage/IStorageAdapter.js +2 -0
  48. package/dist_ts/congodb/storage/MemoryStorageAdapter.d.ts +66 -0
  49. package/dist_ts/congodb/storage/MemoryStorageAdapter.js +367 -0
  50. package/dist_ts/congodb/storage/OpLog.d.ts +93 -0
  51. package/dist_ts/congodb/storage/OpLog.js +221 -0
  52. package/dist_ts/congodb/types/interfaces.d.ts +363 -0
  53. package/dist_ts/congodb/types/interfaces.js +2 -0
  54. package/dist_ts/index.d.ts +1 -0
  55. package/dist_ts/index.js +8 -6
  56. package/npmextra.json +17 -7
  57. package/package.json +20 -12
  58. package/readme.hints.md +79 -0
  59. package/ts/00_commitinfo_data.ts +1 -1
  60. package/ts/congodb/congodb.plugins.ts +17 -0
  61. package/ts/congodb/engine/AggregationEngine.ts +283 -0
  62. package/ts/congodb/engine/IndexEngine.ts +479 -0
  63. package/ts/congodb/engine/QueryEngine.ts +301 -0
  64. package/ts/congodb/engine/TransactionEngine.ts +351 -0
  65. package/ts/congodb/engine/UpdateEngine.ts +506 -0
  66. package/ts/congodb/errors/CongoErrors.ts +181 -0
  67. package/ts/congodb/index.ts +37 -0
  68. package/ts/congodb/server/CommandRouter.ts +180 -0
  69. package/ts/congodb/server/CongoServer.ts +298 -0
  70. package/ts/congodb/server/WireProtocol.ts +416 -0
  71. package/ts/congodb/server/handlers/AdminHandler.ts +614 -0
  72. package/ts/congodb/server/handlers/AggregateHandler.ts +342 -0
  73. package/ts/congodb/server/handlers/DeleteHandler.ts +100 -0
  74. package/ts/congodb/server/handlers/FindHandler.ts +301 -0
  75. package/ts/congodb/server/handlers/HelloHandler.ts +78 -0
  76. package/ts/congodb/server/handlers/IndexHandler.ts +207 -0
  77. package/ts/congodb/server/handlers/InsertHandler.ts +91 -0
  78. package/ts/congodb/server/handlers/UpdateHandler.ts +315 -0
  79. package/ts/congodb/server/handlers/index.ts +10 -0
  80. package/ts/congodb/server/index.ts +10 -0
  81. package/ts/congodb/storage/FileStorageAdapter.ts +479 -0
  82. package/ts/congodb/storage/IStorageAdapter.ts +202 -0
  83. package/ts/congodb/storage/MemoryStorageAdapter.ts +443 -0
  84. package/ts/congodb/storage/OpLog.ts +282 -0
  85. package/ts/congodb/types/interfaces.ts +433 -0
  86. package/ts/index.ts +3 -0
@@ -0,0 +1,506 @@
1
+ import * as plugins from '../congodb.plugins.js';
2
+ import type { Document, IStoredDocument } from '../types/interfaces.js';
3
+ import { QueryEngine } from './QueryEngine.js';
4
+
5
+ /**
6
+ * Update engine for MongoDB-compatible update operations
7
+ */
8
+ export class UpdateEngine {
9
+ /**
10
+ * Apply an update specification to a document
11
+ * Returns the updated document or null if no update was applied
12
+ */
13
+ static applyUpdate(document: IStoredDocument, update: Document, arrayFilters?: Document[]): IStoredDocument {
14
+ // Check if this is an aggregation pipeline update
15
+ if (Array.isArray(update)) {
16
+ // Aggregation pipeline updates are not yet supported
17
+ throw new Error('Aggregation pipeline updates are not yet supported');
18
+ }
19
+
20
+ // Check if this is a replacement (no $ operators at top level)
21
+ const hasOperators = Object.keys(update).some(k => k.startsWith('$'));
22
+
23
+ if (!hasOperators) {
24
+ // This is a replacement - preserve _id
25
+ return {
26
+ _id: document._id,
27
+ ...update,
28
+ };
29
+ }
30
+
31
+ // Apply update operators
32
+ const result = this.deepClone(document);
33
+
34
+ for (const [operator, operand] of Object.entries(update)) {
35
+ switch (operator) {
36
+ case '$set':
37
+ this.applySet(result, operand);
38
+ break;
39
+ case '$unset':
40
+ this.applyUnset(result, operand);
41
+ break;
42
+ case '$inc':
43
+ this.applyInc(result, operand);
44
+ break;
45
+ case '$mul':
46
+ this.applyMul(result, operand);
47
+ break;
48
+ case '$min':
49
+ this.applyMin(result, operand);
50
+ break;
51
+ case '$max':
52
+ this.applyMax(result, operand);
53
+ break;
54
+ case '$rename':
55
+ this.applyRename(result, operand);
56
+ break;
57
+ case '$currentDate':
58
+ this.applyCurrentDate(result, operand);
59
+ break;
60
+ case '$setOnInsert':
61
+ // Only applied during upsert insert, handled elsewhere
62
+ break;
63
+ case '$push':
64
+ this.applyPush(result, operand, arrayFilters);
65
+ break;
66
+ case '$pop':
67
+ this.applyPop(result, operand);
68
+ break;
69
+ case '$pull':
70
+ this.applyPull(result, operand, arrayFilters);
71
+ break;
72
+ case '$pullAll':
73
+ this.applyPullAll(result, operand);
74
+ break;
75
+ case '$addToSet':
76
+ this.applyAddToSet(result, operand);
77
+ break;
78
+ case '$bit':
79
+ this.applyBit(result, operand);
80
+ break;
81
+ default:
82
+ throw new Error(`Unknown update operator: ${operator}`);
83
+ }
84
+ }
85
+
86
+ return result;
87
+ }
88
+
89
+ /**
90
+ * Apply $setOnInsert for upsert operations
91
+ */
92
+ static applySetOnInsert(document: IStoredDocument, setOnInsert: Document): IStoredDocument {
93
+ const result = this.deepClone(document);
94
+ this.applySet(result, setOnInsert);
95
+ return result;
96
+ }
97
+
98
+ /**
99
+ * Deep clone a document
100
+ */
101
+ private static deepClone(obj: any): any {
102
+ if (obj === null || typeof obj !== 'object') {
103
+ return obj;
104
+ }
105
+
106
+ if (obj instanceof plugins.bson.ObjectId) {
107
+ return new plugins.bson.ObjectId(obj.toHexString());
108
+ }
109
+
110
+ if (obj instanceof Date) {
111
+ return new Date(obj.getTime());
112
+ }
113
+
114
+ if (obj instanceof plugins.bson.Timestamp) {
115
+ return new plugins.bson.Timestamp({ t: obj.high, i: obj.low });
116
+ }
117
+
118
+ if (Array.isArray(obj)) {
119
+ return obj.map(item => this.deepClone(item));
120
+ }
121
+
122
+ const cloned: any = {};
123
+ for (const key of Object.keys(obj)) {
124
+ cloned[key] = this.deepClone(obj[key]);
125
+ }
126
+ return cloned;
127
+ }
128
+
129
+ /**
130
+ * Set a nested value
131
+ */
132
+ private static setNestedValue(obj: any, path: string, value: any): void {
133
+ const parts = path.split('.');
134
+ let current = obj;
135
+
136
+ for (let i = 0; i < parts.length - 1; i++) {
137
+ const part = parts[i];
138
+
139
+ // Handle array index notation
140
+ const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
141
+ if (arrayMatch) {
142
+ const [, fieldName, indexStr] = arrayMatch;
143
+ const index = parseInt(indexStr, 10);
144
+ if (!(fieldName in current)) {
145
+ current[fieldName] = [];
146
+ }
147
+ if (!current[fieldName][index]) {
148
+ current[fieldName][index] = {};
149
+ }
150
+ current = current[fieldName][index];
151
+ continue;
152
+ }
153
+
154
+ // Handle numeric index (array positional)
155
+ const numIndex = parseInt(part, 10);
156
+ if (!isNaN(numIndex) && Array.isArray(current)) {
157
+ if (!current[numIndex]) {
158
+ current[numIndex] = {};
159
+ }
160
+ current = current[numIndex];
161
+ continue;
162
+ }
163
+
164
+ if (!(part in current) || current[part] === null) {
165
+ current[part] = {};
166
+ }
167
+ current = current[part];
168
+ }
169
+
170
+ const lastPart = parts[parts.length - 1];
171
+ const numIndex = parseInt(lastPart, 10);
172
+ if (!isNaN(numIndex) && Array.isArray(current)) {
173
+ current[numIndex] = value;
174
+ } else {
175
+ current[lastPart] = value;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Get a nested value
181
+ */
182
+ private static getNestedValue(obj: any, path: string): any {
183
+ return QueryEngine.getNestedValue(obj, path);
184
+ }
185
+
186
+ /**
187
+ * Delete a nested value
188
+ */
189
+ private static deleteNestedValue(obj: any, path: string): void {
190
+ const parts = path.split('.');
191
+ let current = obj;
192
+
193
+ for (let i = 0; i < parts.length - 1; i++) {
194
+ const part = parts[i];
195
+ if (!(part in current)) {
196
+ return;
197
+ }
198
+ current = current[part];
199
+ }
200
+
201
+ delete current[parts[parts.length - 1]];
202
+ }
203
+
204
+ // ============================================================================
205
+ // Field Update Operators
206
+ // ============================================================================
207
+
208
+ private static applySet(doc: any, fields: Document): void {
209
+ for (const [path, value] of Object.entries(fields)) {
210
+ this.setNestedValue(doc, path, this.deepClone(value));
211
+ }
212
+ }
213
+
214
+ private static applyUnset(doc: any, fields: Document): void {
215
+ for (const path of Object.keys(fields)) {
216
+ this.deleteNestedValue(doc, path);
217
+ }
218
+ }
219
+
220
+ private static applyInc(doc: any, fields: Document): void {
221
+ for (const [path, value] of Object.entries(fields)) {
222
+ const current = this.getNestedValue(doc, path) || 0;
223
+ if (typeof current !== 'number') {
224
+ throw new Error(`Cannot apply $inc to non-numeric field: ${path}`);
225
+ }
226
+ this.setNestedValue(doc, path, current + (value as number));
227
+ }
228
+ }
229
+
230
+ private static applyMul(doc: any, fields: Document): void {
231
+ for (const [path, value] of Object.entries(fields)) {
232
+ const current = this.getNestedValue(doc, path) || 0;
233
+ if (typeof current !== 'number') {
234
+ throw new Error(`Cannot apply $mul to non-numeric field: ${path}`);
235
+ }
236
+ this.setNestedValue(doc, path, current * (value as number));
237
+ }
238
+ }
239
+
240
+ private static applyMin(doc: any, fields: Document): void {
241
+ for (const [path, value] of Object.entries(fields)) {
242
+ const current = this.getNestedValue(doc, path);
243
+ if (current === undefined || this.compareValues(value, current) < 0) {
244
+ this.setNestedValue(doc, path, this.deepClone(value));
245
+ }
246
+ }
247
+ }
248
+
249
+ private static applyMax(doc: any, fields: Document): void {
250
+ for (const [path, value] of Object.entries(fields)) {
251
+ const current = this.getNestedValue(doc, path);
252
+ if (current === undefined || this.compareValues(value, current) > 0) {
253
+ this.setNestedValue(doc, path, this.deepClone(value));
254
+ }
255
+ }
256
+ }
257
+
258
+ private static applyRename(doc: any, fields: Document): void {
259
+ for (const [oldPath, newPath] of Object.entries(fields)) {
260
+ const value = this.getNestedValue(doc, oldPath);
261
+ if (value !== undefined) {
262
+ this.deleteNestedValue(doc, oldPath);
263
+ this.setNestedValue(doc, newPath as string, value);
264
+ }
265
+ }
266
+ }
267
+
268
+ private static applyCurrentDate(doc: any, fields: Document): void {
269
+ for (const [path, spec] of Object.entries(fields)) {
270
+ if (spec === true) {
271
+ this.setNestedValue(doc, path, new Date());
272
+ } else if (typeof spec === 'object' && spec.$type === 'date') {
273
+ this.setNestedValue(doc, path, new Date());
274
+ } else if (typeof spec === 'object' && spec.$type === 'timestamp') {
275
+ this.setNestedValue(doc, path, new plugins.bson.Timestamp({ t: Math.floor(Date.now() / 1000), i: 0 }));
276
+ }
277
+ }
278
+ }
279
+
280
+ // ============================================================================
281
+ // Array Update Operators
282
+ // ============================================================================
283
+
284
+ private static applyPush(doc: any, fields: Document, arrayFilters?: Document[]): void {
285
+ for (const [path, spec] of Object.entries(fields)) {
286
+ let arr = this.getNestedValue(doc, path);
287
+ if (arr === undefined) {
288
+ arr = [];
289
+ this.setNestedValue(doc, path, arr);
290
+ }
291
+ if (!Array.isArray(arr)) {
292
+ throw new Error(`Cannot apply $push to non-array field: ${path}`);
293
+ }
294
+
295
+ if (spec && typeof spec === 'object' && '$each' in spec) {
296
+ // $push with modifiers
297
+ let elements = (spec.$each as any[]).map(e => this.deepClone(e));
298
+ const position = spec.$position as number | undefined;
299
+ const slice = spec.$slice as number | undefined;
300
+ const sortSpec = spec.$sort;
301
+
302
+ if (position !== undefined) {
303
+ arr.splice(position, 0, ...elements);
304
+ } else {
305
+ arr.push(...elements);
306
+ }
307
+
308
+ if (sortSpec !== undefined) {
309
+ if (typeof sortSpec === 'number') {
310
+ arr.sort((a, b) => (a - b) * sortSpec);
311
+ } else {
312
+ // Sort by field(s)
313
+ const entries = Object.entries(sortSpec as Document);
314
+ arr.sort((a, b) => {
315
+ for (const [field, dir] of entries) {
316
+ const av = this.getNestedValue(a, field);
317
+ const bv = this.getNestedValue(b, field);
318
+ const cmp = this.compareValues(av, bv) * (dir as number);
319
+ if (cmp !== 0) return cmp;
320
+ }
321
+ return 0;
322
+ });
323
+ }
324
+ }
325
+
326
+ if (slice !== undefined) {
327
+ if (slice >= 0) {
328
+ arr.splice(slice);
329
+ } else {
330
+ arr.splice(0, arr.length + slice);
331
+ }
332
+ }
333
+ } else {
334
+ // Simple push
335
+ arr.push(this.deepClone(spec));
336
+ }
337
+ }
338
+ }
339
+
340
+ private static applyPop(doc: any, fields: Document): void {
341
+ for (const [path, direction] of Object.entries(fields)) {
342
+ const arr = this.getNestedValue(doc, path);
343
+ if (!Array.isArray(arr)) {
344
+ throw new Error(`Cannot apply $pop to non-array field: ${path}`);
345
+ }
346
+
347
+ if ((direction as number) === 1) {
348
+ arr.pop();
349
+ } else {
350
+ arr.shift();
351
+ }
352
+ }
353
+ }
354
+
355
+ private static applyPull(doc: any, fields: Document, arrayFilters?: Document[]): void {
356
+ for (const [path, condition] of Object.entries(fields)) {
357
+ const arr = this.getNestedValue(doc, path);
358
+ if (!Array.isArray(arr)) {
359
+ continue; // Skip if not an array
360
+ }
361
+
362
+ if (typeof condition === 'object' && condition !== null && !Array.isArray(condition)) {
363
+ // Condition is a query filter
364
+ const hasOperators = Object.keys(condition).some(k => k.startsWith('$'));
365
+ if (hasOperators) {
366
+ // Filter using query operators
367
+ const remaining = arr.filter(item => !QueryEngine.matches(item, condition));
368
+ arr.length = 0;
369
+ arr.push(...remaining);
370
+ } else {
371
+ // Match documents with all specified fields
372
+ const remaining = arr.filter(item => {
373
+ if (typeof item !== 'object' || item === null) {
374
+ return true;
375
+ }
376
+ return !Object.entries(condition).every(([k, v]) => {
377
+ const itemVal = this.getNestedValue(item, k);
378
+ return this.valuesEqual(itemVal, v);
379
+ });
380
+ });
381
+ arr.length = 0;
382
+ arr.push(...remaining);
383
+ }
384
+ } else {
385
+ // Direct value match
386
+ const remaining = arr.filter(item => !this.valuesEqual(item, condition));
387
+ arr.length = 0;
388
+ arr.push(...remaining);
389
+ }
390
+ }
391
+ }
392
+
393
+ private static applyPullAll(doc: any, fields: Document): void {
394
+ for (const [path, values] of Object.entries(fields)) {
395
+ const arr = this.getNestedValue(doc, path);
396
+ if (!Array.isArray(arr)) {
397
+ continue;
398
+ }
399
+ if (!Array.isArray(values)) {
400
+ throw new Error(`$pullAll requires an array argument`);
401
+ }
402
+
403
+ const valueSet = new Set(values.map(v => JSON.stringify(v)));
404
+ const remaining = arr.filter(item => !valueSet.has(JSON.stringify(item)));
405
+ arr.length = 0;
406
+ arr.push(...remaining);
407
+ }
408
+ }
409
+
410
+ private static applyAddToSet(doc: any, fields: Document): void {
411
+ for (const [path, spec] of Object.entries(fields)) {
412
+ let arr = this.getNestedValue(doc, path);
413
+ if (arr === undefined) {
414
+ arr = [];
415
+ this.setNestedValue(doc, path, arr);
416
+ }
417
+ if (!Array.isArray(arr)) {
418
+ throw new Error(`Cannot apply $addToSet to non-array field: ${path}`);
419
+ }
420
+
421
+ const existingSet = new Set(arr.map(v => JSON.stringify(v)));
422
+
423
+ if (spec && typeof spec === 'object' && '$each' in spec) {
424
+ for (const item of spec.$each as any[]) {
425
+ const key = JSON.stringify(item);
426
+ if (!existingSet.has(key)) {
427
+ arr.push(this.deepClone(item));
428
+ existingSet.add(key);
429
+ }
430
+ }
431
+ } else {
432
+ const key = JSON.stringify(spec);
433
+ if (!existingSet.has(key)) {
434
+ arr.push(this.deepClone(spec));
435
+ }
436
+ }
437
+ }
438
+ }
439
+
440
+ private static applyBit(doc: any, fields: Document): void {
441
+ for (const [path, operations] of Object.entries(fields)) {
442
+ let current = this.getNestedValue(doc, path) || 0;
443
+ if (typeof current !== 'number') {
444
+ throw new Error(`Cannot apply $bit to non-numeric field: ${path}`);
445
+ }
446
+
447
+ for (const [op, value] of Object.entries(operations as Document)) {
448
+ switch (op) {
449
+ case 'and':
450
+ current = current & (value as number);
451
+ break;
452
+ case 'or':
453
+ current = current | (value as number);
454
+ break;
455
+ case 'xor':
456
+ current = current ^ (value as number);
457
+ break;
458
+ }
459
+ }
460
+
461
+ this.setNestedValue(doc, path, current);
462
+ }
463
+ }
464
+
465
+ // ============================================================================
466
+ // Helper Methods
467
+ // ============================================================================
468
+
469
+ private static compareValues(a: any, b: any): number {
470
+ if (a === b) return 0;
471
+ if (a === null || a === undefined) return -1;
472
+ if (b === null || b === undefined) return 1;
473
+
474
+ if (typeof a === 'number' && typeof b === 'number') {
475
+ return a - b;
476
+ }
477
+
478
+ if (a instanceof Date && b instanceof Date) {
479
+ return a.getTime() - b.getTime();
480
+ }
481
+
482
+ if (typeof a === 'string' && typeof b === 'string') {
483
+ return a.localeCompare(b);
484
+ }
485
+
486
+ return String(a).localeCompare(String(b));
487
+ }
488
+
489
+ private static valuesEqual(a: any, b: any): boolean {
490
+ if (a === b) return true;
491
+
492
+ if (a instanceof plugins.bson.ObjectId && b instanceof plugins.bson.ObjectId) {
493
+ return a.equals(b);
494
+ }
495
+
496
+ if (a instanceof Date && b instanceof Date) {
497
+ return a.getTime() === b.getTime();
498
+ }
499
+
500
+ if (typeof a === 'object' && typeof b === 'object' && a !== null && b !== null) {
501
+ return JSON.stringify(a) === JSON.stringify(b);
502
+ }
503
+
504
+ return false;
505
+ }
506
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Base error class for all CongoDB errors
3
+ * Mirrors MongoDB driver error hierarchy
4
+ */
5
+ export class CongoError extends Error {
6
+ public code?: number;
7
+ public codeName?: string;
8
+
9
+ constructor(message: string, code?: number, codeName?: string) {
10
+ super(message);
11
+ this.name = 'CongoError';
12
+ this.code = code;
13
+ this.codeName = codeName;
14
+ Object.setPrototypeOf(this, new.target.prototype);
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Error thrown during connection issues
20
+ */
21
+ export class CongoConnectionError extends CongoError {
22
+ constructor(message: string) {
23
+ super(message);
24
+ this.name = 'CongoConnectionError';
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Error thrown when an operation times out
30
+ */
31
+ export class CongoTimeoutError extends CongoError {
32
+ constructor(message: string) {
33
+ super(message, 50, 'MaxTimeMSExpired');
34
+ this.name = 'CongoTimeoutError';
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Error thrown during write operations
40
+ */
41
+ export class CongoWriteError extends CongoError {
42
+ public writeErrors?: IWriteError[];
43
+ public result?: any;
44
+
45
+ constructor(message: string, code?: number, writeErrors?: IWriteError[]) {
46
+ super(message, code);
47
+ this.name = 'CongoWriteError';
48
+ this.writeErrors = writeErrors;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Error thrown for duplicate key violations
54
+ */
55
+ export class CongoDuplicateKeyError extends CongoWriteError {
56
+ public keyPattern?: Record<string, 1>;
57
+ public keyValue?: Record<string, any>;
58
+
59
+ constructor(message: string, keyPattern?: Record<string, 1>, keyValue?: Record<string, any>) {
60
+ super(message, 11000);
61
+ this.name = 'CongoDuplicateKeyError';
62
+ this.codeName = 'DuplicateKey';
63
+ this.keyPattern = keyPattern;
64
+ this.keyValue = keyValue;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Error thrown for bulk write failures
70
+ */
71
+ export class CongoBulkWriteError extends CongoError {
72
+ public writeErrors: IWriteError[];
73
+ public result: any;
74
+
75
+ constructor(message: string, writeErrors: IWriteError[], result: any) {
76
+ super(message, 65);
77
+ this.name = 'CongoBulkWriteError';
78
+ this.writeErrors = writeErrors;
79
+ this.result = result;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Error thrown during transaction operations
85
+ */
86
+ export class CongoTransactionError extends CongoError {
87
+ constructor(message: string, code?: number) {
88
+ super(message, code);
89
+ this.name = 'CongoTransactionError';
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Error thrown when a transaction is aborted due to conflict
95
+ */
96
+ export class CongoWriteConflictError extends CongoTransactionError {
97
+ constructor(message: string = 'Write conflict during transaction') {
98
+ super(message, 112);
99
+ this.name = 'CongoWriteConflictError';
100
+ this.codeName = 'WriteConflict';
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Error thrown for invalid arguments
106
+ */
107
+ export class CongoArgumentError extends CongoError {
108
+ constructor(message: string) {
109
+ super(message);
110
+ this.name = 'CongoArgumentError';
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Error thrown when an operation is not supported
116
+ */
117
+ export class CongoNotSupportedError extends CongoError {
118
+ constructor(message: string) {
119
+ super(message, 115);
120
+ this.name = 'CongoNotSupportedError';
121
+ this.codeName = 'CommandNotSupported';
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Error thrown when cursor is exhausted or closed
127
+ */
128
+ export class CongoCursorError extends CongoError {
129
+ constructor(message: string) {
130
+ super(message);
131
+ this.name = 'CongoCursorError';
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Error thrown when a namespace (database.collection) is invalid
137
+ */
138
+ export class CongoNamespaceError extends CongoError {
139
+ constructor(message: string) {
140
+ super(message, 73);
141
+ this.name = 'CongoNamespaceError';
142
+ this.codeName = 'InvalidNamespace';
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Error thrown when an index operation fails
148
+ */
149
+ export class CongoIndexError extends CongoError {
150
+ constructor(message: string, code?: number) {
151
+ super(message, code || 86);
152
+ this.name = 'CongoIndexError';
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Write error detail for bulk operations
158
+ */
159
+ export interface IWriteError {
160
+ index: number;
161
+ code: number;
162
+ errmsg: string;
163
+ op: any;
164
+ }
165
+
166
+ /**
167
+ * Convert any error to a CongoError
168
+ */
169
+ export function toCongoError(error: any): CongoError {
170
+ if (error instanceof CongoError) {
171
+ return error;
172
+ }
173
+ const congoError = new CongoError(error.message || String(error));
174
+ if (error.code) {
175
+ congoError.code = error.code;
176
+ }
177
+ if (error.codeName) {
178
+ congoError.codeName = error.codeName;
179
+ }
180
+ return congoError;
181
+ }