@igniter-js/caller 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -1,266 +1,2476 @@
1
- # @igniter-js/caller - AI Agent Instructions
1
+ # AGENTS.md - @igniter-js/caller
2
2
 
3
- > **Package Version:** 0.1.0
4
- > **Last Updated:** 2025-12-14
5
- > **Status:** Ready for Publication
3
+ > **Last Updated:** 2025-12-23
4
+ > **Version:** 0.1.3
5
+ > **Goal:** Complete operational manual for Code Agents maintaining and developing the HTTP client package.
6
6
 
7
7
  ---
8
8
 
9
- ## Package Overview
9
+ ## 1. Package Vision & Context
10
10
 
11
- **Name:** `@igniter-js/caller`
12
- **Purpose:** Type-safe HTTP client for Igniter.js with interceptors, retries, caching, and schema validation
13
- **Type:** Standalone Library (can be used independently or alongside Igniter.js)
11
+ ### 1.1 Purpose and Problem Statement
14
12
 
15
- ### Core Features
13
+ `@igniter-js/caller` is the type-safe HTTP client for the Igniter.js ecosystem. It solves the fundamental problem of making API calls in modern JavaScript applications with full end-to-end type safety, observability, and developer experience.
16
14
 
17
- - Fluent request builder API (`api.get('/users').execute()`)
18
- - axios-style requests (`api.request({ method, url, body })`)
19
- - Auto content-type detection (JSON, XML, Blob, Stream, etc.)
20
- - Request and response interceptors
21
- - Retry support (linear/exponential backoff + status-based retries)
22
- - Caching (in-memory + optional persistent store adapter)
23
- - Global response events (observe responses across the app)
24
- - Schema validation using `StandardSchemaV1` from `@igniter-js/core`
25
- - Optional Zod response validation via `responseType(zodSchema)`
26
- - Auto-conversion of GET body to query params
15
+ **Core Problems Solved:**
16
+
17
+ 1. **Type Safety Gap:** Most HTTP libraries require manual type definitions for requests and responses, leading to drift between API contracts and implementation.
18
+
19
+ 2. **Observability Silos:** HTTP requests are often "black boxes" in application monitoring, making debugging production issues difficult.
20
+
21
+ 3. **Caching Complexity:** Implementing caching for HTTP responses requires significant boilerplate and careful synchronization.
22
+
23
+ 4. **Retry Logic Reinvention:** Every application reimplements retry strategies for transient failures.
24
+
25
+ 5. **Schema Validation Overhead:** Integrating runtime schema validation (Zod, Valibot, etc.) requires custom middleware.
26
+
27
+ ### 1.2 Design Philosophy
28
+
29
+ The package follows these core principles:
30
+
31
+ - **Fetch-First:** Uses the global `fetch` API, working seamlessly in Node.js 18+, Bun, Deno, and modern browsers without polyfills.
32
+
33
+ - **Type-Safety by Default:** Leverages TypeScript's type system to infer request bodies, path parameters, and response types from schema definitions.
34
+
35
+ - **Observability Built-In:** Telemetry and logging are integrated from the ground up, not bolted on later.
36
+
37
+ - **Immutability First:** Builders use the immutable state pattern, preventing accidental mutation during request construction.
38
+
39
+ - **Schema Agnostic:** Supports any `StandardSchemaV1` implementation (Zod v4+, Valibot, ArkType, etc.).
40
+
41
+ - **Zero Runtime Dependency:** No external HTTP libraries—just `fetch` and `AbortController`.
42
+
43
+ ### 1.3 Position in Igniter.js Ecosystem
44
+
45
+ `@igniter-js/caller` is a standalone library that can be used independently or alongside other Igniter.js packages. It integrates with:
46
+
47
+ - **`@igniter-js/common`:** Uses `IgniterError` base class, `IgniterLogger` interface, and `StandardSchemaV1` type.
48
+
49
+ - **`@igniter-js/telemetry`:** Emits structured telemetry events for request lifecycle monitoring.
50
+
51
+ - **`@igniter-js/store`:** (Optional) Can use Igniter Store adapters for persistent caching.
52
+
53
+ ### 1.4 Client-Safe Design
54
+
55
+ Unlike most Igniter.js packages, `@igniter-js/caller` is **explicitly designed to work in both server and client environments**. It does NOT have a server-only shim because HTTP clients are a valid use case for browser applications.
56
+
57
+ Additional constraints for client usage:
58
+
59
+ - The React layer is exported from `@igniter-js/caller/client` to avoid bundling React into non-React consumers.
60
+ - If a caller instance is configured with server-only store adapters or telemetry managers, do not bundle that instance in browser code. Create a browser-safe instance instead.
27
61
 
28
62
  ---
29
63
 
30
- ## Architecture
64
+ ## I. MAINTAINER GUIDE (Internal Architecture)
65
+
66
+ ### 2. FileSystem Topology (Maintenance)
67
+
68
+ The source code is organized into clear functional domains. This section maps every file and folder to its responsibility.
69
+
70
+ ```
71
+ packages/caller/src/
72
+ ├── index.ts # Main entry point barrel
73
+ ├── builders/ # Builder pattern implementations
74
+ │ ├── index.ts # Builder exports barrel
75
+ │ ├── main.builder.ts # IgniterCallerBuilder (package initializer)
76
+ │ ├── main.builder.spec.ts # Builder type inference tests
77
+ │ ├── request.builder.ts # IgniterCallerRequestBuilder (request lifecycle)
78
+ │ ├── schema.builder.ts # IgniterCallerSchema (schema registry builder)
79
+ │ ├── schema.builder.spec.ts # Schema builder tests
80
+ │ └── schema-path.builder.ts # IgniterCallerSchemaPathBuilder (path+methods)
81
+ ├── client/ # React client layer (Provider + hooks)
82
+ │ ├── index.ts # React client entrypoint barrel
83
+ │ ├── builders/ # Provider + hook factories
84
+ │ ├── interfaces/ # Client-specific types
85
+ │ └── utils/ # Client-only helpers (cache, keys, cookies)
86
+ ├── mock/ # Typed mock registry for schemas
87
+ │ ├── index.ts # Mock exports barrel
88
+ │ ├── main.builder.ts # IgniterCallerMockBuilder
89
+ │ └── manager.ts # IgniterCallerMockManager
90
+ ├── core/ # Runtime execution layer
91
+ │ ├── index.ts # Core exports barrel
92
+ │ ├── manager.ts # IgniterCallerManager (HTTP client runtime)
93
+ │ ├── manager.spec.ts # Manager runtime tests
94
+ │ └── events.ts # IgniterCallerEvents (global event emitter)
95
+ ├── errors/ # Error handling
96
+ │ ├── index.ts # Error exports barrel
97
+ │ └── caller.error.ts # IgniterCallerError class
98
+ ├── telemetry/ # Telemetry definitions
99
+ │ └── index.ts # IgniterCallerTelemetryEvents registry
100
+ ├── types/ # Type definitions (pure contracts)
101
+ │ ├── index.ts # Type exports barrel
102
+ │ ├── builder.ts # Builder state and params types
103
+ │ ├── events.ts # Event callback and pattern types
104
+ │ ├── http.ts # HTTP method constants
105
+ │ ├── infer.ts # Type inference helpers
106
+ │ ├── interceptors.ts # Request/response interceptor types
107
+ │ ├── manager.ts # Manager interface contract
108
+ │ ├── request.ts # Request configuration types
109
+ │ ├── response.ts # Response and content type types
110
+ │ ├── retry.ts # Retry configuration types
111
+ │ ├── schema-builder.ts # Schema builder helper types
112
+ │ ├── schemas.ts # Schema map and endpoint types
113
+ │ └── store.ts # Store adapter types
114
+ └── utils/ # Utility functions
115
+ ├── index.ts # Utility exports barrel
116
+ ├── body.ts # Body normalization utilities
117
+ ├── body.spec.ts # Body utility tests
118
+ ├── cache.ts # Cache utilities (in-memory + store)
119
+ ├── cache.spec.ts # Cache utility tests
120
+ ├── schema.ts # Schema matching and validation
121
+ ├── schema.spec.ts # Schema utility tests
122
+ ├── testing.ts # Testing helpers
123
+ ├── testing.spec.ts # Testing utility tests
124
+ ├── url.ts # URL construction utilities
125
+ └── url.spec.ts # URL utility tests
126
+ ```
127
+
128
+ #### 2.1 Builders Directory (`src/builders/`)
31
129
 
32
- This package is intentionally small and split into predictable layers:
130
+ **Purpose:** Implements the fluent builder pattern for configuration and request construction.
33
131
 
34
- - **Core runtime**: `src/core/igniter-caller.ts`
35
- - **Builders**: `src/builder/*`
36
- - **Types**: `src/types/*`
37
- - **Errors**: `src/errors/*`
38
- - **Utilities**: `src/utils/*`
132
+ | File | Responsibility |
133
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
134
+ | `main.builder.ts` | `IgniterCallerBuilder` class - package initialization with immutable state accumulation. Handles baseURL, headers, cookies, interceptors, store, schemas, and telemetry configuration. |
135
+ | `request.builder.ts` | `IgniterCallerRequestBuilder` class - per-request builder. Manages request lifecycle including URL, body, params, headers, timeout, cache, retry, fallback, and response type. |
136
+ | `schema.builder.ts` | `IgniterCallerSchema` class - schema registry builder with `$Infer` type helpers and `get` runtime helpers. Prevents duplicate keys and paths. |
137
+ | `schema-path.builder.ts` | `IgniterCallerSchemaPathBuilder` class - path-first fluent API for defining HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD) on a path. Provides `ref()` helper for registry references. |
39
138
 
40
- ### Design Principles
139
+ #### 2.2 Client Directory (`src/client/`)
41
140
 
42
- 1. **Fetch-first**
43
- - Uses the global `fetch` API.
44
- - Works in Node.js (18+), Bun, Deno, and modern browsers.
141
+ **Purpose:** React-specific layer that provides Provider + hooks for multiple callers.
45
142
 
46
- 2. **Auto content-type detection**
47
- - Response parsing is automatic based on `Content-Type` header.
48
- - JSON, XML, CSV → parsed and validated if schema provided.
49
- - Blob, Stream, ArrayBuffer returned as-is, no validation.
143
+ | Folder/File | Responsibility |
144
+ | -------------------- | -------------------------------------------------------------------------------------------------------- |
145
+ | `builders/` | `IgniterCallerProvider`, `useIgniterCaller`, and hook factories for `useQuery`/`useMutate`. |
146
+ | `interfaces/` | Client-only types for config, hooks, context, and the wrapper client API. |
147
+ | `utils/` | Client-only helpers (query keys, in-memory cache, cookie header merge). |
148
+ | `index.ts` | Client entrypoint barrel used by `@igniter-js/caller/client`. |
50
149
 
51
- 3. **Typed error surface**
52
- - Predictable errors are `IgniterCallerError` with stable error codes.
150
+ #### 2.3 Core Directory (`src/core/`)
53
151
 
54
- 4. **Separation of concerns**
55
- - `IgniterCallerRequestBuilder` focuses on request composition and execution.
56
- - `IgniterCallerCacheUtils` focuses on caching.
57
- - `IgniterCallerSchemaUtils` focuses on schema matching and validation.
152
+ **Purpose:** Runtime execution and event handling.
58
153
 
59
- 5. **Stable public API**
60
- - Keep exports in `src/index.ts` stable and backwards-compatible.
154
+ | File | Responsibility |
155
+ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
156
+ | `manager.ts` | `IgniterCallerManager` class - the main HTTP client runtime. Creates request builders, executes direct requests, manages global event emission, and provides static methods for cache invalidation and batch requests. |
157
+ | `events.ts` | `IgniterCallerEvents` class - global event emitter supporting exact URL matches and RegExp patterns. Handles listener registration, cleanup, and error-safe emission. |
158
+
159
+ #### 2.4 Types Directory (`src/types/`)
160
+
161
+ **Purpose:** Pure TypeScript contracts—no implementation code.
162
+
163
+ | File | Responsibility |
164
+ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
165
+ | `builder.ts` | `IgniterCallerBuilderState` (builder state), `IgniterCallerRequestBuilderParams` (request builder constructor params), `IgniterCallerMethodRequestBuilder` (public builder type). |
166
+ | `events.ts` | `IgniterCallerEventCallback` (listener signature), `IgniterCallerUrlPattern` (string or RegExp). |
167
+ | `http.ts` | `IgniterCallerHttpMethod` union type. |
168
+ | `infer.ts` | Type inference helpers: `InferSuccessResponse`, `GetEndpoint`, `InferResponse`, `TypedRequestBuilder`. |
169
+ | `interceptors.ts` | `IgniterCallerRequestInterceptor`, `IgniterCallerResponseInterceptor`. |
170
+ | `manager.ts` | `IIgniterCallerManager` interface - public contract for the manager. |
171
+ | `request.ts` | `IgniterCallerBaseRequestOptions`, `IgniterCallerRequestOptions`, `IgniterCallerDirectRequestOptions`. |
172
+ | `response.ts` | `IgniterCallerApiResponse<T>`, `IgniterCallerFileResponse`, `IgniterCallerResponseContentType`, `IgniterCallerValidatableContentType`, `IgniterCallerResponseMarker`. |
173
+ | `retry.ts` | `IgniterCallerRetryOptions`. |
174
+ | `schema-builder.ts` | Complex types for schema builder: `IgniterCallerSchemaRegistry`, `IgniterCallerSchemaEndpointConfig`, schema wrapper types (`SchemaArray`, `SchemaNullable`, `SchemaOptional`, `SchemaRecord`), inference helpers (`IgniterCallerSchemaInfer`, `IgniterCallerSchemaGetters`), build result type. |
175
+ | `schemas.ts` | Core schema types: `IgniterCallerSchemaMethod`, `IgniterCallerEndpointSchema`, `IgniterCallerSchemaMap`, path extraction types (`ExtractPathParams`), inference types (`InferRequestType`, `InferResponseType`, `InferSuccessResponseType`, `InferAllResponseTypes`), path filtering types (`GetPaths`, `PostPaths`, etc.), endpoint info types, validation options. |
176
+ | `store.ts` | `IgniterCallerStoreAdapter<TClient>`, `IgniterCallerStoreOptions`. |
177
+
178
+ #### 2.5 Utils Directory (`src/utils/`)
179
+
180
+ **Purpose:** Pure functions for specific operations.
181
+
182
+ | File | Responsibility |
183
+ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
184
+ | `body.ts` | `IgniterCallerBodyUtils` - detects raw body types (FormData, Blob, ArrayBuffer, etc.), normalizes headers for FormData (removes Content-Type). |
185
+ | `cache.ts" | `IgniterCallerCacheUtils` - in-memory Map-based cache with optional store adapter fallback. Handles get/set/clear with TTL and glob pattern invalidation. |
186
+ | `schema.ts` | `IgniterCallerSchemaUtils` - path matching with param extraction (`matchPath`), schema lookup (`findSchema`), StandardSchemaV1 validation (`validateWithStandardSchema`), request/response validation with strict/soft modes. |
187
+ | `testing.ts` | Testing helpers for mocking and assertions. |
188
+ | `url.ts` | `IgniterCallerUrlUtils` - builds full URLs with base URL concatenation and query parameter encoding using `URLSearchParams`. |
61
189
 
62
190
  ---
63
191
 
64
- ## API Design
192
+ ### 3. Architecture Deep-Dive
193
+
194
+ The package follows a **Builder → Manager → Request Builder → Execution** pattern with immutable state accumulation throughout.
65
195
 
66
- ### HTTP Methods
196
+ #### 3.1 Architectural Layers
67
197
 
68
- HTTP methods accept an optional URL directly and return a builder **without** the `.method()` function (since method is already set):
198
+ ```
199
+ ┌─────────────────────────────────────────────────────────────────────────┐
200
+ │ API Surface │
201
+ │ IgniterCaller.create().withX().build() │
202
+ └────────────────────────┬────────────────────────────────────────────────┘
203
+
204
+
205
+ ┌─────────────────────────────────────────────────────────────────────────┐
206
+ │ Builder Layer │
207
+ │ IgniterCallerBuilder (Immutable State Accumulation) │
208
+ │ ┌─────────────────────────────────────────────────────────────┐ │
209
+ │ │ State: │ │
210
+ │ │ - baseURL, headers, cookies │ │
211
+ │ │ - requestInterceptors, responseInterceptors │ │
212
+ │ │ - store, storeOptions │ │
213
+ │ │ - schemas, schemaValidation │ │
214
+ │ │ - logger, telemetry │ │
215
+ │ └─────────────────────────────────────────────────────────────┘ │
216
+ └────────────────────────┬────────────────────────────────────────────────┘
217
+ │ .build()
218
+
219
+ ┌─────────────────────────────────────────────────────────────────────────┐
220
+ │ Manager Layer │
221
+ │ IgniterCallerManager (Runtime) │
222
+ │ - get/post/put/patch/delete/head() methods │
223
+ │ - request() method (axios-style) │
224
+ │ - Static methods: batch(), on(), off(), invalidate() │
225
+ │ - Global event emission via IgniterCallerEvents │
226
+ └────────────────────────┬────────────────────────────────────────────────┘
227
+ │ Creates
228
+
229
+ ┌─────────────────────────────────────────────────────────────────────────┐
230
+ │ Request Builder Layer │
231
+ │ IgniterCallerRequestBuilder (Per-Request Configuration) │
232
+ │ ┌─────────────────────────────────────────────────────────────┐ │
233
+ │ │ Configuration: │ │
234
+ │ │ - url, method, body, params │ │
235
+ │ │ - headers (merged with defaults) │ │
236
+ │ │ - timeout, cache, staleTime │ │
237
+ │ │ - retry options │ │
238
+ │ │ - fallback function │ │
239
+ │ │ - responseType (schema or type marker) │ │
240
+ │ └─────────────────────────────────────────────────────────────┘ │
241
+ └────────────────────────┬────────────────────────────────────────────────┘
242
+ │ .execute()
243
+
244
+ ┌─────────────────────────────────────────────────────────────────────────┐
245
+ │ Execution Layer (Internal) │
246
+ │ 1. Cache Check (if staleTime set) │
247
+ │ 2. Request Interceptor Chain │
248
+ │ 3. Request Body Validation (if schema configured) │
249
+ │ 4. Fetch with Retry Logic │
250
+ │ 5. Response Parsing (Content-Type auto-detect) │
251
+ │ 6. Response Validation (if schema configured) │
252
+ │ 7. Response Interceptor Chain │
253
+ │ 8. Cache Store (if successful) │
254
+ │ 9. Fallback (if failed and fallback set) │
255
+ │ 10. Telemetry Emission │
256
+ │ 11. Global Event Emission │
257
+ └─────────────────────────────────────────────────────────────────────────┘
258
+ ```
259
+
260
+ #### 3.2 Immutable State Pattern
261
+
262
+ The `IgniterCallerBuilder` implements strict immutability:
69
263
 
70
264
  ```typescript
