@heliyos/heliyos-api-core 1.0.35 → 1.0.36

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 (2) hide show
  1. package/dist/mongoose.js +168 -4
  2. package/package.json +1 -1
package/dist/mongoose.js CHANGED
@@ -10,6 +10,18 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
10
10
  if (k2 === undefined) k2 = k;
11
11
  o[k2] = m[k];
12
12
  }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
13
25
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
26
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
27
  };
@@ -22,13 +34,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
22
34
  step((generator = generator.apply(thisArg, _arguments || [])).next());
23
35
  });
24
36
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
37
  Object.defineProperty(exports, "__esModule", { value: true });
29
38
  exports.mongooseConnection = exports.mongoInstance = exports.MongoConnectionManager = void 0;
30
39
  exports.initializeDatabase = initializeDatabase;
31
- const mongoose_1 = __importDefault(require("mongoose"));
40
+ const mongoose_1 = __importStar(require("mongoose"));
32
41
  const logger_1 = require("./logger");
33
42
  // Export commonly used mongoose types
34
43
  __exportStar(require("mongoose"), exports);
@@ -65,6 +74,7 @@ class MongoConnectionManager {
65
74
  retryReads: true,
66
75
  maxConnecting: 1,
67
76
  minHeartbeatFrequencyMS: 2000,
77
+ family: 4,
68
78
  // Add monitoring capability
69
79
  monitorCommands: true,
70
80
  };
@@ -194,3 +204,157 @@ mongoose_1.default.Query.prototype.paginate = function () {
194
204
  }
195
205
  });
196
206
  };
