@winstonsayno/mcp-gateway 0.0.0-stage → 0.4.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 (118) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/LICENSE +21 -0
  3. package/README.md +432 -2
  4. package/dashboard/README.md +27 -0
  5. package/dashboard/index.html +415 -0
  6. package/dist/auth/middleware.d.ts +24 -0
  7. package/dist/auth/middleware.d.ts.map +1 -0
  8. package/dist/auth/middleware.js +108 -0
  9. package/dist/auth/middleware.js.map +1 -0
  10. package/dist/auth/ratelimit.d.ts +19 -0
  11. package/dist/auth/ratelimit.d.ts.map +1 -0
  12. package/dist/auth/ratelimit.js +81 -0
  13. package/dist/auth/ratelimit.js.map +1 -0
  14. package/dist/cli.d.ts +6 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +129 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/config/loader.d.ts +13 -0
  19. package/dist/config/loader.d.ts.map +1 -0
  20. package/dist/config/loader.js +276 -0
  21. package/dist/config/loader.js.map +1 -0
  22. package/dist/config/watcher.d.ts +29 -0
  23. package/dist/config/watcher.d.ts.map +1 -0
  24. package/dist/config/watcher.js +96 -0
  25. package/dist/config/watcher.js.map +1 -0
  26. package/dist/gateway/api.d.ts +47 -0
  27. package/dist/gateway/api.d.ts.map +1 -0
  28. package/dist/gateway/api.js +324 -0
  29. package/dist/gateway/api.js.map +1 -0
  30. package/dist/gateway/index.d.ts +49 -0
  31. package/dist/gateway/index.d.ts.map +1 -0
  32. package/dist/gateway/index.js +261 -0
  33. package/dist/gateway/index.js.map +1 -0
  34. package/dist/gateway/supervisor.d.ts +58 -0
  35. package/dist/gateway/supervisor.d.ts.map +1 -0
  36. package/dist/gateway/supervisor.js +205 -0
  37. package/dist/gateway/supervisor.js.map +1 -0
  38. package/dist/index.d.ts +18 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +14 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/middleware/cors.d.ts +19 -0
  43. package/dist/middleware/cors.d.ts.map +1 -0
  44. package/dist/middleware/cors.js +65 -0
  45. package/dist/middleware/cors.js.map +1 -0
  46. package/dist/middleware/error-handler.d.ts +40 -0
  47. package/dist/middleware/error-handler.d.ts.map +1 -0
  48. package/dist/middleware/error-handler.js +98 -0
  49. package/dist/middleware/error-handler.js.map +1 -0
  50. package/dist/middleware/request-id.d.ts +23 -0
  51. package/dist/middleware/request-id.d.ts.map +1 -0
  52. package/dist/middleware/request-id.js +30 -0
  53. package/dist/middleware/request-id.js.map +1 -0
  54. package/dist/middleware/timeout.d.ts +12 -0
  55. package/dist/middleware/timeout.d.ts.map +1 -0
  56. package/dist/middleware/timeout.js +39 -0
  57. package/dist/middleware/timeout.js.map +1 -0
  58. package/dist/monitor/index.d.ts +49 -0
  59. package/dist/monitor/index.d.ts.map +1 -0
  60. package/dist/monitor/index.js +204 -0
  61. package/dist/monitor/index.js.map +1 -0
  62. package/dist/proxy/index.d.ts +86 -0
  63. package/dist/proxy/index.d.ts.map +1 -0
  64. package/dist/proxy/index.js +389 -0
  65. package/dist/proxy/index.js.map +1 -0
  66. package/dist/registry/index.d.ts +48 -0
  67. package/dist/registry/index.d.ts.map +1 -0
  68. package/dist/registry/index.js +171 -0
  69. package/dist/registry/index.js.map +1 -0
  70. package/dist/transport/channel.d.ts +61 -0
  71. package/dist/transport/channel.d.ts.map +1 -0
  72. package/dist/transport/channel.js +38 -0
  73. package/dist/transport/channel.js.map +1 -0
  74. package/dist/transport/sse-parser.d.ts +30 -0
  75. package/dist/transport/sse-parser.d.ts.map +1 -0
  76. package/dist/transport/sse-parser.js +107 -0
  77. package/dist/transport/sse-parser.js.map +1 -0
  78. package/dist/transport/sse.d.ts +39 -0
  79. package/dist/transport/sse.d.ts.map +1 -0
  80. package/dist/transport/sse.js +142 -0
  81. package/dist/transport/sse.js.map +1 -0
  82. package/dist/transport/stdio.d.ts +35 -0
  83. package/dist/transport/stdio.d.ts.map +1 -0
  84. package/dist/transport/stdio.js +149 -0
  85. package/dist/transport/stdio.js.map +1 -0
  86. package/dist/transport/streamable-http.d.ts +45 -0
  87. package/dist/transport/streamable-http.d.ts.map +1 -0
  88. package/dist/transport/streamable-http.js +183 -0
  89. package/dist/transport/streamable-http.js.map +1 -0
  90. package/dist/transport/websocket.d.ts +38 -0
  91. package/dist/transport/websocket.d.ts.map +1 -0
  92. package/dist/transport/websocket.js +172 -0
  93. package/dist/transport/websocket.js.map +1 -0
  94. package/dist/utils/logger.d.ts +16 -0
  95. package/dist/utils/logger.d.ts.map +1 -0
  96. package/dist/utils/logger.js +60 -0
  97. package/dist/utils/logger.js.map +1 -0
  98. package/dist/utils/mutex.d.ts +25 -0
  99. package/dist/utils/mutex.d.ts.map +1 -0
  100. package/dist/utils/mutex.js +62 -0
  101. package/dist/utils/mutex.js.map +1 -0
  102. package/dist/utils/semaphore.d.ts +16 -0
  103. package/dist/utils/semaphore.d.ts.map +1 -0
  104. package/dist/utils/semaphore.js +42 -0
  105. package/dist/utils/semaphore.js.map +1 -0
  106. package/dist/utils/tool-filter.d.ts +24 -0
  107. package/dist/utils/tool-filter.d.ts.map +1 -0
  108. package/dist/utils/tool-filter.js +48 -0
  109. package/dist/utils/tool-filter.js.map +1 -0
  110. package/dist/utils/types.d.ts +223 -0
  111. package/dist/utils/types.d.ts.map +1 -0
  112. package/dist/utils/types.js +5 -0
  113. package/dist/utils/types.js.map +1 -0
  114. package/dist/utils/version.d.ts +6 -0
  115. package/dist/utils/version.d.ts.map +1 -0
  116. package/dist/utils/version.js +18 -0
  117. package/dist/utils/version.js.map +1 -0
  118. package/package.json +79 -4
