1000fetches 0.1.0 → 0.1.1

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/index.cjs.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class e extends Error{name="HttpError";status;statusText;data;response;url;method;cause;constructor(e,t,r,s,a,o,n,i){super(`HTTP ${t} ${r}: ${e} (${n} ${o})`,{cause:i}),this.status=t,this.statusText=r,this.data=s,this.response=a,this.url=o,this.method=n}}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,s){super(e,{cause:s}),this.schema=t,this.data=r}}class s extends Error{name="TimeoutError";cause;constructor(e,t){super(e,{cause:t})}}class a extends Error{name="PathParameterError";url;requiredParams;providedParams;cause;constructor(e,t,r,s,a){super(e,{cause:a}),this.url=t,this.requiredParams=r,this.providedParams=s}}class o extends Error{name="InterceptorError";interceptorType;url;method;cause;constructor(e,t,r,s,a){super(e,{cause:a}),this.interceptorType=t,this.url=r,this.method=s}}class n extends Error{name="SerializationError";cause;constructor(e,t){super(e,{cause:t})}}function i(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 c(e){return{parse:t=>{const r=e["~standard"].validate(t);if(r instanceof Promise)throw new Error("Async Standard Schema validation is not supported");if("issues"in r&&r.issues){const e=Array.from(r.issues).map(e=>e.message).join("; ");throw new Error(`Standard Schema validation failed: ${e}`)}return r.value}}}function d(){return{validate(e,t){if(!i(e))throw new Error("Invalid schema: Schema must implement the Standard Schema interface");return c(e).parse(t)},isSchema:e=>i(e)}}function u(){return{validate(e,t){if(!i(e))throw new Error("Invalid schema: Schema must implement the Standard Schema interface");return d().validate(e,t)},isSchema:e=>i(e)}}function h(e,t={}){return e.replace(/:([a-zA-Z0-9_]+)/g,(r,s)=>{if(void 0===t[s])throw new a(`Missing required path parameter: ${s}`,e,[s],Object.keys(t));return String(t[s])})}const l={maxRetries:3,retryDelay:300,backoffFactor:2,retryStatusCodes:[429,500,502,503,504],retryNetworkErrors:!0,maxRetryDelay:3e4,shouldRetry:()=>!0};exports.HttpClient=class{baseUrl;defaultHeaders;defaultTimeout;schemaValidator;requestInterceptors;responseInterceptors;defaultRetryOptions;constructor(e={}){this.baseUrl=function(e){if(!e)return"";try{new URL(e)}catch{throw new Error(`Invalid baseUrl: "${e}". Must be a valid URL.`)}return e.endsWith("/")?e.slice(0,-1):e}(e.baseUrl),this.defaultHeaders=e.headers||{},this.defaultTimeout=e.timeout||3e4,this.schemaValidator=e.schemaValidator||u(),this.requestInterceptors=e.requestInterceptors||[],this.responseInterceptors=e.responseInterceptors||[],this.defaultRetryOptions=e.retryOptions}addRequestInterceptor(e){this.requestInterceptors.push(e)}addResponseInterceptor(e){this.responseInterceptors.push(e)}setSchemaValidator(e){this.schemaValidator=e}async shouldRetry(a,o,n){if(!1===n)return!1;const i=this.getRetryOptions(n);if(o>=i.maxRetries)return!1;if("function"==typeof i.shouldRetry)try{return await i.shouldRetry(a,o)}catch(c){console.warn("Custom shouldRetry function failed:",c)}return!!(a instanceof t&&i.retryNetworkErrors)||(!!(a instanceof e&&i.retryStatusCodes.includes(a.status))||!(a instanceof r)&&a instanceof s)}getRetryDelay(e,t){const r=t.retryDelay*Math.pow(t.backoffFactor,e),s=Math.min(r,t.maxRetryDelay),a=s+.2*s*(2*Math.random()-1);return Math.max(1,Math.floor(a))}getRetryOptions(e){return!1===e?{...l,maxRetries:0}:!0===e?{...l,...this.defaultRetryOptions}:"object"==typeof e?{...l,...this.defaultRetryOptions,...e}:{...l,...this.defaultRetryOptions}}sleep(e){return new Promise(t=>setTimeout(t,e))}async request(e,r,s={}){let a=0;for(;;)try{return await this.executeRequest(e,r,s)}catch(o){if(!(o instanceof Error))throw new t(String(o),void 0);if(await this.shouldRetry(o,a,s.retry)){const e=this.getRetryOptions(s.retry),t=this.getRetryDelay(a,e);await this.sleep(t),a++;continue}throw o}}async executeRequest(i,c,d={}){let u=this.resolveUrl(c,d.pathParams),h={url:u,method:i,params:d.params,headers:this.prepareHeaders(d.headers),body:d.body,fetchOptions:{credentials:d.credentials,cache:d.cache,mode:d.mode,redirect:d.redirect}};for(const e of this.requestInterceptors)try{h=await e(h)}catch(b){throw new o(`Request interceptor failed: ${b instanceof Error?b.message:String(b)}`,"request",u,i,b instanceof Error?b:void 0)}if(u=h.url,h.params&&Object.keys(h.params).length>0){const e=new URLSearchParams;for(const[r,s]of Object.entries(h.params))void 0!==s&&e.append(r,String(s));const t=e.toString();t&&(u+=(u.includes("?")?"&":"?")+t)}const l={method:h.method,headers:h.headers,...h.fetchOptions};if(void 0!==h.body&&("POST"===h.method||"PUT"===h.method||"PATCH"===h.method))if(h.body instanceof FormData||h.body instanceof Blob||h.body instanceof ArrayBuffer||h.body instanceof URLSearchParams||"string"==typeof h.body)l.body=h.body;else try{l.body=JSON.stringify(h.body)}catch(b){throw new n(`Failed to serialize request body: ${b instanceof Error?b.message:"Invalid data structure"}`,b instanceof Error?b:void 0)}const p=d.timeout||this.defaultTimeout,f=new AbortController,m=setTimeout(()=>f.abort(),p),y=h.signal||d.signal;let w;const E=y?(()=>{const e=this.mergeAbortSignals(y,f.signal);return w=e.cleanup,e.signal})():f.signal;try{const e=await fetch(u,{...l,signal:E});let t=await this.processResponse(e,d,i,u);return d.schema&&(t.data=this.validateResponseData(d.schema,t.data)),t}catch(b){if(b instanceof DOMException&&"AbortError"===b.name||b instanceof Error&&"AbortError"===b.name||b instanceof Error&&b.message.toLowerCase().includes("timeout"))throw new s(`Request timed out after ${p}ms`,b instanceof Error?b:void 0);if(b instanceof e||b instanceof r||b instanceof o||b instanceof a||b instanceof n)throw b;throw new t(b instanceof Error?b.message:String(b),b instanceof Error?b:void 0)}finally{clearTimeout(m),w?.()}}validateWithSchema(e,t,s){try{return this.schemaValidator.validate(e,t)}catch(a){throw new r(`${s}: ${a instanceof Error?a.message:"Unknown error"}`,e,t,a instanceof Error?a:void 0)}}validateResponseData(e,t){return this.validateWithSchema(e,t,"Response schema validation failed")}validateSchema(e,t){return this.validateWithSchema(e,t,"Schema validation failed")}prepareHeaders(e={}){const t=new Headers;for(const[r,s]of Object.entries(this.defaultHeaders))null!=s&&t.set(r,String(s));for(const[r,s]of Object.entries(e))null!=s&&t.set(r,String(s));return t}extractHeaders(e){const t={};return e.forEach((e,r)=>{t[r]=e}),t}createHttpError(t,r,s,a,o,n){return new e(`Request failed with status code ${t.status}`,t.status,o||t.statusText,r,t,s,a,n)}async parseResponseBody(e,t){const r=e.headers.get("content-type")??"";try{if("text"===t.responseType)return await e.text();if("blob"===t.responseType)return await e.blob();if("arrayBuffer"===t.responseType)return await e.arrayBuffer();if(r.includes("application/json")){const t=await e.text();return t?JSON.parse(t):null}return await e.text()}catch(s){if(204===e.status)return;if(r.includes("application/json")&&s instanceof SyntaxError)throw new Error(`Failed to parse JSON response: ${s.message}`);throw s}}async processResponse(e,t,r,s){const a=this.extractHeaders(e.headers),n=t.validateStatus||(e=>e>=200&&e<300),i=await this.parseResponseBody(e,t);if(!n(e.status))throw this.createHttpError(e,i,s,r);const c={data:i,status:e.status,statusText:e.statusText,headers:a,method:r,url:s,raw:e};let d=c;const u=c.status;for(const l of this.responseInterceptors)try{d=await l(d)}catch(h){throw new o(`Response interceptor failed: ${h instanceof Error?h.message:String(h)}`,"response",s,r,h instanceof Error?h:void 0)}if(d.status!==u&&!n(d.status))throw this.createHttpError(e,d.data,s,r,d.statusText);return d}mergeAbortSignals(e,t){if(e.aborted||t.aborted){const e=new AbortController;return e.abort(),{signal:e.signal}}const r=new AbortController,s=()=>{r.abort(),a()},a=()=>{e.removeEventListener("abort",s),t.removeEventListener("abort",s)};return e.addEventListener("abort",s),t.addEventListener("abort",s),{signal:r.signal,cleanup:a}}get(e,t){return this.request("GET",e,t||{})}post(e,t,r){return this.request("POST",e,{...r||{},body:t})}put(e,t,r){return this.request("PUT",e,{...r||{},body:t})}patch(e,t,r){return this.request("PATCH",e,{...r||{},body:t})}delete(e,t){return this.request("DELETE",e,t||{})}validatePathParams(e,t){if(!t){const t=this.extractRequiredPathParams(e);if(t.length>0)throw new a(`Missing required path parameters for URL "${e}": ${t.join(", ")}. Please provide pathParams: { ${t.map(e=>`${e}: value`).join(", ")} }`,e,t,[]);return}const r=this.extractRequiredPathParams(e),s=Object.keys(t),o=r.filter(e=>!s.includes(e));if(o.length>0)throw new a(`Missing required path parameters: ${o.join(", ")}. Provided: ${s.join(", ")}`,e,r,s)}extractRequiredPathParams(e){const t=e.match(/:([a-zA-Z0-9_]+)/g);return t?t.map(e=>e.slice(1)):[]}resolveUrl(e,t){this.validatePathParams(e,t);const r=t?h(e,t):e;if(/^https?:\/\//i.test(r))return r;const s=r.startsWith("/")?r.slice(1):r;return`${this.baseUrl}/${s}`}},exports.HttpError=e,exports.InterceptorError=o,exports.NetworkError=t,exports.PathParameterError=a,exports.SchemaValidationError=r,exports.TimeoutError=s,exports.createSchemaValidator=u,exports.createStandardSchemaValidator=d,exports.generatePath=h,exports.isStandardSchema=i,exports.standardSchemaAdapter=c;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class e extends Error{name="HttpError";status;statusText;data;response;url;method;cause;constructor(e,t,r,s,a,o,n,i){super(`HTTP ${t} ${r}: ${e} (${n} ${o})`,{cause:i}),this.status=t,this.statusText=r,this.data=s,this.response=a,this.url=o,this.method=n}}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,s){super(e,{cause:s}),this.schema=t,this.data=r}}class s extends Error{name="TimeoutError";cause;constructor(e,t){super(e,{cause:t})}}class a extends Error{name="PathParameterError";url;requiredParams;providedParams;cause;constructor(e,t,r,s,a){super(e,{cause:a}),this.url=t,this.requiredParams=r,this.providedParams=s}}class o extends Error{name="InterceptorError";interceptorType;url;method;cause;constructor(e,t,r,s,a){super(e,{cause:a}),this.interceptorType=t,this.url=r,this.method=s}}class n extends Error{name="SerializationError";cause;constructor(e,t){super(e,{cause:t})}}function i(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 c(e){return{parse:t=>{const r=e["~standard"].validate(t);if(r instanceof Promise)throw new Error("Async Standard Schema validation is not supported");if("issues"in r&&r.issues){const e=Array.from(r.issues).map(e=>e.message).join("; ");throw new Error(`Standard Schema validation failed: ${e}`)}return r.value}}}function d(){return{validate(e,t){if(!i(e))throw new Error("Invalid schema: Schema must implement the Standard Schema interface");return c(e).parse(t)},isSchema:e=>i(e)}}function u(){return{validate(e,t){if(!i(e))throw new Error("Invalid schema: Schema must implement the Standard Schema interface");return d().validate(e,t)},isSchema:e=>i(e)}}function h(e,t={}){return e.replace(/:([a-zA-Z0-9_]+)/g,(r,s)=>{if(void 0===t[s])throw new a(`Missing required path parameter: ${s}`,e,[s],Object.keys(t));return String(t[s])})}const l={maxRetries:3,retryDelay:300,backoffFactor:2,retryStatusCodes:[429,500,502,503,504],retryNetworkErrors:!0,maxRetryDelay:3e4,shouldRetry:()=>!0};exports.HttpClient=class{baseUrl;defaultHeaders;defaultTimeout;schemaValidator;requestInterceptors;responseInterceptors;defaultRetryOptions;constructor(e={}){this.baseUrl=function(e){if(!e)return"";try{new URL(e)}catch{throw new Error(`Invalid baseUrl: "${e}". Must be a valid URL.`)}return e.endsWith("/")?e.slice(0,-1):e}(e.baseUrl),this.defaultHeaders=e.headers||{},this.defaultTimeout=e.timeout||3e4,this.schemaValidator=e.schemaValidator||u(),this.requestInterceptors=e.requestInterceptors||[],this.responseInterceptors=e.responseInterceptors||[],this.defaultRetryOptions=e.retryOptions}addRequestInterceptor(e){this.requestInterceptors.push(e)}addResponseInterceptor(e){this.responseInterceptors.push(e)}setSchemaValidator(e){this.schemaValidator=e}async shouldRetry(a,o,n){if(!1===n)return!1;const i=this.getRetryOptions(n);if(o>=i.maxRetries)return!1;if("function"==typeof i.shouldRetry)try{return await i.shouldRetry(a,o)}catch(c){console.warn("Custom shouldRetry function failed:",c)}return!!(a instanceof t&&i.retryNetworkErrors)||(!!(a instanceof e&&i.retryStatusCodes.includes(a.status))||!(a instanceof r)&&a instanceof s)}getRetryDelay(e,t){const r=t.retryDelay*Math.pow(t.backoffFactor,e),s=Math.min(r,t.maxRetryDelay),a=s+.2*s*(2*Math.random()-1);return Math.max(1,Math.floor(a))}getRetryOptions(e){return!1===e?{...l,maxRetries:0}:!0===e?{...l,...this.defaultRetryOptions}:"object"==typeof e?{...l,...this.defaultRetryOptions,...e}:{...l,...this.defaultRetryOptions}}sleep(e){return new Promise(t=>setTimeout(t,e))}async request(e,r,s={}){let a=0;for(;;)try{return await this.executeRequest(e,r,s)}catch(o){if(!(o instanceof Error))throw new t(String(o),void 0);if(await this.shouldRetry(o,a,s.retry)){const e=this.getRetryOptions(s.retry),t=this.getRetryDelay(a,e);await this.sleep(t),a++;continue}throw o}}async executeRequest(i,c,d={}){let u=this.resolveUrl(c,d.pathParams),h={url:u,method:i,params:d.params,headers:this.prepareHeaders(d.headers),body:d.body,fetchOptions:{credentials:d.credentials,cache:d.cache,mode:d.mode,redirect:d.redirect}};for(const e of this.requestInterceptors)try{h=await e(h)}catch(b){throw new o(`Request interceptor failed: ${b instanceof Error?b.message:String(b)}`,"request",u,i,b instanceof Error?b:void 0)}if(u=h.url,h.params&&Object.keys(h.params).length>0){const e=new URLSearchParams;for(const[r,s]of Object.entries(h.params))void 0!==s&&e.append(r,String(s));const t=e.toString();t&&(u+=(u.includes("?")?"&":"?")+t)}const l={method:h.method,headers:h.headers,...h.fetchOptions};if(void 0!==h.body&&("POST"===h.method||"PUT"===h.method||"PATCH"===h.method))if(h.body instanceof FormData||h.body instanceof Blob||h.body instanceof ArrayBuffer||h.body instanceof URLSearchParams||"string"==typeof h.body)l.body=h.body;else try{l.body=JSON.stringify(h.body)}catch(b){throw new n(`Failed to serialize request body: ${b instanceof Error?b.message:"Invalid data structure"}`,b instanceof Error?b:void 0)}const p=d.timeout||this.defaultTimeout,f=new AbortController,m=setTimeout(()=>f.abort(),p),y=h.signal||d.signal;let w;const E=y?(()=>{const e=this.mergeAbortSignals(y,f.signal);return w=e.cleanup,e.signal})():f.signal;try{const e=await fetch(u,{...l,signal:E});let t=await this.processResponse(e,d,i,u);return d.schema&&(t.data=this.validateResponseData(d.schema,t.data)),t}catch(b){if(b instanceof DOMException&&"AbortError"===b.name||b instanceof Error&&"AbortError"===b.name||b instanceof Error&&b.message.toLowerCase().includes("timeout"))throw new s(`Request timed out after ${p}ms`,b instanceof Error?b:void 0);if(b instanceof e||b instanceof r||b instanceof o||b instanceof a||b instanceof n)throw b;throw new t(b instanceof Error?b.message:String(b),b instanceof Error?b:void 0)}finally{clearTimeout(m),w?.()}}validateWithSchema(e,t,s){try{return this.schemaValidator.validate(e,t)}catch(a){throw new r(`${s}: ${a instanceof Error?a.message:"Unknown error"}`,e,t,a instanceof Error?a:void 0)}}validateResponseData(e,t){return this.validateWithSchema(e,t,"Response schema validation failed")}validateSchema(e,t){return this.validateWithSchema(e,t,"Schema validation failed")}prepareHeaders(e={}){const t=new Headers;for(const[r,s]of Object.entries(this.defaultHeaders))null!=s&&t.set(r,String(s));for(const[r,s]of Object.entries(e))null!=s&&t.set(r,String(s));return t}extractHeaders(e){const t={};return e.forEach((e,r)=>{t[r]=e}),t}createHttpError(t,r,s,a,o,n){return new e(`Request failed with status code ${t.status}`,t.status,o||t.statusText,r,t,s,a,n)}async parseResponseBody(e,t){const r=e.headers.get("content-type")??"";try{if("text"===t.responseType)return await e.text();if("blob"===t.responseType)return await e.blob();if("arrayBuffer"===t.responseType)return await e.arrayBuffer();if(r.includes("application/json")){const t=await e.text();return t?JSON.parse(t):null}return await e.text()}catch(s){if(204===e.status)return;if(r.includes("application/json")&&s instanceof SyntaxError)throw new Error(`Failed to parse JSON response: ${s.message}`);throw s}}async processResponse(e,t,r,s){const a=this.extractHeaders(e.headers),n=t.validateStatus||(e=>e>=200&&e<300),i=await this.parseResponseBody(e,t);if(!n(e.status))throw this.createHttpError(e,i,s,r);const c={data:i,status:e.status,statusText:e.statusText,headers:a,method:r,url:s,raw:e};let d=c;const u=c.status;for(const l of this.responseInterceptors)try{d=await l(d)}catch(h){throw new o(`Response interceptor failed: ${h instanceof Error?h.message:String(h)}`,"response",s,r,h instanceof Error?h:void 0)}if(d.status!==u&&!n(d.status))throw this.createHttpError(e,d.data,s,r,d.statusText);return d}mergeAbortSignals(e,t){if(e.aborted||t.aborted){const e=new AbortController;return e.abort(),{signal:e.signal}}const r=new AbortController,s=()=>{r.abort(),a()},a=()=>{e.removeEventListener("abort",s),t.removeEventListener("abort",s)};return e.addEventListener("abort",s),t.addEventListener("abort",s),{signal:r.signal,cleanup:a}}get(e,t){return this.request("GET",e,t||{})}post(e,t,r){return this.request("POST",e,{...r||{},body:t})}put(e,t,r){return this.request("PUT",e,{...r||{},body:t})}patch(e,t,r){return this.request("PATCH",e,{...r||{},body:t})}delete(e,t){return this.request("DELETE",e,t||{})}validatePathParams(e,t){if(!t){const t=this.extractRequiredPathParams(e);if(t.length>0)throw new a(`Missing required path parameters for URL "${e}": ${t.join(", ")}. Please provide pathParams: { ${t.map(e=>`${e}: value`).join(", ")} }`,e,t,[]);return}const r=this.extractRequiredPathParams(e),s=Object.keys(t),o=r.filter(e=>!s.includes(e));if(o.length>0)throw new a(`Missing required path parameters: ${o.join(", ")}. Provided: ${s.join(", ")}`,e,r,s)}extractRequiredPathParams(e){const t=e.match(/:([a-zA-Z0-9_]+)/g);return t?t.map(e=>e.slice(1)):[]}resolveUrl(e,t){this.validatePathParams(e,t);const r=t?h(e,t):e;if(/^https?:\/\//i.test(r))return r;const s=r.startsWith("/")?r.slice(1):r;return`${this.baseUrl}/${s}`}},exports.HttpError=e,exports.InterceptorError=o,exports.NetworkError=t,exports.PathParameterError=a,exports.SchemaValidationError=r,exports.SerializationError=n,exports.TimeoutError=s,exports.createSchemaValidator=u,exports.createStandardSchemaValidator=d,exports.generatePath=h,exports.isStandardSchema=i,exports.standardSchemaAdapter=c;
2
2
  //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1,891 @@
1
+ /**
2
+ * Create a default schema validator that supports Standard Schema.
3
+ *
4
+ * This validator only works with schemas that implement the Standard Schema
5
+ * interface. For full support of Zod, Valibot, and Arktype, use the
6
+ * appropriate validator from their respective packages.
7
+ *
8
+ * @returns A schema validator instance
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { createSchemaValidator } from '1000fetches'
13
+ *
14
+ * const validator = createSchemaValidator();
15
+ *
16
+ * // Use with HttpClient
17
+ * const client = new HttpClient({
18
+ * schemaValidator: validator
19
+ * });
20
+ * ```
21
+ */
22
+ export declare function createSchemaValidator(): SchemaValidator;
23
+
24
+ /**
25
+ * Creates a schema validator that supports Standard Schema V1 implementations
26
+ *
27
+ * Provides a validator that can work with any schema library that implements
28
+ * the Standard Schema V1 specification, enabling interoperability between
29
+ * different validation libraries.
30
+ *
31
+ * @returns SchemaValidator that supports Standard Schema V1
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const validator = createStandardSchemaValidator();
36
+ * const client = new HttpClient({ schemaValidator: validator });
37
+ * ```
38
+ */
39
+ export declare function createStandardSchemaValidator(): SchemaValidator;
40
+
41
+ declare type ExtractRouteParams<T extends string> = T extends `${string}:${infer Param}/${infer Rest}` ? Param | ExtractRouteParams<Rest> : T extends `${string}:${infer Param}` ? Param : never;
42
+
43
+ /**
44
+ * Interpolates parameters into a URL template
45
+ * Similar to React Router's generatePath function
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const path = generatePath('/users/:id/posts/:postId', { id: '123', postId: '456' });
50
+ * // => '/users/123/posts/456'
51
+ * ```
52
+ */
53
+ export declare function generatePath<Path extends string>(path: Path, params?: PathParams<Path>): string;
54
+
55
+ declare type HasRequiredParams<T extends string> = T extends `${string}:${string}` ? true : false;
56
+
57
+ /**
58
+ * A modern, type-safe HTTP client with built-in schema validation, interceptors, retry logic, and compile-time path parameter validation.
59
+ *
60
+ * The HttpClient provides a fluent API for making HTTP requests with full TypeScript support,
61
+ * automatic retry logic, request/response interceptors, and schema validation using popular
62
+ * validation libraries like Zod, Valibot, and Arktype.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * import { HttpClient } from '1000fetches'
67
+ * import { z } from 'zod'
68
+ *
69
+ * // Create a client instance
70
+ * const client = new HttpClient({
71
+ * baseUrl: 'https://api.example.com',
72
+ * timeout: 5000,
73
+ * headers: { 'Authorization': 'Bearer token' }
74
+ * });
75
+ *
76
+ * const userSchema = z.object({
77
+ * id: z.string(),
78
+ * name: z.string(),
79
+ * email: z.string()
80
+ * });
81
+ *
82
+ * const response = await client.get('/users/:id', {
83
+ * pathParams: { id: '123' },
84
+ * schema: userSchema
85
+ * });
86
+ */
87
+ export declare class HttpClient {
88
+ private baseUrl;
89
+ private defaultHeaders;
90
+ private defaultTimeout;
91
+ private schemaValidator;
92
+ private requestInterceptors;
93
+ private responseInterceptors;
94
+ private defaultRetryOptions?;
95
+ /**
96
+ * Create a new HttpClient instance.
97
+ *
98
+ * @param options - Configuration options for the HTTP client
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * // Basic configuration
103
+ * const client = new HttpClient({
104
+ * baseUrl: 'https://api.example.com'
105
+ * });
106
+ *
107
+ * // With custom headers and timeout
108
+ * const client = new HttpClient({
109
+ * baseUrl: 'https://api.example.com',
110
+ * headers: { 'Authorization': 'Bearer token' },
111
+ * timeout: 10000
112
+ * });
113
+ *
114
+ * // With custom schema validator
115
+ * const client = new HttpClient({
116
+ * baseUrl: 'https://api.example.com',
117
+ * schemaValidator: createCustomValidator()
118
+ * });
119
+ *
120
+ * // With retry configuration
121
+ * const client = new HttpClient({
122
+ * baseUrl: 'https://api.example.com',
123
+ * retryOptions: {
124
+ * maxRetries: 5,
125
+ * retryDelay: 1000,
126
+ * retryStatusCodes: [500, 502, 503]
127
+ * }
128
+ * });
129
+ * ```
130
+ */
131
+ constructor(options?: HttpClientOptions);
132
+ /**
133
+ * Add a request interceptor to modify requests before they're sent.
134
+ *
135
+ * Request interceptors are executed in the order they were added and can modify
136
+ * the request context before it's sent to the server. This is useful for adding
137
+ * authentication headers, logging, or transforming request data.
138
+ *
139
+ * @param interceptor - The interceptor function to add
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * // Add authentication headers to all requests
144
+ * client.addRequestInterceptor((context) => {
145
+ * context.headers.set('Authorization', `Bearer ${getToken()}`);
146
+ * return context;
147
+ * });
148
+ *
149
+ * // Change the base URL for specific endpoints
150
+ * client.addRequestInterceptor((context) => {
151
+ * if (context.url.includes('/special/')) {
152
+ * context.url = context.url.replace('api.example.com', 'special-api.example.com');
153
+ * }
154
+ * return context;
155
+ * });
156
+ *
157
+ * // Log all outgoing requests
158
+ * client.addRequestInterceptor((context) => {
159
+ * console.log(`Making ${context.method} request to ${context.url}`);
160
+ * return context;
161
+ * });
162
+ * ```
163
+ */
164
+ addRequestInterceptor(interceptor: RequestInterceptor): void;
165
+ /**
166
+ * Add a response interceptor to process responses before they're returned.
167
+ *
168
+ * Response interceptors are executed in the order they were added and can modify
169
+ * the response data before it's returned to the caller. This is useful for
170
+ * transforming data, logging responses, or handling global error conditions.
171
+ *
172
+ * @param interceptor - The interceptor function to add
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * // Log all responses
177
+ * client.addResponseInterceptor((response) => {
178
+ * console.log(`Response from ${response.raw.url}: ${response.status}`);
179
+ * return response;
180
+ * });
181
+ *
182
+ * // Transform response data
183
+ * client.addResponseInterceptor((response) => {
184
+ * if (Array.isArray(response.data)) {
185
+ * response.data = response.data.map(item => transformItem(item));
186
+ * }
187
+ * return response;
188
+ * });
189
+ *
190
+ * // Handle 401 responses by refreshing the token and retrying
191
+ * client.addResponseInterceptor(async (response) => {
192
+ * if (response.status === 401) {
193
+ * await refreshToken();
194
+ * // Make a new request (will use updated token)
195
+ * return await client.request(response.raw.method, response.raw.url);
196
+ * }
197
+ * return response;
198
+ * });
199
+ * ```
200
+ */
201
+ addResponseInterceptor(interceptor: ResponseInterceptor): void;
202
+ /**
203
+ * Set a custom schema validator for request/response validation
204
+ *
205
+ * @param validator - The schema validator to use for validation
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * // Set a custom validator
210
+ * client.setSchemaValidator(customValidator);
211
+ * ```
212
+ */
213
+ /**
214
+ * Set a custom schema validator for the client.
215
+ *
216
+ * This allows you to use a different validation library or custom validation logic
217
+ * for all requests made by this client instance.
218
+ *
219
+ * @param validator - The schema validator to use for all validation operations
220
+ *
221
+ * @example
222
+ * ```ts
223
+ * import { createCustomValidator } from './custom-validator'
224
+ *
225
+ * // Set a custom validator
226
+ * client.setSchemaValidator(createCustomValidator());
227
+ *
228
+ * // Now all requests will use the custom validator
229
+ * const response = await client.post('/users', userData, {
230
+ * schema: customUserSchema
231
+ * });
232
+ * ```
233
+ */
234
+ setSchemaValidator(validator: SchemaValidator): void;
235
+ /**
236
+ * Determines if a request should be retried based on error type and retry configuration
237
+ *
238
+ * @param error - The error that occurred during the request
239
+ * @param retryCount - Current retry attempt number
240
+ * @param retryOptions - Retry configuration for this request
241
+ * @returns Promise resolving to whether the request should be retried
242
+ */
243
+ private shouldRetry;
244
+ /**
245
+ * Calculates the delay for the next retry attempt using exponential backoff with jitter
246
+ *
247
+ * @param retryCount - Current retry attempt number
248
+ * @param options - Retry configuration options
249
+ * @returns Delay in milliseconds before the next retry attempt
250
+ */
251
+ private getRetryDelay;
252
+ /**
253
+ * Merges provided retry options with default configuration
254
+ *
255
+ * @param retryOptions - Retry options to merge with defaults
256
+ * @returns Complete retry configuration with all required properties
257
+ */
258
+ private getRetryOptions;
259
+ /**
260
+ * Creates a promise that resolves after the specified delay
261
+ *
262
+ * @param ms - Delay duration in milliseconds
263
+ * @returns Promise that resolves after the delay
264
+ */
265
+ private sleep;
266
+ /**
267
+ * Makes an HTTP request with automatic retry capability
268
+ *
269
+ * @param method - HTTP method to use for the request
270
+ * @param url - URL path for the request (supports path parameters)
271
+ * @param options - Request configuration options
272
+ * @returns Promise resolving to the response data
273
+ *
274
+ * @example
275
+ * ```ts
276
+ * // Simple GET request
277
+ * const response = await client.request('GET', '/users/1');
278
+ *
279
+ * // POST request with body and path parameters
280
+ * const response = await client.request('POST', '/users/:id/posts', {
281
+ * pathParams: { id: 1 },
282
+ * body: { title: 'New Post', content: 'Hello World' },
283
+ * schema: userPostSchema
284
+ * });
285
+ * ```
286
+ */
287
+ request<Path extends string = string, TResponse = unknown, TBody = unknown, TParams extends RequestParamsType = RequestParamsType>(method: HttpMethod, url: Path, options?: TypedRequestOptions<TBody, TResponse, TParams, Path>): Promise<ResponseType_2<TResponse>>;
288
+ /**
289
+ * Executes a single HTTP request without retry logic
290
+ *
291
+ * @param method - HTTP method for the request
292
+ * @param url - URL path with optional path parameters
293
+ * @param options - Request configuration and validation options
294
+ * @returns Promise resolving to the response data
295
+ */
296
+ private executeRequest;
297
+ private validateWithSchema;
298
+ private validateResponseData;
299
+ /**
300
+ * Public method to validate data against any schema
301
+ *
302
+ * @param schema - Schema to validate against
303
+ * @param data - Data to validate
304
+ * @returns Validated data
305
+ *
306
+ * @example
307
+ * ```ts
308
+ * const validatedUser = client.validateSchema(userSchema, userData);
309
+ * ```
310
+ */
311
+ /**
312
+ * Validate data against a schema using the configured schema validator.
313
+ *
314
+ * This method uses the same schema validator that's configured for the client,
315
+ * ensuring consistent validation behavior across all requests.
316
+ *
317
+ * @template T - The expected type after validation
318
+ * @param schema - The schema to validate against (Zod, Valibot, or Arktype)
319
+ * @param data - The data to validate
320
+ *
321
+ * @returns The validated data with the correct type
322
+ *
323
+ * @throws {SchemaValidationError} If the data doesn't match the schema
324
+ *
325
+ * @example
326
+ * ```ts
327
+ * import { z } from 'zod'
328
+ *
329
+ * const userSchema = z.object({
330
+ * id: z.string(),
331
+ * name: z.string(),
332
+ * email: z.email()
333
+ * });
334
+ *
335
+ * // Validate data manually
336
+ * try {
337
+ * const validatedUser = client.validateSchema(userSchema, userData);
338
+ * console.log('Valid user:', validatedUser);
339
+ * } catch (error) {
340
+ * console.error('Invalid user data:', error);
341
+ * }
342
+ *
343
+ * // This is equivalent to the validation that happens automatically
344
+ * // when you use schema in requests
345
+ * ```
346
+ */
347
+ validateSchema<T>(schema: Schema<T>, data: unknown): T;
348
+ /**
349
+ * Prepares headers for HTTP requests by merging default and custom headers
350
+ *
351
+ * @param customHeaders - Additional headers to include in the request
352
+ * @returns Headers object with all headers properly set
353
+ */
354
+ private prepareHeaders;
355
+ /**
356
+ * Extracts headers from Headers object into a plain record
357
+ *
358
+ * @param headers - Headers object to extract from
359
+ * @returns Record containing header key-value pairs
360
+ */
361
+ private extractHeaders;
362
+ /**
363
+ * Creates an HttpError instance with response details
364
+ *
365
+ * @param response - The HTTP response that caused the error
366
+ * @param data - Response data (if any)
367
+ * @param url - The URL that was requested
368
+ * @param method - The HTTP method used
369
+ * @param customStatusText - Optional custom status text
370
+ * @param cause - Optional underlying error that caused this HTTP error
371
+ * @returns HttpError instance with complete error information
372
+ */
373
+ private createHttpError;
374
+ private parseResponseBody;
375
+ private processResponse;
376
+ private mergeAbortSignals;
377
+ /**
378
+ * Send a GET request with schema validation (type inferred from schema).
379
+ *
380
+ * @example
381
+ * // JSON response
382
+ * const response = await client.get('/users/:id', {
383
+ * pathParams: { id: '123' },
384
+ * schema: z.object({ id: z.string(), name: z.string() })
385
+ * })
386
+ *
387
+ * @example
388
+ * // Blob response
389
+ * const response = await client.get('/avatar', {
390
+ * responseType: 'blob',
391
+ * schema: z.instanceof(Blob)
392
+ * })
393
+ *
394
+ * @example
395
+ * // Text response
396
+ * const response = await client.get('/raw', {
397
+ * responseType: 'text',
398
+ * schema: z.string()
399
+ * })
400
+ */
401
+ get<TResponse, Path extends string = string, TParams extends RequestParamsType = RequestParamsType>(url: Path, options: TypedRequestOptions<void, TResponse, TParams, Path> & {
402
+ schema: Schema<TResponse>;
403
+ }): Promise<ResponseType_2<TResponse>>;
404
+ /**
405
+ * Send a GET request without schema (response.data is unknown).
406
+ *
407
+ * @example
408
+ * const response = await client.get('/users')
409
+ */
410
+ get<Path extends string = string, TParams extends RequestParamsType = RequestParamsType>(url: Path, options?: Omit<TypedRequestOptions<void, unknown, TParams, Path>, 'schema'>): Promise<ResponseType_2<unknown>>;
411
+ /**
412
+ * Send a POST request with schema validation (type inferred from schema).
413
+ *
414
+ * @example
415
+ * const response = await client.post('/users', userData, {
416
+ * schema: z.object({ id: z.string(), name: z.string() })
417
+ * })
418
+ */
419
+ post<TResponse, Path extends string = string, TParams extends RequestParamsType = RequestParamsType, TBody = unknown>(url: Path, body: TBody | undefined, options: Omit<TypedRequestOptions<TBody, TResponse, TParams, Path>, 'body'> & {
420
+ schema: Schema<TResponse>;
421
+ }): Promise<ResponseType_2<TResponse>>;
422
+ /**
423
+ * Send a POST request without schema (response.data is unknown).
424
+ *
425
+ * @example
426
+ * const response = await client.post('/users', userData)
427
+ */
428
+ post<Path extends string = string, TParams extends RequestParamsType = RequestParamsType, TBody = unknown>(url: Path, body?: TBody, options?: Omit<TypedRequestOptions<TBody, unknown, TParams, Path>, 'body' | 'schema'>): Promise<ResponseType_2<unknown>>;
429
+ /**
430
+ * Send a PUT request with schema validation (type inferred from schema).
431
+ *
432
+ * @example
433
+ * const response = await client.put('/users/:id', updatedData, {
434
+ * pathParams: { id: '123' },
435
+ * schema: z.object({ id: z.string(), name: z.string() })
436
+ * })
437
+ */
438
+ put<TResponse, Path extends string = string, TParams extends RequestParamsType = RequestParamsType, TBody = unknown>(url: Path, body: TBody | undefined, options: Omit<TypedRequestOptions<TBody, TResponse, TParams, Path>, 'body'> & {
439
+ schema: Schema<TResponse>;
440
+ }): Promise<ResponseType_2<TResponse>>;
441
+ /**
442
+ * Send a PUT request without schema (response.data is unknown).
443
+ *
444
+ * @example
445
+ * const response = await client.put('/users/:id', updatedData, {
446
+ * pathParams: { id: '123' }
447
+ * })
448
+ */
449
+ put<Path extends string = string, TParams extends RequestParamsType = RequestParamsType, TBody = unknown>(url: Path, body?: TBody, options?: Omit<TypedRequestOptions<TBody, unknown, TParams, Path>, 'body' | 'schema'>): Promise<ResponseType_2<unknown>>;
450
+ /**
451
+ * Send a PATCH request with schema validation (type inferred from schema).
452
+ *
453
+ * @example
454
+ * const response = await client.patch('/users/:id', partialData, {
455
+ * pathParams: { id: '123' },
456
+ * schema: z.object({ id: z.string(), name: z.string() })
457
+ * })
458
+ */
459
+ patch<TResponse, Path extends string = string, TParams extends RequestParamsType = RequestParamsType, TBody = unknown>(url: Path, body: TBody | undefined, options: Omit<TypedRequestOptions<TBody, TResponse, TParams, Path>, 'body'> & {
460
+ schema: Schema<TResponse>;
461
+ }): Promise<ResponseType_2<TResponse>>;
462
+ /**
463
+ * Send a PATCH request without schema (response.data is unknown).
464
+ *
465
+ * @example
466
+ * const response = await client.patch('/users/:id', partialData, {
467
+ * pathParams: { id: '123' }
468
+ * })
469
+ */
470
+ patch<Path extends string = string, TParams extends RequestParamsType = RequestParamsType, TBody = unknown>(url: Path, body?: TBody, options?: Omit<TypedRequestOptions<TBody, unknown, TParams, Path>, 'body' | 'schema'>): Promise<ResponseType_2<unknown>>;
471
+ /**
472
+ * Send a DELETE request with schema validation (type inferred from schema).
473
+ *
474
+ * @example
475
+ * const response = await client.delete('/users/:id', {
476
+ * pathParams: { id: '123' },
477
+ * schema: z.object({ success: z.boolean() })
478
+ * })
479
+ */
480
+ delete<TResponse, Path extends string = string, TParams extends RequestParamsType = RequestParamsType>(url: Path, options: TypedRequestOptions<void, TResponse, TParams, Path> & {
481
+ schema: Schema<TResponse>;
482
+ }): Promise<ResponseType_2<TResponse>>;
483
+ /**
484
+ * Send a DELETE request without schema (response.data is unknown).
485
+ *
486
+ * @example
487
+ * const response = await client.delete('/users/:id', {
488
+ * pathParams: { id: '123' }
489
+ * })
490
+ */
491
+ delete<Path extends string = string, TParams extends RequestParamsType = RequestParamsType>(url: Path, options?: Omit<TypedRequestOptions<void, unknown, TParams, Path>, 'schema'>): Promise<ResponseType_2<unknown>>;
492
+ /**
493
+ * Validates path parameters against the URL template
494
+ *
495
+ * @param url - URL template with path parameters (e.g., '/users/:id')
496
+ * @param pathParams - Path parameters to validate
497
+ * @throws Error if required path parameters are missing or invalid
498
+ */
499
+ private validatePathParams;
500
+ /**
501
+ * Extracts required path parameters from a URL template
502
+ *
503
+ * @param url - URL template to extract parameters from
504
+ * @returns Array of required parameter names
505
+ */
506
+ private extractRequiredPathParams;
507
+ /**
508
+ * Resolves a URL template with path parameters and base URL
509
+ *
510
+ * @param url - URL template with optional path parameters
511
+ * @param pathParams - Path parameters to interpolate into the URL
512
+ * @returns Fully resolved URL with base URL and path parameters applied
513
+ */
514
+ private resolveUrl;
515
+ }
516
+
517
+ declare interface HttpClientError {
518
+ name: string;
519
+ message: string;
520
+ cause?: Error;
521
+ stack?: string;
522
+ }
523
+
524
+ export declare interface HttpClientOptions {
525
+ baseUrl?: string;
526
+ headers?: Record<string, string>;
527
+ timeout?: number;
528
+ schemaValidator?: SchemaValidator;
529
+ requestInterceptors?: RequestInterceptor[];
530
+ responseInterceptors?: ResponseInterceptor[];
531
+ retryOptions?: RetryOptions;
532
+ }
533
+
534
+ export declare class HttpError<TErrorData = unknown> extends Error implements HttpClientError {
535
+ readonly name = "HttpError";
536
+ readonly status: number;
537
+ readonly statusText: string;
538
+ readonly data: TErrorData;
539
+ readonly response: Response;
540
+ readonly url: string;
541
+ readonly method: string;
542
+ readonly cause?: Error;
543
+ constructor(message: string, status: number, statusText: string, data: TErrorData, response: Response, url: string, method: string, cause?: Error);
544
+ }
545
+
546
+ export declare type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
547
+
548
+ export declare class InterceptorError extends Error implements HttpClientError {
549
+ readonly name = "InterceptorError";
550
+ readonly interceptorType: 'request' | 'response';
551
+ readonly url?: string;
552
+ readonly method?: string;
553
+ readonly cause?: Error;
554
+ constructor(message: string, interceptorType: 'request' | 'response', url?: string, method?: string, cause?: Error);
555
+ }
556
+
557
+ /**
558
+ * Type guard to check if an object conforms to the Standard Schema V1 specification
559
+ *
560
+ * Validates that an object has the required structure and properties
561
+ * to be considered a valid Standard Schema implementation.
562
+ *
563
+ * @param obj - The object to check
564
+ * @returns True if the object is a valid Standard Schema V1 implementation
565
+ *
566
+ * @example
567
+ * ```ts
568
+ * if (isStandardSchema(schema)) {
569
+ * // Use the schema with type safety
570
+ * const result = schema['~standard'].validate(data);
571
+ * }
572
+ * ```
573
+ */
574
+ export declare function isStandardSchema(obj: unknown): obj is StandardSchemaV1;
575
+
576
+ export declare class NetworkError extends Error implements HttpClientError {
577
+ readonly name = "NetworkError";
578
+ readonly cause?: Error;
579
+ constructor(message: string, cause?: Error);
580
+ }
581
+
582
+ export declare class PathParameterError extends Error implements HttpClientError {
583
+ readonly name = "PathParameterError";
584
+ readonly url: string;
585
+ readonly requiredParams: string[];
586
+ readonly providedParams: string[];
587
+ readonly cause?: Error;
588
+ constructor(message: string, url: string, requiredParams: string[], providedParams: string[], cause?: Error);
589
+ }
590
+
591
+ declare type PathParams<Path extends string> = ValidateNoOptionalParams<Path> extends never ? never : {
592
+ [K in ExtractRouteParams<Path>]: string | number;
593
+ };
594
+
595
+ /**
596
+ * Request context that interceptors can modify
597
+ */
598
+ export declare interface RequestContext<TBody = unknown> {
599
+ /** The URL to send the request to */
600
+ url: string;
601
+ /** The HTTP method (GET, POST, etc.) */
602
+ method: HttpMethod;
603
+ /** Query parameters to append to the URL */
604
+ params?: RequestParamsType;
605
+ /** Headers to send with the request */
606
+ headers: Headers;
607
+ /** Request body */
608
+ body?: TBody;
609
+ /** AbortSignal for request cancellation */
610
+ signal?: AbortSignal;
611
+ /** Fetch options */
612
+ fetchOptions: Omit<RequestInit, 'body' | 'headers' | 'signal' | 'method'>;
613
+ }
614
+
615
+ /**
616
+ * Request interceptor function
617
+ * Can modify request context before the request is sent
618
+ */
619
+ export declare type RequestInterceptor = <TBody = unknown>(context: RequestContext<TBody>) => RequestContext<TBody> | Promise<RequestContext<TBody>>;
620
+
621
+ export declare interface RequestOptions<TBody = unknown> {
622
+ headers?: Record<string, string>;
623
+ params?: RequestParamsType;
624
+ body?: TBody;
625
+ timeout?: number;
626
+ signal?: AbortSignal;
627
+ validateStatus?: (status: number) => boolean;
628
+ responseType?: 'json' | 'text' | 'blob' | 'arrayBuffer';
629
+ cache?: RequestCache;
630
+ credentials?: RequestCredentials;
631
+ mode?: RequestMode;
632
+ redirect?: RequestRedirect;
633
+ retry?: RetryOptions | boolean;
634
+ }
635
+
636
+ declare type RequestParamsType = Record<string, string | number | boolean | undefined>;
637
+
638
+ declare type RequirePathParams<Path extends string, T> = HasRequiredParams<Path> extends true ? T & {
639
+ pathParams: PathParams<Path>;
640
+ } : T;
641
+
642
+ /**
643
+ * Response interceptor function
644
+ * Can modify response data after the request returns
645
+ */
646
+ export declare type ResponseInterceptor = <T>(response: ResponseType_2<T>) => ResponseType_2<T> | Promise<ResponseType_2<T>>;
647
+
648
+ declare interface ResponseType_2<T = unknown> {
649
+ data: T;
650
+ status: number;
651
+ statusText: string;
652
+ headers: Record<string, string>;
653
+ method: HttpMethod;
654
+ url: string;
655
+ raw: Response;
656
+ }
657
+ export { ResponseType_2 as ResponseType }
658
+
659
+ export declare interface RetryOptions {
660
+ /** Maximum number of retry attempts (default: 3) */
661
+ maxRetries?: number;
662
+ /** Base delay between retries in milliseconds (default: 300) */
663
+ retryDelay?: number;
664
+ /** Exponential backoff factor (default: 2) */
665
+ backoffFactor?: number;
666
+ /** Status codes that should trigger a retry (default: [408, 429, 500, 502, 503, 504]) */
667
+ retryStatusCodes?: number[];
668
+ /** Whether to retry on network errors (default: true) */
669
+ retryNetworkErrors?: boolean;
670
+ /** Maximum retry delay in milliseconds (default: 30_000) */
671
+ maxRetryDelay?: number;
672
+ /** Custom function to determine if a request should be retried */
673
+ shouldRetry?: (error: Error, retryCount: number) => boolean | Promise<boolean>;
674
+ }
675
+
676
+ export declare type Schema<T = unknown> = StandardSchemaV1<unknown, T>;
677
+
678
+ export declare class SchemaValidationError extends Error implements HttpClientError {
679
+ readonly name = "SchemaValidationError";
680
+ readonly schema: unknown;
681
+ readonly data: unknown;
682
+ readonly cause?: Error;
683
+ constructor(message: string, schema: unknown, data: unknown, cause?: Error);
684
+ }
685
+
686
+ /**
687
+ * Interface for schema validators that can validate data against schemas.
688
+ *
689
+ * This interface allows you to create custom schema validators that work
690
+ * with different validation libraries (Zod, Valibot, Arktype, etc.).
691
+ */
692
+ export declare interface SchemaValidator {
693
+ /**
694
+ * Validate data against a schema.
695
+ *
696
+ * @template T - The expected type after validation
697
+ * @param schema - The schema to validate against
698
+ * @param data - The data to validate
699
+ * @returns The validated data with the correct type
700
+ * @throws {SchemaValidationError} If the data doesn't match the schema
701
+ */
702
+ validate<T>(schema: Schema<T>, data: unknown): T;
703
+ /**
704
+ * Check if an object is a valid schema.
705
+ *
706
+ * @param obj - The object to check
707
+ * @returns True if the object is a valid schema, false otherwise
708
+ */
709
+ isSchema(obj: unknown): boolean;
710
+ }
711
+
712
+ export declare class SerializationError extends Error implements HttpClientError {
713
+ readonly name = "SerializationError";
714
+ readonly cause?: Error;
715
+ constructor(message: string, cause?: Error);
716
+ }
717
+
718
+ /**
719
+ * Creates an adapter for Standard Schema V1 implementations
720
+ *
721
+ * Converts Standard Schema validation results into a format compatible
722
+ * with the HTTP client's schema validation system.
723
+ *
724
+ * @param schema - The Standard Schema V1 implementation to adapt
725
+ * @returns Adapter object with parse method
726
+ *
727
+ * @example
728
+ * ```ts
729
+ * const zodAdapter = standardSchemaAdapter(zodUserSchema);
730
+ * const validatedUser = zodAdapter.parse(userData);
731
+ * ```
732
+ */
733
+ export declare function standardSchemaAdapter<T>(schema: StandardSchemaV1<unknown, T>): {
734
+ /**
735
+ * Parses and validates data using the Standard Schema
736
+ *
737
+ * @param data - The data to validate
738
+ * @returns The validated and typed data
739
+ * @throws Error if validation fails
740
+ */
741
+ parse: (data: unknown) => T;
742
+ };
743
+
744
+ /**
745
+ * Standard Schema V1 interface implementation
746
+ *
747
+ * This interface defines the structure for schema validation libraries
748
+ * that conform to the Standard Schema specification.
749
+ *
750
+ * @see https://github.com/standard-schema/standard-schema
751
+ */
752
+ export declare interface StandardSchemaV1<Input = unknown, Output = Input> {
753
+ /** The Standard Schema properties containing validation logic and metadata */
754
+ readonly '~standard': StandardSchemaV1.Props<Input, Output>;
755
+ }
756
+
757
+ /**
758
+ * Namespace containing Standard Schema V1 type definitions
759
+ *
760
+ * Provides type-safe interfaces for schema validation results,
761
+ * error handling, and type inference.
762
+ */
763
+ export declare namespace StandardSchemaV1 {
764
+ /**
765
+ * Core properties interface for Standard Schema V1
766
+ *
767
+ * Contains the essential metadata and validation function
768
+ * that every Standard Schema implementation must provide.
769
+ */
770
+ export interface Props<Input = unknown, Output = Input> {
771
+ /** The version number of the Standard Schema specification */
772
+ readonly version: 1;
773
+ /** The name of the schema validation library (e.g., 'zod', 'valibot') */
774
+ readonly vendor: string;
775
+ /**
776
+ * Validates unknown input values against the schema
777
+ *
778
+ * @param value - The value to validate
779
+ * @returns Promise or direct result containing validation outcome
780
+ */
781
+ readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
782
+ /** Type information for input and output types */
783
+ readonly types?: Types<Input, Output> | undefined;
784
+ }
785
+ /**
786
+ * Union type representing the result of schema validation
787
+ *
788
+ * Can be either a successful validation with the validated value,
789
+ * or a failed validation with error details.
790
+ */
791
+ export type Result<Output> = SuccessResult<Output> | FailureResult;
792
+ /**
793
+ * Interface for successful validation results
794
+ *
795
+ * Contains the validated and typed output value.
796
+ */
797
+ export interface SuccessResult<Output> {
798
+ /** The validated and typed output value */
799
+ readonly value: Output;
800
+ /** Undefined since validation succeeded */
801
+ readonly issues?: undefined;
802
+ }
803
+ /**
804
+ * Interface for failed validation results
805
+ *
806
+ * Contains detailed error information about validation failures.
807
+ */
808
+ export interface FailureResult {
809
+ /** Array of validation error details */
810
+ readonly issues: ReadonlyArray<Issue>;
811
+ }
812
+ /**
813
+ * Interface for individual validation error details
814
+ *
815
+ * Provides error message and optional path information
816
+ * to help identify where validation failed.
817
+ */
818
+ export interface Issue {
819
+ /** Human-readable error message describing the validation failure */
820
+ readonly message: string;
821
+ /**
822
+ * Path to the property that failed validation
823
+ *
824
+ * Useful for nested object validation to pinpoint the exact location
825
+ * of validation failures.
826
+ */
827
+ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
828
+ }
829
+ /**
830
+ * Interface for path segment information in validation errors
831
+ *
832
+ * Used to provide detailed path information for validation failures
833
+ * in complex nested structures.
834
+ */
835
+ export interface PathSegment {
836
+ /** The property key representing this path segment */
837
+ readonly key: PropertyKey;
838
+ }
839
+ /**
840
+ * Interface for type information associated with a schema
841
+ *
842
+ * Provides compile-time type information for input and output types
843
+ * to enable better TypeScript integration and IntelliSense support.
844
+ */
845
+ export interface Types<Input = unknown, Output = Input> {
846
+ /** The input type that the schema expects */
847
+ readonly input: Input;
848
+ /** The output type that the schema produces after validation */
849
+ readonly output: Output;
850
+ }
851
+ /**
852
+ * Type utility to infer the input type from a Standard Schema
853
+ *
854
+ * Extracts the input type that a schema expects for validation.
855
+ *
856
+ * @example
857
+ * ```ts
858
+ * type UserInput = StandardSchemaV1.InferInput<typeof userSchema>
859
+ * ```
860
+ */
861
+ export type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
862
+ /**
863
+ * Type utility to infer the output type from a Standard Schema
864
+ *
865
+ * Extracts the output type that a schema produces after validation.
866
+ *
867
+ * @example
868
+ * ```ts
869
+ * type UserOutput = StandardSchemaV1.InferOutput<typeof userSchema>
870
+ * ```
871
+ */
872
+ export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
873
+ }
874
+
875
+ export declare type StrictTypedRequestOptions<TBody = unknown, TResponse = unknown, TParams extends RequestParamsType = RequestParamsType, Path extends string = string> = RequirePathParams<Path, TypedRequestOptions<TBody, TResponse, TParams, Path>>;
876
+
877
+ export declare class TimeoutError extends Error implements HttpClientError {
878
+ readonly name = "TimeoutError";
879
+ readonly cause?: Error;
880
+ constructor(message: string, cause?: Error);
881
+ }
882
+
883
+ export declare interface TypedRequestOptions<TBody = unknown, TResponse = unknown, TParams extends RequestParamsType = RequestParamsType, Path extends string = string> extends Omit<RequestOptions<TBody>, 'params'> {
884
+ params?: TParams;
885
+ schema?: Schema<TResponse>;
886
+ pathParams?: PathParams<Path>;
887
+ }
888
+
889
+ declare type ValidateNoOptionalParams<T extends string> = T extends `${string}:${string}?${string}` ? never : T extends `${string}:${string}/${infer Rest}` ? ValidateNoOptionalParams<Rest> : T;
890
+
891
+ export { }
package/dist/index.es.js CHANGED
@@ -908,6 +908,7 @@ export {
908
908
  NetworkError,
909
909
  PathParameterError,
910
910
  SchemaValidationError,
911
+ SerializationError,
911
912
  TimeoutError,
912
913
  createSchemaValidator,
913
914
  createStandardSchemaValidator,
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "1000fetches",
3
- "version": "0.1.0",
4
- "description": "A modern, type-safe HTTP client for TypeScript with built-in schema validation, interceptors, retry logic, and compile-time path parameter validation. Features full TypeScript support, automatic retry logic, request/response interceptors, and schema validation using popular libraries like Zod, Valibot, and ArkType.",
5
- "main": "dist/index.js",
6
- "module": "dist/index.mjs",
3
+ "version": "0.1.1",
4
+ "description": "A type-first HTTP client with compile-time path validation, runtime schema verification, smart retry logic, and interceptors powered by native fetch. Supports Zod, Valibot, ArkType, and any Standard Schema-compatible library.",
5
+ "main": "dist/index.cjs.js",
6
+ "module": "dist/index.es.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
@@ -15,6 +15,7 @@
15
15
  "files": [
16
16
  "dist"
17
17
  ],
18
+ "sideEffects": false,
18
19
  "scripts": {
19
20
  "build": "tsc && vite build",
20
21
  "dev": "vite build --watch",
@@ -64,6 +65,7 @@
64
65
  "typescript": "5.9.2",
65
66
  "valibot": "1.1.0",
66
67
  "vite": "7.1.7",
68
+ "vite-plugin-dts": "4.5.4",
67
69
  "vitest": "3.2.4",
68
70
  "zod": "4.1.11"
69
71
  },