@usageflow/core 0.1.3 → 0.1.5

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":";;;;;;AAAA,4CAA2B;AAK3B,MAAa,qBAAqB;IAK9B,YAAoB,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;QAJ1B,OAAE,GAAqB,IAAI,CAAC;QAC5B,UAAK,GAAW,2BAA2B,CAAC;QAC5C,gBAAW,GAAY,KAAK,CAAC;QAGjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,qDAAqD;QACrD,kEAAkE;IACtE,CAAC;IAED,KAAK,CAAC,4BAA4B;QAC9B,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,YAAS,CAAC,IAAI,EAAE,CAAC;YACnD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,OAAO;QACX,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;YAC3F,OAAO;QACX,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,IAAI,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;oBAC3C,OAAO;gBACX,CAAC;gBAED,MAAM,OAAO,GAA2B;oBACpC,aAAa,EAAE,IAAI,CAAC,MAAM;iBAC7B,CAAC;gBAEF,IAAI,CAAC,EAAE,GAAG,IAAI,YAAS,CAAC,IAAI,CAAC,KAAK,EAAE;oBAChC,OAAO;oBACP,MAAM,EAAE,CAAC,CAAE,mBAAmB;iBACjC,CAAC,CAAC;gBAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;oBACpB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;oBACxB,OAAO,EAAE,CAAC;gBACd,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;oBACjC,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;oBACrD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;oBACzB,MAAM,CAAC,KAAK,CAAC,CAAC;gBAClB,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;oBACrB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;oBACzB,qCAAqC;oBACrC,UAAU,CAAC,GAAG,EAAE;wBACZ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;4BACd,IAAI,CAAC,4BAA4B,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;wBAC7D,CAAC;oBACL,CAAC,EAAE,IAAI,CAAC,CAAC;gBACb,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAoB,EAAE,EAAE;oBAC3C,sCAAsC;gBAC1C,CAAC,CAAC,CAAC;YACP,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,KAAK,CAAC,CAAC;gBACzE,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;gBACzB,MAAM,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAGM,KAAK,CAAC,OAAO;QAChB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;YAC3F,OAAO;QACX,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,IAAI,CAAC;gBACD,MAAM,OAAO,GAA2B;oBACpC,aAAa,EAAE,IAAI,CAAC,MAAM;iBAC7B,CAAC;gBAEF,IAAI,CAAC,EAAE,GAAG,IAAI,YAAS,CAAC,IAAI,CAAC,KAAK,EAAE;oBAChC,OAAO;oBACP,MAAM,EAAE,CAAC,CAAE,mBAAmB;iBACjC,CAAC,CAAC;gBAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;oBACpB,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;oBAC9D,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;oBACxB,OAAO,EAAE,CAAC;gBACd,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;oBACjC,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;oBACrD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;oBACzB,MAAM,CAAC,KAAK,CAAC,CAAC;gBAClB,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;oBACrB,OAAO,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;oBACzD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;oBACzB,qCAAqC;oBACrC,UAAU,CAAC,GAAG,EAAE;wBACZ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;4BACd,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;wBACxC,CAAC;oBACL,CAAC,EAAE,IAAI,CAAC,CAAC;gBACb,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAoB,EAAE,EAAE;oBAC3C,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC9E,CAAC,CAAC,CAAC;YACP,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,KAAK,CAAC,CAAC;gBACzE,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;gBACzB,MAAM,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAEM,KAAK,CAAC,SAAS,CAAI,OAA+B;QACrD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,UAAU,CAAI,OAAO,CAAC,CAAC;QACzD,OAAO,cAA4C,CAAC;IACxD,CAAC;IAEM,IAAI,CAAC,OAA+B;QACvC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAE1C,CAAC;IAEM,KAAK;QACR,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;IAC7B,CAAC;IAEM,WAAW;QACd,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAEM,KAAK;QACR,OAAO,IAAI,CAAC,EAAE,CAAC;IACnB,CAAC;IAIO,KAAK,CAAC,UAAU,CAAI,OAA+B;QACvD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBAChC,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;gBAC7C,OAAO;YACX,CAAC;YAED,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACzE,MAAM,OAAO,GAAG,EAAE,EAAE,EAAE,GAAG,OAAO,EAAE,CAAC;YACnC,IAAI,UAAU,GAAG,KAAK,CAAC;YAEvB,+DAA+D;YAC/D,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,UAAU,EAAE,CAAC;oBACd,UAAU,GAAG,IAAI,CAAC;oBAClB,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;wBACV,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;oBAC1C,CAAC;oBACD,MAAM,CAAC,IAAI,KAAK,CAAC,qCAAqC,EAAE,EAAE,CAAC,CAAC,CAAC;gBACjE,CAAC;YACL,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,oBAAoB;YAE/B,gCAAgC;YAChC,MAAM,cAAc,GAAG,CAAC,KAAQ,EAAE,EAAE;gBAChC,IAAI,CAAC,UAAU,EAAE,CAAC;oBACd,YAAY,CAAC,OAAO,CAAC,CAAC;oBACtB,UAAU,GAAG,IAAI,CAAC;oBAClB,OAAO,CAAC,KAAK,CAAC,CAAC;gBACnB,CAAC;YACL,CAAC,CAAC;YAEF,MAAM,aAAa,GAAG,CAAC,IAAoB,EAAE,EAAE;gBAC3C,IAAI,UAAU;oBAAE,OAAO,CAAC,8BAA8B;gBAEtD,IAAI,CAAC;oBACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAC7C,qEAAqE;oBACrE,IAAI,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,QAAQ,CAAC,OAAO,KAAK,EAAE,EAAE,CAAC;wBAChD,OAAO,CAAC,KAAK,CAAC,gDAAgD,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;wBAE9E,8CAA8C;wBAC9C,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;4BACV,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;wBAC1C,CAAC;wBAED,cAAc,CAAC,QAAa,CAAC,CAAC;oBAClC,CAAC;gBACL,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACb,oDAAoD;gBACxD,CAAC;YACL,CAAC,CAAC;YAEF,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YAErC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,GAA6B,EAAE,EAAE;gBACpE,IAAI,GAAG,EAAE,CAAC;oBACN,YAAY,CAAC,OAAO,CAAC,CAAC;oBACtB,IAAI,CAAC,UAAU,EAAE,CAAC;wBACd,UAAU,GAAG,IAAI,CAAC;wBAClB,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;4BACV,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;wBAC1C,CAAC;wBACD,MAAM,CAAC,GAAG,CAAC,CAAC;oBAChB,CAAC;gBACL,CAAC;YACL,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;IAID;;OAEG;IACI,OAAO;QACV,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,IAAI,CAAC,EAAE,CAAC,kBAAkB,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,YAAS,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,YAAS,CAAC,UAAU,EAAE,CAAC;gBACvF,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YACpB,CAAC;YACD,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;YACf,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QAC7B,CAAC;IACL,CAAC;CAIJ;AArPD,sDAqPC"}
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.3",
3
+ "version": "0.1.5",
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
  }
