@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 +124 -0
- package/CHANGELOG.md +14 -0
- package/README.md +233 -0
- package/dist/index.d.mts +738 -0
- package/dist/index.d.ts +738 -0
- package/dist/index.js +1343 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1332 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +70 -0
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
|
+
[](https://www.npmjs.com/package/@igniter-js/caller)
|
|
4
|
+
[](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
|