@usageflow/core 0.1.4 → 0.1.6

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 @@
1
+ {"version":3,"file":"socket.js","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,3 +1,33 @@
1
+ declare global {
2
+ namespace Express {
3
+ interface Request {
4
+ usageflow?: {
5
+ startTime: number;
6
+ eventId?: string;
7
+ metadata?: RequestMetadata;
8
+ };
9
+ baseUrl: string;
10
+ }
11
+ }
12
+ namespace Fastify {
13
+ interface FastifyRequest {
14
+ usageflow?: {
15
+ startTime: number;
16
+ eventId?: string;
17
+ metadata?: RequestMetadata;
18
+ };
19
+ }
20
+ }
21
+ namespace NestJS {
22
+ interface Request {
23
+ usageflow?: {
24
+ startTime: number;
25
+ eventId?: string;
26
+ metadata?: RequestMetadata;
27
+ };
28
+ }
29
+ }
30
+ }
1
31
  export interface Route {
2
32
  method: string;
3
33
  url: string;
@@ -22,6 +52,8 @@ export interface ResponseMetadata {
22
52
  timestamp: string;
23
53
  }
24
54
  export interface UsageFlowConfig {
55
+ url: string;
56
+ method: string;
25
57
  identityFieldName?: string;
26
58
  identityFieldLocation?: string;
27
59
  }
@@ -36,3 +68,46 @@ export type RoutesMap = Record<string, Record<string, boolean>>;
36
68
  export declare class UsageFlowError extends Error {
37
69
  constructor(message: string);
38
70
  }
71
+ export interface RequestForAllocation {
72
+ alias: string;
73
+ amount: number;
74
+ allocationId?: string;
75
+ metadata: RequestMetadata;
76
+ duration?: number;
77
+ }
78
+ export type MessageTypes = 'request_for_allocation' | 'use_allocation' | 'get_application_policies';
79
+ export interface UsageFlowSocketMessage {
80
+ type: MessageTypes;
81
+ payload: RequestForAllocation | null;
82
+ }
83
+ export interface UsageFlowSocketResponse<T> {
84
+ type: 'success' | 'error';
85
+ payload: T;
86
+ status: 'success' | 'error' | 400;
87
+ message: string;
88
+ error?: string;
89
+ }
90
+ export interface UsageFlowRequest {
91
+ method: string;
92
+ url?: string;
93
+ path?: string;
94
+ baseUrl?: string;
95
+ route?: {
96
+ path: string;
97
+ };
98
+ app?: any;
99
+ params?: Record<string, any>;
100
+ query?: Record<string, any>;
101
+ body?: any;
102
+ headers: Record<string, string | string[] | undefined> | Headers;
103
+ ip?: string;
104
+ originalUrl?: string;
105
+ usageflow?: {
106
+ eventId?: string;
107
+ metadata?: RequestMetadata;
108
+ startTime?: number;
109
+ };
110
+ routeOptions?: {
111
+ url: string;
112
+ };
113
+ }
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AA0CA,MAAa,cAAe,SAAQ,KAAK;IACrC,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IACjC,CAAC;CACJ;AALD,wCAKC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AA6EA,MAAa,cAAe,SAAQ,KAAK;IACrC,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IACjC,CAAC;CACJ;AALD,wCAKC"}
package/package.json CHANGED
@@ -1,16 +1,21 @@
1
1
  {
2
2
  "name": "@usageflow/core",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Core functionality for UsageFlow integrations",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
9
  "test": "jest",
10
- "prepare": "npm run build"
10
+ "prepare": "npm run build",
11
+ "prepublishOnly": "npm run build"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
11
15
  },
12
16
  "dependencies": {
13
- "axios": "^1.6.0"
17
+ "axios": "^1.6.0",
18
+ "ws": "^8.16.0"
14
19
  },
15
20
  "homepage": "https://usageflow.io",
16
21
  "repository": {
@@ -20,6 +25,7 @@
20
25
  },
21
26
  "devDependencies": {
22
27
  "@types/node": "^20.0.0",
28
+ "@types/ws": "^8.5.10",
23
29
  "typescript": "^5.0.0",
24
30
  "jest": "^29.0.0",
25
31
  "@types/jest": "^29.0.0",
package/src/base.ts CHANGED
@@ -5,15 +5,20 @@ import {
5
5
  UsageFlowError,
6
6
  RoutesMap,
7
7
  Headers,
8
+ RequestForAllocation,
9
+ RequestMetadata,
10
+ UsageFlowRequest,
8
11
  } from "./types";
12
+ import { UsageFlowSocketManger } from "./socket";
9
13
 
10
14
  export abstract class UsageFlowAPI {
11
15
  protected apiKey: string | null = null;
12
16
  protected usageflowUrl: string = "https://api.usageflow.io";
13
-
14
- protected apiConfig: UsageFlowConfig | null = null;
17
+ protected webServer: 'express' | 'fastify' | 'nestjs' = 'express';
18
+ protected apiConfigs: UsageFlowConfig[] = [];
15
19
  private configUpdateInterval: NodeJS.Timeout | null = null;
16
-
20
+ socketManager: UsageFlowSocketManger | null = null;
21
+ private applicationId: boolean = false;
17
22
  /**
18
23
  * Initialize the UsageFlow API with credentials
19
24
  */
@@ -22,12 +27,24 @@ export abstract class UsageFlowAPI {
22
27
  usageflowUrl: string = "https://api.usageflow.io",
23
28
  ): UsageFlowAPI {
24
29
  this.apiKey = apiKey;
25
-
26
30
  this.usageflowUrl = usageflowUrl;
27
- this.startConfigUpdater();
31
+ // this.startConfigUpdater();
32
+ this.socketManager = new UsageFlowSocketManger(apiKey);
33
+ // Connect the socket manager
34
+ this.socketManager.connect().catch((error) => {
35
+ console.error("[UsageFlow] Failed to establish WebSocket connection:", error);
36
+ }).then(() => {
37
+ this.startConfigUpdater();
38
+ });
39
+
40
+
28
41
  return this;
29
42
  }
30
43
 
44
+ public setWebServer(webServer: 'express' | 'fastify' | 'nestjs'): void {
45
+ this.webServer = webServer;
46
+ }
47
+
31
48
  public getApiKey(): string | null {
32
49
  return this.apiKey;
33
50
  }
@@ -44,40 +61,259 @@ export abstract class UsageFlowAPI {
44
61
  clearInterval(this.configUpdateInterval);
45
62
  }
46
63
 
64
+ this.fetchApiPolicies().catch(console.error);
47
65
  this.configUpdateInterval = setInterval(async () => {
48
- try {
49
- const config = await this.fetchApiConfig();
50
- if (config) {
51
- this.apiConfig = config;
52
- }
53
- } catch (error) {
54
- console.error("Error fetching API config:", error);
55
- }
66
+ await this.fetchApiPolicies();
56
67
  }, 60000);
57
68
 
58
- this.fetchApiConfig().catch(console.error);
69
+ // this.fetchApiConfig().catch(console.error);
59
70
  }
60
71
 
61
- /**
62
- * Fetch API configuration from UsageFlow
63
- */
64
- private async fetchApiConfig(): Promise<UsageFlowConfig | null> {
65
- if (!this.apiKey) {
66
- throw new UsageFlowError("API key not initialized");
72
+ protected getRoutePattern(request: UsageFlowRequest): string {
73
+
74
+ if (this.webServer === 'fastify') {
75
+ return request?.routeOptions?.url || request.url || '';
67
76
  }
68
77
 
78
+ const routePattern = request.route?.path || request.app._router.stack.find((route: any) => {
79
+ // a => a.path == request.url
80
+ if (!route.route) return false;
81
+ if (route.path) {
82
+ return route.path == request.url;
83
+ }
84
+
85
+ if (route.regexp) {
86
+ return route.regexp.test(request.url);
87
+ }
88
+
89
+ })?.route?.path
90
+
91
+ if (routePattern) {
92
+ return routePattern;
93
+ }
94
+
95
+ // Method 1: Use request.route.path (available after route matching)
96
+ if (request.route?.path) {
97
+ const baseUrl = request.baseUrl || "";
98
+ const routePath = request.route.path;
99
+ // Combine baseUrl and routePath to get full pattern
100
+ const fullPath = baseUrl + routePath;
101
+ return fullPath || "/";
102
+ }
103
+
104
+ // Method 2: Try to find route from router stack
69
105
  try {
70
- const response = await axios.get(
71
- `${this.usageflowUrl}/api/v1/strategies/application`,
72
- {
73
- headers: { "x-usage-key": this.apiKey },
74
- timeout: 10000,
75
- },
76
- );
77
- return response.data;
106
+ const app = request.app;
107
+ const router = (app as any)._router;
108
+ if (router && router.stack) {
109
+ const method = request.method.toLowerCase();
110
+ const path = request.path || request.url?.split("?")[0] || "";
111
+
112
+ // Search through router stack for matching route
113
+ for (const layer of router.stack) {
114
+ if (layer.route) {
115
+ const route = layer.route;
116
+ const routePath = route.path;
117
+ const routeMethods = Object.keys(route.methods).map(m => m.toLowerCase());
118
+
119
+ // Check if method matches and path pattern matches
120
+ if (routeMethods.includes(method) || routeMethods.includes("*")) {
121
+ // Try to match the pattern by checking if params match
122
+ const pattern = (request.baseUrl || "") + routePath;
123
+ // Simple check: if we have params and the route has params, it might match
124
+ if (request.params && Object.keys(request.params).length > 0) {
125
+ // Check if route path contains the param names
126
+ const paramNames = Object.keys(request.params);
127
+ const routeHasParams = paramNames.some(param => routePath.includes(`:${param}`));
128
+ if (routeHasParams) {
129
+ return pattern;
130
+ }
131
+ } else if (!routePath.includes(":")) {
132
+ // If no params in request and route has no params, check exact match
133
+ if (path === pattern || path === routePath) {
134
+ return pattern;
135
+ }
136
+ }
137
+ }
138
+ } else if (layer.name === "router" && layer.handle && layer.handle.stack) {
139
+ // Handle router middleware (nested routers)
140
+ const mountPath = layer.regexp.source
141
+ .replace("\\/?", "")
142
+ .replace("(?=\\/|$)", "")
143
+ .replace(/\\\//g, "/")
144
+ .replace(/\^/g, "")
145
+ .replace(/\$/g, "")
146
+ .replace(/\\/g, "");
147
+
148
+ for (const subLayer of layer.handle.stack) {
149
+ if (subLayer.route) {
150
+ const route = subLayer.route;
151
+ const routePath = route.path;
152
+ const routeMethods = Object.keys(route.methods).map(m => m.toLowerCase());
153
+
154
+ if (routeMethods.includes(method) || routeMethods.includes("*")) {
155
+ const fullPath = mountPath + routePath;
156
+ // Check if this route matches by examining params
157
+ if (request.params && Object.keys(request.params).length > 0) {
158
+ const paramNames = Object.keys(request.params);
159
+ const routeHasParams = paramNames.some(param => routePath.includes(`:${param}`));
160
+ if (routeHasParams) {
161
+ return fullPath;
162
+ }
163
+ } else if (!routePath.includes(":")) {
164
+ const currentPath = request.path || request.url?.split("?")[0] || "";
165
+ if (currentPath === fullPath || currentPath === routePath) {
166
+ return fullPath;
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
78
175
  } catch (error) {
79
- console.error("Error fetching API config:", error);
80
- return null;
176
+ // Silently fail and try next method
177
+ console.debug("[UsageFlow] Could not extract route from router stack:", error);
178
+ }
179
+
180
+ // Method 3: Reconstruct pattern from params and path
181
+ if (request.params && Object.keys(request.params).length > 0) {
182
+ let path = request.path || request.url?.split("?")[0] || "/";
183
+ const baseUrl = request.baseUrl || "";
184
+
185
+ // Split path into segments and replace matching segments with param names
186
+ const pathSegments = path.split("/");
187
+ const paramEntries = Object.entries(request.params);
188
+
189
+ // Replace segments that match param values with :paramName
190
+ for (let i = 0; i < pathSegments.length; i++) {
191
+ for (const [paramName, paramValue] of paramEntries) {
192
+ if (pathSegments[i] === paramValue) {
193
+ pathSegments[i] = `:${paramName}`;
194
+ break; // Only replace once per segment
195
+ }
196
+ }
197
+ }
198
+
199
+ const pattern = baseUrl + pathSegments.join("/");
200
+ return pattern;
201
+ }
202
+
203
+ // Fallback: return baseUrl + path or just path
204
+ const baseUrl = request.baseUrl || "";
205
+ const path = request.path || request.url?.split("?")[0] || "/";
206
+ return baseUrl + path || "/";
207
+ }
208
+
209
+ public guessLedgerId(request: UsageFlowRequest): string {
210
+ const method = request.method;
211
+ const url = this.getRoutePattern(request);
212
+ const configs = this.apiConfigs
213
+
214
+ if (!configs.length) {
215
+ return `${method} ${url}`;
216
+ }
217
+
218
+
219
+ for (const config of configs) {
220
+ const fieldName = config.identityFieldName!;
221
+ const location = config.identityFieldLocation;
222
+
223
+ switch (location) {
224
+ case "path_params":
225
+ if (request.params?.[fieldName]) {
226
+ return `${method} ${url} ${request.params[fieldName]}`;
227
+ }
228
+ break;
229
+ case "query_params":
230
+ if (request.query?.[fieldName]) {
231
+ return `${method} ${url} ${request.query[fieldName]}`;
232
+ }
233
+ break;
234
+ case "body":
235
+ if (request.body?.[fieldName]) {
236
+ return `${method} ${url} ${request.body[fieldName]}`;
237
+ }
238
+ break;
239
+ case "bearer_token":
240
+ const authHeader = this.getHeaderValue(request.headers, 'authorization');
241
+ const token = this.extractBearerToken(authHeader || undefined);
242
+ if (token) {
243
+ const claims = this.decodeJwtUnverified(token);
244
+ if (claims?.[fieldName]) {
245
+ return `${method} ${url} ${claims[fieldName]}`;
246
+ }
247
+ }
248
+ break;
249
+
250
+ case "headers": {
251
+ const headerValue = this.getHeaderValue(request.headers, fieldName);
252
+ if (headerValue) {
253
+ return `${method} ${url} ${headerValue}`;
254
+ }
255
+ break;
256
+ }
257
+ }
258
+ }
259
+
260
+ return `${method} ${url}`;
261
+ }
262
+
263
+ private async fetchApiPolicies(): Promise<void> {
264
+ if (this.socketManager && this.socketManager.isConnected()) {
265
+ const response = await this.socketManager.sendAsync<{ policies: UsageFlowConfig[], total: number }>({
266
+ type: "get_application_policies",
267
+ payload: null,
268
+ });
269
+ if (response.type === 'success') {
270
+ this.apiConfigs = response.payload?.policies || [];
271
+ }
272
+ }
273
+ }
274
+
275
+ async useAllocationRequest(
276
+ payload: RequestForAllocation,
277
+ ): Promise<void> {
278
+ if (this.socketManager && this.socketManager.isConnected()) {
279
+ this.socketManager.sendAsync<any>({
280
+ type: "use_allocation",
281
+ payload
282
+ }).catch((error) => {
283
+ console.error("[UsageFlow] Error sending finalization via WebSocket:", error);
284
+ throw error;
285
+ });
286
+ }
287
+ }
288
+
289
+
290
+ async allocationRequest(
291
+ request: UsageFlowRequest,
292
+ payload: RequestForAllocation,
293
+ metadata: RequestMetadata,
294
+ ): Promise<void> {
295
+ if (this.socketManager && this.socketManager.isConnected()) {
296
+ try {
297
+ const allocationResponse = await this.socketManager.sendAsync<any>({
298
+ type: "request_for_allocation",
299
+ payload
300
+ });
301
+
302
+ if (allocationResponse.type === 'error') {
303
+ throw new Error(allocationResponse.message || allocationResponse.error);
304
+ }
305
+ if (allocationResponse.type === 'success') {
306
+ request.usageflow!.eventId = allocationResponse.payload.allocationId;
307
+ request.usageflow!.metadata = metadata;
308
+ return;
309
+ } else {
310
+ throw new Error(allocationResponse.message || "Unknown error occurred");
311
+ }
312
+ } catch (error: any) {
313
+ console.error("[UsageFlow] WebSocket allocation failed, falling back to HTTP:", error);
314
+ throw error;
315
+ // Fall through to HTTP request
316
+ }
81
317
  }
82
318
  }
83
319
 
@@ -191,6 +427,25 @@ export abstract class UsageFlowAPI {
191
427
  return parts[1];
192
428
  }
193
429
 
430
+ /**
431
+ * Get header value from headers object (handles both Record and Headers types)
432
+ */
433
+ private getHeaderValue(headers: Record<string, string | string[] | undefined> | Headers, key: string): string | null {
434
+ // Check if it's a Headers object (from Fetch API) by checking for the 'get' method
435
+ if (headers && typeof (headers as any).get === 'function') {
436
+ return (headers as any).get(key) || null;
437
+ }
438
+ // Otherwise, treat it as a Record
439
+ const value = (headers as Record<string, string | string[] | undefined>)[key];
440
+ if (typeof value === 'string') {
441
+ return value;
442
+ }
443
+ if (Array.isArray(value) && value.length > 0) {
444
+ return value[0];
445
+ }
446
+ return null;
447
+ }
448
+
194
449
  /**
195
450
  * Decodes a JWT token without verifying the signature
196
451
  * @param token The JWT token to decode
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './types';
2
- export * from './base';
2
+ export * from './base';
3
+ export * from './socket';