71
- // These are equivalent:
72
- api.get('/users').execute()
73
- api.get().url('/users').execute()
265
+ // CORRECT: Each with* method returns new instance
266
+ .withBaseUrl(url) // returns new IgniterCallerBuilder<TSchemas>
267
+ .withHeaders(headers) // returns new IgniterCallerBuilder<TSchemas>
268
+ .withSchemas(schemas) // returns new IgniterCallerBuilder<TNewSchemas>
74
269
 
75
- // POST, PUT, PATCH, DELETE, HEAD work the same way:
76
- api.post('/users').body({ name: 'John' }).execute()
77
- api.delete('/users/1').execute()
270
+ // INCORRECT: Mutating state is impossible
271
+ // The constructor is private, and all methods return new instances.
78
272
  ```
79
273
 
80
- ### axios-style Requests
274
+ This ensures that builder instances can be reused safely and that configuration changes don't affect previously built managers.
275
+
276
+ #### 3.3 Schema Builder Architecture
277
+
278
+ The schema builder uses a **two-phase registry system**:
279
+
280
+ 1. **Registry Phase:** Reusable schemas are registered via `.schema(key, schema)`. This allows referencing schemas multiple times across endpoints.
281
+
282
+ 2. **Path Phase:** Endpoints are defined via `.path(path, builder)` with method-specific configurations.
283
+
284
+ The `.ref(key)` helper provides:
285
+
286
+ - `schema` - direct reference to registered schema
287
+ - `array()` - wraps in Zod array
288
+ - `nullable()` - wraps in Zod nullable
289
+ - `optional()` - wraps in Zod optional
290
+ - `record()` - wraps in Zod record
291
+
292
+ After `.build()`, the result includes:
293
+
294
+ - `$Infer` - Type-level inference helpers (Path, Endpoint, Request, Response, Responses, Schema)
295
+ - `get` - Runtime helpers (path, endpoint, request, response, schema)
296
+
297
+ #### 3.4 Event System Architecture
298
+
299
+ The `IgniterCallerEvents` class manages a dual-listener system:
300
+
301
+ - **Exact Match Listeners:** Stored in `Map<string, Set<Callback>>` for URL string patterns.
302
+ - **Pattern Listeners:** Stored in `Map<RegExp, Set<Callback>>` for RegExp patterns.
303
+
304
+ Emission flow:
305
+
306
+ 1. Emit event with URL and result
307
+ 2. Check all exact match listeners for the URL
308
+ 3. Check all pattern listeners - execute if RegExp matches
309
+ 4. All listeners are called with the result and a context object (url, method, timestamp)
310
+
311
+ The event system is static on `IgniterCallerManager`, enabling global observation across all manager instances.
312
+
313
+ ---
314
+
315
+ ### 4. Operational Flow Mapping (Pipelines)
316
+
317
+ This section provides step-by-step flow documentation for every public method.
318
+
319
+ #### 4.1 Method: `IgniterCallerBuilder.withBaseUrl(url)`
320
+
321
+ **Purpose:** Sets the base URL prefix for all requests.
322
+
323
+ **Flow:**
324
+
325
+ 1. **Input Validation:** None (string is accepted directly).
326
+ 2. **State Copy:** Creates new `IgniterCallerBuilder` instance with `...this.state, baseURL: url`.
327
+ 3. **Return:** Returns the new builder instance.
328
+
329
+ **Telemetry Emitted:** None (builder methods don't emit telemetry).
330
+
331
+ **Logging:** None (builder methods don't log).
332
+
333
+ ---
334
+
335
+ #### 4.2 Method: `IgniterCallerBuilder.withHeaders(headers)`
336
+
337
+ **Purpose:** Merges default headers into every request.
338
+
339
+ **Flow:**
340
+
341
+ 1. **Input Validation:** None (Record<string, string> accepted).
342
+ 2. **State Merge:** Creates new builder with merged headers (shallow merge).
343
+ 3. **Return:** Returns new builder instance.
344
+
345
+ **Note:** Headers are merged shallow—nested objects are not deeply merged.
346
+
347
+ ---
348
+
349
+ #### 4.3 Method: `IgniterCallerBuilder.withCookies(cookies)`
350
+
351
+ **Purpose:** Sets default cookies sent as the `Cookie` header.
352
+
353
+ **Flow:**
354
+
355
+ 1. **Input Validation:** None.
356
+ 2. **State Copy:** Creates new builder with `cookies` property.
357
+ 3. **Return:** Returns new builder instance.
358
+
359
+ **Runtime Behavior:** During request construction, cookies are serialized to `key=value; key2=value2` format and set in the `Cookie` header.
360
+
361
+ ---
362
+
363
+ #### 4.4 Method: `IgniterCallerBuilder.withLogger(logger)`
364
+
365
+ **Purpose:** Attaches an `IgniterLogger` instance for request lifecycle logging.
366
+
367
+ **Flow:**
368
+
369
+ 1. **Input Validation:** None (must implement `IgniterLogger` interface).
370
+ 2. **State Copy:** Creates new builder with `logger` property.
371
+ 3. **Return:** Returns new builder instance.
372
+
373
+ **Logging Behavior:**
374
+
375
+ - Request started: `debug` with method, url, baseURL
376
+ - Request success: `info` with method, url, durationMs, status
377
+ - Request failed: `error` with method, url, durationMs, error
378
+
379
+ ---
380
+
381
+ #### 4.5 Method: `IgniterCallerBuilder.withRequestInterceptor(interceptor)`
382
+
383
+ **Purpose:** Adds a function that modifies request options before execution.
384
+
385
+ **Flow:**
386
+
387
+ 1. **Input Validation:** None (must be callable with correct signature).
388
+ 2. **Array Accumulation:** Appends to existing `requestInterceptors` array.
389
+ 3. **State Copy:** Creates new builder with updated array.
390
+ 4. **Return:** Returns new builder instance.
391
+
392
+ **Execution Order:** Interceptors run in the order they were registered (FIFO).
393
+
394
+ ---
395
+
396
+ #### 4.6 Method: `IgniterCallerBuilder.withResponseInterceptor(interceptor)`
397
+
398
+ **Purpose:** Adds a function that transforms responses after execution.
399
+
400
+ **Flow:**
401
+
402
+ 1. **Input Validation:** None.
403
+ 2. **Array Accumulation:** Appends to existing `responseInterceptors` array.
404
+ 3. **State Copy:** Creates new builder with updated array.
405
+ 4. **Return:** Returns new builder instance.
406
+
407
+ **Execution Order:** Interceptors run in the order they were registered (FIFO).
408
+
409
+ ---
410
+
411
+ #### 4.7 Method: `IgniterCallerBuilder.withStore(store, options)`
412
+
413
+ **Purpose:** Configures a persistent store adapter for caching (e.g., Redis).
414
+
415
+ **Flow:**
416
+
417
+ 1. **Input Validation:** None.
418
+ 2. **State Copy:** Creates new builder with `store` and `storeOptions` properties.
419
+ 3. **Return:** Returns new builder instance.
420
+
421
+ **Runtime Behavior:**
422
+
423
+ - On `build()`: Calls `IgniterCallerCacheUtils.setStore(store, options)`
424
+ - Store operations fallback to in-memory cache on failure
425
+
426
+ ---
427
+
428
+ #### 4.8 Method: `IgniterCallerBuilder.withSchemas(schemas, validation)`
429
+
430
+ **Purpose:** Configures schema-based type safety and runtime validation.
81
431
 
82
- The `.request()` method executes immediately with all options:
432
+ **Flow:**
433
+
434
+ 1. **Input Validation:** Type-level validation ensures schemas conform to `IgniterCallerSchemaInput`.
435
+ 2. **State Copy:** Creates new builder with `schemas` and `schemaValidation` properties.
436
+ 3. **Type Narrowing:** Returns builder narrowed to new schema map type `IgniterCallerSchemaMapFrom<TNewSchemas>`.
437
+ 4. **Return:** Returns typed builder instance.
438
+
439
+ **Validation Options:**
440
+
441
+ - `mode`: `'strict'` (throw on failure), `'soft'` (log and continue), `'off'` (skip)
442
+ - `onValidationError`: Custom error handler callback
443
+
444
+ ---
445
+
446
+ #### 4.9 Method: `IgniterCallerBuilder.withTelemetry(telemetry)`
447
+
448
+ **Purpose:** Attaches `IgniterTelemetryManager` for request observability.
449
+
450
+ **Flow:**
451
+
452
+ 1. **Input Validation:** None.
453
+ 2. **State Copy:** Creates new builder with `telemetry` property.
454
+ 3. **Return:** Returns new builder instance.
455
+
456
+ **Telemetry Emitted:**
457
+
458
+ - `request.execute.started`
459
+ - `request.execute.success`
460
+ - `request.execute.error`
461
+ - `request.timeout.error`
462
+ - `cache.read.hit`
463
+ - `retry.attempt.started`
464
+ - `validation.request.error`
465
+ - `validation.response.error`
466
+
467
+ ---
468
+
469
+ #### 4.10 Method: `IgniterCallerBuilder.build()`
470
+
471
+ **Purpose:** Creates the `IgniterCallerManager` instance.
472
+
473
+ **Flow:**
474
+
475
+ 1. **Store Configuration:** If `this.state.store` is set, calls `IgniterCallerCacheUtils.setStore()`.
476
+ 2. **Manager Instantiation:** Creates `new IgniterCallerManager(this.state.baseURL, { ...config })`.
477
+ 3. **Initialization Logging:** Logs manager creation with metadata (baseURL, hasTelemetry, hasStore, hasSchemas).
478
+ 4. **Return:** Returns manager instance.
479
+
480
+ ---
481
+
482
+ #### 4.11 Method: `IgniterCallerManager.get(url)` and similar HTTP methods
483
+
484
+ **Purpose:** Creates a typed request builder for a specific HTTP method.
485
+
486
+ **Flow:**
487
+
488
+ 1. **Builder Params Creation:** Calls `createBuilderParams()` to extract shared configuration (baseURL, headers, cookies, logger, telemetry, interceptors, eventEmitter, schemas, validation).
489
+ 2. **Request Builder Instantiation:** Creates `new IgniterCallerRequestBuilder(params)`.
490
+ 3. **Method Assignment:** Calls `_setMethod('GET')` (internal method).
491
+ 4. **URL Assignment:** If URL is provided, calls `_setUrl(url)`.
492
+ 5. **Type Narrowing:**
493
+ - If URL matches a schema path, returns `TypedRequestBuilder` with inferred types
494
+ - Otherwise, returns `IgniterCallerTypedRequestBuilder` with generic response type
495
+ 6. **Return:** Returns typed request builder.
496
+
497
+ **Type Inference:**
498
+
499
+ - When URL matches a schema path, `TypedRequestBuilder` provides typed `body()` and `params()` methods.
500
+ - Response type is inferred from the schema's success status (200 or 201).
501
+
502
+ ---
503
+
504
+ #### 4.12 Method: `IgniterCallerManager.request(options)`
505
+
506
+ **Purpose:** Executes a request directly with all options in one object (axios-style).
507
+
508
+ **Flow:**
509
+
510
+ 1. **Builder Instantiation:** Creates `new IgniterCallerRequestBuilder(params)` with merged headers.
511
+ 2. **Method Assignment:** Calls `_setMethod(options.method)`.
512
+ 3. **URL Assignment:** Calls `_setUrl(options.url)`.
513
+ 4. **Body Assignment:** If `options.body` is provided, calls `body(options.body)`.
514
+ 5. **Params Assignment:** If `options.params` is provided, calls `params(options.params)`.
515
+ 6. **Timeout Assignment:** If `options.timeout` is provided, calls `timeout(options.timeout)`.
516
+ 7. **Cache Assignment:** If `options.cache` is provided, calls `cache(options.cache, options.cacheKey)`.
517
+ 8. **Stale Time Assignment:** If `options.staleTime` is provided, calls `stale(options.staleTime)`.
518
+ 9. **Retry Assignment:** If `options.retry` is provided, calls `retry(options.retry.maxAttempts, options.retry)`.
519
+ 10. **Fallback Assignment:** If `options.fallback` is provided, calls `fallback(options.fallback)`.
520
+ 11. **Response Schema Assignment:** If `options.responseSchema` is provided, calls `responseType(options.responseSchema)`.
521
+ 12. **Execution:** Calls `execute()`.
522
+ 13. **Return:** Returns `Promise<IgniterCallerApiResponse<T>>`.
523
+
524
+ ---
525
+
526
+ #### 4.13 Method: `IgniterCallerRequestBuilder.execute()`
527
+
528
+ **Purpose:** Executes the HTTP request with full lifecycle management.
529
+
530
+ **Flow:**
531
+
532
+ 1. **Telemetry (Started):** Emits `request.execute.started` with method, url, baseURL, timeoutMs.
533
+ 2. **Logging (Started):** Logs request started at debug level.
534
+ 3. **Cache Check:**
535
+ - Determines effective cache key (provided key or URL).
536
+ - If `staleTime` is set, calls `IgniterCallerCacheUtils.get()`.
537
+ - If cache hit: emits `cache.read.hit`, returns cached result immediately with success telemetry.
538
+ 4. **Retry Loop:**
539
+ - For attempt = 0 to maxAttempts-1:
540
+ - If attempt > 0: calculate delay (linear or exponential), emit `retry.attempt.started`, wait for delay.
541
+ - Call `executeSingleRequest()`.
542
+ - If success: return result.
543
+ - If error is retryable (status code in `retryOnStatus`): continue loop.
544
+ - Otherwise: break and return error result.
545
+ 5. **Fallback:**
546
+ - If request failed and `fallbackFn` is set:
547
+ - Return result with fallback value as data.
548
+ - Emit success telemetry with `ctx.request.fallback: true`.
549
+ 6. **Cache Store:**
550
+ - If request succeeded and `effectiveCacheKey` is set:
551
+ - Call `IgniterCallerCacheUtils.set()` with data and staleTime.
552
+ 7. **Telemetry (Result):**
553
+ - If error: emit `request.execute.error` with error details.
554
+ - If success: emit `request.execute.success` with duration, status, contentType, cache hit flag.
555
+ 8. **Logging (Result):**
556
+ - If error: log error with duration and error details.
557
+ - If success: log success with duration and status.
558
+ 9. **Global Event Emission:** Calls `eventEmitter(url, method, result)`.
559
+ 10. **Return:** Returns `IgniterCallerApiResponse<TResponse>`.
560
+
561
+ ---
562
+
563
+ #### 4.14 Method: `IgniterCallerRequestBuilder.executeSingleRequest()`
564
+
565
+ **Purpose:** Executes a single HTTP request attempt.
566
+
567
+ **Flow:**
568
+
569
+ 1. **Request Building:** Calls `buildRequest()` to construct fetch options, AbortController, and timeout.
570
+ 2. **URL Resolution:** Calls `resolveUrl()` to build final URL with query params and handle GET body → params conversion.
571
+ 3. **Request Interceptors:**
572
+ - If interceptors are configured, chain them in order.
573
+ - Each interceptor receives and returns `IgniterCallerRequestOptions`.
574
+ - Rebuilds request after interceptor chain.
575
+ 4. **Request Validation:**
576
+ - If schemas are configured and endpoint has request schema:
577
+ - Call `IgniterCallerSchemaUtils.validateRequest()`.
578
+ - If validation fails (strict mode): emit `validation.request.error`, return error result immediately.
579
+ 5. **Fetch Execution:**
580
+ - Call `fetch(url, { ...requestInit, signal: controller.signal })`.
581
+ 6. **Timeout Handling:**
582
+ - Clear timeout on response.
583
+ - If `AbortError`: emit `request.timeout.error`, return timeout error result.
584
+ 7. **HTTP Error Handling:**
585
+ - If `!response.ok`: read error text, return HTTP error result with status.
586
+ 8. **Content Type Detection:**
587
+ - Call `detectContentType(response.headers.get('content-type'))`.
588
+ 9. **Response Parsing:**
589
+ - Call `parseResponseByContentType()` based on detected type (json, xml, csv, text, blob, stream, arraybuffer, formdata).
590
+ 10. **Response Validation (Schema Map):**
591
+ - If schemas are configured and endpoint has response schema for status code:
592
+ - Call `IgniterCallerSchemaUtils.validateResponse()`.
593
+ - If validation fails (strict mode): emit `validation.response.error`, return error result.
594
+ 11. **Response Validation (responseType):**
595
+ - If `responseTypeSchema` is set (Zod or StandardSchema):
596
+ - Validate parsed response data.
597
+ - If validation fails: emit `validation.response.error`, return error result.
598
+ 12. **Response Interceptors:**
599
+ - If interceptors are configured, chain them in order.
600
+ - Each interceptor receives and returns `IgniterCallerApiResponse`.
601
+ 13. **Return:** Returns `IgniterCallerApiResponse` with data, status, headers.
602
+
603
+ ---
604
+
605
+ #### 4.15 Method: `IgniterCallerRequestBuilder.buildRequest()`
606
+
607
+ **Purpose:** Constructs fetch options, AbortController, and timeout.
608
+
609
+ **Flow:**
610
+
611
+ 1. **URL Resolution:** Calls `resolveUrl()` to get final URL.
612
+ 2. **Body Handling:**
613
+ - For GET/HEAD: body is converted to query params in `resolveUrl()`, so body is excluded from fetch.
614
+ - For other methods: body is included.
615
+ - Check if body is raw (FormData, Blob, ArrayBuffer, etc.).
616
+ - Normalize headers (remove Content-Type for FormData).
617
+ 3. **Request Init Construction:**
618
+ ```typescript
619
+ const requestInit: RequestInit = {
620
+ method,
621
+ headers: finalHeaders,
622
+ cache,
623
+ ...(shouldIncludeBody
624
+ ? { body: rawBody ? body : JSON.stringify(body) }
625
+ : {}),
626
+ };
627
+ ```
628
+ 4. **AbortController:** Create new `AbortController`.
629
+ 5. **Timeout Setup:** Set `setTimeout(() => controller.abort(), timeout || 30000)`.
630
+ 6. **Return:** Return `{ url, requestInit, controller, timeoutId }`.
631
+
632
+ ---
633
+
634
+ #### 4.16 Method: `IgniterCallerRequestBuilder.resolveUrl()`
635
+
636
+ **Purpose:** Builds final URL with base URL and query parameters.
637
+
638
+ **Flow:**
639
+
640
+ 1. **GET Body Conversion:**
641
+ - If method is GET or HEAD and body is an object:
642
+ - Convert body properties to query params.
643
+ - Merge with existing params.
644
+ 2. **Base URL Concatenation:**
645
+ - If URL is not absolute (doesn't start with `http://` or `https://`) and baseURL is set:
646
+ - Full URL = baseURL + url
647
+ - Otherwise: Full URL = url
648
+ 3. **Query Parameter Encoding:**
649
+ - Use `URLSearchParams` to encode params.
650
+ - Append to URL with `?` or `&` separator.
651
+ 4. **Return:** Return `{ url: fullUrl, safeUrl: fullUrl.split('?')[0] }`.
652
+
653
+ ---
654
+
655
+ #### 4.17 Method: `IgniterCallerManager.on(pattern, callback)`
656
+
657
+ **Purpose:** Registers a global event listener for API responses.
658
+
659
+ **Flow:**
660
+
661
+ 1. **Listener Registration:**
662
+ - If pattern is string: add to `listeners` Map under exact URL key.
663
+ - If pattern is RegExp: add to `patternListeners` Map under RegExp key.
664
+ 2. **Cleanup Function:**
665
+ - Return a function that removes the specific callback from the listener set.
666
+ - If no callbacks remain for the pattern, remove the pattern entry.
667
+ 3. **Return:** Return cleanup function.
668
+
669
+ ---
670
+
671
+ #### 4.18 Method: `IgniterCallerManager.invalidate(key)`
672
+
673
+ **Purpose:** Invalidates a specific cache entry.
674
+
675
+ **Flow:**
676
+
677
+ 1. **Cache Clear:** Call `IgniterCallerCacheUtils.clear(key)`.
678
+ 2. **Return:** Await cache clear completion.
679
+
680
+ ---
681
+
682
+ #### 4.19 Method: `IgniterCallerManager.batch(requests)`
683
+
684
+ **Purpose:** Executes multiple requests in parallel.
685
+
686
+ **Flow:**
687
+
688
+ 1. **Promise All:** Return `Promise.all(requests)`.
689
+ 2. **Type Preservation:** Return typed array preserving individual result types.
690
+ 3. **Return:** `Promise<{ [K in keyof T]: T[K] extends Promise<infer R> ? R : never }>`
691
+
692
+ ---
693
+
694
+ ### 5. Dependency & Type Graph
695
+
696
+ #### 5.1 External Dependencies
697
+
698
+ | Package | Purpose | Peer Dependency |
699
+ | ----------------------- | --------------------------------------------------- | ---------------------------------------- |
700
+ | `@igniter-js/common` | `IgniterError`, `IgniterLogger`, `StandardSchemaV1` | ✅ Required |
701
+ | `@igniter-js/telemetry` | `IgniterTelemetryManager`, `IgniterTelemetryEvents` | ⚙️ Optional |
702
+ | `zod` | Schema validation (v4+) | ⚙️ Optional (any StandardSchemaV1 works) |
703
+
704
+ #### 5.2 Internal Type Flow
705
+
706
+ ```
707
+ IgniterCallerBuilderState<TSchemas>
708
+
709
+ ├─→ IgniterCallerBuilder<TSchemas>
710
+ │ │
711
+ │ └─→ build()
712
+ │ │
713
+ │ ▼
714
+ │ IgniterCallerManager<TSchemas>
715
+ │ │
716
+ │ ├─→ createBuilderParams()
717
+ │ │ │
718
+ │ │ ▼
719
+ │ │ IgniterCallerRequestBuilderParams
720
+ │ │
721
+ │ ├─→ get/post/put/patch/delete/head()
722
+ │ │ │
723
+ │ │ ▼
724
+ │ │ TypedRequestBuilder<TSchemas, TPath, TMethod>
725
+ │ │ │
726
+ │ │ ├─→ body() / params() (typed from schema)
727
+ │ │ └─→ execute()
728
+ │ │ │
729
+ │ │ ▼
730
+ │ │ IgniterCallerApiResponse<TResponse>
731
+
732
+ IgniterCallerSchema<TSchemas, TRegistry>
733
+
734
+ ├─→ schema() → Registry accumulation
735
+
736
+ ├─→ path() → Path + methods accumulation
737
+
738
+ └─→ build() → IgniterCallerSchemaBuildResult
739
+
740
+ ├─→ $Infer (type helpers)
741
+ │ ├─→ Path
742
+ │ ├─→ Endpoint
743
+ │ ├─→ Request
744
+ │ ├─→ Response
745
+ │ ├─→ Responses
746
+ │ └─→ Schema
747
+
748
+ └─→ get (runtime helpers)
749
+ ├─→ path()
750
+ ├─→ endpoint()
751
+ ├─→ request()
752
+ ├─→ response()
753
+ └─→ schema()
754
+ ```
755
+
756
+ #### 5.3 Schema Type Resolution Flow
757
+
758
+ ```
759
+ IgniterCallerSchemaMap
760
+
761
+ ├─→ SchemaMapPaths<TSchemas> = keyof TSchemas
762
+
763
+ ├─→ GetPaths<TSchemas> = PathsForMethod<TSchemas, 'GET'>
764
+ ├─→ PostPaths<TSchemas> = PathsForMethod<TSchemas, 'POST'>
765
+ ├─→ PutPaths<TSchemas> = PathsForMethod<TSchemas, 'PUT'>
766
+ ├─→ PatchPaths<TSchemas> = PathsForMethod<TSchemas, 'PATCH'>
767
+ ├─→ DeletePaths<TSchemas> = PathsForMethod<TSchemas, 'DELETE'>
768
+ ├─→ HeadPaths<TSchemas> = PathsForMethod<TSchemas, 'HEAD'>
769
+
770
+ ├─→ SchemaMapEndpoint<TSchemas, TPath, TMethod> = TSchemas[TPath][TMethod]
771
+
772
+ ├─→ SchemaMapRequestType<TSchemas, TPath, TMethod>
773
+ │ (Infers from TSchemas[TPath][TMethod].request)
774
+
775
+ └─→ SchemaMapResponseType<TSchemas, TPath, TMethod, TStatus>
776
+ (Infers from TSchemas[TPath][TMethod].responses[TStatus])
777
+ ```
778
+
779
+ ---
780
+
781
+ ### 6. Contribution Checklist
782
+
783
+ This section provides a step-by-step guide for making changes to the package.
784
+
785
+ #### 6.1 Adding a New Builder Configuration Option
786
+
787
+ 1. **Define Type:** Add the new property to `IgniterCallerBuilderState` in `src/types/builder.ts`.
788
+ 2. **Add Builder Method:** Implement `withNewOption(value)` in `src/builders/main.builder.ts` that returns a new builder instance.
789
+ 3. **Update Constructor Params:** Add to `IgniterCallerRequestBuilderParams` in `src/types/builder.ts`.
790
+ 4. **Update Manager:** Pass the new option to request builder params in `createBuilderParams()`.
791
+ 5. **Write Tests:** Add type inference tests in `src/builders/main.builder.spec.ts`.
792
+ 6. **Update Docs:** Add to this AGENTS.md and README.md.
793
+
794
+ #### 6.2 Adding a New HTTP Method
795
+
796
+ 1. **Add to Type Union:** Add the method string to `IgniterCallerHttpMethod` in `src/types/http.ts`.
797
+ 2. **Add to Schema Types:** Add to `IgniterCallerSchemaMethod` union and corresponding `XPaths` type in `src/types/schemas.ts`.
798
+ 3. **Implement Manager Method:** Add method in `src/core/manager.ts` (e.g., `options(url)`).
799
+ 4. **Update Schema Path Builder:** Add method in `src/builders/schema-path.builder.ts`.
800
+ 5. **Update Interface:** Add method signature to `IIgniterCallerManager` in `src/types/manager.ts`.
801
+ 6. **Write Tests:** Add tests in `src/core/manager.spec.ts`.
802
+ 7. **Update Docs:** Add to API reference.
803
+
804
+ #### 6.3 Adding a New Telemetry Event
805
+
806
+ 1. **Define Schema:** Add schema definition in `src/telemetry/index.ts` following naming conventions:
807
+ - Attributes use `ctx.<domain>.<attribute>` format
808
+ - Namespace is `igniter.caller.<group>.<event>`
809
+ 2. **Add to Group:** Register event in the appropriate group (request, cache, retry, validation).
810
+ 3. **Emit in Code:** Call `this.telemetry?.emit(IgniterCallerTelemetryEvents.get.key('group.event'), { ...attributes })`.
811
+ 4. **Write Tests:** Add telemetry test in `src/core/manager.spec.ts` with attribute assertions.
812
+ 5. **Update AGENTS.md:** Add to telemetry registry section.
813
+
814
+ #### 6.4 Adding a New Error Code
815
+
816
+ 1. **Add to Union:** Add the error code string to `IgniterCallerErrorCode` in `src/errors/caller.error.ts`.
817
+ 2. **Add to Operation Type:** If new operation, add to `IgniterCallerOperation` type.
818
+ 3. **Throw Error:** Use `new IgniterCallerError({ code, operation, message, ... })`.
819
+ 4. **Update AGENTS.md:** Add to error code library with context, cause, mitigation, solution.
820
+
821
+ #### 6.5 Adding a New Utility Function
822
+
823
+ 1. **Implement Function:** Add to appropriate utility file in `src/utils/`.
824
+ 2. **Export:** Add to `src/utils/index.ts` barrel.
825
+ 3. **Write Tests:** Create `<utility>.spec.ts` in same directory with comprehensive coverage.
826
+ 4. **Type Check:** Run `npm run typecheck`.
827
+ 5. **Lint:** Run `npm run lint`.
828
+
829
+ ---
830
+
831
+ ### 7. Maintainer Troubleshooting
832
+
833
+ This section helps maintainers debug issues in the package code.
834
+
835
+ #### 7.1 Type Inference Not Working
836
+
837
+ **Symptoms:** Response type is `unknown` instead of inferred schema type.
838
+
839
+ **Causes:**
840
+
841
+ - Schema path uses full URL instead of relative path
842
+ - Schema map type is not properly const assertion
843
+ - Generic type parameters not propagating
844
+
845
+ **Debugging Steps:**
846
+
847
+ 1. **Check Schema Path:** Ensure schema keys are relative paths (e.g., `'/users'`) not full URLs.
848
+ 2. **Verify Const Assertion:** Ensure schema object has `as const` assertion.
849
+ 3. **Check Generic Chain:** Verify `TSchemas` type parameter propagates through builder → manager → request builder.
850
+
851
+ **Example Fix:**
83
852
 
