@revenium/openai 1.0.13 → 1.0.14

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.
Files changed (228) hide show
  1. package/.env.example +10 -15
  2. package/CHANGELOG.md +44 -11
  3. package/CODE_OF_CONDUCT.md +57 -0
  4. package/CONTRIBUTING.md +38 -0
  5. package/README.md +104 -216
  6. package/SECURITY.md +34 -0
  7. package/dist/cjs/core/client/index.js +14 -0
  8. package/dist/cjs/core/client/index.js.map +1 -0
  9. package/dist/cjs/core/client/manager.js +109 -0
  10. package/dist/cjs/core/client/manager.js.map +1 -0
  11. package/dist/cjs/core/config/azure-config.js +5 -17
  12. package/dist/cjs/core/config/azure-config.js.map +1 -1
  13. package/dist/cjs/core/config/index.js +2 -2
  14. package/dist/cjs/core/config/index.js.map +1 -1
  15. package/dist/cjs/core/config/loader.js +34 -14
  16. package/dist/cjs/core/config/loader.js.map +1 -1
  17. package/dist/cjs/core/config/manager.js +11 -5
  18. package/dist/cjs/core/config/manager.js.map +1 -1
  19. package/dist/cjs/core/config/validator.js +3 -45
  20. package/dist/cjs/core/config/validator.js.map +1 -1
  21. package/dist/cjs/core/middleware/index.js +17 -0
  22. package/dist/cjs/core/middleware/index.js.map +1 -0
  23. package/dist/cjs/core/middleware/interfaces.js +361 -0
  24. package/dist/cjs/core/middleware/interfaces.js.map +1 -0
  25. package/dist/cjs/core/middleware/revenium-client.js +142 -0
  26. package/dist/cjs/core/middleware/revenium-client.js.map +1 -0
  27. package/dist/cjs/core/providers/detector.js +45 -23
  28. package/dist/cjs/core/providers/detector.js.map +1 -1
  29. package/dist/cjs/core/providers/index.js +2 -1
  30. package/dist/cjs/core/providers/index.js.map +1 -1
  31. package/dist/cjs/core/tracking/api-client.js +14 -13
  32. package/dist/cjs/core/tracking/api-client.js.map +1 -1
  33. package/dist/cjs/core/tracking/payload-builder.js +15 -25
  34. package/dist/cjs/core/tracking/payload-builder.js.map +1 -1
  35. package/dist/cjs/core/tracking/usage-tracker.js +22 -18
  36. package/dist/cjs/core/tracking/usage-tracker.js.map +1 -1
  37. package/dist/cjs/index.js +26 -195
  38. package/dist/cjs/index.js.map +1 -1
  39. package/dist/cjs/types/index.js +0 -8
  40. package/dist/cjs/types/index.js.map +1 -1
  41. package/dist/cjs/types/openai-augmentation.js +0 -49
  42. package/dist/cjs/types/openai-augmentation.js.map +1 -1
  43. package/dist/cjs/utils/constants.js +17 -20
  44. package/dist/cjs/utils/constants.js.map +1 -1
  45. package/dist/cjs/utils/error-handler.js +18 -14
  46. package/dist/cjs/utils/error-handler.js.map +1 -1
  47. package/dist/cjs/utils/metadata-builder.js +17 -16
  48. package/dist/cjs/utils/metadata-builder.js.map +1 -1
  49. package/dist/cjs/utils/provider-detection.js +25 -28
  50. package/dist/cjs/utils/provider-detection.js.map +1 -1
  51. package/dist/esm/core/client/index.js +6 -0
  52. package/dist/esm/core/client/index.js.map +1 -0
  53. package/dist/esm/core/client/manager.js +102 -0
  54. package/dist/esm/core/client/manager.js.map +1 -0
  55. package/dist/esm/core/config/azure-config.js +6 -18
  56. package/dist/esm/core/config/azure-config.js.map +1 -1
  57. package/dist/esm/core/config/index.js +5 -4
  58. package/dist/esm/core/config/index.js.map +1 -1
  59. package/dist/esm/core/config/loader.js +33 -13
  60. package/dist/esm/core/config/loader.js.map +1 -1
  61. package/dist/esm/core/config/manager.js +13 -7
  62. package/dist/esm/core/config/manager.js.map +1 -1
  63. package/dist/esm/core/config/validator.js +3 -44
  64. package/dist/esm/core/config/validator.js.map +1 -1
  65. package/dist/esm/core/middleware/index.js +8 -0
  66. package/dist/esm/core/middleware/index.js.map +1 -0
  67. package/dist/esm/core/middleware/interfaces.js +353 -0
  68. package/dist/esm/core/middleware/interfaces.js.map +1 -0
  69. package/dist/esm/core/middleware/revenium-client.js +105 -0
  70. package/dist/esm/core/middleware/revenium-client.js.map +1 -0
  71. package/dist/esm/core/providers/detector.js +43 -22
  72. package/dist/esm/core/providers/detector.js.map +1 -1
  73. package/dist/esm/core/providers/index.js +2 -2
  74. package/dist/esm/core/providers/index.js.map +1 -1
  75. package/dist/esm/core/tracking/api-client.js +13 -12
  76. package/dist/esm/core/tracking/api-client.js.map +1 -1
  77. package/dist/esm/core/tracking/payload-builder.js +16 -26
  78. package/dist/esm/core/tracking/payload-builder.js.map +1 -1
  79. package/dist/esm/core/tracking/usage-tracker.js +24 -20
  80. package/dist/esm/core/tracking/usage-tracker.js.map +1 -1
  81. package/dist/esm/index.js +9 -177
  82. package/dist/esm/index.js.map +1 -1
  83. package/dist/esm/types/index.js +2 -10
  84. package/dist/esm/types/index.js.map +1 -1
  85. package/dist/esm/types/openai-augmentation.js +0 -49
  86. package/dist/esm/types/openai-augmentation.js.map +1 -1
  87. package/dist/esm/utils/constants.js +16 -19
  88. package/dist/esm/utils/constants.js.map +1 -1
  89. package/dist/esm/utils/error-handler.js +19 -15
  90. package/dist/esm/utils/error-handler.js.map +1 -1
  91. package/dist/esm/utils/metadata-builder.js +17 -16
  92. package/dist/esm/utils/metadata-builder.js.map +1 -1
  93. package/dist/esm/utils/provider-detection.js +26 -29
  94. package/dist/esm/utils/provider-detection.js.map +1 -1
  95. package/dist/types/core/client/index.d.ts +6 -0
  96. package/dist/types/core/client/index.d.ts.map +1 -0
  97. package/dist/types/core/client/manager.d.ts +32 -0
  98. package/dist/types/core/client/manager.d.ts.map +1 -0
  99. package/dist/types/core/config/azure-config.d.ts +2 -2
  100. package/dist/types/core/config/azure-config.d.ts.map +1 -1
  101. package/dist/types/core/config/index.d.ts +4 -4
  102. package/dist/types/core/config/index.d.ts.map +1 -1
  103. package/dist/types/core/config/loader.d.ts +3 -1
  104. package/dist/types/core/config/loader.d.ts.map +1 -1
  105. package/dist/types/core/config/manager.d.ts +1 -1
  106. package/dist/types/core/config/manager.d.ts.map +1 -1
  107. package/dist/types/core/config/validator.d.ts +1 -12
  108. package/dist/types/core/config/validator.d.ts.map +1 -1
  109. package/dist/types/core/middleware/index.d.ts +8 -0
  110. package/dist/types/core/middleware/index.d.ts.map +1 -0
  111. package/dist/types/core/middleware/interfaces.d.ts +74 -0
  112. package/dist/types/core/middleware/interfaces.d.ts.map +1 -0
  113. package/dist/types/core/middleware/revenium-client.d.ts +58 -0
  114. package/dist/types/core/middleware/revenium-client.d.ts.map +1 -0
  115. package/dist/types/core/providers/detector.d.ts +9 -2
  116. package/dist/types/core/providers/detector.d.ts.map +1 -1
  117. package/dist/types/core/providers/index.d.ts +2 -2
  118. package/dist/types/core/providers/index.d.ts.map +1 -1
  119. package/dist/types/core/tracking/api-client.d.ts +1 -1
  120. package/dist/types/core/tracking/api-client.d.ts.map +1 -1
  121. package/dist/types/core/tracking/payload-builder.d.ts +3 -3
  122. package/dist/types/core/tracking/payload-builder.d.ts.map +1 -1
  123. package/dist/types/core/tracking/usage-tracker.d.ts +2 -2
  124. package/dist/types/core/tracking/usage-tracker.d.ts.map +1 -1
  125. package/dist/types/index.d.ts +11 -135
  126. package/dist/types/index.d.ts.map +1 -1
  127. package/dist/types/types/function-parameters.d.ts +2 -23
  128. package/dist/types/types/function-parameters.d.ts.map +1 -1
  129. package/dist/types/types/index.d.ts +11 -105
  130. package/dist/types/types/index.d.ts.map +1 -1
  131. package/dist/types/types/openai-augmentation.d.ts +4 -138
  132. package/dist/types/types/openai-augmentation.d.ts.map +1 -1
  133. package/dist/types/utils/constants.d.ts +7 -1
  134. package/dist/types/utils/constants.d.ts.map +1 -1
  135. package/dist/types/utils/error-handler.d.ts +2 -2
  136. package/dist/types/utils/error-handler.d.ts.map +1 -1
  137. package/dist/types/utils/metadata-builder.d.ts +2 -2
  138. package/dist/types/utils/metadata-builder.d.ts.map +1 -1
  139. package/dist/types/utils/provider-detection.d.ts +3 -3
  140. package/dist/types/utils/provider-detection.d.ts.map +1 -1
  141. package/examples/README.md +282 -198
  142. package/examples/azure/basic.ts +62 -0
  143. package/examples/azure/responses-basic.ts +45 -0
  144. package/examples/azure/responses-stream.ts +61 -0
  145. package/examples/azure/stream.ts +56 -0
  146. package/examples/getting_started.ts +31 -43
  147. package/examples/openai/basic.ts +45 -0
  148. package/examples/openai/metadata.ts +67 -0
  149. package/examples/openai/responses-basic.ts +44 -0
  150. package/examples/openai/responses-embed.ts +34 -0
  151. package/examples/openai/responses-streaming.ts +63 -0
  152. package/examples/openai/streaming.ts +59 -0
  153. package/package.json +20 -13
  154. package/dist/cjs/core/wrapper/index.js +0 -15
  155. package/dist/cjs/core/wrapper/index.js.map +0 -1
  156. package/dist/cjs/core/wrapper/instance-patcher.js +0 -202
  157. package/dist/cjs/core/wrapper/instance-patcher.js.map +0 -1
  158. package/dist/cjs/core/wrapper/request-handler.js +0 -317
  159. package/dist/cjs/core/wrapper/request-handler.js.map +0 -1
  160. package/dist/cjs/core/wrapper/stream-wrapper.js +0 -82
  161. package/dist/cjs/core/wrapper/stream-wrapper.js.map +0 -1
  162. package/dist/cjs/utils/azure-model-resolver.js +0 -211
  163. package/dist/cjs/utils/azure-model-resolver.js.map +0 -1
  164. package/dist/cjs/utils/request-handler-factory.js +0 -185
  165. package/dist/cjs/utils/request-handler-factory.js.map +0 -1
  166. package/dist/esm/core/wrapper/index.js +0 -9
  167. package/dist/esm/core/wrapper/index.js.map +0 -1
  168. package/dist/esm/core/wrapper/instance-patcher.js +0 -199
  169. package/dist/esm/core/wrapper/instance-patcher.js.map +0 -1
  170. package/dist/esm/core/wrapper/request-handler.js +0 -310
  171. package/dist/esm/core/wrapper/request-handler.js.map +0 -1
  172. package/dist/esm/core/wrapper/stream-wrapper.js +0 -79
  173. package/dist/esm/core/wrapper/stream-wrapper.js.map +0 -1
  174. package/dist/esm/utils/azure-model-resolver.js +0 -204
  175. package/dist/esm/utils/azure-model-resolver.js.map +0 -1
  176. package/dist/esm/utils/request-handler-factory.js +0 -146
  177. package/dist/esm/utils/request-handler-factory.js.map +0 -1
  178. package/dist/types/core/wrapper/index.d.ts +0 -8
  179. package/dist/types/core/wrapper/index.d.ts.map +0 -1
  180. package/dist/types/core/wrapper/instance-patcher.d.ts +0 -33
  181. package/dist/types/core/wrapper/instance-patcher.d.ts.map +0 -1
  182. package/dist/types/core/wrapper/request-handler.d.ts +0 -29
  183. package/dist/types/core/wrapper/request-handler.d.ts.map +0 -1
  184. package/dist/types/core/wrapper/stream-wrapper.d.ts +0 -13
  185. package/dist/types/core/wrapper/stream-wrapper.d.ts.map +0 -1
  186. package/dist/types/utils/azure-model-resolver.d.ts +0 -41
  187. package/dist/types/utils/azure-model-resolver.d.ts.map +0 -1
  188. package/dist/types/utils/request-handler-factory.d.ts +0 -81
  189. package/dist/types/utils/request-handler-factory.d.ts.map +0 -1
  190. package/examples/azure-basic.ts +0 -206
  191. package/examples/azure-responses-basic.ts +0 -233
  192. package/examples/azure-responses-streaming.ts +0 -255
  193. package/examples/azure-streaming.ts +0 -209
  194. package/examples/openai-basic.ts +0 -147
  195. package/examples/openai-function-calling.ts +0 -259
  196. package/examples/openai-responses-basic.ts +0 -212
  197. package/examples/openai-responses-streaming.ts +0 -232
  198. package/examples/openai-streaming.ts +0 -172
  199. package/examples/openai-vision.ts +0 -289
  200. package/src/core/config/azure-config.ts +0 -72
  201. package/src/core/config/index.ts +0 -23
  202. package/src/core/config/loader.ts +0 -66
  203. package/src/core/config/manager.ts +0 -95
  204. package/src/core/config/validator.ts +0 -89
  205. package/src/core/providers/detector.ts +0 -159
  206. package/src/core/providers/index.ts +0 -16
  207. package/src/core/tracking/api-client.ts +0 -78
  208. package/src/core/tracking/index.ts +0 -21
  209. package/src/core/tracking/payload-builder.ts +0 -137
  210. package/src/core/tracking/usage-tracker.ts +0 -189
  211. package/src/core/wrapper/index.ts +0 -9
  212. package/src/core/wrapper/instance-patcher.ts +0 -288
  213. package/src/core/wrapper/request-handler.ts +0 -423
  214. package/src/core/wrapper/stream-wrapper.ts +0 -100
  215. package/src/index.ts +0 -360
  216. package/src/types/function-parameters.ts +0 -251
  217. package/src/types/index.ts +0 -310
  218. package/src/types/openai-augmentation.ts +0 -232
  219. package/src/types/responses-api.ts +0 -308
  220. package/src/utils/azure-model-resolver.ts +0 -220
  221. package/src/utils/constants.ts +0 -21
  222. package/src/utils/error-handler.ts +0 -251
  223. package/src/utils/metadata-builder.ts +0 -228
  224. package/src/utils/provider-detection.ts +0 -257
  225. package/src/utils/request-handler-factory.ts +0 -285
  226. package/src/utils/stop-reason-mapper.ts +0 -78
  227. package/src/utils/type-guards.ts +0 -202
  228. package/src/utils/url-builder.ts +0 -68
