1000fetches 0.1.11 → 0.2.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/{dist/README.md → README.md} +145 -83
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +36 -66
- package/dist/index.es.js +315 -155
- package/dist/index.es.js.map +1 -1
- package/package.json +8 -6
- /package/{dist/LICENSE → LICENSE} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# 1000fetches
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
_Schema-powered Fetch 2.0 — where types meet runtime reality_
|
|
4
4
|
<br/>
|
|
5
5
|
|
|
6
6
|
[](https://www.npmjs.com/package/1000fetches)
|
|
7
7
|
[](https://www.typescriptlang.org/)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
|
-
> Built for the 1000th call to be as safe as the first
|
|
10
9
|
|
|
10
|
+
> Built for the 1000th call to be as safe as the first
|
|
11
11
|
|
|
12
12
|
### The problem
|
|
13
13
|
|
|
@@ -15,7 +15,7 @@ You start with `fetch`, then add a wrapper for error handling.
|
|
|
15
15
|
Then generics. Then path params. Then retries and timeouts.
|
|
16
16
|
Soon every project grows its own version — slightly different, equally fragile.
|
|
17
17
|
|
|
18
|
-
Alternatives
|
|
18
|
+
**Alternatives**? Tiny helpers that stop halfway,
|
|
19
19
|
or heavy Axios-style clients that add weight without type guarantees.
|
|
20
20
|
|
|
21
21
|
---
|
|
@@ -23,16 +23,6 @@ or heavy Axios-style clients that add weight without type guarantees.
|
|
|
23
23
|
### The idea
|
|
24
24
|
|
|
25
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
26
|
|
|
37
27
|
---
|
|
38
28
|
|
|
@@ -42,8 +32,9 @@ The point where TypeScript inference, runtime validation, and minimal design con
|
|
|
42
32
|
- 🧩 **Schema-driven validation** — infer types at build time, verify data at runtime
|
|
43
33
|
- ⚡ **Native streaming** — observe, transform, or pipe data chunks as they flow
|
|
44
34
|
- 🔁 **Retries, timeouts, middleware** — production essentials, zero config
|
|
45
|
-
- 🎯 **Method-based API** — `api.get()
|
|
35
|
+
- 🎯 **Method-based API** — `api.get()` with schema validation `.schema()` and extractor `.data()` for clean and concise code
|
|
46
36
|
- 🧠 **Designed for flow** — clear API, predictable behavior, no hidden magic
|
|
37
|
+
|
|
47
38
|
---
|
|
48
39
|
|
|
49
40
|
## Quickstart
|
|
@@ -61,20 +52,28 @@ const api = createHttpClient({
|
|
|
61
52
|
baseUrl: 'https://api.example.com',
|
|
62
53
|
})
|
|
63
54
|
|
|
64
|
-
//
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
55
|
+
// Define schemas for types and safety
|
|
56
|
+
const userSchema = z.object({
|
|
57
|
+
id: z.number(),
|
|
58
|
+
...
|
|
68
59
|
})
|
|
69
|
-
// user.data 👉 { name: string }
|
|
70
60
|
|
|
71
|
-
//
|
|
61
|
+
// Request data with schema validation
|
|
62
|
+
const userResponse = await api
|
|
63
|
+
.get('/users/:id', {
|
|
64
|
+
pathParams: { id: '123' },
|
|
65
|
+
})
|
|
66
|
+
.schema(userSchema)
|
|
67
|
+
// userResponse.data 👉 { id: number, ... }
|
|
68
|
+
|
|
69
|
+
// Clean data extraction with full type safety
|
|
72
70
|
const user = await api
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
.get('/users/:id', {
|
|
72
|
+
pathParams: { id: '123' },
|
|
73
|
+
})
|
|
74
|
+
.schema(userSchema)
|
|
75
|
+
.data()
|
|
76
|
+
// user 👉 { id: number, ... }
|
|
78
77
|
```
|
|
79
78
|
|
|
80
79
|
**Or use the default client for quick requests:**
|
|
@@ -82,10 +81,11 @@ const user = await api
|
|
|
82
81
|
```ts
|
|
83
82
|
import http from '1000fetches'
|
|
84
83
|
|
|
85
|
-
const user = await http
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
})
|
|
84
|
+
const user = await http
|
|
85
|
+
.get('/users/:id', {
|
|
86
|
+
pathParams: { id: '123' },
|
|
87
|
+
})
|
|
88
|
+
.schema(userSchema)
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
## ➡️ Key Features
|
|
@@ -107,9 +107,10 @@ const user = await http.get('/users/:id', {
|
|
|
107
107
|
|
|
108
108
|
### ✔️ Engineer-Friendly DX
|
|
109
109
|
|
|
110
|
-
- **Method-based API** — `api.get()`, `api.post()` with full type safety
|
|
111
|
-
- **
|
|
110
|
+
- **Method-based API** — `api.get()`, `api.post()`, `api.put()`, `api.patch()`, `api.delete()`, and generic `api.request()` with full type safety
|
|
111
|
+
- **Schema-first validation** — Chain `.schema()` for runtime validation and automatic type inference
|
|
112
112
|
- **Smart data extraction** — Chain `.data()` for direct value access without `.data` property
|
|
113
|
+
- **Automatic response parsing** — JSON/text responses parsed automatically
|
|
113
114
|
- **Real-time streaming** — Access actual data chunks during upload/download
|
|
114
115
|
- **Zero dependencies** — Optional peer dependencies, tree-shakable builds
|
|
115
116
|
- **TypeScript-first** — Full type inference and `IntelliSense` support
|
|
@@ -161,13 +162,20 @@ await api.get('/api/files/:id', {
|
|
|
161
162
|
### Error Handling
|
|
162
163
|
|
|
163
164
|
```ts
|
|
164
|
-
import {
|
|
165
|
+
import {
|
|
166
|
+
HttpError,
|
|
167
|
+
NetworkError,
|
|
168
|
+
TimeoutError,
|
|
169
|
+
MiddlewareError,
|
|
170
|
+
PathParameterError,
|
|
171
|
+
} from '1000fetches'
|
|
165
172
|
|
|
166
173
|
try {
|
|
167
|
-
const user = await api
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
174
|
+
const user = await api
|
|
175
|
+
.get('/users/:id', {
|
|
176
|
+
pathParams: { id: '123' },
|
|
177
|
+
})
|
|
178
|
+
.schema(userSchema)
|
|
171
179
|
} catch (error) {
|
|
172
180
|
if (error instanceof HttpError) {
|
|
173
181
|
console.log(`HTTP ${error.status}: ${error.statusText}`)
|
|
@@ -195,42 +203,54 @@ function createHttpClient(config?: HttpClientConfig): HttpClient
|
|
|
195
203
|
|
|
196
204
|
**Configuration Options:**
|
|
197
205
|
|
|
198
|
-
| Option
|
|
199
|
-
|
|
|
200
|
-
| `baseUrl`
|
|
201
|
-
| `headers`
|
|
202
|
-
| `timeout`
|
|
203
|
-
| `retryOptions`
|
|
204
|
-
| `onRequestMiddleware`
|
|
205
|
-
| `onResponseMiddleware`
|
|
206
|
-
| `schemaValidator`
|
|
206
|
+
| Option | Type | Description |
|
|
207
|
+
| ---------------------- | --------------------------------------------- | ------------------------------- |
|
|
208
|
+
| `baseUrl` | `string` | Base URL for all requests |
|
|
209
|
+
| `headers` | `Record<string, string>` | Default headers |
|
|
210
|
+
| `timeout` | `number` | Default timeout in milliseconds |
|
|
211
|
+
| `retryOptions` | `RetryOptions` | Default retry configuration |
|
|
212
|
+
| `onRequestMiddleware` | `(context: RequestContext) => RequestContext` | Request middleware |
|
|
213
|
+
| `onResponseMiddleware` | `(response: ResponseType) => ResponseType` | Response middleware |
|
|
214
|
+
| `schemaValidator` | `SchemaValidator` | Custom schema validator |
|
|
207
215
|
|
|
208
216
|
### <samp>HTTP Methods</samp>
|
|
209
217
|
|
|
210
|
-
All HTTP methods support
|
|
218
|
+
All HTTP methods support schema validation and data extraction:
|
|
211
219
|
|
|
212
220
|
```ts
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
221
|
+
// GET
|
|
222
|
+
const user = await api
|
|
223
|
+
.get('/users/:id', {
|
|
224
|
+
pathParams: { id: '123' },
|
|
225
|
+
})
|
|
226
|
+
.schema(userSchema)
|
|
227
|
+
.data()
|
|
228
|
+
// user 👉 Fully typed user object
|
|
217
229
|
|
|
218
230
|
// POST
|
|
219
|
-
await api
|
|
220
|
-
|
|
221
|
-
|
|
231
|
+
const newUser = await api
|
|
232
|
+
.post('/users', userData)
|
|
233
|
+
.schema(userSchema)
|
|
234
|
+
.data()
|
|
235
|
+
// newUser 👉 Fully typed user object
|
|
222
236
|
|
|
223
237
|
// PUT
|
|
224
|
-
await api
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
})
|
|
238
|
+
const updatedUser = await api
|
|
239
|
+
.put('/users/:id', userData, {
|
|
240
|
+
pathParams: { id: '123' },
|
|
241
|
+
})
|
|
242
|
+
.schema(userSchema)
|
|
243
|
+
.data()
|
|
244
|
+
// updatedUser 👉 Fully typed user object
|
|
228
245
|
|
|
229
246
|
// PATCH
|
|
230
|
-
await api
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
})
|
|
247
|
+
const patchedUser = await api
|
|
248
|
+
.patch('/users/:id', partialData, {
|
|
249
|
+
pathParams: { id: '123' },
|
|
250
|
+
})
|
|
251
|
+
.schema(userSchema)
|
|
252
|
+
.data()
|
|
253
|
+
// patchedUser 👉 Fully typed user object
|
|
234
254
|
|
|
235
255
|
// DELETE
|
|
236
256
|
await api.delete('/users/:id', {
|
|
@@ -240,24 +260,23 @@ await api.delete('/users/:id', {
|
|
|
240
260
|
|
|
241
261
|
### <samp>Request Options</samp>
|
|
242
262
|
|
|
243
|
-
| Option
|
|
244
|
-
|
|
|
245
|
-
| `pathParams`
|
|
246
|
-
| `params`
|
|
247
|
-
| `headers`
|
|
248
|
-
| `body`
|
|
249
|
-
| `
|
|
250
|
-
| `
|
|
251
|
-
| `
|
|
252
|
-
| `
|
|
253
|
-
| `
|
|
254
|
-
| `
|
|
255
|
-
| `
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
258
|
-
| `
|
|
259
|
-
| `
|
|
260
|
-
| `onDownloadStreaming` | `(event: DownloadStreamingEvent) => void` | Download streaming callback |
|
|
263
|
+
| Option | Type | Description |
|
|
264
|
+
| --------------------- | ---------------------------------------------------------- | --------------------------------- |
|
|
265
|
+
| `pathParams` | `Record<string, string \| number>` | Path parameters for URL templates |
|
|
266
|
+
| `params` | `Record<string, string \| number \| boolean \| undefined>` | Query parameters |
|
|
267
|
+
| `headers` | `Record<string, string>` | Request headers |
|
|
268
|
+
| `body` | `any` | Request body |
|
|
269
|
+
| `timeout` | `number` | Request timeout |
|
|
270
|
+
| `signal` | `AbortSignal` | Request cancellation signal |
|
|
271
|
+
| `validateStatus` | `(status: number) => boolean` | Custom status validation |
|
|
272
|
+
| `responseType` | `'text' \| 'blob' \| 'arrayBuffer'` | Response type override |
|
|
273
|
+
| `cache` | `RequestCache` | Cache mode |
|
|
274
|
+
| `credentials` | `RequestCredentials` | Credentials mode |
|
|
275
|
+
| `mode` | `RequestMode` | Request mode |
|
|
276
|
+
| `redirect` | `RequestRedirect` | Redirect mode |
|
|
277
|
+
| `retryOptions` | `RetryOptions` | Retry configuration |
|
|
278
|
+
| `onUploadStreaming` | `(event: UploadStreamingEvent) => void` | Upload streaming callback |
|
|
279
|
+
| `onDownloadStreaming` | `(event: DownloadStreamingEvent) => void` | Download streaming callback |
|
|
261
280
|
|
|
262
281
|
### <samp>Response Object</samp>
|
|
263
282
|
|
|
@@ -275,17 +294,60 @@ interface ResponseType<T> {
|
|
|
275
294
|
}
|
|
276
295
|
```
|
|
277
296
|
|
|
278
|
-
### <samp>
|
|
297
|
+
### <samp>Schema Validation</samp>
|
|
279
298
|
|
|
280
|
-
|
|
299
|
+
The library provides a chainable API for schema validation:
|
|
281
300
|
|
|
282
301
|
```ts
|
|
302
|
+
// Without schema
|
|
303
|
+
const response = await api.get('/users/:id', {
|
|
304
|
+
pathParams: { id: '123' },
|
|
305
|
+
})
|
|
306
|
+
// response 👉 ResponseType<unknown>
|
|
307
|
+
|
|
308
|
+
// With schema
|
|
309
|
+
const response = await api
|
|
310
|
+
.get('/users/:id', {
|
|
311
|
+
pathParams: { id: '123' },
|
|
312
|
+
})
|
|
313
|
+
.schema(userSchema)
|
|
314
|
+
// response 👉 ResponseType<User>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### <samp>Data Extraction</samp>
|
|
318
|
+
|
|
319
|
+
The method-based API provides clean data extraction with full type safety:
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
// Extract untyped data (without schema)
|
|
323
|
+
const data = await api
|
|
324
|
+
.get('/users/:id', {
|
|
325
|
+
pathParams: { id: '123' },
|
|
326
|
+
})
|
|
327
|
+
.data()
|
|
328
|
+
// data 👉 unknown
|
|
329
|
+
|
|
330
|
+
// Direct typed data extraction (with schema)
|
|
283
331
|
const user = await api
|
|
284
332
|
.get('/users/:id', {
|
|
285
333
|
pathParams: { id: '123' },
|
|
286
|
-
schema: userSchema,
|
|
287
334
|
})
|
|
288
|
-
.
|
|
335
|
+
.schema(userSchema)
|
|
336
|
+
.data()
|
|
337
|
+
// user 👉 { id: number, ... }
|
|
338
|
+
|
|
339
|
+
// Works with all HTTP methods
|
|
340
|
+
const newUser = await api
|
|
341
|
+
.post('/users', userData)
|
|
342
|
+
.schema(userSchema)
|
|
343
|
+
.data()
|
|
344
|
+
// newUser 👉 Fully typed user object
|
|
345
|
+
|
|
346
|
+
const updatedUser = await api
|
|
347
|
+
.put('/users/:id', userData, { pathParams: { id: '123' } })
|
|
348
|
+
.schema(userSchema)
|
|
349
|
+
.data()
|
|
350
|
+
// updatedUser 👉 Fully typed user object
|
|
289
351
|
```
|
|
290
352
|
|
|
291
353
|
## ➡️ Feature Comparison
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});class e extends Error{name="HttpError";status;statusText;data;response;url;method;cause;constructor(e,t,r,a,s,n,o,i){super(`HTTP ${t} ${r}: ${e} (${o} ${n})`,{cause:i}),this.status=t,this.statusText=r,this.data=a,this.response=s,this.url=n,this.method=o}}class t extends Error{name="NetworkError";cause;constructor(e,t){super(e,{cause:t})}}class r extends Error{name="SchemaValidationError";schema;data;cause;constructor(e,t,r,a){super(e,{cause:a}),this.schema=t,this.data=r}}class a extends Error{name="TimeoutError";cause;constructor(e,t){super(e,{cause:t})}}class s extends Error{name="PathParameterError";url;requiredParams;providedParams;cause;constructor(e,t,r,a,s){super(e,{cause:s}),this.url=t,this.requiredParams=r,this.providedParams=a}}class n extends Error{name="MiddlewareError";interceptorType;url;method;cause;constructor(e,t,r,a,s){super(e,{cause:s}),this.interceptorType=t,this.url=r,this.method=a}}class o extends Error{name="SerializationError";cause;constructor(e,t){super(e,{cause:t})}}class i extends Error{name="InvalidSchemaError";schema;cause;constructor(e,t,r){super(e,{cause:r}),this.schema=t}}class c extends Error{name="AsyncSchemaValidationError";schema;cause;constructor(e,t,r){super(e,{cause:r}),this.schema=t}}class d extends Error{name="InvalidBaseUrlError";baseUrl;cause;constructor(e,t,r){super(e,{cause:r}),this.baseUrl=t}}function u(e){return null!==e&&("object"==typeof e||"function"==typeof e)&&"~standard"in e&&"object"==typeof e["~standard"]&&null!==e["~standard"]&&"validate"in e["~standard"]&&"function"==typeof e["~standard"].validate&&"version"in e["~standard"]&&"number"==typeof e["~standard"].version&&"vendor"in e["~standard"]&&"string"==typeof e["~standard"].vendor}function l(){return{validate(e,t){if(!u(e))throw new i("Schema must implement the Standard Schema interface",e);const a=e["~standard"].validate(t);if(a instanceof Promise)throw new c("Async Standard Schema validation is not supported in this context",e);if(a.issues)throw new r(JSON.stringify(a.issues),e,t);return a.value},isSchema:e=>u(e)}}function h(e,t,r){let a=0;return new ReadableStream({async start(s){try{for(;;){const{done:n,value:o}=await e.read();if(n)break;a+=o.length,t(o,r,a),s.enqueue(o)}}finally{e.releaseLock(),s.close()}}})}async function f(e,t){if(!t||!e.body)return e;const r=h(e.body.getReader(),(e,r,a)=>{t({chunk:e,totalBytes:r,transferredBytes:a})},e.headers.get("content-length")?parseInt(e.headers.get("content-length")||"0",10):void 0);return new Request(e.url,{method:e.method,headers:e.headers,body:r,signal:e.signal,credentials:e.credentials,cache:e.cache,mode:e.mode,redirect:e.redirect,referrer:e.referrer,referrerPolicy:e.referrerPolicy,integrity:e.integrity,keepalive:e.keepalive,duplex:"half"})}async function p(e,t){if(!t||!e.body)return e;const r=h(e.body.getReader(),(e,r,a)=>{t({chunk:e,totalBytes:r,transferredBytes:a})},e.headers.get("content-length")?parseInt(e.headers.get("content-length")||"0",10):void 0);return new Response(r,{status:e.status,statusText:e.statusText,headers:e.headers})}const m={maxRetries:3,retryDelay:300,backoffFactor:2,retryStatusCodes:[429,500,502,503,504],retryNetworkErrors:!0,maxRetryDelay:3e4,shouldRetry:()=>!0};function y(e={}){const{baseUrl:t="",headers:r={},timeout:o=3e4,schemaValidator:i=l(),fetch:c=fetch,serializeBody:d,serializeParams:u,onRequestMiddleware:h,onResponseMiddleware:y}=e;return async function(l,v={}){const{method:T="GET",headers:R={},params:P,pathParams:U,body:O,schema:$,timeout:A=o,signal:k,credentials:j,cache:q,mode:B,redirect:L,onUploadStreaming:M,onDownloadStreaming:H,validateStatus:C,responseType:D,retryOptions:N}=v;let z=function(e,t={}){return e.replace(/:([a-zA-Z0-9_]+)/g,(r,a)=>{if(void 0===t[a])throw new s(`Missing required path parameter: ${a}`,e,[a],Object.keys(t));return String(t[a])})}(l,U);t&&(z=new URL(z,t).toString());let I={url:z,method:T,params:P,headers:new Headers({...r,...R}),body:O,signal:k,fetchOptions:{credentials:j,cache:q,mode:B,redirect:L}};if(h)try{I=await h(I)}catch(_){throw new n(x("Request middleware failed",_),"request",I.url,I.method,_ instanceof Error?_:void 0)}if(I.params){const e=new URL(I.url),t=u?u(I.params):function(e){const t=new URLSearchParams;for(const[r,a]of Object.entries(e))if(Array.isArray(a))for(const e of a)null!=e&&t.append(r,String(e));else null!=a&&t.append(r,String(a));return t.toString()}(I.params);if(t){const r=u&&t.startsWith("?")?t.slice(1):t;e.search=r}I.url=e.toString()}const V={method:I.method,headers:I.headers,...I.fetchOptions};if(void 0!==I.body&&("POST"===I.method||"PUT"===I.method||"PATCH"===I.method)){let e,t;if(d){const r=d(I.body);null==r?e="":(e=r,E(I.body)&&"string"==typeof r&&!I.headers.has("content-type")&&(t="application/json"))}else{const r=function(e){if("string"==typeof e||e instanceof FormData||e instanceof URLSearchParams||e instanceof ArrayBuffer||e instanceof Blob||e instanceof ReadableStream)return{body:e};if(E(e))return{body:JSON.stringify(e),contentType:"application/json"};return{body:e}}(I.body);e=r.body,t=r.contentType}V.body=e,t&&I.headers.set("content-type",t),V.duplex="half"}const F={...m,...e.retryOptions,...N},J=F.maxRetries;let W;for(let e=0;e<=J;e+=1){const t=new AbortController,r=setTimeout(()=>t.abort(),A),s=I.signal?(()=>{if(I.signal&&I.signal.aborted)return I.signal;const e=new AbortController,a=()=>{clearTimeout(r),e.abort()};return I.signal?.addEventListener("abort",a),t.signal.addEventListener("abort",a),e.signal})():t.signal;try{const e=new Request(I.url,V),t=M?await f(e,M):e,r=await c(t.url,{method:t.method,headers:t.headers,body:t.body,signal:s,...I.fetchOptions,...t.body&&{duplex:"half"}}),a=H?await p(r,H):r,o=await w(a,{validateStatus:C,schema:$,responseType:D},T,I.url,i);if(y)try{return await y(o)}catch(_){throw new n(x("Response middleware failed",_),"response",I.url,T,_ instanceof Error?_:void 0)}return o}catch(_){if(clearTimeout(r),W=_ instanceof Error?_:new Error(String(_)),"AbortError"===W.name&&(W=new a(`Request timeout after ${A}ms`,W)),e<J&&await b(W,e,F)){const t=g(e,F);await S(t);continue}throw W}}throw W||new Error("Request failed")}}async function w(t,a,s,n,i){const c=Object.fromEntries(t.headers.entries()),d=a.validateStatus||(e=>e>=200&&e<300);let u;try{if("text"===a.responseType)u=await t.text();else if("blob"===a.responseType)u=await t.blob();else if("arrayBuffer"===a.responseType)u=await t.arrayBuffer();else{const e=t.headers.get("content-type");u=e?.includes("application/json")?await t.json():e?.includes("text/")?await t.text():await t.arrayBuffer()}}catch(h){throw new o(x("Failed to parse response body",h),h instanceof Error?h:void 0)}if(!d(t.status))throw new e(`HTTP ${t.status} ${t.statusText}`,t.status,t.statusText,u,t,n,s);const l={data:u,status:t.status,statusText:t.statusText,headers:c,method:s,url:n,raw:t};if(a.schema)try{l.data=i.validate(a.schema,l.data)}catch(h){throw new r(`Schema validation failed: ${h instanceof Error?h.message:String(h)}`,a.schema,l.data,h instanceof Error?h:void 0)}return l}async function b(r,a,s){return!!s&&(s.shouldRetry?await s.shouldRetry(r,a):r instanceof e?s.retryStatusCodes?.includes(r.status)??!1:(r instanceof t||"TypeError"===r.name)&&(s.retryNetworkErrors??!1))}function g(e,t){if(!t)return 0;const r=t.retryDelay??300,a=t.backoffFactor??2,s=t.maxRetryDelay??3e4,n=r*Math.pow(a,e);return Math.min(n,s)}function E(e){return e&&"object"==typeof e&&"Object"===e.constructor?.name||Array.isArray(e)||"function"==typeof e?.toJSON}function x(e,t){return`${e}: ${t instanceof Error?t.message:String(t)}`}function S(e){return new Promise(t=>setTimeout(t,e))}function v(e){const t="undefined"!=typeof window&&void 0!==window.location&&"null"!==window.location.origin;if(!e)return t?window.location.origin:"";if(e.startsWith("/"))return t?new URL(e,window.location.origin).href.replace(/\/$/,""):e.replace(/\/$/,"");try{new URL(e)}catch{throw new d(`Invalid baseUrl: "${e}". Must be a valid absolute URL or relative path starting with "/".`,e)}return e.endsWith("/")?e.slice(0,-1):e}function T(e){const t=e;return t.data=async()=>(await e).data,t}function R(e={}){const t=y({...e,baseUrl:v(e.baseUrl)});return{get:function(e,...r){return T(t(e,{...r[0]??{},method:"GET"}))},post:(e,r,...a)=>T(t(e,{...a[0]??{},method:"POST",body:r})),put:(e,r,...a)=>T(t(e,{...a[0]??{},method:"PUT",body:r})),patch:(e,r,...a)=>T(t(e,{...a[0]??{},method:"PATCH",body:r})),delete:(e,...r)=>T(t(e,{...r[0]??{},method:"DELETE"})),request:(e,r)=>T(t(e,r))}}const P=R();exports.AsyncSchemaValidationError=c,exports.HttpError=e,exports.MiddlewareError=n,exports.NetworkError=t,exports.PathParameterError=s,exports.SchemaValidationError=r,exports.SerializationError=o,exports.TimeoutError=a,exports.createHttpClient=R,exports.default=P,exports.http=P;
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});class e extends Error{name="HttpError";status;statusText;data;response;url;method;cause;constructor(e,t,r,a,n,s,o,i){const c=t>=500?"Server Error":t>=400?"Client Error":t>=300?"Redirect":"Success",d=a?JSON.stringify(a):"No response data";super(`HTTP ${c} (${t}): ${e}\nRequest: ${o} ${s}\nData: ${d.length>500?d.substring(0,500)+"...":d}`,{cause:i}),this.status=t,this.statusText=r,this.data=a,this.response=n,this.url=s,this.method=o}}class t extends Error{name="NetworkError";cause;constructor(e,t){super(e,{cause:t})}}class r extends Error{name="SchemaValidationError";schema;data;cause;constructor(e,t,r,a){super(e,{cause:a}),this.schema=t,this.data=r}}class a extends Error{name="TimeoutError";cause;constructor(e,t){super(e,{cause:t})}}class n extends Error{name="PathParameterError";url;requiredParams;providedParams;cause;constructor(e,t,r,a,n){super(`${e}\nURL Template: ${t}\nExpected: [${r.map(e=>`"${e}"`).join(", ")}], Actual: [${a.map(e=>`"${e}"`).join(", ")}]`,{cause:n}),this.url=t,this.requiredParams=r,this.providedParams=a}}class s extends Error{name="MiddlewareError";type;url;method;cause;constructor(e,t,r,a,n){const s=r?`Request: ${a||"UNKNOWN"} ${r}`:"";super(s?`${e}\n${s}`:e,{cause:n}),this.type=t,this.url=r,this.method=a}}class o extends Error{name="SerializationError";cause;constructor(e,t){super(e,{cause:t})}}class i extends Error{name="InvalidSchemaError";schema;cause;constructor(e,t,r){super(e,{cause:r}),this.schema=t}}class c extends Error{name="AsyncSchemaValidationError";schema;cause;constructor(e,t,r){super(e,{cause:r}),this.schema=t}}class d extends Error{name="InvalidBaseUrlError";baseUrl;cause;constructor(e,t,r){super(`${e}\nBase URL: ${t}`,{cause:r}),this.baseUrl=t}}function u(e,t,r,a){let n=0,s=!1,o=!1;const i=()=>{if(!s){s=!0;try{e.releaseLock()}catch{}}},c=()=>{o=!0,i()};return a?.addEventListener("abort",c),new ReadableStream({async start(s){try{for(;!o;){const{done:a,value:o}=await e.read();if(a)break;n+=o.length,t(o,r,n),s.enqueue(o)}o||s.close()}catch(d){throw o||s.error(d),d}finally{a?.removeEventListener("abort",c),i()}},cancel(){o=!0,a?.removeEventListener("abort",c),i()}})}async function l(e,t){if(!t||!e.body)return e;if(!e.body.getReader)return e;const r=u(e.body.getReader(),(e,r,a)=>{t({chunk:e,totalBytes:r,transferredBytes:a})},e.headers.get("content-length")?parseInt(e.headers.get("content-length")||"0",10):void 0,e.signal);return new Request(e.url,{method:e.method,headers:e.headers,body:r,signal:e.signal,credentials:e.credentials,cache:e.cache,mode:e.mode,redirect:e.redirect,referrer:e.referrer,referrerPolicy:e.referrerPolicy,integrity:e.integrity,keepalive:e.keepalive,duplex:"half"})}async function h(e,t){if(!t||!e.body)return e;if(!e.body.getReader)return e;const r=u(e.body.getReader(),(e,r,a)=>{t({chunk:e,totalBytes:r,transferredBytes:a})},e.headers.get("content-length")?parseInt(e.headers.get("content-length")||"0",10):void 0);return new Response(r,{status:e.status,statusText:e.statusText,headers:e.headers})}const f={maxRetries:3,retryDelay:300,backoffFactor:2,retryStatusCodes:[429,500,502,503,504],retryNetworkErrors:!0,maxRetryDelay:3e4,shouldRetry:()=>!0};function p(e={}){const{baseUrl:t="",headers:r={},timeout:o=3e4,fetch:i=fetch,serializeBody:c,serializeParams:d,onRequestMiddleware:u,onResponseMiddleware:p}=e;return async function(S,R={}){const{method:$="GET",headers:T={},params:P,pathParams:U,body:j,timeout:O=o,signal:L,credentials:k,cache:A,mode:q,redirect:N,onUploadStreaming:B,onDownloadStreaming:D,responseType:M,retryOptions:C}=R;let H,I=function(e,t={}){return e.replace(/:([^/\s?#]+)\??/g,(r,a)=>{if(!/^[a-zA-Z0-9_]+$/.test(a))throw new n(`Invalid path parameter name: "${a}"`,e,[a],Object.keys(t));const s=t[a];if(void 0===s)throw new n(`Missing required path parameter: "${a}"`,e,[a],Object.keys(t));const o=String(s);if(o.includes("/")||o.includes("?"))throw new n(`Invalid path parameter value for "${a}": contains invalid characters`,e,[a],Object.keys(t));return o})}(S,U);(t||P)&&(t?(I=function(e,t){if(t.startsWith("http://")||t.startsWith("https://"))return t;const r=new URL(e),a=r.pathname.replace(/\/$/,""),[n,s]=t.split("?");t.startsWith("/")?r.pathname=a+n:t&&(r.pathname=a+"/"+n);s&&(r.search=r.search?`${r.search}&${s}`:`?${s}`);return r.toString()}(t,I),H=new URL(I)):H=new URL(I));const W={url:I,method:$,params:P,headers:new Headers({...r,...T}),body:j,signal:L,fetchOptions:{credentials:k,cache:A,mode:q,redirect:N}};let z=W;if(u)try{const e={...W,headers:new Headers(W.headers),fetchOptions:{...W.fetchOptions}};z=await u(e),z.url!==I&&(H=new URL(z.url))}catch(G){throw new s(g("Request middleware failed",G),"request",z.url,z.method,v(G,"Request middleware"))}if(z.params){H||(H=new URL(z.url));const e=d?d(z.params):function(e){const t=new URLSearchParams;for(const[r,a]of Object.entries(e))if(Array.isArray(a))for(const e of a)null!=e&&t.append(r,String(e));else null!=a&&t.append(r,String(a));return t.toString()}(z.params);if(e){if(d){const t=e.startsWith("?")?e.slice(1):e,r=H.search?"&":"?";H.search+=r+t}else{const t=new URLSearchParams(e);for(const[e,r]of t)H.searchParams.append(e,r)}z.url=H.toString()}}const J={method:z.method,headers:z.headers,...z.fetchOptions};if(void 0!==z.body&&("POST"===z.method||"PUT"===z.method||"PATCH"===z.method)){let e,t;if(c){const r=c(z.body);null==r?e="":(e=r,b(z.body)&&"string"==typeof r&&!z.headers.has("content-type")&&(t="application/json"))}else{const r=function(e){if("string"==typeof e||e instanceof FormData||e instanceof URLSearchParams||e instanceof ArrayBuffer||e instanceof Blob||e instanceof ReadableStream)return{body:e};if(b(e))return{body:JSON.stringify(e),contentType:"application/json"};if(null==e)return{body:""};return{body:String(e)}}(z.body);e=r.body,t=r.contentType}J.body=e,t&&z.headers.set("content-type",t),J.duplex="half"}const V={...f,...e.retryOptions,...C},F=V.maxRetries;let _;for(let e=0;e<=F;e+=1){const t=new AbortController,r=setTimeout(()=>t.abort(),O),n=x(z.signal,t.signal,()=>clearTimeout(r));try{const e=new Request(z.url,J),t=B?await l(e,B):e,r=await i(t.url,{method:t.method,headers:t.headers,body:t.body,signal:n,...z.fetchOptions,...t.body&&{duplex:"half"}}),a=D?await h(r,D):r,o=await m(a,{responseType:M,method:$,url:z.url});if(p)try{const e=await p(o);if(!e||"object"!=typeof e||!("data"in e))throw new s("Response middleware must return a valid ResponseType object","response",z.url,$);return e}catch(G){throw new s(g("Response middleware failed",G),"response",z.url,$,v(G,"Response middleware"))}return o}catch(G){if(clearTimeout(r),_=v(G,"Request execution"),"AbortError"===_.name&&(_=new a(`Request timeout after ${O}ms`,_)),e>=F||!(await y(_,e,V)))throw _;const t=w(e,V);await E(t)}}throw new Error("Retry loop terminated unexpectedly")}}async function m(t,r){const a=Object.fromEntries(t.headers.entries());let n;try{const e=r.responseType,a=t.headers.get("content-type")??"",s=a.includes("application/json")?"json":a.includes("text/")?"text":a.includes("application/octet-stream")||a.includes("application/pdf")||a.includes("image/")||a.includes("video/")||a.includes("audio/")?"arrayBuffer":a.includes("multipart/form-data")||a.includes("application/x-www-form-urlencoded")?"formData":void 0,o=e??s;if("json"===o)n=await t.json();else if("text"===o)n=await t.text();else if("blob"===o)n=await t.blob();else if("arrayBuffer"===o)n=await t.arrayBuffer();else if("formData"===o)n=await t.formData();else{const e=t.clone();try{n=await t.json()}catch{n=await e.text()}}}catch(s){throw new o(g("Failed to parse response body",s),s instanceof Error?s:void 0)}if(!t.ok)throw new e(`HTTP ${t.status} ${t.statusText}`,t.status,t.statusText,n,t,r.url,r.method);return{data:n,status:t.status,statusText:t.statusText,headers:a,method:r.method,url:r.url,raw:t}}async function y(r,a,n){return!!n&&(n.shouldRetry?await n.shouldRetry(r,a):r instanceof e?n.retryStatusCodes?.includes(r.status)??!1:(r instanceof t||"TypeError"===r.name)&&(n.retryNetworkErrors??!1))}function w(e,t){if(!t)return 0;const r=t.retryDelay??300,a=t.backoffFactor??2,n=t.maxRetryDelay??3e4,s=r*Math.pow(a,e);return Math.min(s,n)}function b(e){return null!==e&&"object"==typeof e&&"Object"===e.constructor?.name||Array.isArray(e)||null!==e&&"object"==typeof e&&"toJSON"in e&&"function"==typeof e.toJSON}function g(e,t){return`${e}: ${t instanceof Error?t.message:String(t)}`}function E(e){return new Promise(t=>setTimeout(t,e))}function x(e,t,r){if(!e&&!t)return(new AbortController).signal;if(e?.aborted)return e;const a=new AbortController,n=()=>{r?.(),a.abort()};return e?.addEventListener("abort",n),t?.addEventListener("abort",n),a.signal}function v(e,t){return e instanceof Error?e:new Error(`${t}: ${String(e)}`)}function S(e){return null!==e&&("object"==typeof e||"function"==typeof e)&&"~standard"in e&&"object"==typeof e["~standard"]&&null!==e["~standard"]&&"validate"in e["~standard"]&&"function"==typeof e["~standard"].validate&&"version"in e["~standard"]&&"number"==typeof e["~standard"].version&&"vendor"in e["~standard"]&&"string"==typeof e["~standard"].vendor}function R(e){const t="undefined"!=typeof window&&void 0!==window.location&&"null"!==window.location.origin;if(!e)return t?window.location.origin:"";if(e.startsWith("/"))return t?new URL(e,window.location.origin).href.replace(/\/$/,""):e.replace(/\/$/,"");try{new URL(e)}catch{throw new d(`Invalid baseUrl: "${e}". Must be a valid absolute URL or relative path starting with "/".`,e)}return e.endsWith("/")?e.slice(0,-1):e}function $(e,t,a,n){const s=a(e,t);return Object.assign(s,{schema:e=>function(e){const t=e;return t.data=async()=>(await e).data,t}(s.then(t=>{try{const r=n.validate(e,t.data);return{...t,data:r}}catch(a){throw new r(`Schema validation failed: ${a instanceof Error?a.message:String(a)}`,e,t.data,a instanceof Error?a:void 0)}})),data:async()=>(await s).data})}function T(e={}){const t=p({...e,baseUrl:R(e.baseUrl)}),a=e.schemaValidator??{validate(e,t){if(!S(e))throw new i("Schema must implement the Standard Schema interface",e);const a=e["~standard"].validate(t);if(a instanceof Promise)throw new c("Async Standard Schema validation is not supported in this context",e);if(a.issues)throw new r(JSON.stringify(a.issues),e,t);return a.value},isSchema:e=>S(e)};return{get:function(e,...r){return $(e,{...r[0]??{},method:"GET"},t,a)},post:(e,r,...n)=>$(e,{...n[0]??{},method:"POST",body:r},t,a),put:(e,r,...n)=>$(e,{...n[0]??{},method:"PUT",body:r},t,a),patch:(e,r,...n)=>$(e,{...n[0]??{},method:"PATCH",body:r},t,a),delete:(e,...r)=>$(e,{...r[0]??{},method:"DELETE"},t,a),request:(e,r)=>$(e,r??{},t,a)}}const P=T();exports.AsyncSchemaValidationError=c,exports.HttpError=e,exports.MiddlewareError=s,exports.NetworkError=t,exports.PathParameterError=n,exports.SchemaValidationError=r,exports.SerializationError=o,exports.TimeoutError=a,exports.createHttpClient=T,exports.default=P,exports.http=P;
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|