84
853
  ```typescript
85
- const result = await api.request({
86
- method: 'POST',
87
- url: '/users',
88
- body: { name: 'John' },
89
- headers: { 'X-Custom': 'value' },
90
- timeout: 5000,
91
- staleTime: 30000,
92
- retry: { maxAttempts: 3 },
93
- })
854
+ // WRONG: Full URL in schema key
855
+ const schemas = {
856
+ 'https://api.test/users': { GET: { ... } }
857
+ } as const
858
+
859
+ // ✅ CORRECT: Relative path
860
+ const schemas = {
861
+ '/users': { GET: { ... } }
862
+ } as const
863
+ ```
864
+
865
+ ---
866
+
867
+ #### 7.2 Cache Not Invalidating
868
+
869
+ **Symptoms:** Stale data returned after mutation.
870
+
871
+ **Causes:**
872
+
873
+ - Cache key doesn't match request URL
874
+ - Store adapter not configured correctly
875
+ - Pattern invalidation not supported by store
876
+
877
+ **Debugging Steps:**
878
+
879
+ 1. **Check Cache Key:** Verify `effectiveCacheKey` in `execute()` method.
880
+ 2. **Verify Store Configuration:** Ensure `withStore()` is called before `build()`.
881
+ 3. **Check Pattern:** Pattern-based invalidation only works for in-memory cache.
882
+
883
+ ---
884
+
885
+ #### 7.3 Telemetry Not Emitting
886
+
887
+ **Symptoms:** No telemetry events observed.
888
+
889
+ **Causes:**
890
+
891
+ - Telemetry manager not passed to builder
892
+ - Event key mismatch
893
+ - Telemetry not built into IgniterTelemetry
894
+
895
+ **Debugging Steps:**
896
+
897
+ 1. **Verify Configuration:** Check `this.state.telemetry` in manager constructor.
898
+ 2. **Check Event Key:** Verify `IgniterCallerTelemetryEvents.get.key('group.event')` matches defined event.
899
+ 3. **Add Debug Log:** Add `console.log` before `this.telemetry?.emit()` to verify execution.
900
+
901
+ ---
902
+
903
+ #### 7.4 Request Interceptors Not Running
904
+
905
+ **Symptoms:** Headers not being modified by interceptor.
906
+
907
+ **Causes:**
908
+
909
+ - Interceptor not added to builder
910
+ - Interceptor signature incorrect
911
+ - Interceptor chain not awaited
912
+
913
+ **Debugging Steps:**
914
+
915
+ 1. **Check Builder State:** Verify `requestInterceptors` array in builder state.
916
+ 2. **Verify Signature:** Ensure interceptor accepts and returns `IgniterCallerRequestOptions`.
917
+ 3. **Check Async Handling:** Ensure interceptor chain uses `await`.
918
+
919
+ ---
920
+
921
+ ## II. CONSUMER GUIDE (Developer Manual)
922
+
923
+ ### 8. Distribution Anatomy (Consumption)
924
+
925
+ The package is distributed as a standard npm package with multiple entry points.
926
+
927
+ #### 8.1 Package Structure
928
+
929
+ ```
930
+ @igniter-js/caller/
931
+ ├── dist/
932
+ │ ├── index.js # CommonJS main entry
933
+ │ ├── index.mjs # ESM main entry
934
+ │ ├── index.d.ts # TypeScript definitions
935
+ │ ├── client/
936
+ │ │ ├── index.js # React client entry (CJS)
937
+ │ │ ├── index.mjs # React client entry (ESM)
938
+ │ │ └── index.d.ts # React client types
939
+ │ ├── telemetry/
940
+ │ │ ├── index.js # Telemetry definitions (CJS)
941
+ │ │ ├── index.mjs # Telemetry definitions (ESM)
942
+ │ │ └── index.d.ts # Telemetry type definitions
943
+ │ └── adapters/
944
+ │ ├── index.js # Mock adapter (CJS)
945
+ │ ├── index.mjs # Mock adapter (ESM)
946
+ │ └── index.d.ts # Adapter type definitions
947
+ └── package.json
948
+ ```
949
+
950
+ #### 8.2 Imports
951
+
952
+ ```typescript
953
+ // Main entry - creates caller instances
954
+ import { IgniterCaller, IgniterCallerManager } from "@igniter-js/caller";
955
+
956
+ // Telemetry definitions - for observability setup
957
+ import { IgniterCallerTelemetryEvents } from "@igniter-js/caller/telemetry";
958
+
959
+ // Mock adapter - for testing
960
+ import { MockCallerStoreAdapter } from "@igniter-js/caller/adapters";
961
+
962
+ // React client - Provider + hooks
963
+ import { IgniterCallerProvider, useIgniterCaller } from "@igniter-js/caller/client";
964
+
965
+ // Typed mock registry
966
+ import { IgniterCallerMock } from "@igniter-js/caller";
967
+
968
+ // Types - for extending or advanced usage
969
+ import type {
970
+ IgniterCallerApiResponse,
971
+ IgniterCallerRequestInterceptor,
972
+ IgniterCallerRetryOptions,
973
+ } from "@igniter-js/caller";
974
+ ```
975
+
976
+ #### 8.3 Runtime Support
977
+
978
+ | Runtime | Version Required | Notes |
979
+ | -------- | ---------------- | --------------------------------------------- |
980
+ | Node.js | 18+ | Uses native `fetch` (undici) |
981
+ | Bun | 1.0+ | Native `fetch` support |
982
+ | Deno | 1.30+ | Native `fetch` support |
983
+ | Browsers | Modern (ES2020+) | Native `fetch` and `AbortController` required |
984
+
985
+ ---
986
+
987
+ ### 9. Quick Start & Common Patterns
988
+
989
+ #### 9.1 Basic Usage
990
+
991
+ ```typescript
992
+ import { IgniterCaller } from "@igniter-js/caller";
993
+
994
+ const api = IgniterCaller.create()
995
+ .withBaseUrl("https://api.example.com")
996
+ .withHeaders({ Authorization: `Bearer ${token}` })
997
+ .build();
998
+
999
+ const result = await api.get("/users").execute();
1000
+
1001
+ if (result.error) {
1002
+ console.error(result.error);
1003
+ throw result.error;
1004
+ }
1005
+
1006
+ console.log(result.data);
1007
+ ```
1008
+
1009
+ #### 9.2 With Query Parameters
1010
+
1011
+ ```typescript
1012
+ const result = await api.get("/users").params({ page: 1, limit: 10 }).execute();
94
1013
  ```
