@usageflow/express 0.1.8 → 0.1.10

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
@@ -1,8 +1,9 @@
1
1
  # @usageflow/express
2
2
 
3
- Express.js middleware for UsageFlow API tracking. Easily monitor and analyze your Express.js API usage.
3
+ Express.js middleware for UsageFlow API tracking. Easily monitor and analyze your Express.js API usage with real-time tracking and allocation management.
4
4
 
5
- ⚠️ **Beta Version Notice**: This package is currently in beta. Early adopters may encounter issues. We appreciate your feedback and contributions!
5
+ [![npm version](https://img.shields.io/npm/v/@usageflow/express.svg)](https://www.npmjs.com/package/@usageflow/express)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
7
 
7
8
  ## Installation
8
9
 
@@ -13,78 +14,234 @@ npm install @usageflow/express
13
14
  ## Quick Start
14
15
 
15
16
  ```javascript
16
- const express = require("express");
17
- const { ExpressUsageFlowAPI } = require("@usageflow/express");
17
+ const express = require('express');
18
+ const { ExpressUsageFlowAPI } = require('@usageflow/express');
18
19
 
19
20
  const app = express();
20
21
  app.use(express.json());
21
22
 
22
- // Initialize UsageFlow
23
- const usageFlow = new ExpressUsageFlowAPI();
24
- usageFlow.init("YOUR_API_KEY");
23
+ // Initialize UsageFlow with API key and optional pool size
24
+ const usageFlow = new ExpressUsageFlowAPI({
25
+ apiKey: 'YOUR_API_KEY',
26
+ poolSize: 5, // Optional: Number of WebSocket connections (default: 5)
27
+ });
25
28
 
26
29
  // Create middleware
27
30
  const middleware = usageFlow.createMiddleware(
28
31
  [
29
- { method: "*", url: "*" }, // Track all routes
32
+ { method: '*', url: '*' }, // Track all routes
30
33
  ],
31
34
  [
32
- { method: "GET", url: "/api/health" }, // Whitelist health check
33
- ],
35
+ { method: 'GET', url: '/api/health' }, // Whitelist health check
36
+ ]
34
37
  );
35
38
 
36
39
  // Apply middleware
37
40
  app.use(middleware);
38
41
 
39
42
  // Your routes
40
- app.get("/api/users", (req, res) => {
41
- res.json({ users: ["John", "Jane"] });
43
+ app.get('/api/users', (req, res) => {
44
+ res.json({ users: ['John', 'Jane'] });
42
45
  });
43
46
 
44
47
  app.listen(3000, () => {
45
- console.log("Server running on port 3000");
48
+ console.log('Server running on port 3000');
46
49
  });
47
50
  ```
48
51
 
49
52
  ## TypeScript Support
50
53
 
51
54
  ```typescript
52
- import express from "express";
53
- import { ExpressUsageFlowAPI } from "@usageflow/express";
55
+ import express from 'express';
56
+ import { ExpressUsageFlowAPI } from '@usageflow/express';
54
57
 
55
58
  const app = express();
56
59
  app.use(express.json());
57
60
 
58
- const usageFlow = new ExpressUsageFlowAPI();
59
- usageFlow.init("YOUR_API_KEY");
61
+ // Initialize UsageFlow with API key and optional pool size
62
+ const usageFlow = new ExpressUsageFlowAPI({
63
+ apiKey: 'YOUR_API_KEY',
64
+ poolSize: 5, // Optional: Number of WebSocket connections (default: 5)
65
+ });
66
+
67
+ // Create middleware
68
+ const middleware = usageFlow.createMiddleware(
69
+ [
70
+ { method: '*', url: '*' }, // Track all routes
71
+ ],
72
+ [
73
+ { method: 'GET', url: '/api/health' }, // Whitelist health check
74
+ ]
75
+ );
60
76
 
61
- // Rest of the code remains the same
77
+ // Apply middleware
78
+ app.use(middleware);
79
+
80
+ // Your routes
81
+ app.get('/api/users', (req, res) => {
82
+ res.json({ users: ['John', 'Jane'] });
83
+ });
84
+
85
+ app.listen(3000, () => {
86
+ console.log('Server running on port 3000');
87
+ });
62
88
  ```
63
89
 
64
- ## Configuration Options
90
+ ## Configuration
91
+
92
+ ### ExpressUsageFlowAPI
93
+
94
+ #### Constructor Options
95
+
96
+ ```typescript
97
+ interface UsageFlowAPIConfig {
98
+ apiKey: string; // Your UsageFlow API key (required)
99
+ poolSize?: number; // Number of WebSocket connections (default: 5)
100
+ }
101
+ ```
102
+
103
+ ### createMiddleware
104
+
105
+ Creates Express middleware for tracking API usage.
106
+
107
+ ```typescript
108
+ createMiddleware(
109
+ routes: Route[], // Routes to track
110
+ whitelistRoutes?: Route[] // Routes to exclude from tracking
111
+ ): (req: Request, res: Response, next: NextFunction) => Promise<void>
112
+ ```
113
+
114
+ #### Route Configuration
65
115
 
66
116
  ```typescript
67
117
  interface Route {
68
- method: string; // HTTP method or '*' for all methods
69
- url: string; // URL pattern or '*' for all URLs
118
+ method: string; // HTTP method ('GET', 'POST', 'PUT', 'DELETE', etc.) or '*' for all methods
119
+ url: string; // URL pattern or '*' for all URLs
70
120
  }
121
+ ```
122
+
123
+ #### Examples
124
+
125
+ **Track all routes:**
126
+
127
+ ```typescript
128
+ const middleware = usageFlow.createMiddleware([{ method: '*', url: '*' }]);
129
+ ```
130
+
131
+ **Track specific routes:**
71
132
 
72
- usageFlow.createMiddleware(
73
- routes: Route[], // Routes to track
74
- whitelistRoutes?: Route[] // Routes to exclude from tracking
133
+ ```typescript
134
+ const middleware = usageFlow.createMiddleware([
135
+ { method: 'GET', url: '/api/users' },
136
+ { method: 'POST', url: '/api/users' },
137
+ { method: 'PUT', url: '/api/users/:id' },
138
+ ]);
139
+ ```
140
+
141
+ **Track with whitelist:**
142
+
143
+ ```typescript
144
+ const middleware = usageFlow.createMiddleware(
145
+ [
146
+ { method: '*', url: '*' }, // Track all routes
147
+ ],
148
+ [
149
+ { method: 'GET', url: '/api/health' }, // Exclude health check
150
+ { method: 'GET', url: '/api/metrics' }, // Exclude metrics
151
+ ]
75
152
  );
76
153
  ```
77
154
 
78
- ## Beta Status
155
+ ## Features
156
+
157
+ - **Automatic Route Detection**: Automatically detects route patterns from Express router
158
+ - **Request Metadata Collection**: Collects comprehensive request metadata including headers, query params, path params, and body
159
+ - **Response Tracking**: Tracks response status codes and duration
160
+ - **WebSocket Communication**: Uses WebSocket for real-time communication with UsageFlow API
161
+ - **Connection Pooling**: Maintains a pool of WebSocket connections for better performance
162
+ - **Header Sanitization**: Automatically sanitizes sensitive headers (authorization, API keys)
163
+ - **Error Handling**: Gracefully handles errors and provides meaningful error messages
164
+
165
+ ## Request Metadata
166
+
167
+ The middleware automatically collects the following metadata for each request:
168
+
169
+ - HTTP method
170
+ - Route pattern
171
+ - Raw URL
172
+ - Client IP (with X-Forwarded-For support)
173
+ - User agent
174
+ - Timestamp
175
+ - Headers (sanitized)
176
+ - Query parameters
177
+ - Path parameters
178
+ - Request body
179
+ - Response status code
180
+ - Request duration
181
+
182
+ ## Error Handling
183
+
184
+ If an allocation request fails, the middleware will:
185
+
186
+ 1. Return a 400 status code
187
+ 2. Include an error message in the response
188
+ 3. Set `blocked: true` in the response body
189
+
190
+ ```typescript
191
+ // Error response format
192
+ {
193
+ message: "Error message",
194
+ blocked: true
195
+ }
196
+ ```
197
+
198
+ ## Advanced Usage
199
+
200
+ ### Custom Route Pattern Extraction
201
+
202
+ The middleware automatically extracts route patterns from Express. It supports:
203
+
204
+ - Direct route paths (`req.route.path`)
205
+ - Router stack traversal
206
+ - Nested routers
207
+ - Parameterized routes (`/users/:id`)
208
+
209
+ ### Ledger ID Generation
210
+
211
+ The middleware automatically generates ledger IDs based on:
212
+
213
+ 1. HTTP method and route pattern
214
+ 2. Configured identity fields from UsageFlow policies
215
+ 3. Identity field locations (path params, query params, body, bearer token, headers)
79
216
 
80
- This is a beta release meant for early adopters. You may encounter:
217
+ ## Requirements
81
218
 
82
- - API changes in future versions
83
- - Incomplete features
84
- - Potential bugs
219
+ - Node.js >= 18.0.0
220
+ - Express >= 4.17.0
221
+ - TypeScript >= 5.0.0 (for TypeScript projects)
85
222
 
86
- We're actively working on improvements and appreciate your feedback!
223
+ ## Dependencies
224
+
225
+ - `@usageflow/core`: Core UsageFlow functionality
226
+ - `express`: Express.js framework
227
+
228
+ ## Development
229
+
230
+ ```bash
231
+ # Install dependencies
232
+ npm install
233
+
234
+ # Build the package
235
+ npm run build
236
+
237
+ # Run tests
238
+ npm test
239
+ ```
87
240
 
88
241
  ## License
89
242
 
90
243
  MIT
244
+
245
+ ## Support
246
+
247
+ For issues, questions, or contributions, please visit our [GitHub repository](https://github.com/usageflow/js-mongorepo).
package/dist/plugin.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Request, Response, NextFunction } from "express";
2
- import { UsageFlowAPI, Route } from "@usageflow/core";
2
+ import { UsageFlowAPI, Route, UsageFlowAPIConfig } from "@usageflow/core";
3
3
  export declare class ExpressUsageFlowAPI extends UsageFlowAPI {
4
+ constructor(options: UsageFlowAPIConfig);
4
5
  /**
5
6
  * Get the route pattern (e.g., /express/users/:id) from the request
6
7
  * Tries multiple methods to get the route pattern since request.route
package/dist/plugin.js CHANGED
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExpressUsageFlowAPI = void 0;
4
4
  const core_1 = require("@usageflow/core");
5
5
  class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
6
+ constructor(options) {
7
+ super(options);
8
+ }
6
9
  /**
7
10
  * Get the route pattern (e.g., /express/users/:id) from the request
8
11
  * Tries multiple methods to get the route pattern since request.route
@@ -114,7 +117,6 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
114
117
  }
115
118
  }
116
119
  catch (error) {
117
- console.error("Error parsing response body:", error);
118
120
  }
119
121
  }
120
122
  const headers = {
@@ -132,7 +134,6 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
132
134
  metadata: metadata,
133
135
  };
134
136
  self.useAllocationRequest(payload).catch((err) => {
135
- console.error("[UsageFlow] Error finalizing allocation request:", err);
136
137
  throw err;
137
138
  });
138
139
  // Send via WebSocket if connected
@@ -148,8 +149,6 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
148
149
  next();
149
150
  }
150
151
  catch (error) {
151
- console.error("[UsageFlow] Error executing request with metadata:", error);
152
- console.error("[UsageFlow] Error stack:", error?.stack);
153
152
  response.status(400).json({
154
153
  message: error.message,
155
154
  blocked: true,
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";;;AACA,0CAA+G;AAI/G,MAAa,mBAAoB,SAAQ,mBAAY;IACjD;;;;OAIG;IAEK,KAAK,CAAC,sBAAsB,CAChC,OAAgB;QAEhB,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAChC,OAAO,CAAC,OAAiC,CAC5C,CAAC;QAEF,4CAA4C;QAC5C,IAAI,QAAQ,GAAG,OAAO,CAAC,EAAE,CAAC;QAC1B,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACxD,IAAI,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACnD,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,CAAC;QAED,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAU,EAAE,EAAE;YACtF,6BAA6B;YAC7B,IAAI,CAAC,KAAK,CAAC,KAAK;gBAAE,OAAO,KAAK,CAAC;YAC/B,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;gBACb,OAAO,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC;YACrC,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACf,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC1C,CAAC;QAEL,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC;QAE/B,MAAM,QAAQ,GAAoB;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,GAAG,EAAE,YAAY;YACjB,MAAM,EAAE,OAAO,CAAC,WAAW,IAAI,GAAG;YAClC,QAAQ,EAAE,QAAQ,IAAI,SAAS;YAC/B,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAW;YAClD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO;YACP,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;YACzE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;YAC1E,IAAI,EAAE,OAAO,CAAC,IAAI;SACrB,CAAC;QAEF,OAAO,QAAQ,CAAC;IACpB,CAAC;IAEO,KAAK,CAAC,0BAA0B,CACpC,QAAgB,EAChB,QAAyB,EACzB,OAAgB,EAChB,QAAkB;QAElB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,OAAO,GAAG;YACZ,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,CAAC;YACT,QAAQ;YACR,QAAQ,EAAE,IAAI;SACjB,CAAC;QAEF,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAsC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC5F,CAAC;IAEM,gBAAgB,CAAC,MAAe,EAAE,kBAA2B,EAAE;QAClE,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC;QAGlB,OAAO,KAAK,EAAE,OAAgB,EAAE,QAAkB,EAAE,IAAkB,EAAE,EAAE;YACtE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YACnD,MAAM,GAAG,GAAG,YAAY,CAAC;YAEzB,OAAO,CAAC,SAAS,GAAG;gBAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACxB,CAAC;YAEF,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,YAAY,CAAC,EAAE,CAAC;gBACxD,OAAO,IAAI,EAAE,CAAC;YAClB,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,CAAC,EAAE,CAAC;gBACzD,OAAO,IAAI,EAAE,CAAC;YAClB,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;YAC5D,QAAQ,CAAC,GAAG,GAAG,GAAG,CAAC;YACnB,IAAI,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAE3C,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,0BAA0B,CACjC,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,QAAQ,CACX,CAAC;gBAEF,wBAAwB;gBACxB,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC;gBACjC,QAAQ,CAAC,GAAG,GAAG,UAEX,KAAW,EACX,QAAyB,EACzB,EAAe;oBAEf,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC;wBAC9B,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;oBACjE,CAAC;oBAED,MAAM,QAAQ,GACV,OAAO,CAAC,SAAS,EAAE,QAAQ,IAAI,EAAE,CAAC;oBACtC,QAAQ,CAAC,kBAAkB,GAAG,QAAQ,CAAC,UAAU,CAAC;oBAElD,6CAA6C;oBAC7C,IAAI,KAAK,EAAE,CAAC;wBACR,IAAI,CAAC;4BACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gCAC5B,wCAAwC;gCACxC,IAAI,CAAC;oCACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;oCAChC,QAAgB,CAAC,IAAI,GAAG,MAAM,CAAC;gCACpC,CAAC;gCAAC,MAAM,CAAC;oCACL,0CAA0C;oCACzC,QAAgB,CAAC,IAAI,GAAG,KAAK,CAAC;gCACnC,CAAC;4BACL,CAAC;iCAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gCAChC,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gCACnC,wCAAwC;gCACxC,IAAI,CAAC;oCACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oCAC9B,QAAgB,CAAC,IAAI,GAAG,MAAM,CAAC;gCACpC,CAAC;gCAAC,MAAM,CAAC;oCACL,0CAA0C;oCACzC,QAAgB,CAAC,IAAI,GAAG,GAAG,CAAC;gCACjC,CAAC;4BACL,CAAC;iCAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gCAClC,QAAgB,CAAC,IAAI,GAAG,KAAK,CAAC;4BACnC,CAAC;wBACL,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BACb,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;wBACzD,CAAC;oBACL,CAAC;oBAED,MAAM,OAAO,GAAG;wBACZ,aAAa,EAAE,IAAI,CAAC,MAAO;wBAC3B,cAAc,EAAE,kBAAkB;qBACrC,CAAC;oBAEF,IAAI,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC;wBAC/B,QAAQ,CAAC,eAAe;4BACpB,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC;oBACjD,CAAC;oBAED,MAAM,OAAO,GAAyB;wBAClC,KAAK,EAAE,QAAQ;wBACf,MAAM,EAAE,CAAC;wBACT,YAAY,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO;wBACxC,QAAQ,EAAE,QAA2B;qBACxC,CAAC;oBAEF,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAQ,EAAE,EAAE;wBAClD,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,GAAG,CAAC,CAAC;wBACvE,MAAM,GAAG,CAAC;oBACd,CAAC,CAAC,CAAC;oBACH,kCAAkC;oBAGlC,kCAAkC;oBAClC,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;wBAC9B,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;oBAC5D,CAAC;oBACD,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;wBACjC,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;oBAC3D,CAAC;oBACD,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;gBACjE,CAAwB,CAAC;gBAEzB,IAAI,EAAE,CAAC;YACX,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBAClB,OAAO,CAAC,KAAK,CAAC,oDAAoD,EAAE,KAAK,CAAC,CAAC;gBAC3E,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;gBACxD,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACtB,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,OAAO,EAAE,IAAI;iBAChB,CAAC,CAAC;gBACH,OAAO;YACX,CAAC;QACL,CAAC,CAAC;IACN,CAAC;CAEJ;AAtMD,kDAsMC"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/jest.config.js ADDED
@@ -0,0 +1,14 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ roots: ['<rootDir>/test'],
5
+ testMatch: ['**/*.test.ts'],
6
+ transform: {
7
+ '^.+\\.ts$': 'ts-jest',
8
+ },
9
+ moduleFileExtensions: ['ts', 'js', 'json'],
10
+ collectCoverageFrom: ['src/**/*.ts'],
11
+ coverageDirectory: 'coverage',
12
+ testTimeout: 10000,
13
+ };
14
+
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@usageflow/express",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "UsageFlow plugin for Express applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
- "test": "jest",
9
+ "test": "tsx --test test/plugin.test.ts",
10
10
  "prepare": "npm run build",
11
11
  "prepublishOnly": "npm run build"
12
12
  },
@@ -14,7 +14,8 @@
14
14
  "access": "public"
15
15
  },
16
16
  "dependencies": {
17
- "@usageflow/core": "^0.2.4",
17
+ "@usageflow/core": "^0.2.6",
18
+ "@usageflow/logger": "^0.1.1",
18
19
  "express": "^4.18.0",
19
20
  "ws": "^8.16.0"
20
21
  },
@@ -29,9 +30,7 @@
29
30
  "@types/node": "^20.0.0",
30
31
  "@types/ws": "^8.5.10",
31
32
  "typescript": "^5.0.0",
32
- "jest": "^29.0.0",
33
- "@types/jest": "^29.0.0",
34
- "ts-jest": "^29.0.0"
33
+ "tsx": "^4.7.0"
35
34
  },
