@tahanabavi/typefetch 1.3.0 → 1.4.1

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/index.d.ts CHANGED
@@ -1,56 +1,158 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  /**
4
- * Generic request schema:
5
- *
6
- * request: z.object({
7
- * path: z.object({ ... }).optional(),
8
- * query: z.object({ ... }).optional(),
9
- * body: z.object({ ... }).optional(),
10
- * })
4
+ * Base types for Zod schemas representing request and response structures.
5
+ * These are abstract—each concrete endpoint will define its own Zod object for these.
11
6
  */
12
7
  type RequestSchema = z.ZodTypeAny;
13
8
  type ResponseSchema = z.ZodTypeAny;
9
+ /**
10
+ * EndpointDef
11
+ * ============
12
+ * Defines the structure of a **single API endpoint**, including:
13
+ * - HTTP method and path
14
+ * - request/response validation schemas
15
+ * - optional authentication requirement
16
+ * - optional mock or static mock data
17
+ * - optional custom headers and body format
18
+ */
14
19
  type EndpointDef<TReq extends RequestSchema, TRes extends ResponseSchema> = {
20
+ /** HTTP method used by this endpoint */
15
21
  method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
22
+ /** URL path for this endpoint, e.g. "/users/:id" */
16
23
  path: string;
24
+ /** Whether this endpoint requires an Authorization token */
17
25
  auth?: boolean;
26
+ /** Zod schema describing the expected request structure */
18
27
  request: TReq;
28
+ /** Zod schema describing the expected response structure */
19
29
  response: TRes;
30
+ /**
31
+ * Mock data support — enables quick testing or local dev mode:
32
+ * - Either a function returning a mock response object
33
+ * - Or a static mock response object
34
+ */
20
35
  mockData?: (() => z.infer<TRes>) | z.infer<TRes>;
36
+ /**
37
+ * Optional custom headers. Can be:
38
+ * - A fixed record of header key/values
39
+ * - A function returning headers derived from the input data
40
+ */
21
41
  headers?: Record<string, string> | ((input: z.infer<TReq>) => Record<string, string>);
42
+ /**
43
+ * Defines how the request body should be sent:
44
+ * - `"json"` (default): serialized as JSON
45
+ * - `"form-data"`: multipart form
46
+ */
22
47
  bodyType?: "json" | "form-data";
23
48
  };
49
+ /**
50
+ * Contracts
51
+ * =========
52
+ * A collection of modules, each containing one or more endpoints.
53
+ * This defines a **hierarchical API contract**.
54
+ *
55
+ * For example:
56
+ * {
57
+ * users: {
58
+ * getUser: EndpointDef(...),
59
+ * updateUser: EndpointDef(...)
60
+ * },
61
+ * posts: {
62
+ * createPost: EndpointDef(...),
63
+ * listPosts: EndpointDef(...)
64
+ * }
65
+ * }
66
+ */
24
67
  type Contracts = {
25
68
  [ModuleName: string]: {
26
69
  [EndpointName: string]: EndpointDef<RequestSchema, ResponseSchema>;
27
70
  };
28
71
  };
72
+ /**
73
+ * Context passed to all middleware functions.
74
+ * Contains the current request URL and initialization object
75
+ * (headers, method, body, etc.).
76
+ */
29
77
  interface MiddlewareContext {
30
78
  url: string;
31
79
  init: RequestInit;
32
80
  }
81
+ /**
82
+ * The `next()` function type signature used inside middleware.
83
+ * When called, it executes the next function in the chain
84
+ * or finally performs the fetch request.
85
+ */
33
86
  type MiddlewareNext = () => Promise<Response>;
