@pawells/nestjs-prometheus 1.0.0-dev.4c8c698

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.
@@ -0,0 +1,380 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var PrometheusExporter_1;
11
+ import { Injectable } from '@nestjs/common';
12
+ import { Counter, Gauge, Histogram, Registry, collectDefaultMetrics } from 'prom-client';
13
+ import { AppLogger, getErrorMessage } from '@pawells/nestjs-shared/common';
14
+ /**
15
+ * Prometheus metrics exporter implementation
16
+ *
17
+ * Implements the IMetricsExporter interface for Prometheus pull-based metrics collection.
18
+ * Uses prom-client library to manage Prometheus instruments (Counter, Gauge, Histogram)
19
+ * and exports metrics in Prometheus text format on demand.
20
+ *
21
+ * Architecture:
22
+ * - **Event-based buffering**: Metric values are buffered as they are recorded
23
+ * - **Pull-based export**: Flushes all pending values when `/metrics` endpoint is scraped
24
+ * - **Atomic swaps**: Uses atomic array swaps during flush to prevent concurrent data loss
25
+ * - **Bounded memory**: Limits pending values to 1000 per metric to prevent unbounded growth
26
+ * - **Node.js defaults**: Automatically collects Node.js process metrics (CPU, memory, GC, etc.)
27
+ *
28
+ * Type mapping:
29
+ * - `counter` → prom-client Counter (monotonically increasing)
30
+ * - `gauge` → prom-client Gauge (point-in-time value)
31
+ * - `updown_counter` → prom-client Gauge (can increase or decrease)
32
+ * - `histogram` → prom-client Histogram (distribution with buckets)
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const exporter = new PrometheusExporter();
37
+ * registry.registerExporter(exporter);
38
+ *
39
+ * // Later, get metrics for Prometheus scrape endpoint
40
+ * const prometheusMetrics = await exporter.getMetrics();
41
+ * ```
42
+ */
43
+ let PrometheusExporter = class PrometheusExporter {
44
+ static { PrometheusExporter_1 = this; }
45
+ /**
46
+ * This exporter buffers metric values as they are recorded and flushes
47
+ * them into prom-client instruments on each pull (getMetrics)
48
+ */
49
+ supportsEventBased = true;
50
+ /**
51
+ * This exporter supports pull-based metric retrieval
52
+ */
53
+ supportsPull = true;
54
+ /**
55
+ * Prom-client Registry instance for managing instruments
56
+ * @private
57
+ */
58
+ registry;
59
+ /**
60
+ * Cache of created prom-client instruments (Counter, Histogram, Gauge)
61
+ * Keyed by metric name
62
+ * @private
63
+ */
64
+ instruments;
65
+ /**
66
+ * Pending metric values to be flushed into prom-client instruments on next getMetrics()
67
+ * Keyed by metric name
68
+ * @private
69
+ */
70
+ pending;
71
+ /**
72
+ * Running totals for updown_counter metrics (persistent across scrapes)
73
+ * Keyed by metric name, then by normalized label key
74
+ * @private
75
+ */
76
+ gaugeValues;
77
+ /**
78
+ * Maximum number of pending metric values per metric before culling oldest entries
79
+ * Prevents unbounded memory growth if metrics are recorded much faster than pulled
80
+ * @private
81
+ */
82
+ // eslint-disable-next-line no-magic-numbers
83
+ static MAX_PENDING_PER_METRIC = 1000;
84
+ /**
85
+ * Logger instance for warnings and errors
86
+ */
87
+ logger;
88
+ /**
89
+ * Normalize label keys to handle consistent ordering regardless of insertion order
90
+ * @private
91
+ */
92
+ static normalizeLabelKey(labels) {
93
+ const sortedEntries = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b));
94
+ return JSON.stringify(Object.fromEntries(sortedEntries));
95
+ }
96
+ /**
97
+ * Create a new PrometheusExporter instance
98
+ *
99
+ * Initializes a prom-client Registry and starts collecting Node.js default metrics
100
+ * (process CPU, memory, event loop, garbage collection, etc.).
101
+ */
102
+ constructor() {
103
+ this.registry = new Registry();
104
+ this.instruments = new Map();
105
+ this.pending = new Map();
106
+ this.gaugeValues = new Map();
107
+ this.logger = new AppLogger(undefined, PrometheusExporter_1.name);
108
+ // Collect Node.js default metrics into our registry
109
+ collectDefaultMetrics({ register: this.registry });
110
+ }
111
+ /**
112
+ * Called when a metric descriptor is registered in InstrumentationRegistry
113
+ *
114
+ * Pre-creates the corresponding prom-client instrument (Counter, Gauge, or Histogram)
115
+ * based on the descriptor's type. The created instrument is cached for future use.
116
+ * Initializes an empty pending array for the metric to buffer future recorded values.
117
+ *
118
+ * Type mapping:
119
+ * - `counter` → Counter (incremented by value via inc())
120
+ * - `histogram` → Histogram (observed via observe())
121
+ * - `gauge` or `updown_counter` → Gauge (set via set())
122
+ *
123
+ * @param descriptor - The metric descriptor being registered
124
+ * @throws Error if descriptor type is not supported
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * exporter.onDescriptorRegistered({
129
+ * name: 'http_request_duration_seconds',
130
+ * type: 'histogram',
131
+ * help: 'Request duration in seconds',
132
+ * labelNames: ['method', 'route', 'status_code'],
133
+ * buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1],
134
+ * });
135
+ * ```
136
+ */
137
+ onDescriptorRegistered(descriptor) {
138
+ const { name, type, help, labelNames, buckets } = descriptor;
139
+ // Guard against duplicate registration
140
+ if (this.instruments.has(name)) {
141
+ return;
142
+ }
143
+ // Validate required fields
144
+ if (!name) {
145
+ this.logger.warn('Metric descriptor registered with empty name');
146
+ return;
147
+ }
148
+ if (!help) {
149
+ this.logger.warn(`Metric descriptor "${name}" registered with empty help text`);
150
+ return;
151
+ }
152
+ let instrument;
153
+ switch (type) {
154
+ case 'counter':
155
+ instrument = new Counter({
156
+ name,
157
+ help,
158
+ labelNames,
159
+ registers: [this.registry],
160
+ });
161
+ break;
162
+ case 'histogram':
163
+ instrument = new Histogram({
164
+ name,
165
+ help,
166
+ labelNames,
167
+ buckets,
168
+ registers: [this.registry],
169
+ });
170
+ break;
171
+ case 'gauge':
172
+ case 'updown_counter':
173
+ // Both gauge and updown_counter map to prom-client Gauge
174
+ instrument = new Gauge({
175
+ name,
176
+ help,
177
+ labelNames,
178
+ registers: [this.registry],
179
+ });
180
+ // Initialize running totals map for this metric
181
+ if (type === 'updown_counter') {
182
+ this.gaugeValues.set(name, new Map());
183
+ }
184
+ break;
185
+ default:
186
+ throw new Error(`Unsupported metric type "${type}" for descriptor "${name}"`);
187
+ }
188
+ this.instruments.set(name, instrument);
189
+ this.pending.set(name, []);
190
+ }
191
+ /**
192
+ * Buffer a metric value to be flushed on next getMetrics() call
193
+ *
194
+ * Values are buffered rather than immediately recorded into prom-client.
195
+ * They are applied to the appropriate instrument during the next pull (getMetrics) call.
196
+ *
197
+ * Buffering behavior:
198
+ * - Values are appended to the pending array for the metric
199
+ * - If the pending array exceeds MAX_PENDING_PER_METRIC (1000), oldest entries are culled
200
+ * - If the metric descriptor was not registered first, a warning is logged and value is dropped
201
+ *
202
+ * @param value - The metric value to buffer
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * exporter.onMetricRecorded({
207
+ * descriptor: { name: 'http_request_duration_seconds' },
208
+ * value: 0.125,
209
+ * labels: { method: 'GET', status_code: '200' },
210
+ * timestamp: performance.now(),
211
+ * });
212
+ * ```
213
+ */
214
+ onMetricRecorded(value) {
215
+ const metricName = value.descriptor.name;
216
+ const pendingArray = this.pending.get(metricName);
217
+ if (pendingArray) {
218
+ pendingArray.push(value);
219
+ // Cap pending array to prevent unbounded memory growth
220
+ if (pendingArray.length > PrometheusExporter_1.MAX_PENDING_PER_METRIC) {
221
+ pendingArray.shift();
222
+ }
223
+ }
224
+ else {
225
+ // Warn if metric recorded before descriptor registration (data will be lost)
226
+ this.logger.warn(`Metric recorded before descriptor registration: ${metricName}`);
227
+ }
228
+ }
229
+ /**
230
+ * Retrieve all metrics in Prometheus text format
231
+ *
232
+ * Flushes all pending metric values into prom-client instruments, then returns
233
+ * the complete metrics output in Prometheus text format (version 0.0.4).
234
+ *
235
+ * Flush process:
236
+ * 1. For each metric, atomically swap the pending array with a fresh empty array
237
+ * 2. Apply all pending values to the corresponding instrument:
238
+ * - Counter: increment by value
239
+ * - Histogram: observe value
240
+ * - Gauge: set to value
241
+ * 3. Skip any values where the instrument was not pre-created (shouldn't happen)
242
+ * 4. Return the serialized metrics from the registry
243
+ *
244
+ * Includes Node.js default metrics and all custom metrics registered with descriptors.
245
+ *
246
+ * @returns Promise resolving to metrics in Prometheus text format (version 0.0.4)
247
+ * @throws Error if metrics generation fails (logged but re-thrown)
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * const prometheusMetrics = await exporter.getMetrics();
252
+ * // Returns:
253
+ * // # HELP http_request_duration_seconds Duration of HTTP requests in seconds
254
+ * // # TYPE http_request_duration_seconds histogram
255
+ * // http_request_duration_seconds_bucket{le="0.001",...} 0
256
+ * // http_request_duration_seconds_bucket{le="0.01",...} 5
257
+ * ```
258
+ */
259
+ async getMetrics() {
260
+ // Flush all pending values into prom-client instruments
261
+ // Use atomic swap pattern: capture current values and replace with empty array
262
+ // to prevent loss of metrics recorded concurrently during flush
263
+ for (const [metricName, pendingValues] of this.pending.entries()) {
264
+ // Atomically swap pending array with a fresh one
265
+ this.pending.set(metricName, []);
266
+ if (pendingValues.length === 0) {
267
+ continue;
268
+ }
269
+ const instrument = this.instruments.get(metricName);
270
+ if (!instrument) {
271
+ // Instrument not yet created, skip pending values
272
+ continue;
273
+ }
274
+ // Record all pending values into the appropriate instrument
275
+ if (instrument instanceof Counter) {
276
+ // Counter: increment by value for each pending entry
277
+ for (const metricValue of pendingValues) {
278
+ try {
279
+ instrument.inc(metricValue.labels, metricValue.value);
280
+ }
281
+ catch (recordError) {
282
+ this.logger.warn(`Failed to record metric value for "${metricName}": ${getErrorMessage(recordError)}`);
283
+ }
284
+ }
285
+ }
286
+ else if (instrument instanceof Histogram) {
287
+ // Histogram: observe value for each pending entry
288
+ for (const metricValue of pendingValues) {
289
+ try {
290
+ instrument.observe(metricValue.labels, metricValue.value);
291
+ }
292
+ catch (recordError) {
293
+ this.logger.warn(`Failed to record metric value for "${metricName}": ${getErrorMessage(recordError)}`);
294
+ }
295
+ }
296
+ }
297
+ else if (instrument instanceof Gauge) {
298
+ // Gauge: set values directly per label set
299
+ // updown_counter: accumulate values per label set and maintain running total
300
+ const accumulatedValues = new Map();
301
+ for (const metricValue of pendingValues) {
302
+ const labelKey = PrometheusExporter_1.normalizeLabelKey(metricValue.labels);
303
+ const existing = accumulatedValues.get(labelKey);
304
+ if (existing) {
305
+ existing.value += metricValue.value;
306
+ }
307
+ else {
308
+ accumulatedValues.set(labelKey, {
309
+ labels: metricValue.labels,
310
+ value: metricValue.value,
311
+ });
312
+ }
313
+ }
314
+ // Get running totals map for this metric (if it exists, it's an updown_counter)
315
+ const runningTotals = this.gaugeValues.get(metricName);
316
+ // Apply accumulated values to the gauge (iterate only through unique label sets)
317
+ for (const { labels, value: accumulatedValue } of accumulatedValues.values()) {
318
+ try {
319
+ let finalValue = accumulatedValue;
320
+ // For updown_counters, add to running total; for regular gauges, use value directly
321
+ if (runningTotals) {
322
+ const normalizedKey = PrometheusExporter_1.normalizeLabelKey(labels);
323
+ const currentTotal = runningTotals.get(normalizedKey) ?? 0;
324
+ finalValue = currentTotal + accumulatedValue;
325
+ runningTotals.set(normalizedKey, finalValue);
326
+ }
327
+ // Convert number values to strings if needed for prom-client
328
+ const labelsForProm = {};
329
+ for (const [key, val] of Object.entries(labels)) {
330
+ labelsForProm[key] = String(val);
331
+ }
332
+ instrument.set(labelsForProm, finalValue);
333
+ }
334
+ catch (recordError) {
335
+ this.logger.warn(`Failed to record metric value for "${metricName}": ${getErrorMessage(recordError)}`);
336
+ }
337
+ }
338
+ }
339
+ }
340
+ // Return metrics in Prometheus text format
341
+ try {
342
+ return await this.registry.metrics();
343
+ }
344
+ catch (error) {
345
+ const message = getErrorMessage(error);
346
+ this.logger.error(`Failed to generate Prometheus metrics: ${message}`);
347
+ throw error;
348
+ }
349
+ }
350
+ /**
351
+ * Shutdown the exporter and clean up resources
352
+ *
353
+ * Clears the prom-client registry and releases all internal state:
354
+ * - registry.clear() — removes all instruments
355
+ * - instruments.clear() — removes cached instrument references
356
+ * - pending.clear() — discards any buffered but unflushed metric values
357
+ *
358
+ * Called by PrometheusModule during application shutdown (onApplicationShutdown).
359
+ *
360
+ * @returns Promise that resolves when shutdown is complete
361
+ *
362
+ * @example
363
+ * ```typescript
364
+ * await exporter.shutdown();
365
+ * ```
366
+ */
367
+ // eslint-disable-next-line require-await
368
+ async shutdown() {
369
+ this.registry.clear();
370
+ this.instruments.clear();
371
+ this.pending.clear();
372
+ this.gaugeValues.clear();
373
+ }
374
+ };
375
+ PrometheusExporter = PrometheusExporter_1 = __decorate([
376
+ Injectable(),
377
+ __metadata("design:paramtypes", [])
378
+ ], PrometheusExporter);
379
+ export { PrometheusExporter };
380
+ //# sourceMappingURL=prometheus.exporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prometheus.exporter.js","sourceRoot":"","sources":["../src/prometheus.exporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import { OnModuleInit, OnApplicationShutdown, type DynamicModule } from '@nestjs/common';
2
+ import { InstrumentationRegistry } from '@pawells/nestjs-shared';
3
+ import { PrometheusExporter } from './prometheus.exporter.js';
4
+ /**
5
+ * Prometheus metrics module for NestJS
6
+ *
7
+ * Provides integration with @pawells/nestjs-shared InstrumentationRegistry
8
+ * to export metrics in Prometheus format via a `/metrics` HTTP endpoint.
9
+ *
10
+ * Features:
11
+ * - Event-based metric collection: buffers metric values and flushes on pull
12
+ * - Automatic HTTP endpoint: exposes GET /metrics with Prometheus text format
13
+ * - Node.js default metrics: process CPU, memory, event loop, garbage collection
14
+ * - MetricsGuard integration: optional METRICS_API_KEY authentication
15
+ * - Lifecycle management: registers exporter on init, cleans up on shutdown
16
+ *
17
+ * Registers PrometheusExporter globally and automatically connects it to the
18
+ * InstrumentationRegistry so that metric descriptors and values are forwarded
19
+ * to the Prometheus exporter.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { PrometheusModule } from '@pawells/nestjs-prometheus';
24
+ *
25
+ * @Module({
26
+ * imports: [
27
+ * ConfigModule,
28
+ * CommonModule,
29
+ * PrometheusModule.forRoot(), // Register module globally
30
+ * ],
31
+ * })
32
+ * export class AppModule {}
33
+ *
34
+ * // Metrics are now available at: GET /metrics
35
+ * ```
36
+ */
37
+ export declare class PrometheusModule implements OnModuleInit, OnApplicationShutdown {
38
+ private readonly exporter;
39
+ private readonly registry;
40
+ /**
41
+ * Create a global PrometheusModule with automatic registration
42
+ *
43
+ * Returns a DynamicModule that marks this module as global, enabling
44
+ * it to be imported once at the top level and used throughout the application.
45
+ *
46
+ * @returns DynamicModule configured as global
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * @Module({
51
+ * imports: [PrometheusModule.forRoot()],
52
+ * })
53
+ * export class AppModule {}
54
+ * ```
55
+ */
56
+ static forRoot(): DynamicModule;
57
+ constructor(exporter: PrometheusExporter, registry: InstrumentationRegistry);
58
+ /**
59
+ * Initialize the module and register the Prometheus exporter
60
+ *
61
+ * Called by NestJS during module initialization. Registers the PrometheusExporter
62
+ * with the InstrumentationRegistry so that it receives descriptor registration
63
+ * events and metric values.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * // During application startup:
68
+ * // 1. PrometheusModule is initialized
69
+ * // 2. onModuleInit() registers PrometheusExporter with InstrumentationRegistry
70
+ * // 3. All metrics registered with registry are now tracked by Prometheus exporter
71
+ * ```
72
+ */
73
+ onModuleInit(): void;
74
+ /**
75
+ * Clean up resources on application shutdown
76
+ *
77
+ * Called by NestJS during application shutdown. Calls the PrometheusExporter's
78
+ * shutdown method to clean up the registry and release resources.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * // During application shutdown:
83
+ * // onApplicationShutdown() is called
84
+ * // PrometheusExporter clears registry and internal state
85
+ * ```
86
+ */
87
+ onApplicationShutdown(): Promise<void>;
88
+ }
89
+ //# sourceMappingURL=prometheus.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prometheus.module.d.ts","sourceRoot":"","sources":["../src/prometheus.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,YAAY,EAAE,qBAAqB,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAMa,gBAAiB,YAAW,YAAY,EAAE,qBAAqB;IAwB1E,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAxB1B;;;;;;;;;;;;;;;OAeG;WACW,OAAO,IAAI,aAAa;gBAOpB,QAAQ,EAAE,kBAAkB,EAC5B,QAAQ,EAAE,uBAAuB;IAGnD;;;;;;;;;;;;;;OAcG;IACI,YAAY,IAAI,IAAI;IAI3B;;;;;;;;;;;;OAYG;IACU,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;CAGnD"}
@@ -0,0 +1,122 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var PrometheusModule_1;
11
+ import { Global, Module } from '@nestjs/common';
12
+ import { InstrumentationRegistry } from '@pawells/nestjs-shared';
13
+ import { PrometheusExporter } from './prometheus.exporter.js';
14
+ import { MetricsController } from './controllers/metrics.controller.js';
15
+ /**
16
+ * Prometheus metrics module for NestJS
17
+ *
18
+ * Provides integration with @pawells/nestjs-shared InstrumentationRegistry
19
+ * to export metrics in Prometheus format via a `/metrics` HTTP endpoint.
20
+ *
21
+ * Features:
22
+ * - Event-based metric collection: buffers metric values and flushes on pull
23
+ * - Automatic HTTP endpoint: exposes GET /metrics with Prometheus text format
24
+ * - Node.js default metrics: process CPU, memory, event loop, garbage collection
25
+ * - MetricsGuard integration: optional METRICS_API_KEY authentication
26
+ * - Lifecycle management: registers exporter on init, cleans up on shutdown
27
+ *
28
+ * Registers PrometheusExporter globally and automatically connects it to the
29
+ * InstrumentationRegistry so that metric descriptors and values are forwarded
30
+ * to the Prometheus exporter.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { PrometheusModule } from '@pawells/nestjs-prometheus';
35
+ *
36
+ * @Module({
37
+ * imports: [
38
+ * ConfigModule,
39
+ * CommonModule,
40
+ * PrometheusModule.forRoot(), // Register module globally
41
+ * ],
42
+ * })
43
+ * export class AppModule {}
44
+ *
45
+ * // Metrics are now available at: GET /metrics
46
+ * ```
47
+ */
48
+ let PrometheusModule = PrometheusModule_1 = class PrometheusModule {
49
+ exporter;
50
+ registry;
51
+ /**
52
+ * Create a global PrometheusModule with automatic registration
53
+ *
54
+ * Returns a DynamicModule that marks this module as global, enabling
55
+ * it to be imported once at the top level and used throughout the application.
56
+ *
57
+ * @returns DynamicModule configured as global
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * @Module({
62
+ * imports: [PrometheusModule.forRoot()],
63
+ * })
64
+ * export class AppModule {}
65
+ * ```
66
+ */
67
+ static forRoot() {
68
+ return {
69
+ module: PrometheusModule_1,
70
+ };
71
+ }
72
+ constructor(exporter, registry) {
73
+ this.exporter = exporter;
74
+ this.registry = registry;
75
+ }
76
+ /**
77
+ * Initialize the module and register the Prometheus exporter
78
+ *
79
+ * Called by NestJS during module initialization. Registers the PrometheusExporter
80
+ * with the InstrumentationRegistry so that it receives descriptor registration
81
+ * events and metric values.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * // During application startup:
86
+ * // 1. PrometheusModule is initialized
87
+ * // 2. onModuleInit() registers PrometheusExporter with InstrumentationRegistry
88
+ * // 3. All metrics registered with registry are now tracked by Prometheus exporter
89
+ * ```
90
+ */
91
+ onModuleInit() {
92
+ this.registry.registerExporter(this.exporter);
93
+ }
94
+ /**
95
+ * Clean up resources on application shutdown
96
+ *
97
+ * Called by NestJS during application shutdown. Calls the PrometheusExporter's
98
+ * shutdown method to clean up the registry and release resources.
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * // During application shutdown:
103
+ * // onApplicationShutdown() is called
104
+ * // PrometheusExporter clears registry and internal state
105
+ * ```
106
+ */
107
+ async onApplicationShutdown() {
108
+ await this.exporter.shutdown();
109
+ }
110
+ };
111
+ PrometheusModule = PrometheusModule_1 = __decorate([
112
+ Global(),
113
+ Module({
114
+ providers: [PrometheusExporter],
115
+ exports: [PrometheusExporter],
116
+ controllers: [MetricsController],
117
+ }),
118
+ __metadata("design:paramtypes", [PrometheusExporter,
119
+ InstrumentationRegistry])
120
+ ], PrometheusModule);
121
+ export { PrometheusModule };
122
+ //# sourceMappingURL=prometheus.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prometheus.module.js","sourceRoot":"","sources":["../src/prometheus.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAA2D,MAAM,gBAAgB,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAOI,IAAM,gBAAgB,wBAAtB,MAAM,gBAAgB;IAwBV;IACA;IAxBlB;;;;;;;;;;;;;;;OAeG;IACI,MAAM,CAAC,OAAO;QACpB,OAAO;YACN,MAAM,EAAE,kBAAgB;SACxB,CAAC;IACH,CAAC;IAED,YACkB,QAA4B,EAC5B,QAAiC;QADjC,aAAQ,GAAR,QAAQ,CAAoB;QAC5B,aAAQ,GAAR,QAAQ,CAAyB;IAChD,CAAC;IAEJ;;;;;;;;;;;;;;OAcG;IACI,YAAY;QAClB,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;;;;;;;;OAYG;IACI,KAAK,CAAC,qBAAqB;QACjC,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAChC,CAAC;CACD,CAAA;AA/DY,gBAAgB;IAN5B,MAAM,EAAE;IACR,MAAM,CAAC;QACP,SAAS,EAAE,CAAC,kBAAkB,CAAC;QAC/B,OAAO,EAAE,CAAC,kBAAkB,CAAC;QAC7B,WAAW,EAAE,CAAC,iBAAiB,CAAC;KAChC,CAAC;qCAyB2B,kBAAkB;QAClB,uBAAuB;GAzBvC,gBAAgB,CA+D5B"}
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ //# sourceMappingURL=test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,4 @@
1
+ // Test setup file for vitest
2
+ // Import reflect-metadata as required by NestJS
3
+ import 'reflect-metadata';
4
+ //# sourceMappingURL=test-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.js","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,gDAAgD;AAChD,OAAO,kBAAkB,CAAC"}