95
1014
 
96
- ### Response Type Detection
1015
+ #### 9.3 With POST Body
97
1016
 
98
- Response is parsed automatically based on `Content-Type` header:
1017
+ ```typescript
1018
+ const result = await api
1019
+ .post("/users")
1020
+ .body({ name: "John Doe", email: "john@example.com" })
1021
+ .execute();
1022
+ ```
99
1023
 
100
- | Content-Type | Parsed As |
101
- |-------------|-----------|
102
- | `application/json` | JSON object |
103
- | `text/xml`, `application/xml` | Text |
104
- | `text/csv` | Text |
105
- | `text/html`, `text/plain` | Text |
106
- | `image/*`, `audio/*`, `video/*` | Blob |
107
- | `application/octet-stream` | Blob |
1024
+ #### 9.4 With Caching
108
1025
 
109
- ### GET Body → Query Params
1026
+ ```typescript
1027
+ const result = await api
1028
+ .get("/users")
1029
+ .stale(30_000) // Cache for 30 seconds
1030
+ .execute();
1031
+ ```
110
1032
 
111
- Body in GET/HEAD requests is automatically converted to query params:
1033
+ #### 9.5 With Retry
112
1034
 
113
1035
  ```typescript
114
- api.get('/search').body({ q: 'test', page: 1 }).execute()
115
- // → GET /search?q=test&page=1
1036
+ const result = await api
1037
+ .get("/health")
1038
+ .retry(3, {
1039
+ baseDelay: 250,
1040
+ backoff: "exponential",
1041
+ retryOnStatus: [408, 429, 500, 502, 503, 504],
1042
+ })
1043
+ .execute();
116
1044
  ```
117
1045
 
1046
+ #### 9.6 React Client (Provider + Hooks)
1047
+
1048
+ ```tsx
1049
+ import { IgniterCallerProvider, useIgniterCaller } from "@igniter-js/caller/client";
1050
+
1051
+ export function CallerProvider() {
1052
+ return (
1053
+ <IgniterCallerProvider callers={{ github: githubApi }}>
1054
+ <App />
1055
+ </IgniterCallerProvider>
1056
+ );
1057
+ }
1058
+
1059
+ export const useCaller = useIgniterCaller<{
1060
+ github: typeof githubApi;
1061
+ }>();
1062
+
1063
+ function Profile() {
1064
+ const github = useCaller("github");
1065
+ const { data, isLoading, error, refetch, invalidate } = github
1066
+ .get("/me")
1067
+ .useQuery({ staleTime: 5000 });
1068
+
1069
+ if (isLoading) return <div>Loading...</div>;
1070
+ if (error) return <div>Error</div>;
1071
+ return <pre>{JSON.stringify(data, null, 2)}</pre>;
1072
+ }
1073
+ ```
1074
+
1075
+ Notes:
1076
+
1077
+ - `@igniter-js/caller/client` is the only entrypoint that depends on React.
1078
+ - Global defaults can be set via `github.config.set(...)`.
1079
+ - Invalidation supports an optional data payload for optimistic updates.
1080
+
1081
+ #### 9.7 Typed Mocks (IgniterCallerMock)
1082
+
1083
+ ```typescript
1084
+ import { IgniterCaller, IgniterCallerMock } from "@igniter-js/caller";
1085
+
1086
+ const schemas = {
1087
+ "/users/:id": {
1088
+ GET: {
1089
+ responses: {
1090
+ 200: UserSchema,
1091
+ },
1092
+ },
1093
+ },
1094
+ };
1095
+
1096
+ const mock = IgniterCallerMock.create()
1097
+ .withSchemas(schemas)
1098
+ .mock("/users/:id", {
1099
+ GET: (request) => ({
1100
+ response: { id: request.params.id },
1101
+ status: 200,
1102
+ delayMs: 150,
1103
+ }),
1104
+ })
1105
+ .build();
1106
+
1107
+ const api = IgniterCaller.create()
1108
+ .withSchemas(schemas)
1109
+ .withMock({ enabled: true, mock })
1110
+ .build();
1111
+ ```
1112
+
1113
+ Notes:
1114
+
1115
+ - Mock handlers receive the full request context (method, url, headers, query, params, body).
1116
+ - When mock is enabled and no handler is registered, the request falls back to the real transport.
1117
+
118
1118
  ---
119
1119
 
120
- ## CLI Integration
1120
+ ### 10. Real-World Use Case Library
1121
+
1122
+ #### Case A: E-Commerce Product Catalog
1123
+
1124
+ **Scenario:** An e-commerce platform needs to fetch product listings with caching, type safety, and error handling.
1125
+
1126
+ ```typescript
1127
+ import { IgniterCaller } from "@igniter-js/caller";
1128
+ import { z } from "zod";
1129
+
1130
+ // Define schemas
1131
+ const ProductSchema = z.object({
1132
+ id: z.string(),
1133
+ name: z.string(),
1134
+ price: z.number(),
1135
+ inStock: z.boolean(),
1136
+ });
1137
+
1138
+ const ProductsResponseSchema = z.object({
1139
+ products: z.array(ProductSchema),
1140
+ total: z.number(),
1141
+ });
1142
+
1143
+ const api = IgniterCaller.create()
1144
+ .withBaseUrl("https://shop-api.example.com")
1145
+ .build();
1146
+
1147
+ // Fetch products with caching (5 minutes)
1148
+ async function getProducts(category?: string) {
1149
+ const result = await api
1150
+ .get("/products")
1151
+ .params(category ? { category } : {})
1152
+ .responseType(ProductsResponseSchema)
1153
+ .stale(300_000) // 5 minutes
1154
+ .execute();
1155
+
1156
+ if (result.error) {
1157
+ throw new Error(`Failed to fetch products: ${result.error.message}`);
1158
+ }
1159
+
1160
+ return result.data;
1161
+ }
1162
+
1163
+ // Usage
1164
+ const products = await getProducts("electronics");
1165
+ console.log(`Found ${products.total} products`);
1166
+ ```
1167
+
1168
+ **Best Practices Applied:**
121
1169
 
122
- - The Igniter CLI provides `igniter generate caller` to ingest an OpenAPI 3 spec (URL or file) and emit `schema.ts` + `index.ts` under `src/callers/<hostname>` by default.
123
- - Generated schemas must follow `IgniterCallerSchemaMap` (`{ [path]: { METHOD: { request?: ..., responses: { <status>: zod } } } }`) to work with `.withSchemas()`.
124
- - The CLI prefixes schemas and the exported caller with `--name` (e.g., `facebookCaller`, `FacebookSchema`).
125
- - If runtime schema expectations change, update both the CLI generator and this documentation so consumers stay aligned.
1170
+ - Caching for high-traffic endpoints
1171
+ - Schema validation for response integrity
1172
+ - Type-safe product objects
1173
+ - Centralized error handling
126
1174
 
127
1175
  ---
128
1176
 
129
- ## File Structure
1177
+ #### Case B: Fintech Payment Processing
130
1178
 
1179
+ **Scenario:** A fintech application needs to process payments with retry logic, timeout handling, and secure headers.
1180
+
1181
+ ```typescript
1182
+ import { IgniterCaller } from "@igniter-js/caller";
1183
+ import { z } from "zod";
1184
+
1185
+ // Payment request schema
1186
+ const PaymentRequestSchema = z.object({
1187
+ amount: z.number().positive(),
1188
+ currency: z.enum(["USD", "EUR", "GBP"]),
1189
+ recipient: z.object({
1190
+ accountNumber: z.string().length(10),
1191
+ routingNumber: z.string().length(9),
1192
+ }),
1193
+ reference: z.string(),
1194
+ });
1195
+
1196
+ // Payment response schema
1197
+ const PaymentResponseSchema = z.object({
1198
+ id: z.string(),
1199
+ status: z.enum(["pending", "completed", "failed"]),
1200
+ transactionId: z.string(),
1201
+ });
1202
+
1203
+ const api = IgniterCaller.create()
1204
+ .withBaseUrl("https://payments-api.example.com")
1205
+ .withHeaders({
1206
+ "X-API-Key": process.env.PAYMENT_API_KEY!,
1207
+ "Content-Type": "application/json",
1208
+ })
1209
+ .build();
1210
+
1211
+ // Process payment with retry and timeout
1212
+ async function processPayment(payment: z.infer<typeof PaymentRequestSchema>) {
1213
+ const result = await api
1214
+ .post("/payments")
1215
+ .body(payment)
1216
+ .responseType(PaymentResponseSchema)
1217
+ .timeout(10_000) // 10 second timeout
1218
+ .retry(3, {
1219
+ baseDelay: 500,
1220
+ backoff: "exponential",
1221
+ retryOnStatus: [503, 504], // Only retry on server errors
1222
+ })
1223
+ .execute();
1224
+
1225
+ if (result.error) {
1226
+ // Log payment failure
1227
+ console.error(`Payment failed: ${result.error.message}`);
1228
+
1229
+ // Re-throw for upstream handling
1230
+ throw result.error;
1231
+ }
1232
+
1233
+ return result.data;
1234
+ }
1235
+
1236
+ // Usage
1237
+ try {
1238
+ const payment = await processPayment({
1239
+ amount: 100.0,
1240
+ currency: "USD",
1241
+ recipient: {
1242
+ accountNumber: "1234567890",
1243
+ routingNumber: "987654321",
1244
+ },
1245
+ reference: `ORDER-${Date.now()}`,
1246
+ });
1247
+
1248
+ console.log(`Payment initiated: ${payment.id}`);
1249
+ } catch (error) {
1250
+ console.error("Payment processing failed");
1251
+ }
131
1252
  ```