87
+ /**
88
+ * Middleware
89
+ * ==========
90
+ * Defines the standard structure for a request middleware.
91
+ * Middlewares can intercept, modify, or even short-circuit requests.
92
+ *
93
+ * @example Logging Example:
94
+ * const logMiddleware: Middleware = async (ctx, next) => {
95
+ * console.log("Request:", ctx.url);
96
+ * const res = await next();
97
+ * console.log("Response:", res.status);
98
+ * return res;
99
+ * };
100
+ */
34
101
  type Middleware<Options = any> = (ctx: MiddlewareContext, next: MiddlewareNext, options?: Options) => Promise<Response>;
102
+ /**
103
+ * ErrorLike
104
+ * =========
105
+ * Represents the normalized error shape used across the client.
106
+ * Provides consistency for error handling modules such as RichError.
107
+ */
35
108
  type ErrorLike = {
36
109
  message: string;
37
110
  status?: number;
38
111
  code?: string;
39
112
  [key: string]: any;
40
113
  };
114
+ /**
115
+ * Convenience alias that pins both generic types
116
+ * to `z.ZodTypeAny`, simplifying the contract declarations.
117
+ */
41
118
  type EndpointDefZ = EndpointDef<RequestSchema, ResponseSchema>;
42
119
  /**
43
- * For each endpoint we expose a method:
44
- * (input: z.infer<Endpoint["request"]>) => Promise<z.infer<Endpoint["response"]>>
120
+ * RequestOptions
121
+ * ==============
122
+ * Per-request options passed to the client execution:
123
+ * - Optional AbortSignal (for cancellation)
124
+ * - Optional timeout (in milliseconds)
125
+ */
126
+ type RequestOptions = {
127
+ signal?: AbortSignal;
128
+ timeout?: number;
129
+ };
130
+ /**
131
+ * EndpointMethods
132
+ * ================
133
+ * Automatically generated method signatures for all endpoints
134
+ * within a module, based on the Zod contract definitions.
45
135
  *
46
- * So VSCode will show you:
47
- * { path?: { ... }, query?: { ... }, body?: { ... } }
136
+ * Each endpoint method:
137
+ * - Validates input against its request schema
138
+ * - Returns a Promise of the parsed and validated response type
48
139
  */
49
140
  type EndpointMethods<M extends Record<string, EndpointDefZ>> = {
50
- [K in keyof M]: (input: z.infer<M[K]["request"]>) => Promise<z.infer<M[K]["response"]>>;
141
+ [K in keyof M]: (input: z.infer<M[K]["request"]>, // Auto‑derived input type from Zod schema
142
+ options?: RequestOptions) => Promise<z.infer<M[K]["response"]>>;
51
143
  };
144
+ /**
145
+ * TokenProvider
146
+ * =============
147
+ * Specifies the contract for a function that supplies authentication tokens.
148
+ * Can be synchronous or async, e.g. fetching from localStorage or refreshing with an API.
149
+ */
52
150
  type TokenProvider = () => string | Promise<string>;
53
151
 
152
+ /**
153
+ * A richer extension of the native Error class that captures
154
+ * additional API error details such as HTTP status, code, and field errors.
155
+ */
54
156
  declare class RichError extends Error implements ErrorLike {
55
157
  status?: number;
56
158
  code?: string;
@@ -62,7 +164,15 @@ declare class RichError extends Error implements ErrorLike {
62
164
  });
63
165
  }
64
166
  /**
65
- * Strongly-typed HTTP client built from Zod contracts.
167
+ * ApiClient
168
+ * =========
169
+ * A robust, strongly-typed HTTP client that automatically builds API endpoint
170
+ * methods from Zod-based contracts, providing:
171
+ * - Input and response validation via Zod
172
+ * - Middleware support
173
+ * - Token-based authentication
174
+ * - Error normalization (RichError)
175
+ * - Optional caching, retries, and mock data
66
176
  */