@@ -45,39 +62,257 @@ export abstract class UsageFlowAPI {
45
62
  }
46
63
 
47
64
  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
- }
65
+ await this.fetchApiPolicies();
56
66
  }, 60000);
57
67
 
58
- this.fetchApiConfig().catch(console.error);
68
+ // this.fetchApiConfig().catch(console.error);
59
69
  }
60
70
 
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");
71
+ protected getRoutePattern(request: UsageFlowRequest): string {
72
+
73
+ if (this.webServer === 'fastify') {
74
+ return request?.routeOptions?.url || request.url || '';
67
75
  }
68
76
 
77
+ const routePattern = request.route?.path || request.app._router.stack.find((route: any) => {
78
+ // a => a.path == request.url
79
+ if (!route.route) return false;
80
+ if (route.path) {
81
+ return route.path == request.url;
82
+ }
83
+
84
+ if (route.regexp) {
85
+ return route.regexp.test(request.url);
86
+ }
87
+
88
+ })?.route?.path
89
+
90
+ if (routePattern) {
91
+ return routePattern;
92
+ }
93
+
94
+ // Method 1: Use request.route.path (available after route matching)
95
+ if (request.route?.path) {
96
+ const baseUrl = request.baseUrl || "";
97
+ const routePath = request.route.path;
98
+ // Combine baseUrl and routePath to get full pattern
99
+ const fullPath = baseUrl + routePath;
100
+ return fullPath || "/";
101
+ }
102
+
103
+ // Method 2: Try to find route from router stack
69
104
  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;
105
+ const app = request.app;
106
+ const router = (app as any)._router;
107
+ if (router && router.stack) {
108
+ const method = request.method.toLowerCase();
109
+ const path = request.path || request.url?.split("?")[0] || "";
110
+
111
+ // Search through router stack for matching route
112
+ for (const layer of router.stack) {
113
+ if (layer.route) {
114
+ const route = layer.route;
115
+ const routePath = route.path;
116
+ const routeMethods = Object.keys(route.methods).map(m => m.toLowerCase());
117
+
118
+ // Check if method matches and path pattern matches
119
+ if (routeMethods.includes(method) || routeMethods.includes("*")) {
120
+ // Try to match the pattern by checking if params match
121
+ const pattern = (request.baseUrl || "") + routePath;
122
+ // Simple check: if we have params and the route has params, it might match
123
+ if (request.params && Object.keys(request.params).length > 0) {
124
+ // Check if route path contains the param names
125
+ const paramNames = Object.keys(request.params);
126
+ const routeHasParams = paramNames.some(param => routePath.includes(`:${param}`));
127
+ if (routeHasParams) {
128
+ return pattern;
129
+ }
130
+ } else if (!routePath.includes(":")) {
131
+ // If no params in request and route has no params, check exact match
132
+ if (path === pattern || path === routePath) {
133
+ return pattern;
134
+ }
135
+ }
136
+ }
137
+ } else if (layer.name === "router" && layer.handle && layer.handle.stack) {
138
+ // Handle router middleware (nested routers)
139
+ const mountPath = layer.regexp.source
140
+ .replace("\\/?", "")
141
+ .replace("(?=\\/|$)", "")
142
+ .replace(/\\\//g, "/")
143
+ .replace(/\^/g, "")
144
+ .replace(/\$/g, "")
145
+ .replace(/\\/g, "");
146
+
147
+ for (const subLayer of layer.handle.stack) {
148
+ if (subLayer.route) {
149
+ const route = subLayer.route;
150
+ const routePath = route.path;
151
+ const routeMethods = Object.keys(route.methods).map(m => m.toLowerCase());
152
+
153
+ if (routeMethods.includes(method) || routeMethods.includes("*")) {
154
+ const fullPath = mountPath + routePath;
155
+ // Check if this route matches by examining params
156
+ if (request.params && Object.keys(request.params).length > 0) {
157
+ const paramNames = Object.keys(request.params);
158
+ const routeHasParams = paramNames.some(param => routePath.includes(`:${param}`));
159
+ if (routeHasParams) {
160
+ return fullPath;
161
+ }
162
+ } else if (!routePath.includes(":")) {
163
+ const currentPath = request.path || request.url?.split("?")[0] || "";
164
+ if (currentPath === fullPath || currentPath === routePath) {
165
+ return fullPath;
166
+ }
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
78
174
  } catch (error) {
79
- console.error("Error fetching API config:", error);
80
- return null;
175
+ // Silently fail and try next method
176
+ console.debug("[UsageFlow] Could not extract route from router stack:", error);
177
+ }
178
+
179
+ // Method 3: Reconstruct pattern from params and path
180
+ if (request.params && Object.keys(request.params).length > 0) {
181
+ let path = request.path || request.url?.split("?")[0] || "/";
182
+ const baseUrl = request.baseUrl || "";
183
+
184
+ // Split path into segments and replace matching segments with param names
185
+ const pathSegments = path.split("/");
186
+ const paramEntries = Object.entries(request.params);
187
+
188
+ // Replace segments that match param values with :paramName
189
+ for (let i = 0; i < pathSegments.length; i++) {
190
+ for (const [paramName, paramValue] of paramEntries) {
191
+ if (pathSegments[i] === paramValue) {
192
+ pathSegments[i] = `:${paramName}`;
193
+ break; // Only replace once per segment
194
+ }
195
+ }
196
+ }
197
+
198
+ const pattern = baseUrl + pathSegments.join("/");
199
+ return pattern;
200
+ }
201
+
202
+ // Fallback: return baseUrl + path or just path
203
+ const baseUrl = request.baseUrl || "";
204
+ const path = request.path || request.url?.split("?")[0] || "/";
205
+ return baseUrl + path || "/";
206
+ }
207
+
208
+ public guessLedgerId(request: UsageFlowRequest): string {
209
+ const method = request.method;
210
+ const url = this.getRoutePattern(request);
211
+ const configs = this.apiConfigs
212
+
213
+ if (!configs.length) {
214
+ return `${method} ${url}`;
215
+ }
216
+
217
+
218
+ for (const config of configs) {
219
+ const fieldName = config.identityFieldName!;
220
+ const location = config.identityFieldLocation;
221
+
222
+ switch (location) {
223
+ case "path_params":
224
+ if (request.params?.[fieldName]) {
225
+ return `${method} ${url} ${request.params[fieldName]}`;
226
+ }
227
+ break;
228
+ case "query_params":
229
+ if (request.query?.[fieldName]) {
230
+ return `${method} ${url} ${request.query[fieldName]}`;
231
+ }
232
+ break;
233
+ case "body":
234
+ if (request.body?.[fieldName]) {
235
+ return `${method} ${url} ${request.body[fieldName]}`;
236
+ }
237
+ break;
238
+ case "bearer_token":
239
+ const authHeader = this.getHeaderValue(request.headers, 'authorization');
240
+ const token = this.extractBearerToken(authHeader || undefined);
241
+ if (token) {
242
+ const claims = this.decodeJwtUnverified(token);
243
+ if (claims?.[fieldName]) {
244
+ return `${method} ${url} ${claims[fieldName]}`;
245
+ }
246
+ }
247
+ break;
248
+
249
+ case "headers": {
250
+ const headerValue = this.getHeaderValue(request.headers, fieldName);
251
+ if (headerValue) {
252
+ return `${method} ${url} ${headerValue}`;
253
+ }
254
+ break;
255
+ }
256
+ }
257
+ }
258
+
259
+ return `${method} ${url}`;
260
+ }
261
+
262
+ private async fetchApiPolicies(): Promise<void> {
263
+ if (this.socketManager && this.socketManager.isConnected()) {
264
+ const response = await this.socketManager.sendAsync<{ policies: UsageFlowConfig[], total: number }>({
265
+ type: "get_application_policies",
266
+ payload: null,
267
+ });
268
+ if (response.type === 'success') {
269
+ this.apiConfigs = response.payload?.policies || [];
270
+ }
271
+ }
272
+ }
273
+
274
+ async useAllocationRequest(
275
+ payload: RequestForAllocation,
276
+ ): Promise<void> {
277
+ if (this.socketManager && this.socketManager.isConnected()) {
278
+ this.socketManager.sendAsync<any>({
279
+ type: "use_allocation",
280
+ payload
281
+ }).catch((error) => {
282
+ console.error("[UsageFlow] Error sending finalization via WebSocket:", error);
283
+ throw error;
284
+ });
285
+ }
286
+ }
287
+
288
+
289
+ async allocationRequest(
290
+ request: UsageFlowRequest,
291
+ payload: RequestForAllocation,
292
+ metadata: RequestMetadata,
293
+ ): Promise<void> {
294
+ if (this.socketManager && this.socketManager.isConnected()) {
295
+ try {
296
+ const allocationResponse = await this.socketManager.sendAsync<any>({
297
+ type: "request_for_allocation",
298
+ payload
299
+ });
300
+
301
+ if (allocationResponse.type === 'error') {
302
+ throw new Error(allocationResponse.message || allocationResponse.error);
303
+ }
304
+ if (allocationResponse.type === 'success') {
305
+ request.usageflow!.eventId = allocationResponse.payload.allocationId;
306
+ request.usageflow!.metadata = metadata;
307
+ return;
308
+ } else {
309
+ throw new Error(allocationResponse.message || "Unknown error occurred");
310
+ }
311
+ } catch (error: any) {
312
+ console.error("[UsageFlow] WebSocket allocation failed, falling back to HTTP:", error);
313
+ throw error;
314
+ // Fall through to HTTP request
315
+ }
81
316
  }
82
317
  }
83
318
 
@@ -191,6 +426,25 @@ export abstract class UsageFlowAPI {
191
426
  return parts[1];
192
427
  }
193
428
 
429
+ /**
430
+ * Get header value from headers object (handles both Record and Headers types)
431
+ */
432
+ private getHeaderValue(headers: Record<string, string | string[] | undefined> | Headers, key: string): string | null {
433
+ // Check if it's a Headers object (from Fetch API) by checking for the 'get' method
434
+ if (headers && typeof (headers as any).get === 'function') {
435
+ return (headers as any).get(key) || null;
436
+ }
437
+ // Otherwise, treat it as a Record
438
+ const value = (headers as Record<string, string | string[] | undefined>)[key];
439
+ if (typeof value === 'string') {
440
+ return value;
441
+ }
442
+ if (Array.isArray(value) && value.length > 0) {
443
+ return value[0];
444
+ }
445
+ return null;
446
+ }
447
+
194
448
  /**
195
449
  * Decodes a JWT token without verifying the signature
196
450
  * @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';