132
- packages/caller/
133
- ├── src/
134
- │ ├── index.ts # Public exports
135
- │ ├── igniter-caller.spec.ts # Tests
136
- │ ├── core/
137
- │ │ ├── igniter-caller.ts # Main IgniterCaller class
138
- │ │ └── igniter-caller-events.ts # Event emitter
139
- │ ├── builder/
140
- │ │ ├── igniter-caller.builder.ts # Builder for configuration
141
- │ │ └── igniter-caller-request.builder.ts # Request builder
142
- │ ├── errors/
143
- │ │ └── igniter-caller.error.ts # Error class
144
- │ ├── types/
145
- │ │ ├── events.ts
146
- │ │ ├── http.ts
147
- │ │ ├── interceptors.ts
148
- │ │ ├── request.ts
149
- │ │ ├── response.ts
150
- │ │ ├── retry.ts
151
- │ │ ├── schemas.ts
152
- │ │ └── store.ts
153
- │ └── utils/
154
- │ ├── body.ts
155
- │ ├── cache.ts
156
- │ ├── schema.ts
157
- │ ├── testing.ts
158
- │ └── url.ts
159
- ├── package.json
160
- ├── tsconfig.json
161
- ├── tsup.config.ts
162
- ├── vitest.config.ts
163
- ├── README.md
164
- ├── CHANGELOG.md
165
- └── AGENTS.md
1253
+
1254
+ **Best Practices Applied:**
1255
+
1256
+ - Strict timeout for financial operations
1257
+ - Exponential backoff for transient failures
1258
+ - Schema validation for request/response
1259
+ - Secure API key management via headers
1260
+ - Only retry on server-side errors
1261
+
1262
+ ---
1263
+
1264
+ #### Case C: Social Media Feed with Real-Time Updates
1265
+
1266
+ **Scenario:** A social media app needs to fetch feed posts, cache aggressively, and invalidate cache on new posts.
1267
+
1268
+ ```typescript
1269
+ import { IgniterCaller, IgniterCallerManager } from "@igniter-js/caller";
1270
+ import { z } from "zod";
1271
+
1272
+ const PostSchema = z.object({
1273
+ id: z.string(),
1274
+ author: z.object({
1275
+ id: z.string(),
1276
+ name: z.string(),
1277
+ avatar: z.string().url(),
1278
+ }),
1279
+ content: z.string(),
1280
+ createdAt: z.string(),
1281
+ likes: z.number(),
1282
+ });
1283
+
1284
+ const FeedResponseSchema = z.object({
1285
+ posts: z.array(PostSchema),
1286
+ nextCursor: z.string().nullable(),
1287
+ });
1288
+
1289
+ // Set up global event listener for cache invalidation
1290
+ IgniterCallerManager.on(/^\/feed/, async (result, ctx) => {
1291
+ if (!result.error) {
1292
+ console.log(`Feed fetched: ${ctx.method} ${ctx.url}`);
1293
+ }
1294
+ });
1295
+
1296
+ const api = IgniterCaller.create()
1297
+ .withBaseUrl("https://social-api.example.com")
1298
+ .withHeaders({
1299
+ Authorization: `Bearer ${authToken}`,
1300
+ })
1301
+ .build();
1302
+
1303
+ // Fetch feed with aggressive caching (2 minutes)
1304
+ async function getFeed(cursor?: string) {
1305
+ const result = await api
1306
+ .get("/feed")
1307
+ .params(cursor ? { cursor } : {})
1308
+ .responseType(FeedResponseSchema)
1309
+ .stale(120_000) // 2 minutes
1310
+ .execute();
1311
+
1312
+ if (result.error) {
1313
+ throw new Error(`Failed to fetch feed: ${result.error.message}`);
1314
+ }
1315
+
1316
+ return result.data;
1317
+ }
1318
+
1319
+ // Create new post and invalidate feed cache
1320
+ async function createPost(content: string) {
1321
+ const result = await api
1322
+ .post("/posts")
1323
+ .body({ content })
1324
+ .responseType(PostSchema)
1325
+ .execute();
1326
+
1327
+ if (result.error) {
1328
+ throw new Error(`Failed to create post: ${result.error.message}`);
1329
+ }
1330
+
1331
+ // Invalidate feed cache after creating post
1332
+ await IgniterCallerManager.invalidate("/feed");
1333
+
1334
+ return result.data;
1335
+ }
1336
+
1337
+ // Usage
1338
+ const feed = await getFeed();
1339
+ console.log(`Feed has ${feed.posts.length} posts`);
1340
+
1341
+ await createPost("Hello, world!");
1342
+ const freshFeed = await getFeed(); // This will fetch fresh data
166
1343
  ```
167
1344
 
1345
+ **Best Practices Applied:**
1346
+
1347
+ - Aggressive caching for read-heavy feeds
1348
+ - Global event listener for observability
1349
+ - Manual cache invalidation after mutations
1350
+ - Cursor-based pagination support
1351
+
168
1352
  ---
169
1353
 
170
- ## Development Guidelines
1354
+ #### Case D: IoT Device Management
171
1355
 
172
- ### Adding features
1356
+ **Scenario:** An IoT platform needs to manage devices with high concurrency, retry logic, and efficient JSON parsing.
173
1357
 
174
- - If it changes the runtime behavior of request execution, prefer implementing inside `IgniterCallerRequestBuilder`.
175
- - If it changes caching behavior, prefer implementing in `IgniterCallerCacheUtils`.
176
- - If it changes schema matching/validation, implement in `IgniterCallerSchemaUtils`.
177
- - Keep new public options discoverable from the builder API.
1358
+ ```typescript
1359
+ import { IgniterCaller, IgniterCallerManager } from "@igniter-js/caller";
1360
+ import { z } from "zod";
1361
+
1362
+ const DeviceSchema = z.object({
1363
+ id: z.string(),
1364
+ name: z.string(),
1365
+ type: z.enum(["sensor", "actuator", "controller"]),
1366
+ status: z.enum(["online", "offline", "error"]),
1367
+ lastSeen: z.string(),
1368
+ metadata: z.record(z.unknown()),
1369
+ });
1370
+
1371
+ const api = IgniterCaller.create()
1372
+ .withBaseUrl("https://iot-api.example.com")
1373
+ .withHeaders({
1374
+ "X-Platform-Key": process.env.IOT_PLATFORM_KEY!,
1375
+ })
1376
+ .build();
1377
+
1378
+ // Fetch all devices with batch parallelization
1379
+ async function getAllDevices() {
1380
+ // Split into multiple requests for pagination
1381
+ const page1 = api.get("/devices").params({ page: 1, limit: 50 }).execute();
1382
+ const page2 = api.get("/devices").params({ page: 2, limit: 50 }).execute();
1383
+ const page3 = api.get("/devices").params({ page: 3, limit: 50 }).execute();
1384
+
1385
+ // Execute all requests in parallel
1386
+ const [result1, result2, result3] = await IgniterCallerManager.batch([
1387
+ page1,
1388
+ page2,
1389
+ page3,
1390
+ ]);
1391
+
1392
+ if (result1.error || result2.error || result3.error) {
1393
+ throw new Error("Failed to fetch all devices");
1394
+ }
1395
+
1396
+ return [...result1.data, ...result2.data, ...result3.data];
1397
+ }
1398
+
1399
+ // Update device status with retry
1400
+ async function updateDeviceStatus(
1401
+ deviceId: string,
1402
+ status: "online" | "offline",
1403
+ ) {
1404
+ const result = await api
1405
+ .put(`/devices/${deviceId}`)
1406
+ .body({ status })
1407
+ .responseType(DeviceSchema)
1408
+ .retry(5, {
1409
+ baseDelay: 1000,
1410
+ backoff: "linear",
1411
+ retryOnStatus: [408, 429, 500, 502, 503, 504],
1412
+ })
1413
+ .execute();
1414
+
1415
+ if (result.error) {
1416
+ throw new Error(`Failed to update device: ${result.error.message}`);
1417
+ }
1418
+
1419
+ return result.data;
1420
+ }
1421
+
1422
+ // Usage
1423
+ const devices = await getAllDevices();
1424
+ console.log(`Managing ${devices.length} devices`);
1425
+
1426
+ await updateDeviceStatus(devices[0].id, "online");
1427
+ ```
178
1428
 
179
- ### Error handling
1429
+ **Best Practices Applied:**
180
1430
 
181
- - Use `IgniterCallerError` for predictable failures.
182
- - Prefer stable `code` values over new ad-hoc error messages.
1431
+ - Parallel batch requests for efficiency
1432
+ - Retry with linear backoff for IoT network conditions
1433
+ - Type-safe device objects
1434
+ - Efficient JSON parsing (automatic)
1435
+
1436
+ ---
183
1437
 
184
- ### Error Codes
1438
+ #### Case E: Healthcare Patient Records
185
1439
 
186
- | Code | Description |
187
- |------|-------------|
188
- | `IGNITER_CALLER_HTTP_ERROR` | HTTP response with non-2xx status |
189
- | `IGNITER_CALLER_TIMEOUT` | Request timeout |
190
- | `IGNITER_CALLER_REQUEST_VALIDATION_FAILED` | Request body schema validation failed |
191
- | `IGNITER_CALLER_RESPONSE_VALIDATION_FAILED` | Response schema validation failed |
192
- | `IGNITER_CALLER_UNKNOWN_ERROR` | Unexpected error |
1440
+ **Scenario:** A healthcare application needs to fetch patient records with strict validation, HIPAA-compliant logging, and fallback handling.
1441
+
1442
+ ```typescript
1443
+ import { IgniterCaller } from "@igniter-js/caller";
1444
+ import { z } from "zod";
1445
+
1446
+ // Patient schema with validation
1447
+ const PatientSchema = z.object({
1448
+ id: z.string(),
1449
+ name: z.object({
1450
+ first: z.string().min(1),
1451
+ last: z.string().min(1),
1452
+ }),
1453
+ dateOfBirth: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
1454
+ medications: z.array(
1455
+ z.object({
1456
+ name: z.string(),
1457
+ dosage: z.string(),
1458
+ frequency: z.string(),
1459
+ }),
1460
+ ),
1461
+ });
1462
+
1463
+ // Error schema
1464
+ const ApiErrorSchema = z.object({
1465
+ code: z.string(),
1466
+ message: z.string(),
1467
+ });
1468
+
1469
+ const api = IgniterCaller.create()
1470
+ .withBaseUrl("https://healthcare-api.example.com")
1471
+ .withHeaders({
1472
+ Authorization: `Bearer ${process.env.HEALTHCARE_API_KEY!}`,
1473
+ "X-Request-Id": crypto.randomUUID(),
1474
+ })
1475
+ .build();
1476
+
1477
+ // Fetch patient with strict validation and fallback
1478
+ async function getPatient(patientId: string) {
1479
+ const result = await api
1480
+ .get(`/patients/${patientId}`)
1481
+ .responseType(PatientSchema)
1482
+ .fallback(() => {
1483
+ // Return default patient structure if API is unavailable
1484
+ return {
1485
+ id: patientId,
1486
+ name: { first: "Unknown", last: "Patient" },
1487
+ dateOfBirth: "1900-01-01",
1488
+ medications: [],
1489
+ };
1490
+ })
1491
+ .execute();
1492
+
1493
+ return result.data;
1494
+ }
1495
+
1496
+ // Create medication record with request validation
1497
+ async function addMedication(
1498
+ patientId: string,
1499
+ medication: { name: string; dosage: string; frequency: string },
1500
+ ) {
1501
+ const MedicationSchema = z.object({
1502
+ patientId: z.string(),
1503
+ name: z.string().min(1),
1504
+ dosage: z.string().min(1),
1505
+ frequency: z.string().min(1),
1506
+ });
1507
+
1508
+ const result = await api
1509
+ .post(`/patients/${patientId}/medications`)
1510
+ .body(medication)
1511
+ .responseType(z.object({ id: z.string() }))
1512
+ .execute();
1513
+
1514
+ if (result.error) {
1515
+ // Log error for compliance audit
1516
+ console.error(`Medication add failed: ${result.error.message}`);
1517
+ throw result.error;
1518
+ }
1519
+
1520
+ return result.data;
1521
+ }
1522
+
1523
+ // Usage
1524
+ try {
1525
+ const patient = await getPatient("PAT-12345");
1526
+ console.log(`Patient: ${patient.name.first} ${patient.name.last}`);
1527
+
1528
+ await addMedication("PAT-12345", {
1529
+ name: "Aspirin",
1530
+ dosage: "81mg",
1531
+ frequency: "Daily",
1532
+ });
1533
+ } catch (error) {
1534
+ console.error("Healthcare operation failed");
1535
+ }
1536
+ ```
193
1537
 
194
- ### Dependencies
1538
+ **Best Practices Applied:**
195
1539
 
196
- - `@igniter-js/core` is a **peer dependency** (for shared types like `IgniterLogger` and `StandardSchemaV1`).
197
- - `zod` is a **peer dependency** (used when consumers call `responseType(zodSchema)`).
1540
+ - Strict schema validation for sensitive data
1541
+ - Fallback values for API unavailability
1542
+ - Request ID tracking for audit trails
1543
+ - Centralized error logging for compliance
198
1544
 
199
1545
  ---
200
1546
 
201
- ## Testing Strategy
1547
+ #### Case F: Content Management System (CMS)
202
1548
 
203
- - Mock `globalThis.fetch` for request execution tests.
204
- - Prefer tests that validate:
205
- - Request schema validation (strict mode) blocks the network call.
206
- - Response schema validation returns `IgniterCallerError` on invalid payload.
207
- - Caching returns cached data without calling `fetch` again.
208
- - Event emission happens for responses.
209
- - URL is passed correctly to HTTP methods.
210
- - Body is converted to query params for GET requests.
211
- - Content-type detection works correctly.
1549
+ **Scenario:** A CMS needs to fetch content, handle file uploads, and cache aggressively.
1550
+
1551
+ ```typescript
1552
+ import { IgniterCaller } from "@igniter-js/caller";
1553
+ import { z } from "zod";
1554
+
1555
+ const ContentSchema = z.object({
1556
+ id: z.string(),
1557
+ title: z.string(),
1558
+ body: z.string(),
1559
+ publishedAt: z.string().nullable(),
1560
+ author: z.object({
1561
+ id: z.string(),
1562
+ name: z.string(),
1563
+ }),
1564
+ });
1565
+
1566
+ const ContentListSchema = z.object({
1567
+ items: z.array(ContentSchema),
1568
+ total: z.number(),
1569
+ });
1570
+
1571
+ const api = IgniterCaller.create()
1572
+ .withBaseUrl("https://cms-api.example.com")
1573
+ .withHeaders({
1574
+ "X-CMS-API-Key": process.env.CMS_API_KEY!,
1575
+ })
1576
+ .build();
1577
+
1578
+ // Fetch published articles with caching (10 minutes)
1579
+ async function getArticles(params: { page?: number; category?: string }) {
1580
+ const result = await api
1581
+ .get("/articles")
1582
+ .params(params)
1583
+ .responseType(ContentListSchema)
1584
+ .stale(600_000) // 10 minutes
1585
+ .execute();
1586
+
1587
+ if (result.error) {
1588
+ throw new Error(`Failed to fetch articles: ${result.error.message}`);
1589
+ }
1590
+
1591
+ return result.data;
1592
+ }
1593
+
1594
+ // Upload image file
1595
+ async function uploadImage(file: File) {
1596
+ const formData = new FormData();
1597
+ formData.append("file", file);
1598
+
1599
+ const result = await api
1600
+ .post("/images")
1601
+ .body(formData)
1602
+ .headers({ "Content-Type": "multipart/form-data" }) // Let fetch set boundary
1603
+ .execute();
1604
+
1605
+ if (result.error) {
1606
+ throw new Error(`Failed to upload image: ${result.error.message}`);
1607
+ }
1608
+
1609
+ return result.data;
1610
+ }
1611
+
1612
+ // Create article and invalidate cache
1613
+ async function createArticle(article: { title: string; body: string }) {
1614
+ const result = await api
1615
+ .post("/articles")
1616
+ .body(article)
1617
+ .responseType(ContentSchema)
1618
+ .execute();
1619
+
1620
+ if (result.error) {
1621
+ throw new Error(`Failed to create article: ${result.error.message}`);
1622
+ }
1623
+
1624
+ // Invalidate article list cache
1625
+ await IgniterCallerManager.invalidate("/articles");
1626
+
1627
+ return result.data;
1628
+ }
1629
+
1630
+ // Usage
1631
+ const articles = await getArticles({ page: 1, category: "news" });
1632
+ console.log(`Found ${articles.total} articles`);
1633
+
1634
+ const imageUpload = await uploadImage(fileInput.files[0]);
1635
+ console.log(`Image uploaded: ${imageUpload.id}`);
1636
+ ```
1637
+
1638
+ **Best Practices Applied:**
1639
+
1640
+ - FormData handling for file uploads
1641
+ - Aggressive caching for content
1642
+ - Cache invalidation after mutations
1643
+ - Type-safe content objects
1644
+
1645
+ ---
212
1646
 
