0http-bun 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -206,11 +206,14 @@ Bun.serve({
206
206
 
207
207
  0http-bun includes a comprehensive middleware system with built-in middlewares for common use cases:
208
208
 
209
+ > 📦 **Note**: Starting with v1.2.2, some middleware dependencies are optional. Install only what you need: `jose` (JWT), `pino` (Logger), `prom-client` (Prometheus).
210
+
209
211
  - **[Body Parser](./lib/middleware/README.md#body-parser)** - Automatic request body parsing (JSON, form data, text)
210
212
  - **[CORS](./lib/middleware/README.md#cors)** - Cross-Origin Resource Sharing with flexible configuration
211
213
  - **[JWT Authentication](./lib/middleware/README.md#jwt-authentication)** - JSON Web Token authentication and authorization
212
214
  - **[Logger](./lib/middleware/README.md#logger)** - Request logging with multiple output formats
213
215
  - **[Rate Limiting](./lib/middleware/README.md#rate-limiting)** - Flexible rate limiting with sliding window support
216
+ - **[Prometheus Metrics](./lib/middleware/README.md#prometheus-metrics)** - Export metrics for monitoring and alerting
214
217
 
215
218
  ### Quick Example
216
219
 
@@ -2,6 +2,30 @@
2
2
 
3
3
  0http-bun provides a comprehensive middleware system with built-in middlewares for common use cases. All middleware functions are TypeScript-ready and follow the standard middleware pattern.
4
4
 
5
+ ## Dependency Installation
6
+
7
+ ⚠️ **Important**: Starting with v1.2.2, middleware dependencies are now **optional** and must be installed separately when needed. This reduces the framework's footprint and improves startup performance through lazy loading.
8
+
9
+ Install only the dependencies you need:
10
+
11
+ ```bash
12
+ # For JWT Authentication middleware
13
+ bun install jose
14
+
15
+ # For Logger middleware
16
+ bun install pino
17
+
18
+ # For Prometheus Metrics middleware
19
+ bun install prom-client
20
+ ```
21
+
22
+ **Benefits of Lazy Loading:**
23
+
24
+ - 📦 **Smaller Bundle**: Only install what you use
25
+ - ⚡ **Faster Startup**: Dependencies loaded only when middleware is used
26
+ - 💾 **Lower Memory**: Reduced initial memory footprint
27
+ - 🔧 **Better Control**: Explicit dependency management
28
+
5
29
  ## Table of Contents
6
30
 
7
31
  - [Middleware Pattern](#middleware-pattern)
@@ -10,6 +34,7 @@
10
34
  - [CORS](#cors)
11
35
  - [JWT Authentication](#jwt-authentication)
12
36
  - [Logger](#logger)
37
+ - [Prometheus Metrics](#prometheus-metrics)
13
38
  - [Rate Limiting](#rate-limiting)
14
39
  - [Creating Custom Middleware](#creating-custom-middleware)
15
40
 
@@ -50,6 +75,7 @@ import {
50
75
  createLogger,
51
76
  createJWTAuth,
52
77
  createRateLimit,
78
+ createPrometheusIntegration,
53
79
  } from '0http-bun/lib/middleware'
54
80
  ```
55
81
 
@@ -65,6 +91,9 @@ const {
65
91
  createJWTAuth,
66
92
  createLogger,
67
93
  createRateLimit,
94
+ createPrometheusMiddleware,
95
+ createMetricsHandler,
96
+ createPrometheusIntegration,
68
97
  } = require('0http-bun/lib/middleware')
69
98
  ```
70
99
 
@@ -78,6 +107,9 @@ import {
78
107
  createJWTAuth,
79
108
  createLogger,
80
109
  createRateLimit,
110
+ createPrometheusMiddleware,
111
+ createMetricsHandler,
112
+ createPrometheusIntegration,
81
113
  } from '0http-bun/lib/middleware'
82
114
 
83
115
  // Import types
@@ -94,6 +126,8 @@ import type {
94
126
 
95
127
  Automatically parses request bodies based on Content-Type header.
96
128
 
129
+ > ✅ **No additional dependencies required** - Uses Bun's built-in parsing capabilities.
130
+
97
131
  ```javascript
98
132
  const {createBodyParser} = require('0http-bun/lib/middleware')
99
133
 
@@ -142,6 +176,8 @@ router.use(createBodyParser(bodyParserOptions))
142
176
 
143
177
  Cross-Origin Resource Sharing middleware with flexible configuration.
144
178
 
179
+ > ✅ **No additional dependencies required** - Built-in CORS implementation.
180
+
145
181
  ```javascript
146
182
  const {createCORS} = require('0http-bun/lib/middleware')
147
183
 
@@ -173,7 +209,7 @@ router.use(
173
209
  },
174
210
  }),
175
211
  )
176
- ````
212
+ ```
177
213
 
178
214
  **TypeScript Usage:**
179
215
 
@@ -194,6 +230,8 @@ router.use(createCORS(corsOptions))
194
230
 
195
231
  JSON Web Token authentication and authorization middleware with support for static secrets, JWKS endpoints, and API key authentication.
196
232
 
233
+ > 📦 **Required dependency**: `bun install jose`
234
+
197
235
  #### Basic JWT with Static Secret
198
236
 
199
237
  ```javascript
@@ -445,6 +483,9 @@ router.get('/api/profile', (req) => {
445
483
 
446
484
  Request logging middleware with customizable output formats.
447
485
 
486
+ > 📦 **Required dependency for structured logging**: `bun install pino`
487
+ > ✅ **Simple logger** (`simpleLogger`) has no dependencies - uses `console.log`
488
+
448
489
  ```javascript
449
490
  const {createLogger, simpleLogger} = require('0http-bun/lib/middleware')
450
491
 
@@ -503,10 +544,228 @@ router.use(createLogger(loggerOptions))
503
544
  - `tiny` - Minimal output
504
545
  - `dev` - Development-friendly colored output
505
546
 
547
+ ### Prometheus Metrics
548
+
549
+ Comprehensive Prometheus metrics integration for monitoring and observability with built-in security and performance optimizations.
550
+
551
+ > 📦 **Required dependency**: `bun install prom-client`
552
+
553
+ ```javascript
554
+ const {
555
+ createPrometheusMiddleware,
556
+ createMetricsHandler,
557
+ createPrometheusIntegration,
558
+ } = require('0http-bun/lib/middleware')
559
+
560
+ // Simple setup with default metrics
561
+ const prometheus = createPrometheusIntegration()
562
+
563
+ router.use(prometheus.middleware)
564
+ router.get('/metrics', prometheus.metricsHandler)
565
+ ```
566
+
567
+ #### Default Metrics Collected
568
+
569
+ The Prometheus middleware automatically collects:
570
+
571
+ - **HTTP Request Duration** - Histogram of request durations in seconds (buckets: 0.001, 0.005, 0.015, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 1, 2, 5, 10)
572
+ - **HTTP Request Count** - Counter of total requests by method, route, and status
573
+ - **HTTP Request Size** - Histogram of request body sizes (buckets: 1B, 10B, 100B, 1KB, 10KB, 100KB, 1MB, 10MB)
574
+ - **HTTP Response Size** - Histogram of response body sizes (buckets: 1B, 10B, 100B, 1KB, 10KB, 100KB, 1MB, 10MB)
575
+ - **Active Connections** - Gauge of currently active HTTP connections
576
+ - **Node.js Metrics** - Memory usage, CPU, garbage collection (custom buckets), event loop lag (5ms precision)
577
+
578
+ #### Advanced Configuration
579
+
580
+ ```javascript
581
+ const {
582
+ createPrometheusMiddleware,
583
+ createMetricsHandler,
584
+ createPrometheusIntegration,
585
+ } = require('0http-bun/lib/middleware')
586
+
587
+ const prometheus = createPrometheusIntegration({
588
+ // Control default Node.js metrics collection
589
+ collectDefaultMetrics: true,
590
+
591
+ // Exclude paths from metrics collection (optimized for performance)
592
+ excludePaths: ['/health', '/ping', '/favicon.ico'],
593
+
594
+ // Skip certain HTTP methods
595
+ skipMethods: ['OPTIONS'],
596
+
597
+ // Custom route normalization with security controls
598
+ normalizeRoute: (req) => {
599
+ const url = new URL(req.url, 'http://localhost')
600
+ return url.pathname
601
+ .replace(/\/users\/\d+/, '/users/:id')
602
+ .replace(/\/api\/v\d+/, '/api/:version')
603
+ },
604
+
605
+ // Add custom labels with automatic sanitization
606
+ extractLabels: (req, response) => {
607
+ return {
608
+ user_type: req.headers.get('x-user-type') || 'anonymous',
609
+ api_version: req.headers.get('x-api-version') || 'v1',
610
+ }
611
+ },
612
+
613
+ // Use custom metrics object instead of default metrics
614
+ metrics: customMetricsObject,
615
+ })
616
+ ```
617
+
618
+ #### Custom Business Metrics
619
+
620
+ ```javascript
621
+ const {
622
+ createPrometheusIntegration,
623
+ } = require('0http-bun/lib/middleware')
624
+
625
+ // Get the prometheus client from the integration
626
+ const prometheus = createPrometheusIntegration()
627
+ const {promClient} = prometheus
628
+
629
+ // Create custom metrics
630
+ const orderCounter = new promClient.Counter({
631
+ name: 'orders_total',
632
+ help: 'Total number of orders processed',
633
+ labelNames: ['status', 'payment_method'],
634
+ })
635
+
636
+ const orderValue = new promClient.Histogram({
637
+ name: 'order_value_dollars',
638
+ help: 'Value of orders in dollars',
639
+ labelNames: ['payment_method'],
640
+ buckets: [10, 50, 100, 500, 1000, 5000],
641
+ })
642
+
643
+ // Use in your routes
644
+ router.post('/orders', async (req) => {
645
+ const order = await processOrder(req.body)
646
+
647
+ // Record custom metrics
648
+ orderCounter.inc({
649
+ status: order.status,
650
+ payment_method: order.payment_method,
651
+ })
652
+
653
+ if (order.status === 'completed') {
654
+ orderValue.observe(
655
+ {
656
+ payment_method: order.payment_method,
657
+ },
658
+ order.amount,
659
+ )
660
+ }
661
+
662
+ return Response.json(order)
663
+ })
664
+ ```
665
+
666
+ #### Metrics Endpoint Options
667
+
668
+ ```javascript
669
+ const {createMetricsHandler} = require('0http-bun/lib/middleware')
670
+
671
+ // Custom metrics endpoint
672
+ const metricsHandler = createMetricsHandler({
673
+ endpoint: '/custom-metrics', // Default: '/metrics'
674
+ registry: customRegistry, // Default: promClient.register
675
+ })
676
+
677
+ router.get('/custom-metrics', metricsHandler)
678
+ ```
679
+
680
+ #### Route Normalization & Security
681
+
682
+ The middleware automatically normalizes routes and implements security measures to prevent high cardinality and potential attacks:
683
+
684
+ ```javascript
685
+ // URLs like these:
686
+ // /users/123, /users/456, /users/789
687
+ // Are normalized to: /users/:id
688
+
689
+ // /products/abc-123, /products/def-456
690
+ // Are normalized to: /products/:slug
691
+
692
+ // /api/v1/data, /api/v2/data
693
+ // Are normalized to: /api/:version/data
694
+
695
+ // Route sanitization examples:
696
+ // /users/:id → _users__id (special characters replaced with underscores)
697
+ // /api/v1/orders → _api_v1_orders
698
+ // Very long tokens → _api__token (pattern-based normalization)
699
+ ```
700
+
701
+ **Route Sanitization:**
702
+
703
+ - Special characters (`/`, `:`, etc.) are replaced with underscores (`_`) for Prometheus compatibility
704
+ - UUIDs are automatically normalized to `:id` patterns
705
+ - Long tokens (>20 characters) are normalized to `:token` patterns
706
+ - Numeric IDs are normalized to `:id` patterns
707
+ - Route complexity is limited to 10 segments maximum
708
+
709
+ **Security Features:**
710
+
711
+ - **Label Sanitization**: Removes potentially dangerous characters from metric labels and truncates values to 100 characters
712
+ - **Cardinality Limits**: Prevents memory exhaustion from too many unique metric combinations
713
+ - **Route Complexity Limits**: Caps the number of route segments to 10 to prevent DoS attacks
714
+ - **Size Limits**: Limits request/response body size processing (up to 100MB) to prevent memory issues
715
+ - **Header Processing Limits**: Caps the number of headers processed per request (50 for requests, 20 for responses)
716
+ - **URL Processing**: Handles both full URLs and pathname-only URLs with proper fallback handling
717
+
718
+ #### Performance Optimizations
719
+
720
+ - **Fast Path for Excluded Routes**: Bypasses all metric collection for excluded paths with smart URL parsing
721
+ - **Lazy Evaluation**: Only processes metrics when actually needed
722
+ - **Efficient Size Calculation**: Optimized request/response size measurement with capping at 1MB estimation
723
+ - **Error Handling**: Graceful handling of malformed URLs and invalid data with fallback mechanisms
724
+ - **Header Count Limits**: Prevents excessive header processing overhead (50 request headers, 20 response headers)
725
+ - **Smart URL Parsing**: Handles both full URLs and pathname-only URLs efficiently
726
+
727
+ #### Production Considerations
728
+
729
+ - **Performance**: Adds <1ms overhead per request with optimized fast paths
730
+ - **Memory**: Metrics stored in memory with cardinality controls; use recording rules for high cardinality
731
+ - **Security**: Built-in protections against label injection and cardinality bombs
732
+ - **Cardinality**: Automatic limits prevent high cardinality issues
733
+ - **Monitoring**: Consider protecting `/metrics` endpoint in production
734
+
735
+ #### Integration with Monitoring
736
+
737
+ ```yaml
738
+ # prometheus.yml
739
+ scrape_configs:
740
+ - job_name: '0http-bun-app'
741
+ static_configs:
742
+ - targets: ['localhost:3000']
743
+ scrape_interval: 15s
744
+ metrics_path: /metrics
745
+ ```
746
+
747
+ #### Troubleshooting
748
+
749
+ **Common Issues:**
750
+
751
+ - **High Memory Usage**: Check for high cardinality metrics. Route patterns should be normalized (e.g., `/users/:id` not `/users/12345`)
752
+ - **Missing Metrics**: Ensure paths aren't in `excludePaths` and HTTP methods aren't in `skipMethods`
753
+ - **Route Sanitization**: Routes are automatically sanitized (special characters become underscores: `/users/:id` → `_users__id`)
754
+ - **URL Parsing Errors**: The middleware handles both full URLs and pathname-only URLs with graceful fallback
755
+
756
+ **Performance Tips:**
757
+
758
+ - Use `excludePaths` for health checks and static assets
759
+ - Consider using `skipMethods` for OPTIONS requests
760
+ - Monitor memory usage in production for metric cardinality
761
+ - Use Prometheus recording rules for high-cardinality aggregations
762
+
506
763
  ### Rate Limiting
507
764
 
508
765
  Configurable rate limiting middleware with multiple store options.
509
766
 
767
+ > ✅ **No additional dependencies required** - Uses built-in memory store.
768
+
510
769
  ```javascript
511
770
  const {createRateLimit, MemoryStore} = require('0http-bun/lib/middleware')
512
771
 
@@ -590,15 +849,127 @@ class CustomStore implements RateLimitStore {
590
849
  }
591
850
  ```
592
851
 
593
- ````
852
+ #### Sliding Window Rate Limiter
853
+
854
+ For more precise rate limiting, use the sliding window implementation that **prevents burst traffic** at any point in time:
855
+
856
+ ```javascript
857
+ const {createSlidingWindowRateLimit} = require('0http-bun/lib/middleware')
858
+
859
+ // Basic sliding window rate limiter
860
+ router.use(
861
+ createSlidingWindowRateLimit({
862
+ windowMs: 60 * 1000, // 1 minute sliding window
863
+ max: 10, // Max 10 requests per minute
864
+ keyGenerator: (req) => req.headers.get('x-forwarded-for') || 'default',
865
+ }),
866
+ )
867
+ ```
868
+
869
+ **TypeScript Usage:**
870
+
871
+ ```typescript
872
+ import {createSlidingWindowRateLimit} from '0http-bun/lib/middleware'
873
+ import type {RateLimitOptions} from '0http-bun/lib/middleware'
874
+
875
+ const slidingOptions: RateLimitOptions = {
876
+ windowMs: 60 * 1000, // 1 minute
877
+ max: 10, // 10 requests max
878
+ keyGenerator: (req) => req.user?.id || req.headers.get('x-forwarded-for'),
879
+ handler: (req, hits, max, resetTime) => {
880
+ return Response.json(
881
+ {
882
+ error: 'Rate limit exceeded',
883
+ retryAfter: Math.ceil((resetTime.getTime() - Date.now()) / 1000),
884
+ limit: max,
885
+ used: hits,
886
+ },
887
+ {status: 429},
888
+ )
889
+ },
890
+ }
891
+
892
+ router.use(createSlidingWindowRateLimit(slidingOptions))
893
+ ```
894
+
895
+ **How Sliding Window Differs from Fixed Window:**
896
+
897
+ The sliding window approach provides **more accurate and fair rate limiting** by tracking individual request timestamps:
898
+
899
+ - **Fixed Window**: Divides time into discrete chunks (e.g., 09:00:00-09:00:59, 09:01:00-09:01:59)
900
+ - ⚠️ **Problem**: Allows burst traffic at window boundaries (20 requests in 2 seconds)
901
+ - **Sliding Window**: Uses a continuous, moving time window from current moment
902
+ - ✅ **Advantage**: Prevents bursts at any point in time (true rate limiting)
903
+
904
+ **Use Cases for Sliding Window:**
905
+
906
+ ```javascript
907
+ // Financial API - Zero tolerance for payment bursts
908
+ router.use(
909
+ '/api/payments/*',
910
+ createSlidingWindowRateLimit({
911
+ windowMs: 60 * 1000, // 1 minute
912
+ max: 3, // Only 3 payment attempts per minute
913
+ keyGenerator: (req) => req.user.accountId,
914
+ }),
915
+ )
916
+
917
+ // User Registration - Prevent automated signups
918
+ router.use(
919
+ '/api/register',
920
+ createSlidingWindowRateLimit({
921
+ windowMs: 3600 * 1000, // 1 hour
922
+ max: 3, // 3 accounts per IP per hour
923
+ keyGenerator: (req) => req.headers.get('x-forwarded-for'),
924
+ }),
925
+ )
926
+
927
+ // File Upload - Prevent abuse
928
+ router.use(
929
+ '/api/upload',
930
+ createSlidingWindowRateLimit({
931
+ windowMs: 300 * 1000, // 5 minutes
932
+ max: 10, // 10 uploads per 5 minutes
933
+ keyGenerator: (req) => req.user.id,
934
+ }),
935
+ )
936
+ ```
937
+
938
+ **Performance Considerations:**
939
+
940
+ - **Memory Usage**: Higher than fixed window (stores timestamp arrays)
941
+ - **Time Complexity**: O(n) per request where n = requests in window
942
+ - **Best For**: Critical APIs, financial transactions, user-facing features
943
+ - **Use Fixed Window For**: High-volume APIs where approximate limiting is acceptable
944
+
945
+ **Advanced Configuration:**
946
+
947
+ ```typescript
948
+ // Tiered rate limiting based on user level
949
+ const createTieredRateLimit = (req) => {
950
+ const userTier = req.user?.tier || 'free'
951
+ const configs = {
952
+ free: {windowMs: 60 * 1000, max: 10},
953
+ premium: {windowMs: 60 * 1000, max: 100},
954
+ enterprise: {windowMs: 60 * 1000, max: 1000},
955
+ }
956
+ return createSlidingWindowRateLimit(configs[userTier])
957
+ }
958
+ ```
594
959
 
595
960
  **Rate Limit Headers:**
596
961
 
962
+ Both rate limiters send the following headers when `standardHeaders: true`:
963
+
597
964
  - `X-RateLimit-Limit` - Request limit
598
965
  - `X-RateLimit-Remaining` - Remaining requests
599
966
  - `X-RateLimit-Reset` - Reset time (Unix timestamp)
600
967
  - `X-RateLimit-Used` - Used requests
601
968
 
969
+ **Error Handling:**
970
+
971
+ Rate limiting middleware allows errors to bubble up as proper HTTP 500 responses. If your `keyGenerator` function or custom `store.increment()` method throws an error, it will not be caught and masked - the error will propagate up the middleware chain for proper error handling.
972
+
602
973
  ## Creating Custom Middleware
603
974
 
604
975
  ### Basic Middleware
@@ -619,7 +990,7 @@ const customMiddleware = (req: ZeroRequest, next: StepFunction) => {
619
990
  }
620
991
 
621
992
  router.use(customMiddleware)
622
- ````
993
+ ```
623
994
 
624
995
  ### Async Middleware
625
996
 
@@ -682,8 +1053,8 @@ Apply middleware only to specific paths:
682
1053
 
683
1054
  ```typescript
684
1055
  // API-only middleware
685
- router.use('/api/*', jwtAuth({secret: 'api-secret'}))
686
- router.use('/api/*', rateLimit({max: 1000}))
1056
+ router.use('/api/*', createJWTAuth({secret: 'api-secret'}))
1057
+ router.use('/api/*', createRateLimit({max: 1000}))
687
1058
 
688
1059
  // Admin-only middleware
689
1060
  router.use('/admin/*', adminAuthMiddleware)
@@ -755,4 +1126,24 @@ router.get('/api/public/status', () => Response.json({status: 'ok'}))
755
1126
  router.get('/api/protected/data', (req) => Response.json({user: req.user}))
756
1127
  ```
757
1128
 
1129
+ ## Dependency Summary
1130
+
1131
+ For your convenience, here's a quick reference of which dependencies you need to install for each middleware:
1132
+
1133
+ | Middleware | Dependencies Required | Install Command |
1134
+ | ----------------------- | --------------------- | ------------------------- |
1135
+ | **Body Parser** | ✅ None | Built-in |
1136
+ | **CORS** | ✅ None | Built-in |
1137
+ | **Rate Limiting** | ✅ None | Built-in |
1138
+ | **Logger** (simple) | ✅ None | Built-in |
1139
+ | **Logger** (structured) | 📦 `pino` | `bun install pino` |
1140
+ | **JWT Authentication** | 📦 `jose` | `bun install jose` |
1141
+ | **Prometheus Metrics** | 📦 `prom-client` | `bun install prom-client` |
1142
+
1143
+ **Install all optional dependencies at once:**
1144
+
1145
+ ```bash
1146
+ bun install pino jose prom-client
1147
+ ```
1148
+
758
1149
  This middleware stack provides a solid foundation for most web applications with security, logging, and performance features built-in.
@@ -1,5 +1,4 @@
1
- import {RequestHandler, ZeroRequest, StepFunction} from '../../common'
2
- import {Logger} from 'pino'
1
+ import {RequestHandler, ZeroRequest} from '../../common'
3
2
 
4
3
  // Logger middleware types
5
4
  export interface LoggerOptions {
@@ -234,3 +233,68 @@ export function createMultipartParser(
234
233
  export function createBodyParser(options?: BodyParserOptions): RequestHandler
235
234
  export function hasBody(req: ZeroRequest): boolean
236
235
  export function shouldParse(req: ZeroRequest, type: string): boolean
236
+
237
+ // Prometheus metrics middleware types
238
+ export interface PrometheusMetrics {
239
+ httpRequestDuration: any // prom-client Histogram
240
+ httpRequestTotal: any // prom-client Counter
241
+ httpRequestSize: any // prom-client Histogram
242
+ httpResponseSize: any // prom-client Histogram
243
+ httpActiveConnections: any // prom-client Gauge
244
+ }
245
+
246
+ export interface PrometheusMiddlewareOptions {
247
+ /** Custom metrics object to use instead of default metrics */
248
+ metrics?: PrometheusMetrics
249
+ /** Paths to exclude from metrics collection (default: ['/health', '/ping', '/favicon.ico', '/metrics']) */
250
+ excludePaths?: string[]
251
+ /** Whether to collect default Node.js metrics (default: true) */
252
+ collectDefaultMetrics?: boolean
253
+ /** Custom route normalization function */
254
+ normalizeRoute?: (req: ZeroRequest) => string
255
+ /** Custom label extraction function */
256
+ extractLabels?: (
257
+ req: ZeroRequest,
258
+ response: Response,
259
+ ) => Record<string, string>
260
+ /** HTTP methods to skip from metrics collection */
261
+ skipMethods?: string[]
262
+ }
263
+
264
+ export interface MetricsHandlerOptions {
265
+ /** The endpoint path for metrics (default: '/metrics') */
266
+ endpoint?: string
267
+ /** Custom Prometheus registry to use */
268
+ registry?: any // prom-client Registry
269
+ }
270
+
271
+ export interface PrometheusIntegration {
272
+ /** The middleware function */
273
+ middleware: RequestHandler
274
+ /** The metrics handler function */
275
+ metricsHandler: RequestHandler
276
+ /** The Prometheus registry */
277
+ registry: any // prom-client Registry
278
+ /** The prom-client module */
279
+ promClient: any
280
+ }
281
+
282
+ export function createPrometheusMiddleware(
283
+ options?: PrometheusMiddlewareOptions,
284
+ ): RequestHandler
285
+ export function createMetricsHandler(
286
+ options?: MetricsHandlerOptions,
287
+ ): RequestHandler
288
+ export function createPrometheusIntegration(
289
+ options?: PrometheusMiddlewareOptions & MetricsHandlerOptions,
290
+ ): PrometheusIntegration
291
+ export function createDefaultMetrics(): PrometheusMetrics
292
+ export function extractRoutePattern(req: ZeroRequest): string
293
+
294
+ // Simple interface exports for common use cases
295
+ export const logger: typeof createLogger
296
+ export const jwtAuth: typeof createJWTAuth
297
+ export const rateLimit: typeof createRateLimit
298
+ export const cors: typeof createCORS
299
+ export const bodyParser: typeof createBodyParser
300
+ export const prometheus: typeof createPrometheusIntegration
@@ -4,6 +4,7 @@ const jwtAuthModule = require('./jwt-auth')
4
4
  const rateLimitModule = require('./rate-limit')
5
5
  const corsModule = require('./cors')
6
6
  const bodyParserModule = require('./body-parser')
7
+ const prometheusModule = require('./prometheus')
7
8
 
8
9
  module.exports = {
9
10
  // Simple interface for common use cases (matches test expectations)
@@ -12,6 +13,7 @@ module.exports = {
12
13
  rateLimit: rateLimitModule.createRateLimit,
13
14
  cors: corsModule.createCORS,
14
15
  bodyParser: bodyParserModule.createBodyParser,
16
+ prometheus: prometheusModule.createPrometheusIntegration,
15
17
 
16
18
  // Complete factory functions for advanced usage
17
19
  createLogger: loggerModule.createLogger,
@@ -42,4 +44,11 @@ module.exports = {
42
44
  createBodyParser: bodyParserModule.createBodyParser,
43
45
  hasBody: bodyParserModule.hasBody,
44
46
  shouldParse: bodyParserModule.shouldParse,
47
+
48
+ // Prometheus metrics middleware
49
+ createPrometheusMiddleware: prometheusModule.createPrometheusMiddleware,
50
+ createMetricsHandler: prometheusModule.createMetricsHandler,
51
+ createPrometheusIntegration: prometheusModule.createPrometheusIntegration,
52
+ createDefaultMetrics: prometheusModule.createDefaultMetrics,
53
+ extractRoutePattern: prometheusModule.extractRoutePattern,
45
54
  }
@@ -1,4 +1,17 @@
1
- const {jwtVerify, createRemoteJWKSet, errors} = require('jose')
1
+ // Lazy load jose to improve startup performance
2
+ let joseLib = null
3
+ function loadJose() {
4
+ if (!joseLib) {
5
+ try {
6
+ joseLib = require('jose')
7
+ } catch (error) {
8
+ throw new Error(
9
+ 'jose is required for JWT middleware. Install it with: bun install jose',
10
+ )
11
+ }
12
+ }
13
+ return joseLib
14
+ }
2
15
 
3
16
  /**
4
17
  * Creates JWT authentication middleware
@@ -60,6 +73,7 @@ function createJWTAuth(options = {}) {
60
73
  keyLike = jwks
61
74
  }
62
75
  } else if (jwksUri) {
76
+ const {createRemoteJWKSet} = loadJose()
63
77
  keyLike = createRemoteJWKSet(new URL(jwksUri))
64
78
  } else if (typeof secret === 'function') {
65
79
  keyLike = secret
@@ -143,6 +157,7 @@ function createJWTAuth(options = {}) {
143
157
  }
144
158
 
145
159
  // Verify JWT token
160
+ const {jwtVerify} = loadJose()
146
161
  const {payload, protectedHeader} = await jwtVerify(
147
162
  token,
148
163
  keyLike,
@@ -302,16 +317,19 @@ function handleAuthError(error, handlers = {}, req) {
302
317
  message = 'Invalid API key'
303
318
  } else if (error.message === 'JWT verification not configured') {
304
319
  message = 'JWT verification not configured'
305
- } else if (error instanceof errors.JWTExpired) {
306
- message = 'Token expired'
307
- } else if (error instanceof errors.JWTInvalid) {
308
- message = 'Invalid token format'
309
- } else if (error instanceof errors.JWKSNoMatchingKey) {
310
- message = 'Token signature verification failed'
311
- } else if (error.message.includes('audience')) {
312
- message = 'Invalid token audience'
313
- } else if (error.message.includes('issuer')) {
314
- message = 'Invalid token issuer'
320
+ } else {
321
+ const {errors} = loadJose()
322
+ if (error instanceof errors.JWTExpired) {
323
+ message = 'Token expired'
324
+ } else if (error instanceof errors.JWTInvalid) {
325
+ message = 'Invalid token format'
326
+ } else if (error instanceof errors.JWKSNoMatchingKey) {
327
+ message = 'Token signature verification failed'
328
+ } else if (error.message.includes('audience')) {
329
+ message = 'Invalid token audience'
330
+ } else if (error.message.includes('issuer')) {
331
+ message = 'Invalid token issuer'
332
+ }
315
333
  }
316
334
 
317
335
  return new Response(JSON.stringify({error: message}), {