@@ -1,202 +0,0 @@
1
- /**
2
- * Type Guards Module
3
- *
4
- * Runtime type checking utilities to ensure type safety.
5
- * These functions validate that objects match expected interfaces.
6
- */
7
-
8
- import {
9
- OpenAIChatResponse,
10
- OpenAIEmbeddingResponse,
11
- OpenAIChatRequest,
12
- OpenAIEmbeddingRequest,
13
- OpenAIClientInstance,
14
- StreamChunk,
15
- } from '../types/function-parameters.js';
16
-
17
- /**
18
- * Type guard for OpenAI chat response
19
- */
20
- export function isOpenAIChatResponse(obj: unknown): obj is OpenAIChatResponse {
21
- if (!obj || typeof obj !== 'object') return false;
22
-
23
- const response = obj as Record<string, unknown>;
24
- return (
25
- typeof response.id === 'string' &&
26
- typeof response.model === 'string' &&
27
- typeof response.usage === 'object' &&
28
- response.usage !== null &&
29
- typeof (response.usage as Record<string, unknown>).prompt_tokens === 'number' &&
30
- typeof (response.usage as Record<string, unknown>).completion_tokens === 'number' &&
31
- typeof (response.usage as Record<string, unknown>).total_tokens === 'number' &&
32
- Array.isArray(response.choices)
33
- );
34
- }
35
-
36
- /**
37
- * Type guard for OpenAI embedding response
38
- */
39
- export function isOpenAIEmbeddingResponse(obj: unknown): obj is OpenAIEmbeddingResponse {
40
- if (!obj || typeof obj !== 'object') return false;
41
-
42
- const response = obj as Record<string, unknown>;
43
- return (
44
- typeof response.model === 'string' &&
45
- typeof response.usage === 'object' &&
46
- response.usage !== null &&
47
- typeof (response.usage as Record<string, unknown>).prompt_tokens === 'number' &&
48
- typeof (response.usage as Record<string, unknown>).total_tokens === 'number' &&
49
- Array.isArray(response.data) &&
50
- typeof response.object === 'string'
51
- );
52
- }
53
-
54
- /**
55
- * Type guard for OpenAI chat request
56
- */
57
- export function isOpenAIChatRequest(obj: unknown): obj is OpenAIChatRequest {
58
- if (!obj || typeof obj !== 'object') return false;
59
-
60
- const request = obj as Record<string, unknown>;
61
- return (
62
- typeof request.model === 'string' &&
63
- Array.isArray(request.messages) &&
64
- request.messages.length > 0 &&
65
- request.messages.every((msg: unknown) => {
66
- if (!msg || typeof msg !== 'object') return false;
67
- const message = msg as Record<string, unknown>;
68
- return typeof message.role === 'string' && typeof message.content === 'string';
69
- })
70
- );
71
- }
72
-
73
- /**
74
- * Type guard for OpenAI embedding request
75
- */
76
- export function isOpenAIEmbeddingRequest(obj: unknown): obj is OpenAIEmbeddingRequest {
77
- if (!obj || typeof obj !== 'object') return false;
78
-
79
- const request = obj as Record<string, unknown>;
80
- return (
81
- typeof request.model === 'string' &&
82
- (typeof request.input === 'string' || Array.isArray(request.input))
83
- );
84
- }
85
-
86
- /**
87
- * Type guard for OpenAI client instance
88
- */
89
- export function isOpenAIClientInstance(obj: unknown): obj is OpenAIClientInstance {
90
- if (!obj || typeof obj !== 'object') return false;
91
-
92
- const client = obj as Record<string, unknown>;
93
-
94
- // Must have at least one of the expected OpenAI client properties
95
- const hasBaseURL = typeof client.baseURL === 'string' || client.baseURL instanceof URL;
96
- const hasChat = typeof client.chat === 'object' && client.chat !== null;
97
- const hasEmbeddings = typeof client.embeddings === 'object' && client.embeddings !== null;
98
-
99
- // A valid OpenAI client should have at least one of these properties
100
- return hasBaseURL || hasChat || hasEmbeddings;
101
- }
102
-
103
- /**
104
- * Type guard for stream chunk
105
- */
106
- export function isStreamChunk(obj: unknown): obj is StreamChunk {
107
- if (!obj || typeof obj !== 'object') return false;
108
- const chunk = obj as Record<string, unknown>;
109
- return typeof chunk.id === 'string' && typeof chunk.model === 'string';
110
- }
111
-
112
- /**
113
- * Type guard for usage object
114
- */
115
- export function hasValidUsage(
116
- obj: unknown
117
- ): obj is { usage: { prompt_tokens: number; total_tokens: number } } {
118
- if (!obj || typeof obj !== 'object') return false;
119
-
120
- const response = obj as Record<string, unknown>;
121
- return (
122
- typeof response.usage === 'object' &&
123
- response.usage !== null &&
124
- typeof (response.usage as Record<string, unknown>).prompt_tokens === 'number' &&
125
- typeof (response.usage as Record<string, unknown>).total_tokens === 'number'
126
- );
127
- }
128
-
129
- /**
130
- * Type guard for function
131
- */
132
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
133
- export function isFunction(obj: unknown): obj is any {
134
- return typeof obj === 'function';
135
- }
136
-
137
- /**
138
- * Type guard for string
139
- */
140
- export function isString(obj: unknown): obj is string {
141
- return typeof obj === 'string';
142
- }
143
-
144
- /**
145
- * Type guard for number
146
- */
147
- export function isNumber(obj: unknown): obj is number {
148
- return typeof obj === 'number' && !isNaN(obj);
149
- }
150
-
151
- /**
152
- * Type guard for boolean
153
- */
154
- export function isBoolean(obj: unknown): obj is boolean {
155
- return typeof obj === 'boolean';
156
- }
157
-
158
- /**
159
- * Type guard for object
160
- */
161
- export function isObject(obj: unknown): obj is Record<string, unknown> {
162
- return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
163
- }
164
-
165
- /**
166
- * Type guard for array
167
- */
168
- export function isArray(obj: unknown): obj is unknown[] {
169
- return Array.isArray(obj);
170
- }
171
-
172
- /**
173
- * Safe property access with type checking
174
- */
175
- export function safeGetProperty<T>(
176
- obj: unknown,
177
- property: string,
178
- typeGuard: (value: unknown) => value is T
179
- ): T | undefined {
180
- if (!isObject(obj)) return undefined;
181
-
182
- const value = obj[property];
183
- return typeGuard(value) ? value : undefined;
184
- }
185
-
186
- /**
187
- * Safe nested property access
188
- */
189
- export function safeGetNestedProperty<T>(
190
- obj: unknown,
191
- path: string[],
192
- typeGuard: (value: unknown) => value is T
193
- ): T | undefined {
194
- let current = obj;
195
-
196
- for (const property of path) {
197
- if (!isObject(current)) return undefined;
198
- current = current[property];
199
- }
200
-
201
- return typeGuard(current) ? current : undefined;
202
- }
@@ -1,68 +0,0 @@
1
- /**
2
- * URL Builder Utilities
3
- *
4
- * Centralized URL construction logic to eliminate nested conditionals.
5
- * Replaces the complex URL building logic from tracking.ts.
6
- */
7
-
8
- /**
9
- * Build Revenium API URL with proper path handling
10
- *
11
- * This function intelligently handles base URLs and ensures the correct path structure.
12
- * Logic:
13
- * - If URL has /meter/v2 → append endpoint as-is
14
- * - If URL has /meter only → append /v2 and endpoint
15
- * - If URL has neither → append /meter/v2 and endpoint
16
- *
17
- * Examples:
18
- * - 'https://api.revenium.ai' + '/ai/completions' → 'https://api.revenium.ai/meter/v2/ai/completions'
19
- * - 'https://api.revenium.ai/meter' + '/ai/completions' → 'https://api.revenium.ai/meter/v2/ai/completions'
20
- * - 'https://api.revenium.ai/meter/v2' + '/ai/completions' → 'https://api.revenium.ai/meter/v2/ai/completions'
21
- *
22
- * @param baseUrl - The base URL from configuration (may include /meter or /meter/v2)
23
- * @param endpoint - The API endpoint to append (e.g., '/ai/completions')
24
- * @returns Complete URL for the API call
25
- */
26
- export function buildReveniumUrl(baseUrl: string, endpoint: string): string {
27
- // Normalize the base URL by removing trailing slashes
28
- let normalizedBase = baseUrl.replace(/\/+$/, '');
29
-
30
- // Check if /meter/v2 is already at the end
31
- const hasMeterV2AtEnd = /\/meter\/v2$/i.test(normalizedBase);
32
- if (hasMeterV2AtEnd) {
33
- // Already has /meter/v2, just append endpoint
34
- return `${normalizedBase}${endpoint}`;
35
- }
36
-
37
- // Check if /meter is at the end (but not /meter/v2)
38
- const hasMeterAtEnd = /\/meter$/i.test(normalizedBase);
39
- if (hasMeterAtEnd) {
40
- // Has /meter but not /v2, append /v2 and endpoint
41
- return `${normalizedBase}/v2${endpoint}`;
42
- }
43
-
44
- // Check if /v2 is at the end (without /meter)
45
- const hasV2AtEnd = /\/v2$/i.test(normalizedBase);
46
- if (hasV2AtEnd) {
47
- // Has /v2 but not /meter, append endpoint as-is
48
- return `${normalizedBase}${endpoint}`;
49
- }
50
-
51
- // Has neither /meter nor /v2, append /meter/v2 and endpoint
52
- return `${normalizedBase}/meter/v2${endpoint}`;
53
- }
54
-
55
- /**
56
- * Validate URL format
57
- *
58
- * @param url - URL to validate
59
- * @returns true if valid, false otherwise
60
- */
61
- export function isValidUrl(url: string): boolean {
62
- try {
63
- new URL(url);
64
- return true;
65
- } catch {
66
- return false;
67
- }
68
- }