213
- Run tests:
1647
+ #### Case G: Analytics Dashboard
214
1648
 
215
- ```bash
216
- npm test --filter @igniter-js/caller
1649
+ **Scenario:** An analytics dashboard needs to fetch metrics with long polling, caching, and error recovery.
1650
+
1651
+ ```typescript
1652
+ import { IgniterCaller } from "@igniter-js/caller";
1653
+ import { z } from "zod";
1654
+
1655
+ const MetricSchema = z.object({
1656
+ name: z.string(),
1657
+ value: z.number(),
1658
+ unit: z.string(),
1659
+ timestamp: z.string(),
1660
+ });
1661
+
1662
+ const MetricsResponseSchema = z.object({
1663
+ metrics: z.array(MetricSchema),
1664
+ period: z.object({
1665
+ start: z.string(),
1666
+ end: z.string(),
1667
+ }),
1668
+ });
1669
+
1670
+ const api = IgniterCaller.create()
1671
+ .withBaseUrl("https://analytics-api.example.com")
1672
+ .withHeaders({
1673
+ "X-Analytics-Key": process.env.ANALYTICS_API_KEY!,
1674
+ })
1675
+ .build();
1676
+
1677
+ // Fetch metrics with short-term caching (30 seconds)
1678
+ async function getMetrics(period: { start: string; end: string }) {
1679
+ const result = await api
1680
+ .get("/metrics")
1681
+ .params(period)
1682
+ .responseType(MetricsResponseSchema)
1683
+ .stale(30_000) // 30 seconds
1684
+ .execute();
1685
+
1686
+ if (result.error) {
1687
+ // Return empty metrics on error (graceful degradation)
1688
+ return {
1689
+ metrics: [],
1690
+ period: {
1691
+ start: period.start,
1692
+ end: period.end,
1693
+ },
1694
+ };
1695
+ }
1696
+
1697
+ return result.data;
1698
+ }
1699
+
1700
+ // Start polling for real-time updates
1701
+ function startPolling(intervalMs: number) {
1702
+ const poll = async () => {
1703
+ const end = new Date().toISOString();
1704
+ const start = new Date(Date.now() - 300_000).toISOString(); // Last 5 minutes
1705
+
1706
+ const metrics = await getMetrics({ start, end });
1707
+
1708
+ // Update UI with new metrics
1709
+ updateDashboard(metrics);
1710
+ };
1711
+
1712
+ poll(); // Initial fetch
1713
+ return setInterval(poll, intervalMs);
1714
+ }
1715
+
1716
+ // Usage
1717
+ const pollInterval = startPolling(30_000); // Poll every 30 seconds
1718
+
1719
+ // Later: stop polling
1720
+ clearInterval(pollInterval);
217
1721
  ```
218
1722
 
1723
+ **Best Practices Applied:**
1724
+
1725
+ - Short-term caching for polling
1726
+ - Graceful degradation on errors
1727
+ - Interval-based polling for real-time updates
1728
+ - Type-safe metric objects
1729
+
1730
+ ---
1731
+
1732
+ #### Case H: Travel Booking System
1733
+
1734
+ **Scenario:** A travel booking system needs to search flights, handle rate limiting, and cache search results.
1735
+
1736
+ ```typescript
1737
+ import { IgniterCaller } from "@igniter-js/caller";
1738
+ import { z } from "zod";
1739
+
1740
+ const FlightSchema = z.object({
1741
+ id: z.string(),
1742
+ airline: z.string(),
1743
+ flightNumber: z.string(),
1744
+ departure: z.object({
1745
+ airport: z.string(),
1746
+ time: z.string(),
1747
+ }),
1748
+ arrival: z.object({
1749
+ airport: z.string(),
1750
+ time: z.string(),
1751
+ }),
1752
+ price: z.object({
1753
+ amount: z.number(),
1754
+ currency: z.string(),
1755
+ }),
1756
+ seatsAvailable: z.number(),
1757
+ });
1758
+
1759
+ const FlightSearchResponseSchema = z.object({
1760
+ flights: z.array(FlightSchema),
1761
+ searchId: z.string(),
1762
+ expiresAt: z.string(),
1763
+ });
1764
+
1765
+ const api = IgniterCaller.create()
1766
+ .withBaseUrl("https://flights-api.example.com")
1767
+ .withHeaders({
1768
+ "X-Travel-API-Key": process.env.TRAVEL_API_KEY!,
1769
+ })
1770
+ .build();
1771
+
1772
+ // Search flights with rate-limit-aware retry
1773
+ async function searchFlights(search: {
1774
+ origin: string;
1775
+ destination: string;
1776
+ date: string;
1777
+ }) {
1778
+ const result = await api
1779
+ .get("/flights/search")
1780
+ .params(search)
1781
+ .responseType(FlightSearchResponseSchema)
1782
+ .retry(5, {
1783
+ baseDelay: 2000,
1784
+ backoff: "exponential",
1785
+ retryOnStatus: [429, 500, 502, 503, 504], // Include rate limit (429)
1786
+ })
1787
+ .stale(60_000) // Cache for 1 minute
1788
+ .execute();
1789
+
1790
+ if (result.error) {
1791
+ // Check if rate limited
1792
+ if ((result.error as any).statusCode === 429) {
1793
+ throw new Error("Rate limited. Please try again later.");
1794
+ }
1795
+ throw new Error(`Search failed: ${result.error.message}`);
1796
+ }
1797
+
1798
+ return result.data;
1799
+ }
1800
+
1801
+ // Book flight with validation
1802
+ async function bookFlight(
1803
+ flightId: string,
1804
+ passenger: { name: string; email: string },
1805
+ ) {
1806
+ const BookingRequestSchema = z.object({
1807
+ flightId: z.string(),
1808
+ passenger: z.object({
1809
+ name: z.string().min(2),
1810
+ email: z.string().email(),
1811
+ }),
1812
+ });
1813
+
1814
+ const result = await api
1815
+ .post("/flights/book")
1816
+ .body({ flightId, passenger })
1817
+ .responseType(
1818
+ z.object({
1819
+ bookingId: z.string(),
1820
+ confirmationCode: z.string(),
1821
+ }),
1822
+ )
1823
+ .timeout(15_000) // 15 second timeout for booking
1824
+ .execute();
1825
+
1826
+ if (result.error) {
1827
+ throw new Error(`Booking failed: ${result.error.message}`);
1828
+ }
1829
+
1830
+ return result.data;
1831
+ }
1832
+
1833
+ // Usage
1834
+ const search = await searchFlights({
1835
+ origin: "JFK",
1836
+ destination: "LAX",
1837
+ date: "2024-03-15",
1838
+ });
1839
+
1840
+ console.log(`Found ${search.flights.length} flights`);
1841
+
1842
+ const booking = await bookFlight(search.flights[0].id, {
1843
+ name: "John Doe",
1844
+ email: "john@example.com",
1845
+ });
1846
+
1847
+ console.log(`Booked! Confirmation: ${booking.confirmationCode}`);
1848
+ ```
1849
+
1850
+ **Best Practices Applied:**
1851
+
1852
+ - Rate limit aware retry with exponential backoff
1853
+ - Aggressive caching for expensive search operations
1854
+ - Extended timeout for booking operations
1855
+ - Request validation
1856
+
219
1857
  ---
220
1858
 
221
- ## Key Implementation Details
1859
+ ### 11. Domain-Specific Guidance
222
1860
 
223
- ### IgniterCallerMethodRequestBuilder
1861
+ #### 11.1 High-Performance Caching
224
1862
 
225
- When using specific HTTP methods (get, post, etc.), the builder returns a type that omits internal methods:
1863
+ For high-traffic APIs, use aggressive caching and store-based persistence:
226
1864
 
227
1865
  ```typescript
228
- export type IgniterCallerMethodRequestBuilder<TResponse = unknown> = Omit<
229
- IgniterCallerRequestBuilder<TResponse>,
230
- '_setMethod' | '_setUrl'
231
- >
1866
+ import { IgniterCaller } from "@igniter-js/caller";
1867
+ import { MockCallerStoreAdapter } from "@igniter-js/caller/adapters";
1868
+
1869
+ // Use mock store for development, switch to Redis in production
1870
+ const store =
1871
+ process.env.NODE_ENV === "production"
1872
+ ? createRedisStore()
1873
+ : MockCallerStoreAdapter.create();
1874
+
1875
+ const api = IgniterCaller.create()
1876
+ .withBaseUrl("https://high-traffic-api.example.com")
1877
+ .withStore(store, {
1878
+ ttl: 3600, // 1 hour
1879
+ keyPrefix: "api-cache:",
1880
+ })
1881
+ .build();
1882
+
1883
+ // Cache for 10 minutes (shorter than TTL for fresh data)
1884
+ const result = await api.get("/popular-items").stale(600_000).execute();
232
1885
  ```
233
1886
 
234
- This prevents calling `.method()` on a request that already has a method set.
1887
+ **Recommendations:**
235
1888
 
236
- ### Content-Type Detection
1889
+ - Set stale time < store TTL for cache revalidation
1890
+ - Use Redis for production (shared cache across instances)
1891
+ - Use pattern-based invalidation for batch updates
237
1892
 
238
- The `detectContentType` function maps headers to response types:
1893
+ ---
1894
+
1895
+ #### 11.2 Schema-First Development
1896
+
1897
+ For type-safe API clients, define schemas first, then generate callers:
1898
+
1899
+ ```typescript
1900
+ import { IgniterCaller, IgniterCallerSchema } from '@igniter-js/caller'
1901
+ import { z } from 'zod'
1902
+
1903
+ // Define reusable schemas
1904
+ const UserSchema = z.object({
1905
+ id: z.string(),
1906
+ name: z.string(),
1907
+ email: z.string().email(),
1908
+ })
1909
+
1910
+ const ErrorSchema = z.object({
1911
+ message: z.string(),
1912
+ code: z.string(),
1913
+ })
1914
+
1915
+ // Build schema registry with path-first API
1916
+ const apiSchemas = IgniterCallerSchema.create()
1917
+ .schema('User', UserSchema)
1918
+ .schema('Error', ErrorSchema)
1919
+ .path('/users', (path) =>
1920
+ path.get({
1921
+ responses: {
1922
+ 200: path.ref('User').array(),
1923
+ 401: path.ref('Error').schema,
1924
+ },
1925
+ })
1926
+ .post({
1927
+ request: z.object({
1928
+ name: z.string(),
1929
+ email: z.string().email(),
1930
+ }),
1931
+ responses: {
1932
+ 201: path.ref('User').schema,
1933
+ 400: path.ref('Error').schema,
1934
+ },
1935
+ })
1936
+ )
1937
+ .path('/users/:id', (path) =>
1938
+ path.get({
1939
+ responses: {
1940
+ 200: path.ref('User').schema,
1941
+ 404: path.ref('Error').schema,
1942
+ },
1943
+ })
1944
+ .delete({
1945
+ responses: {
1946
+ 204: z.void(),
1947
+ 404: path.ref('Error').schema,
1948
+ },
1949
+ })
1950
+ )
1951
+ .build()
1952
+
1953
+ // Create typed API client
1954
+ const api = IgniterCaller.create()
1955
+ .withBaseUrl('https://api.example.com')
1956
+ .withSchemas(apiSchemas, { mode: 'strict' })
1957
+ .build()
1958
+
1959
+ // Type inference works automatically
1960
+ type UsersResponse = Awaited<ReturnType<typeof api.get('/users').execute>>
1961
+
1962
+ async function main() {
1963
+ // All calls are fully typed
1964
+ const usersResult = await api.get('/users').execute()
1965
+ const users = usersResult.data // User[] | undefined
1966
+
1967
+ const userResult = await api.get('/users/123')
1968
+ .params({ id: '123' }) // params are typed from path pattern
1969
+ .execute()
1970
+ const user = userResult.data // User | undefined
1971
+
1972
+ const createResult = await api.post('/users')
1973
+ .body({ name: 'John', email: 'john@example.com' }) // body is typed
1974
+ .execute()
1975
+ const created = createResult.data // User | undefined
1976
+ }
1977
+ ```
1978
+
1979
+ ---
1980
+
1981
+ #### 11.3 Error Boundary Integration
1982
+
1983
+ Integrate with React error boundaries:
1984
+
1985
+ ```typescript
1986
+ import { useQuery } from "@tanstack/react-query";
1987
+ import { IgniterCaller } from "@igniter-js/caller";
1988
+
1989
+ const api = IgniterCaller.create()
1990
+ .withBaseUrl("https://api.example.com")
1991
+ .build();
1992
+
1993
+ function useUsers() {
1994
+ return useQuery({
1995
+ queryKey: ["users"],
1996
+ queryFn: async () => {
1997
+ const result = await api.get("/users").execute();
1998
+ if (result.error) throw result.error;
1999
+ return result.data;
2000
+ },
2001
+ retry: (failureCount, error) => {
2002
+ // Only retry on transient errors
2003
+ const isTransient =
2004
+ error instanceof IgniterCallerError &&
2005
+ [408, 429, 500, 502, 503, 504].includes(error.statusCode || 0);
2006
+
2007
+ return isTransient && failureCount < 3;
2008
+ },
2009
+ });
2010
+ }
2011
+ ```
2012
+
2013
+ ---
2014
+
2015
+ ### 12. Best Practices & Anti-Patterns
2016
+
2017
+ #### 12.1 Builder Configuration
2018
+
2019
+ | Practice | Why | Example |
2020
+ | -------------------------------------- | --------------------------------------------------- | ---------------------------------------------------- |
2021
+ | ✅ Chain builder methods | Fluent API improves readability | `IgniterCaller.create().withBaseUrl('...').build()` |
2022
+ | ✅ Use immutable builder pattern | Prevents accidental mutation | Each `with*` returns new instance |
2023
+ | ✅ Configure logging early | Captures all initialization errors | `.withLogger(logger)` before `.build()` |
2024
+ | ❌ Don't reuse builder instances | Builder state persists, causing unexpected behavior | Create new builder or manager for each configuration |
2025
+ | ❌ Don't call `build()` multiple times | Each call creates a new manager instance | Store manager in a variable |
2026
+
2027
+ #### 12.2 Request Execution
2028
+
2029
+ | Practice | Why | Example |
2030
+ | -------------------------------------------------- | -------------------------------- | ------------------------------------------- |
2031
+ | ✅ Check `result.error` | Result is always returned | `if (result.error) throw result.error` |
2032
+ | ✅ Use `stale()` for caching | Simple cache strategy | `.stale(60_000)` for 1-minute cache |
2033
+ | ✅ Set reasonable timeouts | Prevents hanging requests | `.timeout(30_000)` for 30-second limit |
2034
+ | ❌ Don't ignore errors | Errors indicate problems | Always handle or re-throw |
2035
+ | ❌ Don't use short stale times with network errors | Causes repeated failed requests | Increase stale time or fix underlying error |
2036
+ | ❌ Don't retry on client errors (4xx) | Only server errors are transient | Exclude 4xx from `retryOnStatus` |
2037
+
2038
+ #### 12.3 Schema Validation
2039
+
2040
+ | Practice | Why | Example |
2041
+ | -------------------------------- | ---------------------------------- | ----------------------------------------- |
2042
+ | ✅ Define schemas first | Enables full type inference | Use `IgniterCallerSchema.create()` |
2043
+ | ✅ Use relative paths in schemas | Allows base URL configuration | `'/users'` not `'https://api.test/users'` |
2044
+ | ✅ Use `as const` assertion | Enables literal type inference | `schemas as const` |
2045
+ | ❌ Don't mix Zod versions | Incompatible APIs can cause issues | Use Zod v4+ only |
2046
+ | ❌ Don't use loose validation | Type safety is lost | Use `mode: 'strict'` for production |
2047
+
2048
+ #### 12.4 Cache Management
2049
+
2050
+ | Practice | Why | Example |
2051
+ | --------------------------------------- | ---------------------------------- | ------------------------------------------- |
2052
+ | ✅ Invalidate after mutations | Ensures fresh data | `IgniterCallerManager.invalidate('/users')` |
2053
+ | ✅ Use appropriate stale times | Balances freshness and performance | `.stale(300_000)` for rarely-changing data |
2054
+ | ❌ Don't cache sensitive data | Security risk | Avoid caching personal information |
2055
+ | ❌ Don't rely solely on in-memory cache | Lost on restart | Use store adapter for persistence |
2056
+
2057
+ ---
2058
+
2059
+ ### 13. Exhaustive Error & Troubleshooting Library
2060
+
2061
+ This section provides detailed information for every error code emitted by `@igniter-js/caller`.
2062
+
2063
+ #### IGNITER_CALLER_HTTP_ERROR
2064
+
2065
+ - **Context:** Returned when the HTTP response status indicates an error (4xx or 5xx).
2066
+ - **Cause:** The server returned an error status code. This can be due to invalid request data, authentication issues, or server errors.
2067
+ - **Mitigation:**
2068
+ - Check the request body and parameters for correctness.
2069
+ - Verify authentication credentials.
2070
+ - Review the server's API documentation.
2071
+ - **Solution:**
2072
+
2073
+ ```typescript
2074
+ const result = await api.get("/users").execute();
2075
+
2076
+ if (result.error) {
2077
+ if (
2078
+ IgniterCallerError.is(result.error) &&
2079
+ result.error.code === "IGNITER_CALLER_HTTP_ERROR"
2080
+ ) {
2081
+ console.error(
2082
+ `HTTP ${result.error.statusCode}: ${result.error.statusText}`,
2083
+ );
2084
+ // Handle specific status codes
2085
+ if (result.error.statusCode === 401) {
2086
+ // Redirect to login
2087
+ } else if (result.error.statusCode === 404) {
2088
+ // Show not found message
2089
+ }
2090
+ }
2091
+ throw result.error;
2092
+ }
2093
+ ```
2094
+
2095
+ ---
2096
+
2097
+ #### IGNITER_CALLER_TIMEOUT
2098
+
2099
+ - **Context:** Returned when the request exceeds the configured timeout duration.
2100
+ - **Cause:** The server did not respond within the timeout period. This can be due to network issues, slow server response, or a hung request.
2101
+ - **Mitigation:**
2102
+ - Increase the timeout for long-running operations.
2103
+ - Check network connectivity.
2104
+ - Monitor server performance.
2105
+ - **Solution:**
2106
+
2107
+ ```typescript
2108
+ const result = await api
2109
+ .get("/slow-endpoint")
2110
+ .timeout(60_000) // Increase timeout to 60 seconds
2111
+ .execute();
2112
+
2113
+ if (result.error?.code === "IGNITER_CALLER_TIMEOUT") {
2114
+ console.error("Request timed out. Please try again.");
2115
+ // Implement retry logic or user notification
2116
+ }
2117
+ ```
2118
+
2119
+ ---
2120
+
2121
+ #### IGNITER_CALLER_REQUEST_VALIDATION_FAILED
2122
+
2123
+ - **Context:** Returned when the request body fails schema validation in strict mode.
2124
+ - **Cause:** The request body does not match the defined schema. This can be due to missing fields, incorrect types, or validation constraint violations.
2125
+ - **Mitigation:**
2126
+ - Review the request body against the schema.
2127
+ - Ensure all required fields are present.
2128
+ - Check field types and formats.
2129
+ - **Solution:**
2130
+
2131
+ ```typescript
2132
+ const result = await api
2133
+ .post("/users")
2134
+ .body({ name: "John" }) // Missing email field
2135
+ .execute();
2136
+
2137
+ if (result.error?.code === "IGNITER_CALLER_REQUEST_VALIDATION_FAILED") {
2138
+ console.error("Request validation failed:", result.error.details);
2139
+ // Show validation errors to user
2140
+ if (result.error.details) {
2141
+ for (const issue of result.error.details as any[]) {
2142
+ console.error("- " + issue.message);
2143
+ }
2144
+ }
2145
+ }
2146
+ ```
2147
+
2148
+ ---
2149
+
2150
+ #### IGNITER_CALLER_RESPONSE_VALIDATION_FAILED
2151
+
2152
+ - **Context:** Returned when the response data fails schema validation in strict mode.
2153
+ - **Cause:** The server response does not match the defined schema. This can be due to API contract changes, missing fields, or incorrect types.
2154
+ - **Mitigation:**
2155
+ - Review the API documentation.
2156
+ - Update the schema to match the current API response.
2157
+ - Contact API provider if the contract has changed.
2158
+ - **Solution:**
239
2159
 
240
2160
  ```typescript