207
+ const ENABLE_METRICS = process.env.MONGO_METRICS === "true";
208
+ const DEBUG_QUERIES = process.env.MONGO_DEBUG === "true";
209
+ const originalExec = mongoose_1.Query.prototype.exec;
210
+ mongoose_1.Query.prototype.exec = function (...args) {
211
+ return __awaiter(this, void 0, void 0, function* () {
212
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
213
+ // Skip metrics collection if disabled
214
+ if (!ENABLE_METRICS) {
215
+ return originalExec.apply(this, args);
216
+ }
217
+ const startTime = Date.now();
218
+ const queryName = this.model.modelName;
219
+ const operation = this.op;
220
+ // Enhanced query details for debugging
221
+ const queryDetails = {
222
+ filter: this.getFilter(),
223
+ sort: this.getSort(),
224
+ limit: this.options.limit,
225
+ skip: this.options.skip,
226
+ select: this.getOptions().select,
227
+ collection: this.model.collection.name,
228
+ operation: this.op,
229
+ options: this.getOptions(),
230
+ };
231
+ // Debug log the query details
232
+ if (DEBUG_QUERIES) {
233
+ logger_1.logger.debug(`MongoDB Query Details [${queryName}:${operation}]:`, {
234
+ timestamp: new Date().toISOString(),
235
+ queryDetails,
236
+ stack: (_a = new Error().stack) === null || _a === void 0 ? void 0 : _a.split("\n").slice(2).map((line) => line.trim()),
237
+ });
238
+ }
239
+ try {
240
+ // Run explain only for read operations and when debugging is enabled
241
+ let explainPromise;
242
+ if (DEBUG_QUERIES &&
243
+ ["find", "findOne", "count", "countDocuments"].includes(operation)) {
244
+ explainPromise = this.model
245
+ .find(queryDetails.filter)
246
+ .explain("executionStats")
247
+ .catch((err) => {
248
+ logger_1.logger.debug("Explain analysis failed:", {
249
+ error: err.message,
250
+ queryName,
251
+ operation,
252
+ });
253
+ return null;
254
+ });
255
+ }
256
+ // Execute original query
257
+ const result = yield originalExec.apply(this, args);
258
+ const executionTimeMs = Date.now() - startTime;
259
+ // Get explain data if available
260
+ const explainData = yield explainPromise;
261
+ const metrics = Object.assign({ queryName,
262
+ operation,
263
+ executionTimeMs, timestamp: new Date().toISOString(), documentCount: Array.isArray(result) ? result.length : 1, queryDetails }, (explainData && {
264
+ explainData: {
265
+ nReturned: (_b = explainData.executionStats) === null || _b === void 0 ? void 0 : _b.nReturned,
266
+ totalKeysExamined: (_c = explainData.executionStats) === null || _c === void 0 ? void 0 : _c.totalKeysExamined,
267
+ totalDocsExamined: (_d = explainData.executionStats) === null || _d === void 0 ? void 0 : _d.totalDocsExamined,
268
+ executionTimeMillis: (_e = explainData.executionStats) === null || _e === void 0 ? void 0 : _e.executionTimeMillis,
269
+ indexesUsed: ((_h = (_g = (_f = explainData.queryPlanner) === null || _f === void 0 ? void 0 : _f.winningPlan) === null || _g === void 0 ? void 0 : _g.inputStage) === null || _h === void 0 ? void 0 : _h.indexName)
270
+ ? [explainData.queryPlanner.winningPlan.inputStage.indexName]
271
+ : [],
272
+ isMultiKey: (_l = (_k = (_j = explainData.queryPlanner) === null || _j === void 0 ? void 0 : _j.winningPlan) === null || _k === void 0 ? void 0 : _k.inputStage) === null || _l === void 0 ? void 0 : _l.isMultiKey,
273
+ inMemorySort: ((_o = (_m = explainData.executionStats) === null || _m === void 0 ? void 0 : _m.executionStages) === null || _o === void 0 ? void 0 : _o.stage) === "SORT",
274
+ queryPlan: DEBUG_QUERIES ? explainData.queryPlanner : undefined,
275
+ },
276
+ }));
277
+ // Analyze query performance and log recommendations
278
+ analyzeQueryPerformance(metrics);
279
+ // Debug log the result summary
280
+ if (DEBUG_QUERIES) {
281
+ logger_1.logger.debug(`MongoDB Query Complete [${queryName}:${operation}]:`, {
282
+ executionTimeMs,
283
+ resultSize: Array.isArray(result) ? result.length : 1,
284
+ resultSample: Array.isArray(result)
285
+ ? result.slice(0, 2).map((doc) => (Object.assign({ _id: doc._id }, doc)))
286
+ : result === null || result === void 0 ? void 0 : result._id,
287
+ });
288
+ }
289
+ return result;
290
+ }
291
+ catch (error) {
292
+ const errorDetails = {
293
+ queryName,
294
+ operation,
295
+ queryDetails,
296
+ error: {
297
+ message: error.message,
298
+ code: error.code,
299
+ name: error.name,
300
+ stack: DEBUG_QUERIES ? error.stack : undefined,
301
+ },
302
+ duration: Date.now() - startTime,
303
+ };
304
+ logger_1.logger.error("Query execution failed:", errorDetails);
305
+ throw error;
306
+ }
307
+ });
308
+ };
309
+ function analyzeQueryPerformance(metrics) {
310
+ const warnings = [];
311
+ const suggestions = [];
312
+ let logLevel = "info";
313
+ // Performance thresholds
314
+ const SLOW_QUERY_THRESHOLD = 100; // ms
315
+ const VERY_SLOW_QUERY_THRESHOLD = 1000; // ms
316
+ const DOCS_EXAMINED_RATIO_THRESHOLD = 3;
317
+ const LARGE_RESULT_SET = 1000;
318
+ // Check execution time
319
+ if (metrics.executionTimeMs > VERY_SLOW_QUERY_THRESHOLD) {
320
+ warnings.push(`Very slow query: ${metrics.executionTimeMs}ms`);
321
+ logLevel = "error";
322
+ }
323
+ else if (metrics.executionTimeMs > SLOW_QUERY_THRESHOLD) {
324
+ warnings.push(`Slow query: ${metrics.executionTimeMs}ms`);
325
+ logLevel = "warn";
326
+ }
327
+ // Analyze explain data if available
328
+ if (metrics.explainData) {
329
+ const { totalDocsExamined, nReturned, indexesUsed, inMemorySort } = metrics.explainData;
330
+ // Check index usage
331
+ if (totalDocsExamined && !(indexesUsed === null || indexesUsed === void 0 ? void 0 : indexesUsed.length)) {
332
+ warnings.push("No indexes used (COLLSCAN)");
333
+ suggestions.push("Consider adding an index for this query pattern");
334
+ }
335
+ // Check documents examined ratio
336
+ if (totalDocsExamined && nReturned) {
337
+ const examineRatio = totalDocsExamined / nReturned;
338
+ if (examineRatio > DOCS_EXAMINED_RATIO_THRESHOLD) {
339
+ warnings.push(`High document examine ratio: ${examineRatio.toFixed(1)}`);
340
+ suggestions.push("Review index coverage or query selectivity");
341
+ }
342
+ }
343
+ // Check for in-memory sorts
344
+ if (inMemorySort) {
345
+ warnings.push("In-memory sort detected");
346
+ suggestions.push("Add index to support sort operation");
347
+ }
348
+ }
349
+ // Check result set size
350
+ if (metrics.documentCount > LARGE_RESULT_SET) {
351
+ warnings.push(`Large result set: ${metrics.documentCount} documents`);
352
+ suggestions.push("Consider implementing pagination");
353
+ }
354
+ // Check for potential limit issues
355
+ if (!metrics.queryDetails.limit && metrics.operation === "find") {
356
+ suggestions.push("Consider adding a limit to the query");
357
+ }
358
+ // Log the analysis
359
+ logger_1.logger[logLevel]("Query Performance Analysis:", Object.assign(Object.assign({}, metrics), { warnings: warnings.length ? warnings : undefined, suggestions: suggestions.length ? suggestions : undefined, performanceCategory: logLevel }));
360
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heliyos/heliyos-api-core",
3
- "version": "1.0.35",
3
+ "version": "1.0.36",
4
4
  "description": "Heliyos's core api functions and middlewares. Its a private package hosted on npm.",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {