express-performance-toolkit 1.0.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.
- package/README.md +217 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +182 -0
- package/dist/cache.js.map +1 -0
- package/dist/compression.d.ts +7 -0
- package/dist/compression.d.ts.map +1 -0
- package/dist/compression.js +26 -0
- package/dist/compression.js.map +1 -0
- package/dist/dashboard/dashboard.html +756 -0
- package/dist/dashboard/dashboardRouter.d.ts +9 -0
- package/dist/dashboard/dashboardRouter.d.ts.map +1 -0
- package/dist/dashboard/dashboardRouter.js +71 -0
- package/dist/dashboard/dashboardRouter.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +130 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +70 -0
- package/dist/logger.js.map +1 -0
- package/dist/queryHelper.d.ts +8 -0
- package/dist/queryHelper.d.ts.map +1 -0
- package/dist/queryHelper.js +39 -0
- package/dist/queryHelper.js.map +1 -0
- package/dist/store.d.ts +24 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +108 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/example/server.ts +126 -0
- package/example/tsconfig.json +17 -0
- package/jest.config.js +10 -0
- package/package.json +57 -0
- package/src/cache.ts +228 -0
- package/src/compression.ts +25 -0
- package/src/dashboard/dashboard.html +756 -0
- package/src/dashboard/dashboardRouter.ts +45 -0
- package/src/index.ts +141 -0
- package/src/logger.ts +83 -0
- package/src/queryHelper.ts +49 -0
- package/src/store.ts +134 -0
- package/src/types.ts +155 -0
- package/tests/cache.test.ts +76 -0
- package/tests/integration.test.ts +124 -0
- package/tests/store.test.ts +103 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { MetricsStore } from '../store';
|
|
3
|
+
import { DashboardOptions } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Create the dashboard Express router.
|
|
6
|
+
* Serves the HTML dashboard and JSON metrics API.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createDashboardRouter(store: MetricsStore, _options?: DashboardOptions): Router;
|
|
9
|
+
//# sourceMappingURL=dashboardRouter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboardRouter.d.ts","sourceRoot":"","sources":["../../src/dashboard/dashboardRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,EACnB,QAAQ,GAAE,gBAAqB,GAC9B,MAAM,CA+BR"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
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 () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createDashboardRouter = createDashboardRouter;
|
|
37
|
+
const express_1 = require("express");
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
/**
|
|
41
|
+
* Create the dashboard Express router.
|
|
42
|
+
* Serves the HTML dashboard and JSON metrics API.
|
|
43
|
+
*/
|
|
44
|
+
function createDashboardRouter(store, _options = {}) {
|
|
45
|
+
const router = (0, express_1.Router)();
|
|
46
|
+
// Cache the HTML file in memory
|
|
47
|
+
const htmlPath = path.join(__dirname, 'dashboard.html');
|
|
48
|
+
let dashboardHtml;
|
|
49
|
+
try {
|
|
50
|
+
dashboardHtml = fs.readFileSync(htmlPath, 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
dashboardHtml = '<h1>Dashboard HTML not found</h1><p>Ensure dashboard.html is in the dist/dashboard/ directory.</p>';
|
|
54
|
+
}
|
|
55
|
+
// Serve dashboard HTML
|
|
56
|
+
router.get('/', (_req, res) => {
|
|
57
|
+
res.set('Content-Type', 'text/html');
|
|
58
|
+
res.send(dashboardHtml);
|
|
59
|
+
});
|
|
60
|
+
// JSON metrics API endpoint
|
|
61
|
+
router.get('/api/metrics', (_req, res) => {
|
|
62
|
+
res.json(store.getMetrics());
|
|
63
|
+
});
|
|
64
|
+
// Reset metrics endpoint
|
|
65
|
+
router.post('/api/reset', (_req, res) => {
|
|
66
|
+
store.reset();
|
|
67
|
+
res.json({ success: true, message: 'Metrics reset' });
|
|
68
|
+
});
|
|
69
|
+
return router;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=dashboardRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboardRouter.js","sourceRoot":"","sources":["../../src/dashboard/dashboardRouter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,sDAkCC;AA5CD,qCAAoD;AACpD,2CAA6B;AAC7B,uCAAyB;AAIzB;;;GAGG;AACH,SAAgB,qBAAqB,CACnC,KAAmB,EACnB,WAA6B,EAAE;IAE/B,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;IAExB,gCAAgC;IAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;IACxD,IAAI,aAAqB,CAAC;IAE1B,IAAI,CAAC;QACH,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,aAAa,GAAG,oGAAoG,CAAC;IACvH,CAAC;IAED,uBAAuB;IACvB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QAC/C,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QACrC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,4BAA4B;IAC5B,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QAC1D,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,yBAAyB;IACzB,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QACzD,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Request, Response, NextFunction, Router } from 'express';
|
|
2
|
+
import { MetricsStore } from './store';
|
|
3
|
+
import { ToolkitOptions, Metrics, CacheMiddleware } from './types';
|
|
4
|
+
export interface ToolkitInstance {
|
|
5
|
+
/** The composed Express middleware */
|
|
6
|
+
middleware: (req: Request, res: Response, next: NextFunction) => void;
|
|
7
|
+
/** The dashboard Express router — mount this if you want the dashboard */
|
|
8
|
+
dashboardRouter: Router;
|
|
9
|
+
/** Get current metrics snapshot */
|
|
10
|
+
getMetrics: () => Metrics;
|
|
11
|
+
/** Reset all metrics */
|
|
12
|
+
resetMetrics: () => void;
|
|
13
|
+
/** Access the cache middleware (for manual cache control) */
|
|
14
|
+
cache: CacheMiddleware | null;
|
|
15
|
+
/** The underlying metrics store */
|
|
16
|
+
store: MetricsStore;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* ⚡ Express Performance Toolkit
|
|
20
|
+
*
|
|
21
|
+
* Creates a composable middleware stack that optimizes your Express app.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { performanceToolkit } from 'express-performance-toolkit';
|
|
26
|
+
*
|
|
27
|
+
* const toolkit = performanceToolkit({
|
|
28
|
+
* cache: true,
|
|
29
|
+
* logSlowRequests: true,
|
|
30
|
+
* dashboard: true,
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* app.use(toolkit.middleware);
|
|
34
|
+
* app.use('/__perf', toolkit.dashboardRouter);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function performanceToolkit(options?: ToolkitOptions): ToolkitInstance;
|
|
38
|
+
export { MetricsStore } from './store';
|
|
39
|
+
export { LRUCache, createCacheMiddleware } from './cache';
|
|
40
|
+
export { createCompressionMiddleware } from './compression';
|
|
41
|
+
export { createLoggerMiddleware } from './logger';
|
|
42
|
+
export { createQueryHelperMiddleware } from './queryHelper';
|
|
43
|
+
export { createDashboardRouter } from './dashboard/dashboardRouter';
|
|
44
|
+
export * from './types';
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAMvC,OAAO,EACL,cAAc,EAMd,OAAO,EACP,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACtE,0EAA0E;IAC1E,eAAe,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,wBAAwB;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,6DAA6D;IAC7D,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,mCAAmC;IACnC,KAAK,EAAE,YAAY,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,cAAmB,GAAG,eAAe,CA+DhF;AAmBD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,cAAc,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.createDashboardRouter = exports.createQueryHelperMiddleware = exports.createLoggerMiddleware = exports.createCompressionMiddleware = exports.createCacheMiddleware = exports.LRUCache = exports.MetricsStore = void 0;
|
|
18
|
+
exports.performanceToolkit = performanceToolkit;
|
|
19
|
+
const store_1 = require("./store");
|
|
20
|
+
const cache_1 = require("./cache");
|
|
21
|
+
const compression_1 = require("./compression");
|
|
22
|
+
const logger_1 = require("./logger");
|
|
23
|
+
const queryHelper_1 = require("./queryHelper");
|
|
24
|
+
const dashboardRouter_1 = require("./dashboard/dashboardRouter");
|
|
25
|
+
/**
|
|
26
|
+
* ⚡ Express Performance Toolkit
|
|
27
|
+
*
|
|
28
|
+
* Creates a composable middleware stack that optimizes your Express app.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { performanceToolkit } from 'express-performance-toolkit';
|
|
33
|
+
*
|
|
34
|
+
* const toolkit = performanceToolkit({
|
|
35
|
+
* cache: true,
|
|
36
|
+
* logSlowRequests: true,
|
|
37
|
+
* dashboard: true,
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* app.use(toolkit.middleware);
|
|
41
|
+
* app.use('/__perf', toolkit.dashboardRouter);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
function performanceToolkit(options = {}) {
|
|
45
|
+
const store = new store_1.MetricsStore({ maxLogs: options.maxLogs || 1000 });
|
|
46
|
+
const middlewares = [];
|
|
47
|
+
let cacheMiddlewareInstance = null;
|
|
48
|
+
// ── Compression ──────────────────────────────────────────
|
|
49
|
+
const compressionConfig = normalizeOption(options.compression, { enabled: true });
|
|
50
|
+
if (compressionConfig.enabled !== false) {
|
|
51
|
+
middlewares.push((0, compression_1.createCompressionMiddleware)(compressionConfig));
|
|
52
|
+
}
|
|
53
|
+
// ── Query Helper ─────────────────────────────────────────
|
|
54
|
+
const queryConfig = normalizeOption(options.queryHelper, { enabled: false });
|
|
55
|
+
if (queryConfig.enabled !== false) {
|
|
56
|
+
middlewares.push((0, queryHelper_1.createQueryHelperMiddleware)(queryConfig));
|
|
57
|
+
}
|
|
58
|
+
// ── Logger (slow request detection) ──────────────────────
|
|
59
|
+
const loggerConfig = normalizeOption(options.logSlowRequests, { enabled: true });
|
|
60
|
+
if (loggerConfig.enabled !== false) {
|
|
61
|
+
middlewares.push((0, logger_1.createLoggerMiddleware)(loggerConfig, store));
|
|
62
|
+
}
|
|
63
|
+
// ── Cache ────────────────────────────────────────────────
|
|
64
|
+
const cacheConfig = normalizeOption(options.cache, { enabled: false });
|
|
65
|
+
if (cacheConfig.enabled !== false) {
|
|
66
|
+
cacheMiddlewareInstance = (0, cache_1.createCacheMiddleware)(cacheConfig, store);
|
|
67
|
+
middlewares.push(cacheMiddlewareInstance);
|
|
68
|
+
}
|
|
69
|
+
// ── Dashboard Router ─────────────────────────────────────
|
|
70
|
+
const dashboardConfig = normalizeOption(options.dashboard, { enabled: true });
|
|
71
|
+
const dashboardPath = dashboardConfig.path || '/__perf';
|
|
72
|
+
const dashboardRouter = (0, dashboardRouter_1.createDashboardRouter)(store, dashboardConfig);
|
|
73
|
+
// ── Composed Middleware ──────────────────────────────────
|
|
74
|
+
function composedMiddleware(req, res, next) {
|
|
75
|
+
let index = 0;
|
|
76
|
+
function runNext(err) {
|
|
77
|
+
if (err)
|
|
78
|
+
return next(err);
|
|
79
|
+
if (index >= middlewares.length)
|
|
80
|
+
return next();
|
|
81
|
+
const current = middlewares[index++];
|
|
82
|
+
try {
|
|
83
|
+
current(req, res, runNext);
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
next(e);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
runNext();
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
middleware: composedMiddleware,
|
|
93
|
+
dashboardRouter,
|
|
94
|
+
getMetrics: () => store.getMetrics(),
|
|
95
|
+
resetMetrics: () => store.reset(),
|
|
96
|
+
cache: cacheMiddlewareInstance,
|
|
97
|
+
store,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Normalize a boolean | object option into a config object.
|
|
102
|
+
* - `true` → defaults with enabled: true
|
|
103
|
+
* - `false` → defaults with enabled: false
|
|
104
|
+
* - object → merged with defaults
|
|
105
|
+
*/
|
|
106
|
+
function normalizeOption(value, defaults) {
|
|
107
|
+
if (value === true)
|
|
108
|
+
return { ...defaults, enabled: true };
|
|
109
|
+
if (value === false)
|
|
110
|
+
return { ...defaults, enabled: false };
|
|
111
|
+
if (typeof value === 'object')
|
|
112
|
+
return { ...defaults, ...value, enabled: true };
|
|
113
|
+
return defaults;
|
|
114
|
+
}
|
|
115
|
+
// ── Re-exports ─────────────────────────────────────────────
|
|
116
|
+
var store_2 = require("./store");
|
|
117
|
+
Object.defineProperty(exports, "MetricsStore", { enumerable: true, get: function () { return store_2.MetricsStore; } });
|
|
118
|
+
var cache_2 = require("./cache");
|
|
119
|
+
Object.defineProperty(exports, "LRUCache", { enumerable: true, get: function () { return cache_2.LRUCache; } });
|
|
120
|
+
Object.defineProperty(exports, "createCacheMiddleware", { enumerable: true, get: function () { return cache_2.createCacheMiddleware; } });
|
|
121
|
+
var compression_2 = require("./compression");
|
|
122
|
+
Object.defineProperty(exports, "createCompressionMiddleware", { enumerable: true, get: function () { return compression_2.createCompressionMiddleware; } });
|
|
123
|
+
var logger_2 = require("./logger");
|
|
124
|
+
Object.defineProperty(exports, "createLoggerMiddleware", { enumerable: true, get: function () { return logger_2.createLoggerMiddleware; } });
|
|
125
|
+
var queryHelper_2 = require("./queryHelper");
|
|
126
|
+
Object.defineProperty(exports, "createQueryHelperMiddleware", { enumerable: true, get: function () { return queryHelper_2.createQueryHelperMiddleware; } });
|
|
127
|
+
var dashboardRouter_2 = require("./dashboard/dashboardRouter");
|
|
128
|
+
Object.defineProperty(exports, "createDashboardRouter", { enumerable: true, get: function () { return dashboardRouter_2.createDashboardRouter; } });
|
|
129
|
+
__exportStar(require("./types"), exports);
|
|
130
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { LoggerOptions } from './types';
|
|
3
|
+
import { MetricsStore } from './store';
|
|
4
|
+
/**
|
|
5
|
+
* Create request logging & slow API detection middleware.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createLoggerMiddleware(options: LoggerOptions | undefined, store: MetricsStore): (req: Request, res: Response, next: NextFunction) => void;
|
|
8
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAY,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAcvC;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,aAAa,YAAK,EAC3B,KAAK,EAAE,YAAY,GAClB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CA2D3D"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createLoggerMiddleware = createLoggerMiddleware;
|
|
7
|
+
const on_finished_1 = __importDefault(require("on-finished"));
|
|
8
|
+
/**
|
|
9
|
+
* Default log formatter for console output.
|
|
10
|
+
*/
|
|
11
|
+
function defaultFormatter(entry) {
|
|
12
|
+
const slow = entry.slow ? ' 🔥 SLOW' : '';
|
|
13
|
+
const cached = entry.cached ? ' [CACHED]' : '';
|
|
14
|
+
const status = entry.statusCode;
|
|
15
|
+
const time = `${entry.responseTime}ms`;
|
|
16
|
+
return `[perf] ${entry.method} ${entry.path} → ${status} ${time}${cached}${slow}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create request logging & slow API detection middleware.
|
|
20
|
+
*/
|
|
21
|
+
function createLoggerMiddleware(options = {}, store) {
|
|
22
|
+
const { slowThreshold = 1000, console: logToConsole = true, formatter = defaultFormatter, } = options;
|
|
23
|
+
return (req, res, next) => {
|
|
24
|
+
const startTime = Date.now();
|
|
25
|
+
// Attach perf data to request
|
|
26
|
+
if (!req.perfToolkit) {
|
|
27
|
+
req.perfToolkit = {
|
|
28
|
+
startTime,
|
|
29
|
+
queryCount: 0,
|
|
30
|
+
trackQuery: (label) => {
|
|
31
|
+
req.perfToolkit.queryCount++;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
(0, on_finished_1.default)(res, (_err, finishedRes) => {
|
|
36
|
+
const responseTime = Date.now() - startTime;
|
|
37
|
+
const isSlow = responseTime >= slowThreshold;
|
|
38
|
+
const entry = {
|
|
39
|
+
method: req.method,
|
|
40
|
+
path: req.originalUrl || req.url,
|
|
41
|
+
statusCode: finishedRes.statusCode,
|
|
42
|
+
responseTime,
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
slow: isSlow,
|
|
45
|
+
cached: res.getHeader('X-Cache') === 'HIT',
|
|
46
|
+
queryCount: req.perfToolkit?.queryCount,
|
|
47
|
+
contentLength: parseInt(res.getHeader('content-length'), 10) || undefined,
|
|
48
|
+
userAgent: req.get('user-agent'),
|
|
49
|
+
ip: req.ip,
|
|
50
|
+
};
|
|
51
|
+
// Record in store
|
|
52
|
+
store.addLog(entry);
|
|
53
|
+
if (isSlow) {
|
|
54
|
+
store.recordSlowRequest();
|
|
55
|
+
}
|
|
56
|
+
// Console output
|
|
57
|
+
if (logToConsole) {
|
|
58
|
+
const message = formatter(entry);
|
|
59
|
+
if (isSlow) {
|
|
60
|
+
console.warn(message);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.log(message);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
next();
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";;;;;AAoBA,wDA8DC;AAjFD,8DAAqC;AAIrC;;GAEG;AACH,SAAS,gBAAgB,CAAC,KAAe;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/C,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC;IAChC,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,YAAY,IAAI,CAAC;IAEvC,OAAO,UAAU,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,MAAM,MAAM,IAAI,IAAI,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC;AACpF,CAAC;AAED;;GAEG;AACH,SAAgB,sBAAsB,CACpC,UAAyB,EAAE,EAC3B,KAAmB;IAEnB,MAAM,EACJ,aAAa,GAAG,IAAI,EACpB,OAAO,EAAE,YAAY,GAAG,IAAI,EAC5B,SAAS,GAAG,gBAAgB,GAC7B,GAAG,OAAO,CAAC;IAEZ,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;QAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,8BAA8B;QAC9B,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YACrB,GAAG,CAAC,WAAW,GAAG;gBAChB,SAAS;gBACT,UAAU,EAAE,CAAC;gBACb,UAAU,EAAE,CAAC,KAAc,EAAE,EAAE;oBAC7B,GAAG,CAAC,WAAY,CAAC,UAAU,EAAE,CAAC;gBAChC,CAAC;aACF,CAAC;QACJ,CAAC;QAED,IAAA,qBAAU,EAAC,GAAG,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE;YACpC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAC5C,MAAM,MAAM,GAAG,YAAY,IAAI,aAAa,CAAC;YAE7C,MAAM,KAAK,GAAa;gBACtB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,IAAI,EAAE,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG;gBAChC,UAAU,EAAE,WAAW,CAAC,UAAU;gBAClC,YAAY;gBACZ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;gBACrB,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,KAAK;gBAC1C,UAAU,EAAE,GAAG,CAAC,WAAW,EAAE,UAAU;gBACvC,aAAa,EAAE,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAW,EAAE,EAAE,CAAC,IAAI,SAAS;gBACnF,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;gBAChC,EAAE,EAAE,GAAG,CAAC,EAAE;aACX,CAAC;YAEF,kBAAkB;YAClB,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAEpB,IAAI,MAAM,EAAE,CAAC;gBACX,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,CAAC;YAED,iBAAiB;YACjB,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;gBACjC,IAAI,MAAM,EAAE,CAAC;oBACX,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACxB,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { QueryHelperOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Create query optimization helper middleware.
|
|
5
|
+
* Tracks database queries per request and warns about potential N+1 issues.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createQueryHelperMiddleware(options?: QueryHelperOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
8
|
+
//# sourceMappingURL=queryHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryHelper.d.ts","sourceRoot":"","sources":["../src/queryHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,kBAAuB,GAC/B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAuC3D"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createQueryHelperMiddleware = createQueryHelperMiddleware;
|
|
4
|
+
/**
|
|
5
|
+
* Create query optimization helper middleware.
|
|
6
|
+
* Tracks database queries per request and warns about potential N+1 issues.
|
|
7
|
+
*/
|
|
8
|
+
function createQueryHelperMiddleware(options = {}) {
|
|
9
|
+
const { threshold = 10 } = options;
|
|
10
|
+
return (req, _res, next) => {
|
|
11
|
+
const queries = [];
|
|
12
|
+
// Initialize or extend perfToolkit on the request
|
|
13
|
+
if (!req.perfToolkit) {
|
|
14
|
+
req.perfToolkit = {
|
|
15
|
+
startTime: Date.now(),
|
|
16
|
+
queryCount: 0,
|
|
17
|
+
trackQuery: () => { },
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
req.perfToolkit.trackQuery = (label) => {
|
|
21
|
+
req.perfToolkit.queryCount++;
|
|
22
|
+
queries.push({
|
|
23
|
+
label: label || `query-${req.perfToolkit.queryCount}`,
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
});
|
|
26
|
+
// Warn if threshold exceeded
|
|
27
|
+
if (req.perfToolkit.queryCount === threshold) {
|
|
28
|
+
console.warn(`[perf] ⚠️ N+1 Alert: ${req.method} ${req.originalUrl || req.url} ` +
|
|
29
|
+
`has made ${threshold}+ queries. Consider optimizing with batch/join queries.`);
|
|
30
|
+
console.warn(`[perf] Recent queries: ${queries
|
|
31
|
+
.slice(-5)
|
|
32
|
+
.map((q) => q.label)
|
|
33
|
+
.join(', ')}`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
next();
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=queryHelper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryHelper.js","sourceRoot":"","sources":["../src/queryHelper.ts"],"names":[],"mappings":";;AAOA,kEAyCC;AA7CD;;;GAGG;AACH,SAAgB,2BAA2B,CACzC,UAA8B,EAAE;IAEhC,MAAM,EAAE,SAAS,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAEnC,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAQ,EAAE;QAChE,MAAM,OAAO,GAA2C,EAAE,CAAC;QAE3D,kDAAkD;QAClD,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YACrB,GAAG,CAAC,WAAW,GAAG;gBAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;gBACrB,UAAU,EAAE,CAAC;gBACb,UAAU,EAAE,GAAG,EAAE,GAAE,CAAC;aACrB,CAAC;QACJ,CAAC;QAED,GAAG,CAAC,WAAW,CAAC,UAAU,GAAG,CAAC,KAAc,EAAQ,EAAE;YACpD,GAAG,CAAC,WAAY,CAAC,UAAU,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC;gBACX,KAAK,EAAE,KAAK,IAAI,SAAS,GAAG,CAAC,WAAY,CAAC,UAAU,EAAE;gBACtD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC,CAAC;YAEH,6BAA6B;YAC7B,IAAI,GAAG,CAAC,WAAY,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC9C,OAAO,CAAC,IAAI,CACV,yBAAyB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,GAAG;oBAClE,YAAY,SAAS,yDAAyD,CACjF,CAAC;gBACF,OAAO,CAAC,IAAI,CACV,4BAA4B,OAAO;qBAChC,KAAK,CAAC,CAAC,CAAC,CAAC;qBACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;qBACnB,IAAI,CAAC,IAAI,CAAC,EAAE,CAChB,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;QAEF,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { LogEntry, Metrics } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* In-memory metrics store — shared state between all middleware components.
|
|
4
|
+
* Uses a ring buffer for request logs and counters for aggregate stats.
|
|
5
|
+
*/
|
|
6
|
+
export declare class MetricsStore {
|
|
7
|
+
private maxLogs;
|
|
8
|
+
private logs;
|
|
9
|
+
private stats;
|
|
10
|
+
constructor(options?: {
|
|
11
|
+
maxLogs?: number;
|
|
12
|
+
});
|
|
13
|
+
/** Add a request log entry to the ring buffer. */
|
|
14
|
+
addLog(entry: LogEntry): void;
|
|
15
|
+
recordSlowRequest(): void;
|
|
16
|
+
recordCacheHit(): void;
|
|
17
|
+
recordCacheMiss(): void;
|
|
18
|
+
setCacheSize(size: number): void;
|
|
19
|
+
/** Get all metrics data for the dashboard. */
|
|
20
|
+
getMetrics(): Metrics;
|
|
21
|
+
/** Reset all metrics. */
|
|
22
|
+
reset(): void;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAc,MAAM,SAAS,CAAC;AAExD;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,KAAK,CAUX;gBAEU,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;IAgB9C,kDAAkD;IAClD,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAsC7B,iBAAiB,IAAI,IAAI;IAIzB,cAAc,IAAI,IAAI;IAItB,eAAe,IAAI,IAAI;IAIvB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIhC,8CAA8C;IAC9C,UAAU,IAAI,OAAO;IA2BrB,yBAAyB;IACzB,KAAK,IAAI,IAAI;CAYd"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MetricsStore = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* In-memory metrics store — shared state between all middleware components.
|
|
6
|
+
* Uses a ring buffer for request logs and counters for aggregate stats.
|
|
7
|
+
*/
|
|
8
|
+
class MetricsStore {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.maxLogs = options.maxLogs || 1000;
|
|
11
|
+
this.logs = [];
|
|
12
|
+
this.stats = {
|
|
13
|
+
totalRequests: 0,
|
|
14
|
+
totalResponseTime: 0,
|
|
15
|
+
slowRequests: 0,
|
|
16
|
+
cacheHits: 0,
|
|
17
|
+
cacheMisses: 0,
|
|
18
|
+
cacheSize: 0,
|
|
19
|
+
statusCodes: {},
|
|
20
|
+
routes: {},
|
|
21
|
+
startTime: Date.now(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Add a request log entry to the ring buffer. */
|
|
25
|
+
addLog(entry) {
|
|
26
|
+
this.logs.push({
|
|
27
|
+
...entry,
|
|
28
|
+
timestamp: entry.timestamp || Date.now(),
|
|
29
|
+
});
|
|
30
|
+
// Ring buffer — drop oldest entries when full
|
|
31
|
+
if (this.logs.length > this.maxLogs) {
|
|
32
|
+
this.logs.shift();
|
|
33
|
+
}
|
|
34
|
+
// Update aggregate stats
|
|
35
|
+
this.stats.totalRequests++;
|
|
36
|
+
this.stats.totalResponseTime += entry.responseTime || 0;
|
|
37
|
+
// Track status codes
|
|
38
|
+
const code = entry.statusCode || 0;
|
|
39
|
+
this.stats.statusCodes[code] = (this.stats.statusCodes[code] || 0) + 1;
|
|
40
|
+
// Track per-route stats
|
|
41
|
+
const routeKey = `${entry.method} ${entry.path}`;
|
|
42
|
+
if (!this.stats.routes[routeKey]) {
|
|
43
|
+
this.stats.routes[routeKey] = {
|
|
44
|
+
count: 0,
|
|
45
|
+
totalTime: 0,
|
|
46
|
+
slowCount: 0,
|
|
47
|
+
avgTime: 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const route = this.stats.routes[routeKey];
|
|
51
|
+
route.count++;
|
|
52
|
+
route.totalTime += entry.responseTime || 0;
|
|
53
|
+
route.avgTime = Math.round(route.totalTime / route.count);
|
|
54
|
+
if (entry.slow) {
|
|
55
|
+
route.slowCount++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
recordSlowRequest() {
|
|
59
|
+
this.stats.slowRequests++;
|
|
60
|
+
}
|
|
61
|
+
recordCacheHit() {
|
|
62
|
+
this.stats.cacheHits++;
|
|
63
|
+
}
|
|
64
|
+
recordCacheMiss() {
|
|
65
|
+
this.stats.cacheMisses++;
|
|
66
|
+
}
|
|
67
|
+
setCacheSize(size) {
|
|
68
|
+
this.stats.cacheSize = size;
|
|
69
|
+
}
|
|
70
|
+
/** Get all metrics data for the dashboard. */
|
|
71
|
+
getMetrics() {
|
|
72
|
+
const avgResponseTime = this.stats.totalRequests > 0
|
|
73
|
+
? Math.round(this.stats.totalResponseTime / this.stats.totalRequests)
|
|
74
|
+
: 0;
|
|
75
|
+
const cacheTotal = this.stats.cacheHits + this.stats.cacheMisses;
|
|
76
|
+
const cacheHitRate = cacheTotal > 0
|
|
77
|
+
? Math.round((this.stats.cacheHits / cacheTotal) * 100)
|
|
78
|
+
: 0;
|
|
79
|
+
return {
|
|
80
|
+
uptime: Date.now() - this.stats.startTime,
|
|
81
|
+
totalRequests: this.stats.totalRequests,
|
|
82
|
+
avgResponseTime,
|
|
83
|
+
slowRequests: this.stats.slowRequests,
|
|
84
|
+
cacheHits: this.stats.cacheHits,
|
|
85
|
+
cacheMisses: this.stats.cacheMisses,
|
|
86
|
+
cacheHitRate,
|
|
87
|
+
cacheSize: this.stats.cacheSize,
|
|
88
|
+
statusCodes: { ...this.stats.statusCodes },
|
|
89
|
+
routes: { ...this.stats.routes },
|
|
90
|
+
recentLogs: this.logs.slice(-100),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Reset all metrics. */
|
|
94
|
+
reset() {
|
|
95
|
+
this.logs = [];
|
|
96
|
+
this.stats.totalRequests = 0;
|
|
97
|
+
this.stats.totalResponseTime = 0;
|
|
98
|
+
this.stats.slowRequests = 0;
|
|
99
|
+
this.stats.cacheHits = 0;
|
|
100
|
+
this.stats.cacheMisses = 0;
|
|
101
|
+
this.stats.cacheSize = 0;
|
|
102
|
+
this.stats.statusCodes = {};
|
|
103
|
+
this.stats.routes = {};
|
|
104
|
+
this.stats.startTime = Date.now();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.MetricsStore = MetricsStore;
|
|
108
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":";;;AAEA;;;GAGG;AACH,MAAa,YAAY;IAevB,YAAY,UAAgC,EAAE;QAC5C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,CAAC;YAChB,iBAAiB,EAAE,CAAC;YACpB,YAAY,EAAE,CAAC;YACf,SAAS,EAAE,CAAC;YACZ,WAAW,EAAE,CAAC;YACd,SAAS,EAAE,CAAC;YACZ,WAAW,EAAE,EAAE;YACf,MAAM,EAAE,EAAE;YACV,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;IACJ,CAAC;IAED,kDAAkD;IAClD,MAAM,CAAC,KAAe;QACpB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YACb,GAAG,KAAK;YACR,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE;SACzC,CAAC,CAAC;QAEH,8CAA8C;QAC9C,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;QAED,yBAAyB;QACzB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,iBAAiB,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC;QAExD,qBAAqB;QACrB,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAEvE,wBAAwB;QACxB,MAAM,QAAQ,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG;gBAC5B,KAAK,EAAE,CAAC;gBACR,SAAS,EAAE,CAAC;gBACZ,SAAS,EAAE,CAAC;gBACZ,OAAO,EAAE,CAAC;aACX,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1C,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC;QAC3C,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1D,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAED,cAAc;QACZ,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;IACzB,CAAC;IAED,eAAe;QACb,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;IAC3B,CAAC;IAED,YAAY,CAAC,IAAY;QACvB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;IAC9B,CAAC;IAED,8CAA8C;IAC9C,UAAU;QACR,MAAM,eAAe,GACnB,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC;YAC1B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;YACrE,CAAC,CAAC,CAAC,CAAC;QAER,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;QACjE,MAAM,YAAY,GAChB,UAAU,GAAG,CAAC;YACZ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC;YACvD,CAAC,CAAC,CAAC,CAAC;QAER,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS;YACzC,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa;YACvC,eAAe;YACf,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;YACrC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS;YAC/B,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW;YACnC,YAAY;YACZ,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS;YAC/B,WAAW,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAC1C,MAAM,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YAChC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC;SAClC,CAAC;IACJ,CAAC;IAED,yBAAyB;IACzB,KAAK;QACH,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACpC,CAAC;CACF;AA/HD,oCA+HC"}
|