241
- function detectContentType(contentType: string | null): IgniterCallerResponseContentType
2161
+ const result = await api.get("/users").execute();
2162
+
2163
+ if (result.error?.code === "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED") {
2164
+ console.error("Response validation failed:", result.error.details);
2165
+ // Log unexpected response for debugging
2166
+ console.error("Unexpected response:", JSON.stringify(result.error.cause));
2167
+ // Consider updating schema or using mode: 'soft' for migration
2168
+ }
242
2169
  ```
243
2170
 
244
- Returns one of: `'json' | 'xml' | 'csv' | 'text' | 'html' | 'blob' | 'stream' | 'arraybuffer' | 'formdata'`
2171
+ ---
2172
+
2173
+ #### IGNITER_CALLER_SCHEMA_DUPLICATE
2174
+
2175
+ - **Context:** Thrown when trying to register a duplicate schema key or duplicate method on a path.
2176
+ - **Cause:** Attempted to register the same registry key twice or define the same method on a path twice.
2177
+ - **Mitigation:**
2178
+ - Use unique keys for schema registry.
2179
+ - Ensure each method is only defined once per path.
2180
+ - **Solution:**
2181
+
2182
+ ```typescript
2183
+ try {
2184
+ const schemas = IgniterCallerSchema.create()
2185
+ .schema("User", UserSchema)
2186
+ .schema("User", UserSchema) // Duplicate key!
2187
+ .build();
2188
+ } catch (error) {
2189
+ if (
2190
+ IgniterCallerError.is(error) &&
2191
+ error.code === "IGNITER_CALLER_SCHEMA_DUPLICATE"
2192
+ ) {
2193
+ console.error("Schema key already exists:", error.metadata?.key);
2194
+ // Use a different key or remove duplicate registration
2195
+ }
2196
+ }
2197
+ ```
2198
+
2199
+ ---
2200
+
2201
+ #### IGNITER_CALLER_SCHEMA_INVALID
2202
+
2203
+ - **Context:** Thrown when schema path or key is invalid.
2204
+ - **Cause:**
2205
+ - Schema path is empty.
2206
+ - Schema path does not start with `/`.
2207
+ - Schema registry key is empty.
2208
+ - **Mitigation:**
2209
+ - Ensure paths start with `/`.
2210
+ - Ensure registry keys are non-empty strings.
2211
+ - **Solution:**
2212
+
2213
+ ```typescript
2214
+ try {
2215
+ const schemas = IgniterCallerSchema.create()
2216
+ .path(
2217
+ "users",
2218
+ (
2219
+ path, // Missing leading slash!
2220
+ ) => path.get({ responses: { 200: UserSchema } }),
2221
+ )
2222
+ .build();
2223
+ } catch (error) {
2224
+ if (
2225
+ IgniterCallerError.is(error) &&
2226
+ error.code === "IGNITER_CALLER_SCHEMA_INVALID"
2227
+ ) {
2228
+ console.error("Invalid schema path:", error.message);
2229
+ // Fix path to '/users'
2230
+ }
2231
+ }
2232
+ ```
2233
+
2234
+ ---
245
2235
 
246
- ### Schema Validation
2236
+ #### IGNITER_CALLER_UNKNOWN_ERROR
247
2237
 
248
- Schema validation only runs for validatable content types:
2238
+ - **Context:** Returned when an unexpected error occurs during request execution.
2239
+ - **Cause:**
2240
+ - Network failure (DNS resolution, connection refused).
2241
+ - Runtime error in request builder.
2242
+ - AbortController cancellation without timeout.
2243
+ - **Mitigation:**
2244
+ - Check network connectivity.
2245
+ - Verify the URL is correct.
2246
+ - Review error stack trace for debugging.
2247
+ - **Solution:**
249
2248
 
250
2249
  ```typescript