36
35
  "peerDependencies": {
37
36
  "express": ">=4.17.0"
package/src/plugin.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import { Request, Response, NextFunction } from "express";
2
- import { UsageFlowAPI, Route, RequestMetadata, RequestForAllocation, UsageFlowRequest } from "@usageflow/core";
2
+ import { UsageFlowAPI, Route, RequestMetadata, RequestForAllocation, UsageFlowRequest, UsageFlowAPIConfig } from "@usageflow/core";
3
3
 
4
4
 
5
5
 
6
6
  export class ExpressUsageFlowAPI extends UsageFlowAPI {
7
+ constructor(options: UsageFlowAPIConfig) {
8
+ super(options);
9
+ }
7
10
  /**
8
11
  * Get the route pattern (e.g., /express/users/:id) from the request
9
12
  * Tries multiple methods to get the route pattern since request.route
@@ -150,7 +153,6 @@ export class ExpressUsageFlowAPI extends UsageFlowAPI {
150
153
  (metadata as any).body = chunk;
151
154
  }
152
155
  } catch (error) {
153
- console.error("Error parsing response body:", error);
154
156
  }
155
157
  }
156
158
 
@@ -172,7 +174,6 @@ export class ExpressUsageFlowAPI extends UsageFlowAPI {
172
174
  };
173
175
 
174
176
  self.useAllocationRequest(payload).catch((err: any) => {
175
- console.error("[UsageFlow] Error finalizing allocation request:", err);
176
177
  throw err;
177
178
  });
178
179
  // Send via WebSocket if connected
@@ -190,8 +191,6 @@ export class ExpressUsageFlowAPI extends UsageFlowAPI {
190
191
 
191
192
  next();
192
193
  } catch (error: any) {
193
- console.error("[UsageFlow] Error executing request with metadata:", error);
194
- console.error("[UsageFlow] Error stack:", error?.stack);
195
194
  response.status(400).json({
196
195
  message: error.message,
197
196
  blocked: true,
@@ -0,0 +1,201 @@
1
+ import { test, describe, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { ExpressUsageFlowAPI } from '../src/plugin';
4
+ import { Route } from '@usageflow/core';
5
+
6
+ describe('ExpressUsageFlowAPI', () => {
7
+ let api: ExpressUsageFlowAPI;
8
+
9
+ beforeEach(() => {
10
+ api = new ExpressUsageFlowAPI({
11
+ apiKey: 'test-api-key',
12
+ poolSize: 1
13
+ });
14
+ // Mock socket manager
15
+ api.socketManager = {
16
+ connected: false,
17
+ async connect() { this.connected = true; },
18
+ isConnected() { return this.connected; },
19
+ async sendAsync<T>(payload: any): Promise<any> {
20
+ return { type: 'success', payload: { allocationId: 'test-id' } };
21
+ },
22
+ send() { },
23
+ close() { },
24
+ destroy() { }
25
+ } as any;
26
+ });
27
+
28
+ afterEach(() => {
29
+ api.destroy();
30
+ });
31
+
32
+ test('should create middleware', () => {
33
+ const routes: Route[] = [
34
+ { method: 'GET', url: '/users' }
35
+ ];
36
+
37
+ const middleware = api.createMiddleware(routes);
38
+ assert.strictEqual(typeof middleware, 'function');
39
+ assert.strictEqual(middleware.length, 3); // Express middleware signature
40
+ });
41
+
42
+ test('should create middleware with whitelist', () => {
43
+ const routes: Route[] = [
44
+ { method: '*', url: '*' }
45
+ ];
46
+ const whitelistRoutes: Route[] = [
47
+ { method: 'GET', url: '/health' }
48
+ ];
49
+
50
+ const middleware = api.createMiddleware(routes, whitelistRoutes);
51
+ assert.strictEqual(typeof middleware, 'function');
52
+ });
53
+
54
+ test.skip('should skip whitelisted routes', async () => {
55
+ const routes: Route[] = [
56
+ { method: '*', url: '*' }
57
+ ];
58
+ const whitelistRoutes: Route[] = [
59
+ { method: 'GET', url: '/health' }
60
+ ];
61
+
62
+ const middleware = api.createMiddleware(routes, whitelistRoutes);
63
+
64
+ const req: any = {
65
+ method: 'GET',
66
+ url: '/health',
67
+ path: '/health',
68
+ route: { path: '/health' },
69
+ headers: {},
70
+ query: {},
71
+ params: {},
72
+ body: {},
73
+ app: { _router: { stack: [] } },
74
+ usageflow: undefined
75
+ };
76
+
77
+ const res: any = {
78
+ statusCode: 200,
79
+ end: (chunk?: any, encoding?: any, cb?: any) => {
80
+ if (typeof chunk === 'function') {
81
+ chunk();
82
+ } else if (typeof encoding === 'function') {
83
+ encoding();
84
+ } else if (cb) {
85
+ cb();
86
+ }
87
+ }
88
+ };
89
+
90
+ let nextCalled = false;
91
+ const next = () => { nextCalled = true; };
92
+
93
+ await middleware(req, res, next);
94
+
95
+ assert.strictEqual(nextCalled, true);
96
+ assert.strictEqual(req.usageflow, undefined);
97
+ });
98
+
99
+ test('should monitor matching routes', async () => {
100
+ const routes: Route[] = [
101
+ { method: 'GET', url: '/users' }
102
+ ];
103
+
104
+ const middleware = api.createMiddleware(routes);
105
+
106
+ const req: any = {
107
+ method: 'GET',
108
+ url: '/users',
109
+ path: '/users',
110
+ route: { path: '/users' },
111
+ headers: {},
112
+ query: {},
113
+ params: {},
114
+ body: {},
115
+ ip: '127.0.0.1',
116
+ originalUrl: '/users',
117
+ app: { _router: { stack: [] } },
118
+ usageflow: undefined
119
+ };
120
+
121
+ const res: any = {
122
+ statusCode: 200,
123
+ end: (chunk?: any, encoding?: any, cb?: any) => {
124
+ if (typeof chunk === 'function') {
125
+ chunk();
126
+ } else if (typeof encoding === 'function') {
127
+ encoding();
128
+ } else if (cb) {
129
+ cb();
130
+ }
131
+ }
132
+ };
133
+
134
+ let nextCalled = false;
135
+ const next = () => { nextCalled = true; };
136
+
137
+ await middleware(req, res, next);
138
+
139
+ assert.strictEqual(nextCalled, true);
140
+ assert.ok(req.usageflow);
141
+ assert.ok(req.usageflow.startTime);
142
+ });
143
+
144
+ test.skip('should handle errors', async () => {
145
+ // Make socket manager throw error
146
+ api.socketManager = {
147
+ connected: false,
148
+ async connect() { this.connected = true; },
149
+ isConnected() { return this.connected; },
150
+ async sendAsync<T>(payload: any): Promise<any> {
151
+ throw new Error('Connection failed');
152
+ },
153
+ send() { },
154
+ close() { },
155
+ destroy() { }
156
+ } as any;
157
+
158
+ const routes: Route[] = [
159
+ { method: 'GET', url: '/users' }
160
+ ];
161
+
162
+ const middleware = api.createMiddleware(routes);
163
+
164
+ const req: any = {
165
+ method: 'GET',
166
+ url: '/users',
167
+ path: '/users',
168
+ route: { path: '/users' },
169
+ headers: {},
170
+ query: {},
171
+ params: {},
172
+ body: {},
173
+ ip: '127.0.0.1',
174
+ originalUrl: '/users',
175
+ app: { _router: { stack: [] } },
176
+ usageflow: undefined
177
+ };
178
+
179
+ let statusCode = 200;
180
+ let responseBody: any = null;
181
+ const res: any = {
182
+ statusCode: 200,
183
+ status: (code: number) => {
184
+ statusCode = code;
185
+ return res;
186
+ },
187
+ json: (body: any) => {
188
+ responseBody = body;
189
+ },
190
+ end: () => { }
191
+ };
192
+
193
+ const next = () => { };
194
+
195
+ await middleware(req, res, next);
196
+
197
+ assert.strictEqual(statusCode, 400);
198
+ assert.strictEqual(responseBody.blocked, true);
199
+ });
200
+ });
201
+
package/tsconfig.json CHANGED
@@ -5,5 +5,6 @@
5
5
  "rootDir": "./src"
6
6
  },
7
7
  "include": ["src/**/*"],
8
+ "exclude": ["test/**/*", "node_modules", "dist"],
8
9
  "references": [{ "path": "../core" }]
9
10
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./test-compiled",
5
+ "rootDir": "./test",
6
+ "noEmit": false
7
+ },
8
+ "include": ["test/**/*"],
9
+ "exclude": ["src/**/*"]
10
+ }