1000fetches 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LICENSE +21 -0
- package/dist/README.md +328 -0
- package/package.json +9 -11
package/dist/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Max Tarsis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
# 1000fetches
|
|
2
|
+
|
|
3
|
+
_Type-first HTTP client with schema validation, compile-time path safety, and streaming_
|
|
4
|
+
<br/>
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/1000fetches)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
> Built for the 1000th call to be as safe as the first
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### The problem
|
|
13
|
+
|
|
14
|
+
You start with `fetch`, then add a wrapper for error handling.
|
|
15
|
+
Then generics. Then path params. Then retries and timeouts.
|
|
16
|
+
Soon every project grows its own version — slightly different, equally fragile.
|
|
17
|
+
|
|
18
|
+
Alternatives? Tiny helpers that stop halfway,
|
|
19
|
+
or heavy Axios-style clients that add weight without type guarantees.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### The idea
|
|
24
|
+
|
|
25
|
+
A type-first HTTP client that unifies validation, retries, streaming, and middleware on top of native `fetch`.
|
|
26
|
+
<br/>
|
|
27
|
+
No magic, no layers of indirection. Just a single, explicit API that makes every request verifiably safe.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### The claim
|
|
32
|
+
|
|
33
|
+
> Schema-powered Fetch 2.0 — where types meet runtime reality
|
|
34
|
+
|
|
35
|
+
The point where TypeScript inference, runtime validation, and minimal design converge.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
### Highlights
|
|
40
|
+
|
|
41
|
+
- 🧭 **Compile-time path safety** — `:pathParam` can't slip through undefined
|
|
42
|
+
- 🧩 **Schema-driven validation** — infer types at build time, verify data at runtime
|
|
43
|
+
- ⚡ **Native streaming** — observe, transform, or pipe data chunks as they flow
|
|
44
|
+
- 🔁 **Retries, timeouts, middleware** — production essentials, zero config
|
|
45
|
+
- 🎯 **Method-based API** — `api.get()`, `api.post()` with `.data()` extractor for clean code
|
|
46
|
+
- 🧠 **Designed for flow** — clear API, predictable behavior, no hidden magic
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Quickstart
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install 1000fetches
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { createHttpClient } from '1000fetches'
|
|
57
|
+
import { z } from 'zod'
|
|
58
|
+
|
|
59
|
+
// Create client
|
|
60
|
+
const api = createHttpClient({
|
|
61
|
+
baseUrl: 'https://api.example.com',
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// Type-safe request with path params
|
|
65
|
+
const userResponse = await api.get('/users/:id', {
|
|
66
|
+
pathParams: { id: '123' },
|
|
67
|
+
schema: z.object({ name: z.string() }),
|
|
68
|
+
})
|
|
69
|
+
// user.data 👉 { name: string }
|
|
70
|
+
|
|
71
|
+
// Extractor
|
|
72
|
+
const user = await api
|
|
73
|
+
.get('/users/:id', {
|
|
74
|
+
pathParams: { id: '123' },
|
|
75
|
+
schema: z.object({ name: z.string() }),
|
|
76
|
+
})
|
|
77
|
+
.data() // Returns Promise<{ name: string }> directly, skipping the .data property
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Or use the default client for quick requests:**
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import http from '1000fetches'
|
|
84
|
+
|
|
85
|
+
const user = await http.get('/users/:id', {
|
|
86
|
+
pathParams: { id: '123' },
|
|
87
|
+
schema: userSchema,
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## ➡️ Key Features
|
|
92
|
+
|
|
93
|
+
### ✔️ Type Safety That Actually Works
|
|
94
|
+
|
|
95
|
+
- **Compile-time path validation** — TypeScript catches missing `:userId` parameters before runtime
|
|
96
|
+
- **Runtime schema validation** — Schemas verify data at the network boundary
|
|
97
|
+
- **Schema-based type inference** — Types inferred from schemas, no generics needed
|
|
98
|
+
- **Multi-schema support** — [Zod](https://github.com/colinhacks/zod), [Valibot](https://github.com/fabian-hiller/valibot), [ArkType](https://github.com/arktypeio/arktype), or any [Standard Schema](https://github.com/standard-schema/standard-schema)-compatible library
|
|
99
|
+
|
|
100
|
+
### ✔️ Production-Ready Quality
|
|
101
|
+
|
|
102
|
+
- **Smart retry logic** — Exponential backoff with jitter for resilient requests
|
|
103
|
+
- **Timeout and cancellation support** — Built-in `AbortController` integration
|
|
104
|
+
- **Structured error handling** — `HttpError`, `NetworkError`, `TimeoutError` with full context
|
|
105
|
+
- **Request/Response middleware** — Authentication, logging, transformations
|
|
106
|
+
- **Minimalistic footprint** — Enterprise features without the bloat
|
|
107
|
+
|
|
108
|
+
### ✔️ Engineer-Friendly DX
|
|
109
|
+
|
|
110
|
+
- **Method-based API** — `api.get()`, `api.post()` with full type safety
|
|
111
|
+
- **Automatic response parsing** — JSON/text responses parsed automatically
|
|
112
|
+
- **Smart data extraction** — Chain `.data()` for direct value access without `.data` property
|
|
113
|
+
- **Real-time streaming** — Access actual data chunks during upload/download
|
|
114
|
+
- **Zero dependencies** — Optional peer dependencies, tree-shakable builds
|
|
115
|
+
- **TypeScript-first** — Full type inference and `IntelliSense` support
|
|
116
|
+
|
|
117
|
+
**1000fetches** combines native `fetch` performance with enterprise-grade features and bulletproof type safety.
|
|
118
|
+
|
|
119
|
+
## ➡️ Usage Examples
|
|
120
|
+
|
|
121
|
+
### Authentication
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const api = createHttpClient({
|
|
125
|
+
baseUrl: 'https://api.example.com',
|
|
126
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Or dynamic auth
|
|
130
|
+
const api = createHttpClient({
|
|
131
|
+
baseUrl: 'https://api.example.com',
|
|
132
|
+
onRequestMiddleware: async context => {
|
|
133
|
+
const token = await getToken()
|
|
134
|
+
context.headers.set('Authorization', `Bearer ${token}`)
|
|
135
|
+
return context
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Streaming
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// Upload progress with actual data chunks
|
|
144
|
+
await api.post('/api/files', fileData, {
|
|
145
|
+
onUploadStreaming: ({ chunk, transferredBytes, totalBytes }) => {
|
|
146
|
+
const progress = Math.round((transferredBytes / totalBytes) * 100)
|
|
147
|
+
console.log(`Uploading: ${progress}% - chunk size: ${chunk.length} bytes`)
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// Download progress
|
|
152
|
+
await api.get('/api/files/:id', {
|
|
153
|
+
pathParams: { id: '123' },
|
|
154
|
+
onDownloadStreaming: ({ chunk, transferredBytes, totalBytes }) => {
|
|
155
|
+
const progress = Math.round((transferredBytes / totalBytes) * 100)
|
|
156
|
+
console.log(`Downloading: ${progress}% - chunk size: ${chunk.length} bytes`)
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Error Handling
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { HttpError, NetworkError, TimeoutError, MiddlewareError, PathParameterError } from '1000fetches'
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const user = await api.get('/users/:id', {
|
|
168
|
+
pathParams: { id: '123' },
|
|
169
|
+
schema: userSchema,
|
|
170
|
+
})
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (error instanceof HttpError) {
|
|
173
|
+
console.log(`HTTP ${error.status}: ${error.statusText}`)
|
|
174
|
+
} else if (error instanceof NetworkError) {
|
|
175
|
+
console.log('Network error:', error.message)
|
|
176
|
+
} else if (error instanceof TimeoutError) {
|
|
177
|
+
console.log('Request timed out')
|
|
178
|
+
} else if (error instanceof MiddlewareError) {
|
|
179
|
+
console.log('Middleware error:', error.message)
|
|
180
|
+
} else if (error instanceof PathParameterError) {
|
|
181
|
+
console.log('Path parameter error:', error.message)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## API Reference
|
|
187
|
+
|
|
188
|
+
### <samp>createHttpClient(config?)</samp>
|
|
189
|
+
|
|
190
|
+
Creates a new HTTP client with optional configuration.
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
function createHttpClient(config?: HttpClientConfig): HttpClient
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Configuration Options:**
|
|
197
|
+
|
|
198
|
+
| Option | Type | Description |
|
|
199
|
+
| ----------------------------- | --------------------------------------- | ------------------------------------- |
|
|
200
|
+
| `baseUrl` | `string` | Base URL for all requests |
|
|
201
|
+
| `headers` | `Record<string, string>` | Default headers |
|
|
202
|
+
| `timeout` | `number` | Default timeout in milliseconds |
|
|
203
|
+
| `retryOptions` | `RetryOptions` | Default retry configuration |
|
|
204
|
+
| `onRequestMiddleware` | `(context: RequestContext) => RequestContext` | Request middleware |
|
|
205
|
+
| `onResponseMiddleware` | `(response: ResponseType) => ResponseType` | Response middleware |
|
|
206
|
+
| `schemaValidator` | `SchemaValidator` | Custom schema validator |
|
|
207
|
+
|
|
208
|
+
### <samp>HTTP Methods</samp>
|
|
209
|
+
|
|
210
|
+
All HTTP methods support the same options:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
await api.get('/users/:id', {
|
|
214
|
+
pathParams: { id: '123' },
|
|
215
|
+
schema: userSchema,
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// POST
|
|
219
|
+
await api.post('/users', userData, {
|
|
220
|
+
schema: userSchema,
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// PUT
|
|
224
|
+
await api.put('/users/:id', userData, {
|
|
225
|
+
pathParams: { id: '123' },
|
|
226
|
+
schema: userSchema,
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
// PATCH
|
|
230
|
+
await api.patch('/users/:id', partialData, {
|
|
231
|
+
pathParams: { id: '123' },
|
|
232
|
+
schema: userSchema,
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// DELETE
|
|
236
|
+
await api.delete('/users/:id', {
|
|
237
|
+
pathParams: { id: '123' },
|
|
238
|
+
})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### <samp>Request Options</samp>
|
|
242
|
+
|
|
243
|
+
| Option | Type | Description |
|
|
244
|
+
| ----------------------------- | --------------------------------------- | ------------------------------------- |
|
|
245
|
+
| `pathParams` | `Record<string, string \| number>` | Path parameters for URL templates |
|
|
246
|
+
| `params` | `Record<string, string \| number \| boolean \| undefined>` | Query parameters |
|
|
247
|
+
| `headers` | `Record<string, string>` | Request headers |
|
|
248
|
+
| `body` | `any` | Request body |
|
|
249
|
+
| `schema` | `Schema` | Response validation schema |
|
|
250
|
+
| `timeout` | `number` | Request timeout |
|
|
251
|
+
| `signal` | `AbortSignal` | Request cancellation signal |
|
|
252
|
+
| `validateStatus` | `(status: number) => boolean` | Custom status validation |
|
|
253
|
+
| `responseType` | `'text' \| 'blob' \| 'arrayBuffer'` | Response type override |
|
|
254
|
+
| `cache` | `RequestCache` | Cache mode |
|
|
255
|
+
| `credentials` | `RequestCredentials` | Credentials mode |
|
|
256
|
+
| `mode` | `RequestMode` | Request mode |
|
|
257
|
+
| `redirect` | `RequestRedirect` | Redirect mode |
|
|
258
|
+
| `retryOptions` | `RetryOptions` | Retry configuration |
|
|
259
|
+
| `onUploadStreaming` | `(event: UploadStreamingEvent) => void` | Upload streaming callback |
|
|
260
|
+
| `onDownloadStreaming` | `(event: DownloadStreamingEvent) => void` | Download streaming callback |
|
|
261
|
+
|
|
262
|
+
### <samp>Response Object</samp>
|
|
263
|
+
|
|
264
|
+
All requests return a `ResponseType<T>` object:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
interface ResponseType<T> {
|
|
268
|
+
data: T // Parsed response data
|
|
269
|
+
status: number // HTTP status code
|
|
270
|
+
statusText: string // HTTP status text
|
|
271
|
+
headers: Record<string, string> // Response headers
|
|
272
|
+
method: HttpMethod // HTTP method used
|
|
273
|
+
url: string // Final URL
|
|
274
|
+
raw: Response // Raw fetch Response
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### <samp>Data Extractor</samp>
|
|
279
|
+
|
|
280
|
+
For convenience, you can extract data directly:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
const user = await api
|
|
284
|
+
.get('/users/:id', {
|
|
285
|
+
pathParams: { id: '123' },
|
|
286
|
+
schema: userSchema,
|
|
287
|
+
})
|
|
288
|
+
.data() // Returns Promise<User> instead of ResponseType<User>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## ➡️ Feature Comparison
|
|
292
|
+
|
|
293
|
+
**1000fetches vs Popular Alternatives**
|
|
294
|
+
|
|
295
|
+
| Feature | 1000fetches | Axios | Better-fetch | Up-fetch | Native Fetch |
|
|
296
|
+
| --------------------- | ----------------- | --------------- | ------------------ | ------------------ | ------------------ |
|
|
297
|
+
| **Bundle Size (gz)** | ≈4.3 kB | ≈14.75 kB | ≈3.07 kB | ≈1.6 kB | 0 kB |
|
|
298
|
+
| **TypeScript** | ✅ Full inference | ⚠️ Limited | ⚠️ Limited | ✅ Good | ❌ Manual |
|
|
299
|
+
| **Path Params** | ✅ Compile-time | ❌ Manual | ❌ Manual | ❌ Manual | ❌ Manual |
|
|
300
|
+
| **Schema Validation** | ✅ Multi-library | ❌ None | ⚠️ Limited | ✅ Multi-library | ❌ None |
|
|
301
|
+
| **Retry Logic** | ✅ Built-in | ✅ Built-in | ❌ Manual | ✅ Built-in | ❌ Manual |
|
|
302
|
+
| **Error Handling** | ✅ Structured | ✅ Good | ⚠️ Basic | ✅ Good | ⚠️ Verbose |
|
|
303
|
+
| **Middleware** | ✅ Full support | ✅ Full support | ⚠️ Limited | ✅ Lifecycle hooks | ❌ None |
|
|
304
|
+
| **Streaming** | ✅ Real chunks | ❌ None | ❌ None | ✅ Real chunks | ✅ Native |
|
|
305
|
+
| **API Design** | ✅ Method-based | ✅ Method-based | ❌ Single function | ❌ Single function | ❌ Single function |
|
|
306
|
+
| **Tree Shaking** | ✅ Good | ⚠️ Partial | ✅ Good | ✅ Perfect | ✅ |
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 📄 License
|
|
311
|
+
|
|
312
|
+
MIT License - see [LICENSE](./LICENSE) for details.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
<div align="center">
|
|
317
|
+
|
|
318
|
+
**Built for developers who believe type safety shouldn't be optional.**
|
|
319
|
+
**Because your backend deserves skepticism.**
|
|
320
|
+
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<!-- Links -->
|
|
324
|
+
|
|
325
|
+
[zod]: https://zod.dev/
|
|
326
|
+
[valibot]: https://valibot.dev/
|
|
327
|
+
[arktype]: https://arktype.dev/
|
|
328
|
+
[standard-schema]: https://github.com/standard-schema/standard-schema
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "1000fetches",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "A type-first HTTP client with compile-time path validation, schema validation, middleware, retries, and real-time streaming — powered by native fetch. Supports Zod, Valibot, ArkType, and any Standard Schema-compatible library.",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"sideEffects": false,
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "tsc && vite build",
|
|
20
|
+
"build": "tsc && vite build && node scripts/copy-assets.js",
|
|
21
21
|
"dev": "vite build --watch",
|
|
22
22
|
"test": "vitest --run",
|
|
23
23
|
"test:watch": "vitest",
|
|
@@ -25,9 +25,7 @@
|
|
|
25
25
|
"tsc": "tsc --noEmit",
|
|
26
26
|
"lint": "prettier --check .",
|
|
27
27
|
"lint:fix": "prettier --write .",
|
|
28
|
-
"
|
|
29
|
-
"bundle:size": "node ../../scripts/bundle-size.js",
|
|
30
|
-
"bundle": "LIB_BUILD=true vite build && yarn bundle:size",
|
|
28
|
+
"copy-assets": "node scripts/copy-assets.js",
|
|
31
29
|
"clean": "rm -rf dist coverage"
|
|
32
30
|
},
|
|
33
31
|
"keywords": [
|
|
@@ -43,27 +41,27 @@
|
|
|
43
41
|
"valibot",
|
|
44
42
|
"arktype"
|
|
45
43
|
],
|
|
46
|
-
"author": "
|
|
44
|
+
"author": "kioviensis",
|
|
47
45
|
"license": "MIT",
|
|
48
46
|
"repository": {
|
|
49
47
|
"type": "git",
|
|
50
|
-
"url": "https://github.com/
|
|
48
|
+
"url": "git+https://github.com/kioviensis/1000fetches.git"
|
|
51
49
|
},
|
|
52
50
|
"bugs": {
|
|
53
|
-
"url": "https://github.com/
|
|
51
|
+
"url": "https://github.com/kioviensis/1000fetches/issues"
|
|
54
52
|
},
|
|
55
|
-
"homepage": "https://github.com/
|
|
53
|
+
"homepage": "https://github.com/kioviensis/1000fetches#readme",
|
|
56
54
|
"devDependencies": {
|
|
57
55
|
"@types/node": "22.18.9",
|
|
58
56
|
"@vitest/coverage-v8": "3.2.4",
|
|
59
|
-
"arktype": "2.1.
|
|
57
|
+
"arktype": "2.1.23",
|
|
60
58
|
"expect-type": "1.2.2",
|
|
61
59
|
"msw": "2.11.5",
|
|
62
60
|
"rollup-plugin-visualizer": "6.0.4",
|
|
63
61
|
"terser": "5.44.0",
|
|
64
62
|
"typescript": "5.9.3",
|
|
65
63
|
"valibot": "1.1.0",
|
|
66
|
-
"vite": "7.1.
|
|
64
|
+
"vite": "7.1.10",
|
|
67
65
|
"vite-plugin-dts": "4.5.4",
|
|
68
66
|
"vitest": "3.2.4",
|
|
69
67
|
"zod": "4.1.12"
|