251
- const VALIDATABLE_CONTENT_TYPES = ['json', 'xml', 'csv']
2250
+ const result = await api.get("/users").execute();
2251
+
2252
+ if (result.error?.code === "IGNITER_CALLER_UNKNOWN_ERROR") {
2253
+ console.error("Unexpected error:", result.error.message);
2254
+ console.error("Cause:", result.error.cause);
2255
+
2256
+ // Handle network errors
2257
+ if (result.error.cause instanceof TypeError) {
2258
+ console.error("Network error. Check your connection.");
2259
+ }
2260
+
2261
+ // Throw for upstream error handling
2262
+ throw result.error;
2263
+ }
252
2264
  ```
253
2265
 
254
- Binary responses (Blob, Stream, ArrayBuffer) are never validated.
2266
+ ---
2267
+
2268
+ ## III. TECHNICAL REFERENCE & RESILIENCE
2269
+
2270
+ ### 14. Exhaustive API Reference
2271
+
2272
+ #### 14.1 Classes
2273
+
2274
+ | Class | Purpose | Location |
2275
+ | ----------------------------------------------------- | ----------------------------------- | ------------------------------------- |
2276
+ | `IgniterCallerBuilder<TSchemas>` | Package initializer with fluent API | `src/builders/main.builder.ts` |
2277
+ | `IgniterCallerManager<TSchemas>` | HTTP client runtime | `src/core/manager.ts` |
2278
+ | `IgniterCallerRequestBuilder<TResponse>` | Per-request configuration | `src/builders/request.builder.ts` |
2279
+ | `IgniterCallerSchema<TSchemas, TRegistry>` | Schema registry builder | `src/builders/schema.builder.ts` |
2280
+ | `IgniterCallerSchemaPathBuilder<TMethods, TRegistry>` | Path + methods builder | `src/builders/schema-path.builder.ts` |
2281
+ | `IgniterCallerEvents` | Global event emitter | `src/core/events.ts` |
2282
+ | `IgniterCallerError` | Typed error class | `src/errors/caller.error.ts` |
2283
+ | `IgniterCallerCacheUtils` | Cache utilities | `src/utils/cache.ts` |
2284
+ | `IgniterCallerBodyUtils` | Body normalization | `src/utils/body.ts` |
2285
+ | `IgniterCallerSchemaUtils` | Schema validation | `src/utils/schema.ts` |
2286
+ | `IgniterCallerUrlUtils` | URL construction | `src/utils/url.ts` |
2287
+ | `MockCallerStoreAdapter` | Mock store for testing | `src/adapters/mock.adapter.ts` |
255
2288
 
256
2289
  ---
257
2290
 
258
- ## Publishing Checklist
2291
+ #### 14.2 IgniterCallerBuilder Methods
2292
+
2293
+ | Method | Signature | Description |
2294
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ |
2295
+ | `create` | `static create(): IgniterCallerBuilder<{}>` | Creates a new builder instance |
2296
+ | `withBaseUrl` | `withBaseUrl(baseURL: string): IgniterCallerBuilder<TSchemas>` | Sets base URL for all requests |
2297
+ | `withHeaders` | `withHeaders(headers: Record<string, string>): IgniterCallerBuilder<TSchemas>` | Sets default headers |
2298
+ | `withCookies" | `withCookies(cookies: Record<string, string>): IgniterCallerBuilder<TSchemas>` | Sets default cookies |
2299
+ | `withLogger` | `withLogger(logger: IgniterLogger): IgniterCallerBuilder<TSchemas>` | Attaches logger instance |
2300
+ | `withRequestInterceptor` | `withRequestInterceptor(interceptor: IgniterCallerRequestInterceptor): IgniterCallerBuilder<TSchemas>` | Adds request interceptor |
2301
+ | `withResponseInterceptor` | `withResponseInterceptor(interceptor: IgniterCallerResponseInterceptor): IgniterCallerBuilder<TSchemas>` | Adds response interceptor |
2302
+ | `withStore` | `withStore(store: IgniterCallerStoreAdapter, options?: IgniterCallerStoreOptions): IgniterCallerBuilder<TSchemas>` | Configures persistent store |
2303
+ | `withSchemas` | `withSchemas<TNewSchemas>(schemas: TNewSchemas, validation?: IgniterCallerSchemaValidationOptions): IgniterCallerBuilder<IgniterCallerSchemaMapFrom<TNewSchemas>>` | Configures schema validation |
2304
+ | `withTelemetry` | `withTelemetry(telemetry: IgniterTelemetryManager): IgniterCallerBuilder<TSchemas>` | Attaches telemetry manager |
2305
+ | `build` | `build(): IgniterCallerManager<TSchemas>` | Builds the manager instance |
2306
+
2307
+ ---
2308
+
2309
+ #### 14.3 IgniterCallerManager Methods
2310
+
2311
+ | Method | Signature | Description |
2312
+ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
2313
+ | `get` | `get<TPath extends GetPaths<TSchemas>>(url: TPath): TypedRequestBuilder<TSchemas, TPath, 'GET'>` | Creates a GET request |
2314
+ | `post` | `post<TPath extends PostPaths<TSchemas>>(url: TPath): TypedRequestBuilder<TSchemas, TPath, 'POST'>` | Creates a POST request |
2315
+ | `put` | `put<TPath extends PutPaths<TSchemas>>(url: TPath): TypedRequestBuilder<TSchemas, TPath, 'PUT'>` | Creates a PUT request |
2316
+ | `patch` | `patch<TPath extends PatchPaths<TSchemas>>(url: TPath): TypedRequestBuilder<TSchemas, TPath, 'PATCH'>` | Creates a PATCH request |
2317
+ | `delete` | `delete<TPath extends DeletePaths<TSchemas>>(url: TPath): TypedRequestBuilder<TSchemas, TPath, 'DELETE'>` | Creates a DELETE request |
2318
+ | `head` | `head<TPath extends HeadPaths<TSchemas>>(url: TPath): TypedRequestBuilder<TSchemas, TPath, 'HEAD'>` | Creates a HEAD request |
2319
+ | `request` | `async request<T = unknown>(options: IgniterCallerDirectRequestOptions): Promise<IgniterCallerApiResponse<T>>` | Executes request directly (axios-style) |
2320
+ | `batch` | `static async batch<T extends readonly Promise<IgniterCallerApiResponse<any>>[]>(requests: [...T]): Promise<{ [K in keyof T]: T[K] extends Promise<infer R> ? R : never }>` | Executes requests in parallel |
2321
+ | `on` | `static on(pattern: IgniterCallerUrlPattern, callback: IgniterCallerEventCallback): () => void` | Registers global event listener |
2322
+ | `off` | `static off(pattern: IgniterCallerUrlPattern, callback?: IgniterCallerEventCallback): void` | Removes event listener |
2323
+ | `invalidate` | `static async invalidate(key: string): Promise<void>` | Invalidates specific cache entry |
2324
+ | `invalidatePattern` | `static async invalidatePattern(pattern: string): Promise<void>` | Invalidates cache by pattern |
2325
+
2326
+ ---
2327
+
2328
+ #### 14.4 IgniterCallerRequestBuilder Methods
2329
+
2330
+ | Method | Signature | Description |
2331
+ | -------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
2332
+ | `url` | `url(url: string): this` | Sets the request URL |
2333
+ | `body` | `body<TBody>(body: TBody): this` | Sets the request body |
2334
+ | `params` | `params(params: Record<string, string \| number \| boolean>): this` | Sets query parameters |
2335
+ | `headers` | `headers(headers: Record<string, string>): this` | Merges additional headers |
2336
+ | `timeout` | `timeout(timeout: number): this` | Sets request timeout in milliseconds |
2337
+ | `cache` | `cache(cache: RequestCache, key?: string): this` | Sets cache strategy and optional key |
2338
+ | `retry` | `retry(maxAttempts: number, options?: Omit<IgniterCallerRetryOptions, 'maxAttempts'>): this` | Configures retry behavior |
2339
+ | `fallback` | `fallback<T>(fn: () => T): this` | Provides fallback value on failure |
2340
+ | `stale` | `stale(milliseconds: number): this` | Sets cache stale time in milliseconds |
2341
+ | `responseType` | `responseType<T>(schema?: z.ZodSchema<T> \| StandardSchemaV1): IgniterCallerRequestBuilder<T>` | Sets expected response type for typing and validation |
2342
+ | `execute` | `async execute(): Promise<IgniterCallerApiResponse<TResponse>>` | Executes the HTTP request |
2343
+ | `getFile` | `getFile(url: string): { execute: () => Promise<IgniterCallerFileResponse> }` | Downloads a file (deprecated) |
2344
+
2345
+ ---
2346
+
2347
+ #### 14.5 IgniterCallerSchema Methods
2348
+
2349
+ | Method | Signature | Description |
2350
+ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
2351
+ | `create` | `static create(): IgniterCallerSchema<{}, {}>` | Creates a new empty schema builder |
2352
+ | `schema` | `schema<TKey extends string, TSchema extends StandardSchemaV1>(key: TKey, schema: TSchema): IgniterCallerSchema<TSchemas, TRegistry & { [K in TKey]: TSchema }>` | Registers a reusable schema |
2353
+ | `path` | `path<TPath extends string, TMethods extends Partial<Record<IgniterCallerSchemaMethod, IgniterCallerEndpointSchema<any, any>>>>(path: TPath, builder: (pathBuilder: IgniterCallerSchemaPathBuilder<{}, TRegistry>) => IgniterCallerSchemaPathBuilder<TMethods, TRegistry>): IgniterCallerSchema<TSchemas & { [K in TPath]: TMethods }, TRegistry>` | Defines a path with methods |
2354
+ | `build` | `build(): IgniterCallerSchemaBuildResult<TSchemas, TRegistry>` | Builds schema map and attaches helpers |
2355
+
2356
+ ---
2357
+
2358
+ #### 14.6 IgniterCallerSchemaPathBuilder Methods
2359
+
2360
+ | Method | Signature | Description |
2361
+ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
2362
+ | `create` | `static create<TRegistry extends IgniterCallerSchemaRegistry>(registry: TRegistry): IgniterCallerSchemaPathBuilder<{}, TRegistry>` | Creates a new path builder |
2363
+ | `ref` | `ref<TKey extends keyof TRegistry>(key: TKey): { schema: TRegistry[TKey]; array(): SchemaArray<TRegistry[TKey]>; nullable(): SchemaNullable<TRegistry[TKey]>; optional(): SchemaOptional<TRegistry[TKey]>; record(keyType?: StandardSchemaV1): SchemaRecord<TRegistry[TKey]> }` | Returns registry reference helper |
2364
+ | `get` | `get<TRequest, TResponses>(config: IgniterCallerSchemaEndpointConfig<TRequest, TResponses>): IgniterCallerSchemaPathBuilder<TMethods & { GET: IgniterCallerEndpointSchema<TRequest, TResponses> }, TRegistry>` | Defines a GET endpoint |
2365
+ | `post` | `post<TRequest, TResponses>(config: IgniterCallerSchemaEndpointConfig<TRequest, TResponses>): IgniterCallerSchemaPathBuilder<TMethods & { POST: IgniterCallerEndpointSchema<TRequest, TResponses> }, TRegistry>` | Defines a POST endpoint |
2366
+ | `put` | `put<TRequest, TResponses>(config: IgniterCallerSchemaEndpointConfig<TRequest, TResponses>): IgniterCallerSchemaPathBuilder<TMethods & { PUT: IgniterCallerEndpointSchema<TRequest, TResponses> }, TRegistry>` | Defines a PUT endpoint |
2367
+ | `patch` | `patch<TRequest, TResponses>(config: IgniterCallerSchemaEndpointConfig<TRequest, TResponses>): IgniterCallerSchemaPathBuilder<TMethods & { PATCH: IgniterCallerEndpointSchema<TRequest, TResponses> }, TRegistry>` | Defines a PATCH endpoint |
2368
+ | `delete` | `delete<TRequest, TResponses>(config: IgniterCallerSchemaEndpointConfig<TRequest, TResponses>): IgniterCallerSchemaPathBuilder<TMethods & { DELETE: IgniterCallerEndpointSchema<TRequest, TResponses> }, TRegistry>` | Defines a DELETE endpoint |
2369
+ | `head` | `head<TRequest, TResponses>(config: IgniterCallerSchemaEndpointConfig<TRequest, TResponses>): IgniterCallerSchemaPathBuilder<TMethods & { HEAD: IgniterCallerEndpointSchema<TRequest, TResponses> }, TRegistry>` | Defines a HEAD endpoint |
2370
+ | `build` | `build(): TMethods` | Builds the method map for the path |
2371
+
2372
+ ---
2373
+
2374
+ #### 14.7 IgniterCallerCacheUtils Static Methods
2375
+
2376
+ | Method | Signature | Description |
2377
+ | -------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------- |
2378
+ | `setStore` | `static setStore(store: IgniterCallerStoreAdapter, options?: IgniterCallerStoreOptions): void` | Configures persistent store adapter |
2379
+ | `getStore` | `static getStore(): IgniterCallerStoreAdapter \| null` | Gets configured store adapter |
2380
+ | `get` | `static async get<T>(key: string, staleTime?: number): Promise<T \| undefined>` | Gets cached data if not stale |
2381
+ | `set` | `static async set(key: string, data: unknown, ttl?: number): Promise<void>` | Stores data in cache |
2382
+ | `clear` | `static async clear(key: string): Promise<void>` | Clears specific cache entry |
2383
+ | `clearPattern` | `static async clearPattern(pattern: string): Promise<void>` | Clears entries matching pattern |
2384
+ | `clearAll` | `static async clearAll(): Promise<void>` | Clears all in-memory cache |
2385
+
2386
+ ---
2387
+
2388
+ ### 15. Telemetry & Observability Registry
2389
+
2390
+ #### 15.1 Telemetry Events
2391
+
2392
+ All telemetry events use the namespace `igniter.caller` and follow the `igniter.caller.<group>.<event>` naming convention.
2393
+
2394
+ | Event Key | Group | Description | Attributes |
2395
+ | --------------------------- | ---------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2396
+ | `request.execute.started` | request | Emitted when a request starts | `ctx.request.method`, `ctx.request.url`, `ctx.request.baseUrl`, `ctx.request.timeoutMs` |
2397
+ | `request.execute.success` | request | Emitted when a request succeeds | `ctx.request.method`, `ctx.request.url`, `ctx.request.durationMs`, `ctx.response.status`, `ctx.response.contentType`, `ctx.cache.hit`, `ctx.request.fallback` |
2398
+ | `request.execute.error` | request | Emitted when a request fails | `ctx.request.method`, `ctx.request.url`, `ctx.request.durationMs`, `ctx.response.status`, `ctx.error.code`, `ctx.error.message` |
2399
+ | `request.timeout.error` | request | Emitted when a request times out | `ctx.request.method`, `ctx.request.url`, `ctx.request.timeoutMs` |
2400
+ | `cache.read.hit` | cache | Emitted when cache hit occurs | `ctx.request.method`, `ctx.request.url`, `ctx.cache.key`, `ctx.cache.staleTime` |
2401
+ | `retry.attempt.started` | retry | Emitted before each retry attempt | `ctx.request.method`, `ctx.request.url`, `ctx.retry.attempt`, `ctx.retry.maxAttempts`, `ctx.retry.delayMs` |
2402
+ | `validation.request.error` | validation | Emitted when request validation fails | `ctx.request.method`, `ctx.request.url`, `ctx.validation.type`, `ctx.validation.error` |
2403
+ | `validation.response.error` | validation | Emitted when response validation fails | `ctx.request.method`, `ctx.request.url`, `ctx.validation.type`, `ctx.validation.error`, `ctx.response.status` |
2404
+
2405
+ #### 15.2 Attribute Naming Convention
2406
+
2407
+ All telemetry attributes follow the pattern `ctx.<domain>.<attribute>`:
2408
+
2409
+ - `ctx.request.*` - Request-related attributes (method, url, timeout)
2410
+ - `ctx.response.*` - Response-related attributes (status, contentType)
2411
+ - `ctx.cache.*` - Cache-related attributes (key, hit, staleTime)
2412
+ - `ctx.retry.*` - Retry-related attributes (attempt, maxAttempts, delayMs)
2413
+ - `ctx.validation.*` - Validation-related attributes (type, error)
2414
+ - `ctx.error.*` - Error-related attributes (code, message)
2415
+
2416
+ ---
2417
+
2418
+ ### 16. Type Reference Summary
2419
+
2420
+ #### 16.1 Core Types
2421
+
2422
+ | Type | Description |
2423
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------- |
2424
+ | `IgniterCallerApiResponse<T>` | Response envelope with `data?`, `error?`, `status?`, `headers?` |
2425
+ | `IgniterCallerFileResponse` | File download response with `file: File \| null`, `error: Error \| null` |
2426
+ | `IgniterCallerHttpMethod` | Union: `'GET' \| 'POST' \| 'PUT' \| 'PATCH' \| 'DELETE' \| 'HEAD'` |
2427
+ | `IgniterCallerUrlPattern` | Union: `string \| RegExp` |
2428
+ | `IgniterCallerEventCallback` | Signature: `(result: IgniterApiResponse, context: { url, method, timestamp }) => void \| Promise<void>` |
2429
+
2430
+ #### 16.2 Builder Types
2431
+
2432
+ | Type | Description |
2433
+ | ---------------------------------------------- | ------------------------------------------------------------------------------------- |
2434
+ | `IgniterCallerBuilderState<TSchemas>` | Builder state with baseURL, headers, cookies, interceptors, store, schemas, telemetry |
2435
+ | `IgniterCallerRequestBuilderParams` | Constructor params for request builder |
2436
+ | `IgniterCallerMethodRequestBuilder<TResponse>` | Request builder without internal `_setMethod` and `_setUrl` |
2437
+ | `IgniterCallerTypedRequestBuilder<TResponse>` | Typed request builder for HTTP methods |
2438
+
2439
+ #### 16.3 Schema Types
2440
+
2441
+ | Type | Description |
2442
+ | ----------------------------------------------------- | ------------------------------------------------------------------ |
2443
+ | `IgniterCallerSchemaMap` | Record of path → method → endpoint schema |
2444
+ | `IgniterCallerSchemaMethod` | Union: `'GET' \| 'POST' \| 'PUT' \| 'PATCH' \| 'DELETE' \| 'HEAD'` |
2445
+ | `IgniterCallerEndpointSchema<TRequest, TResponses>` | Endpoint with optional request schema and response schemas |
2446
+ | `IgniterCallerSchemaValidationOptions` | `{ mode?: 'strict' \| 'soft' \| 'off', onValidationError?: ... }` |
2447
+ | `IgniterCallerSchemaRegistry` | Record of reusable schema names to schemas |
2448
+ | `IgniterCallerSchemaBuildResult<TSchemas, TRegistry>` | Built schema map with `$Infer` and `get` helpers |
2449
+ | `IgniterCallerSchemaInfer<TSchemas, TRegistry>` | Type helpers: Path, Endpoint, Request, Response, Responses, Schema |
2450
+ | `IgniterCallerSchemaGetters<TSchemas, TRegistry>` | Runtime helpers: path, endpoint, request, response, schema |
2451
+
2452
+ #### 16.4 Configuration Types
2453
+
2454
+ | Type | Description |
2455
+ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
2456
+ | `IgniterCallerRequestOptions<TBody>` | Request configuration with method, url, body, params, headers, timeout, cache |
2457
+ | `IgniterCallerDirectRequestOptions<TBody>` | Axios-style options with all configurations in one object |
2458
+ | `IgniterCallerRetryOptions` | `{ maxAttempts: number, backoff?: 'linear' \| 'exponential', baseDelay?: number, retryOnStatus?: number[] }` |
2459
+ | `IgniterCallerStoreAdapter<TClient>` | Store adapter interface with `client`, `get`, `set`, `delete`, `has` methods |
2460
+ | `IgniterCallerStoreOptions` | `{ ttl?: number, keyPrefix?: string, fallbackToFetch?: boolean }` |
2461
+ | `IgniterCallerRequestInterceptor` | Signature: `(config: IgniterCallerRequestOptions) => Promise<IgniterCallerRequestOptions> \| IgniterCallerRequestOptions` |
2462
+ | `IgniterCallerResponseInterceptor` | Signature: `<T>(response: IgniterCallerApiResponse<T>) => Promise<IgniterCallerApiResponse<T>> \| IgniterCallerApiResponse<T>` |
2463
+
2464
+ #### 16.5 Content Type Types
2465
+
2466
+ | Type | Description |
2467
+ | ------------------------------------- | --------------------------------------------------------------------------------------------------- |
2468
+ | `IgniterCallerResponseContentType` | `'json' \| 'xml' \| 'csv' \| 'text' \| 'html' \| 'blob' \| 'stream' \| 'arraybuffer' \| 'formdata'` |
2469
+ | `IgniterCallerValidatableContentType` | `'json' \| 'xml' \| 'csv'` |
2470
+ | `IgniterCallerResponseMarker` | `File \| Blob \| ReadableStream \| ArrayBuffer \| FormData` |
2471
+
2472
+ ---
259
2473
 
260
- - [ ] `npm run build --filter @igniter-js/caller`
261
- - [ ] `npm test --filter @igniter-js/caller`
262
- - [ ] `npm run typecheck --filter @igniter-js/caller`
263
- - [ ] `npm run lint --filter @igniter-js/caller`
264
- - [ ] README and CHANGELOG are up-to-date
2474
+ ## End of Document
265
2475
 
266
- **Version policy:** never bump versions without user approval.
2476
+ This AGENTS.md document provides a complete reference for maintaining and using the `@igniter-js/caller` package. For updates or questions, refer to the package repository and contribution guidelines.