1000fetches 0.1.10 → 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.
@@ -1,13 +1,13 @@
1
1
  # 1000fetches
2
2
 
3
- _Type-first HTTP client with schema validation, compile-time path safety, and streaming_
3
+ _Schema-powered Fetch 2.0 where types meet runtime reality_
4
4
  <br/>
5
5
 
6
6
  [![npm version](https://img.shields.io/npm/v/1000fetches?style=flat-square)](https://www.npmjs.com/package/1000fetches)
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org/)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](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? Tiny helpers that stop halfway,
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()`, `api.post()` with `.data()` extractor for clean code
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
- // 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() }),
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
- // Extractor
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
- .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
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.get('/users/:id', {
86
- pathParams: { id: '123' },
87
- schema: userSchema,
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
- - **Automatic response parsing** — JSON/text responses parsed automatically
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 { HttpError, NetworkError, TimeoutError, MiddlewareError, PathParameterError } from '1000fetches'
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.get('/users/:id', {
168
- pathParams: { id: '123' },
169
- schema: userSchema,
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 | 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 |
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 the same options:
218
+ All HTTP methods support schema validation and data extraction:
211
219
 
212
220
  ```ts
213
- await api.get('/users/:id', {
214
- pathParams: { id: '123' },
215
- schema: userSchema,
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.post('/users', userData, {
220
- schema: userSchema,
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.put('/users/:id', userData, {
225
- pathParams: { id: '123' },
226
- schema: userSchema,
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.patch('/users/:id', partialData, {
231
- pathParams: { id: '123' },
232
- schema: userSchema,
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 | 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 |
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>Data Extractor</samp>
297
+ ### <samp>Schema Validation</samp>
279
298
 
280
- For convenience, you can extract data directly:
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
- .data() // Returns Promise<User> instead of ResponseType<User>
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 m(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 p={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:k=o,signal:q,credentials:A,cache:j,mode:B,redirect:L,onUploadStreaming:M,onDownloadStreaming:N,validateStatus:H,responseType:C,retryOptions:D}=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:q,fetchOptions:{credentials:A,cache:j,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):new URLSearchParams(JSON.parse(JSON.stringify(I.params))).toString();if(t){const r=u&&t.startsWith("?")?t.slice(1):t;e.search=r}I.url=e.toString()}const J={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}J.body=e,t&&I.headers.set("content-type",t),J.duplex="half"}const V={...p,...e.retryOptions,...D},F=V.maxRetries;let W;for(let e=0;e<=F;e+=1){const t=new AbortController,r=setTimeout(()=>t.abort(),k),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,J),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=N?await m(r,N):r,o=await w(a,{validateStatus:H,schema:$,responseType:C},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 ${k}ms`,W)),e<F&&await b(W,e,V)){const t=g(e,V);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