@spinajs/telemetry 2.0.482

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 (214) hide show
  1. package/README.md +482 -0
  2. package/lib/cjs/InMemoryPerfSink.d.ts +51 -0
  3. package/lib/cjs/InMemoryPerfSink.d.ts.map +1 -0
  4. package/lib/cjs/InMemoryPerfSink.js +100 -0
  5. package/lib/cjs/InMemoryPerfSink.js.map +1 -0
  6. package/lib/cjs/PromMetricSink.d.ts +26 -0
  7. package/lib/cjs/PromMetricSink.d.ts.map +1 -0
  8. package/lib/cjs/PromMetricSink.js +68 -0
  9. package/lib/cjs/PromMetricSink.js.map +1 -0
  10. package/lib/cjs/bootstrap.d.ts +13 -0
  11. package/lib/cjs/bootstrap.d.ts.map +1 -0
  12. package/lib/cjs/bootstrap.js +57 -0
  13. package/lib/cjs/bootstrap.js.map +1 -0
  14. package/lib/cjs/config/telemetry.d.ts +63 -0
  15. package/lib/cjs/config/telemetry.d.ts.map +1 -0
  16. package/lib/cjs/config/telemetry.js +78 -0
  17. package/lib/cjs/config/telemetry.js.map +1 -0
  18. package/lib/cjs/controllers/Metrics.d.ts +26 -0
  19. package/lib/cjs/controllers/Metrics.d.ts.map +1 -0
  20. package/lib/cjs/controllers/Metrics.js +63 -0
  21. package/lib/cjs/controllers/Metrics.js.map +1 -0
  22. package/lib/cjs/controllers/Telemetry.d.ts +92 -0
  23. package/lib/cjs/controllers/Telemetry.d.ts.map +1 -0
  24. package/lib/cjs/controllers/Telemetry.js +226 -0
  25. package/lib/cjs/controllers/Telemetry.js.map +1 -0
  26. package/lib/cjs/dto/index.d.ts +21 -0
  27. package/lib/cjs/dto/index.d.ts.map +1 -0
  28. package/lib/cjs/dto/index.js +64 -0
  29. package/lib/cjs/dto/index.js.map +1 -0
  30. package/lib/cjs/endpoints.d.ts +32 -0
  31. package/lib/cjs/endpoints.d.ts.map +1 -0
  32. package/lib/cjs/endpoints.js +38 -0
  33. package/lib/cjs/endpoints.js.map +1 -0
  34. package/lib/cjs/health.d.ts +55 -0
  35. package/lib/cjs/health.d.ts.map +1 -0
  36. package/lib/cjs/health.js +111 -0
  37. package/lib/cjs/health.js.map +1 -0
  38. package/lib/cjs/index.d.ts +16 -0
  39. package/lib/cjs/index.d.ts.map +1 -0
  40. package/lib/cjs/index.js +32 -0
  41. package/lib/cjs/index.js.map +1 -0
  42. package/lib/cjs/metrics.d.ts +16 -0
  43. package/lib/cjs/metrics.d.ts.map +1 -0
  44. package/lib/cjs/metrics.js +32 -0
  45. package/lib/cjs/metrics.js.map +1 -0
  46. package/lib/cjs/middleware.d.ts +81 -0
  47. package/lib/cjs/middleware.d.ts.map +1 -0
  48. package/lib/cjs/middleware.js +210 -0
  49. package/lib/cjs/middleware.js.map +1 -0
  50. package/lib/cjs/package.json +1 -0
  51. package/lib/cjs/policies/PublicPolicy.d.ts +16 -0
  52. package/lib/cjs/policies/PublicPolicy.d.ts.map +1 -0
  53. package/lib/cjs/policies/PublicPolicy.js +34 -0
  54. package/lib/cjs/policies/PublicPolicy.js.map +1 -0
  55. package/lib/cjs/policies/TelemetryTokenPolicy.d.ts +19 -0
  56. package/lib/cjs/policies/TelemetryTokenPolicy.d.ts.map +1 -0
  57. package/lib/cjs/policies/TelemetryTokenPolicy.js +65 -0
  58. package/lib/cjs/policies/TelemetryTokenPolicy.js.map +1 -0
  59. package/lib/cjs/requestStats.d.ts +81 -0
  60. package/lib/cjs/requestStats.d.ts.map +1 -0
  61. package/lib/cjs/requestStats.js +134 -0
  62. package/lib/cjs/requestStats.js.map +1 -0
  63. package/lib/cjs/responses.d.ts +23 -0
  64. package/lib/cjs/responses.d.ts.map +1 -0
  65. package/lib/cjs/responses.js +35 -0
  66. package/lib/cjs/responses.js.map +1 -0
  67. package/lib/cjs/routeStats.d.ts +43 -0
  68. package/lib/cjs/routeStats.d.ts.map +1 -0
  69. package/lib/cjs/routeStats.js +59 -0
  70. package/lib/cjs/routeStats.js.map +1 -0
  71. package/lib/cjs/schemas/HealthResponse.schema.d.ts +30 -0
  72. package/lib/cjs/schemas/HealthResponse.schema.d.ts.map +1 -0
  73. package/lib/cjs/schemas/HealthResponse.schema.js +15 -0
  74. package/lib/cjs/schemas/HealthResponse.schema.js.map +1 -0
  75. package/lib/cjs/schemas/PerfResponse.schema.d.ts +56 -0
  76. package/lib/cjs/schemas/PerfResponse.schema.d.ts.map +1 -0
  77. package/lib/cjs/schemas/PerfResponse.schema.js +36 -0
  78. package/lib/cjs/schemas/PerfResponse.schema.js.map +1 -0
  79. package/lib/cjs/schemas/ReadyResponse.schema.d.ts +36 -0
  80. package/lib/cjs/schemas/ReadyResponse.schema.d.ts.map +1 -0
  81. package/lib/cjs/schemas/ReadyResponse.schema.js +23 -0
  82. package/lib/cjs/schemas/ReadyResponse.schema.js.map +1 -0
  83. package/lib/cjs/schemas/RequestStatsSnapshot.schema.d.ts +79 -0
  84. package/lib/cjs/schemas/RequestStatsSnapshot.schema.d.ts.map +1 -0
  85. package/lib/cjs/schemas/RequestStatsSnapshot.schema.js +30 -0
  86. package/lib/cjs/schemas/RequestStatsSnapshot.schema.js.map +1 -0
  87. package/lib/cjs/schemas/RoutesResponse.schema.d.ts +100 -0
  88. package/lib/cjs/schemas/RoutesResponse.schema.d.ts.map +1 -0
  89. package/lib/cjs/schemas/RoutesResponse.schema.js +25 -0
  90. package/lib/cjs/schemas/RoutesResponse.schema.js.map +1 -0
  91. package/lib/cjs/schemas/StatsResponse.schema.d.ts +158 -0
  92. package/lib/cjs/schemas/StatsResponse.schema.d.ts.map +1 -0
  93. package/lib/cjs/schemas/StatsResponse.schema.js +19 -0
  94. package/lib/cjs/schemas/StatsResponse.schema.js.map +1 -0
  95. package/lib/cjs/schemas/TimelineResponse.schema.d.ts +109 -0
  96. package/lib/cjs/schemas/TimelineResponse.schema.d.ts.map +1 -0
  97. package/lib/cjs/schemas/TimelineResponse.schema.js +28 -0
  98. package/lib/cjs/schemas/TimelineResponse.schema.js.map +1 -0
  99. package/lib/cjs/store.d.ts +58 -0
  100. package/lib/cjs/store.d.ts.map +1 -0
  101. package/lib/cjs/store.js +108 -0
  102. package/lib/cjs/store.js.map +1 -0
  103. package/lib/cjs/timeline.d.ts +48 -0
  104. package/lib/cjs/timeline.d.ts.map +1 -0
  105. package/lib/cjs/timeline.js +70 -0
  106. package/lib/cjs/timeline.js.map +1 -0
  107. package/lib/mjs/InMemoryPerfSink.d.ts +51 -0
  108. package/lib/mjs/InMemoryPerfSink.d.ts.map +1 -0
  109. package/lib/mjs/InMemoryPerfSink.js +97 -0
  110. package/lib/mjs/InMemoryPerfSink.js.map +1 -0
  111. package/lib/mjs/PromMetricSink.d.ts +26 -0
  112. package/lib/mjs/PromMetricSink.d.ts.map +1 -0
  113. package/lib/mjs/PromMetricSink.js +65 -0
  114. package/lib/mjs/PromMetricSink.js.map +1 -0
  115. package/lib/mjs/bootstrap.d.ts +13 -0
  116. package/lib/mjs/bootstrap.d.ts.map +1 -0
  117. package/lib/mjs/bootstrap.js +54 -0
  118. package/lib/mjs/bootstrap.js.map +1 -0
  119. package/lib/mjs/config/telemetry.d.ts +63 -0
  120. package/lib/mjs/config/telemetry.d.ts.map +1 -0
  121. package/lib/mjs/config/telemetry.js +76 -0
  122. package/lib/mjs/config/telemetry.js.map +1 -0
  123. package/lib/mjs/controllers/Metrics.d.ts +26 -0
  124. package/lib/mjs/controllers/Metrics.d.ts.map +1 -0
  125. package/lib/mjs/controllers/Metrics.js +60 -0
  126. package/lib/mjs/controllers/Metrics.js.map +1 -0
  127. package/lib/mjs/controllers/Telemetry.d.ts +92 -0
  128. package/lib/mjs/controllers/Telemetry.d.ts.map +1 -0
  129. package/lib/mjs/controllers/Telemetry.js +223 -0
  130. package/lib/mjs/controllers/Telemetry.js.map +1 -0
  131. package/lib/mjs/dto/index.d.ts +21 -0
  132. package/lib/mjs/dto/index.d.ts.map +1 -0
  133. package/lib/mjs/dto/index.js +58 -0
  134. package/lib/mjs/dto/index.js.map +1 -0
  135. package/lib/mjs/endpoints.d.ts +32 -0
  136. package/lib/mjs/endpoints.d.ts.map +1 -0
  137. package/lib/mjs/endpoints.js +34 -0
  138. package/lib/mjs/endpoints.js.map +1 -0
  139. package/lib/mjs/health.d.ts +55 -0
  140. package/lib/mjs/health.d.ts.map +1 -0
  141. package/lib/mjs/health.js +107 -0
  142. package/lib/mjs/health.js.map +1 -0
  143. package/lib/mjs/index.d.ts +16 -0
  144. package/lib/mjs/index.d.ts.map +1 -0
  145. package/lib/mjs/index.js +16 -0
  146. package/lib/mjs/index.js.map +1 -0
  147. package/lib/mjs/metrics.d.ts +16 -0
  148. package/lib/mjs/metrics.d.ts.map +1 -0
  149. package/lib/mjs/metrics.js +16 -0
  150. package/lib/mjs/metrics.js.map +1 -0
  151. package/lib/mjs/middleware.d.ts +81 -0
  152. package/lib/mjs/middleware.d.ts.map +1 -0
  153. package/lib/mjs/middleware.js +207 -0
  154. package/lib/mjs/middleware.js.map +1 -0
  155. package/lib/mjs/package.json +1 -0
  156. package/lib/mjs/policies/PublicPolicy.d.ts +16 -0
  157. package/lib/mjs/policies/PublicPolicy.d.ts.map +1 -0
  158. package/lib/mjs/policies/PublicPolicy.js +31 -0
  159. package/lib/mjs/policies/PublicPolicy.js.map +1 -0
  160. package/lib/mjs/policies/TelemetryTokenPolicy.d.ts +19 -0
  161. package/lib/mjs/policies/TelemetryTokenPolicy.d.ts.map +1 -0
  162. package/lib/mjs/policies/TelemetryTokenPolicy.js +62 -0
  163. package/lib/mjs/policies/TelemetryTokenPolicy.js.map +1 -0
  164. package/lib/mjs/requestStats.d.ts +81 -0
  165. package/lib/mjs/requestStats.d.ts.map +1 -0
  166. package/lib/mjs/requestStats.js +130 -0
  167. package/lib/mjs/requestStats.js.map +1 -0
  168. package/lib/mjs/responses.d.ts +23 -0
  169. package/lib/mjs/responses.d.ts.map +1 -0
  170. package/lib/mjs/responses.js +30 -0
  171. package/lib/mjs/responses.js.map +1 -0
  172. package/lib/mjs/routeStats.d.ts +43 -0
  173. package/lib/mjs/routeStats.d.ts.map +1 -0
  174. package/lib/mjs/routeStats.js +55 -0
  175. package/lib/mjs/routeStats.js.map +1 -0
  176. package/lib/mjs/schemas/HealthResponse.schema.d.ts +30 -0
  177. package/lib/mjs/schemas/HealthResponse.schema.d.ts.map +1 -0
  178. package/lib/mjs/schemas/HealthResponse.schema.js +13 -0
  179. package/lib/mjs/schemas/HealthResponse.schema.js.map +1 -0
  180. package/lib/mjs/schemas/PerfResponse.schema.d.ts +56 -0
  181. package/lib/mjs/schemas/PerfResponse.schema.d.ts.map +1 -0
  182. package/lib/mjs/schemas/PerfResponse.schema.js +34 -0
  183. package/lib/mjs/schemas/PerfResponse.schema.js.map +1 -0
  184. package/lib/mjs/schemas/ReadyResponse.schema.d.ts +36 -0
  185. package/lib/mjs/schemas/ReadyResponse.schema.d.ts.map +1 -0
  186. package/lib/mjs/schemas/ReadyResponse.schema.js +21 -0
  187. package/lib/mjs/schemas/ReadyResponse.schema.js.map +1 -0
  188. package/lib/mjs/schemas/RequestStatsSnapshot.schema.d.ts +79 -0
  189. package/lib/mjs/schemas/RequestStatsSnapshot.schema.d.ts.map +1 -0
  190. package/lib/mjs/schemas/RequestStatsSnapshot.schema.js +28 -0
  191. package/lib/mjs/schemas/RequestStatsSnapshot.schema.js.map +1 -0
  192. package/lib/mjs/schemas/RoutesResponse.schema.d.ts +100 -0
  193. package/lib/mjs/schemas/RoutesResponse.schema.d.ts.map +1 -0
  194. package/lib/mjs/schemas/RoutesResponse.schema.js +20 -0
  195. package/lib/mjs/schemas/RoutesResponse.schema.js.map +1 -0
  196. package/lib/mjs/schemas/StatsResponse.schema.d.ts +158 -0
  197. package/lib/mjs/schemas/StatsResponse.schema.d.ts.map +1 -0
  198. package/lib/mjs/schemas/StatsResponse.schema.js +14 -0
  199. package/lib/mjs/schemas/StatsResponse.schema.js.map +1 -0
  200. package/lib/mjs/schemas/TimelineResponse.schema.d.ts +109 -0
  201. package/lib/mjs/schemas/TimelineResponse.schema.d.ts.map +1 -0
  202. package/lib/mjs/schemas/TimelineResponse.schema.js +23 -0
  203. package/lib/mjs/schemas/TimelineResponse.schema.js.map +1 -0
  204. package/lib/mjs/store.d.ts +58 -0
  205. package/lib/mjs/store.d.ts.map +1 -0
  206. package/lib/mjs/store.js +105 -0
  207. package/lib/mjs/store.js.map +1 -0
  208. package/lib/mjs/timeline.d.ts +48 -0
  209. package/lib/mjs/timeline.d.ts.map +1 -0
  210. package/lib/mjs/timeline.js +66 -0
  211. package/lib/mjs/timeline.js.map +1 -0
  212. package/lib/tsconfig.cjs.tsbuildinfo +1 -0
  213. package/lib/tsconfig.mjs.tsbuildinfo +1 -0
  214. package/package.json +69 -0
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The metric abstraction lives in `@spinajs/telemetry-common`, which depends on nothing but
3
+ * `@spinajs/di` and prom-client.
4
+ *
5
+ * It was extracted for one reason: `@spinajs/orm` publishes connection-pool telemetry, and this
6
+ * package depends on `@spinajs/http`, `@spinajs/log`, `@spinajs/validation` and
7
+ * `@spinajs/configuration`. An ORM that depended on `@spinajs/telemetry` would put the whole HTTP
8
+ * stack underneath every database connection. Same reason `@spinajs/configuration-common` and
9
+ * `@spinajs/log-common` exist.
10
+ *
11
+ * Re-exported here so `import { Metrics } from '@spinajs/telemetry'` keeps working, and so both
12
+ * packages share ONE `Metrics` class identity — DI resolves the singleton by that identity, and a
13
+ * second copy would mean a second, separate registry.
14
+ */
15
+ export * from '@spinajs/telemetry-common';
16
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,32 @@
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
+ /**
18
+ * The metric abstraction lives in `@spinajs/telemetry-common`, which depends on nothing but
19
+ * `@spinajs/di` and prom-client.
20
+ *
21
+ * It was extracted for one reason: `@spinajs/orm` publishes connection-pool telemetry, and this
22
+ * package depends on `@spinajs/http`, `@spinajs/log`, `@spinajs/validation` and
23
+ * `@spinajs/configuration`. An ORM that depended on `@spinajs/telemetry` would put the whole HTTP
24
+ * stack underneath every database connection. Same reason `@spinajs/configuration-common` and
25
+ * `@spinajs/log-common` exist.
26
+ *
27
+ * Re-exported here so `import { Metrics } from '@spinajs/telemetry'` keeps working, and so both
28
+ * packages share ONE `Metrics` class identity — DI resolves the singleton by that identity, and a
29
+ * second copy would mean a second, separate registry.
30
+ */
31
+ __exportStar(require("@spinajs/telemetry-common"), exports);
32
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/metrics.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;GAaG;AACH,4DAA0C"}
@@ -0,0 +1,81 @@
1
+ import * as express from 'express';
2
+ import { Log } from '@spinajs/log';
3
+ import { ServerMiddleware, Request as sRequest } from '@spinajs/http';
4
+ import { Counter, Gauge, Histogram } from 'prom-client';
5
+ import { Metrics } from './metrics.js';
6
+ import { TelemetryStore } from './store.js';
7
+ /**
8
+ * Default metric-name prefix for the HTTP telemetry metrics.
9
+ */
10
+ export declare const DEFAULT_PREFIX = "http";
11
+ /**
12
+ * Histogram bucket boundaries ( ms ) for request duration.
13
+ */
14
+ export declare const DURATION_BUCKETS_MS: number[];
15
+ /**
16
+ * HTTP request-timing / metrics middleware.
17
+ *
18
+ * Times every request and records:
19
+ * - `${prefix}_requests_total{method,route,status}` — a Counter,
20
+ * - `${prefix}_request_duration_ms{method,route,status}` — a Histogram,
21
+ * - `${prefix}_requests_in_flight` — a Gauge,
22
+ * against the DI-resolved {@link Metrics} registry, and feeds the shared
23
+ * {@link TelemetryStore} that backs the JSON stats endpoints.
24
+ *
25
+ * All of the end-of-request work runs from a `res.on( 'finish' )` handler
26
+ * registered in `before()` — `ServerMiddleware.after()` is never reached for a
27
+ * normal matched route, the same reason http's own `AccessLog` / `PerfRollup`
28
+ * middlewares work this way. A `res.on( 'close' )` handler balances the in-flight
29
+ * gauge — and only the gauge — for requests the client aborted, where `'finish'`
30
+ * never fires. Every bit of it is guarded so telemetry can never break a request.
31
+ */
32
+ export declare class TelemetryMiddleware extends ServerMiddleware {
33
+ protected Log: Log;
34
+ /**
35
+ * The shared aggregates. This middleware is the only writer.
36
+ */
37
+ protected Store: TelemetryStore;
38
+ /**
39
+ * Metric-name prefix. Set it with the `telemetry.prefix` configuration key —
40
+ * that is the supported way to re-prefix, and it needs no subclass.
41
+ *
42
+ * The precedence implemented in {@link ensureMetrics} is: a value assigned to
43
+ * this field that differs from {@link DEFAULT_PREFIX} wins, otherwise
44
+ * `telemetry.prefix` is used, otherwise {@link DEFAULT_PREFIX}. That override
45
+ * exists for subclasses that are replacing the middleware wholesale — see the
46
+ * README's "Replacing the middleware" section, which also covers the
47
+ * double-registration hazard of subclassing. Do NOT subclass merely to change
48
+ * the prefix.
49
+ */
50
+ protected Prefix: string;
51
+ protected ConfiguredPrefix: string;
52
+ protected Buckets: number[];
53
+ protected metrics: Metrics;
54
+ protected requestsTotal: Counter<string>;
55
+ protected requestDuration: Histogram<string>;
56
+ protected inFlight: Gauge<string>;
57
+ private defined;
58
+ constructor();
59
+ /**
60
+ * Lazily resolve {@link Metrics} and define the metric set on first use.
61
+ * Idempotent — {@link Metrics.defineMetrics} removes+recreates duplicates.
62
+ */
63
+ protected ensureMetrics(): void;
64
+ /**
65
+ * Derive a stable, low-cardinality route label. Prefers the matched route
66
+ * path ( `req.route.path` or `req.storage.route` ) over the raw request path.
67
+ */
68
+ protected routeLabel(req: sRequest): string;
69
+ /**
70
+ * Do the end-of-request accounting. Called from the response `finish` event,
71
+ * never from `after()`.
72
+ */
73
+ protected onFinish(req: sRequest, res: express.Response): void;
74
+ before(): (req: sRequest, res: express.Response, next: express.NextFunction) => void;
75
+ /**
76
+ * Nothing to do after the controllers — express does not run this for a
77
+ * matched route. See {@link onFinish}.
78
+ */
79
+ after(): null;
80
+ }
81
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAGnC,OAAO,EAAU,GAAG,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEtE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,OAAO,EAAa,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C;;GAEG;AACH,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC;;GAEG;AACH,eAAO,MAAM,mBAAmB,UAA0D,CAAC;AAO3F;;;;;;;;;;;;;;;;GAgBG;AACH,qBACa,mBAAoB,SAAQ,gBAAgB;IAEvD,SAAS,CAAC,GAAG,EAAG,GAAG,CAAC;IAEpB;;OAEG;IAEH,SAAS,CAAC,KAAK,EAAG,cAAc,CAAC;IAEjC;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,MAAM,SAAkB;IAGlC,SAAS,CAAC,gBAAgB,EAAG,MAAM,CAAC;IAGpC,SAAS,CAAC,OAAO,EAAG,MAAM,EAAE,CAAC;IAE7B,SAAS,CAAC,OAAO,EAAG,OAAO,CAAC;IAC5B,SAAS,CAAC,aAAa,EAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,SAAS,CAAC,eAAe,EAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC9C,SAAS,CAAC,QAAQ,EAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAEnC,OAAO,CAAC,OAAO,CAAS;;IASxB;;;OAGG;IACH,SAAS,CAAC,aAAa,IAAI,IAAI;IAmC/B;;;OAGG;IACH,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM;IAO3C;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,GAAG,IAAI;IA0BvD,MAAM,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,YAAY,KAAK,IAAI;IA0C3F;;;OAGG;IACI,KAAK,IAAI,IAAI;CAGrB"}
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TelemetryMiddleware = exports.DURATION_BUCKETS_MS = exports.DEFAULT_PREFIX = void 0;
13
+ const di_1 = require("@spinajs/di");
14
+ const configuration_1 = require("@spinajs/configuration");
15
+ const log_1 = require("@spinajs/log");
16
+ const http_1 = require("@spinajs/http");
17
+ const metrics_js_1 = require("./metrics.js");
18
+ const store_js_1 = require("./store.js");
19
+ /**
20
+ * Default metric-name prefix for the HTTP telemetry metrics.
21
+ */
22
+ exports.DEFAULT_PREFIX = 'http';
23
+ /**
24
+ * Histogram bucket boundaries ( ms ) for request duration.
25
+ */
26
+ exports.DURATION_BUCKETS_MS = [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000];
27
+ /**
28
+ * Storage key under `req.storage` holding the per-request start time.
29
+ */
30
+ const START_KEY = '__telemetryStart';
31
+ /**
32
+ * HTTP request-timing / metrics middleware.
33
+ *
34
+ * Times every request and records:
35
+ * - `${prefix}_requests_total{method,route,status}` — a Counter,
36
+ * - `${prefix}_request_duration_ms{method,route,status}` — a Histogram,
37
+ * - `${prefix}_requests_in_flight` — a Gauge,
38
+ * against the DI-resolved {@link Metrics} registry, and feeds the shared
39
+ * {@link TelemetryStore} that backs the JSON stats endpoints.
40
+ *
41
+ * All of the end-of-request work runs from a `res.on( 'finish' )` handler
42
+ * registered in `before()` — `ServerMiddleware.after()` is never reached for a
43
+ * normal matched route, the same reason http's own `AccessLog` / `PerfRollup`
44
+ * middlewares work this way. A `res.on( 'close' )` handler balances the in-flight
45
+ * gauge — and only the gauge — for requests the client aborted, where `'finish'`
46
+ * never fires. Every bit of it is guarded so telemetry can never break a request.
47
+ */
48
+ let TelemetryMiddleware = class TelemetryMiddleware extends http_1.ServerMiddleware {
49
+ constructor() {
50
+ super();
51
+ /**
52
+ * Metric-name prefix. Set it with the `telemetry.prefix` configuration key —
53
+ * that is the supported way to re-prefix, and it needs no subclass.
54
+ *
55
+ * The precedence implemented in {@link ensureMetrics} is: a value assigned to
56
+ * this field that differs from {@link DEFAULT_PREFIX} wins, otherwise
57
+ * `telemetry.prefix` is used, otherwise {@link DEFAULT_PREFIX}. That override
58
+ * exists for subclasses that are replacing the middleware wholesale — see the
59
+ * README's "Replacing the middleware" section, which also covers the
60
+ * double-registration hazard of subclassing. Do NOT subclass merely to change
61
+ * the prefix.
62
+ */
63
+ this.Prefix = exports.DEFAULT_PREFIX;
64
+ this.defined = false;
65
+ // Needs req.storage ( ReqStorage = -2 ) and sits alongside RequestId /
66
+ // AccessLog ( Order 2 ).
67
+ this.Order = 2;
68
+ }
69
+ /**
70
+ * Lazily resolve {@link Metrics} and define the metric set on first use.
71
+ * Idempotent — {@link Metrics.defineMetrics} removes+recreates duplicates.
72
+ */
73
+ ensureMetrics() {
74
+ if (this.defined)
75
+ return;
76
+ this.metrics = di_1.DI.get(metrics_js_1.Metrics) ?? di_1.DI.resolve(metrics_js_1.Metrics);
77
+ // A subclass that overrode Prefix keeps it; otherwise configuration wins.
78
+ const prefix = this.Prefix !== exports.DEFAULT_PREFIX ? this.Prefix : this.ConfiguredPrefix ?? exports.DEFAULT_PREFIX;
79
+ const map = this.metrics.defineMetrics(prefix, [
80
+ {
81
+ name: 'requests_total',
82
+ help: 'Total number of HTTP requests',
83
+ type: 'counter',
84
+ labelNames: ['method', 'route', 'status'],
85
+ },
86
+ {
87
+ name: 'request_duration_ms',
88
+ help: 'HTTP request duration in milliseconds',
89
+ type: 'histogram',
90
+ labelNames: ['method', 'route', 'status'],
91
+ buckets: this.Buckets ?? exports.DURATION_BUCKETS_MS,
92
+ },
93
+ {
94
+ name: 'requests_in_flight',
95
+ help: 'Number of HTTP requests currently in flight',
96
+ type: 'gauge',
97
+ },
98
+ ]);
99
+ this.requestsTotal = map['requests_total'];
100
+ this.requestDuration = map['request_duration_ms'];
101
+ this.inFlight = map['requests_in_flight'];
102
+ this.defined = true;
103
+ }
104
+ /**
105
+ * Derive a stable, low-cardinality route label. Prefers the matched route
106
+ * path ( `req.route.path` or `req.storage.route` ) over the raw request path.
107
+ */
108
+ routeLabel(req) {
109
+ const anyReq = req;
110
+ const matched = anyReq.route?.path ?? req.storage?.route;
111
+ if (typeof matched === 'string' && matched.length > 0)
112
+ return matched;
113
+ return req.path ?? anyReq.originalUrl ?? 'unknown';
114
+ }
115
+ /**
116
+ * Do the end-of-request accounting. Called from the response `finish` event,
117
+ * never from `after()`.
118
+ */
119
+ onFinish(req, res) {
120
+ try {
121
+ const start = req.storage?.[START_KEY];
122
+ const durationMs = start !== undefined ? Number(process.hrtime.bigint() - start) / 1e6 : 0;
123
+ const method = req.method ?? 'GET';
124
+ // Read the route label HERE, not in before(): `req.route` is only
125
+ // populated once the router has matched.
126
+ const route = this.routeLabel(req);
127
+ const status = res.statusCode;
128
+ const labels = { method, route, status: String(status) };
129
+ // Balance before()'s inc() FIRST: a throw from anything below would
130
+ // otherwise strand the gauge one higher for the rest of the process.
131
+ this.inFlight.dec();
132
+ this.requestDuration.observe(labels, durationMs);
133
+ this.requestsTotal.inc(labels);
134
+ this.Store.record(method, route, status, durationMs);
135
+ }
136
+ catch (err) {
137
+ this.Log?.warn(err, 'telemetry finish handler failed');
138
+ }
139
+ }
140
+ before() {
141
+ return (req, res, next) => {
142
+ try {
143
+ this.ensureMetrics();
144
+ req.storage[START_KEY] = process.hrtime.bigint();
145
+ this.inFlight.inc();
146
+ // The inc() above must be balanced exactly once, whichever event wins.
147
+ // 'finish' does NOT fire when the client disconnects mid-response, only
148
+ // 'close' does — without the 'close' arm the gauge drifts monotonically
149
+ // upward for the life of the process behind a load balancer with client
150
+ // timeouts. On a normal request both fire, 'finish' first, so the flag
151
+ // is what keeps the gauge from being decremented twice.
152
+ let settled = false;
153
+ res.on('finish', () => {
154
+ if (settled)
155
+ return;
156
+ settled = true;
157
+ this.onFinish(req, res);
158
+ });
159
+ res.on('close', () => {
160
+ if (settled)
161
+ return;
162
+ settled = true;
163
+ // Aborted request: balance the gauge and NOTHING else. `res.statusCode`
164
+ // is still the un-sent default here, so recording it would pollute the
165
+ // status-class counters and the route stats with a response that was
166
+ // never sent.
167
+ try {
168
+ this.inFlight.dec();
169
+ }
170
+ catch (err) {
171
+ this.Log?.warn(err, 'telemetry close handler failed');
172
+ }
173
+ });
174
+ }
175
+ catch (err) {
176
+ this.Log?.warn(err, 'telemetry before() failed');
177
+ }
178
+ next();
179
+ };
180
+ }
181
+ /**
182
+ * Nothing to do after the controllers — express does not run this for a
183
+ * matched route. See {@link onFinish}.
184
+ */
185
+ after() {
186
+ return null;
187
+ }
188
+ };
189
+ exports.TelemetryMiddleware = TelemetryMiddleware;
190
+ __decorate([
191
+ (0, log_1.Logger)('telemetry'),
192
+ __metadata("design:type", log_1.Log)
193
+ ], TelemetryMiddleware.prototype, "Log", void 0);
194
+ __decorate([
195
+ (0, di_1.Autoinject)(store_js_1.TelemetryStore),
196
+ __metadata("design:type", store_js_1.TelemetryStore)
197
+ ], TelemetryMiddleware.prototype, "Store", void 0);
198
+ __decorate([
199
+ (0, configuration_1.Config)('telemetry.prefix', { defaultValue: exports.DEFAULT_PREFIX }),
200
+ __metadata("design:type", String)
201
+ ], TelemetryMiddleware.prototype, "ConfiguredPrefix", void 0);
202
+ __decorate([
203
+ (0, configuration_1.Config)('telemetry.buckets', { defaultValue: exports.DURATION_BUCKETS_MS }),
204
+ __metadata("design:type", Array)
205
+ ], TelemetryMiddleware.prototype, "Buckets", void 0);
206
+ exports.TelemetryMiddleware = TelemetryMiddleware = __decorate([
207
+ (0, di_1.Injectable)(http_1.ServerMiddleware),
208
+ __metadata("design:paramtypes", [])
209
+ ], TelemetryMiddleware);
210
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,16 @@
1
+ import { BasePolicy, IController, IRoute, Request } from '@spinajs/http';
2
+ /**
3
+ * A policy that permits everything. Used as the explicit default for the
4
+ * liveness / readiness endpoints, which must be reachable by kubelet probes and
5
+ * load balancers that cannot carry a token.
6
+ *
7
+ * This exists so the config can name a real class. Leaving the policy config
8
+ * key empty would work, but the http layer logs `No policy named ... is
9
+ * registered` on every boot for an unresolvable value, which reads like a
10
+ * misconfiguration.
11
+ */
12
+ export declare class PublicPolicy extends BasePolicy {
13
+ isEnabled(_action: IRoute, _instance: IController): boolean;
14
+ execute(_req: Request): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=PublicPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublicPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/PublicPolicy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEzE;;;;;;;;;GASG;AACH,qBACa,YAAa,SAAQ,UAAU;IACnC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO;IAI3D,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAG7C"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.PublicPolicy = void 0;
10
+ const di_1 = require("@spinajs/di");
11
+ const http_1 = require("@spinajs/http");
12
+ /**
13
+ * A policy that permits everything. Used as the explicit default for the
14
+ * liveness / readiness endpoints, which must be reachable by kubelet probes and
15
+ * load balancers that cannot carry a token.
16
+ *
17
+ * This exists so the config can name a real class. Leaving the policy config
18
+ * key empty would work, but the http layer logs `No policy named ... is
19
+ * registered` on every boot for an unresolvable value, which reads like a
20
+ * misconfiguration.
21
+ */
22
+ let PublicPolicy = class PublicPolicy extends http_1.BasePolicy {
23
+ isEnabled(_action, _instance) {
24
+ return true;
25
+ }
26
+ execute(_req) {
27
+ return Promise.resolve();
28
+ }
29
+ };
30
+ exports.PublicPolicy = PublicPolicy;
31
+ exports.PublicPolicy = PublicPolicy = __decorate([
32
+ (0, di_1.Injectable)(http_1.BasePolicy)
33
+ ], PublicPolicy);
34
+ //# sourceMappingURL=PublicPolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublicPolicy.js","sourceRoot":"","sources":["../../../src/policies/PublicPolicy.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oCAAyC;AACzC,wCAAyE;AAEzE;;;;;;;;;GASG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,iBAAU;IACnC,SAAS,CAAC,OAAe,EAAE,SAAsB;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,OAAO,CAAC,IAAa;QAC1B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;CACF,CAAA;AARY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,eAAU,EAAC,iBAAU,CAAC;GACV,YAAY,CAQxB"}
@@ -0,0 +1,19 @@
1
+ import { BasePolicy, IController, IRoute, Request } from '@spinajs/http';
2
+ import { Log } from '@spinajs/log';
3
+ /**
4
+ * Shared-token guard for the telemetry endpoints. Reads the expected token from
5
+ * `telemetry.auth.token` and compares it against the `x-metrics-token` header.
6
+ * The header name is deliberately unchanged from the retired
7
+ * `@spinajs/metrics` package so existing scrape configs keep working.
8
+ *
9
+ * Bypassed entirely in development.
10
+ */
11
+ export declare class TelemetryTokenPolicy extends BasePolicy {
12
+ protected Log: Log;
13
+ protected Token: string;
14
+ protected isDev: boolean;
15
+ protected HEADER_TOKEN_FIELD: string;
16
+ isEnabled(_action: IRoute, _instance: IController): boolean;
17
+ execute(req: Request): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=TelemetryTokenPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TelemetryTokenPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/TelemetryTokenPolicy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEzE,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAG3C;;;;;;;GAOG;AACH,qBACa,oBAAqB,SAAQ,UAAU;IAElD,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAGnB,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC;IAGxB,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC;IAEzB,SAAS,CAAC,kBAAkB,SAAqB;IAE1C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO;IAIrD,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAgBlD"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TelemetryTokenPolicy = void 0;
13
+ const configuration_1 = require("@spinajs/configuration");
14
+ const http_1 = require("@spinajs/http");
15
+ const exceptions_1 = require("@spinajs/exceptions");
16
+ const log_1 = require("@spinajs/log");
17
+ const di_1 = require("@spinajs/di");
18
+ /**
19
+ * Shared-token guard for the telemetry endpoints. Reads the expected token from
20
+ * `telemetry.auth.token` and compares it against the `x-metrics-token` header.
21
+ * The header name is deliberately unchanged from the retired
22
+ * `@spinajs/metrics` package so existing scrape configs keep working.
23
+ *
24
+ * Bypassed entirely in development.
25
+ */
26
+ let TelemetryTokenPolicy = class TelemetryTokenPolicy extends http_1.BasePolicy {
27
+ constructor() {
28
+ super(...arguments);
29
+ this.HEADER_TOKEN_FIELD = 'x-metrics-token';
30
+ }
31
+ isEnabled(_action, _instance) {
32
+ return true;
33
+ }
34
+ async execute(req) {
35
+ if (this.isDev) {
36
+ return;
37
+ }
38
+ const token = req.headers[this.HEADER_TOKEN_FIELD];
39
+ if (!token) {
40
+ this.Log.warn(`No token is set for restricted area, header field: ${this.HEADER_TOKEN_FIELD}, policy: TelemetryTokenPolicy, ip: ${req.storage.realIp}`);
41
+ throw new exceptions_1.Forbidden('access token is not set');
42
+ }
43
+ if (token !== this.Token) {
44
+ this.Log.warn(`Invalid access token received, header field: ${this.HEADER_TOKEN_FIELD}, policy: TelemetryTokenPolicy, ip: ${req.storage.realIp}`);
45
+ throw new exceptions_1.Forbidden('invalid access token');
46
+ }
47
+ }
48
+ };
49
+ exports.TelemetryTokenPolicy = TelemetryTokenPolicy;
50
+ __decorate([
51
+ (0, log_1.Logger)('Security'),
52
+ __metadata("design:type", log_1.Log)
53
+ ], TelemetryTokenPolicy.prototype, "Log", void 0);
54
+ __decorate([
55
+ (0, configuration_1.Config)('telemetry.auth.token'),
56
+ __metadata("design:type", String)
57
+ ], TelemetryTokenPolicy.prototype, "Token", void 0);
58
+ __decorate([
59
+ (0, configuration_1.Config)('configuration.isDevelopment'),
60
+ __metadata("design:type", Boolean)
61
+ ], TelemetryTokenPolicy.prototype, "isDev", void 0);
62
+ exports.TelemetryTokenPolicy = TelemetryTokenPolicy = __decorate([
63
+ (0, di_1.Injectable)(http_1.BasePolicy)
64
+ ], TelemetryTokenPolicy);
65
+ //# sourceMappingURL=TelemetryTokenPolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TelemetryTokenPolicy.js","sourceRoot":"","sources":["../../../src/policies/TelemetryTokenPolicy.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,0DAAgD;AAChD,wCAAyE;AACzE,oDAAgD;AAChD,sCAA2C;AAC3C,oCAAyC;AAEzC;;;;;;;GAOG;AAEI,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,iBAAU;IAA7C;;QAUK,uBAAkB,GAAG,iBAAiB,CAAC;IAsBnD,CAAC;IApBQ,SAAS,CAAC,OAAe,EAAE,SAAsB;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,GAAY;QAC/B,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,sDAAsD,IAAI,CAAC,kBAAkB,uCAAuC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YACxJ,MAAM,IAAI,sBAAS,CAAC,yBAAyB,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,gDAAgD,IAAI,CAAC,kBAAkB,uCAAuC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YAClJ,MAAM,IAAI,sBAAS,CAAC,sBAAsB,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;CACF,CAAA;AAhCY,oDAAoB;AAErB;IADT,IAAA,YAAM,EAAC,UAAU,CAAC;8BACJ,SAAG;iDAAC;AAGT;IADT,IAAA,sBAAM,EAAC,sBAAsB,CAAC;;mDACP;AAGd;IADT,IAAA,sBAAM,EAAC,6BAA6B,CAAC;;mDACb;+BARd,oBAAoB;IADhC,IAAA,eAAU,EAAC,iBAAU,CAAC;GACV,oBAAoB,CAgChC"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Serializable snapshot of {@link RequestStats}.
3
+ */
4
+ export interface IRequestStatsSnapshot {
5
+ requests: number;
6
+ responses: number;
7
+ errors: number;
8
+ info: number;
9
+ success: number;
10
+ redirect: number;
11
+ client_error: number;
12
+ server_error: number;
13
+ total_time: number;
14
+ max_time: number;
15
+ min_time: number;
16
+ avg_time: number;
17
+ apdex_satisfied: number;
18
+ apdex_tolerated: number;
19
+ apdex_score: number;
20
+ req_rate: number;
21
+ err_rate: number;
22
+ }
23
+ /**
24
+ * Port of swagger-stats' `swsReqResStats` — accumulates request/response
25
+ * counters, response-time aggregates ( total / max / min / avg ), an Apdex
26
+ * score, and request/error rates over its lifetime.
27
+ *
28
+ * Pure and dependency-free; time is always passed in so it is deterministic.
29
+ */
30
+ export declare class RequestStats {
31
+ /**
32
+ * Apdex satisfied-response threshold, in milliseconds. Responses up to
33
+ * `4 * threshold` are "tolerated".
34
+ */
35
+ readonly apdexThreshold: number;
36
+ requests: number;
37
+ responses: number;
38
+ errors: number;
39
+ info: number;
40
+ success: number;
41
+ redirect: number;
42
+ client_error: number;
43
+ server_error: number;
44
+ totalTime: number;
45
+ maxTime: number;
46
+ minTime: number;
47
+ avgTime: number;
48
+ apdexSatisfied: number;
49
+ apdexTolerated: number;
50
+ reqRate: number;
51
+ errRate: number;
52
+ constructor(apdexThreshold?: number);
53
+ /**
54
+ * Count an inbound request ( before the response is known ).
55
+ */
56
+ countRequest(): void;
57
+ /**
58
+ * Count a completed response, updating status-class counters, response-time
59
+ * aggregates and Apdex.
60
+ *
61
+ * @param statusCode - the HTTP status code of the response
62
+ * @param durationMs - the request duration in milliseconds
63
+ */
64
+ countResponse(statusCode: number, durationMs: number): void;
65
+ /**
66
+ * The current Apdex score: `(satisfied + tolerated/2) / responses`.
67
+ * Returns 0 when there are no responses yet.
68
+ */
69
+ apdex(): number;
70
+ /**
71
+ * Recompute request/error rates ( per second ) over an elapsed window.
72
+ *
73
+ * @param elapsedMs - the width of the window in milliseconds
74
+ */
75
+ updateRates(elapsedMs: number): void;
76
+ /**
77
+ * A plain-object snapshot of the current stats.
78
+ */
79
+ toJSON(): IRequestStatsSnapshot;
80
+ }
81
+ //# sourceMappingURL=requestStats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestStats.d.ts","sourceRoot":"","sources":["../../src/requestStats.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IAEf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IAErB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IAEjB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IAEpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID;;;;;;GAMG;AACH,qBAAa,YAAY;IACvB;;;OAGG;IACH,SAAgB,cAAc,EAAE,MAAM,CAAC;IAGhC,QAAQ,SAAK;IACb,SAAS,SAAK;IACd,MAAM,SAAK;IAGX,IAAI,SAAK;IACT,OAAO,SAAK;IACZ,QAAQ,SAAK;IACb,YAAY,SAAK;IACjB,YAAY,SAAK;IAGjB,SAAS,SAAK;IACd,OAAO,SAAK;IACZ,OAAO,SAAK;IACZ,OAAO,SAAK;IAGZ,cAAc,SAAK;IACnB,cAAc,SAAK;IAGnB,OAAO,SAAK;IACZ,OAAO,SAAK;gBAEP,cAAc,GAAE,MAAmC;IAI/D;;OAEG;IACI,YAAY,IAAI,IAAI;IAI3B;;;;;;OAMG;IACI,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA6BlE;;;OAGG;IACI,KAAK,IAAI,MAAM;IAKtB;;;;OAIG;IACI,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAW3C;;OAEG;IACI,MAAM,IAAI,qBAAqB;CAyBvC"}