@@ -0,0 +1,65 @@
1
+ /**
2
+ * CORS Middleware
3
+ *
4
+ * Configurable Cross-Origin Resource Sharing headers.
5
+ * Supports wildcard, specific origins list, and regex patterns.
6
+ *
7
+ * @module middleware/cors
8
+ */
9
+ const DEFAULT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
10
+ const DEFAULT_HEADERS = [
11
+ 'Content-Type',
12
+ 'Authorization',
13
+ 'X-API-Key',
14
+ 'X-Request-Id',
15
+ ];
16
+ export function corsMiddleware(options) {
17
+ const { origins, methods = DEFAULT_METHODS, allowedHeaders = DEFAULT_HEADERS, exposedHeaders = ['X-Request-Id', 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-RateLimit-Reset'], credentials = false, maxAge = 86400, } = options;
18
+ const isAllowed = (origin) => {
19
+ for (const o of origins) {
20
+ if (o === '*')
21
+ return true;
22
+ if (o === origin)
23
+ return true;
24
+ // Support regex patterns wrapped in /…/
25
+ if (o.startsWith('/') && o.endsWith('/')) {
26
+ try {
27
+ if (new RegExp(o.slice(1, -1)).test(origin))
28
+ return true;
29
+ }
30
+ catch {
31
+ // invalid regex — skip
32
+ }
33
+ }
34
+ }
35
+ return false;
36
+ };
37
+ return (req, res, next) => {
38
+ const origin = req.headers.origin ?? '';
39
+ const wildcard = origins.includes('*');
40
+ if (wildcard && !credentials) {
41
+ res.setHeader('Access-Control-Allow-Origin', '*');
42
+ }
43
+ else {
44
+ // Response depends on Origin: tell caches so (even when disallowed).
45
+ res.vary('Origin');
46
+ // Browsers reject "*" together with credentials, so reflect the origin.
47
+ if (origin && isAllowed(origin)) {
48
+ res.setHeader('Access-Control-Allow-Origin', origin);
49
+ }
50
+ }
51
+ if (credentials) {
52
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
53
+ }
54
+ res.setHeader('Access-Control-Expose-Headers', exposedHeaders.join(', '));
55
+ if (req.method === 'OPTIONS') {
56
+ res.setHeader('Access-Control-Allow-Methods', methods.join(', '));
57
+ res.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(', '));
58
+ res.setHeader('Access-Control-Max-Age', String(maxAge));
59
+ res.status(204).end();
60
+ return;
61
+ }
62
+ next();
63
+ };
64
+ }
65
+ //# sourceMappingURL=cors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cors.js","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC7E,MAAM,eAAe,GAAG;IACtB,cAAc;IACd,eAAe;IACf,WAAW;IACX,cAAc;CACf,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,OAAoB;IACjD,MAAM,EACJ,OAAO,EACP,OAAO,GAAG,eAAe,EACzB,cAAc,GAAG,eAAe,EAChC,cAAc,GAAG,CAAC,cAAc,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,mBAAmB,CAAC,EACpG,WAAW,GAAG,KAAK,EACnB,MAAM,GAAG,KAAK,GACf,GAAG,OAAO,CAAC;IAEZ,MAAM,SAAS,GAAG,CAAC,MAAc,EAAW,EAAE;QAC5C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,GAAG;gBAAE,OAAO,IAAI,CAAC;YAC3B,IAAI,CAAC,KAAK,MAAM;gBAAE,OAAO,IAAI,CAAC;YAC9B,wCAAwC;YACxC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzC,IAAI,CAAC;oBACH,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;wBAAE,OAAO,IAAI,CAAC;gBAC3D,CAAC;gBAAC,MAAM,CAAC;oBACP,uBAAuB;gBACzB,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;QAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;QAExC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,QAAQ,IAAI,CAAC,WAAW,EAAE,CAAC;YAC7B,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,qEAAqE;YACrE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACnB,wEAAwE;YACxE,IAAI,MAAM,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChC,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,GAAG,CAAC,SAAS,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;QAC5D,CAAC;QAED,GAAG,CAAC,SAAS,CAAC,+BAA+B,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAE1E,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAClE,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACzE,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YACxD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Centralized Error Handler Middleware
3
+ *
4
+ * Bug fix (v0.1.0): unhandled errors leaked raw stack traces and returned
5
+ * inconsistent response shapes. This middleware normalises all errors into
6
+ * a single JSON envelope and never exposes internal stack traces in production.
7
+ *
8
+ * @module middleware/error-handler
9
+ */
10
+ import { Request, Response, NextFunction } from 'express';
11
+ export interface ErrorResponse {
12
+ error: {
13
+ code: string;
14
+ message: string;
15
+ requestId?: string;
16
+ details?: unknown;
17
+ };
18
+ }
19
+ export declare class GatewayError extends Error {
20
+ readonly statusCode: number;
21
+ readonly code: string;
22
+ readonly details?: unknown | undefined;
23
+ constructor(statusCode: number, code: string, message: string, details?: unknown | undefined);
24
+ }
25
+ export declare const ErrorCodes: {
26
+ readonly UNAUTHORIZED: "UNAUTHORIZED";
27
+ readonly FORBIDDEN: "FORBIDDEN";
28
+ readonly NOT_FOUND: "NOT_FOUND";
29
+ readonly TOOL_NOT_FOUND: "TOOL_NOT_FOUND";
30
+ readonly SERVER_NOT_FOUND: "SERVER_NOT_FOUND";
31
+ readonly SERVER_UNAVAILABLE: "SERVER_UNAVAILABLE";
32
+ readonly RATE_LIMITED: "RATE_LIMITED";
33
+ readonly TIMEOUT: "TIMEOUT";
34
+ readonly BAD_REQUEST: "BAD_REQUEST";
35
+ readonly INTERNAL_ERROR: "INTERNAL_ERROR";
36
+ readonly VALIDATION_ERROR: "VALIDATION_ERROR";
37
+ };
38
+ export declare function errorHandler(err: unknown, req: Request, res: Response, _next: NextFunction): void;
39
+ export declare function notFoundHandler(req: Request, res: Response): void;
40
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;CACH;AAED,qBAAa,YAAa,SAAQ,KAAK;aAEnB,UAAU,EAAE,MAAM;aAClB,IAAI,EAAE,MAAM;aAEZ,OAAO,CAAC,EAAE,OAAO;gBAHjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,OAAO,CAAC,EAAE,OAAO,YAAA;CAKpC;AAGD,eAAO,MAAM,UAAU;;;;;;;;;;;;CAYb,CAAC;AAEX,wBAAgB,YAAY,CAC1B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,YAAY,GAClB,IAAI,CAmDN;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CASjE"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Centralized Error Handler Middleware
3
+ *
4
+ * Bug fix (v0.1.0): unhandled errors leaked raw stack traces and returned
5
+ * inconsistent response shapes. This middleware normalises all errors into
6
+ * a single JSON envelope and never exposes internal stack traces in production.
7
+ *
8
+ * @module middleware/error-handler
9
+ */
10
+ export class GatewayError extends Error {
11
+ statusCode;
12
+ code;
13
+ details;
14
+ constructor(statusCode, code, message, details) {
15
+ super(message);
16
+ this.statusCode = statusCode;
17
+ this.code = code;
18
+ this.details = details;
19
+ this.name = 'GatewayError';
20
+ }
21
+ }
22
+ // Well-known error codes
23
+ export const ErrorCodes = {
24
+ UNAUTHORIZED: 'UNAUTHORIZED',
25
+ FORBIDDEN: 'FORBIDDEN',
26
+ NOT_FOUND: 'NOT_FOUND',
27
+ TOOL_NOT_FOUND: 'TOOL_NOT_FOUND',
28
+ SERVER_NOT_FOUND: 'SERVER_NOT_FOUND',
29
+ SERVER_UNAVAILABLE: 'SERVER_UNAVAILABLE',
30
+ RATE_LIMITED: 'RATE_LIMITED',
31
+ TIMEOUT: 'TIMEOUT',
32
+ BAD_REQUEST: 'BAD_REQUEST',
33
+ INTERNAL_ERROR: 'INTERNAL_ERROR',
34
+ VALIDATION_ERROR: 'VALIDATION_ERROR',
35
+ };
36
+ export function errorHandler(err, req, res, _next) {
37
+ const isDev = process.env.NODE_ENV !== 'production';
38
+ // Headers already flushed (e.g. a late timeout): let Express close the socket.
39
+ if (res.headersSent) {
40
+ _next(err);
41
+ return;
42
+ }
43
+ if (err instanceof GatewayError) {
44
+ const body = {
45
+ error: {
46
+ code: err.code,
47
+ message: err.message,
48
+ requestId: req.requestId,
49
+ ...(isDev && err.details ? { details: err.details } : {}),
50
+ },
51
+ };
52
+ res.status(err.statusCode).json(body);
53
+ return;
54
+ }
55
+ // Client errors raised by Express / body-parser (malformed JSON, payload too
56
+ // large, …) carry a 4xx status and must not be reported as 500s.
57
+ const status = getHttpStatus(err);
58
+ if (status !== undefined && status >= 400 && status < 500) {
59
+ const body = {
60
+ error: {
61
+ code: status === 413 ? 'PAYLOAD_TOO_LARGE' : ErrorCodes.BAD_REQUEST,
62
+ message: err instanceof Error ? err.message : 'Bad request',
63
+ requestId: req.requestId,
64
+ },
65
+ };
66
+ res.status(status).json(body);
67
+ return;
68
+ }
69
+ // Unknown / unexpected errors
70
+ const message = isDev && err instanceof Error ? err.message : 'Internal server error';
71
+ const body = {
72
+ error: {
73
+ code: ErrorCodes.INTERNAL_ERROR,
74
+ message,
75
+ requestId: req.requestId,
76
+ ...(isDev && err instanceof Error ? { details: err.stack } : {}),
77
+ },
78
+ };
79
+ res.status(500).json(body);
80
+ }
81
+ export function notFoundHandler(req, res) {
82
+ const body = {
83
+ error: {
84
+ code: ErrorCodes.NOT_FOUND,
85
+ message: `Route ${req.method} ${req.path} not found`,
86
+ requestId: req.requestId,
87
+ },
88
+ };
89
+ res.status(404).json(body);
90
+ }
91
+ function getHttpStatus(err) {
92
+ if (!err || typeof err !== 'object')
93
+ return undefined;
94
+ const e = err;
95
+ const s = typeof e.status === 'number' ? e.status : typeof e.statusCode === 'number' ? e.statusCode : undefined;
96
+ return s !== undefined && Number.isInteger(s) ? s : undefined;
97
+ }
98
+ //# sourceMappingURL=error-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Request ID Middleware
3
+ *
4
+ * Attaches a unique `X-Request-Id` header to every request and response,
5
+ * enabling end-to-end tracing across logs and downstream systems.
6
+ *
7
+ * Priority order:
8
+ * 1. Honour an existing `X-Request-Id` header sent by the client
9
+ * 2. Honour `X-Correlation-Id` (common in enterprise proxies)
10
+ * 3. Generate a new UUID v4
11
+ *
12
+ * @module middleware/request-id
13
+ */
14
+ import { Request, Response, NextFunction } from 'express';
15
+ declare global {
16
+ namespace Express {
17
+ interface Request {
18
+ requestId: string;
19
+ }
20
+ }
21
+ }
22
+ export declare function requestIdMiddleware(req: Request, res: Response, next: NextFunction): void;
23
+ //# sourceMappingURL=request-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-id.d.ts","sourceRoot":"","sources":["../../src/middleware/request-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,OAAO,CAAC,MAAM,CAAC;IAEb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,SAAS,EAAE,MAAM,CAAC;SACnB;KACF;CACF;AAWD,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,IAAI,CASN"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Request ID Middleware
3
+ *
4
+ * Attaches a unique `X-Request-Id` header to every request and response,
5
+ * enabling end-to-end tracing across logs and downstream systems.
6
+ *
7
+ * Priority order:
8
+ * 1. Honour an existing `X-Request-Id` header sent by the client
9
+ * 2. Honour `X-Correlation-Id` (common in enterprise proxies)
10
+ * 3. Generate a new UUID v4
11
+ *
12
+ * @module middleware/request-id
13
+ */
14
+ import { randomUUID } from 'crypto';
15
+ // Client-supplied ids are echoed into headers and logs: accept only a safe,
16
+ // bounded charset to prevent log forging / oversized headers.
17
+ const SAFE_ID = /^[A-Za-z0-9._:\-]{1,128}$/;
18
+ function pick(value) {
19
+ const v = Array.isArray(value) ? value[0] : value;
20
+ return v && SAFE_ID.test(v) ? v : undefined;
21
+ }
22
+ export function requestIdMiddleware(req, res, next) {
23
+ const id = pick(req.headers['x-request-id']) ||
24
+ pick(req.headers['x-correlation-id']) ||
25
+ randomUUID();
26
+ req.requestId = id;
27
+ res.setHeader('X-Request-Id', id);
28
+ next();
29
+ }
30
+ //# sourceMappingURL=request-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-id.js","sourceRoot":"","sources":["../../src/middleware/request-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAWpC,4EAA4E;AAC5E,8DAA8D;AAC9D,MAAM,OAAO,GAAG,2BAA2B,CAAC;AAE5C,SAAS,IAAI,CAAC,KAAoC;IAChD,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAClD,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,GAAY,EACZ,GAAa,EACb,IAAkB;IAElB,MAAM,EAAE,GACN,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QACrC,UAAU,EAAE,CAAC;IAEf,GAAG,CAAC,SAAS,GAAG,EAAE,CAAC;IACnB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAClC,IAAI,EAAE,CAAC;AACT,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Request Timeout Middleware
3
+ *
4
+ * Bug fix (v0.1.0): tool-call requests to slow or unresponsive MCP servers
5
+ * could hang indefinitely, exhausting the Node.js event loop.
6
+ * This middleware enforces a per-request deadline and returns a 504 response.
7
+ *
8
+ * @module middleware/timeout
9
+ */
10
+ import { Request, Response, NextFunction } from 'express';
11
+ export declare function timeoutMiddleware(timeoutMs: number): (req: Request, res: Response, next: NextFunction) => void;
12
+ //# sourceMappingURL=timeout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../src/middleware/timeout.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,IACzC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAiC/D"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Request Timeout Middleware
3
+ *
4
+ * Bug fix (v0.1.0): tool-call requests to slow or unresponsive MCP servers
5
+ * could hang indefinitely, exhausting the Node.js event loop.
6
+ * This middleware enforces a per-request deadline and returns a 504 response.
7
+ *
8
+ * @module middleware/timeout
9
+ */
10
+ import { GatewayError, ErrorCodes } from './error-handler.js';
11
+ export function timeoutMiddleware(timeoutMs) {
12
+ return (req, res, next) => {
13
+ // Skip timeout for SSE streaming endpoints
14
+ if (req.path.endsWith('/stream')) {
15
+ next();
16
+ return;
17
+ }
18
+ let timedOut = false;
19
+ const timer = setTimeout(() => {
20
+ timedOut = true;
21
+ // The handler may have started streaming; nothing safe to send then.
22
+ if (res.headersSent)
23
+ return;
24
+ res.set('Retry-After', String(Math.max(1, Math.ceil(timeoutMs / 1000))));
25
+ next(new GatewayError(504, ErrorCodes.TIMEOUT, `Request timed out after ${timeoutMs}ms`, { path: req.path, method: req.method }));
26
+ }, timeoutMs);
27
+ // Clear the timer once the response is sent
28
+ res.on('finish', () => {
29
+ if (!timedOut)
30
+ clearTimeout(timer);
31
+ });
32
+ res.on('close', () => {
33
+ if (!timedOut)
34
+ clearTimeout(timer);
35
+ });
36
+ next();
37
+ };
38
+ }
39
+ //# sourceMappingURL=timeout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../src/middleware/timeout.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;QAC/D,2CAA2C;QAC3C,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACjC,IAAI,EAAE,CAAC;YACP,OAAO;QACT,CAAC;QAED,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,QAAQ,GAAG,IAAI,CAAC;YAChB,qEAAqE;YACrE,IAAI,GAAG,CAAC,WAAW;gBAAE,OAAO;YAC5B,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACzE,IAAI,CACF,IAAI,YAAY,CACd,GAAG,EACH,UAAU,CAAC,OAAO,EAClB,2BAA2B,SAAS,IAAI,EACxC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CACvC,CACF,CAAC;QACJ,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,4CAA4C;QAC5C,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YACpB,IAAI,CAAC,QAAQ;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,IAAI,CAAC,QAAQ;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Metrics collection and aggregation for mcp-gateway
3
+ *
4
+ * Audit fixes:
5
+ * - Prometheus `*_total` series were the count of the *last minute*, which
6
+ * goes down over time and breaks `rate()`; they are now real monotonic
7
+ * counters kept independently of the retention window.
8
+ * - Label values are escaped per the exposition format.
9
+ * - Percentiles use the nearest-rank method; `requestsPerMinute` without a
10
+ * window is computed from the actual time span instead of request count.
11
+ * - The in-memory log is capped (`maxEntries`) so high traffic within the
12
+ * retention window cannot exhaust memory; cleanup timer is unref()'d.
13
+ */
14
+ import { EventEmitter } from 'events';
15
+ import type { RequestMetric, AggregatedMetrics, MonitorConfig } from '../utils/types.js';
16
+ export interface MetricsOptions {
17
+ /** Hard cap on retained request records (oldest dropped first). Default 100k. */
18
+ maxEntries?: number;
19
+ }
20
+ /** Per-server connection state exported as Prometheus gauges / counters. */
21
+ export interface ServerStateSample {
22
+ id: string;
23
+ status: string;
24
+ /** 1 when connected (online or degraded), else 0. */
25
+ up: number;
26
+ /** Successful automatic reconnects since start (monotonic). */
27
+ reconnects: number;
28
+ /** Consecutive failed reconnect attempts right now. */
29
+ reconnectAttempt: number;
30
+ latencyMs?: number;
31
+ }
32
+ export declare class MetricsCollector extends EventEmitter {
33
+ private metrics;
34
+ private readonly retentionMs;
35
+ private readonly maxEntries;
36
+ private cleanupInterval?;
37
+ private readonly counters;
38
+ constructor(config?: MonitorConfig, options?: MetricsOptions);
39
+ record(metric: Omit<RequestMetric, 'id' | 'timestamp'>): RequestMetric;
40
+ aggregate(windowMs?: number): AggregatedMetrics;
41
+ toPrometheusText(servers?: ServerStateSample[]): string;
42
+ start(): void;
43
+ /** Drop records older than the retention period. */
44
+ prune(now?: number): number;
45
+ stop(): void;
46
+ getRecent(limit?: number): RequestMetric[];
47
+ clear(): void;
48
+ }
49
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/monitor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEzF,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;gBAElD,MAAM,CAAC,EAAE,aAAa,EAAE,OAAO,GAAE,cAAmB;IAQhE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,aAAa;IAwBtE,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,iBAAiB;IAsD/C,gBAAgB,CAAC,OAAO,GAAE,iBAAiB,EAAO,GAAG,MAAM;IAkG3D,KAAK,IAAI,IAAI;IAMb,oDAAoD;IACpD,KAAK,CAAC,GAAG,SAAa,GAAG,MAAM;IAS/B,IAAI,IAAI,IAAI;IAOZ,SAAS,CAAC,KAAK,SAAM,GAAG,aAAa,EAAE;IAKvC,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Metrics collection and aggregation for mcp-gateway
3
+ *
4
+ * Audit fixes:
5
+ * - Prometheus `*_total` series were the count of the *last minute*, which
6
+ * goes down over time and breaks `rate()`; they are now real monotonic
7
+ * counters kept independently of the retention window.
8
+ * - Label values are escaped per the exposition format.
9
+ * - Percentiles use the nearest-rank method; `requestsPerMinute` without a
10
+ * window is computed from the actual time span instead of request count.
11
+ * - The in-memory log is capped (`maxEntries`) so high traffic within the
12
+ * retention window cannot exhaust memory; cleanup timer is unref()'d.
13
+ */
14
+ import { EventEmitter } from 'events';
15
+ import { randomUUID } from 'crypto';
16
+ const SERVER_STATUSES = ['online', 'degraded', 'reconnecting', 'offline', 'unknown'];
17
+ export class MetricsCollector extends EventEmitter {
18
+ metrics = [];
19
+ retentionMs;
20
+ maxEntries;
21
+ cleanupInterval;
22
+ counters = new Map();
23
+ constructor(config, options = {}) {
24
+ super();
25
+ this.retentionMs = (config?.retentionHours ?? 24) * 60 * 60 * 1000;
26
+ this.maxEntries = Math.max(1, options.maxEntries ?? 100_000);
27
+ }
28
+ // ─── Recording ──────────────────────────────────────────────────────────────
29
+ record(metric) {
30
+ const full = {
31
+ ...metric,
32
+ id: randomUUID(),
33
+ timestamp: new Date(),
34
+ };
35
+ this.metrics.push(full);
36
+ // Trim in batches to keep this amortised O(1).
37
+ if (this.metrics.length > this.maxEntries * 1.1) {
38
+ this.metrics.splice(0, this.metrics.length - this.maxEntries);
39
+ }
40
+ const c = this.counters.get(metric.serverId) ?? { requests: 0, errors: 0, durationMsSum: 0 };
41
+ c.requests++;
42
+ if (!metric.success)
43
+ c.errors++;
44
+ c.durationMsSum += metric.durationMs;
45
+ this.counters.set(metric.serverId, c);
46
+ this.emit('metric', full);
47
+ return full;
48
+ }
49
+ // ─── Aggregation ────────────────────────────────────────────────────────────
50
+ aggregate(windowMs) {
51
+ const now = Date.now();
52
+ const validWindow = windowMs !== undefined && Number.isFinite(windowMs) && windowMs > 0;
53
+ const cutoff = validWindow ? now - windowMs : 0;
54
+ const recent = cutoff > 0 ? this.metrics.filter((m) => m.timestamp.getTime() >= cutoff) : this.metrics;
55
+ if (recent.length === 0) {
56
+ return {
57
+ totalRequests: 0,
58
+ successRate: 1,
59
+ avgLatencyMs: 0,
60
+ p95LatencyMs: 0,
61
+ p99LatencyMs: 0,
62
+ requestsPerMinute: 0,
63
+ topTools: [],
64
+ topServers: [],
65
+ errorsByServer: {},
66
+ };
67
+ }
68
+ let successCount = 0;
69
+ let latencySum = 0;
70
+ const latencies = new Array(recent.length);
71
+ const toolCounts = new Map();
72
+ const serverCounts = new Map();
73
+ const errorsByServer = {};
74
+ recent.forEach((m, i) => {
75
+ latencies[i] = m.durationMs;
76
+ latencySum += m.durationMs;
77
+ if (m.success)
78
+ successCount++;
79
+ else
80
+ errorsByServer[m.serverId] = (errorsByServer[m.serverId] ?? 0) + 1;
81
+ toolCounts.set(m.toolName, (toolCounts.get(m.toolName) ?? 0) + 1);
82
+ serverCounts.set(m.serverId, (serverCounts.get(m.serverId) ?? 0) + 1);
83
+ });
84
+ latencies.sort((a, b) => a - b);
85
+ const spanMs = validWindow ? windowMs : Math.max(60_000, now - recent[0].timestamp.getTime());
86
+ return {
87
+ totalRequests: recent.length,
88
+ successRate: successCount / recent.length,
89
+ avgLatencyMs: latencySum / recent.length,
90
+ p95LatencyMs: percentile(latencies, 0.95),
91
+ p99LatencyMs: percentile(latencies, 0.99),
92
+ requestsPerMinute: recent.length / (spanMs / 60_000),
93
+ topTools: topN(toolCounts).map(([name, count]) => ({ name, count })),
94
+ topServers: topN(serverCounts).map(([id, count]) => ({ id, count })),
95
+ errorsByServer,
96
+ };
97
+ }
98
+ // ─── Prometheus Format ──────────────────────────────────────────────────────
99
+ toPrometheusText(servers = []) {
100
+ const agg = this.aggregate(60_000); // gauges over the last minute
101
+ let total = 0;
102
+ let errors = 0;
103
+ for (const c of this.counters.values()) {
104
+ total += c.requests;
105
+ errors += c.errors;
106
+ }
107
+ const lines = [
108
+ '# HELP mcp_gateway_requests_total Total number of tool calls since start',
109
+ '# TYPE mcp_gateway_requests_total counter',
110
+ `mcp_gateway_requests_total ${total}`,
111
+ '# HELP mcp_gateway_errors_total Total number of failed tool calls since start',
112
+ '# TYPE mcp_gateway_errors_total counter',
113
+ `mcp_gateway_errors_total ${errors}`,
114
+ '# HELP mcp_gateway_server_requests_total Tool calls per server since start',
115
+ '# TYPE mcp_gateway_server_requests_total counter',
116
+ ];
117
+ for (const [id, c] of this.counters) {
118
+ lines.push(`mcp_gateway_server_requests_total{server="${escapeLabel(id)}"} ${c.requests}`);
119
+ }
120
+ lines.push('# HELP mcp_gateway_server_errors_total Failed tool calls per server since start', '# TYPE mcp_gateway_server_errors_total counter');
121
+ for (const [id, c] of this.counters) {
122
+ lines.push(`mcp_gateway_server_errors_total{server="${escapeLabel(id)}"} ${c.errors}`);
123
+ }
124
+ lines.push('# HELP mcp_gateway_server_duration_ms_sum Sum of tool call latency per server since start (ms)', '# TYPE mcp_gateway_server_duration_ms_sum counter');
125
+ for (const [id, c] of this.counters) {
126
+ lines.push(`mcp_gateway_server_duration_ms_sum{server="${escapeLabel(id)}"} ${c.durationMsSum}`);
127
+ }
128
+ lines.push('# HELP mcp_gateway_success_rate Request success rate over the last minute (0-1)', '# TYPE mcp_gateway_success_rate gauge', `mcp_gateway_success_rate ${agg.successRate.toFixed(4)}`, '# HELP mcp_gateway_latency_avg_ms Average latency over the last minute (ms)', '# TYPE mcp_gateway_latency_avg_ms gauge', `mcp_gateway_latency_avg_ms ${agg.avgLatencyMs.toFixed(2)}`, '# HELP mcp_gateway_latency_p95_ms P95 latency over the last minute (ms)', '# TYPE mcp_gateway_latency_p95_ms gauge', `mcp_gateway_latency_p95_ms ${agg.p95LatencyMs.toFixed(2)}`, '# HELP mcp_gateway_latency_p99_ms P99 latency over the last minute (ms)', '# TYPE mcp_gateway_latency_p99_ms gauge', `mcp_gateway_latency_p99_ms ${agg.p99LatencyMs.toFixed(2)}`);
129
+ if (servers.length > 0) {
130
+ lines.push('# HELP mcp_gateway_server_up Whether the gateway currently has a session with the server (1/0)', '# TYPE mcp_gateway_server_up gauge');
131
+ for (const s of servers)
132
+ lines.push(`mcp_gateway_server_up{server="${escapeLabel(s.id)}"} ${s.up}`);
133
+ lines.push('# HELP mcp_gateway_server_status Current server status (1 for the active status label)', '# TYPE mcp_gateway_server_status gauge');
134
+ for (const s of servers) {
135
+ for (const st of SERVER_STATUSES) {
136
+ lines.push(`mcp_gateway_server_status{server="${escapeLabel(s.id)}",status="${st}"} ${s.status === st ? 1 : 0}`);
137
+ }
138
+ }
139
+ lines.push('# HELP mcp_gateway_server_reconnects_total Successful automatic reconnects per server since start', '# TYPE mcp_gateway_server_reconnects_total counter');
140
+ for (const s of servers) {
141
+ lines.push(`mcp_gateway_server_reconnects_total{server="${escapeLabel(s.id)}"} ${s.reconnects}`);
142
+ }
143
+ lines.push('# HELP mcp_gateway_server_reconnect_attempt Consecutive failed reconnect attempts (0 when healthy)', '# TYPE mcp_gateway_server_reconnect_attempt gauge');
144
+ for (const s of servers) {
145
+ lines.push(`mcp_gateway_server_reconnect_attempt{server="${escapeLabel(s.id)}"} ${s.reconnectAttempt}`);
146
+ }
147
+ const pinged = servers.filter((s) => typeof s.latencyMs === 'number');
148
+ if (pinged.length > 0) {
149
+ lines.push('# HELP mcp_gateway_server_ping_ms Latency of the last health ping (ms)', '# TYPE mcp_gateway_server_ping_ms gauge');
150
+ for (const s of pinged)
151
+ lines.push(`mcp_gateway_server_ping_ms{server="${escapeLabel(s.id)}"} ${s.latencyMs}`);
152
+ }
153
+ }
154
+ lines.push('');
155
+ return lines.join('\n');
156
+ }
157
+ // ─── Lifecycle ──────────────────────────────────────────────────────────────
158
+ start() {
159
+ this.stop();
160
+ this.cleanupInterval = setInterval(() => this.prune(), 60_000);
161
+ this.cleanupInterval.unref();
162
+ }
163
+ /** Drop records older than the retention period. */
164
+ prune(now = Date.now()) {
165
+ const cutoff = now - this.retentionMs;
166
+ // Records are appended in time order: find the first one to keep.
167
+ let i = 0;
168
+ while (i < this.metrics.length && this.metrics[i].timestamp.getTime() < cutoff)
169
+ i++;
170
+ if (i > 0)
171
+ this.metrics.splice(0, i);
172
+ return i;
173
+ }
174
+ stop() {
175
+ if (this.cleanupInterval) {
176
+ clearInterval(this.cleanupInterval);
177
+ this.cleanupInterval = undefined;
178
+ }
179
+ }
180
+ getRecent(limit = 100) {
181
+ if (!Number.isFinite(limit) || limit <= 0)
182
+ return [];
183
+ return this.metrics.slice(-Math.floor(limit)).reverse();
184
+ }
185
+ clear() {
186
+ this.metrics = [];
187
+ this.counters.clear();
188
+ }
189
+ }
190
+ function percentile(sorted, p) {
191
+ if (sorted.length === 0)
192
+ return 0;
193
+ const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(p * sorted.length) - 1));
194
+ return sorted[idx];
195
+ }
196
+ function topN(counts, n = 10) {
197
+ return Array.from(counts.entries())
198
+ .sort((a, b) => b[1] - a[1])
199
+ .slice(0, n);
200
+ }
201
+ function escapeLabel(v) {
202
+ return v.replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/"/g, '\\"');
203
+ }
204
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/monitor/index.ts"],"names":[],"mappings":"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"}