nx-mongo 3.8.5 → 3.8.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/src/simpleMongoHelper.ts
CHANGED
|
@@ -1,182 +1,6 @@
|
|
|
1
1
|
import { MongoClient, Db, Collection, Filter, UpdateFilter, OptionalUnlessRequiredId, Document, WithId, ClientSession, Sort, IndexSpecification, CreateIndexesOptions } from 'mongodb';
|
|
2
2
|
import { createHash } from 'crypto';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Log levels ordered from lowest to highest severity
|
|
7
|
-
*/
|
|
8
|
-
type LogLevel = 'verbose' | 'debug' | 'info' | 'warn' | 'error';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Log level numeric values for comparison (lower = more verbose)
|
|
12
|
-
*/
|
|
13
|
-
const LOG_LEVEL_VALUES: Record<LogLevel, number> = {
|
|
14
|
-
verbose: 0,
|
|
15
|
-
debug: 1,
|
|
16
|
-
info: 2,
|
|
17
|
-
warn: 3,
|
|
18
|
-
error: 4,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Console logger with environment-based level control
|
|
23
|
-
*/
|
|
24
|
-
class ConsoleLogger {
|
|
25
|
-
private globalMinLevel: LogLevel;
|
|
26
|
-
private debugNamespaces: Set<string> = new Set();
|
|
27
|
-
private debugPatterns: string[] = [];
|
|
28
|
-
|
|
29
|
-
constructor() {
|
|
30
|
-
// Auto-load config from .env files
|
|
31
|
-
autoLoadConfig();
|
|
32
|
-
|
|
33
|
-
// Get ENV_PREFIX from environment or use default
|
|
34
|
-
const envPrefix = process.env.ENV_PREFIX || '';
|
|
35
|
-
|
|
36
|
-
// Read LOGS_LEVEL from environment
|
|
37
|
-
// Priority: {ENV_PREFIX}_LOG_LEVEL > LOGS_LEVEL > default 'info'
|
|
38
|
-
const logLevelEnv = (envPrefix ? process.env[`${envPrefix}_LOG_LEVEL`] : null) ||
|
|
39
|
-
process.env.LOGS_LEVEL ||
|
|
40
|
-
'info';
|
|
41
|
-
|
|
42
|
-
// Validate and set global minimum level
|
|
43
|
-
if (this.isValidLogLevel(logLevelEnv)) {
|
|
44
|
-
this.globalMinLevel = logLevelEnv as LogLevel;
|
|
45
|
-
} else {
|
|
46
|
-
this.globalMinLevel = 'info';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Parse DEBUG environment variable for namespace matching
|
|
50
|
-
const debugEnv = process.env.DEBUG || '';
|
|
51
|
-
if (debugEnv) {
|
|
52
|
-
const patterns = debugEnv.split(',').map(p => p.trim());
|
|
53
|
-
for (const pattern of patterns) {
|
|
54
|
-
if (pattern === '*') {
|
|
55
|
-
// Wildcard matches everything
|
|
56
|
-
this.debugPatterns.push('*');
|
|
57
|
-
} else {
|
|
58
|
-
// Store pattern for matching
|
|
59
|
-
this.debugPatterns.push(pattern);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Checks if a string is a valid log level
|
|
67
|
-
*/
|
|
68
|
-
private isValidLogLevel(level: string): level is LogLevel {
|
|
69
|
-
return ['verbose', 'debug', 'info', 'warn', 'error'].includes(level);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Checks if a namespace matches any DEBUG pattern
|
|
74
|
-
*/
|
|
75
|
-
private matchesDebugPattern(namespace: string): boolean {
|
|
76
|
-
if (this.debugPatterns.length === 0) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
for (const pattern of this.debugPatterns) {
|
|
81
|
-
if (pattern === '*') {
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Simple wildcard matching: "my-pkg*" matches "my-pkg", "my-pkg:sub", etc.
|
|
86
|
-
if (pattern.endsWith('*')) {
|
|
87
|
-
const prefix = pattern.slice(0, -1);
|
|
88
|
-
if (namespace.startsWith(prefix)) {
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
} else if (pattern === namespace) {
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Determines if a log entry should be printed to console
|
|
101
|
-
*/
|
|
102
|
-
private shouldPrint(level: LogLevel, namespace?: string): boolean {
|
|
103
|
-
const levelValue = LOG_LEVEL_VALUES[level];
|
|
104
|
-
const minLevelValue = LOG_LEVEL_VALUES[this.globalMinLevel];
|
|
105
|
-
|
|
106
|
-
// Rule 1: Print if level >= global minimum level
|
|
107
|
-
if (levelValue >= minLevelValue) {
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Rule 2: Print verbose/debug if namespace matches DEBUG pattern
|
|
112
|
-
if ((level === 'verbose' || level === 'debug') && namespace) {
|
|
113
|
-
if (this.matchesDebugPattern(namespace)) {
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Logs a message to console
|
|
123
|
-
*/
|
|
124
|
-
log(level: LogLevel, message: string, namespace?: string, throwError: boolean = false): void {
|
|
125
|
-
if (!this.shouldPrint(level, namespace)) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Format message with namespace if provided
|
|
130
|
-
const formattedMessage = namespace ? `[${namespace}] ${message}` : message;
|
|
131
|
-
|
|
132
|
-
// Use appropriate console method
|
|
133
|
-
switch (level) {
|
|
134
|
-
case 'verbose':
|
|
135
|
-
case 'debug':
|
|
136
|
-
console.debug(formattedMessage);
|
|
137
|
-
break;
|
|
138
|
-
case 'info':
|
|
139
|
-
console.info(formattedMessage);
|
|
140
|
-
break;
|
|
141
|
-
case 'warn':
|
|
142
|
-
console.warn(formattedMessage);
|
|
143
|
-
break;
|
|
144
|
-
case 'error':
|
|
145
|
-
console.error(formattedMessage);
|
|
146
|
-
// Errors throw by default unless explicitly disabled
|
|
147
|
-
if (throwError !== false) {
|
|
148
|
-
throw new Error(formattedMessage);
|
|
149
|
-
}
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Convenience methods for each log level
|
|
156
|
-
*/
|
|
157
|
-
verbose(message: string, namespace?: string): void {
|
|
158
|
-
this.log('verbose', message, namespace, false);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
debug(message: string, namespace?: string): void {
|
|
162
|
-
this.log('debug', message, namespace, false);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
info(message: string, namespace?: string): void {
|
|
166
|
-
this.log('info', message, namespace, false);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
warn(message: string, namespace?: string): void {
|
|
170
|
-
this.log('warn', message, namespace, false);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
error(message: string, namespace?: string, throwError: boolean = true): void {
|
|
174
|
-
this.log('error', message, namespace, throwError);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Singleton logger instance
|
|
179
|
-
const logger = new ConsoleLogger();
|
|
3
|
+
import { Logger } from 'micro-logs';
|
|
180
4
|
|
|
181
5
|
export interface PaginationOptions {
|
|
182
6
|
page?: number;
|
|
@@ -916,7 +740,7 @@ export class SimpleMongoHelper {
|
|
|
916
740
|
public readonly progress: ProgressAPI;
|
|
917
741
|
private cleanupRegistered: boolean = false;
|
|
918
742
|
private isDisconnecting: boolean = false;
|
|
919
|
-
private
|
|
743
|
+
private logger: Logger;
|
|
920
744
|
|
|
921
745
|
constructor(connectionString: string, retryOptions?: RetryOptions, config?: HelperConfig) {
|
|
922
746
|
// Store original connection string for database name extraction
|
|
@@ -931,6 +755,12 @@ export class SimpleMongoHelper {
|
|
|
931
755
|
this.config = config || null;
|
|
932
756
|
this.progress = new ProgressAPIImpl(this, config?.progress);
|
|
933
757
|
|
|
758
|
+
// Initialize logger with micro-logs
|
|
759
|
+
this.logger = new Logger({
|
|
760
|
+
packageName: 'nx-mongo',
|
|
761
|
+
debugNamespace: 'nx-mongo'
|
|
762
|
+
});
|
|
763
|
+
|
|
934
764
|
// Register automatic cleanup on process exit
|
|
935
765
|
this.registerCleanup();
|
|
936
766
|
}
|
|
@@ -942,7 +772,7 @@ export class SimpleMongoHelper {
|
|
|
942
772
|
*/
|
|
943
773
|
useConfig(config: HelperConfig): this {
|
|
944
774
|
this.config = config;
|
|
945
|
-
logger.debug('Configuration updated'
|
|
775
|
+
this.logger.debug('Configuration updated');
|
|
946
776
|
return this;
|
|
947
777
|
}
|
|
948
778
|
|
|
@@ -952,31 +782,35 @@ export class SimpleMongoHelper {
|
|
|
952
782
|
* @returns This instance for method chaining
|
|
953
783
|
*/
|
|
954
784
|
setNamespace(namespace: string): this {
|
|
955
|
-
|
|
785
|
+
// Update logger with new namespace
|
|
786
|
+
this.logger = new Logger({
|
|
787
|
+
packageName: 'nx-mongo',
|
|
788
|
+
debugNamespace: namespace
|
|
789
|
+
});
|
|
956
790
|
return this;
|
|
957
791
|
}
|
|
958
792
|
|
|
959
793
|
/**
|
|
960
|
-
* Logging methods - delegates to
|
|
794
|
+
* Logging methods - delegates to micro-logs Logger
|
|
961
795
|
*/
|
|
962
|
-
logVerbose(message: string): void {
|
|
963
|
-
logger.verbose(message,
|
|
796
|
+
logVerbose(message: string, data?: any): void {
|
|
797
|
+
this.logger.verbose(message, data);
|
|
964
798
|
}
|
|
965
799
|
|
|
966
|
-
logDebug(message: string): void {
|
|
967
|
-
logger.debug(message,
|
|
800
|
+
logDebug(message: string, data?: any): void {
|
|
801
|
+
this.logger.debug(message, data);
|
|
968
802
|
}
|
|
969
803
|
|
|
970
|
-
logInfo(message: string): void {
|
|
971
|
-
logger.info(message,
|
|
804
|
+
logInfo(message: string, data?: any): void {
|
|
805
|
+
this.logger.info(message, data);
|
|
972
806
|
}
|
|
973
807
|
|
|
974
|
-
logWarn(message: string): void {
|
|
975
|
-
logger.warn(message,
|
|
808
|
+
logWarn(message: string, data?: any): void {
|
|
809
|
+
this.logger.warn(message, data);
|
|
976
810
|
}
|
|
977
811
|
|
|
978
|
-
logError(message: string,
|
|
979
|
-
logger.error(message,
|
|
812
|
+
logError(message: string, data?: any): void {
|
|
813
|
+
this.logger.error(message, data);
|
|
980
814
|
}
|
|
981
815
|
|
|
982
816
|
/**
|
|
@@ -1240,10 +1074,12 @@ export class SimpleMongoHelper {
|
|
|
1240
1074
|
*/
|
|
1241
1075
|
async initialize(options?: { connectionString?: string; databaseName?: string }): Promise<void> {
|
|
1242
1076
|
if (this.isInitialized) {
|
|
1243
|
-
|
|
1077
|
+
const errorMsg = 'SimpleMongoHelper is already initialized';
|
|
1078
|
+
this.logError(errorMsg);
|
|
1079
|
+
throw new Error(errorMsg);
|
|
1244
1080
|
}
|
|
1245
1081
|
|
|
1246
|
-
logger.debug('Initializing MongoDB connection'
|
|
1082
|
+
this.logger.debug('Initializing MongoDB connection');
|
|
1247
1083
|
|
|
1248
1084
|
// Use provided connection string or original (not stripped) connection string
|
|
1249
1085
|
const connectionString = options?.connectionString || this.originalConnectionString;
|
|
@@ -1260,14 +1096,18 @@ export class SimpleMongoHelper {
|
|
|
1260
1096
|
|
|
1261
1097
|
// ✅ Validate database name
|
|
1262
1098
|
if (dbName && !isValidDatabaseName(dbName)) {
|
|
1263
|
-
|
|
1099
|
+
const errorMsg = `Invalid database name: '${dbName}'. Database names cannot contain: / ? & =`;
|
|
1100
|
+
this.logError(errorMsg);
|
|
1101
|
+
throw new Error(errorMsg);
|
|
1264
1102
|
}
|
|
1265
1103
|
|
|
1266
1104
|
if (!dbName) {
|
|
1267
|
-
|
|
1105
|
+
const errorMsg = 'Database name is required. Provide databaseName parameter or include it in connection string path.';
|
|
1106
|
+
this.logError(errorMsg);
|
|
1107
|
+
throw new Error(errorMsg);
|
|
1268
1108
|
}
|
|
1269
1109
|
|
|
1270
|
-
logger.debug(`Using database: ${dbName}
|
|
1110
|
+
this.logger.debug(`Using database: ${dbName}`);
|
|
1271
1111
|
|
|
1272
1112
|
// ✅ Clean connection string if databaseName was provided separately
|
|
1273
1113
|
// (to avoid using database name from URI path when we have explicit databaseName)
|
|
@@ -1278,32 +1118,34 @@ export class SimpleMongoHelper {
|
|
|
1278
1118
|
let lastError: Error | null = null;
|
|
1279
1119
|
for (let attempt = 0; attempt <= this.retryOptions.maxRetries!; attempt++) {
|
|
1280
1120
|
try {
|
|
1281
|
-
logger.verbose(`Connection attempt ${attempt + 1}/${this.retryOptions.maxRetries! + 1}
|
|
1121
|
+
this.logger.verbose(`Connection attempt ${attempt + 1}/${this.retryOptions.maxRetries! + 1}`);
|
|
1282
1122
|
this.client = new MongoClient(cleanUri);
|
|
1283
1123
|
await this.client.connect();
|
|
1284
1124
|
// Use the resolved database name
|
|
1285
1125
|
this.db = this.client.db(dbName);
|
|
1286
1126
|
this.isInitialized = true;
|
|
1287
|
-
logger.info(`Successfully connected to MongoDB database: ${dbName}
|
|
1127
|
+
this.logger.info(`Successfully connected to MongoDB database: ${dbName}`);
|
|
1288
1128
|
return;
|
|
1289
1129
|
} catch (error) {
|
|
1290
1130
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
1291
1131
|
this.client = null;
|
|
1292
1132
|
this.db = null;
|
|
1293
1133
|
|
|
1294
|
-
logger.warn(`Connection attempt ${attempt + 1} failed: ${lastError.message}
|
|
1134
|
+
this.logger.warn(`Connection attempt ${attempt + 1} failed: ${lastError.message}`);
|
|
1295
1135
|
|
|
1296
1136
|
if (attempt < this.retryOptions.maxRetries!) {
|
|
1297
1137
|
const delay = this.retryOptions.exponentialBackoff
|
|
1298
1138
|
? this.retryOptions.retryDelay! * Math.pow(2, attempt)
|
|
1299
1139
|
: this.retryOptions.retryDelay!;
|
|
1300
|
-
logger.debug(`Retrying in ${delay}ms
|
|
1140
|
+
this.logger.debug(`Retrying in ${delay}ms...`);
|
|
1301
1141
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1302
1142
|
}
|
|
1303
1143
|
}
|
|
1304
1144
|
}
|
|
1305
1145
|
|
|
1306
|
-
|
|
1146
|
+
const errorMsg = `Failed to initialize MongoDB connection after ${this.retryOptions.maxRetries! + 1} attempts: ${lastError?.message}`;
|
|
1147
|
+
this.logError(errorMsg);
|
|
1148
|
+
throw new Error(errorMsg);
|
|
1307
1149
|
}
|
|
1308
1150
|
|
|
1309
1151
|
/**
|