67
177
  declare class ApiClient<C extends Contracts, E extends ErrorLike = RichError> {
68
178
  private config;
@@ -74,6 +184,7 @@ declare class ApiClient<C extends Contracts, E extends ErrorLike = RichError> {
74
184
  private mockDelay;
75
185
  private responseWrapper?;
76
186
  private tokenProvider?;
187
+ private retryConfig?;
77
188
  private _modules;
78
189
  constructor(config: {
79
190
  baseUrl: string;
@@ -86,91 +197,64 @@ declare class ApiClient<C extends Contracts, E extends ErrorLike = RichError> {
86
197
  };
87
198
  }, contracts: C);
88
199
  /**
89
- * Builds the strongly-typed `modules` API from the provided contracts.
90
- * Must be called once after constructing the client.
200
+ * Builds all API methods (`modules`) dynamically from the Zod contract definition.
201
+ * After `init()` is called, each endpoint can be invoked through `client.modules.moduleName.endpointName()`.
91
202
  */
92
203
  init(): void;
93
- /**
94
- * Type-safe entrypoint for calling API endpoints.
95
- * Populated by `init()` based on the `contracts` passed to the constructor.
96
- */
204
+ /** Provides access to initialized modules after calling `init()`. */
97
205
  get modules(): { [M in keyof C]: EndpointMethods<C[M]>; };
98
- /**
99
- * Registers a middleware in the pipeline.
100
- * Middlewares are executed in reverse order of registration.
101
- */
206
+ /** Registers a new middleware in the client’s pipeline. */
102
207
  use<T>(middleware: Middleware<T>, options?: T): void;
103
- /**
104
- * Registers a global error handler.
105
- * The handler is invoked for normalized errors before they are re-thrown.
106
- */
208
+ /** Sets a global error handler function to unify error behavior. */
107
209
  onError(handler: (error: E) => void): void;
108
- /**
109
- * Registers a transformation function applied to all successful responses
110
- * after Zod parsing.
111
- */
210
+ /** Defines a global transform function applied to all validated responses. */
112
211
  useResponseTransform(fn: (data: any) => any): void;
113
- /**
114
- * Enables or disables mock mode. When enabled, endpoints with `mockData`
115
- * return mocked responses instead of performing network requests.
116
- */
212
+ /** Configures the retry logic (max attempts, backoff mode, etc.). */
213
+ setRetryConfig(config: ApiClient<C>["retryConfig"]): void;
214
+ /** Provides a custom token provider that returns tokens dynamically. */
215
+ setTokenProvider(provider: TokenProvider): void;
216
+ /** Enables mock responses instead of network requests. */
117
217
  setMockMode(enabled: boolean, delay?: {
118
218
  min: number;
119
219
  max: number;
120
220
  }): void;
121
- /**
122
- * Registers a schema wrapper for APIs that wrap data in an envelope.
123
- * Example: { success, data, message, code, ... }.
124
- */
221
+ /** Registers a wrapper schema for APIs that nest response data (e.g. `{ data, success, message }`). */
125
222
  setResponseWrapper(wrapper: (successResponse: z.ZodTypeAny) => z.ZodTypeAny): void;
126
- /**
127
- * Sets or updates the token provider used for authenticated endpoints.
128
- * Overrides any static token provided in the constructor.
129
- */
130
- setTokenProvider(provider: TokenProvider): void;
131
- /**
132
- * Returns the current token, preferring the tokenProvider if present,
133
- * otherwise falling back to the static token from the constructor.
134
- */
223
+ /** Retrieves the current auth token, using a provider if available. */
135
224
  getCurrentToken(): Promise<string | undefined>;
136
225
  /**
137
- * Executes a single endpoint request.
138
- *
139
- * Expected request shape (new style):
140
- * z.object({
141
- * path: z.object({...}).optional(),
142
- * query: z.object({...}).optional(),
143
- * body: z.any().optional(),
144
- * header: z.object({...}).optional(),
145
- * })
146
- *
147
- * If the parsed request does not contain `path`, `header`,`query` or `body`,
148
- * the entire input is treated as the legacy flat request body.
226
+ * Core request entry point used by auto-generated endpoint methods.
227
+ * Handles caching, deduplication, and mock mode routing.
149
228
  */
150
229
  private request;
151
230
  /**
152
- * Builds final URL and body from endpoint + request input.
153
- * Supports both structured `{ path, query, body, headers }` and legacy flat input.
231
+ * Full HTTP request workflow:
232
+ * - Token injection
233
+ * - Timeout support
234
+ * - Middleware pipeline
235
+ * - Fetch + response handling
236
+ * - Zod parsing + transformation
237
+ * - Caching
154
238
  */
239
+ private performRequestLogic;
240
+ /** Executes a function with retry logic and configurable backoff strategy. */
241
+ private executeWithRetry;
242
+ /** Calculates retry delay intervals for various backoff strategies. */
243
+ private getBackoffDelay;
244
+ /** Builds the final URL and request body from the endpoint definition and input payload. */
155
245
  private buildUrlAndBody;
156
- /**
157
- * Returns a mocked response based on `endpoint.mockData`,
158
- * respecting the configured mock delay and response wrapper.
159
- */
160
- private handleMockRequest;
161
- /**
162
- * Returns a random delay in milliseconds within the current mock delay range.
163
- */
164
- private getRandomDelay;
165
- /**
166
- * Creates a RichError instance from a partial error description.
167
- */
246
+ /** Creates and returns a RichError from details. */
168
247
  private createError;
169
248
  /**
170
- * Normalizes unknown errors into a RichError instance.
171
- * Zod validation errors are converted into a standardized validation error.
249
+ * Converts any thrown error to a standardized RichError instance.
250
+ * Also flattens Zod validation errors into readable messages.
172
251
  */
173
252
  private normalizeError;
253
+ /**
254
+ * Handles mock-mode requests by simulating a delayed network call
255
+ * and returning validated mock data.
256
+ */
257
+ private handleMockRequest;
174
258
  }
175
259
 
176
260
  type LoggingOptions = {
@@ -186,14 +270,99 @@ type RetryOptions = {
186
270
  };
187
271
  declare const retryMiddleware: (options?: RetryOptions) => Middleware;
188
272
 
273
+ /**
274
+ * TokenManagementOptions
275
+ * ======================
276
+ * Configuration structure for supplying credentials to the authentication layer.
277
+ *
278
+ * @property refreshToken A required asynchronous function responsible for
279
+ * obtaining a current, valid access token string. This allows
280
+ * for token fetching from storage or re-issuance upon expiry.
281
+ */
189
282
  type AuthOptions = {
190
283
  refreshToken?: () => Promise<string>;
191
284
  };
285
+ /**
286
+ * AuthenticationInjectorMiddleware
287
+ * ==================================
288
+ * This middleware operates early in the request pipeline to ensure every
289
+ * outgoing request is properly authorized by prepending an Authorization header.
290
+ *
291
+ * Core Logic:
292
+ * -----------
293
+ * 1. It checks if an explicit `refreshToken` supplier was configured in its options.
294
+ * 2. If present, it synchronously calls this supplier to obtain the latest token.
295
+ * 3. The resulting token is formatted as a standard 'Bearer' token and merged
296
+ * into the request's `init.headers`.
297
+ * 4. The request context (`ctx`) is then passed downstream.
298
+ *
299
+ * Note on Error Handling:
300
+ * -----------------------
301
+ * Any failure during the token retrieval process (e.g., if `refreshToken` throws)
302
+ * results in the error being caught, and the request proceeds **without** an
303
+ * Authorization header. This design defers failure response handling to
304
+ * subsequent middleware or the final network fetcher.
305
+ *
306
+ * @param ctx The current request context object, including mutable `init` properties.
307
+ * @param next The function to execute the rest of the middleware chain.
308
+ * @param options The specific configuration passed to this middleware instance.
309
+ *
310
+ * @returns The final `Response` object after the network call completes.
311
+ *
312
+ * @example
313
+ * // Assuming token retrieval logic is defined elsewhere
314
+ * const tokenSupplier = () => fetchTokenFromSecureStorage();
315
+ *
316
+ * client.addInterceptor(
317
+ * authMiddleware({ refreshToken: tokenSupplier })
318
+ * );
319
+ */
192
320
  declare const authMiddleware: Middleware<AuthOptions>;
193
321
 
322
+ /**
323
+ * CacheOptions
324
+ * ============
325
+ * Options for configuring the cache middleware.
326
+ * - `ttl` (Time To Live): Duration (in milliseconds) to keep cached GET responses.
327
+ * After this time, cached data expires and a fresh network call is performed.
328
+ */
194
329
  type CacheOptions = {
195
330
  ttl?: number;
196
331
  };
332
+ /**
333
+ * cacheMiddleware
334
+ * ===============
335
+ * A generic caching middleware for GET requests in the ApiClient.
336
+ * It stores successful responses in memory based on URL and method,
337
+ * returning cached data for subsequent identical requests until the TTL expires.
338
+ *
339
+ * Purpose:
340
+ * --------
341
+ * - Reduces redundant network calls
342
+ * - Improves performance for frequently fetched resources
343
+ * - Useful for lightweight front-end caching (not suitable for sensitive data)
344
+ *
345
+ * Behavior:
346
+ * ---------
347
+ * 1. Only applies to `GET` requests; all other HTTP methods bypass caching.
348
+ * 2. Caches the parsed JSON response in a simple in-memory Map.
349
+ * 3. On subsequent requests:
350
+ * - If the cache entry exists and hasn’t expired, returns a synthetic
351
+ * `Response` object built from cached JSON.
352
+ * - Otherwise performs the network call and refreshes the cache.
353
+ *
354
+ * @param options - Optional cache configuration (TTL in ms)
355
+ *
356
+ * @returns Middleware function compatible with the ApiClient pipeline.
357
+ *
358
+ * @example
359
+ * client.use(
360
+ * cacheMiddleware({ ttl: 120000 }) // cache GET results for 2 minutes
361
+ * );
362
+ *
363
+ * @note Each middleware instance maintains its own internal cache
364
+ * and is memory-scoped (not persistent between reloads).
365
+ */
197
366
  declare const cacheMiddleware: (options?: CacheOptions) => (ctx: MiddlewareContext, next: MiddlewareNext) => Promise<Response>;
198
367
 
199
368
  declare const makeRequestSchema: <TPath extends z.ZodRawShape = {}, TQuery extends z.ZodRawShape = {}, TBody extends z.ZodTypeAny = z.ZodUndefined>() => (defs: {
@@ -218,4 +387,4 @@ declare const makeRequestSchema: <TPath extends z.ZodRawShape = {}, TQuery exten
218
387
  headers: any;
219
388
  }> extends infer T_9 ? { [k_5 in keyof T_9]: T_9[k_5]; } : never>;
220
389
 
221
- export { ApiClient, type AuthOptions, type CacheOptions, type Contracts, type EndpointDef, type EndpointDefZ, type EndpointMethods, type ErrorLike, type LoggingOptions, type Middleware, type MiddlewareContext, type MiddlewareNext, type RequestSchema, type ResponseSchema, type RetryOptions, RichError, type TokenProvider, authMiddleware, cacheMiddleware, loggingMiddleware, makeRequestSchema, retryMiddleware };
390
+ export { ApiClient, type AuthOptions, type CacheOptions, type Contracts, type EndpointDef, type EndpointDefZ, type EndpointMethods, type ErrorLike, type LoggingOptions, type Middleware, type MiddlewareContext, type MiddlewareNext, type RequestOptions, type RequestSchema, type ResponseSchema, type RetryOptions, RichError, type TokenProvider, authMiddleware, cacheMiddleware, loggingMiddleware, makeRequestSchema, retryMiddleware };