zlient 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/endpoint/base-endpoint.d.ts +33 -11
- package/dist/endpoint/base-endpoint.d.ts.map +1 -1
- package/dist/http/http-client.d.ts.map +1 -1
- package/dist/index.cjs +62 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +62 -41
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -6,20 +6,22 @@ import { HTTPMethod } from '../types';
|
|
|
6
6
|
* Wrapper object containing all request parameters.
|
|
7
7
|
*
|
|
8
8
|
* @template ReqSchema - Zod schema for request validation
|
|
9
|
+
* @template PathParams - Type for path parameters
|
|
10
|
+
* @template QueryParams - Type for query parameters
|
|
9
11
|
*/
|
|
10
|
-
export type EndpointCallConfig<ReqSchema extends z.ZodType> = {
|
|
12
|
+
export type EndpointCallConfig<ReqSchema extends z.ZodType, PathParams = never, QueryParams = never> = {
|
|
11
13
|
/** Request body data (for POST, PUT, PATCH, etc.) or request args */
|
|
12
14
|
data?: z.infer<ReqSchema>;
|
|
13
15
|
/** Path parameters for dynamic path construction */
|
|
14
|
-
pathParams?:
|
|
16
|
+
pathParams?: PathParams;
|
|
15
17
|
/** Query string parameters */
|
|
16
|
-
query?:
|
|
18
|
+
query?: QueryParams;
|
|
17
19
|
/** Request headers */
|
|
18
20
|
headers?: Record<string, string>;
|
|
19
21
|
/** Override base URL for this call */
|
|
20
22
|
baseUrlKey?: string;
|
|
21
23
|
/** Abort controller signal for cancellation */
|
|
22
|
-
signal?: AbortSignal;
|
|
24
|
+
signal?: globalThis.AbortSignal;
|
|
23
25
|
};
|
|
24
26
|
/**
|
|
25
27
|
* Generic, strongly-typed endpoint with Zod schemas for request and response validation.
|
|
@@ -27,15 +29,25 @@ export type EndpointCallConfig<ReqSchema extends z.ZodType> = {
|
|
|
27
29
|
*
|
|
28
30
|
* @template ReqSchema - Zod schema for request validation
|
|
29
31
|
* @template ResSchema - Zod schema for response validation
|
|
32
|
+
* @template PathParams - Type for path parameters (optional)
|
|
33
|
+
* @template QueryParams - Type for query parameters (optional)
|
|
30
34
|
*
|
|
31
35
|
* @example
|
|
32
36
|
* ```ts
|
|
33
37
|
* const UserSchema = z.object({ id: z.number(), name: z.string() });
|
|
34
38
|
* const CreateUserSchema = z.object({ name: z.string() });
|
|
35
39
|
*
|
|
36
|
-
*
|
|
40
|
+
* type UserPathParams = { id: string };
|
|
41
|
+
* type UserQueryParams = { include?: string; limit?: number };
|
|
42
|
+
*
|
|
43
|
+
* class GetUser extends BaseEndpoint<
|
|
44
|
+
* typeof CreateUserSchema,
|
|
45
|
+
* typeof UserSchema,
|
|
46
|
+
* UserPathParams,
|
|
47
|
+
* UserQueryParams
|
|
48
|
+
* > {
|
|
37
49
|
* protected method = 'GET' as const;
|
|
38
|
-
* protected path = (
|
|
50
|
+
* protected path = (params: UserPathParams) => `/users/${params.id}`;
|
|
39
51
|
*
|
|
40
52
|
* constructor(client: HttpClient) {
|
|
41
53
|
* super(client, {
|
|
@@ -44,14 +56,20 @@ export type EndpointCallConfig<ReqSchema extends z.ZodType> = {
|
|
|
44
56
|
* });
|
|
45
57
|
* }
|
|
46
58
|
* }
|
|
59
|
+
*
|
|
60
|
+
* // Usage:
|
|
61
|
+
* const user = await endpoint.call({
|
|
62
|
+
* pathParams: { id: '123' },
|
|
63
|
+
* query: { include: 'posts', limit: 10 }
|
|
64
|
+
* });
|
|
47
65
|
* ```
|
|
48
66
|
*/
|
|
49
|
-
export declare abstract class BaseEndpoint<ReqSchema extends z.ZodType, ResSchema extends z.ZodType> {
|
|
67
|
+
export declare abstract class BaseEndpoint<ReqSchema extends z.ZodType, ResSchema extends z.ZodType, PathParams = never, QueryParams = never> {
|
|
50
68
|
protected client: HttpClient;
|
|
51
69
|
/** HTTP method for this endpoint */
|
|
52
70
|
protected abstract readonly method: keyof typeof HTTPMethod;
|
|
53
71
|
/** URL path (can be a function for dynamic paths) */
|
|
54
|
-
protected abstract readonly path: string | ((params:
|
|
72
|
+
protected abstract readonly path: string | ((params: PathParams) => string);
|
|
55
73
|
/** Additional options for the request */
|
|
56
74
|
protected readonly options?: {
|
|
57
75
|
/** Override base URL for this call */
|
|
@@ -81,15 +99,19 @@ export declare abstract class BaseEndpoint<ReqSchema extends z.ZodType, ResSchem
|
|
|
81
99
|
* @example
|
|
82
100
|
* ```ts
|
|
83
101
|
* const endpoint = new GetUser(client);
|
|
84
|
-
* const user = await endpoint.call({
|
|
102
|
+
* const user = await endpoint.call({
|
|
103
|
+
* pathParams: { id: '123' },
|
|
104
|
+
* query: { include: 'posts' }
|
|
105
|
+
* });
|
|
85
106
|
* // With additional options:
|
|
86
107
|
* const user = await endpoint.call({
|
|
87
|
-
* data: {
|
|
108
|
+
* data: { name: 'John' },
|
|
109
|
+
* pathParams: { id: '123' },
|
|
88
110
|
* headers: { 'X-Custom': 'value' },
|
|
89
111
|
* query: { include: 'posts' }
|
|
90
112
|
* });
|
|
91
113
|
* ```
|
|
92
114
|
*/
|
|
93
|
-
call(config?: EndpointCallConfig<ReqSchema>): Promise<z.infer<ResSchema>>;
|
|
115
|
+
call(config?: EndpointCallConfig<ReqSchema, PathParams, QueryParams>): Promise<z.infer<ResSchema>>;
|
|
94
116
|
}
|
|
95
117
|
//# sourceMappingURL=base-endpoint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-endpoint.d.ts","sourceRoot":"","sources":["../../lib/endpoint/base-endpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC
|
|
1
|
+
{"version":3,"file":"base-endpoint.d.ts","sourceRoot":"","sources":["../../lib/endpoint/base-endpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,CAC5B,SAAS,SAAS,CAAC,CAAC,OAAO,EAC3B,UAAU,GAAG,KAAK,EAClB,WAAW,GAAG,KAAK,IACjB;IACF,qEAAqE;IACrE,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1B,oDAAoD;IACpD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,UAAU,CAAC,WAAW,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,8BAAsB,YAAY,CAChC,SAAS,SAAS,CAAC,CAAC,OAAO,EAC3B,SAAS,SAAS,CAAC,CAAC,OAAO,EAC3B,UAAU,GAAG,KAAK,EAClB,WAAW,GAAG,KAAK;IAqBjB,SAAS,CAAC,MAAM,EAAE,UAAU;IAnB9B,oCAAoC;IACpC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,UAAU,CAAC;IAC5D,qDAAqD;IACrD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,CAAC,CAAC;IAC5E,yCAAyC;IACzC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;QAC3B,sCAAsC;QACtC,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,6CAA6C;IAC7C,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IAC7C,qCAAqC;IACrC,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,SAAS,CAAC;IAE7C;;;OAGG;gBAES,MAAM,EAAE,UAAU,EAC5B,GAAG,EAAE;QACH,aAAa,CAAC,EAAE,SAAS,CAAC;QAC1B,cAAc,EAAE,SAAS,CAAC;KAC3B;IAMH;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,IAAI,CACR,MAAM,GAAE,kBAAkB,CAAC,SAAS,EAAE,UAAU,EAAE,WAAW,CAI5D,GACA,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;CAgC/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../lib/http/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAEL,aAAa,EAEb,UAAU,EAEV,cAAc,EAGf,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAe;IAC3B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAmB;IAElC;;;;;OAKG;gBACS,IAAI,EAAE,aAAa;IA4C/B;;;;;;;;OAQG;IACH,OAAO,CAAC,IAAI,EAAE,YAAY;IAI1B,OAAO,CAAC,cAAc;IAUtB;;;OAGG;IACH,OAAO,CAAC,KAAK;IAIb;;;OAGG;YACW,SAAS;IAmBvB;;;OAGG;YACW,cAAc;IAM5B;;;OAGG;YACW,aAAa;IAM3B;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,CAAC,CAAC,GAAG,OAAO,EACvB,MAAM,EAAE,MAAM,OAAO,UAAU,EAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;IAmI3C;;;;;;;OAOG;IACG,GAAG,CAAC,CAAC,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../lib/http/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAEL,aAAa,EAEb,UAAU,EAEV,cAAc,EAGf,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAe;IAC3B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAmB;IAElC;;;;;OAKG;gBACS,IAAI,EAAE,aAAa;IA4C/B;;;;;;;;OAQG;IACH,OAAO,CAAC,IAAI,EAAE,YAAY;IAI1B,OAAO,CAAC,cAAc;IAUtB;;;OAGG;IACH,OAAO,CAAC,KAAK;IAIb;;;OAGG;YACW,SAAS;IAmBvB;;;OAGG;YACW,cAAc;IAM5B;;;OAGG;YACW,aAAa;IAM3B;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,CAAC,CAAC,GAAG,OAAO,EACvB,MAAM,EAAE,MAAM,OAAO,UAAU,EAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;IAmI3C;;;;;;;OAOG;IACG,GAAG,CAAC,CAAC,GAAG,OAAO,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;IAI3C;;;;;;;OAOG;IACG,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;IAI3C;;;;;;;OAOG;IACG,GAAG,CAAC,CAAC,GAAG,OAAO,EACnB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;IAI3C;;;;;;;OAOG;IACG,KAAK,CAAC,CAAC,GAAG,OAAO,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;IAI3C;;;;;;;OAOG;IACG,MAAM,CAAC,CAAC,GAAG,OAAO,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;CAG5C"}
|
package/dist/index.cjs
CHANGED
|
@@ -37,7 +37,7 @@ const HTTPMethod = {
|
|
|
37
37
|
/**
|
|
38
38
|
* Custom error class for API-related errors.
|
|
39
39
|
* Includes HTTP status codes, response details, and validation errors.
|
|
40
|
-
*
|
|
40
|
+
*
|
|
41
41
|
* @example
|
|
42
42
|
* ```ts
|
|
43
43
|
* throw new ApiError('Invalid request', {
|
|
@@ -103,10 +103,10 @@ const PaginationSchema = zod.z.object({
|
|
|
103
103
|
/**
|
|
104
104
|
* Converts query parameters to a URL query string.
|
|
105
105
|
* Filters out undefined values automatically.
|
|
106
|
-
*
|
|
106
|
+
*
|
|
107
107
|
* @param q - Query parameters as URLSearchParams or object
|
|
108
108
|
* @returns Query string with leading '?' or empty string
|
|
109
|
-
*
|
|
109
|
+
*
|
|
110
110
|
* @example
|
|
111
111
|
* ```ts
|
|
112
112
|
* toQueryString({ page: 1, filter: 'active' }) // "?page=1&filter=active"
|
|
@@ -139,12 +139,12 @@ var NoAuth = class {
|
|
|
139
139
|
/**
|
|
140
140
|
* API Key authentication provider.
|
|
141
141
|
* Supports both header-based and query parameter-based authentication.
|
|
142
|
-
*
|
|
142
|
+
*
|
|
143
143
|
* @example
|
|
144
144
|
* ```ts
|
|
145
145
|
* // Header-based
|
|
146
146
|
* const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });
|
|
147
|
-
*
|
|
147
|
+
*
|
|
148
148
|
* // Query parameter-based
|
|
149
149
|
* const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });
|
|
150
150
|
* ```
|
|
@@ -170,12 +170,12 @@ var ApiKeyAuth = class {
|
|
|
170
170
|
/**
|
|
171
171
|
* Bearer token authentication provider.
|
|
172
172
|
* Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).
|
|
173
|
-
*
|
|
173
|
+
*
|
|
174
174
|
* @example
|
|
175
175
|
* ```ts
|
|
176
176
|
* // Static token
|
|
177
177
|
* const auth = new BearerTokenAuth(() => 'my-token');
|
|
178
|
-
*
|
|
178
|
+
*
|
|
179
179
|
* // Dynamic token with refresh
|
|
180
180
|
* const auth = new BearerTokenAuth(async () => {
|
|
181
181
|
* return await refreshAccessToken();
|
|
@@ -201,11 +201,11 @@ var BearerTokenAuth = class {
|
|
|
201
201
|
/**
|
|
202
202
|
* Safely parse data with a Zod schema without throwing.
|
|
203
203
|
* Returns a result object with success status and data or error.
|
|
204
|
-
*
|
|
204
|
+
*
|
|
205
205
|
* @param schema - Zod schema to validate against
|
|
206
206
|
* @param data - Data to validate
|
|
207
207
|
* @returns Result object with success flag and data/error
|
|
208
|
-
*
|
|
208
|
+
*
|
|
209
209
|
* @example
|
|
210
210
|
* ```ts
|
|
211
211
|
* const result = safeParse(UserSchema, userData);
|
|
@@ -230,12 +230,12 @@ function safeParse(schema, data) {
|
|
|
230
230
|
/**
|
|
231
231
|
* Parse data with a Zod schema, throwing an ApiError on validation failure.
|
|
232
232
|
* Use this when you want to fail fast on invalid data.
|
|
233
|
-
*
|
|
233
|
+
*
|
|
234
234
|
* @param schema - Zod schema to validate against
|
|
235
235
|
* @param data - Data to validate
|
|
236
236
|
* @returns Validated and typed data
|
|
237
237
|
* @throws {ApiError} If validation fails
|
|
238
|
-
*
|
|
238
|
+
*
|
|
239
239
|
* @example
|
|
240
240
|
* ```ts
|
|
241
241
|
* try {
|
|
@@ -428,7 +428,7 @@ var ConsoleMetricsCollector = class {
|
|
|
428
428
|
/**
|
|
429
429
|
* HTTP client with built-in retry logic, authentication, and interceptors.
|
|
430
430
|
* Supports multiple base URLs, type-safe requests, and comprehensive error handling.
|
|
431
|
-
*
|
|
431
|
+
*
|
|
432
432
|
* @example
|
|
433
433
|
* ```ts
|
|
434
434
|
* const client = new HttpClient({
|
|
@@ -437,7 +437,7 @@ var ConsoleMetricsCollector = class {
|
|
|
437
437
|
* retry: { maxRetries: 3, baseDelayMs: 1000 },
|
|
438
438
|
* timeout: { requestTimeoutMs: 30000 }
|
|
439
439
|
* });
|
|
440
|
-
*
|
|
440
|
+
*
|
|
441
441
|
* const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });
|
|
442
442
|
* ```
|
|
443
443
|
*/
|
|
@@ -453,7 +453,7 @@ var HttpClient = class {
|
|
|
453
453
|
metrics;
|
|
454
454
|
/**
|
|
455
455
|
* Creates a new HTTP client instance.
|
|
456
|
-
*
|
|
456
|
+
*
|
|
457
457
|
* @param opts - Client configuration options
|
|
458
458
|
* @throws {Error} If no fetch implementation is available
|
|
459
459
|
*/
|
|
@@ -482,7 +482,7 @@ var HttpClient = class {
|
|
|
482
482
|
}
|
|
483
483
|
/**
|
|
484
484
|
* Set or update the authentication provider.
|
|
485
|
-
*
|
|
485
|
+
*
|
|
486
486
|
* @param auth - Authentication provider instance
|
|
487
487
|
* @example
|
|
488
488
|
* ```ts
|
|
@@ -551,14 +551,14 @@ var HttpClient = class {
|
|
|
551
551
|
}
|
|
552
552
|
/**
|
|
553
553
|
* Make an HTTP request with automatic retry, authentication, and validation.
|
|
554
|
-
*
|
|
554
|
+
*
|
|
555
555
|
* @param method - HTTP method (GET, POST, PUT, etc.)
|
|
556
556
|
* @param path - Request path (will be appended to base URL)
|
|
557
557
|
* @param body - Request body (will be JSON.stringify'd if Content-Type is json)
|
|
558
558
|
* @param options - Additional request options (headers, query params, etc.)
|
|
559
559
|
* @returns Promise resolving to response data and Response object
|
|
560
560
|
* @throws {ApiError} If request fails or response validation fails
|
|
561
|
-
*
|
|
561
|
+
*
|
|
562
562
|
* @example
|
|
563
563
|
* ```ts
|
|
564
564
|
* const { data, response } = await client.request('GET', '/users', undefined, {
|
|
@@ -673,7 +673,7 @@ var HttpClient = class {
|
|
|
673
673
|
}
|
|
674
674
|
/**
|
|
675
675
|
* Convenience method for GET requests.
|
|
676
|
-
*
|
|
676
|
+
*
|
|
677
677
|
* @example
|
|
678
678
|
* ```ts
|
|
679
679
|
* const { data } = await client.get('/users', { query: { page: 1 } });
|
|
@@ -684,7 +684,7 @@ var HttpClient = class {
|
|
|
684
684
|
}
|
|
685
685
|
/**
|
|
686
686
|
* Convenience method for POST requests.
|
|
687
|
-
*
|
|
687
|
+
*
|
|
688
688
|
* @example
|
|
689
689
|
* ```ts
|
|
690
690
|
* const { data } = await client.post('/users', { name: 'John' });
|
|
@@ -695,7 +695,7 @@ var HttpClient = class {
|
|
|
695
695
|
}
|
|
696
696
|
/**
|
|
697
697
|
* Convenience method for PUT requests.
|
|
698
|
-
*
|
|
698
|
+
*
|
|
699
699
|
* @example
|
|
700
700
|
* ```ts
|
|
701
701
|
* const { data } = await client.put('/users/1', { name: 'John Updated' });
|
|
@@ -706,7 +706,7 @@ var HttpClient = class {
|
|
|
706
706
|
}
|
|
707
707
|
/**
|
|
708
708
|
* Convenience method for PATCH requests.
|
|
709
|
-
*
|
|
709
|
+
*
|
|
710
710
|
* @example
|
|
711
711
|
* ```ts
|
|
712
712
|
* const { data } = await client.patch('/users/1', { name: 'John' });
|
|
@@ -717,7 +717,7 @@ var HttpClient = class {
|
|
|
717
717
|
}
|
|
718
718
|
/**
|
|
719
719
|
* Convenience method for DELETE requests.
|
|
720
|
-
*
|
|
720
|
+
*
|
|
721
721
|
* @example
|
|
722
722
|
* ```ts
|
|
723
723
|
* const { data } = await client.delete('/users/1');
|
|
@@ -733,26 +733,42 @@ var HttpClient = class {
|
|
|
733
733
|
/**
|
|
734
734
|
* Generic, strongly-typed endpoint with Zod schemas for request and response validation.
|
|
735
735
|
* Extend this class to create type-safe API endpoints.
|
|
736
|
-
*
|
|
736
|
+
*
|
|
737
737
|
* @template ReqSchema - Zod schema for request validation
|
|
738
738
|
* @template ResSchema - Zod schema for response validation
|
|
739
|
-
*
|
|
739
|
+
* @template PathParams - Type for path parameters (optional)
|
|
740
|
+
* @template QueryParams - Type for query parameters (optional)
|
|
741
|
+
*
|
|
740
742
|
* @example
|
|
741
743
|
* ```ts
|
|
742
744
|
* const UserSchema = z.object({ id: z.number(), name: z.string() });
|
|
743
745
|
* const CreateUserSchema = z.object({ name: z.string() });
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
+
*
|
|
747
|
+
* type UserPathParams = { id: string };
|
|
748
|
+
* type UserQueryParams = { include?: string; limit?: number };
|
|
749
|
+
*
|
|
750
|
+
* class GetUser extends BaseEndpoint<
|
|
751
|
+
* typeof CreateUserSchema,
|
|
752
|
+
* typeof UserSchema,
|
|
753
|
+
* UserPathParams,
|
|
754
|
+
* UserQueryParams
|
|
755
|
+
* > {
|
|
746
756
|
* protected method = 'GET' as const;
|
|
747
|
-
* protected path = (
|
|
748
|
-
*
|
|
757
|
+
* protected path = (params: UserPathParams) => `/users/${params.id}`;
|
|
758
|
+
*
|
|
749
759
|
* constructor(client: HttpClient) {
|
|
750
|
-
* super(client, {
|
|
760
|
+
* super(client, {
|
|
751
761
|
* requestSchema: CreateUserSchema,
|
|
752
|
-
* responseSchema: UserSchema
|
|
762
|
+
* responseSchema: UserSchema
|
|
753
763
|
* });
|
|
754
764
|
* }
|
|
755
765
|
* }
|
|
766
|
+
*
|
|
767
|
+
* // Usage:
|
|
768
|
+
* const user = await endpoint.call({
|
|
769
|
+
* pathParams: { id: '123' },
|
|
770
|
+
* query: { include: 'posts', limit: 10 }
|
|
771
|
+
* });
|
|
756
772
|
* ```
|
|
757
773
|
*/
|
|
758
774
|
var BaseEndpoint = class {
|
|
@@ -774,19 +790,23 @@ var BaseEndpoint = class {
|
|
|
774
790
|
/**
|
|
775
791
|
* Call the endpoint with strong typing derived from schemas.
|
|
776
792
|
* Validates request data before sending and response data after receiving.
|
|
777
|
-
*
|
|
793
|
+
*
|
|
778
794
|
* @param config - Request configuration object containing all parameters
|
|
779
795
|
* @returns Promise resolving to validated response data (typed by ResSchema)
|
|
780
796
|
* @throws {ZodError} If request validation fails
|
|
781
797
|
* @throws {ApiError} If response validation fails or request fails
|
|
782
|
-
*
|
|
798
|
+
*
|
|
783
799
|
* @example
|
|
784
800
|
* ```ts
|
|
785
801
|
* const endpoint = new GetUser(client);
|
|
786
|
-
* const user = await endpoint.call({
|
|
802
|
+
* const user = await endpoint.call({
|
|
803
|
+
* pathParams: { id: '123' },
|
|
804
|
+
* query: { include: 'posts' }
|
|
805
|
+
* });
|
|
787
806
|
* // With additional options:
|
|
788
|
-
* const user = await endpoint.call({
|
|
789
|
-
* data: {
|
|
807
|
+
* const user = await endpoint.call({
|
|
808
|
+
* data: { name: 'John' },
|
|
809
|
+
* pathParams: { id: '123' },
|
|
790
810
|
* headers: { 'X-Custom': 'value' },
|
|
791
811
|
* query: { include: 'posts' }
|
|
792
812
|
* });
|
|
@@ -801,8 +821,9 @@ var BaseEndpoint = class {
|
|
|
801
821
|
const pathArgs = pathParams ?? data;
|
|
802
822
|
const path = typeof this.path === "function" ? this.path(pathArgs) : this.path;
|
|
803
823
|
const body = this.method !== "GET" && this.method !== "HEAD" ? data : void 0;
|
|
824
|
+
const queryForRequest = query;
|
|
804
825
|
const { data: responseData } = await this.client.request(this.method, path, body, {
|
|
805
|
-
query,
|
|
826
|
+
query: queryForRequest,
|
|
806
827
|
headers,
|
|
807
828
|
baseUrlKey: baseUrlKey ?? this.options?.baseUrlKey,
|
|
808
829
|
signal
|
|
@@ -816,7 +837,7 @@ var BaseEndpoint = class {
|
|
|
816
837
|
/**
|
|
817
838
|
* Common ID type that supports strings, numbers, or UUIDs.
|
|
818
839
|
* Use this for entity identifiers in your schemas.
|
|
819
|
-
*
|
|
840
|
+
*
|
|
820
841
|
* @example
|
|
821
842
|
* ```ts
|
|
822
843
|
* const UserSchema = z.object({ id: Id, name: z.string() });
|
|
@@ -830,7 +851,7 @@ const Id = zod.z.union([
|
|
|
830
851
|
/**
|
|
831
852
|
* Common timestamp fields for entities.
|
|
832
853
|
* Use this for database models with creation/update tracking.
|
|
833
|
-
*
|
|
854
|
+
*
|
|
834
855
|
* @example
|
|
835
856
|
* ```ts
|
|
836
857
|
* const UserSchema = z.object({
|
|
@@ -872,14 +893,14 @@ const ApiErrorSchema = zod.z.object({
|
|
|
872
893
|
/**
|
|
873
894
|
* Generic envelope wrapper for API responses.
|
|
874
895
|
* Provides consistent structure with success flag, data, error, and metadata.
|
|
875
|
-
*
|
|
896
|
+
*
|
|
876
897
|
* @param inner - Zod schema for the response data
|
|
877
898
|
* @returns Envelope schema wrapping the inner schema
|
|
878
|
-
*
|
|
899
|
+
*
|
|
879
900
|
* @example
|
|
880
901
|
* ```ts
|
|
881
902
|
* const UserResponseSchema = Envelope(z.object({ id: Id, name: z.string() }));
|
|
882
|
-
*
|
|
903
|
+
*
|
|
883
904
|
* // Response structure:
|
|
884
905
|
* // {
|
|
885
906
|
* // success: true,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["z","s","opts: { header?: string; query?: string; value: string }","getToken: () => Promise<string> | string","minLevel: LogLevel","logger: Logger","k: string","err: unknown","init: RequestInit & { __urlOverride?: string }","timeoutId: NodeJS.Timeout | number | undefined","client: HttpClient","z"],"sources":["../lib/types.ts","../lib/auth.ts","../lib/validation.ts","../lib/logger.ts","../lib/metrics.ts","../lib/http/http-client.ts","../lib/endpoint/base-endpoint.ts","../lib/schemas/common.ts"],"sourcesContent":["import { z, ZodError } from 'zod';\nimport { AuthProvider } from './auth';\nimport { Logger } from './logger';\nimport { MetricsCollector } from './metrics';\n\nexport type Dictionary<T = unknown> = Record<string, T>;\n\nexport type FetchLike = (input: string | Request | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Map of base URLs for different services.\n * The 'default' key is required and used when no specific key is provided.\n * \n * @example\n * ```ts\n * {\n * default: 'https://api.example.com',\n * auth: 'https://auth.example.com',\n * cdn: 'https://cdn.example.com'\n * }\n * ```\n */\nexport type BaseUrlMap = {\n default: string;\n [service: string]: string;\n};\n\n/**\n * Configuration for retry behavior on failed requests.\n * Implements exponential backoff with optional jitter.\n * \n * @example\n * ```ts\n * {\n * maxRetries: 3,\n * baseDelayMs: 1000,\n * jitter: 0.2,\n * retryMethods: ['GET', 'HEAD', 'PUT']\n * }\n * ```\n */\nexport type RetryStrategy = {\n /** Maximum number of retry attempts */\n maxRetries: number;\n /** Base delay in milliseconds (will be exponentially increased) */\n baseDelayMs: number;\n /** Jitter factor 0..1 to randomize delays and prevent thundering herd */\n jitter?: number;\n /** HTTP methods eligible for retry */\n retryMethods?: (keyof typeof HTTPMethod)[];\n /** Custom function to determine if a request should be retried */\n shouldRetry?: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean;\n};\n\nexport const HTTPMethod = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n PATCH: 'PATCH',\n DELETE: 'DELETE',\n HEAD: 'HEAD',\n OPTIONS: 'OPTIONS',\n} as const;\n\nexport type HttpMethod = keyof typeof HTTPMethod;\n\n/**\n * Hook called after a response is received and parsed.\n * Useful for logging, metrics, or global error handling.\n */\nexport type AfterResponseHook = (ctx: {\n request: Request;\n response: Response;\n parsed?: unknown;\n}) => Promise<void> | void;\n\n/**\n * Hook called before a request is sent.\n * Useful for logging, adding headers, or modifying the request.\n */\nexport type BeforeRequestHook = (ctx: { url: string; init: RequestInit }) => Promise<void> | void;\n\nexport interface Interceptors {\n /** Hooks executed before each request is sent */\n beforeRequest?: BeforeRequestHook[];\n /** Hooks executed after each response is received */\n afterResponse?: AfterResponseHook[];\n}\n\nexport interface TimeoutOptions {\n /** Request timeout in milliseconds */\n requestTimeoutMs?: number;\n}\n\n/**\n * Configuration options for the HTTP client.\n * \n * @example\n * ```ts\n * const options: ClientOptions = {\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'X-API-Version': '1.0' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * }\n * ```\n */\nexport interface ClientOptions {\n /** Map of base URLs for different services */\n baseUrls: BaseUrlMap;\n /** Custom fetch implementation (defaults to globalThis.fetch) */\n fetch?: FetchLike;\n /** Default headers applied to all requests */\n headers?: Record<string, string>;\n /** Retry strategy configuration */\n retry?: RetryStrategy;\n /** Request/response interceptors */\n interceptors?: Interceptors;\n /** Timeout configuration */\n timeout?: TimeoutOptions;\n /** Authentication provider */\n auth?: AuthProvider;\n /** Logger instance */\n logger?: Logger;\n /** Metrics collector */\n metrics?: MetricsCollector;\n}\n\nexport type SafeParseResult<T> = { success: true; data: T } | { success: false; error: ZodError };\n\n/**\n * Custom error class for API-related errors.\n * Includes HTTP status codes, response details, and validation errors.\n * \n * @example\n * ```ts\n * throw new ApiError('Invalid request', {\n * status: 400,\n * details: { field: 'email', message: 'Invalid format' }\n * });\n * ```\n */\nexport class ApiError extends Error {\n public status?: number;\n public details?: unknown;\n public zodError?: ZodError;\n\n constructor(\n message: string,\n options?: { status?: number; cause?: unknown; details?: unknown; zodError?: ZodError }\n ) {\n super(message);\n this.name = 'ApiError';\n this.status = options?.status;\n this.details = options?.details;\n this.cause = options?.cause as any;\n this.zodError = options?.zodError;\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ApiError);\n }\n }\n\n /**\n * Check if this is a validation error (has zodError)\n */\n isValidationError(): boolean {\n return !!this.zodError;\n }\n\n /**\n * Check if this is a client error (4xx status)\n */\n isClientError(): boolean {\n return !!this.status && this.status >= 400 && this.status < 500;\n }\n\n /**\n * Check if this is a server error (5xx status)\n */\n isServerError(): boolean {\n return !!this.status && this.status >= 500;\n }\n\n /**\n * Get a formatted error message with all available details\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n details: this.details,\n zodError: this.zodError?.issues,\n stack: this.stack,\n };\n }\n}\n\nexport type Paginated<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n};\n\n/**\n * Schema for paginated responses\n */\nexport const PaginationSchema = z.object({\n items: z.array(z.unknown()),\n total: z.number().int().nonnegative(),\n page: z.number().int().nonnegative(),\n pageSize: z.number().int().positive(),\n});\n\n/**\n * Options that can be passed to individual requests to override defaults.\n * \n * @example\n * ```ts\n * await endpoint.call(data, {\n * baseUrlKey: 'v2',\n * headers: { 'X-Custom': 'value' },\n * query: { filter: 'active' }\n * });\n * ```\n */\nexport type RequestOptions = {\n /** Override base URL for a single call */\n baseUrlKey?: keyof BaseUrlMap;\n /** Additional headers for this call only */\n headers?: Record<string, string>;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n /** Custom query params */\n query?: URLSearchParams | Record<string, string | number | boolean | undefined>;\n};\n\n/**\n * Converts query parameters to a URL query string.\n * Filters out undefined values automatically.\n * \n * @param q - Query parameters as URLSearchParams or object\n * @returns Query string with leading '?' or empty string\n * \n * @example\n * ```ts\n * toQueryString({ page: 1, filter: 'active' }) // \"?page=1&filter=active\"\n * toQueryString({ optional: undefined }) // \"\"\n * ```\n */\nexport function toQueryString(q?: RequestOptions['query']): string {\n if (!q) return '';\n if (q instanceof URLSearchParams) {\n const s = q.toString();\n return s ? `?${s}` : '';\n }\n const params = new URLSearchParams();\n Object.entries(q).forEach(([k, v]) => {\n if (v !== undefined) {\n params.append(k, String(v));\n }\n });\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n","import type { RequestOptions as ReqOpts } from './types';\n\n/**\n * Extended RequestInit with URL override capability for query-based auth.\n */\nexport interface AuthContext {\n url: string;\n init: RequestInit & { __urlOverride?: string };\n options?: ReqOpts;\n}\n\n/**\n * Interface for authentication providers.\n * Implement this to create custom authentication strategies.\n * \n * @example\n * ```ts\n * class CustomAuth implements AuthProvider {\n * async apply({ init }) {\n * init.headers = { ...init.headers, 'X-Custom-Auth': 'token' };\n * }\n * }\n * ```\n */\nexport interface AuthProvider {\n /**\n * Apply authentication to the outgoing request.\n * Called after SDK headers are assembled, but before request is sent.\n * \n * @param req - Request context including URL, init, and options\n */\n apply(req: AuthContext): Promise<void> | void;\n}\n\n/**\n * No-op authentication provider (no authentication applied).\n * Use this when you don't need authentication.\n */\nexport class NoAuth implements AuthProvider {\n async apply() {\n /* no-op */\n }\n}\n\n/**\n * API Key authentication provider.\n * Supports both header-based and query parameter-based authentication.\n * \n * @example\n * ```ts\n * // Header-based\n * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });\n * \n * // Query parameter-based\n * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });\n * ```\n */\nexport class ApiKeyAuth implements AuthProvider {\n constructor(private opts: { header?: string; query?: string; value: string }) {\n if (!opts.header && !opts.query) {\n throw new Error('ApiKeyAuth requires either \"header\" or \"query\" option');\n }\n if (opts.header && opts.query) {\n throw new Error('ApiKeyAuth cannot use both \"header\" and \"query\" options');\n }\n }\n apply({ url, init }: AuthContext) {\n if (this.opts.header) {\n init.headers = { ...(init.headers as any), [this.opts.header]: this.opts.value };\n } else if (this.opts.query) {\n const u = new URL(url);\n u.searchParams.set(this.opts.query, this.opts.value);\n init.__urlOverride = u.toString();\n }\n }\n}\n\n/**\n * Bearer token authentication provider.\n * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).\n * \n * @example\n * ```ts\n * // Static token\n * const auth = new BearerTokenAuth(() => 'my-token');\n * \n * // Dynamic token with refresh\n * const auth = new BearerTokenAuth(async () => {\n * return await refreshAccessToken();\n * });\n * ```\n */\nexport class BearerTokenAuth implements AuthProvider {\n constructor(private getToken: () => Promise<string> | string) { }\n async apply({ init }: AuthContext) {\n const token = await this.getToken();\n if (!token) {\n throw new Error('BearerTokenAuth: token is empty or undefined');\n }\n init.headers = { ...(init.headers as any), Authorization: `Bearer ${token}` };\n }\n}\n","import { z } from 'zod';\nimport { ApiError, SafeParseResult } from './types';\n\n/**\n * Safely parse data with a Zod schema without throwing.\n * Returns a result object with success status and data or error.\n * \n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Result object with success flag and data/error\n * \n * @example\n * ```ts\n * const result = safeParse(UserSchema, userData);\n * if (result.success) {\n * console.log(result.data);\n * } else {\n * console.error(result.error);\n * }\n * ```\n */\nexport function safeParse<T>(schema: z.ZodType, data: unknown): SafeParseResult<T> {\n const res = schema.safeParse(data);\n if (res.success) return { success: true, data: res.data as T };\n return { success: false, error: res.error };\n}\n\n/**\n * Parse data with a Zod schema, throwing an ApiError on validation failure.\n * Use this when you want to fail fast on invalid data.\n * \n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {ApiError} If validation fails\n * \n * @example\n * ```ts\n * try {\n * const user = parseOrThrow(UserSchema, userData);\n * console.log(user);\n * } catch (error) {\n * if (error instanceof ApiError && error.zodError) {\n * console.error('Validation failed:', error.zodError.issues);\n * }\n * }\n * ```\n */\nexport function parseOrThrow<T>(schema: z.ZodType, data: unknown): T {\n const res = schema.safeParse(data);\n if (!res.success) {\n throw new ApiError('Response validation failed', { zodError: res.error });\n }\n return res.data as T;\n}\n","/**\n * Log levels for structured logging.\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Structured log entry with metadata.\n */\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: string;\n context?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Logger interface for custom logger implementations.\n * Implement this to integrate with your logging infrastructure.\n * \n * @example\n * ```ts\n * class ConsoleLogger implements Logger {\n * log(entry: LogEntry) {\n * console.log(JSON.stringify(entry));\n * }\n * }\n * ```\n */\nexport interface Logger {\n log(entry: LogEntry): void;\n}\n\n/**\n * Default console logger implementation.\n * Formats log entries as JSON for easy parsing.\n */\nexport class ConsoleLogger implements Logger {\n constructor(private minLevel: LogLevel = LogLevel.INFO) { }\n\n log(entry: LogEntry): void {\n const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];\n const entryLevelIndex = levels.indexOf(entry.level);\n const minLevelIndex = levels.indexOf(this.minLevel);\n\n if (entryLevelIndex < minLevelIndex) return;\n\n const output = {\n ...entry,\n error: entry.error\n ? {\n message: entry.error.message,\n stack: entry.error.stack,\n name: entry.error.name,\n }\n : undefined,\n };\n\n switch (entry.level) {\n case LogLevel.DEBUG:\n console.debug(JSON.stringify(output));\n break;\n case LogLevel.INFO:\n console.info(JSON.stringify(output));\n break;\n case LogLevel.WARN:\n console.warn(JSON.stringify(output));\n break;\n case LogLevel.ERROR:\n console.error(JSON.stringify(output));\n break;\n }\n }\n}\n\n/**\n * No-op logger that discards all log entries.\n * Use this in production if you don't want any logging.\n */\nexport class NoOpLogger implements Logger {\n log(_entry: LogEntry): void {\n // no-op\n }\n}\n\n/**\n * Utility class for creating structured log entries.\n */\nexport class LoggerUtil {\n constructor(private logger: Logger) { }\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.DEBUG,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.INFO,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.WARN,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n error(message: string, error?: Error, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.ERROR,\n message,\n timestamp: new Date().toISOString(),\n context,\n error,\n });\n }\n}\n","/**\n * Metrics data for HTTP requests.\n */\nexport interface RequestMetrics {\n method: string;\n path: string;\n status?: number;\n durationMs: number;\n timestamp: string;\n success: boolean;\n error?: string;\n}\n\n/**\n * Interface for metrics collectors.\n * Implement this to integrate with your monitoring infrastructure (DataDog, Prometheus, etc.).\n * \n * @example\n * ```ts\n * class DataDogMetrics implements MetricsCollector {\n * collect(metrics: RequestMetrics) {\n * dogstatsd.histogram('http.request.duration', metrics.durationMs, {\n * method: metrics.method,\n * status: String(metrics.status)\n * });\n * }\n * }\n * ```\n */\nexport interface MetricsCollector {\n collect(metrics: RequestMetrics): void;\n}\n\n/**\n * No-op metrics collector that discards all metrics.\n */\nexport class NoOpMetricsCollector implements MetricsCollector {\n collect(_metrics: RequestMetrics): void {\n // no-op\n }\n}\n\n/**\n * In-memory metrics collector for testing and development.\n * Stores metrics in memory with configurable retention.\n */\nexport class InMemoryMetricsCollector implements MetricsCollector {\n private metrics: RequestMetrics[] = [];\n private readonly maxEntries: number;\n\n constructor(maxEntries = 1000) {\n this.maxEntries = maxEntries;\n }\n\n collect(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n if (this.metrics.length > this.maxEntries) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get all collected metrics.\n */\n getMetrics(): RequestMetrics[] {\n return [...this.metrics];\n }\n\n /**\n * Get metrics summary statistics.\n */\n getSummary(): {\n total: number;\n successful: number;\n failed: number;\n avgDurationMs: number;\n minDurationMs: number;\n maxDurationMs: number;\n } {\n if (this.metrics.length === 0) {\n return {\n total: 0,\n successful: 0,\n failed: 0,\n avgDurationMs: 0,\n minDurationMs: 0,\n maxDurationMs: 0,\n };\n }\n\n const successful = this.metrics.filter((m) => m.success).length;\n const durations = this.metrics.map((m) => m.durationMs);\n const sum = durations.reduce((a, b) => a + b, 0);\n\n return {\n total: this.metrics.length,\n successful,\n failed: this.metrics.length - successful,\n avgDurationMs: sum / this.metrics.length,\n minDurationMs: Math.min(...durations),\n maxDurationMs: Math.max(...durations),\n };\n }\n\n /**\n * Clear all collected metrics.\n */\n clear(): void {\n this.metrics = [];\n }\n}\n\n/**\n * Console-based metrics collector for debugging.\n */\nexport class ConsoleMetricsCollector implements MetricsCollector {\n collect(metrics: RequestMetrics): void {\n console.log('[METRICS]', JSON.stringify(metrics));\n }\n}\n","import type { AuthProvider } from '../auth';\nimport { NoAuth } from '../auth';\nimport { LoggerUtil, NoOpLogger } from '../logger';\nimport { MetricsCollector, NoOpMetricsCollector } from '../metrics';\nimport {\n ApiError,\n ClientOptions,\n FetchLike,\n HTTPMethod,\n Interceptors,\n RequestOptions,\n RetryStrategy,\n toQueryString,\n} from '../types';\n\n/**\n * HTTP client with built-in retry logic, authentication, and interceptors.\n * Supports multiple base URLs, type-safe requests, and comprehensive error handling.\n * \n * @example\n * ```ts\n * const client = new HttpClient({\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'Content-Type': 'application/json' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * });\n * \n * const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });\n * ```\n */\nexport class HttpClient {\n private fetchImpl: FetchLike;\n private baseUrls: ClientOptions['baseUrls'];\n private headers: Record<string, string>;\n private interceptors: Interceptors;\n private retry: RetryStrategy;\n private timeoutMs?: number;\n private auth: AuthProvider;\n private logger: LoggerUtil;\n private metrics: MetricsCollector;\n\n /**\n * Creates a new HTTP client instance.\n * \n * @param opts - Client configuration options\n * @throws {Error} If no fetch implementation is available\n */\n constructor(opts: ClientOptions) {\n this.fetchImpl = opts.fetch ?? (globalThis.fetch?.bind(globalThis) as FetchLike);\n if (!this.fetchImpl)\n throw new Error('No fetch implementation found. Pass one via options.fetch.');\n\n // Validate baseUrls configuration\n if (!opts.baseUrls || typeof opts.baseUrls !== 'object') {\n throw new Error('baseUrls must be provided and must be an object');\n }\n if (!opts.baseUrls.default) {\n throw new Error('baseUrls must include a \"default\" key');\n }\n\n this.baseUrls = opts.baseUrls;\n this.headers = opts.headers ?? { 'Content-Type': 'application/json' };\n this.interceptors = opts.interceptors ?? {};\n this.retry = opts.retry ?? {\n maxRetries: 2,\n baseDelayMs: 250,\n jitter: 0.2,\n retryMethods: ['GET', 'HEAD'],\n };\n\n // Validate retry configuration\n if (this.retry.maxRetries < 0) {\n throw new Error('retry.maxRetries must be non-negative');\n }\n if (this.retry.baseDelayMs < 0) {\n throw new Error('retry.baseDelayMs must be non-negative');\n }\n if (this.retry.jitter !== undefined && (this.retry.jitter < 0 || this.retry.jitter > 1)) {\n throw new Error('retry.jitter must be between 0 and 1');\n }\n\n this.timeoutMs = opts.timeout?.requestTimeoutMs;\n if (this.timeoutMs !== undefined && this.timeoutMs < 0) {\n throw new Error('timeout.requestTimeoutMs must be non-negative');\n }\n\n this.auth = opts['auth'] ?? new NoAuth();\n this.logger = new LoggerUtil(opts.logger ?? new NoOpLogger());\n this.metrics = opts.metrics ?? new NoOpMetricsCollector();\n }\n\n /**\n * Set or update the authentication provider.\n * \n * @param auth - Authentication provider instance\n * @example\n * ```ts\n * client.setAuth(new BearerTokenAuth(() => getToken()));\n * ```\n */\n setAuth(auth: AuthProvider) {\n this.auth = auth;\n }\n\n private resolveBaseUrl(key?: keyof typeof this.baseUrls) {\n const k: string = (key as string) || 'default';\n const url = this.baseUrls[k];\n if (!url) {\n const availableKeys = Object.keys(this.baseUrls).join(', ');\n throw new Error(`Unknown baseUrl key: \"${k}\". Available keys: ${availableKeys}`);\n }\n return url.replace(/\\/$/, '');\n }\n\n /**\n * Sleep for a specified duration (used for retry backoff).\n * @private\n */\n private sleep(ms: number) {\n return new Promise((res) => setTimeout(res, ms));\n }\n\n /**\n * Execute a function with retry logic and exponential backoff.\n * @private\n */\n private async withRetry<T>(\n fn: () => Promise<T>,\n canRetry: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean\n ): Promise<T> {\n let attempt = 0;\n const { maxRetries, baseDelayMs, jitter = 0.2 } = this.retry;\n while (true) {\n try {\n return await fn();\n } catch (err: unknown) {\n if (attempt >= maxRetries || !canRetry({ attempt, error: err })) throw err;\n const backoff = baseDelayMs * 2 ** attempt;\n const j = 1 + (Math.random() * 2 - 1) * jitter;\n await this.sleep(backoff * j);\n attempt++;\n }\n }\n }\n\n /**\n * Run all registered before-request hooks.\n * @private\n */\n private async runBeforeHooks(url: string, init: RequestInit & { __urlOverride?: string }) {\n for (const h of this.interceptors.beforeRequest ?? []) {\n await h({ url, init });\n }\n }\n\n /**\n * Run all registered after-response hooks.\n * @private\n */\n private async runAfterHooks(req: Request, res: Response, parsed?: unknown) {\n for (const h of this.interceptors.afterResponse ?? []) {\n await h({ request: req, response: res, parsed });\n }\n }\n\n /**\n * Make an HTTP request with automatic retry, authentication, and validation.\n * \n * @param method - HTTP method (GET, POST, PUT, etc.)\n * @param path - Request path (will be appended to base URL)\n * @param body - Request body (will be JSON.stringify'd if Content-Type is json)\n * @param options - Additional request options (headers, query params, etc.)\n * @returns Promise resolving to response data and Response object\n * @throws {ApiError} If request fails or response validation fails\n * \n * @example\n * ```ts\n * const { data, response } = await client.request('GET', '/users', undefined, {\n * query: { page: 1, limit: 10 },\n * headers: { 'X-Custom': 'value' }\n * });\n * ```\n */\n async request<T = unknown>(\n method: keyof typeof HTTPMethod,\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n const startTime = Date.now();\n const base = this.resolveBaseUrl(options?.baseUrlKey);\n let url = `${base}${path}${toQueryString(options?.query)}`;\n\n this.logger.debug('HTTP request initiated', {\n method,\n path,\n baseUrlKey: options?.baseUrlKey,\n hasBody: body !== undefined,\n });\n\n const headers = { ...this.headers, ...(options?.headers ?? {}) };\n\n const controller = new AbortController();\n const signal = options?.signal ?? controller.signal;\n const init: RequestInit & { __urlOverride?: string } = {\n method,\n headers,\n body:\n body != null\n ? headers['Content-Type']?.includes('json')\n ? JSON.stringify(body)\n : String(body)\n : undefined,\n signal,\n };\n\n await this.auth.apply({ url, init, options });\n if (init.__urlOverride) url = init.__urlOverride;\n await this.runBeforeHooks(url, init);\n\n const doFetch = async () => {\n // Apply timeout if configured\n let timeoutId: NodeJS.Timeout | number | undefined;\n if (this.timeoutMs && !options?.signal) {\n timeoutId = setTimeout(() => {\n const timeoutError = new Error('Request timeout');\n timeoutError.name = 'TimeoutError';\n controller.abort(timeoutError);\n }, this.timeoutMs);\n }\n\n try {\n const req = new Request(url, init);\n\n const res = await this.fetchImpl(req);\n if (!res.ok) {\n // Read response safely\n let text = '';\n try {\n text = await res.text();\n } catch (readError) {\n text = `Failed to read response: ${readError instanceof Error ? readError.message : String(readError)}`;\n }\n throw new ApiError(`HTTP ${res.status}: ${res.statusText}`, {\n status: res.status,\n details: text,\n });\n }\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('json') ? await res.json() : await res.text();\n await this.runAfterHooks(new Request(url, init), res, data);\n\n const duration = Date.now() - startTime;\n this.logger.info('HTTP request successful', {\n method,\n url,\n status: res.status,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: res.status,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: true,\n });\n\n return { data: data as T, response: res };\n } catch (error) {\n const duration = Date.now() - startTime;\n this.logger.error('HTTP request failed', error as Error, {\n method,\n url,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: error instanceof ApiError ? error.status : undefined,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n };\n\n const canRetry = ({\n response,\n error,\n }: {\n response?: Response;\n error?: unknown;\n attempt: number;\n }) => {\n // Don't retry timeouts or aborts\n if (error && typeof error === 'object' && 'name' in error) {\n const errorName = (error as { name?: string }).name;\n if (errorName === 'AbortError' || errorName === 'TimeoutError') return false;\n }\n // Retry on network errors or 5xx\n if (error instanceof ApiError && error.status && error.status >= 500) return true;\n if (error && !response) return true; // network error\n return false;\n };\n\n if (!this.retry.retryMethods?.includes(method)) {\n return doFetch();\n }\n return this.withRetry(doFetch, canRetry);\n }\n\n /**\n * Convenience method for GET requests.\n * \n * @example\n * ```ts\n * const { data } = await client.get('/users', { query: { page: 1 } });\n * ```\n */\n async get<T = unknown>(path: string, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('GET', path, undefined, options);\n }\n\n /**\n * Convenience method for POST requests.\n * \n * @example\n * ```ts\n * const { data } = await client.post('/users', { name: 'John' });\n * ```\n */\n async post<T = unknown>(path: string, body?: unknown, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('POST', path, body, options);\n }\n\n /**\n * Convenience method for PUT requests.\n * \n * @example\n * ```ts\n * const { data } = await client.put('/users/1', { name: 'John Updated' });\n * ```\n */\n async put<T = unknown>(path: string, body?: unknown, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('PUT', path, body, options);\n }\n\n /**\n * Convenience method for PATCH requests.\n * \n * @example\n * ```ts\n * const { data } = await client.patch('/users/1', { name: 'John' });\n * ```\n */\n async patch<T = unknown>(path: string, body?: unknown, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('PATCH', path, body, options);\n }\n\n /**\n * Convenience method for DELETE requests.\n * \n * @example\n * ```ts\n * const { data } = await client.delete('/users/1');\n * ```\n */\n async delete<T = unknown>(path: string, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('DELETE', path, undefined, options);\n }\n}\n","import { z } from 'zod';\nimport { HttpClient } from '../http/http-client';\nimport { HTTPMethod } from '../types';\nimport { parseOrThrow } from '../validation';\n\n/**\n * Request configuration for endpoint calls.\n * Wrapper object containing all request parameters.\n *\n * @template ReqSchema - Zod schema for request validation\n */\nexport type EndpointCallConfig<ReqSchema extends z.ZodType> = {\n /** Request body data (for POST, PUT, PATCH, etc.) or request args */\n data?: z.infer<ReqSchema>;\n /** Path parameters for dynamic path construction */\n pathParams?: Record<string, string | number>;\n /** Query string parameters */\n query?: Record<string, string | number | boolean | undefined> | URLSearchParams;\n /** Request headers */\n headers?: Record<string, string>;\n /** Override base URL for this call */\n baseUrlKey?: string;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n};\n\n/**\n * Generic, strongly-typed endpoint with Zod schemas for request and response validation.\n * Extend this class to create type-safe API endpoints.\n * \n * @template ReqSchema - Zod schema for request validation\n * @template ResSchema - Zod schema for response validation\n * \n * @example\n * ```ts\n * const UserSchema = z.object({ id: z.number(), name: z.string() });\n * const CreateUserSchema = z.object({ name: z.string() });\n * \n * class GetUser extends BaseEndpoint<typeof CreateUserSchema, typeof UserSchema> {\n * protected method = 'GET' as const;\n * protected path = (args: z.infer<typeof CreateUserSchema>) => `/users/${args.id}`;\n * \n * constructor(client: HttpClient) {\n * super(client, { \n * requestSchema: CreateUserSchema,\n * responseSchema: UserSchema \n * });\n * }\n * }\n * ```\n */\nexport abstract class BaseEndpoint<ReqSchema extends z.ZodType, ResSchema extends z.ZodType> {\n /** HTTP method for this endpoint */\n protected abstract readonly method: keyof typeof HTTPMethod;\n /** URL path (can be a function for dynamic paths) */\n protected abstract readonly path: string | ((params: z.infer<ReqSchema>) => string);\n /** Additional options for the request */\n protected readonly options?: {\n /** Override base URL for this call */\n baseUrlKey?: string;\n };\n /** Optional request schema for validation */\n protected readonly requestSchema?: ReqSchema;\n /** Response schema for validation */\n protected readonly responseSchema: ResSchema;\n\n /**\n * @param client - HttpClient instance\n * @param cfg - Configuration with request and response schemas\n */\n constructor(\n protected client: HttpClient,\n cfg: { requestSchema?: ReqSchema; responseSchema: ResSchema }\n ) {\n this.requestSchema = cfg.requestSchema;\n this.responseSchema = cfg.responseSchema;\n }\n\n /**\n * Call the endpoint with strong typing derived from schemas.\n * Validates request data before sending and response data after receiving.\n * \n * @param config - Request configuration object containing all parameters\n * @returns Promise resolving to validated response data (typed by ResSchema)\n * @throws {ZodError} If request validation fails\n * @throws {ApiError} If response validation fails or request fails\n * \n * @example\n * ```ts\n * const endpoint = new GetUser(client);\n * const user = await endpoint.call({ data: { id: 1 } });\n * // With additional options:\n * const user = await endpoint.call({ \n * data: { id: 1 },\n * headers: { 'X-Custom': 'value' },\n * query: { include: 'posts' }\n * });\n * ```\n */\n async call(config: EndpointCallConfig<ReqSchema> = {}): Promise<z.infer<ResSchema>> {\n const { data, query, headers, baseUrlKey, signal, pathParams } = config;\n\n // Validate request body/params before sending (when schema provided)\n if (this.requestSchema && data !== undefined) {\n const parsed = this.requestSchema.safeParse(data);\n if (!parsed.success) throw parsed.error;\n }\n\n // Build path - use pathParams if provided, otherwise use data\n const pathArgs = pathParams ?? data;\n const path = typeof this.path === 'function' ? this.path(pathArgs as z.infer<ReqSchema>) : this.path;\n\n // For GET/HEAD methods, don't send body\n const shouldHaveBody = this.method !== 'GET' && this.method !== 'HEAD';\n const body = shouldHaveBody ? data : undefined;\n\n const { data: responseData } = await this.client.request(this.method, path, body, {\n query,\n headers,\n baseUrlKey: baseUrlKey ?? this.options?.baseUrlKey,\n signal,\n });\n\n return parseOrThrow<z.infer<ResSchema>>(this.responseSchema, responseData);\n }\n}\n","import { z } from 'zod';\n\n/**\n * Common ID type that supports strings, numbers, or UUIDs.\n * Use this for entity identifiers in your schemas.\n * \n * @example\n * ```ts\n * const UserSchema = z.object({ id: Id, name: z.string() });\n * ```\n */\nexport const Id = z.union([\n z.string().min(1),\n z.number(),\n z.uuid({\n version: 'v4',\n }),\n]);\nexport type IdType = z.infer<typeof Id>;\n\n/**\n * Common timestamp fields for entities.\n * Use this for database models with creation/update tracking.\n * \n * @example\n * ```ts\n * const UserSchema = z.object({\n * id: Id,\n * name: z.string(),\n * ...Timestamps.shape\n * });\n * ```\n */\nexport const Timestamps = z.object({\n createdAt: z.iso.datetime(),\n updatedAt: z.iso.datetime(),\n});\n\n/**\n * Metadata information typically included in API responses.\n * Contains request tracking and debugging information.\n */\nexport const Meta = z.object({\n requestId: z.string().optional(),\n timestamp: z.iso.datetime().optional(),\n traceId: z.string().optional(),\n});\n\n/**\n * Detailed error information for a specific field or path.\n */\nexport const ErrorDetail = z.object({\n path: z.string().optional(),\n message: z.string(),\n});\n\n/**\n * Standard API error response schema.\n * Use this for consistent error handling across your API.\n */\nexport const ApiErrorSchema = z.object({\n code: z.string(),\n message: z.string(),\n details: z.array(ErrorDetail).optional(),\n});\n\n/**\n * Generic envelope wrapper for API responses.\n * Provides consistent structure with success flag, data, error, and metadata.\n * \n * @param inner - Zod schema for the response data\n * @returns Envelope schema wrapping the inner schema\n * \n * @example\n * ```ts\n * const UserResponseSchema = Envelope(z.object({ id: Id, name: z.string() }));\n * \n * // Response structure:\n * // {\n * // success: true,\n * // data: { id: 1, name: 'John' },\n * // meta: { requestId: '...' }\n * // }\n * ```\n */\nexport const Envelope = <T extends z.ZodType>(inner: T) =>\n z.object({\n success: z.boolean(),\n data: inner.optional().nullable(),\n error: ApiErrorSchema.optional(),\n meta: Meta.optional(),\n });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["z","s","opts: { header?: string; query?: string; value: string }","getToken: () => Promise<string> | string","minLevel: LogLevel","logger: Logger","k: string","err: unknown","init: RequestInit & { __urlOverride?: string }","timeoutId: NodeJS.Timeout | number | undefined","client: HttpClient","z"],"sources":["../lib/types.ts","../lib/auth.ts","../lib/validation.ts","../lib/logger.ts","../lib/metrics.ts","../lib/http/http-client.ts","../lib/endpoint/base-endpoint.ts","../lib/schemas/common.ts"],"sourcesContent":["import { z, ZodError } from 'zod';\nimport { AuthProvider } from './auth';\nimport { Logger } from './logger';\nimport { MetricsCollector } from './metrics';\n\nexport type Dictionary<T = unknown> = Record<string, T>;\n\nexport type FetchLike = (input: string | Request | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Map of base URLs for different services.\n * The 'default' key is required and used when no specific key is provided.\n *\n * @example\n * ```ts\n * {\n * default: 'https://api.example.com',\n * auth: 'https://auth.example.com',\n * cdn: 'https://cdn.example.com'\n * }\n * ```\n */\nexport type BaseUrlMap = {\n default: string;\n [service: string]: string;\n};\n\n/**\n * Configuration for retry behavior on failed requests.\n * Implements exponential backoff with optional jitter.\n *\n * @example\n * ```ts\n * {\n * maxRetries: 3,\n * baseDelayMs: 1000,\n * jitter: 0.2,\n * retryMethods: ['GET', 'HEAD', 'PUT']\n * }\n * ```\n */\nexport type RetryStrategy = {\n /** Maximum number of retry attempts */\n maxRetries: number;\n /** Base delay in milliseconds (will be exponentially increased) */\n baseDelayMs: number;\n /** Jitter factor 0..1 to randomize delays and prevent thundering herd */\n jitter?: number;\n /** HTTP methods eligible for retry */\n retryMethods?: (keyof typeof HTTPMethod)[];\n /** Custom function to determine if a request should be retried */\n shouldRetry?: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean;\n};\n\nexport const HTTPMethod = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n PATCH: 'PATCH',\n DELETE: 'DELETE',\n HEAD: 'HEAD',\n OPTIONS: 'OPTIONS',\n} as const;\n\nexport type HttpMethod = keyof typeof HTTPMethod;\n\n/**\n * Hook called after a response is received and parsed.\n * Useful for logging, metrics, or global error handling.\n */\nexport type AfterResponseHook = (ctx: {\n request: Request;\n response: Response;\n parsed?: unknown;\n}) => Promise<void> | void;\n\n/**\n * Hook called before a request is sent.\n * Useful for logging, adding headers, or modifying the request.\n */\nexport type BeforeRequestHook = (ctx: { url: string; init: RequestInit }) => Promise<void> | void;\n\nexport interface Interceptors {\n /** Hooks executed before each request is sent */\n beforeRequest?: BeforeRequestHook[];\n /** Hooks executed after each response is received */\n afterResponse?: AfterResponseHook[];\n}\n\nexport interface TimeoutOptions {\n /** Request timeout in milliseconds */\n requestTimeoutMs?: number;\n}\n\n/**\n * Configuration options for the HTTP client.\n *\n * @example\n * ```ts\n * const options: ClientOptions = {\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'X-API-Version': '1.0' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * }\n * ```\n */\nexport interface ClientOptions {\n /** Map of base URLs for different services */\n baseUrls: BaseUrlMap;\n /** Custom fetch implementation (defaults to globalThis.fetch) */\n fetch?: FetchLike;\n /** Default headers applied to all requests */\n headers?: Record<string, string>;\n /** Retry strategy configuration */\n retry?: RetryStrategy;\n /** Request/response interceptors */\n interceptors?: Interceptors;\n /** Timeout configuration */\n timeout?: TimeoutOptions;\n /** Authentication provider */\n auth?: AuthProvider;\n /** Logger instance */\n logger?: Logger;\n /** Metrics collector */\n metrics?: MetricsCollector;\n}\n\nexport type SafeParseResult<T> = { success: true; data: T } | { success: false; error: ZodError };\n\n/**\n * Custom error class for API-related errors.\n * Includes HTTP status codes, response details, and validation errors.\n *\n * @example\n * ```ts\n * throw new ApiError('Invalid request', {\n * status: 400,\n * details: { field: 'email', message: 'Invalid format' }\n * });\n * ```\n */\nexport class ApiError extends Error {\n public status?: number;\n public details?: unknown;\n public zodError?: ZodError;\n\n constructor(\n message: string,\n options?: { status?: number; cause?: unknown; details?: unknown; zodError?: ZodError }\n ) {\n super(message);\n this.name = 'ApiError';\n this.status = options?.status;\n this.details = options?.details;\n this.cause = options?.cause as any;\n this.zodError = options?.zodError;\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ApiError);\n }\n }\n\n /**\n * Check if this is a validation error (has zodError)\n */\n isValidationError(): boolean {\n return !!this.zodError;\n }\n\n /**\n * Check if this is a client error (4xx status)\n */\n isClientError(): boolean {\n return !!this.status && this.status >= 400 && this.status < 500;\n }\n\n /**\n * Check if this is a server error (5xx status)\n */\n isServerError(): boolean {\n return !!this.status && this.status >= 500;\n }\n\n /**\n * Get a formatted error message with all available details\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n details: this.details,\n zodError: this.zodError?.issues,\n stack: this.stack,\n };\n }\n}\n\nexport type Paginated<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n};\n\n/**\n * Schema for paginated responses\n */\nexport const PaginationSchema = z.object({\n items: z.array(z.unknown()),\n total: z.number().int().nonnegative(),\n page: z.number().int().nonnegative(),\n pageSize: z.number().int().positive(),\n});\n\n/**\n * Options that can be passed to individual requests to override defaults.\n *\n * @example\n * ```ts\n * await endpoint.call(data, {\n * baseUrlKey: 'v2',\n * headers: { 'X-Custom': 'value' },\n * query: { filter: 'active' }\n * });\n * ```\n */\nexport type RequestOptions = {\n /** Override base URL for a single call */\n baseUrlKey?: keyof BaseUrlMap;\n /** Additional headers for this call only */\n headers?: Record<string, string>;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n /** Custom query params */\n query?: URLSearchParams | Record<string, string | number | boolean | undefined>;\n};\n\n/**\n * Converts query parameters to a URL query string.\n * Filters out undefined values automatically.\n *\n * @param q - Query parameters as URLSearchParams or object\n * @returns Query string with leading '?' or empty string\n *\n * @example\n * ```ts\n * toQueryString({ page: 1, filter: 'active' }) // \"?page=1&filter=active\"\n * toQueryString({ optional: undefined }) // \"\"\n * ```\n */\nexport function toQueryString(q?: RequestOptions['query']): string {\n if (!q) return '';\n if (q instanceof URLSearchParams) {\n const s = q.toString();\n return s ? `?${s}` : '';\n }\n const params = new URLSearchParams();\n Object.entries(q).forEach(([k, v]) => {\n if (v !== undefined) {\n params.append(k, String(v));\n }\n });\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n","import type { RequestOptions as ReqOpts } from './types';\n\n/**\n * Extended RequestInit with URL override capability for query-based auth.\n */\nexport interface AuthContext {\n url: string;\n init: RequestInit & { __urlOverride?: string };\n options?: ReqOpts;\n}\n\n/**\n * Interface for authentication providers.\n * Implement this to create custom authentication strategies.\n *\n * @example\n * ```ts\n * class CustomAuth implements AuthProvider {\n * async apply({ init }) {\n * init.headers = { ...init.headers, 'X-Custom-Auth': 'token' };\n * }\n * }\n * ```\n */\nexport interface AuthProvider {\n /**\n * Apply authentication to the outgoing request.\n * Called after SDK headers are assembled, but before request is sent.\n *\n * @param req - Request context including URL, init, and options\n */\n apply(req: AuthContext): Promise<void> | void;\n}\n\n/**\n * No-op authentication provider (no authentication applied).\n * Use this when you don't need authentication.\n */\nexport class NoAuth implements AuthProvider {\n async apply() {\n /* no-op */\n }\n}\n\n/**\n * API Key authentication provider.\n * Supports both header-based and query parameter-based authentication.\n *\n * @example\n * ```ts\n * // Header-based\n * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });\n *\n * // Query parameter-based\n * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });\n * ```\n */\nexport class ApiKeyAuth implements AuthProvider {\n constructor(private opts: { header?: string; query?: string; value: string }) {\n if (!opts.header && !opts.query) {\n throw new Error('ApiKeyAuth requires either \"header\" or \"query\" option');\n }\n if (opts.header && opts.query) {\n throw new Error('ApiKeyAuth cannot use both \"header\" and \"query\" options');\n }\n }\n apply({ url, init }: AuthContext) {\n if (this.opts.header) {\n init.headers = { ...(init.headers as any), [this.opts.header]: this.opts.value };\n } else if (this.opts.query) {\n const u = new URL(url);\n u.searchParams.set(this.opts.query, this.opts.value);\n init.__urlOverride = u.toString();\n }\n }\n}\n\n/**\n * Bearer token authentication provider.\n * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).\n *\n * @example\n * ```ts\n * // Static token\n * const auth = new BearerTokenAuth(() => 'my-token');\n *\n * // Dynamic token with refresh\n * const auth = new BearerTokenAuth(async () => {\n * return await refreshAccessToken();\n * });\n * ```\n */\nexport class BearerTokenAuth implements AuthProvider {\n constructor(private getToken: () => Promise<string> | string) {}\n async apply({ init }: AuthContext) {\n const token = await this.getToken();\n if (!token) {\n throw new Error('BearerTokenAuth: token is empty or undefined');\n }\n init.headers = { ...(init.headers as any), Authorization: `Bearer ${token}` };\n }\n}\n","import { z } from 'zod';\nimport { ApiError, SafeParseResult } from './types';\n\n/**\n * Safely parse data with a Zod schema without throwing.\n * Returns a result object with success status and data or error.\n *\n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Result object with success flag and data/error\n *\n * @example\n * ```ts\n * const result = safeParse(UserSchema, userData);\n * if (result.success) {\n * console.log(result.data);\n * } else {\n * console.error(result.error);\n * }\n * ```\n */\nexport function safeParse<T>(schema: z.ZodType, data: unknown): SafeParseResult<T> {\n const res = schema.safeParse(data);\n if (res.success) return { success: true, data: res.data as T };\n return { success: false, error: res.error };\n}\n\n/**\n * Parse data with a Zod schema, throwing an ApiError on validation failure.\n * Use this when you want to fail fast on invalid data.\n *\n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {ApiError} If validation fails\n *\n * @example\n * ```ts\n * try {\n * const user = parseOrThrow(UserSchema, userData);\n * console.log(user);\n * } catch (error) {\n * if (error instanceof ApiError && error.zodError) {\n * console.error('Validation failed:', error.zodError.issues);\n * }\n * }\n * ```\n */\nexport function parseOrThrow<T>(schema: z.ZodType, data: unknown): T {\n const res = schema.safeParse(data);\n if (!res.success) {\n throw new ApiError('Response validation failed', { zodError: res.error });\n }\n return res.data as T;\n}\n","/**\n * Log levels for structured logging.\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Structured log entry with metadata.\n */\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: string;\n context?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Logger interface for custom logger implementations.\n * Implement this to integrate with your logging infrastructure.\n *\n * @example\n * ```ts\n * class ConsoleLogger implements Logger {\n * log(entry: LogEntry) {\n * console.log(JSON.stringify(entry));\n * }\n * }\n * ```\n */\nexport interface Logger {\n log(entry: LogEntry): void;\n}\n\n/**\n * Default console logger implementation.\n * Formats log entries as JSON for easy parsing.\n */\nexport class ConsoleLogger implements Logger {\n constructor(private minLevel: LogLevel = LogLevel.INFO) {}\n\n log(entry: LogEntry): void {\n const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];\n const entryLevelIndex = levels.indexOf(entry.level);\n const minLevelIndex = levels.indexOf(this.minLevel);\n\n if (entryLevelIndex < minLevelIndex) return;\n\n const output = {\n ...entry,\n error: entry.error\n ? {\n message: entry.error.message,\n stack: entry.error.stack,\n name: entry.error.name,\n }\n : undefined,\n };\n\n switch (entry.level) {\n case LogLevel.DEBUG:\n console.debug(JSON.stringify(output));\n break;\n case LogLevel.INFO:\n console.info(JSON.stringify(output));\n break;\n case LogLevel.WARN:\n console.warn(JSON.stringify(output));\n break;\n case LogLevel.ERROR:\n console.error(JSON.stringify(output));\n break;\n }\n }\n}\n\n/**\n * No-op logger that discards all log entries.\n * Use this in production if you don't want any logging.\n */\nexport class NoOpLogger implements Logger {\n log(_entry: LogEntry): void {\n // no-op\n }\n}\n\n/**\n * Utility class for creating structured log entries.\n */\nexport class LoggerUtil {\n constructor(private logger: Logger) {}\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.DEBUG,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.INFO,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.WARN,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n error(message: string, error?: Error, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.ERROR,\n message,\n timestamp: new Date().toISOString(),\n context,\n error,\n });\n }\n}\n","/**\n * Metrics data for HTTP requests.\n */\nexport interface RequestMetrics {\n method: string;\n path: string;\n status?: number;\n durationMs: number;\n timestamp: string;\n success: boolean;\n error?: string;\n}\n\n/**\n * Interface for metrics collectors.\n * Implement this to integrate with your monitoring infrastructure (DataDog, Prometheus, etc.).\n *\n * @example\n * ```ts\n * class DataDogMetrics implements MetricsCollector {\n * collect(metrics: RequestMetrics) {\n * dogstatsd.histogram('http.request.duration', metrics.durationMs, {\n * method: metrics.method,\n * status: String(metrics.status)\n * });\n * }\n * }\n * ```\n */\nexport interface MetricsCollector {\n collect(metrics: RequestMetrics): void;\n}\n\n/**\n * No-op metrics collector that discards all metrics.\n */\nexport class NoOpMetricsCollector implements MetricsCollector {\n collect(_metrics: RequestMetrics): void {\n // no-op\n }\n}\n\n/**\n * In-memory metrics collector for testing and development.\n * Stores metrics in memory with configurable retention.\n */\nexport class InMemoryMetricsCollector implements MetricsCollector {\n private metrics: RequestMetrics[] = [];\n private readonly maxEntries: number;\n\n constructor(maxEntries = 1000) {\n this.maxEntries = maxEntries;\n }\n\n collect(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n if (this.metrics.length > this.maxEntries) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get all collected metrics.\n */\n getMetrics(): RequestMetrics[] {\n return [...this.metrics];\n }\n\n /**\n * Get metrics summary statistics.\n */\n getSummary(): {\n total: number;\n successful: number;\n failed: number;\n avgDurationMs: number;\n minDurationMs: number;\n maxDurationMs: number;\n } {\n if (this.metrics.length === 0) {\n return {\n total: 0,\n successful: 0,\n failed: 0,\n avgDurationMs: 0,\n minDurationMs: 0,\n maxDurationMs: 0,\n };\n }\n\n const successful = this.metrics.filter((m) => m.success).length;\n const durations = this.metrics.map((m) => m.durationMs);\n const sum = durations.reduce((a, b) => a + b, 0);\n\n return {\n total: this.metrics.length,\n successful,\n failed: this.metrics.length - successful,\n avgDurationMs: sum / this.metrics.length,\n minDurationMs: Math.min(...durations),\n maxDurationMs: Math.max(...durations),\n };\n }\n\n /**\n * Clear all collected metrics.\n */\n clear(): void {\n this.metrics = [];\n }\n}\n\n/**\n * Console-based metrics collector for debugging.\n */\nexport class ConsoleMetricsCollector implements MetricsCollector {\n collect(metrics: RequestMetrics): void {\n console.log('[METRICS]', JSON.stringify(metrics));\n }\n}\n","import type { AuthProvider } from '../auth';\nimport { NoAuth } from '../auth';\nimport { LoggerUtil, NoOpLogger } from '../logger';\nimport { MetricsCollector, NoOpMetricsCollector } from '../metrics';\nimport {\n ApiError,\n ClientOptions,\n FetchLike,\n HTTPMethod,\n Interceptors,\n RequestOptions,\n RetryStrategy,\n toQueryString,\n} from '../types';\n\n/**\n * HTTP client with built-in retry logic, authentication, and interceptors.\n * Supports multiple base URLs, type-safe requests, and comprehensive error handling.\n *\n * @example\n * ```ts\n * const client = new HttpClient({\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'Content-Type': 'application/json' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * });\n *\n * const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });\n * ```\n */\nexport class HttpClient {\n private fetchImpl: FetchLike;\n private baseUrls: ClientOptions['baseUrls'];\n private headers: Record<string, string>;\n private interceptors: Interceptors;\n private retry: RetryStrategy;\n private timeoutMs?: number;\n private auth: AuthProvider;\n private logger: LoggerUtil;\n private metrics: MetricsCollector;\n\n /**\n * Creates a new HTTP client instance.\n *\n * @param opts - Client configuration options\n * @throws {Error} If no fetch implementation is available\n */\n constructor(opts: ClientOptions) {\n this.fetchImpl = opts.fetch ?? (globalThis.fetch?.bind(globalThis) as FetchLike);\n if (!this.fetchImpl)\n throw new Error('No fetch implementation found. Pass one via options.fetch.');\n\n // Validate baseUrls configuration\n if (!opts.baseUrls || typeof opts.baseUrls !== 'object') {\n throw new Error('baseUrls must be provided and must be an object');\n }\n if (!opts.baseUrls.default) {\n throw new Error('baseUrls must include a \"default\" key');\n }\n\n this.baseUrls = opts.baseUrls;\n this.headers = opts.headers ?? { 'Content-Type': 'application/json' };\n this.interceptors = opts.interceptors ?? {};\n this.retry = opts.retry ?? {\n maxRetries: 2,\n baseDelayMs: 250,\n jitter: 0.2,\n retryMethods: ['GET', 'HEAD'],\n };\n\n // Validate retry configuration\n if (this.retry.maxRetries < 0) {\n throw new Error('retry.maxRetries must be non-negative');\n }\n if (this.retry.baseDelayMs < 0) {\n throw new Error('retry.baseDelayMs must be non-negative');\n }\n if (this.retry.jitter !== undefined && (this.retry.jitter < 0 || this.retry.jitter > 1)) {\n throw new Error('retry.jitter must be between 0 and 1');\n }\n\n this.timeoutMs = opts.timeout?.requestTimeoutMs;\n if (this.timeoutMs !== undefined && this.timeoutMs < 0) {\n throw new Error('timeout.requestTimeoutMs must be non-negative');\n }\n\n this.auth = opts['auth'] ?? new NoAuth();\n this.logger = new LoggerUtil(opts.logger ?? new NoOpLogger());\n this.metrics = opts.metrics ?? new NoOpMetricsCollector();\n }\n\n /**\n * Set or update the authentication provider.\n *\n * @param auth - Authentication provider instance\n * @example\n * ```ts\n * client.setAuth(new BearerTokenAuth(() => getToken()));\n * ```\n */\n setAuth(auth: AuthProvider) {\n this.auth = auth;\n }\n\n private resolveBaseUrl(key?: keyof typeof this.baseUrls) {\n const k: string = (key as string) || 'default';\n const url = this.baseUrls[k];\n if (!url) {\n const availableKeys = Object.keys(this.baseUrls).join(', ');\n throw new Error(`Unknown baseUrl key: \"${k}\". Available keys: ${availableKeys}`);\n }\n return url.replace(/\\/$/, '');\n }\n\n /**\n * Sleep for a specified duration (used for retry backoff).\n * @private\n */\n private sleep(ms: number) {\n return new Promise((res) => setTimeout(res, ms));\n }\n\n /**\n * Execute a function with retry logic and exponential backoff.\n * @private\n */\n private async withRetry<T>(\n fn: () => Promise<T>,\n canRetry: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean\n ): Promise<T> {\n let attempt = 0;\n const { maxRetries, baseDelayMs, jitter = 0.2 } = this.retry;\n while (true) {\n try {\n return await fn();\n } catch (err: unknown) {\n if (attempt >= maxRetries || !canRetry({ attempt, error: err })) throw err;\n const backoff = baseDelayMs * 2 ** attempt;\n const j = 1 + (Math.random() * 2 - 1) * jitter;\n await this.sleep(backoff * j);\n attempt++;\n }\n }\n }\n\n /**\n * Run all registered before-request hooks.\n * @private\n */\n private async runBeforeHooks(url: string, init: RequestInit & { __urlOverride?: string }) {\n for (const h of this.interceptors.beforeRequest ?? []) {\n await h({ url, init });\n }\n }\n\n /**\n * Run all registered after-response hooks.\n * @private\n */\n private async runAfterHooks(req: Request, res: Response, parsed?: unknown) {\n for (const h of this.interceptors.afterResponse ?? []) {\n await h({ request: req, response: res, parsed });\n }\n }\n\n /**\n * Make an HTTP request with automatic retry, authentication, and validation.\n *\n * @param method - HTTP method (GET, POST, PUT, etc.)\n * @param path - Request path (will be appended to base URL)\n * @param body - Request body (will be JSON.stringify'd if Content-Type is json)\n * @param options - Additional request options (headers, query params, etc.)\n * @returns Promise resolving to response data and Response object\n * @throws {ApiError} If request fails or response validation fails\n *\n * @example\n * ```ts\n * const { data, response } = await client.request('GET', '/users', undefined, {\n * query: { page: 1, limit: 10 },\n * headers: { 'X-Custom': 'value' }\n * });\n * ```\n */\n async request<T = unknown>(\n method: keyof typeof HTTPMethod,\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n const startTime = Date.now();\n const base = this.resolveBaseUrl(options?.baseUrlKey);\n let url = `${base}${path}${toQueryString(options?.query)}`;\n\n this.logger.debug('HTTP request initiated', {\n method,\n path,\n baseUrlKey: options?.baseUrlKey,\n hasBody: body !== undefined,\n });\n\n const headers = { ...this.headers, ...(options?.headers ?? {}) };\n\n const controller = new AbortController();\n const signal = options?.signal ?? controller.signal;\n const init: RequestInit & { __urlOverride?: string } = {\n method,\n headers,\n body:\n body != null\n ? headers['Content-Type']?.includes('json')\n ? JSON.stringify(body)\n : String(body)\n : undefined,\n signal,\n };\n\n await this.auth.apply({ url, init, options });\n if (init.__urlOverride) url = init.__urlOverride;\n await this.runBeforeHooks(url, init);\n\n const doFetch = async () => {\n // Apply timeout if configured\n let timeoutId: NodeJS.Timeout | number | undefined;\n if (this.timeoutMs && !options?.signal) {\n timeoutId = setTimeout(() => {\n const timeoutError = new Error('Request timeout');\n timeoutError.name = 'TimeoutError';\n controller.abort(timeoutError);\n }, this.timeoutMs);\n }\n\n try {\n const req = new Request(url, init);\n\n const res = await this.fetchImpl(req);\n if (!res.ok) {\n // Read response safely\n let text = '';\n try {\n text = await res.text();\n } catch (readError) {\n text = `Failed to read response: ${readError instanceof Error ? readError.message : String(readError)}`;\n }\n throw new ApiError(`HTTP ${res.status}: ${res.statusText}`, {\n status: res.status,\n details: text,\n });\n }\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('json') ? await res.json() : await res.text();\n await this.runAfterHooks(new Request(url, init), res, data);\n\n const duration = Date.now() - startTime;\n this.logger.info('HTTP request successful', {\n method,\n url,\n status: res.status,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: res.status,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: true,\n });\n\n return { data: data as T, response: res };\n } catch (error) {\n const duration = Date.now() - startTime;\n this.logger.error('HTTP request failed', error as Error, {\n method,\n url,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: error instanceof ApiError ? error.status : undefined,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n };\n\n const canRetry = ({\n response,\n error,\n }: {\n response?: Response;\n error?: unknown;\n attempt: number;\n }) => {\n // Don't retry timeouts or aborts\n if (error && typeof error === 'object' && 'name' in error) {\n const errorName = (error as { name?: string }).name;\n if (errorName === 'AbortError' || errorName === 'TimeoutError') return false;\n }\n // Retry on network errors or 5xx\n if (error instanceof ApiError && error.status && error.status >= 500) return true;\n if (error && !response) return true; // network error\n return false;\n };\n\n if (!this.retry.retryMethods?.includes(method)) {\n return doFetch();\n }\n return this.withRetry(doFetch, canRetry);\n }\n\n /**\n * Convenience method for GET requests.\n *\n * @example\n * ```ts\n * const { data } = await client.get('/users', { query: { page: 1 } });\n * ```\n */\n async get<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('GET', path, undefined, options);\n }\n\n /**\n * Convenience method for POST requests.\n *\n * @example\n * ```ts\n * const { data } = await client.post('/users', { name: 'John' });\n * ```\n */\n async post<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('POST', path, body, options);\n }\n\n /**\n * Convenience method for PUT requests.\n *\n * @example\n * ```ts\n * const { data } = await client.put('/users/1', { name: 'John Updated' });\n * ```\n */\n async put<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('PUT', path, body, options);\n }\n\n /**\n * Convenience method for PATCH requests.\n *\n * @example\n * ```ts\n * const { data } = await client.patch('/users/1', { name: 'John' });\n * ```\n */\n async patch<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('PATCH', path, body, options);\n }\n\n /**\n * Convenience method for DELETE requests.\n *\n * @example\n * ```ts\n * const { data } = await client.delete('/users/1');\n * ```\n */\n async delete<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('DELETE', path, undefined, options);\n }\n}\n","import { z } from 'zod';\nimport { HttpClient } from '../http/http-client';\nimport { HTTPMethod } from '../types';\nimport { parseOrThrow } from '../validation';\n\n/**\n * Request configuration for endpoint calls.\n * Wrapper object containing all request parameters.\n *\n * @template ReqSchema - Zod schema for request validation\n * @template PathParams - Type for path parameters\n * @template QueryParams - Type for query parameters\n */\nexport type EndpointCallConfig<\n ReqSchema extends z.ZodType,\n PathParams = never,\n QueryParams = never,\n> = {\n /** Request body data (for POST, PUT, PATCH, etc.) or request args */\n data?: z.infer<ReqSchema>;\n /** Path parameters for dynamic path construction */\n pathParams?: PathParams;\n /** Query string parameters */\n query?: QueryParams;\n /** Request headers */\n headers?: Record<string, string>;\n /** Override base URL for this call */\n baseUrlKey?: string;\n /** Abort controller signal for cancellation */\n signal?: globalThis.AbortSignal;\n};\n\n/**\n * Generic, strongly-typed endpoint with Zod schemas for request and response validation.\n * Extend this class to create type-safe API endpoints.\n *\n * @template ReqSchema - Zod schema for request validation\n * @template ResSchema - Zod schema for response validation\n * @template PathParams - Type for path parameters (optional)\n * @template QueryParams - Type for query parameters (optional)\n *\n * @example\n * ```ts\n * const UserSchema = z.object({ id: z.number(), name: z.string() });\n * const CreateUserSchema = z.object({ name: z.string() });\n *\n * type UserPathParams = { id: string };\n * type UserQueryParams = { include?: string; limit?: number };\n *\n * class GetUser extends BaseEndpoint<\n * typeof CreateUserSchema,\n * typeof UserSchema,\n * UserPathParams,\n * UserQueryParams\n * > {\n * protected method = 'GET' as const;\n * protected path = (params: UserPathParams) => `/users/${params.id}`;\n *\n * constructor(client: HttpClient) {\n * super(client, {\n * requestSchema: CreateUserSchema,\n * responseSchema: UserSchema\n * });\n * }\n * }\n *\n * // Usage:\n * const user = await endpoint.call({\n * pathParams: { id: '123' },\n * query: { include: 'posts', limit: 10 }\n * });\n * ```\n */\nexport abstract class BaseEndpoint<\n ReqSchema extends z.ZodType,\n ResSchema extends z.ZodType,\n PathParams = never,\n QueryParams = never,\n> {\n /** HTTP method for this endpoint */\n protected abstract readonly method: keyof typeof HTTPMethod;\n /** URL path (can be a function for dynamic paths) */\n protected abstract readonly path: string | ((params: PathParams) => string);\n /** Additional options for the request */\n protected readonly options?: {\n /** Override base URL for this call */\n baseUrlKey?: string;\n };\n /** Optional request schema for validation */\n protected readonly requestSchema?: ReqSchema;\n /** Response schema for validation */\n protected readonly responseSchema: ResSchema;\n\n /**\n * @param client - HttpClient instance\n * @param cfg - Configuration with request and response schemas\n */\n constructor(\n protected client: HttpClient,\n cfg: {\n requestSchema?: ReqSchema;\n responseSchema: ResSchema;\n }\n ) {\n this.requestSchema = cfg.requestSchema;\n this.responseSchema = cfg.responseSchema;\n }\n\n /**\n * Call the endpoint with strong typing derived from schemas.\n * Validates request data before sending and response data after receiving.\n *\n * @param config - Request configuration object containing all parameters\n * @returns Promise resolving to validated response data (typed by ResSchema)\n * @throws {ZodError} If request validation fails\n * @throws {ApiError} If response validation fails or request fails\n *\n * @example\n * ```ts\n * const endpoint = new GetUser(client);\n * const user = await endpoint.call({\n * pathParams: { id: '123' },\n * query: { include: 'posts' }\n * });\n * // With additional options:\n * const user = await endpoint.call({\n * data: { name: 'John' },\n * pathParams: { id: '123' },\n * headers: { 'X-Custom': 'value' },\n * query: { include: 'posts' }\n * });\n * ```\n */\n async call(\n config: EndpointCallConfig<ReqSchema, PathParams, QueryParams> = {} as EndpointCallConfig<\n ReqSchema,\n PathParams,\n QueryParams\n >\n ): Promise<z.infer<ResSchema>> {\n const { data, query, headers, baseUrlKey, signal, pathParams } = config;\n\n // Validate request body/params before sending (when schema provided)\n if (this.requestSchema && data !== undefined) {\n const parsed = this.requestSchema.safeParse(data);\n if (!parsed.success) throw parsed.error;\n }\n\n // Build path - use pathParams if provided, otherwise use data for backwards compatibility\n const pathArgs = (pathParams ?? data) as PathParams;\n const path = typeof this.path === 'function' ? this.path(pathArgs) : this.path;\n\n // For GET/HEAD methods, don't send body\n const shouldHaveBody = this.method !== 'GET' && this.method !== 'HEAD';\n const body = shouldHaveBody ? data : undefined;\n\n // Convert query params to the format expected by http-client\n const queryForRequest = query as\n | Record<string, string | number | boolean | undefined>\n | URLSearchParams\n | undefined;\n\n const { data: responseData } = await this.client.request(this.method, path, body, {\n query: queryForRequest,\n headers,\n baseUrlKey: baseUrlKey ?? this.options?.baseUrlKey,\n signal,\n });\n\n return parseOrThrow<z.infer<ResSchema>>(this.responseSchema, responseData);\n }\n}\n","import { z } from 'zod';\n\n/**\n * Common ID type that supports strings, numbers, or UUIDs.\n * Use this for entity identifiers in your schemas.\n *\n * @example\n * ```ts\n * const UserSchema = z.object({ id: Id, name: z.string() });\n * ```\n */\nexport const Id = z.union([\n z.string().min(1),\n z.number(),\n z.uuid({\n version: 'v4',\n }),\n]);\nexport type IdType = z.infer<typeof Id>;\n\n/**\n * Common timestamp fields for entities.\n * Use this for database models with creation/update tracking.\n *\n * @example\n * ```ts\n * const UserSchema = z.object({\n * id: Id,\n * name: z.string(),\n * ...Timestamps.shape\n * });\n * ```\n */\nexport const Timestamps = z.object({\n createdAt: z.iso.datetime(),\n updatedAt: z.iso.datetime(),\n});\n\n/**\n * Metadata information typically included in API responses.\n * Contains request tracking and debugging information.\n */\nexport const Meta = z.object({\n requestId: z.string().optional(),\n timestamp: z.iso.datetime().optional(),\n traceId: z.string().optional(),\n});\n\n/**\n * Detailed error information for a specific field or path.\n */\nexport const ErrorDetail = z.object({\n path: z.string().optional(),\n message: z.string(),\n});\n\n/**\n * Standard API error response schema.\n * Use this for consistent error handling across your API.\n */\nexport const ApiErrorSchema = z.object({\n code: z.string(),\n message: z.string(),\n details: z.array(ErrorDetail).optional(),\n});\n\n/**\n * Generic envelope wrapper for API responses.\n * Provides consistent structure with success flag, data, error, and metadata.\n *\n * @param inner - Zod schema for the response data\n * @returns Envelope schema wrapping the inner schema\n *\n * @example\n * ```ts\n * const UserResponseSchema = Envelope(z.object({ id: Id, name: z.string() }));\n *\n * // Response structure:\n * // {\n * // success: true,\n * // data: { id: 1, name: 'John' },\n * // meta: { requestId: '...' }\n * // }\n * ```\n */\nexport const Envelope = <T extends z.ZodType>(inner: T) =>\n z.object({\n success: z.boolean(),\n data: inner.optional().nullable(),\n error: ApiErrorSchema.optional(),\n meta: Meta.optional(),\n });\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ const HTTPMethod = {
|
|
|
13
13
|
/**
|
|
14
14
|
* Custom error class for API-related errors.
|
|
15
15
|
* Includes HTTP status codes, response details, and validation errors.
|
|
16
|
-
*
|
|
16
|
+
*
|
|
17
17
|
* @example
|
|
18
18
|
* ```ts
|
|
19
19
|
* throw new ApiError('Invalid request', {
|
|
@@ -79,10 +79,10 @@ const PaginationSchema = z.object({
|
|
|
79
79
|
/**
|
|
80
80
|
* Converts query parameters to a URL query string.
|
|
81
81
|
* Filters out undefined values automatically.
|
|
82
|
-
*
|
|
82
|
+
*
|
|
83
83
|
* @param q - Query parameters as URLSearchParams or object
|
|
84
84
|
* @returns Query string with leading '?' or empty string
|
|
85
|
-
*
|
|
85
|
+
*
|
|
86
86
|
* @example
|
|
87
87
|
* ```ts
|
|
88
88
|
* toQueryString({ page: 1, filter: 'active' }) // "?page=1&filter=active"
|
|
@@ -115,12 +115,12 @@ var NoAuth = class {
|
|
|
115
115
|
/**
|
|
116
116
|
* API Key authentication provider.
|
|
117
117
|
* Supports both header-based and query parameter-based authentication.
|
|
118
|
-
*
|
|
118
|
+
*
|
|
119
119
|
* @example
|
|
120
120
|
* ```ts
|
|
121
121
|
* // Header-based
|
|
122
122
|
* const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });
|
|
123
|
-
*
|
|
123
|
+
*
|
|
124
124
|
* // Query parameter-based
|
|
125
125
|
* const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });
|
|
126
126
|
* ```
|
|
@@ -146,12 +146,12 @@ var ApiKeyAuth = class {
|
|
|
146
146
|
/**
|
|
147
147
|
* Bearer token authentication provider.
|
|
148
148
|
* Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).
|
|
149
|
-
*
|
|
149
|
+
*
|
|
150
150
|
* @example
|
|
151
151
|
* ```ts
|
|
152
152
|
* // Static token
|
|
153
153
|
* const auth = new BearerTokenAuth(() => 'my-token');
|
|
154
|
-
*
|
|
154
|
+
*
|
|
155
155
|
* // Dynamic token with refresh
|
|
156
156
|
* const auth = new BearerTokenAuth(async () => {
|
|
157
157
|
* return await refreshAccessToken();
|
|
@@ -177,11 +177,11 @@ var BearerTokenAuth = class {
|
|
|
177
177
|
/**
|
|
178
178
|
* Safely parse data with a Zod schema without throwing.
|
|
179
179
|
* Returns a result object with success status and data or error.
|
|
180
|
-
*
|
|
180
|
+
*
|
|
181
181
|
* @param schema - Zod schema to validate against
|
|
182
182
|
* @param data - Data to validate
|
|
183
183
|
* @returns Result object with success flag and data/error
|
|
184
|
-
*
|
|
184
|
+
*
|
|
185
185
|
* @example
|
|
186
186
|
* ```ts
|
|
187
187
|
* const result = safeParse(UserSchema, userData);
|
|
@@ -206,12 +206,12 @@ function safeParse(schema, data) {
|
|
|
206
206
|
/**
|
|
207
207
|
* Parse data with a Zod schema, throwing an ApiError on validation failure.
|
|
208
208
|
* Use this when you want to fail fast on invalid data.
|
|
209
|
-
*
|
|
209
|
+
*
|
|
210
210
|
* @param schema - Zod schema to validate against
|
|
211
211
|
* @param data - Data to validate
|
|
212
212
|
* @returns Validated and typed data
|
|
213
213
|
* @throws {ApiError} If validation fails
|
|
214
|
-
*
|
|
214
|
+
*
|
|
215
215
|
* @example
|
|
216
216
|
* ```ts
|
|
217
217
|
* try {
|
|
@@ -404,7 +404,7 @@ var ConsoleMetricsCollector = class {
|
|
|
404
404
|
/**
|
|
405
405
|
* HTTP client with built-in retry logic, authentication, and interceptors.
|
|
406
406
|
* Supports multiple base URLs, type-safe requests, and comprehensive error handling.
|
|
407
|
-
*
|
|
407
|
+
*
|
|
408
408
|
* @example
|
|
409
409
|
* ```ts
|
|
410
410
|
* const client = new HttpClient({
|
|
@@ -413,7 +413,7 @@ var ConsoleMetricsCollector = class {
|
|
|
413
413
|
* retry: { maxRetries: 3, baseDelayMs: 1000 },
|
|
414
414
|
* timeout: { requestTimeoutMs: 30000 }
|
|
415
415
|
* });
|
|
416
|
-
*
|
|
416
|
+
*
|
|
417
417
|
* const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });
|
|
418
418
|
* ```
|
|
419
419
|
*/
|
|
@@ -429,7 +429,7 @@ var HttpClient = class {
|
|
|
429
429
|
metrics;
|
|
430
430
|
/**
|
|
431
431
|
* Creates a new HTTP client instance.
|
|
432
|
-
*
|
|
432
|
+
*
|
|
433
433
|
* @param opts - Client configuration options
|
|
434
434
|
* @throws {Error} If no fetch implementation is available
|
|
435
435
|
*/
|
|
@@ -458,7 +458,7 @@ var HttpClient = class {
|
|
|
458
458
|
}
|
|
459
459
|
/**
|
|
460
460
|
* Set or update the authentication provider.
|
|
461
|
-
*
|
|
461
|
+
*
|
|
462
462
|
* @param auth - Authentication provider instance
|
|
463
463
|
* @example
|
|
464
464
|
* ```ts
|
|
@@ -527,14 +527,14 @@ var HttpClient = class {
|
|
|
527
527
|
}
|
|
528
528
|
/**
|
|
529
529
|
* Make an HTTP request with automatic retry, authentication, and validation.
|
|
530
|
-
*
|
|
530
|
+
*
|
|
531
531
|
* @param method - HTTP method (GET, POST, PUT, etc.)
|
|
532
532
|
* @param path - Request path (will be appended to base URL)
|
|
533
533
|
* @param body - Request body (will be JSON.stringify'd if Content-Type is json)
|
|
534
534
|
* @param options - Additional request options (headers, query params, etc.)
|
|
535
535
|
* @returns Promise resolving to response data and Response object
|
|
536
536
|
* @throws {ApiError} If request fails or response validation fails
|
|
537
|
-
*
|
|
537
|
+
*
|
|
538
538
|
* @example
|
|
539
539
|
* ```ts
|
|
540
540
|
* const { data, response } = await client.request('GET', '/users', undefined, {
|
|
@@ -649,7 +649,7 @@ var HttpClient = class {
|
|
|
649
649
|
}
|
|
650
650
|
/**
|
|
651
651
|
* Convenience method for GET requests.
|
|
652
|
-
*
|
|
652
|
+
*
|
|
653
653
|
* @example
|
|
654
654
|
* ```ts
|
|
655
655
|
* const { data } = await client.get('/users', { query: { page: 1 } });
|
|
@@ -660,7 +660,7 @@ var HttpClient = class {
|
|
|
660
660
|
}
|
|
661
661
|
/**
|
|
662
662
|
* Convenience method for POST requests.
|
|
663
|
-
*
|
|
663
|
+
*
|
|
664
664
|
* @example
|
|
665
665
|
* ```ts
|
|
666
666
|
* const { data } = await client.post('/users', { name: 'John' });
|
|
@@ -671,7 +671,7 @@ var HttpClient = class {
|
|
|
671
671
|
}
|
|
672
672
|
/**
|
|
673
673
|
* Convenience method for PUT requests.
|
|
674
|
-
*
|
|
674
|
+
*
|
|
675
675
|
* @example
|
|
676
676
|
* ```ts
|
|
677
677
|
* const { data } = await client.put('/users/1', { name: 'John Updated' });
|
|
@@ -682,7 +682,7 @@ var HttpClient = class {
|
|
|
682
682
|
}
|
|
683
683
|
/**
|
|
684
684
|
* Convenience method for PATCH requests.
|
|
685
|
-
*
|
|
685
|
+
*
|
|
686
686
|
* @example
|
|
687
687
|
* ```ts
|
|
688
688
|
* const { data } = await client.patch('/users/1', { name: 'John' });
|
|
@@ -693,7 +693,7 @@ var HttpClient = class {
|
|
|
693
693
|
}
|
|
694
694
|
/**
|
|
695
695
|
* Convenience method for DELETE requests.
|
|
696
|
-
*
|
|
696
|
+
*
|
|
697
697
|
* @example
|
|
698
698
|
* ```ts
|
|
699
699
|
* const { data } = await client.delete('/users/1');
|
|
@@ -709,26 +709,42 @@ var HttpClient = class {
|
|
|
709
709
|
/**
|
|
710
710
|
* Generic, strongly-typed endpoint with Zod schemas for request and response validation.
|
|
711
711
|
* Extend this class to create type-safe API endpoints.
|
|
712
|
-
*
|
|
712
|
+
*
|
|
713
713
|
* @template ReqSchema - Zod schema for request validation
|
|
714
714
|
* @template ResSchema - Zod schema for response validation
|
|
715
|
-
*
|
|
715
|
+
* @template PathParams - Type for path parameters (optional)
|
|
716
|
+
* @template QueryParams - Type for query parameters (optional)
|
|
717
|
+
*
|
|
716
718
|
* @example
|
|
717
719
|
* ```ts
|
|
718
720
|
* const UserSchema = z.object({ id: z.number(), name: z.string() });
|
|
719
721
|
* const CreateUserSchema = z.object({ name: z.string() });
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
+
*
|
|
723
|
+
* type UserPathParams = { id: string };
|
|
724
|
+
* type UserQueryParams = { include?: string; limit?: number };
|
|
725
|
+
*
|
|
726
|
+
* class GetUser extends BaseEndpoint<
|
|
727
|
+
* typeof CreateUserSchema,
|
|
728
|
+
* typeof UserSchema,
|
|
729
|
+
* UserPathParams,
|
|
730
|
+
* UserQueryParams
|
|
731
|
+
* > {
|
|
722
732
|
* protected method = 'GET' as const;
|
|
723
|
-
* protected path = (
|
|
724
|
-
*
|
|
733
|
+
* protected path = (params: UserPathParams) => `/users/${params.id}`;
|
|
734
|
+
*
|
|
725
735
|
* constructor(client: HttpClient) {
|
|
726
|
-
* super(client, {
|
|
736
|
+
* super(client, {
|
|
727
737
|
* requestSchema: CreateUserSchema,
|
|
728
|
-
* responseSchema: UserSchema
|
|
738
|
+
* responseSchema: UserSchema
|
|
729
739
|
* });
|
|
730
740
|
* }
|
|
731
741
|
* }
|
|
742
|
+
*
|
|
743
|
+
* // Usage:
|
|
744
|
+
* const user = await endpoint.call({
|
|
745
|
+
* pathParams: { id: '123' },
|
|
746
|
+
* query: { include: 'posts', limit: 10 }
|
|
747
|
+
* });
|
|
732
748
|
* ```
|
|
733
749
|
*/
|
|
734
750
|
var BaseEndpoint = class {
|
|
@@ -750,19 +766,23 @@ var BaseEndpoint = class {
|
|
|
750
766
|
/**
|
|
751
767
|
* Call the endpoint with strong typing derived from schemas.
|
|
752
768
|
* Validates request data before sending and response data after receiving.
|
|
753
|
-
*
|
|
769
|
+
*
|
|
754
770
|
* @param config - Request configuration object containing all parameters
|
|
755
771
|
* @returns Promise resolving to validated response data (typed by ResSchema)
|
|
756
772
|
* @throws {ZodError} If request validation fails
|
|
757
773
|
* @throws {ApiError} If response validation fails or request fails
|
|
758
|
-
*
|
|
774
|
+
*
|
|
759
775
|
* @example
|
|
760
776
|
* ```ts
|
|
761
777
|
* const endpoint = new GetUser(client);
|
|
762
|
-
* const user = await endpoint.call({
|
|
778
|
+
* const user = await endpoint.call({
|
|
779
|
+
* pathParams: { id: '123' },
|
|
780
|
+
* query: { include: 'posts' }
|
|
781
|
+
* });
|
|
763
782
|
* // With additional options:
|
|
764
|
-
* const user = await endpoint.call({
|
|
765
|
-
* data: {
|
|
783
|
+
* const user = await endpoint.call({
|
|
784
|
+
* data: { name: 'John' },
|
|
785
|
+
* pathParams: { id: '123' },
|
|
766
786
|
* headers: { 'X-Custom': 'value' },
|
|
767
787
|
* query: { include: 'posts' }
|
|
768
788
|
* });
|
|
@@ -777,8 +797,9 @@ var BaseEndpoint = class {
|
|
|
777
797
|
const pathArgs = pathParams ?? data;
|
|
778
798
|
const path = typeof this.path === "function" ? this.path(pathArgs) : this.path;
|
|
779
799
|
const body = this.method !== "GET" && this.method !== "HEAD" ? data : void 0;
|
|
800
|
+
const queryForRequest = query;
|
|
780
801
|
const { data: responseData } = await this.client.request(this.method, path, body, {
|
|
781
|
-
query,
|
|
802
|
+
query: queryForRequest,
|
|
782
803
|
headers,
|
|
783
804
|
baseUrlKey: baseUrlKey ?? this.options?.baseUrlKey,
|
|
784
805
|
signal
|
|
@@ -792,7 +813,7 @@ var BaseEndpoint = class {
|
|
|
792
813
|
/**
|
|
793
814
|
* Common ID type that supports strings, numbers, or UUIDs.
|
|
794
815
|
* Use this for entity identifiers in your schemas.
|
|
795
|
-
*
|
|
816
|
+
*
|
|
796
817
|
* @example
|
|
797
818
|
* ```ts
|
|
798
819
|
* const UserSchema = z.object({ id: Id, name: z.string() });
|
|
@@ -806,7 +827,7 @@ const Id = z.union([
|
|
|
806
827
|
/**
|
|
807
828
|
* Common timestamp fields for entities.
|
|
808
829
|
* Use this for database models with creation/update tracking.
|
|
809
|
-
*
|
|
830
|
+
*
|
|
810
831
|
* @example
|
|
811
832
|
* ```ts
|
|
812
833
|
* const UserSchema = z.object({
|
|
@@ -848,14 +869,14 @@ const ApiErrorSchema = z.object({
|
|
|
848
869
|
/**
|
|
849
870
|
* Generic envelope wrapper for API responses.
|
|
850
871
|
* Provides consistent structure with success flag, data, error, and metadata.
|
|
851
|
-
*
|
|
872
|
+
*
|
|
852
873
|
* @param inner - Zod schema for the response data
|
|
853
874
|
* @returns Envelope schema wrapping the inner schema
|
|
854
|
-
*
|
|
875
|
+
*
|
|
855
876
|
* @example
|
|
856
877
|
* ```ts
|
|
857
878
|
* const UserResponseSchema = Envelope(z.object({ id: Id, name: z.string() }));
|
|
858
|
-
*
|
|
879
|
+
*
|
|
859
880
|
* // Response structure:
|
|
860
881
|
* // {
|
|
861
882
|
* // success: true,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["s","opts: { header?: string; query?: string; value: string }","getToken: () => Promise<string> | string","minLevel: LogLevel","logger: Logger","k: string","err: unknown","init: RequestInit & { __urlOverride?: string }","timeoutId: NodeJS.Timeout | number | undefined","client: HttpClient"],"sources":["../lib/types.ts","../lib/auth.ts","../lib/validation.ts","../lib/logger.ts","../lib/metrics.ts","../lib/http/http-client.ts","../lib/endpoint/base-endpoint.ts","../lib/schemas/common.ts"],"sourcesContent":["import { z, ZodError } from 'zod';\nimport { AuthProvider } from './auth';\nimport { Logger } from './logger';\nimport { MetricsCollector } from './metrics';\n\nexport type Dictionary<T = unknown> = Record<string, T>;\n\nexport type FetchLike = (input: string | Request | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Map of base URLs for different services.\n * The 'default' key is required and used when no specific key is provided.\n * \n * @example\n * ```ts\n * {\n * default: 'https://api.example.com',\n * auth: 'https://auth.example.com',\n * cdn: 'https://cdn.example.com'\n * }\n * ```\n */\nexport type BaseUrlMap = {\n default: string;\n [service: string]: string;\n};\n\n/**\n * Configuration for retry behavior on failed requests.\n * Implements exponential backoff with optional jitter.\n * \n * @example\n * ```ts\n * {\n * maxRetries: 3,\n * baseDelayMs: 1000,\n * jitter: 0.2,\n * retryMethods: ['GET', 'HEAD', 'PUT']\n * }\n * ```\n */\nexport type RetryStrategy = {\n /** Maximum number of retry attempts */\n maxRetries: number;\n /** Base delay in milliseconds (will be exponentially increased) */\n baseDelayMs: number;\n /** Jitter factor 0..1 to randomize delays and prevent thundering herd */\n jitter?: number;\n /** HTTP methods eligible for retry */\n retryMethods?: (keyof typeof HTTPMethod)[];\n /** Custom function to determine if a request should be retried */\n shouldRetry?: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean;\n};\n\nexport const HTTPMethod = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n PATCH: 'PATCH',\n DELETE: 'DELETE',\n HEAD: 'HEAD',\n OPTIONS: 'OPTIONS',\n} as const;\n\nexport type HttpMethod = keyof typeof HTTPMethod;\n\n/**\n * Hook called after a response is received and parsed.\n * Useful for logging, metrics, or global error handling.\n */\nexport type AfterResponseHook = (ctx: {\n request: Request;\n response: Response;\n parsed?: unknown;\n}) => Promise<void> | void;\n\n/**\n * Hook called before a request is sent.\n * Useful for logging, adding headers, or modifying the request.\n */\nexport type BeforeRequestHook = (ctx: { url: string; init: RequestInit }) => Promise<void> | void;\n\nexport interface Interceptors {\n /** Hooks executed before each request is sent */\n beforeRequest?: BeforeRequestHook[];\n /** Hooks executed after each response is received */\n afterResponse?: AfterResponseHook[];\n}\n\nexport interface TimeoutOptions {\n /** Request timeout in milliseconds */\n requestTimeoutMs?: number;\n}\n\n/**\n * Configuration options for the HTTP client.\n * \n * @example\n * ```ts\n * const options: ClientOptions = {\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'X-API-Version': '1.0' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * }\n * ```\n */\nexport interface ClientOptions {\n /** Map of base URLs for different services */\n baseUrls: BaseUrlMap;\n /** Custom fetch implementation (defaults to globalThis.fetch) */\n fetch?: FetchLike;\n /** Default headers applied to all requests */\n headers?: Record<string, string>;\n /** Retry strategy configuration */\n retry?: RetryStrategy;\n /** Request/response interceptors */\n interceptors?: Interceptors;\n /** Timeout configuration */\n timeout?: TimeoutOptions;\n /** Authentication provider */\n auth?: AuthProvider;\n /** Logger instance */\n logger?: Logger;\n /** Metrics collector */\n metrics?: MetricsCollector;\n}\n\nexport type SafeParseResult<T> = { success: true; data: T } | { success: false; error: ZodError };\n\n/**\n * Custom error class for API-related errors.\n * Includes HTTP status codes, response details, and validation errors.\n * \n * @example\n * ```ts\n * throw new ApiError('Invalid request', {\n * status: 400,\n * details: { field: 'email', message: 'Invalid format' }\n * });\n * ```\n */\nexport class ApiError extends Error {\n public status?: number;\n public details?: unknown;\n public zodError?: ZodError;\n\n constructor(\n message: string,\n options?: { status?: number; cause?: unknown; details?: unknown; zodError?: ZodError }\n ) {\n super(message);\n this.name = 'ApiError';\n this.status = options?.status;\n this.details = options?.details;\n this.cause = options?.cause as any;\n this.zodError = options?.zodError;\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ApiError);\n }\n }\n\n /**\n * Check if this is a validation error (has zodError)\n */\n isValidationError(): boolean {\n return !!this.zodError;\n }\n\n /**\n * Check if this is a client error (4xx status)\n */\n isClientError(): boolean {\n return !!this.status && this.status >= 400 && this.status < 500;\n }\n\n /**\n * Check if this is a server error (5xx status)\n */\n isServerError(): boolean {\n return !!this.status && this.status >= 500;\n }\n\n /**\n * Get a formatted error message with all available details\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n details: this.details,\n zodError: this.zodError?.issues,\n stack: this.stack,\n };\n }\n}\n\nexport type Paginated<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n};\n\n/**\n * Schema for paginated responses\n */\nexport const PaginationSchema = z.object({\n items: z.array(z.unknown()),\n total: z.number().int().nonnegative(),\n page: z.number().int().nonnegative(),\n pageSize: z.number().int().positive(),\n});\n\n/**\n * Options that can be passed to individual requests to override defaults.\n * \n * @example\n * ```ts\n * await endpoint.call(data, {\n * baseUrlKey: 'v2',\n * headers: { 'X-Custom': 'value' },\n * query: { filter: 'active' }\n * });\n * ```\n */\nexport type RequestOptions = {\n /** Override base URL for a single call */\n baseUrlKey?: keyof BaseUrlMap;\n /** Additional headers for this call only */\n headers?: Record<string, string>;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n /** Custom query params */\n query?: URLSearchParams | Record<string, string | number | boolean | undefined>;\n};\n\n/**\n * Converts query parameters to a URL query string.\n * Filters out undefined values automatically.\n * \n * @param q - Query parameters as URLSearchParams or object\n * @returns Query string with leading '?' or empty string\n * \n * @example\n * ```ts\n * toQueryString({ page: 1, filter: 'active' }) // \"?page=1&filter=active\"\n * toQueryString({ optional: undefined }) // \"\"\n * ```\n */\nexport function toQueryString(q?: RequestOptions['query']): string {\n if (!q) return '';\n if (q instanceof URLSearchParams) {\n const s = q.toString();\n return s ? `?${s}` : '';\n }\n const params = new URLSearchParams();\n Object.entries(q).forEach(([k, v]) => {\n if (v !== undefined) {\n params.append(k, String(v));\n }\n });\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n","import type { RequestOptions as ReqOpts } from './types';\n\n/**\n * Extended RequestInit with URL override capability for query-based auth.\n */\nexport interface AuthContext {\n url: string;\n init: RequestInit & { __urlOverride?: string };\n options?: ReqOpts;\n}\n\n/**\n * Interface for authentication providers.\n * Implement this to create custom authentication strategies.\n * \n * @example\n * ```ts\n * class CustomAuth implements AuthProvider {\n * async apply({ init }) {\n * init.headers = { ...init.headers, 'X-Custom-Auth': 'token' };\n * }\n * }\n * ```\n */\nexport interface AuthProvider {\n /**\n * Apply authentication to the outgoing request.\n * Called after SDK headers are assembled, but before request is sent.\n * \n * @param req - Request context including URL, init, and options\n */\n apply(req: AuthContext): Promise<void> | void;\n}\n\n/**\n * No-op authentication provider (no authentication applied).\n * Use this when you don't need authentication.\n */\nexport class NoAuth implements AuthProvider {\n async apply() {\n /* no-op */\n }\n}\n\n/**\n * API Key authentication provider.\n * Supports both header-based and query parameter-based authentication.\n * \n * @example\n * ```ts\n * // Header-based\n * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });\n * \n * // Query parameter-based\n * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });\n * ```\n */\nexport class ApiKeyAuth implements AuthProvider {\n constructor(private opts: { header?: string; query?: string; value: string }) {\n if (!opts.header && !opts.query) {\n throw new Error('ApiKeyAuth requires either \"header\" or \"query\" option');\n }\n if (opts.header && opts.query) {\n throw new Error('ApiKeyAuth cannot use both \"header\" and \"query\" options');\n }\n }\n apply({ url, init }: AuthContext) {\n if (this.opts.header) {\n init.headers = { ...(init.headers as any), [this.opts.header]: this.opts.value };\n } else if (this.opts.query) {\n const u = new URL(url);\n u.searchParams.set(this.opts.query, this.opts.value);\n init.__urlOverride = u.toString();\n }\n }\n}\n\n/**\n * Bearer token authentication provider.\n * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).\n * \n * @example\n * ```ts\n * // Static token\n * const auth = new BearerTokenAuth(() => 'my-token');\n * \n * // Dynamic token with refresh\n * const auth = new BearerTokenAuth(async () => {\n * return await refreshAccessToken();\n * });\n * ```\n */\nexport class BearerTokenAuth implements AuthProvider {\n constructor(private getToken: () => Promise<string> | string) { }\n async apply({ init }: AuthContext) {\n const token = await this.getToken();\n if (!token) {\n throw new Error('BearerTokenAuth: token is empty or undefined');\n }\n init.headers = { ...(init.headers as any), Authorization: `Bearer ${token}` };\n }\n}\n","import { z } from 'zod';\nimport { ApiError, SafeParseResult } from './types';\n\n/**\n * Safely parse data with a Zod schema without throwing.\n * Returns a result object with success status and data or error.\n * \n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Result object with success flag and data/error\n * \n * @example\n * ```ts\n * const result = safeParse(UserSchema, userData);\n * if (result.success) {\n * console.log(result.data);\n * } else {\n * console.error(result.error);\n * }\n * ```\n */\nexport function safeParse<T>(schema: z.ZodType, data: unknown): SafeParseResult<T> {\n const res = schema.safeParse(data);\n if (res.success) return { success: true, data: res.data as T };\n return { success: false, error: res.error };\n}\n\n/**\n * Parse data with a Zod schema, throwing an ApiError on validation failure.\n * Use this when you want to fail fast on invalid data.\n * \n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {ApiError} If validation fails\n * \n * @example\n * ```ts\n * try {\n * const user = parseOrThrow(UserSchema, userData);\n * console.log(user);\n * } catch (error) {\n * if (error instanceof ApiError && error.zodError) {\n * console.error('Validation failed:', error.zodError.issues);\n * }\n * }\n * ```\n */\nexport function parseOrThrow<T>(schema: z.ZodType, data: unknown): T {\n const res = schema.safeParse(data);\n if (!res.success) {\n throw new ApiError('Response validation failed', { zodError: res.error });\n }\n return res.data as T;\n}\n","/**\n * Log levels for structured logging.\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Structured log entry with metadata.\n */\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: string;\n context?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Logger interface for custom logger implementations.\n * Implement this to integrate with your logging infrastructure.\n * \n * @example\n * ```ts\n * class ConsoleLogger implements Logger {\n * log(entry: LogEntry) {\n * console.log(JSON.stringify(entry));\n * }\n * }\n * ```\n */\nexport interface Logger {\n log(entry: LogEntry): void;\n}\n\n/**\n * Default console logger implementation.\n * Formats log entries as JSON for easy parsing.\n */\nexport class ConsoleLogger implements Logger {\n constructor(private minLevel: LogLevel = LogLevel.INFO) { }\n\n log(entry: LogEntry): void {\n const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];\n const entryLevelIndex = levels.indexOf(entry.level);\n const minLevelIndex = levels.indexOf(this.minLevel);\n\n if (entryLevelIndex < minLevelIndex) return;\n\n const output = {\n ...entry,\n error: entry.error\n ? {\n message: entry.error.message,\n stack: entry.error.stack,\n name: entry.error.name,\n }\n : undefined,\n };\n\n switch (entry.level) {\n case LogLevel.DEBUG:\n console.debug(JSON.stringify(output));\n break;\n case LogLevel.INFO:\n console.info(JSON.stringify(output));\n break;\n case LogLevel.WARN:\n console.warn(JSON.stringify(output));\n break;\n case LogLevel.ERROR:\n console.error(JSON.stringify(output));\n break;\n }\n }\n}\n\n/**\n * No-op logger that discards all log entries.\n * Use this in production if you don't want any logging.\n */\nexport class NoOpLogger implements Logger {\n log(_entry: LogEntry): void {\n // no-op\n }\n}\n\n/**\n * Utility class for creating structured log entries.\n */\nexport class LoggerUtil {\n constructor(private logger: Logger) { }\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.DEBUG,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.INFO,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.WARN,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n error(message: string, error?: Error, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.ERROR,\n message,\n timestamp: new Date().toISOString(),\n context,\n error,\n });\n }\n}\n","/**\n * Metrics data for HTTP requests.\n */\nexport interface RequestMetrics {\n method: string;\n path: string;\n status?: number;\n durationMs: number;\n timestamp: string;\n success: boolean;\n error?: string;\n}\n\n/**\n * Interface for metrics collectors.\n * Implement this to integrate with your monitoring infrastructure (DataDog, Prometheus, etc.).\n * \n * @example\n * ```ts\n * class DataDogMetrics implements MetricsCollector {\n * collect(metrics: RequestMetrics) {\n * dogstatsd.histogram('http.request.duration', metrics.durationMs, {\n * method: metrics.method,\n * status: String(metrics.status)\n * });\n * }\n * }\n * ```\n */\nexport interface MetricsCollector {\n collect(metrics: RequestMetrics): void;\n}\n\n/**\n * No-op metrics collector that discards all metrics.\n */\nexport class NoOpMetricsCollector implements MetricsCollector {\n collect(_metrics: RequestMetrics): void {\n // no-op\n }\n}\n\n/**\n * In-memory metrics collector for testing and development.\n * Stores metrics in memory with configurable retention.\n */\nexport class InMemoryMetricsCollector implements MetricsCollector {\n private metrics: RequestMetrics[] = [];\n private readonly maxEntries: number;\n\n constructor(maxEntries = 1000) {\n this.maxEntries = maxEntries;\n }\n\n collect(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n if (this.metrics.length > this.maxEntries) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get all collected metrics.\n */\n getMetrics(): RequestMetrics[] {\n return [...this.metrics];\n }\n\n /**\n * Get metrics summary statistics.\n */\n getSummary(): {\n total: number;\n successful: number;\n failed: number;\n avgDurationMs: number;\n minDurationMs: number;\n maxDurationMs: number;\n } {\n if (this.metrics.length === 0) {\n return {\n total: 0,\n successful: 0,\n failed: 0,\n avgDurationMs: 0,\n minDurationMs: 0,\n maxDurationMs: 0,\n };\n }\n\n const successful = this.metrics.filter((m) => m.success).length;\n const durations = this.metrics.map((m) => m.durationMs);\n const sum = durations.reduce((a, b) => a + b, 0);\n\n return {\n total: this.metrics.length,\n successful,\n failed: this.metrics.length - successful,\n avgDurationMs: sum / this.metrics.length,\n minDurationMs: Math.min(...durations),\n maxDurationMs: Math.max(...durations),\n };\n }\n\n /**\n * Clear all collected metrics.\n */\n clear(): void {\n this.metrics = [];\n }\n}\n\n/**\n * Console-based metrics collector for debugging.\n */\nexport class ConsoleMetricsCollector implements MetricsCollector {\n collect(metrics: RequestMetrics): void {\n console.log('[METRICS]', JSON.stringify(metrics));\n }\n}\n","import type { AuthProvider } from '../auth';\nimport { NoAuth } from '../auth';\nimport { LoggerUtil, NoOpLogger } from '../logger';\nimport { MetricsCollector, NoOpMetricsCollector } from '../metrics';\nimport {\n ApiError,\n ClientOptions,\n FetchLike,\n HTTPMethod,\n Interceptors,\n RequestOptions,\n RetryStrategy,\n toQueryString,\n} from '../types';\n\n/**\n * HTTP client with built-in retry logic, authentication, and interceptors.\n * Supports multiple base URLs, type-safe requests, and comprehensive error handling.\n * \n * @example\n * ```ts\n * const client = new HttpClient({\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'Content-Type': 'application/json' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * });\n * \n * const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });\n * ```\n */\nexport class HttpClient {\n private fetchImpl: FetchLike;\n private baseUrls: ClientOptions['baseUrls'];\n private headers: Record<string, string>;\n private interceptors: Interceptors;\n private retry: RetryStrategy;\n private timeoutMs?: number;\n private auth: AuthProvider;\n private logger: LoggerUtil;\n private metrics: MetricsCollector;\n\n /**\n * Creates a new HTTP client instance.\n * \n * @param opts - Client configuration options\n * @throws {Error} If no fetch implementation is available\n */\n constructor(opts: ClientOptions) {\n this.fetchImpl = opts.fetch ?? (globalThis.fetch?.bind(globalThis) as FetchLike);\n if (!this.fetchImpl)\n throw new Error('No fetch implementation found. Pass one via options.fetch.');\n\n // Validate baseUrls configuration\n if (!opts.baseUrls || typeof opts.baseUrls !== 'object') {\n throw new Error('baseUrls must be provided and must be an object');\n }\n if (!opts.baseUrls.default) {\n throw new Error('baseUrls must include a \"default\" key');\n }\n\n this.baseUrls = opts.baseUrls;\n this.headers = opts.headers ?? { 'Content-Type': 'application/json' };\n this.interceptors = opts.interceptors ?? {};\n this.retry = opts.retry ?? {\n maxRetries: 2,\n baseDelayMs: 250,\n jitter: 0.2,\n retryMethods: ['GET', 'HEAD'],\n };\n\n // Validate retry configuration\n if (this.retry.maxRetries < 0) {\n throw new Error('retry.maxRetries must be non-negative');\n }\n if (this.retry.baseDelayMs < 0) {\n throw new Error('retry.baseDelayMs must be non-negative');\n }\n if (this.retry.jitter !== undefined && (this.retry.jitter < 0 || this.retry.jitter > 1)) {\n throw new Error('retry.jitter must be between 0 and 1');\n }\n\n this.timeoutMs = opts.timeout?.requestTimeoutMs;\n if (this.timeoutMs !== undefined && this.timeoutMs < 0) {\n throw new Error('timeout.requestTimeoutMs must be non-negative');\n }\n\n this.auth = opts['auth'] ?? new NoAuth();\n this.logger = new LoggerUtil(opts.logger ?? new NoOpLogger());\n this.metrics = opts.metrics ?? new NoOpMetricsCollector();\n }\n\n /**\n * Set or update the authentication provider.\n * \n * @param auth - Authentication provider instance\n * @example\n * ```ts\n * client.setAuth(new BearerTokenAuth(() => getToken()));\n * ```\n */\n setAuth(auth: AuthProvider) {\n this.auth = auth;\n }\n\n private resolveBaseUrl(key?: keyof typeof this.baseUrls) {\n const k: string = (key as string) || 'default';\n const url = this.baseUrls[k];\n if (!url) {\n const availableKeys = Object.keys(this.baseUrls).join(', ');\n throw new Error(`Unknown baseUrl key: \"${k}\". Available keys: ${availableKeys}`);\n }\n return url.replace(/\\/$/, '');\n }\n\n /**\n * Sleep for a specified duration (used for retry backoff).\n * @private\n */\n private sleep(ms: number) {\n return new Promise((res) => setTimeout(res, ms));\n }\n\n /**\n * Execute a function with retry logic and exponential backoff.\n * @private\n */\n private async withRetry<T>(\n fn: () => Promise<T>,\n canRetry: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean\n ): Promise<T> {\n let attempt = 0;\n const { maxRetries, baseDelayMs, jitter = 0.2 } = this.retry;\n while (true) {\n try {\n return await fn();\n } catch (err: unknown) {\n if (attempt >= maxRetries || !canRetry({ attempt, error: err })) throw err;\n const backoff = baseDelayMs * 2 ** attempt;\n const j = 1 + (Math.random() * 2 - 1) * jitter;\n await this.sleep(backoff * j);\n attempt++;\n }\n }\n }\n\n /**\n * Run all registered before-request hooks.\n * @private\n */\n private async runBeforeHooks(url: string, init: RequestInit & { __urlOverride?: string }) {\n for (const h of this.interceptors.beforeRequest ?? []) {\n await h({ url, init });\n }\n }\n\n /**\n * Run all registered after-response hooks.\n * @private\n */\n private async runAfterHooks(req: Request, res: Response, parsed?: unknown) {\n for (const h of this.interceptors.afterResponse ?? []) {\n await h({ request: req, response: res, parsed });\n }\n }\n\n /**\n * Make an HTTP request with automatic retry, authentication, and validation.\n * \n * @param method - HTTP method (GET, POST, PUT, etc.)\n * @param path - Request path (will be appended to base URL)\n * @param body - Request body (will be JSON.stringify'd if Content-Type is json)\n * @param options - Additional request options (headers, query params, etc.)\n * @returns Promise resolving to response data and Response object\n * @throws {ApiError} If request fails or response validation fails\n * \n * @example\n * ```ts\n * const { data, response } = await client.request('GET', '/users', undefined, {\n * query: { page: 1, limit: 10 },\n * headers: { 'X-Custom': 'value' }\n * });\n * ```\n */\n async request<T = unknown>(\n method: keyof typeof HTTPMethod,\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n const startTime = Date.now();\n const base = this.resolveBaseUrl(options?.baseUrlKey);\n let url = `${base}${path}${toQueryString(options?.query)}`;\n\n this.logger.debug('HTTP request initiated', {\n method,\n path,\n baseUrlKey: options?.baseUrlKey,\n hasBody: body !== undefined,\n });\n\n const headers = { ...this.headers, ...(options?.headers ?? {}) };\n\n const controller = new AbortController();\n const signal = options?.signal ?? controller.signal;\n const init: RequestInit & { __urlOverride?: string } = {\n method,\n headers,\n body:\n body != null\n ? headers['Content-Type']?.includes('json')\n ? JSON.stringify(body)\n : String(body)\n : undefined,\n signal,\n };\n\n await this.auth.apply({ url, init, options });\n if (init.__urlOverride) url = init.__urlOverride;\n await this.runBeforeHooks(url, init);\n\n const doFetch = async () => {\n // Apply timeout if configured\n let timeoutId: NodeJS.Timeout | number | undefined;\n if (this.timeoutMs && !options?.signal) {\n timeoutId = setTimeout(() => {\n const timeoutError = new Error('Request timeout');\n timeoutError.name = 'TimeoutError';\n controller.abort(timeoutError);\n }, this.timeoutMs);\n }\n\n try {\n const req = new Request(url, init);\n\n const res = await this.fetchImpl(req);\n if (!res.ok) {\n // Read response safely\n let text = '';\n try {\n text = await res.text();\n } catch (readError) {\n text = `Failed to read response: ${readError instanceof Error ? readError.message : String(readError)}`;\n }\n throw new ApiError(`HTTP ${res.status}: ${res.statusText}`, {\n status: res.status,\n details: text,\n });\n }\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('json') ? await res.json() : await res.text();\n await this.runAfterHooks(new Request(url, init), res, data);\n\n const duration = Date.now() - startTime;\n this.logger.info('HTTP request successful', {\n method,\n url,\n status: res.status,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: res.status,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: true,\n });\n\n return { data: data as T, response: res };\n } catch (error) {\n const duration = Date.now() - startTime;\n this.logger.error('HTTP request failed', error as Error, {\n method,\n url,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: error instanceof ApiError ? error.status : undefined,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n };\n\n const canRetry = ({\n response,\n error,\n }: {\n response?: Response;\n error?: unknown;\n attempt: number;\n }) => {\n // Don't retry timeouts or aborts\n if (error && typeof error === 'object' && 'name' in error) {\n const errorName = (error as { name?: string }).name;\n if (errorName === 'AbortError' || errorName === 'TimeoutError') return false;\n }\n // Retry on network errors or 5xx\n if (error instanceof ApiError && error.status && error.status >= 500) return true;\n if (error && !response) return true; // network error\n return false;\n };\n\n if (!this.retry.retryMethods?.includes(method)) {\n return doFetch();\n }\n return this.withRetry(doFetch, canRetry);\n }\n\n /**\n * Convenience method for GET requests.\n * \n * @example\n * ```ts\n * const { data } = await client.get('/users', { query: { page: 1 } });\n * ```\n */\n async get<T = unknown>(path: string, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('GET', path, undefined, options);\n }\n\n /**\n * Convenience method for POST requests.\n * \n * @example\n * ```ts\n * const { data } = await client.post('/users', { name: 'John' });\n * ```\n */\n async post<T = unknown>(path: string, body?: unknown, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('POST', path, body, options);\n }\n\n /**\n * Convenience method for PUT requests.\n * \n * @example\n * ```ts\n * const { data } = await client.put('/users/1', { name: 'John Updated' });\n * ```\n */\n async put<T = unknown>(path: string, body?: unknown, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('PUT', path, body, options);\n }\n\n /**\n * Convenience method for PATCH requests.\n * \n * @example\n * ```ts\n * const { data } = await client.patch('/users/1', { name: 'John' });\n * ```\n */\n async patch<T = unknown>(path: string, body?: unknown, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('PATCH', path, body, options);\n }\n\n /**\n * Convenience method for DELETE requests.\n * \n * @example\n * ```ts\n * const { data } = await client.delete('/users/1');\n * ```\n */\n async delete<T = unknown>(path: string, options?: RequestOptions): Promise<{ data: T; response: Response }> {\n return this.request<T>('DELETE', path, undefined, options);\n }\n}\n","import { z } from 'zod';\nimport { HttpClient } from '../http/http-client';\nimport { HTTPMethod } from '../types';\nimport { parseOrThrow } from '../validation';\n\n/**\n * Request configuration for endpoint calls.\n * Wrapper object containing all request parameters.\n *\n * @template ReqSchema - Zod schema for request validation\n */\nexport type EndpointCallConfig<ReqSchema extends z.ZodType> = {\n /** Request body data (for POST, PUT, PATCH, etc.) or request args */\n data?: z.infer<ReqSchema>;\n /** Path parameters for dynamic path construction */\n pathParams?: Record<string, string | number>;\n /** Query string parameters */\n query?: Record<string, string | number | boolean | undefined> | URLSearchParams;\n /** Request headers */\n headers?: Record<string, string>;\n /** Override base URL for this call */\n baseUrlKey?: string;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n};\n\n/**\n * Generic, strongly-typed endpoint with Zod schemas for request and response validation.\n * Extend this class to create type-safe API endpoints.\n * \n * @template ReqSchema - Zod schema for request validation\n * @template ResSchema - Zod schema for response validation\n * \n * @example\n * ```ts\n * const UserSchema = z.object({ id: z.number(), name: z.string() });\n * const CreateUserSchema = z.object({ name: z.string() });\n * \n * class GetUser extends BaseEndpoint<typeof CreateUserSchema, typeof UserSchema> {\n * protected method = 'GET' as const;\n * protected path = (args: z.infer<typeof CreateUserSchema>) => `/users/${args.id}`;\n * \n * constructor(client: HttpClient) {\n * super(client, { \n * requestSchema: CreateUserSchema,\n * responseSchema: UserSchema \n * });\n * }\n * }\n * ```\n */\nexport abstract class BaseEndpoint<ReqSchema extends z.ZodType, ResSchema extends z.ZodType> {\n /** HTTP method for this endpoint */\n protected abstract readonly method: keyof typeof HTTPMethod;\n /** URL path (can be a function for dynamic paths) */\n protected abstract readonly path: string | ((params: z.infer<ReqSchema>) => string);\n /** Additional options for the request */\n protected readonly options?: {\n /** Override base URL for this call */\n baseUrlKey?: string;\n };\n /** Optional request schema for validation */\n protected readonly requestSchema?: ReqSchema;\n /** Response schema for validation */\n protected readonly responseSchema: ResSchema;\n\n /**\n * @param client - HttpClient instance\n * @param cfg - Configuration with request and response schemas\n */\n constructor(\n protected client: HttpClient,\n cfg: { requestSchema?: ReqSchema; responseSchema: ResSchema }\n ) {\n this.requestSchema = cfg.requestSchema;\n this.responseSchema = cfg.responseSchema;\n }\n\n /**\n * Call the endpoint with strong typing derived from schemas.\n * Validates request data before sending and response data after receiving.\n * \n * @param config - Request configuration object containing all parameters\n * @returns Promise resolving to validated response data (typed by ResSchema)\n * @throws {ZodError} If request validation fails\n * @throws {ApiError} If response validation fails or request fails\n * \n * @example\n * ```ts\n * const endpoint = new GetUser(client);\n * const user = await endpoint.call({ data: { id: 1 } });\n * // With additional options:\n * const user = await endpoint.call({ \n * data: { id: 1 },\n * headers: { 'X-Custom': 'value' },\n * query: { include: 'posts' }\n * });\n * ```\n */\n async call(config: EndpointCallConfig<ReqSchema> = {}): Promise<z.infer<ResSchema>> {\n const { data, query, headers, baseUrlKey, signal, pathParams } = config;\n\n // Validate request body/params before sending (when schema provided)\n if (this.requestSchema && data !== undefined) {\n const parsed = this.requestSchema.safeParse(data);\n if (!parsed.success) throw parsed.error;\n }\n\n // Build path - use pathParams if provided, otherwise use data\n const pathArgs = pathParams ?? data;\n const path = typeof this.path === 'function' ? this.path(pathArgs as z.infer<ReqSchema>) : this.path;\n\n // For GET/HEAD methods, don't send body\n const shouldHaveBody = this.method !== 'GET' && this.method !== 'HEAD';\n const body = shouldHaveBody ? data : undefined;\n\n const { data: responseData } = await this.client.request(this.method, path, body, {\n query,\n headers,\n baseUrlKey: baseUrlKey ?? this.options?.baseUrlKey,\n signal,\n });\n\n return parseOrThrow<z.infer<ResSchema>>(this.responseSchema, responseData);\n }\n}\n","import { z } from 'zod';\n\n/**\n * Common ID type that supports strings, numbers, or UUIDs.\n * Use this for entity identifiers in your schemas.\n * \n * @example\n * ```ts\n * const UserSchema = z.object({ id: Id, name: z.string() });\n * ```\n */\nexport const Id = z.union([\n z.string().min(1),\n z.number(),\n z.uuid({\n version: 'v4',\n }),\n]);\nexport type IdType = z.infer<typeof Id>;\n\n/**\n * Common timestamp fields for entities.\n * Use this for database models with creation/update tracking.\n * \n * @example\n * ```ts\n * const UserSchema = z.object({\n * id: Id,\n * name: z.string(),\n * ...Timestamps.shape\n * });\n * ```\n */\nexport const Timestamps = z.object({\n createdAt: z.iso.datetime(),\n updatedAt: z.iso.datetime(),\n});\n\n/**\n * Metadata information typically included in API responses.\n * Contains request tracking and debugging information.\n */\nexport const Meta = z.object({\n requestId: z.string().optional(),\n timestamp: z.iso.datetime().optional(),\n traceId: z.string().optional(),\n});\n\n/**\n * Detailed error information for a specific field or path.\n */\nexport const ErrorDetail = z.object({\n path: z.string().optional(),\n message: z.string(),\n});\n\n/**\n * Standard API error response schema.\n * Use this for consistent error handling across your API.\n */\nexport const ApiErrorSchema = z.object({\n code: z.string(),\n message: z.string(),\n details: z.array(ErrorDetail).optional(),\n});\n\n/**\n * Generic envelope wrapper for API responses.\n * Provides consistent structure with success flag, data, error, and metadata.\n * \n * @param inner - Zod schema for the response data\n * @returns Envelope schema wrapping the inner schema\n * \n * @example\n * ```ts\n * const UserResponseSchema = Envelope(z.object({ id: Id, name: z.string() }));\n * \n * // Response structure:\n * // {\n * // success: true,\n * // data: { id: 1, name: 'John' },\n * // meta: { requestId: '...' }\n * // }\n * ```\n */\nexport const Envelope = <T extends z.ZodType>(inner: T) =>\n z.object({\n success: z.boolean(),\n data: inner.optional().nullable(),\n error: ApiErrorSchema.optional(),\n meta: Meta.optional(),\n });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["s","opts: { header?: string; query?: string; value: string }","getToken: () => Promise<string> | string","minLevel: LogLevel","logger: Logger","k: string","err: unknown","init: RequestInit & { __urlOverride?: string }","timeoutId: NodeJS.Timeout | number | undefined","client: HttpClient"],"sources":["../lib/types.ts","../lib/auth.ts","../lib/validation.ts","../lib/logger.ts","../lib/metrics.ts","../lib/http/http-client.ts","../lib/endpoint/base-endpoint.ts","../lib/schemas/common.ts"],"sourcesContent":["import { z, ZodError } from 'zod';\nimport { AuthProvider } from './auth';\nimport { Logger } from './logger';\nimport { MetricsCollector } from './metrics';\n\nexport type Dictionary<T = unknown> = Record<string, T>;\n\nexport type FetchLike = (input: string | Request | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Map of base URLs for different services.\n * The 'default' key is required and used when no specific key is provided.\n *\n * @example\n * ```ts\n * {\n * default: 'https://api.example.com',\n * auth: 'https://auth.example.com',\n * cdn: 'https://cdn.example.com'\n * }\n * ```\n */\nexport type BaseUrlMap = {\n default: string;\n [service: string]: string;\n};\n\n/**\n * Configuration for retry behavior on failed requests.\n * Implements exponential backoff with optional jitter.\n *\n * @example\n * ```ts\n * {\n * maxRetries: 3,\n * baseDelayMs: 1000,\n * jitter: 0.2,\n * retryMethods: ['GET', 'HEAD', 'PUT']\n * }\n * ```\n */\nexport type RetryStrategy = {\n /** Maximum number of retry attempts */\n maxRetries: number;\n /** Base delay in milliseconds (will be exponentially increased) */\n baseDelayMs: number;\n /** Jitter factor 0..1 to randomize delays and prevent thundering herd */\n jitter?: number;\n /** HTTP methods eligible for retry */\n retryMethods?: (keyof typeof HTTPMethod)[];\n /** Custom function to determine if a request should be retried */\n shouldRetry?: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean;\n};\n\nexport const HTTPMethod = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n PATCH: 'PATCH',\n DELETE: 'DELETE',\n HEAD: 'HEAD',\n OPTIONS: 'OPTIONS',\n} as const;\n\nexport type HttpMethod = keyof typeof HTTPMethod;\n\n/**\n * Hook called after a response is received and parsed.\n * Useful for logging, metrics, or global error handling.\n */\nexport type AfterResponseHook = (ctx: {\n request: Request;\n response: Response;\n parsed?: unknown;\n}) => Promise<void> | void;\n\n/**\n * Hook called before a request is sent.\n * Useful for logging, adding headers, or modifying the request.\n */\nexport type BeforeRequestHook = (ctx: { url: string; init: RequestInit }) => Promise<void> | void;\n\nexport interface Interceptors {\n /** Hooks executed before each request is sent */\n beforeRequest?: BeforeRequestHook[];\n /** Hooks executed after each response is received */\n afterResponse?: AfterResponseHook[];\n}\n\nexport interface TimeoutOptions {\n /** Request timeout in milliseconds */\n requestTimeoutMs?: number;\n}\n\n/**\n * Configuration options for the HTTP client.\n *\n * @example\n * ```ts\n * const options: ClientOptions = {\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'X-API-Version': '1.0' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * }\n * ```\n */\nexport interface ClientOptions {\n /** Map of base URLs for different services */\n baseUrls: BaseUrlMap;\n /** Custom fetch implementation (defaults to globalThis.fetch) */\n fetch?: FetchLike;\n /** Default headers applied to all requests */\n headers?: Record<string, string>;\n /** Retry strategy configuration */\n retry?: RetryStrategy;\n /** Request/response interceptors */\n interceptors?: Interceptors;\n /** Timeout configuration */\n timeout?: TimeoutOptions;\n /** Authentication provider */\n auth?: AuthProvider;\n /** Logger instance */\n logger?: Logger;\n /** Metrics collector */\n metrics?: MetricsCollector;\n}\n\nexport type SafeParseResult<T> = { success: true; data: T } | { success: false; error: ZodError };\n\n/**\n * Custom error class for API-related errors.\n * Includes HTTP status codes, response details, and validation errors.\n *\n * @example\n * ```ts\n * throw new ApiError('Invalid request', {\n * status: 400,\n * details: { field: 'email', message: 'Invalid format' }\n * });\n * ```\n */\nexport class ApiError extends Error {\n public status?: number;\n public details?: unknown;\n public zodError?: ZodError;\n\n constructor(\n message: string,\n options?: { status?: number; cause?: unknown; details?: unknown; zodError?: ZodError }\n ) {\n super(message);\n this.name = 'ApiError';\n this.status = options?.status;\n this.details = options?.details;\n this.cause = options?.cause as any;\n this.zodError = options?.zodError;\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ApiError);\n }\n }\n\n /**\n * Check if this is a validation error (has zodError)\n */\n isValidationError(): boolean {\n return !!this.zodError;\n }\n\n /**\n * Check if this is a client error (4xx status)\n */\n isClientError(): boolean {\n return !!this.status && this.status >= 400 && this.status < 500;\n }\n\n /**\n * Check if this is a server error (5xx status)\n */\n isServerError(): boolean {\n return !!this.status && this.status >= 500;\n }\n\n /**\n * Get a formatted error message with all available details\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n details: this.details,\n zodError: this.zodError?.issues,\n stack: this.stack,\n };\n }\n}\n\nexport type Paginated<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n};\n\n/**\n * Schema for paginated responses\n */\nexport const PaginationSchema = z.object({\n items: z.array(z.unknown()),\n total: z.number().int().nonnegative(),\n page: z.number().int().nonnegative(),\n pageSize: z.number().int().positive(),\n});\n\n/**\n * Options that can be passed to individual requests to override defaults.\n *\n * @example\n * ```ts\n * await endpoint.call(data, {\n * baseUrlKey: 'v2',\n * headers: { 'X-Custom': 'value' },\n * query: { filter: 'active' }\n * });\n * ```\n */\nexport type RequestOptions = {\n /** Override base URL for a single call */\n baseUrlKey?: keyof BaseUrlMap;\n /** Additional headers for this call only */\n headers?: Record<string, string>;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n /** Custom query params */\n query?: URLSearchParams | Record<string, string | number | boolean | undefined>;\n};\n\n/**\n * Converts query parameters to a URL query string.\n * Filters out undefined values automatically.\n *\n * @param q - Query parameters as URLSearchParams or object\n * @returns Query string with leading '?' or empty string\n *\n * @example\n * ```ts\n * toQueryString({ page: 1, filter: 'active' }) // \"?page=1&filter=active\"\n * toQueryString({ optional: undefined }) // \"\"\n * ```\n */\nexport function toQueryString(q?: RequestOptions['query']): string {\n if (!q) return '';\n if (q instanceof URLSearchParams) {\n const s = q.toString();\n return s ? `?${s}` : '';\n }\n const params = new URLSearchParams();\n Object.entries(q).forEach(([k, v]) => {\n if (v !== undefined) {\n params.append(k, String(v));\n }\n });\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n","import type { RequestOptions as ReqOpts } from './types';\n\n/**\n * Extended RequestInit with URL override capability for query-based auth.\n */\nexport interface AuthContext {\n url: string;\n init: RequestInit & { __urlOverride?: string };\n options?: ReqOpts;\n}\n\n/**\n * Interface for authentication providers.\n * Implement this to create custom authentication strategies.\n *\n * @example\n * ```ts\n * class CustomAuth implements AuthProvider {\n * async apply({ init }) {\n * init.headers = { ...init.headers, 'X-Custom-Auth': 'token' };\n * }\n * }\n * ```\n */\nexport interface AuthProvider {\n /**\n * Apply authentication to the outgoing request.\n * Called after SDK headers are assembled, but before request is sent.\n *\n * @param req - Request context including URL, init, and options\n */\n apply(req: AuthContext): Promise<void> | void;\n}\n\n/**\n * No-op authentication provider (no authentication applied).\n * Use this when you don't need authentication.\n */\nexport class NoAuth implements AuthProvider {\n async apply() {\n /* no-op */\n }\n}\n\n/**\n * API Key authentication provider.\n * Supports both header-based and query parameter-based authentication.\n *\n * @example\n * ```ts\n * // Header-based\n * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });\n *\n * // Query parameter-based\n * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });\n * ```\n */\nexport class ApiKeyAuth implements AuthProvider {\n constructor(private opts: { header?: string; query?: string; value: string }) {\n if (!opts.header && !opts.query) {\n throw new Error('ApiKeyAuth requires either \"header\" or \"query\" option');\n }\n if (opts.header && opts.query) {\n throw new Error('ApiKeyAuth cannot use both \"header\" and \"query\" options');\n }\n }\n apply({ url, init }: AuthContext) {\n if (this.opts.header) {\n init.headers = { ...(init.headers as any), [this.opts.header]: this.opts.value };\n } else if (this.opts.query) {\n const u = new URL(url);\n u.searchParams.set(this.opts.query, this.opts.value);\n init.__urlOverride = u.toString();\n }\n }\n}\n\n/**\n * Bearer token authentication provider.\n * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).\n *\n * @example\n * ```ts\n * // Static token\n * const auth = new BearerTokenAuth(() => 'my-token');\n *\n * // Dynamic token with refresh\n * const auth = new BearerTokenAuth(async () => {\n * return await refreshAccessToken();\n * });\n * ```\n */\nexport class BearerTokenAuth implements AuthProvider {\n constructor(private getToken: () => Promise<string> | string) {}\n async apply({ init }: AuthContext) {\n const token = await this.getToken();\n if (!token) {\n throw new Error('BearerTokenAuth: token is empty or undefined');\n }\n init.headers = { ...(init.headers as any), Authorization: `Bearer ${token}` };\n }\n}\n","import { z } from 'zod';\nimport { ApiError, SafeParseResult } from './types';\n\n/**\n * Safely parse data with a Zod schema without throwing.\n * Returns a result object with success status and data or error.\n *\n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Result object with success flag and data/error\n *\n * @example\n * ```ts\n * const result = safeParse(UserSchema, userData);\n * if (result.success) {\n * console.log(result.data);\n * } else {\n * console.error(result.error);\n * }\n * ```\n */\nexport function safeParse<T>(schema: z.ZodType, data: unknown): SafeParseResult<T> {\n const res = schema.safeParse(data);\n if (res.success) return { success: true, data: res.data as T };\n return { success: false, error: res.error };\n}\n\n/**\n * Parse data with a Zod schema, throwing an ApiError on validation failure.\n * Use this when you want to fail fast on invalid data.\n *\n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {ApiError} If validation fails\n *\n * @example\n * ```ts\n * try {\n * const user = parseOrThrow(UserSchema, userData);\n * console.log(user);\n * } catch (error) {\n * if (error instanceof ApiError && error.zodError) {\n * console.error('Validation failed:', error.zodError.issues);\n * }\n * }\n * ```\n */\nexport function parseOrThrow<T>(schema: z.ZodType, data: unknown): T {\n const res = schema.safeParse(data);\n if (!res.success) {\n throw new ApiError('Response validation failed', { zodError: res.error });\n }\n return res.data as T;\n}\n","/**\n * Log levels for structured logging.\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Structured log entry with metadata.\n */\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: string;\n context?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Logger interface for custom logger implementations.\n * Implement this to integrate with your logging infrastructure.\n *\n * @example\n * ```ts\n * class ConsoleLogger implements Logger {\n * log(entry: LogEntry) {\n * console.log(JSON.stringify(entry));\n * }\n * }\n * ```\n */\nexport interface Logger {\n log(entry: LogEntry): void;\n}\n\n/**\n * Default console logger implementation.\n * Formats log entries as JSON for easy parsing.\n */\nexport class ConsoleLogger implements Logger {\n constructor(private minLevel: LogLevel = LogLevel.INFO) {}\n\n log(entry: LogEntry): void {\n const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];\n const entryLevelIndex = levels.indexOf(entry.level);\n const minLevelIndex = levels.indexOf(this.minLevel);\n\n if (entryLevelIndex < minLevelIndex) return;\n\n const output = {\n ...entry,\n error: entry.error\n ? {\n message: entry.error.message,\n stack: entry.error.stack,\n name: entry.error.name,\n }\n : undefined,\n };\n\n switch (entry.level) {\n case LogLevel.DEBUG:\n console.debug(JSON.stringify(output));\n break;\n case LogLevel.INFO:\n console.info(JSON.stringify(output));\n break;\n case LogLevel.WARN:\n console.warn(JSON.stringify(output));\n break;\n case LogLevel.ERROR:\n console.error(JSON.stringify(output));\n break;\n }\n }\n}\n\n/**\n * No-op logger that discards all log entries.\n * Use this in production if you don't want any logging.\n */\nexport class NoOpLogger implements Logger {\n log(_entry: LogEntry): void {\n // no-op\n }\n}\n\n/**\n * Utility class for creating structured log entries.\n */\nexport class LoggerUtil {\n constructor(private logger: Logger) {}\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.DEBUG,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.INFO,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.WARN,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n error(message: string, error?: Error, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.ERROR,\n message,\n timestamp: new Date().toISOString(),\n context,\n error,\n });\n }\n}\n","/**\n * Metrics data for HTTP requests.\n */\nexport interface RequestMetrics {\n method: string;\n path: string;\n status?: number;\n durationMs: number;\n timestamp: string;\n success: boolean;\n error?: string;\n}\n\n/**\n * Interface for metrics collectors.\n * Implement this to integrate with your monitoring infrastructure (DataDog, Prometheus, etc.).\n *\n * @example\n * ```ts\n * class DataDogMetrics implements MetricsCollector {\n * collect(metrics: RequestMetrics) {\n * dogstatsd.histogram('http.request.duration', metrics.durationMs, {\n * method: metrics.method,\n * status: String(metrics.status)\n * });\n * }\n * }\n * ```\n */\nexport interface MetricsCollector {\n collect(metrics: RequestMetrics): void;\n}\n\n/**\n * No-op metrics collector that discards all metrics.\n */\nexport class NoOpMetricsCollector implements MetricsCollector {\n collect(_metrics: RequestMetrics): void {\n // no-op\n }\n}\n\n/**\n * In-memory metrics collector for testing and development.\n * Stores metrics in memory with configurable retention.\n */\nexport class InMemoryMetricsCollector implements MetricsCollector {\n private metrics: RequestMetrics[] = [];\n private readonly maxEntries: number;\n\n constructor(maxEntries = 1000) {\n this.maxEntries = maxEntries;\n }\n\n collect(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n if (this.metrics.length > this.maxEntries) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get all collected metrics.\n */\n getMetrics(): RequestMetrics[] {\n return [...this.metrics];\n }\n\n /**\n * Get metrics summary statistics.\n */\n getSummary(): {\n total: number;\n successful: number;\n failed: number;\n avgDurationMs: number;\n minDurationMs: number;\n maxDurationMs: number;\n } {\n if (this.metrics.length === 0) {\n return {\n total: 0,\n successful: 0,\n failed: 0,\n avgDurationMs: 0,\n minDurationMs: 0,\n maxDurationMs: 0,\n };\n }\n\n const successful = this.metrics.filter((m) => m.success).length;\n const durations = this.metrics.map((m) => m.durationMs);\n const sum = durations.reduce((a, b) => a + b, 0);\n\n return {\n total: this.metrics.length,\n successful,\n failed: this.metrics.length - successful,\n avgDurationMs: sum / this.metrics.length,\n minDurationMs: Math.min(...durations),\n maxDurationMs: Math.max(...durations),\n };\n }\n\n /**\n * Clear all collected metrics.\n */\n clear(): void {\n this.metrics = [];\n }\n}\n\n/**\n * Console-based metrics collector for debugging.\n */\nexport class ConsoleMetricsCollector implements MetricsCollector {\n collect(metrics: RequestMetrics): void {\n console.log('[METRICS]', JSON.stringify(metrics));\n }\n}\n","import type { AuthProvider } from '../auth';\nimport { NoAuth } from '../auth';\nimport { LoggerUtil, NoOpLogger } from '../logger';\nimport { MetricsCollector, NoOpMetricsCollector } from '../metrics';\nimport {\n ApiError,\n ClientOptions,\n FetchLike,\n HTTPMethod,\n Interceptors,\n RequestOptions,\n RetryStrategy,\n toQueryString,\n} from '../types';\n\n/**\n * HTTP client with built-in retry logic, authentication, and interceptors.\n * Supports multiple base URLs, type-safe requests, and comprehensive error handling.\n *\n * @example\n * ```ts\n * const client = new HttpClient({\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'Content-Type': 'application/json' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * });\n *\n * const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });\n * ```\n */\nexport class HttpClient {\n private fetchImpl: FetchLike;\n private baseUrls: ClientOptions['baseUrls'];\n private headers: Record<string, string>;\n private interceptors: Interceptors;\n private retry: RetryStrategy;\n private timeoutMs?: number;\n private auth: AuthProvider;\n private logger: LoggerUtil;\n private metrics: MetricsCollector;\n\n /**\n * Creates a new HTTP client instance.\n *\n * @param opts - Client configuration options\n * @throws {Error} If no fetch implementation is available\n */\n constructor(opts: ClientOptions) {\n this.fetchImpl = opts.fetch ?? (globalThis.fetch?.bind(globalThis) as FetchLike);\n if (!this.fetchImpl)\n throw new Error('No fetch implementation found. Pass one via options.fetch.');\n\n // Validate baseUrls configuration\n if (!opts.baseUrls || typeof opts.baseUrls !== 'object') {\n throw new Error('baseUrls must be provided and must be an object');\n }\n if (!opts.baseUrls.default) {\n throw new Error('baseUrls must include a \"default\" key');\n }\n\n this.baseUrls = opts.baseUrls;\n this.headers = opts.headers ?? { 'Content-Type': 'application/json' };\n this.interceptors = opts.interceptors ?? {};\n this.retry = opts.retry ?? {\n maxRetries: 2,\n baseDelayMs: 250,\n jitter: 0.2,\n retryMethods: ['GET', 'HEAD'],\n };\n\n // Validate retry configuration\n if (this.retry.maxRetries < 0) {\n throw new Error('retry.maxRetries must be non-negative');\n }\n if (this.retry.baseDelayMs < 0) {\n throw new Error('retry.baseDelayMs must be non-negative');\n }\n if (this.retry.jitter !== undefined && (this.retry.jitter < 0 || this.retry.jitter > 1)) {\n throw new Error('retry.jitter must be between 0 and 1');\n }\n\n this.timeoutMs = opts.timeout?.requestTimeoutMs;\n if (this.timeoutMs !== undefined && this.timeoutMs < 0) {\n throw new Error('timeout.requestTimeoutMs must be non-negative');\n }\n\n this.auth = opts['auth'] ?? new NoAuth();\n this.logger = new LoggerUtil(opts.logger ?? new NoOpLogger());\n this.metrics = opts.metrics ?? new NoOpMetricsCollector();\n }\n\n /**\n * Set or update the authentication provider.\n *\n * @param auth - Authentication provider instance\n * @example\n * ```ts\n * client.setAuth(new BearerTokenAuth(() => getToken()));\n * ```\n */\n setAuth(auth: AuthProvider) {\n this.auth = auth;\n }\n\n private resolveBaseUrl(key?: keyof typeof this.baseUrls) {\n const k: string = (key as string) || 'default';\n const url = this.baseUrls[k];\n if (!url) {\n const availableKeys = Object.keys(this.baseUrls).join(', ');\n throw new Error(`Unknown baseUrl key: \"${k}\". Available keys: ${availableKeys}`);\n }\n return url.replace(/\\/$/, '');\n }\n\n /**\n * Sleep for a specified duration (used for retry backoff).\n * @private\n */\n private sleep(ms: number) {\n return new Promise((res) => setTimeout(res, ms));\n }\n\n /**\n * Execute a function with retry logic and exponential backoff.\n * @private\n */\n private async withRetry<T>(\n fn: () => Promise<T>,\n canRetry: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean\n ): Promise<T> {\n let attempt = 0;\n const { maxRetries, baseDelayMs, jitter = 0.2 } = this.retry;\n while (true) {\n try {\n return await fn();\n } catch (err: unknown) {\n if (attempt >= maxRetries || !canRetry({ attempt, error: err })) throw err;\n const backoff = baseDelayMs * 2 ** attempt;\n const j = 1 + (Math.random() * 2 - 1) * jitter;\n await this.sleep(backoff * j);\n attempt++;\n }\n }\n }\n\n /**\n * Run all registered before-request hooks.\n * @private\n */\n private async runBeforeHooks(url: string, init: RequestInit & { __urlOverride?: string }) {\n for (const h of this.interceptors.beforeRequest ?? []) {\n await h({ url, init });\n }\n }\n\n /**\n * Run all registered after-response hooks.\n * @private\n */\n private async runAfterHooks(req: Request, res: Response, parsed?: unknown) {\n for (const h of this.interceptors.afterResponse ?? []) {\n await h({ request: req, response: res, parsed });\n }\n }\n\n /**\n * Make an HTTP request with automatic retry, authentication, and validation.\n *\n * @param method - HTTP method (GET, POST, PUT, etc.)\n * @param path - Request path (will be appended to base URL)\n * @param body - Request body (will be JSON.stringify'd if Content-Type is json)\n * @param options - Additional request options (headers, query params, etc.)\n * @returns Promise resolving to response data and Response object\n * @throws {ApiError} If request fails or response validation fails\n *\n * @example\n * ```ts\n * const { data, response } = await client.request('GET', '/users', undefined, {\n * query: { page: 1, limit: 10 },\n * headers: { 'X-Custom': 'value' }\n * });\n * ```\n */\n async request<T = unknown>(\n method: keyof typeof HTTPMethod,\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n const startTime = Date.now();\n const base = this.resolveBaseUrl(options?.baseUrlKey);\n let url = `${base}${path}${toQueryString(options?.query)}`;\n\n this.logger.debug('HTTP request initiated', {\n method,\n path,\n baseUrlKey: options?.baseUrlKey,\n hasBody: body !== undefined,\n });\n\n const headers = { ...this.headers, ...(options?.headers ?? {}) };\n\n const controller = new AbortController();\n const signal = options?.signal ?? controller.signal;\n const init: RequestInit & { __urlOverride?: string } = {\n method,\n headers,\n body:\n body != null\n ? headers['Content-Type']?.includes('json')\n ? JSON.stringify(body)\n : String(body)\n : undefined,\n signal,\n };\n\n await this.auth.apply({ url, init, options });\n if (init.__urlOverride) url = init.__urlOverride;\n await this.runBeforeHooks(url, init);\n\n const doFetch = async () => {\n // Apply timeout if configured\n let timeoutId: NodeJS.Timeout | number | undefined;\n if (this.timeoutMs && !options?.signal) {\n timeoutId = setTimeout(() => {\n const timeoutError = new Error('Request timeout');\n timeoutError.name = 'TimeoutError';\n controller.abort(timeoutError);\n }, this.timeoutMs);\n }\n\n try {\n const req = new Request(url, init);\n\n const res = await this.fetchImpl(req);\n if (!res.ok) {\n // Read response safely\n let text = '';\n try {\n text = await res.text();\n } catch (readError) {\n text = `Failed to read response: ${readError instanceof Error ? readError.message : String(readError)}`;\n }\n throw new ApiError(`HTTP ${res.status}: ${res.statusText}`, {\n status: res.status,\n details: text,\n });\n }\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('json') ? await res.json() : await res.text();\n await this.runAfterHooks(new Request(url, init), res, data);\n\n const duration = Date.now() - startTime;\n this.logger.info('HTTP request successful', {\n method,\n url,\n status: res.status,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: res.status,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: true,\n });\n\n return { data: data as T, response: res };\n } catch (error) {\n const duration = Date.now() - startTime;\n this.logger.error('HTTP request failed', error as Error, {\n method,\n url,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: error instanceof ApiError ? error.status : undefined,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n };\n\n const canRetry = ({\n response,\n error,\n }: {\n response?: Response;\n error?: unknown;\n attempt: number;\n }) => {\n // Don't retry timeouts or aborts\n if (error && typeof error === 'object' && 'name' in error) {\n const errorName = (error as { name?: string }).name;\n if (errorName === 'AbortError' || errorName === 'TimeoutError') return false;\n }\n // Retry on network errors or 5xx\n if (error instanceof ApiError && error.status && error.status >= 500) return true;\n if (error && !response) return true; // network error\n return false;\n };\n\n if (!this.retry.retryMethods?.includes(method)) {\n return doFetch();\n }\n return this.withRetry(doFetch, canRetry);\n }\n\n /**\n * Convenience method for GET requests.\n *\n * @example\n * ```ts\n * const { data } = await client.get('/users', { query: { page: 1 } });\n * ```\n */\n async get<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('GET', path, undefined, options);\n }\n\n /**\n * Convenience method for POST requests.\n *\n * @example\n * ```ts\n * const { data } = await client.post('/users', { name: 'John' });\n * ```\n */\n async post<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('POST', path, body, options);\n }\n\n /**\n * Convenience method for PUT requests.\n *\n * @example\n * ```ts\n * const { data } = await client.put('/users/1', { name: 'John Updated' });\n * ```\n */\n async put<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('PUT', path, body, options);\n }\n\n /**\n * Convenience method for PATCH requests.\n *\n * @example\n * ```ts\n * const { data } = await client.patch('/users/1', { name: 'John' });\n * ```\n */\n async patch<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('PATCH', path, body, options);\n }\n\n /**\n * Convenience method for DELETE requests.\n *\n * @example\n * ```ts\n * const { data } = await client.delete('/users/1');\n * ```\n */\n async delete<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; response: Response }> {\n return this.request<T>('DELETE', path, undefined, options);\n }\n}\n","import { z } from 'zod';\nimport { HttpClient } from '../http/http-client';\nimport { HTTPMethod } from '../types';\nimport { parseOrThrow } from '../validation';\n\n/**\n * Request configuration for endpoint calls.\n * Wrapper object containing all request parameters.\n *\n * @template ReqSchema - Zod schema for request validation\n * @template PathParams - Type for path parameters\n * @template QueryParams - Type for query parameters\n */\nexport type EndpointCallConfig<\n ReqSchema extends z.ZodType,\n PathParams = never,\n QueryParams = never,\n> = {\n /** Request body data (for POST, PUT, PATCH, etc.) or request args */\n data?: z.infer<ReqSchema>;\n /** Path parameters for dynamic path construction */\n pathParams?: PathParams;\n /** Query string parameters */\n query?: QueryParams;\n /** Request headers */\n headers?: Record<string, string>;\n /** Override base URL for this call */\n baseUrlKey?: string;\n /** Abort controller signal for cancellation */\n signal?: globalThis.AbortSignal;\n};\n\n/**\n * Generic, strongly-typed endpoint with Zod schemas for request and response validation.\n * Extend this class to create type-safe API endpoints.\n *\n * @template ReqSchema - Zod schema for request validation\n * @template ResSchema - Zod schema for response validation\n * @template PathParams - Type for path parameters (optional)\n * @template QueryParams - Type for query parameters (optional)\n *\n * @example\n * ```ts\n * const UserSchema = z.object({ id: z.number(), name: z.string() });\n * const CreateUserSchema = z.object({ name: z.string() });\n *\n * type UserPathParams = { id: string };\n * type UserQueryParams = { include?: string; limit?: number };\n *\n * class GetUser extends BaseEndpoint<\n * typeof CreateUserSchema,\n * typeof UserSchema,\n * UserPathParams,\n * UserQueryParams\n * > {\n * protected method = 'GET' as const;\n * protected path = (params: UserPathParams) => `/users/${params.id}`;\n *\n * constructor(client: HttpClient) {\n * super(client, {\n * requestSchema: CreateUserSchema,\n * responseSchema: UserSchema\n * });\n * }\n * }\n *\n * // Usage:\n * const user = await endpoint.call({\n * pathParams: { id: '123' },\n * query: { include: 'posts', limit: 10 }\n * });\n * ```\n */\nexport abstract class BaseEndpoint<\n ReqSchema extends z.ZodType,\n ResSchema extends z.ZodType,\n PathParams = never,\n QueryParams = never,\n> {\n /** HTTP method for this endpoint */\n protected abstract readonly method: keyof typeof HTTPMethod;\n /** URL path (can be a function for dynamic paths) */\n protected abstract readonly path: string | ((params: PathParams) => string);\n /** Additional options for the request */\n protected readonly options?: {\n /** Override base URL for this call */\n baseUrlKey?: string;\n };\n /** Optional request schema for validation */\n protected readonly requestSchema?: ReqSchema;\n /** Response schema for validation */\n protected readonly responseSchema: ResSchema;\n\n /**\n * @param client - HttpClient instance\n * @param cfg - Configuration with request and response schemas\n */\n constructor(\n protected client: HttpClient,\n cfg: {\n requestSchema?: ReqSchema;\n responseSchema: ResSchema;\n }\n ) {\n this.requestSchema = cfg.requestSchema;\n this.responseSchema = cfg.responseSchema;\n }\n\n /**\n * Call the endpoint with strong typing derived from schemas.\n * Validates request data before sending and response data after receiving.\n *\n * @param config - Request configuration object containing all parameters\n * @returns Promise resolving to validated response data (typed by ResSchema)\n * @throws {ZodError} If request validation fails\n * @throws {ApiError} If response validation fails or request fails\n *\n * @example\n * ```ts\n * const endpoint = new GetUser(client);\n * const user = await endpoint.call({\n * pathParams: { id: '123' },\n * query: { include: 'posts' }\n * });\n * // With additional options:\n * const user = await endpoint.call({\n * data: { name: 'John' },\n * pathParams: { id: '123' },\n * headers: { 'X-Custom': 'value' },\n * query: { include: 'posts' }\n * });\n * ```\n */\n async call(\n config: EndpointCallConfig<ReqSchema, PathParams, QueryParams> = {} as EndpointCallConfig<\n ReqSchema,\n PathParams,\n QueryParams\n >\n ): Promise<z.infer<ResSchema>> {\n const { data, query, headers, baseUrlKey, signal, pathParams } = config;\n\n // Validate request body/params before sending (when schema provided)\n if (this.requestSchema && data !== undefined) {\n const parsed = this.requestSchema.safeParse(data);\n if (!parsed.success) throw parsed.error;\n }\n\n // Build path - use pathParams if provided, otherwise use data for backwards compatibility\n const pathArgs = (pathParams ?? data) as PathParams;\n const path = typeof this.path === 'function' ? this.path(pathArgs) : this.path;\n\n // For GET/HEAD methods, don't send body\n const shouldHaveBody = this.method !== 'GET' && this.method !== 'HEAD';\n const body = shouldHaveBody ? data : undefined;\n\n // Convert query params to the format expected by http-client\n const queryForRequest = query as\n | Record<string, string | number | boolean | undefined>\n | URLSearchParams\n | undefined;\n\n const { data: responseData } = await this.client.request(this.method, path, body, {\n query: queryForRequest,\n headers,\n baseUrlKey: baseUrlKey ?? this.options?.baseUrlKey,\n signal,\n });\n\n return parseOrThrow<z.infer<ResSchema>>(this.responseSchema, responseData);\n }\n}\n","import { z } from 'zod';\n\n/**\n * Common ID type that supports strings, numbers, or UUIDs.\n * Use this for entity identifiers in your schemas.\n *\n * @example\n * ```ts\n * const UserSchema = z.object({ id: Id, name: z.string() });\n * ```\n */\nexport const Id = z.union([\n z.string().min(1),\n z.number(),\n z.uuid({\n version: 'v4',\n }),\n]);\nexport type IdType = z.infer<typeof Id>;\n\n/**\n * Common timestamp fields for entities.\n * Use this for database models with creation/update tracking.\n *\n * @example\n * ```ts\n * const UserSchema = z.object({\n * id: Id,\n * name: z.string(),\n * ...Timestamps.shape\n * });\n * ```\n */\nexport const Timestamps = z.object({\n createdAt: z.iso.datetime(),\n updatedAt: z.iso.datetime(),\n});\n\n/**\n * Metadata information typically included in API responses.\n * Contains request tracking and debugging information.\n */\nexport const Meta = z.object({\n requestId: z.string().optional(),\n timestamp: z.iso.datetime().optional(),\n traceId: z.string().optional(),\n});\n\n/**\n * Detailed error information for a specific field or path.\n */\nexport const ErrorDetail = z.object({\n path: z.string().optional(),\n message: z.string(),\n});\n\n/**\n * Standard API error response schema.\n * Use this for consistent error handling across your API.\n */\nexport const ApiErrorSchema = z.object({\n code: z.string(),\n message: z.string(),\n details: z.array(ErrorDetail).optional(),\n});\n\n/**\n * Generic envelope wrapper for API responses.\n * Provides consistent structure with success flag, data, error, and metadata.\n *\n * @param inner - Zod schema for the response data\n * @returns Envelope schema wrapping the inner schema\n *\n * @example\n * ```ts\n * const UserResponseSchema = Envelope(z.object({ id: Id, name: z.string() }));\n *\n * // Response structure:\n * // {\n * // success: true,\n * // data: { id: 1, name: 'John' },\n * // meta: { requestId: '...' }\n * // }\n * ```\n */\nexport const Envelope = <T extends z.ZodType>(inner: T) =>\n z.object({\n success: z.boolean(),\n data: inner.optional().nullable(),\n error: ApiErrorSchema.optional(),\n meta: Meta.optional(),\n });\n"],"mappings":"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"}
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../lib/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,QAAQ;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../lib/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,QAAQ;IAClB,KAAK,UAAU;IACf,IAAI,SAAS;IACb,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC5B;AAED;;;GAGG;AACH,qBAAa,aAAc,YAAW,MAAM;IAC9B,OAAO,CAAC,QAAQ;gBAAR,QAAQ,GAAE,QAAwB;IAEtD,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;CAiC3B;AAED;;;GAGG;AACH,qBAAa,UAAW,YAAW,MAAM;IACvC,GAAG,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI;CAG5B;AAED;;GAEG;AACH,qBAAa,UAAU;IACT,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAElC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAS/D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAS9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAS9D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAS/E"}
|
package/dist/metrics.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../lib/metrics.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../lib/metrics.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;CACxC;AAED;;GAEG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;IAC3D,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;CAGxC;AAED;;;GAGG;AACH,qBAAa,wBAAyB,YAAW,gBAAgB;IAC/D,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,UAAU,SAAO;IAI7B,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAOtC;;OAEG;IACH,UAAU,IAAI,cAAc,EAAE;IAI9B;;OAEG;IACH,UAAU,IAAI;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;KACvB;IA0BD;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;GAEG;AACH,qBAAa,uBAAwB,YAAW,gBAAgB;IAC9D,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;CAGvC"}
|