@igniter-js/caller 0.1.0

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 ADDED
@@ -0,0 +1,124 @@
1
+ # @igniter-js/caller - AI Agent Instructions
2
+
3
+ > **Package Version:** 0.1.0
4
+ > **Last Updated:** 2025-12-13
5
+ > **Status:** Ready for Publication
6
+
7
+ ---
8
+
9
+ ## Package Overview
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)
14
+
15
+ ### Core Features
16
+
17
+ - Fluent request builder API (`IgniterCaller.create()` + `.get()/.post()/.request()`)
18
+ - Request and response interceptors
19
+ - Retry support (linear/exponential backoff + status-based retries)
20
+ - Caching (in-memory + optional persistent store adapter)
21
+ - Global response events (observe responses across the app)
22
+ - Schema validation using `StandardSchemaV1` from `@igniter-js/core`
23
+ - Optional Zod response validation via `responseType(zodSchema)`
24
+
25
+ ---
26
+
27
+ ## Architecture
28
+
29
+ This package is intentionally small and split into predictable layers:
30
+
31
+ - **Core runtime**: `src/core/igniter-caller.ts`
32
+ - **Builders**: `src/builder/*`
33
+ - **Types**: `src/types/*`
34
+ - **Errors**: `src/errors/*`
35
+ - **Utilities**: `src/utils/*`
36
+
37
+ ### Design Principles
38
+
39
+ 1. **Fetch-first**
40
+ - Uses the global `fetch` API.
41
+ - Works in Node.js (18+), Bun, and modern runtimes.
42
+
43
+ 2. **Typed error surface**
44
+ - Predictable errors are `IgniterCallerError` with stable error codes.
45
+
46
+ 3. **Separation of concerns**
47
+ - `IgniterCallerRequestBuilder` focuses on request composition and execution.
48
+ - `IgniterCallerCacheUtils` focuses on caching.
49
+ - `IgniterCallerSchemaUtils` focuses on schema matching and validation.
50
+
51
+ 4. **Stable public API**
52
+ - Keep exports in `src/index.ts` stable and backwards-compatible.
53
+
54
+ ---
55
+
56
+ ## File Structure
57
+
58
+ ```
59
+ packages/caller/
60
+ ├── src/
61
+ │ ├── index.ts
62
+ │ ├── core/
63
+ │ ├── builder/
64
+ │ ├── errors/
65
+ │ ├── types/
66
+ │ └── utils/
67
+ ├── package.json
68
+ ├── tsconfig.json
69
+ ├── tsup.config.ts
70
+ ├── vitest.config.ts
71
+ ├── README.md
72
+ ├── CHANGELOG.md
73
+ └── AGENTS.md
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Development Guidelines
79
+
80
+ ### Adding features
81
+
82
+ - If it changes the runtime behavior of request execution, prefer implementing inside `IgniterCallerRequestBuilder`.
83
+ - If it changes caching behavior, prefer implementing in `IgniterCallerCacheUtils`.
84
+ - If it changes schema matching/validation, implement in `IgniterCallerSchemaUtils`.
85
+ - Keep new public options discoverable from the builder API.
86
+
87
+ ### Error handling
88
+
89
+ - Use `IgniterCallerError` for predictable failures.
90
+ - Prefer stable `code` values over new ad-hoc error messages.
91
+
92
+ ### Dependencies
93
+
94
+ - `@igniter-js/core` is a **peer dependency** (for shared types like `IgniterError` and `StandardSchemaV1`).
95
+ - `zod` is a **peer dependency** (used when consumers call `responseType(zodSchema)` and referenced in public types).
96
+
97
+ ---
98
+
99
+ ## Testing Strategy
100
+
101
+ - Mock `globalThis.fetch` for request execution tests.
102
+ - Prefer tests that validate:
103
+ - Request schema validation (strict mode) blocks the network call.
104
+ - Response schema validation returns `IgniterCallerError` on invalid payload.
105
+ - Caching returns cached data without calling `fetch` again.
106
+ - Event emission happens for responses.
107
+
108
+ Run tests:
109
+
110
+ ```bash
111
+ npm test --filter @igniter-js/caller
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Publishing Checklist
117
+
118
+ - [ ] `npm run build --filter @igniter-js/caller`
119
+ - [ ] `npm test --filter @igniter-js/caller`
120
+ - [ ] `npm run typecheck --filter @igniter-js/caller`
121
+ - [ ] `npm run lint --filter @igniter-js/caller`
122
+ - [ ] README and CHANGELOG are up-to-date
123
+
124
+ **Version policy:** never bump versions without user approval.
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package will be documented in this file.
4
+
5
+ ## 0.1.0
6
+
7
+ - Initial release of `@igniter-js/caller`.
8
+ - Type-safe HTTP client built on `fetch`.
9
+ - Fluent builder API (`IgniterCaller.create()` + request builder).
10
+ - Request/response interceptors.
11
+ - Retry support with linear/exponential backoff.
12
+ - In-memory caching with optional store adapter.
13
+ - Schema validation via `StandardSchemaV1` (and optional Zod `responseType`).
14
+ - Global response events for observability.
package/README.md ADDED
@@ -0,0 +1,233 @@
1
+ # @igniter-js/caller
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/@igniter-js/caller.svg)](https://www.npmjs.com/package/@igniter-js/caller)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Type-safe HTTP client for Igniter.js apps. Built on top of `fetch`, it gives you a fluent request builder, interceptors, retries, caching (memory or store), schema validation (Standard Schema V1), and global response events.
7
+
8
+ ## Features
9
+
10
+ - ✅ **Fluent API** - `IgniterCaller.create().withBaseUrl(...).build()`
11
+ - ✅ **Interceptors** - modify requests and responses in one place
12
+ - ✅ **Retries** - linear or exponential backoff + status-based retry
13
+ - ✅ **Caching** - in-memory cache + optional persistent store adapter
14
+ - ✅ **Schema Validation** - validate request/response using `StandardSchemaV1`
15
+ - ✅ **Zod Support** - optional per-request `responseType(zodSchema)` validation
16
+ - ✅ **Global Events** - observe responses for logging/telemetry/cache invalidation
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ # npm
22
+ npm install @igniter-js/caller @igniter-js/core zod
23
+
24
+ # pnpm
25
+ pnpm add @igniter-js/caller @igniter-js/core zod
26
+
27
+ # yarn
28
+ yarn add @igniter-js/caller @igniter-js/core zod
29
+
30
+ # bun
31
+ bun add @igniter-js/caller @igniter-js/core zod
32
+ ```
33
+
34
+ > `@igniter-js/core` and `zod` are peer dependencies.
35
+
36
+ ## Quick Start
37
+
38
+ ```ts
39
+ import { IgniterCaller } from '@igniter-js/caller'
40
+
41
+ export const api = IgniterCaller.create()
42
+ .withBaseUrl('https://api.example.com')
43
+ .withHeaders({ Authorization: `Bearer ${process.env.API_TOKEN}` })
44
+ .build()
45
+
46
+ const result = await api
47
+ .get()
48
+ .url('/users')
49
+ .params({ page: 1 })
50
+ .stale(10_000) // cache for 10s
51
+ .execute<{ id: string; name: string }[]>()
52
+
53
+ if (result.error) {
54
+ throw result.error
55
+ }
56
+
57
+ console.log(result.data)
58
+ ```
59
+
60
+ ## Interceptors
61
+
62
+ Interceptors are great for cross-cutting concerns like auth headers, request ids, logging, and response normalization.
63
+
64
+ ```ts
65
+ import { IgniterCaller } from '@igniter-js/caller'
66
+
67
+ const api = IgniterCaller.create()
68
+ .withBaseUrl('https://api.example.com')
69
+ .withRequestInterceptor(async (request) => {
70
+ return {
71
+ ...request,
72
+ headers: {
73
+ ...request.headers,
74
+ 'x-request-id': crypto.randomUUID(),
75
+ },
76
+ }
77
+ })
78
+ .withResponseInterceptor(async (response) => {
79
+ // Example: normalize empty responses
80
+ if (response.data === '') {
81
+ return { ...response, data: null as any }
82
+ }
83
+
84
+ return response
85
+ })
86
+ .build()
87
+ ```
88
+
89
+ ## Retries
90
+
91
+ Configure retry behavior for transient errors:
92
+
93
+ ```ts
94
+ const result = await api
95
+ .get()
96
+ .url('/health')
97
+ .retry(3, {
98
+ baseDelay: 250,
99
+ backoff: 'exponential',
100
+ retryOnStatus: [408, 429, 500, 502, 503, 504],
101
+ })
102
+ .execute()
103
+ ```
104
+
105
+ ## Caching
106
+
107
+ ### In-memory caching
108
+
109
+ Use `.stale(ms)` to enable caching. The cache key defaults to the request URL, or you can set it via `.cache(cache, key)`.
110
+
111
+ ```ts
112
+ const users = await api.get().url('/users').stale(30_000).execute()
113
+ ```
114
+
115
+ ### Store-based caching
116
+
117
+ You can plug any store that matches `IgniterCallerStoreAdapter` (Redis, etc.).
118
+
119
+ ```ts
120
+ import { IgniterCaller } from '@igniter-js/caller'
121
+
122
+ const store = {
123
+ client: null,
124
+ async get(key) {
125
+ return null
126
+ },
127
+ async set(key, value) {
128
+ void key
129
+ void value
130
+ },
131
+ async delete(key) {
132
+ void key
133
+ },
134
+ async has(key) {
135
+ void key
136
+ return false
137
+ },
138
+ }
139
+
140
+ const api = IgniterCaller.create().withStore(store, {
141
+ ttl: 3600,
142
+ keyPrefix: 'igniter:caller:',
143
+ }).build()
144
+ ```
145
+
146
+ ## Schema Validation (StandardSchemaV1)
147
+
148
+ If you already use schemas in your Igniter.js app, you can validate requests and responses automatically.
149
+
150
+ ```ts
151
+ import { IgniterCaller } from '@igniter-js/caller'
152
+ import { z } from 'zod'
153
+
154
+ const schemas = {
155
+ '/users/:id': {
156
+ GET: {
157
+ responses: {
158
+ 200: z.object({ id: z.string(), name: z.string() }),
159
+ },
160
+ },
161
+ },
162
+ } as const
163
+
164
+ const api = IgniterCaller.create()
165
+ .withBaseUrl('https://api.example.com')
166
+ .withSchemas(schemas, { mode: 'strict' })
167
+ .build()
168
+
169
+ const result = await api.get().url('/users/123').execute()
170
+ ```
171
+
172
+ ## Zod `responseType()`
173
+
174
+ For one-off validation, you can attach a Zod schema to a request:
175
+
176
+ ```ts
177
+ import { z } from 'zod'
178
+
179
+ const result = await api
180
+ .get()
181
+ .url('/users')
182
+ .responseType(z.array(z.object({ id: z.string(), name: z.string() })))
183
+ .execute()
184
+ ```
185
+
186
+ ## Global Events
187
+
188
+ You can observe responses globally using `IgniterCaller.on()`:
189
+
190
+ ```ts
191
+ import { IgniterCaller } from '@igniter-js/caller'
192
+
193
+ const unsubscribe = IgniterCaller.on(/^\/users/, (result, ctx) => {
194
+ console.log(`[${ctx.method}] ${ctx.url}`, {
195
+ ok: !result.error,
196
+ })
197
+ })
198
+
199
+ // later
200
+ unsubscribe()
201
+ ```
202
+
203
+ ## Error Handling
204
+
205
+ All predictable failures return an `IgniterCallerError` with stable error codes.
206
+
207
+ ```ts
208
+ import { IgniterCallerError } from '@igniter-js/caller'
209
+
210
+ const result = await api.get().url('/users').execute()
211
+
212
+ if (result.error) {
213
+ if (IgniterCallerError.is(result.error)) {
214
+ console.error(result.error.code, result.error.operation)
215
+ }
216
+ throw result.error
217
+ }
218
+ ```
219
+
220
+ ## Contributing
221
+
222
+ Contributions are welcome! Please see the main [CONTRIBUTING.md](https://github.com/felipebarcelospro/igniter-js/blob/main/CONTRIBUTING.md) for details.
223
+
224
+ ## License
225
+
226
+ MIT License - see [LICENSE](https://github.com/felipebarcelospro/igniter-js/blob/main/LICENSE) for details.
227
+
228
+ ## Links
229
+
230
+ - **Documentation:** https://igniterjs.com/docs
231
+ - **GitHub:** https://github.com/felipebarcelospro/igniter-js
232
+ - **NPM:** https://www.npmjs.com/package/@igniter-js/caller
233
+ - **Issues:** https://github.com/felipebarcelospro/igniter-js/issues