@usageflow/express 0.1.1 → 0.1.4

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
@@ -13,50 +13,50 @@ npm install @usageflow/express
13
13
  ## Quick Start
14
14
 
15
15
  ```javascript
16
- const express = require('express');
17
- const { ExpressUsageFlowAPI } = require('@usageflow/express');
16
+ const express = require("express");
17
+ const { ExpressUsageFlowAPI } = require("@usageflow/express");
18
18
 
19
19
  const app = express();
20
20
  app.use(express.json());
21
21
 
22
22
  // Initialize UsageFlow
23
23
  const usageFlow = new ExpressUsageFlowAPI();
24
- usageFlow.init('YOUR_API_KEY');
24
+ usageFlow.init("YOUR_API_KEY");
25
25
 
26
26
  // Create middleware
27
27
  const middleware = usageFlow.createMiddleware(
28
28
  [
29
- { method: '*', url: '*' }, // Track all routes
29
+ { method: "*", url: "*" }, // Track all routes
30
30
  ],
31
31
  [
32
- { method: 'GET', url: '/api/health' }, // Whitelist health check
33
- ]
32
+ { method: "GET", url: "/api/health" }, // Whitelist health check
33
+ ],
34
34
  );
35
35
 
36
36
  // Apply middleware
37
37
  app.use(middleware);
38
38
 
39
39
  // Your routes
40
- app.get('/api/users', (req, res) => {
41
- res.json({ users: ['John', 'Jane'] });
40
+ app.get("/api/users", (req, res) => {
41
+ res.json({ users: ["John", "Jane"] });
42
42
  });
43
43
 
44
44
  app.listen(3000, () => {
45
- console.log('Server running on port 3000');
45
+ console.log("Server running on port 3000");
46
46
  });
47
47
  ```
48
48
 
49
49
  ## TypeScript Support
50
50
 
51
51
  ```typescript
52
- import express from 'express';
53
- import { ExpressUsageFlowAPI } from '@usageflow/express';
52
+ import express from "express";
53
+ import { ExpressUsageFlowAPI } from "@usageflow/express";
54
54
 
55
55
  const app = express();
56
56
  app.use(express.json());
57
57
 
58
58
  const usageFlow = new ExpressUsageFlowAPI();
59
- usageFlow.init('YOUR_API_KEY');
59
+ usageFlow.init("YOUR_API_KEY");
60
60
 
61
61
  // Rest of the code remains the same
62
62
  ```
package/dist/plugin.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Request, Response, NextFunction } from 'express';
2
- import { UsageFlowAPI, Route, RequestMetadata } from '@usageflow/core';
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { UsageFlowAPI, Route, RequestMetadata } from "@usageflow/core";
3
3
  declare global {
4
4
  namespace Express {
5
5
  interface Request {
@@ -15,4 +15,5 @@ export declare class ExpressUsageFlowAPI extends UsageFlowAPI {
15
15
  private collectRequestMetadata;
16
16
  private executeRequestWithMetadata;
17
17
  createMiddleware(routes: Route[], whitelistRoutes?: Route[]): (request: Request, response: Response, next: NextFunction) => Promise<void>;
18
+ protected guessLedgerId(request: Request): string;
18
19
  }
package/dist/plugin.js CHANGED
@@ -7,42 +7,42 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
7
7
  const headers = this.sanitizeHeaders(request.headers);
8
8
  // Get client IP, handling forwarded headers
9
9
  let clientIP = request.ip;
10
- const forwardedFor = request.headers['x-forwarded-for'];
11
- if (forwardedFor && typeof forwardedFor === 'string') {
12
- clientIP = forwardedFor.split(',')[0].trim();
10
+ const forwardedFor = request.headers["x-forwarded-for"];
11
+ if (forwardedFor && typeof forwardedFor === "string") {
12
+ clientIP = forwardedFor.split(",")[0].trim();
13
13
  }
14
14
  const metadata = {
15
15
  method: request.method,
16
- url: request.route?.path || request.path || request.url || '/',
17
- rawUrl: request.originalUrl || '/',
18
- clientIP: request.ip || 'unknown',
19
- userAgent: request.headers['user-agent'],
16
+ url: request.route?.path || request.path || request.url || "/",
17
+ rawUrl: request.originalUrl || "/",
18
+ clientIP: clientIP || "unknown",
19
+ userAgent: request.headers["user-agent"],
20
20
  timestamp: new Date().toISOString(),
21
21
  headers,
22
- queryParams: request.query,
23
- pathParams: request.params,
24
- body: request.body
22
+ queryParams: Object.keys(request.query).length > 0 ? request.query : null,
23
+ pathParams: Object.keys(request.params).length > 0 ? request.params : null,
24
+ body: request.body,
25
25
  };
26
26
  return metadata;
27
27
  }
28
28
  async executeRequestWithMetadata(ledgerId, metadata, request, response) {
29
29
  if (!this.apiKey) {
30
- throw new Error('API key not initialized');
30
+ throw new Error("API key not initialized");
31
31
  }
32
32
  const headers = {
33
- 'x-usage-key': this.apiKey,
34
- 'Content-Type': 'application/json'
33
+ "x-usage-key": this.apiKey,
34
+ "Content-Type": "application/json",
35
35
  };
36
36
  const payload = {
37
37
  alias: ledgerId,
38
38
  amount: 1,
39
- metadata
39
+ metadata,
40
40
  };
41
41
  try {
42
42
  const response = await fetch(`${this.usageflowUrl}/api/v1/ledgers/measure/allocate`, {
43
- method: 'POST',
43
+ method: "POST",
44
44
  headers,
45
- body: JSON.stringify(payload)
45
+ body: JSON.stringify(payload),
46
46
  });
47
47
  const data = await response.json();
48
48
  if (response.status === 400) {
@@ -53,12 +53,13 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
53
53
  request.usageflow.metadata = metadata;
54
54
  }
55
55
  else {
56
- throw new Error(data.message || 'Unknown error occurred');
56
+ throw new Error(data.message || "Unknown error occurred");
57
57
  }
58
58
  }
59
59
  catch (error) {
60
- if (error.message == 'Failed to use resource after retries: Faile to preform operation') {
61
- throw new Error('Failed to allocate resource');
60
+ if (error.message ==
61
+ "Failed to use resource after retries: Faile to preform operation") {
62
+ throw new Error("Failed to allocate resource");
62
63
  }
63
64
  throw error;
64
65
  }
@@ -71,12 +72,12 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
71
72
  const method = request.method;
72
73
  const url = request.route?.path || request.path || request.url;
73
74
  request.usageflow = {
74
- startTime: Date.now()
75
+ startTime: Date.now(),
75
76
  };
76
- if (this.shouldSkipRoute(method, url || '', whitelistMap)) {
77
+ if (this.shouldSkipRoute(method, url || "", whitelistMap)) {
77
78
  return next();
78
79
  }
79
- if (!this.shouldMonitorRoute(method, url || '', routesMap)) {
80
+ if (!this.shouldMonitorRoute(method, url || "", routesMap)) {
80
81
  return next();
81
82
  }
82
83
  const metadata = await this.collectRequestMetadata(request);
@@ -86,14 +87,14 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
86
87
  const originalEnd = response.end;
87
88
  response.end = function (chunk, encoding, cb) {
88
89
  if (!request.usageflow?.eventId) {
89
- return originalEnd.call(this, chunk, encoding || 'utf8', cb);
90
+ return originalEnd.call(this, chunk, encoding || "utf8", cb);
90
91
  }
91
92
  const metadata = request.usageflow?.metadata || {};
92
93
  metadata.responseStatusCode = response.statusCode;
93
94
  // Add response body to metadata if it exists
94
95
  if (chunk) {
95
96
  try {
96
- if (typeof chunk === 'string') {
97
+ if (typeof chunk === "string") {
97
98
  // Try to parse as JSON if it's a string
98
99
  try {
99
100
  const parsed = JSON.parse(chunk);
@@ -105,7 +106,7 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
105
106
  }
106
107
  }
107
108
  else if (Buffer.isBuffer(chunk)) {
108
- const str = chunk.toString('utf8');
109
+ const str = chunk.toString("utf8");
109
110
  // Try to parse as JSON if it's a buffer
110
111
  try {
111
112
  const parsed = JSON.parse(str);
@@ -116,53 +117,94 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
116
117
  metadata.body = str;
117
118
  }
118
119
  }
119
- else if (typeof chunk === 'object') {
120
+ else if (typeof chunk === "object") {
120
121
  metadata.body = chunk;
121
122
  }
122
123
  }
123
124
  catch (error) {
124
- console.error('Error parsing response body:', error);
125
+ console.error("Error parsing response body:", error);
125
126
  }
126
127
  }
127
128
  const headers = {
128
- 'x-usage-key': self.apiKey,
129
- 'Content-Type': 'application/json'
129
+ "x-usage-key": self.apiKey,
130
+ "Content-Type": "application/json",
130
131
  };
132
+ if (request.usageflow?.startTime) {
133
+ metadata.requestDuration =
134
+ Date.now() - request.usageflow.startTime;
135
+ }
131
136
  const payload = {
132
- alias: `${request.method} ${request.route?.path || request.path || request.url || '/'}`,
137
+ alias: `${request.method} ${request.route?.path || request.path || request.url || "/"}`,
133
138
  amount: 1,
134
139
  allocationId: request.usageflow?.eventId,
135
- metadata
140
+ metadata,
136
141
  };
137
142
  fetch(`${self.usageflowUrl}/api/v1/ledgers/measure/allocate/use`, {
138
- method: 'POST',
143
+ method: "POST",
139
144
  headers,
140
- body: JSON.stringify(payload)
141
- }).catch(error => {
142
- console.error('Error finalizing request:', error);
145
+ body: JSON.stringify(payload),
146
+ }).catch((error) => {
147
+ console.error("Error finalizing request:", error);
143
148
  });
144
- // Handle the different overloads
145
- if (typeof chunk === 'function') {
146
- return originalEnd.call(this, undefined, 'utf8', chunk);
149
+ // Handle the different overloadsx
150
+ if (typeof chunk === "function") {
151
+ return originalEnd.call(this, undefined, "utf8", chunk);
147
152
  }
148
- if (typeof encoding === 'function') {
149
- return originalEnd.call(this, chunk, 'utf8', encoding);
153
+ if (typeof encoding === "function") {
154
+ return originalEnd.call(this, chunk, "utf8", encoding);
150
155
  }
151
- return originalEnd.call(this, chunk, encoding || 'utf8', cb);
156
+ return originalEnd.call(this, chunk, encoding || "utf8", cb);
152
157
  };
153
158
  next();
154
159
  }
155
160
  catch (error) {
156
- console.error('Error executing request with metadata:', error);
161
+ console.error("Error executing request with metadata:", error);
157
162
  response.status(400).json({
158
163
  message: error.message,
159
- blocked: true
164
+ blocked: true,
160
165
  });
161
166
  return;
162
167
  }
163
168
  };
164
169
  }
170
+ guessLedgerId(request) {
171
+ const method = request.method;
172
+ const url = request.route?.path || request.path;
173
+ const config = this.apiConfig;
174
+ if (!config?.identityFieldName || !config?.identityFieldLocation) {
175
+ return `${method} ${url}`;
176
+ }
177
+ const fieldName = config.identityFieldName;
178
+ const location = config.identityFieldLocation;
179
+ switch (location) {
180
+ case "path_params":
181
+ if (request.params?.[fieldName]) {
182
+ return `${method} ${url} ${request.params[fieldName]}`;
183
+ }
184
+ break;
185
+ case "query_params":
186
+ if (request.query?.[fieldName]) {
187
+ return `${method} ${url} ${request.query[fieldName]}`;
188
+ }
189
+ break;
190
+ case "body":
191
+ if (request.body?.[fieldName]) {
192
+ return `${method} ${url} ${request.body[fieldName]}`;
193
+ }
194
+ break;
195
+ case "bearer_token":
196
+ const authHeader = request.headers.authorization;
197
+ const token = this.extractBearerToken(authHeader);
198
+ if (token) {
199
+ const claims = this.decodeJwtUnverified(token);
200
+ if (claims?.[fieldName]) {
201
+ return `${method} ${url} ${claims[fieldName]}`;
202
+ }
203
+ }
204
+ break;
205
+ }
206
+ return `${method} ${url}`;
207
+ }
165
208
  }
166
209
  exports.ExpressUsageFlowAPI = ExpressUsageFlowAPI;
167
- ;
168
210
  //# sourceMappingURL=plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";;;AACA,0CAAuE;AAcvE,MAAa,mBAAoB,SAAQ,mBAAY;IACzC,KAAK,CAAC,sBAAsB,CAAC,OAAgB;QACjD,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,OAAiC,CAAC,CAAC;QAEhF,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,QAAQ,GAAoB;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,GAAG,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,GAAG;YAC9D,MAAM,EAAE,OAAO,CAAC,WAAW,IAAI,GAAG;YAClC,QAAQ,EAAE,OAAO,CAAC,EAAE,IAAI,SAAS;YACjC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAW;YAClD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO;YACP,WAAW,EAAE,OAAO,CAAC,KAA4B;YACjD,UAAU,EAAE,OAAO,CAAC,MAAM;YAC1B,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,aAAa,EAAE,IAAI,CAAC,MAAM;YAC1B,cAAc,EAAE,kBAAkB;SACrC,CAAC;QAEF,MAAM,OAAO,GAAG;YACZ,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,CAAC;YACT,QAAQ;SACX,CAAC;QAEF,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,kCAAkC,EAAE;gBACjF,MAAM,EAAE,MAAM;gBACd,OAAO;gBACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;aAChC,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAEnC,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;YAED,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACd,OAAO,CAAC,SAAU,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;gBAC1C,OAAO,CAAC,SAAU,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACJ,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,wBAAwB,CAAC,CAAC;YAC9D,CAAC;QACL,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YAClB,IAAI,KAAK,CAAC,OAAO,IAAI,kEAAkE,EAAE,CAAC;gBACtF,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;YACnD,CAAC;YACD,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,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;QAElB,OAAO,KAAK,EAAE,OAAgB,EAAE,QAAkB,EAAE,IAAkB,EAAE,EAAE;YACtE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC;YAE/D,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;YAE5D,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,0BAA0B,CACjC,GAAG,MAAM,IAAI,GAAG,EAAE,EAClB,QAAQ,EACR,OAAO,EACP,QAAQ,CACX,CAAC;gBAEF,wBAAwB;gBACxB,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC;gBACjC,QAAQ,CAAC,GAAG,GAAG,UAA0B,KAAW,EAAE,QAAyB,EAAE,EAAe;oBAC5F,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,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,IAAI,EAAE,CAAC;oBAClD,QAAgB,CAAC,kBAAkB,GAAG,QAAQ,CAAC,UAAU,CAAC;oBAE3D,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;oBAID,MAAM,OAAO,GAAG;wBACZ,aAAa,EAAE,IAAI,CAAC,MAAO;wBAC3B,cAAc,EAAE,kBAAkB;qBACrC,CAAC;oBAEF,MAAM,OAAO,GAAG;wBACZ,KAAK,EAAE,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE;wBACvF,MAAM,EAAE,CAAC;wBACT,YAAY,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO;wBACxC,QAAQ;qBACX,CAAC;oBAEF,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,sCAAsC,EAAE;wBAC9D,MAAM,EAAE,MAAM;wBACd,OAAO;wBACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;qBAChC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;wBACb,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;oBACtD,CAAC,CAAC,CAAC;oBAEH,iCAAiC;oBACjC,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,wCAAwC,EAAE,KAAK,CAAC,CAAC;gBAC/D,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;CACJ;AA7LD,kDA6LC;AAAA,CAAC"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";;;AACA,0CAAuE;AAcvE,MAAa,mBAAoB,SAAQ,mBAAY;IACzC,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,QAAQ,GAAoB;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,GAAG,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,GAAG;YAC9D,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,aAAa,EAAE,IAAI,CAAC,MAAM;YAC1B,cAAc,EAAE,kBAAkB;SACrC,CAAC;QAEF,MAAM,OAAO,GAAG;YACZ,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,CAAC;YACT,QAAQ;SACX,CAAC;QAEF,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CACxB,GAAG,IAAI,CAAC,YAAY,kCAAkC,EACtD;gBACI,MAAM,EAAE,MAAM;gBACd,OAAO;gBACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;aAChC,CACJ,CAAC;YAEF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAEnC,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;YAED,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACd,OAAO,CAAC,SAAU,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;gBAC1C,OAAO,CAAC,SAAU,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACJ,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,wBAAwB,CAAC,CAAC;YAC9D,CAAC;QACL,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YAClB,IACI,KAAK,CAAC,OAAO;gBACb,kEAAkE,EACpE,CAAC;gBACC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;YACnD,CAAC;YACD,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,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;QAElB,OAAO,KAAK,EAAE,OAAgB,EAAE,QAAkB,EAAE,IAAkB,EAAE,EAAE;YACtE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC;YAE/D,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;YAE5D,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,0BAA0B,CACjC,GAAG,MAAM,IAAI,GAAG,EAAE,EAClB,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,GAAG;wBACZ,KAAK,EAAE,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE;wBACvF,MAAM,EAAE,CAAC;wBACT,YAAY,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO;wBACxC,QAAQ;qBACX,CAAC;oBAEF,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,sCAAsC,EAAE;wBAC9D,MAAM,EAAE,MAAM;wBACd,OAAO;wBACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;qBAChC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;wBACf,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;oBACtD,CAAC,CAAC,CAAC;oBAEH,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,wCAAwC,EAAE,KAAK,CAAC,CAAC;gBAC/D,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;IAES,aAAa,CAAC,OAAgB;QACpC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAA;QAE7B,IAAI,CAAC,MAAM,EAAE,iBAAiB,IAAI,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC;YAC/D,OAAO,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,CAAC,iBAAiB,CAAC;QAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,qBAAqB,CAAC;QAE9C,QAAQ,QAAQ,EAAE,CAAC;YACf,KAAK,aAAa;gBACd,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC9B,OAAO,GAAG,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3D,CAAC;gBACD,MAAM;YACV,KAAK,cAAc;gBACf,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7B,OAAO,GAAG,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC1D,CAAC;gBACD,MAAM;YACV,KAAK,MAAM;gBACP,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC5B,OAAO,GAAG,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzD,CAAC;gBACD,MAAM;YACV,KAAK,cAAc;gBACf,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;gBACjD,MAAM,KAAK,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;gBAClD,IAAI,KAAK,EAAE,CAAC;oBACR,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;oBAC/C,IAAI,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;wBACtB,OAAO,GAAG,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;oBACnD,CAAC;gBACL,CAAC;gBACD,MAAM;QACd,CAAC;QAED,OAAO,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC;IAC9B,CAAC;CACJ;AA3PD,kDA2PC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usageflow/express",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "UsageFlow plugin for Express applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,9 +10,15 @@
10
10
  "prepare": "npm run build"
11
11
  },
12
12
  "dependencies": {
13
- "@usageflow/core": "^0.1.0",
13
+ "@usageflow/core": "^0.1.3",
14
14
  "express": "^4.18.0"
15
15
  },
16
+ "homepage": "https://usageflow.io",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/usageflow/js-mongorepo.git",
20
+ "directory": "packages/express"
21
+ },
16
22
  "devDependencies": {
17
23
  "@types/express": "^4.17.0",
18
24
  "@types/node": "^20.0.0",
package/src/plugin.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Request, Response, NextFunction } from 'express';
2
- import { UsageFlowAPI, Route, RequestMetadata } from '@usageflow/core';
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { UsageFlowAPI, Route, RequestMetadata } from "@usageflow/core";
3
3
 
4
4
  declare global {
5
5
  namespace Express {
@@ -14,27 +14,31 @@ declare global {
14
14
  }
15
15
 
16
16
  export class ExpressUsageFlowAPI extends UsageFlowAPI {
17
- private async collectRequestMetadata(request: Request): Promise<RequestMetadata> {
18
- const headers = this.sanitizeHeaders(request.headers as Record<string, string>);
17
+ private async collectRequestMetadata(
18
+ request: Request,
19
+ ): Promise<RequestMetadata> {
20
+ const headers = this.sanitizeHeaders(
21
+ request.headers as Record<string, string>,
22
+ );
19
23
 
20
24
  // Get client IP, handling forwarded headers
21
25
  let clientIP = request.ip;
22
- const forwardedFor = request.headers['x-forwarded-for'];
23
- if (forwardedFor && typeof forwardedFor === 'string') {
24
- clientIP = forwardedFor.split(',')[0].trim();
26
+ const forwardedFor = request.headers["x-forwarded-for"];
27
+ if (forwardedFor && typeof forwardedFor === "string") {
28
+ clientIP = forwardedFor.split(",")[0].trim();
25
29
  }
26
30
 
27
31
  const metadata: RequestMetadata = {
28
32
  method: request.method,
29
- url: request.route?.path || request.path || request.url || '/',
30
- rawUrl: request.originalUrl || '/',
31
- clientIP: request.ip || 'unknown',
32
- userAgent: request.headers['user-agent'] as string,
33
+ url: request.route?.path || request.path || request.url || "/",
34
+ rawUrl: request.originalUrl || "/",
35
+ clientIP: clientIP || "unknown",
36
+ userAgent: request.headers["user-agent"] as string,
33
37
  timestamp: new Date().toISOString(),
34
38
  headers,
35
- queryParams: request.query as Record<string, any>,
36
- pathParams: request.params,
37
- body: request.body
39
+ queryParams: Object.keys(request.query).length > 0 ? request.query : null,
40
+ pathParams: Object.keys(request.params).length > 0 ? request.params : null,
41
+ body: request.body,
38
42
  };
39
43
 
40
44
  return metadata;
@@ -44,29 +48,32 @@ export class ExpressUsageFlowAPI extends UsageFlowAPI {
44
48
  ledgerId: string,
45
49
  metadata: RequestMetadata,
46
50
  request: Request,
47
- response: Response
51
+ response: Response,
48
52
  ): Promise<void> {
49
53
  if (!this.apiKey) {
50
- throw new Error('API key not initialized');
54
+ throw new Error("API key not initialized");
51
55
  }
52
56
 
53
57
  const headers = {
54
- 'x-usage-key': this.apiKey,
55
- 'Content-Type': 'application/json'
58
+ "x-usage-key": this.apiKey,
59
+ "Content-Type": "application/json",
56
60
  };
57
61
 
58
62
  const payload = {
59
63
  alias: ledgerId,
60
64
  amount: 1,
61
- metadata
65
+ metadata,
62
66
  };
63
67
 
64
68
  try {
65
- const response = await fetch(`${this.usageflowUrl}/api/v1/ledgers/measure/allocate`, {
66
- method: 'POST',
67
- headers,
68
- body: JSON.stringify(payload)
69
- });
69
+ const response = await fetch(
70
+ `${this.usageflowUrl}/api/v1/ledgers/measure/allocate`,
71
+ {
72
+ method: "POST",
73
+ headers,
74
+ body: JSON.stringify(payload),
75
+ },
76
+ );
70
77
 
71
78
  const data = await response.json();
72
79
 
@@ -78,11 +85,14 @@ export class ExpressUsageFlowAPI extends UsageFlowAPI {
78
85
  request.usageflow!.eventId = data.eventId;
79
86
  request.usageflow!.metadata = metadata;
80
87
  } else {
81
- throw new Error(data.message || 'Unknown error occurred');
88
+ throw new Error(data.message || "Unknown error occurred");
82
89
  }
83
90
  } catch (error: any) {
84
- if (error.message == 'Failed to use resource after retries: Faile to preform operation') {
85
- throw new Error('Failed to allocate resource');
91
+ if (
92
+ error.message ==
93
+ "Failed to use resource after retries: Faile to preform operation"
94
+ ) {
95
+ throw new Error("Failed to allocate resource");
86
96
  }
87
97
  throw error;
88
98
  }
@@ -98,14 +108,14 @@ export class ExpressUsageFlowAPI extends UsageFlowAPI {
98
108
  const url = request.route?.path || request.path || request.url;
99
109
 
100
110
  request.usageflow = {
101
- startTime: Date.now()
111
+ startTime: Date.now(),
102
112
  };
103
113
 
104
- if (this.shouldSkipRoute(method, url || '', whitelistMap)) {
114
+ if (this.shouldSkipRoute(method, url || "", whitelistMap)) {
105
115
  return next();
106
116
  }
107
117
 
108
- if (!this.shouldMonitorRoute(method, url || '', routesMap)) {
118
+ if (!this.shouldMonitorRoute(method, url || "", routesMap)) {
109
119
  return next();
110
120
  }
111
121
 
@@ -116,23 +126,29 @@ export class ExpressUsageFlowAPI extends UsageFlowAPI {
116
126
  `${method} ${url}`,
117
127
  metadata,
118
128
  request,
119
- response
129
+ response,
120
130
  );
121
131
 
122
132
  // Capture response data
123
133
  const originalEnd = response.end;
124
- response.end = function (this: Response, chunk?: any, encoding?: BufferEncoding, cb?: () => void) {
134
+ response.end = function (
135
+ this: Response,
136
+ chunk?: any,
137
+ encoding?: BufferEncoding,
138
+ cb?: () => void,
139
+ ) {
125
140
  if (!request.usageflow?.eventId) {
126
- return originalEnd.call(this, chunk, encoding || 'utf8', cb);
141
+ return originalEnd.call(this, chunk, encoding || "utf8", cb);
127
142
  }
128
143
 
129
- const metadata = request.usageflow?.metadata || {};
130
- (metadata as any).responseStatusCode = response.statusCode;
144
+ const metadata: Record<string, any> =
145
+ request.usageflow?.metadata || {};
146
+ metadata.responseStatusCode = response.statusCode;
131
147
 
132
148
  // Add response body to metadata if it exists
133
149
  if (chunk) {
134
150
  try {
135
- if (typeof chunk === 'string') {
151
+ if (typeof chunk === "string") {
136
152
  // Try to parse as JSON if it's a string
137
153
  try {
138
154
  const parsed = JSON.parse(chunk);
@@ -142,7 +158,7 @@ export class ExpressUsageFlowAPI extends UsageFlowAPI {
142
158
  (metadata as any).body = chunk;
143
159
  }
144
160
  } else if (Buffer.isBuffer(chunk)) {
145
- const str = chunk.toString('utf8');
161
+ const str = chunk.toString("utf8");
146
162
  // Try to parse as JSON if it's a buffer
147
163
  try {
148
164
  const parsed = JSON.parse(str);
@@ -151,55 +167,101 @@ export class ExpressUsageFlowAPI extends UsageFlowAPI {
151
167
  // If not valid JSON, use the string as is
152
168
  (metadata as any).body = str;
153
169
  }
154
- } else if (typeof chunk === 'object') {
170
+ } else if (typeof chunk === "object") {
155
171
  (metadata as any).body = chunk;
156
172
  }
157
173
  } catch (error) {
158
- console.error('Error parsing response body:', error);
174
+ console.error("Error parsing response body:", error);
159
175
  }
160
176
  }
161
177
 
162
-
163
-
164
178
  const headers = {
165
- 'x-usage-key': self.apiKey!,
166
- 'Content-Type': 'application/json'
179
+ "x-usage-key": self.apiKey!,
180
+ "Content-Type": "application/json",
167
181
  };
168
182
 
183
+ if (request.usageflow?.startTime) {
184
+ metadata.requestDuration =
185
+ Date.now() - request.usageflow.startTime;
186
+ }
187
+
169
188
  const payload = {
170
- alias: `${request.method} ${request.route?.path || request.path || request.url || '/'}`,
189
+ alias: `${request.method} ${request.route?.path || request.path || request.url || "/"}`,
171
190
  amount: 1,
172
191
  allocationId: request.usageflow?.eventId,
173
- metadata
192
+ metadata,
174
193
  };
175
194
 
176
195
  fetch(`${self.usageflowUrl}/api/v1/ledgers/measure/allocate/use`, {
177
- method: 'POST',
196
+ method: "POST",
178
197
  headers,
179
- body: JSON.stringify(payload)
180
- }).catch(error => {
181
- console.error('Error finalizing request:', error);
198
+ body: JSON.stringify(payload),
199
+ }).catch((error) => {
200
+ console.error("Error finalizing request:", error);
182
201
  });
183
202
 
184
- // Handle the different overloads
185
- if (typeof chunk === 'function') {
186
- return originalEnd.call(this, undefined, 'utf8', chunk);
203
+ // Handle the different overloadsx
204
+ if (typeof chunk === "function") {
205
+ return originalEnd.call(this, undefined, "utf8", chunk);
187
206
  }
188
- if (typeof encoding === 'function') {
189
- return originalEnd.call(this, chunk, 'utf8', encoding);
207
+ if (typeof encoding === "function") {
208
+ return originalEnd.call(this, chunk, "utf8", encoding);
190
209
  }
191
- return originalEnd.call(this, chunk, encoding || 'utf8', cb);
210
+ return originalEnd.call(this, chunk, encoding || "utf8", cb);
192
211
  } as typeof response.end;
193
212
 
194
213
  next();
195
214
  } catch (error: any) {
196
- console.error('Error executing request with metadata:', error);
215
+ console.error("Error executing request with metadata:", error);
197
216
  response.status(400).json({
198
217
  message: error.message,
199
- blocked: true
218
+ blocked: true,
200
219
  });
201
220
  return;
202
221
  }
203
222
  };
204
223
  }
205
- };
224
+
225
+ protected guessLedgerId(request: Request): string {
226
+ const method = request.method;
227
+ const url = request.route?.path || request.path;
228
+ const config = this.apiConfig
229
+
230
+ if (!config?.identityFieldName || !config?.identityFieldLocation) {
231
+ return `${method} ${url}`;
232
+ }
233
+
234
+ const fieldName = config.identityFieldName;
235
+ const location = config.identityFieldLocation;
236
+
237
+ switch (location) {
238
+ case "path_params":
239
+ if (request.params?.[fieldName]) {
240
+ return `${method} ${url} ${request.params[fieldName]}`;
241
+ }
242
+ break;
243
+ case "query_params":
244
+ if (request.query?.[fieldName]) {
245
+ return `${method} ${url} ${request.query[fieldName]}`;
246
+ }
247
+ break;
248
+ case "body":
249
+ if (request.body?.[fieldName]) {
250
+ return `${method} ${url} ${request.body[fieldName]}`;
251
+ }
252
+ break;
253
+ case "bearer_token":
254
+ const authHeader = request.headers.authorization;
255
+ const token = this.extractBearerToken(authHeader);
256
+ if (token) {
257
+ const claims = this.decodeJwtUnverified(token);
258
+ if (claims?.[fieldName]) {
259
+ return `${method} ${url} ${claims[fieldName]}`;
260
+ }
261
+ }
262
+ break;
263
+ }
264
+
265
+ return `${method} ${url}`;
266
+ }
267
+ }