@firekid/hurl 1.0.3 → 1.0.5

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/README.md CHANGED
@@ -6,6 +6,10 @@ A modern HTTP client for Node.js and edge runtimes. Zero dependencies. Full Type
6
6
  npm install @firekid/hurl
7
7
  ```
8
8
 
9
+ ## GitHub
10
+
11
+ https://github.com/Firekid-is-him/hurl
12
+
9
13
  ## Purpose
10
14
 
11
15
  `hurl` solves the problems that `request` left behind when it was deprecated and that `axios` never fully addressed: no edge runtime support, a 35KB bundle, no built-in retry logic, no request deduplication, and no upload progress tracking. `hurl` ships all of these in under 3KB with zero runtime dependencies.
package/dist/index.d.mts CHANGED
@@ -47,10 +47,10 @@ type HurlRequestOptions = {
47
47
  cache?: CacheConfig;
48
48
  signal?: AbortSignal;
49
49
  followRedirects?: boolean;
50
- maxRedirects?: number;
51
50
  onUploadProgress?: ProgressCallback;
52
51
  onDownloadProgress?: ProgressCallback;
53
52
  stream?: boolean;
53
+ throwOnError?: boolean;
54
54
  debug?: boolean;
55
55
  requestId?: string;
56
56
  deduplicate?: boolean;
package/dist/index.d.ts CHANGED
@@ -47,10 +47,10 @@ type HurlRequestOptions = {
47
47
  cache?: CacheConfig;
48
48
  signal?: AbortSignal;
49
49
  followRedirects?: boolean;
50
- maxRedirects?: number;
51
50
  onUploadProgress?: ProgressCallback;
52
51
  onDownloadProgress?: ProgressCallback;
53
52
  stream?: boolean;
53
+ throwOnError?: boolean;
54
54
  debug?: boolean;
55
55
  requestId?: string;
56
56
  deduplicate?: boolean;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var O=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var oe=Object.prototype.hasOwnProperty;var ue=(e,r)=>{for(var t in r)O(e,t,{get:r[t],enumerable:!0})},ie=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of se(r))!oe.call(e,s)&&s!==t&&O(e,s,{get:()=>r[s],enumerable:!(n=ne(r,s))||n.enumerable});return e};var le=e=>ie(O({},"__esModule",{value:!0}),e);var me={};ue(me,{HurlError:()=>l,clearCache:()=>J,createInstance:()=>w,default:()=>Re});module.exports=le(me);var l=class extends Error{constructor(r){super(r.message),this.name="HurlError",this.type=r.type,this.status=r.status,this.statusText=r.statusText,this.data=r.data,this.headers=r.headers,this.requestId=r.requestId,this.retries=r.retries??0}};function $(e){return new l({message:`HTTP ${e.status}: ${e.statusText}`,type:"HTTP_ERROR",...e})}function v(e,r){return new l({message:e,type:"NETWORK_ERROR",requestId:r})}function F(e,r){return new l({message:`Request timed out after ${e}ms`,type:"TIMEOUT_ERROR",requestId:r})}function S(e){return new l({message:"Request was aborted",type:"ABORT_ERROR",requestId:e})}function B(e,r){return new l({message:`Failed to parse response: ${e}`,type:"PARSE_ERROR",requestId:r})}async function _(e,r){let t=e.body?.getReader(),n=parseInt(e.headers.get("content-length")??"0",10);if(!t)return e.text();let s=[],u=0;for(;;){let{done:i,value:c}=await t.read();if(i)break;s.push(c),u+=c.length,r({loaded:u,total:n,percent:n>0?Math.round(u/n*100):0})}let a=new Uint8Array(u),o=0;for(let i of s)a.set(i,o),o+=i.length;return new TextDecoder().decode(a)}function I(e){let r={};return e.forEach((t,n)=>{r[n]=t}),r}async function K(e,r,t,n){if(n==="HEAD"||e.status===204||e.headers.get("content-length")==="0")return null;let s=e.headers.get("content-type")??"";try{if(t){let a=await _(e,t);return s.includes("application/json")?JSON.parse(a):a}if(s.includes("application/json"))return await e.json();if(s.includes("text/"))return await e.text();if(s.includes("application/octet-stream")||s.includes("image/"))return await e.arrayBuffer();let u=await e.text();return u||null}catch(u){throw B(u.message,r)}}function L(e,r,t,n){let s=Date.now();return{data:e,status:r.status,statusText:r.statusText,headers:I(r.headers),requestId:t,timing:{start:n,end:s,duration:s-n},fromCache:!1}}function M(e,r,t){if(t.type==="bearer"&&(e.Authorization=`Bearer ${t.token}`),t.type==="basic"){let n=btoa(`${t.username}:${t.password}`);e.Authorization=`Basic ${n}`}t.type==="apikey"&&(t.in==="query"?r[t.key]=t.value:e[t.key]=t.value)}function j(e){return e==null?null:typeof e=="number"?{count:e,delay:300,backoff:"exponential"}:e}function N(e,r,t){return t>=r.count||e.type==="ABORT_ERROR"?!1:r.on&&e.status?r.on.includes(e.status):!!(e.type==="NETWORK_ERROR"||e.type==="TIMEOUT_ERROR"||e.status&&e.status>=500)}async function G(e,r){let t=e.delay??300,n=e.backoff==="exponential"?t*Math.pow(2,r):t*(r+1);await new Promise(s=>setTimeout(s,n))}var b=new Map;function P(e,r){return r?.key??e}function W(e){let r=b.get(e);return r?Date.now()>r.expiresAt?(b.delete(e),null):{...r.response,fromCache:!0}:null}function z(e,r,t){b.set(e,{response:r,expiresAt:Date.now()+t.ttl})}function J(){b.clear()}var k=new Map;function Q(e){return k.get(e)??null}function V(e,r){k.set(e,r),r.finally(()=>k.delete(e))}function X(e,r){console.group(`[hurl] \u2192 ${r.method??"GET"} ${e}`),r.headers&&Object.keys(r.headers).length>0&&console.log("headers:",r.headers),r.query&&console.log("query:",r.query),r.body&&console.log("body:",r.body),r.timeout&&console.log("timeout:",r.timeout),r.retry&&console.log("retry:",r.retry),console.groupEnd()}function C(e){let r=e.status>=400?"\u{1F534}":e.status>=300?"\u{1F7E1}":"\u{1F7E2}";console.group(`[hurl] ${r} ${e.status} ${e.statusText} (${e.timing.duration}ms)`),console.log("requestId:",e.requestId),e.fromCache&&console.log("served from cache"),console.log("headers:",e.headers),console.log("data:",e.data),console.groupEnd()}function Y(e){console.group("[hurl] \u{1F534} Error"),console.error(e),console.groupEnd()}function ae(){return Math.random().toString(36).slice(2,10)}function ce(e,r,t){let n;if(r.startsWith("http://")||r.startsWith("https://")){if(e){let u=new URL(e).origin,a=new URL(r).origin;if(u!==a)throw new Error(`Absolute URL "${r}" does not match baseUrl origin "${u}". Pass the full URL without baseUrl, or use a path-relative URL.`)}n=r}else{if(r.startsWith("//"))throw new Error("Protocol-relative URLs are not supported. Use an explicit https:// or http:// scheme.");n=e?`${e.replace(/\/$/,"")}/${r.replace(/^\//,"")}`:r}if(!t||Object.keys(t).length===0)return n;let s=new URLSearchParams;for(let[u,a]of Object.entries(t))s.set(u,String(a));return`${n}?${s.toString()}`}function pe(e,r){let t={...r.headers,...e.headers},n=e.body;return n&&typeof n=="object"&&!(n instanceof FormData)&&!(n instanceof Blob)&&(t["Content-Type"]=t["Content-Type"]??"application/json"),t}function fe(e){if(e!=null)return e instanceof FormData||e instanceof Blob||e instanceof ArrayBuffer||typeof e=="string"?e:JSON.stringify(e)}function de(e,r){if(r)return!0;let t=e;return!!(t?.name==="TimeoutError"||t?.name==="AbortError"&&t?.message?.includes("timeout"))}async function Z(e,r,t){let n=r.requestId??ae(),s=r.method??"GET",u=Date.now(),a=j(r.retry??t.retry),o=r.debug??t.debug??!1,i={...t.query,...r.query},c=pe(r,t),y=r.timeout??t.timeout,T=r.auth??t.auth;T&&M(c,i,T);let R=ce(t.baseUrl??"",e,Object.keys(i).length>0?i:void 0),m=r.cache??t.cache,f=!!m&&!m.bypass&&s==="GET";if(f){let d=P(R,m),h=W(d);if(h)return o&&C(h),h}let H=r.deduplicate??t.deduplicate??!1;if(H&&s==="GET"){let d=Q(R);if(d)return d}o&&X(R,{...r,method:s});let A=async d=>{let h=null,U=!1,x=new AbortController;r.signal&&r.signal.addEventListener("abort",()=>x.abort()),y&&(h=setTimeout(()=>{U=!0,x.abort()},y));try{let p=await fetch(R,{method:s,headers:c,body:fe(r.body),signal:x.signal,redirect:r.followRedirects??!0?"follow":"manual"});h&&clearTimeout(h);let g=await K(p,n,r.onDownloadProgress,s);if(!p.ok)throw $({status:p.status,statusText:p.statusText,data:g,headers:I(p.headers),requestId:n,retries:d});let q=L(g,p,n,u);return f&&m&&z(P(R,m),q,m),o&&C(q),q}catch(p){h&&clearTimeout(h);let g;if(p instanceof l?g=p:p.name==="AbortError"||p.name==="TimeoutError"?g=de(p,U)?F(y??0,n):S(n):g=v(p.message,n),g.retries=d,a&&N(g,a,d))return o&&console.log(`[hurl] retrying (${d+1}/${a.count})...`),await G(a,d),A(d+1);throw o&&Y(g),g}},D=A(0);return H&&s==="GET"&&V(R,D),D}function E(){let e=[];return{use(r){return e.push(r),()=>{let t=e.indexOf(r);t!==-1&&e.splice(t,1)}},clear(){e.length=0},getAll(){return[...e]}}}async function ee(e,r,t){let n={url:r,options:t};for(let s of e)n=await s(n.url,n.options);return n}async function re(e,r){let t=r;for(let n of e)t=await n(t);return t}async function te(e,r){let t=r;for(let n of e)t instanceof l&&(t=await n(t));return t}function w(e={}){let r={...e},t=E(),n=E(),s=E();async function u(o,i={}){let c=o,y=i,T=t.getAll(),R=n.getAll(),m=s.getAll();if(T.length>0){let f=await ee(T,o,i);c=f.url,y=f.options}try{let f=await Z(c,y,r);return R.length>0?await re(R,f):f}catch(f){if(f instanceof l&&m.length>0){let H=await te(m,f);if(!(H instanceof l))return H;throw H}throw f}}return{request:u,get(o,i){return u(o,{...i,method:"GET"})},post(o,i,c){return u(o,{...c,method:"POST",body:i})},put(o,i,c){return u(o,{...c,method:"PUT",body:i})},patch(o,i,c){return u(o,{...c,method:"PATCH",body:i})},delete(o,i){return u(o,{...i,method:"DELETE"})},head(o,i){return u(o,{...i,method:"HEAD"})},options(o,i){return u(o,{...i,method:"OPTIONS"})},all(o){return Promise.all(o)},defaults:{set(o){r={...r,...o}},get(){return{...r}},reset(){r={...e}}},interceptors:{request:{use:t.use.bind(t),clear:t.clear.bind(t)},response:{use:n.use.bind(n),clear:n.clear.bind(n)},error:{use:s.use.bind(s),clear:s.clear.bind(s)}},create(o){return w({...r,...o})},extend(o){return w({...r,...o})}}}var ge=w(),Re=ge;0&&(module.exports={HurlError,clearCache,createInstance});
1
+ "use strict";var P=Object.defineProperty;var ae=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var ce=Object.prototype.hasOwnProperty;var pe=(e,r)=>{for(var t in r)P(e,t,{get:r[t],enumerable:!0})},fe=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of le(r))!ce.call(e,s)&&s!==t&&P(e,s,{get:()=>r[s],enumerable:!(n=ae(r,s))||n.enumerable});return e};var de=e=>fe(P({},"__esModule",{value:!0}),e);var we={};pe(we,{HurlError:()=>c,clearCache:()=>Z,createInstance:()=>I,default:()=>be});module.exports=de(we);var c=class extends Error{constructor(r){super(r.message),this.name="HurlError",this.type=r.type,this.status=r.status,this.statusText=r.statusText,this.data=r.data,this.headers=r.headers,this.requestId=r.requestId,this.retries=r.retries??0}};function F(e){return new c({message:`HTTP ${e.status}: ${e.statusText}`,type:"HTTP_ERROR",...e})}function _(e,r){return new c({message:e,type:"NETWORK_ERROR",requestId:r})}function M(e,r){return new c({message:`Request timed out after ${e}ms`,type:"TIMEOUT_ERROR",requestId:r})}function j(e){return new c({message:"Request was aborted",type:"ABORT_ERROR",requestId:e})}function k(e,r){return new c({message:`Failed to parse response: ${e}`,type:"PARSE_ERROR",requestId:r})}async function K(e,r){let t=e.body?.getReader(),n=parseInt(e.headers.get("content-length")??"0",10);if(!t)return new ArrayBuffer(0);let s=[],u=0;for(;;){let{done:i,value:p}=await t.read();if(i)break;s.push(p),u+=p.byteLength,r({loaded:u,total:n,percent:n>0?Math.round(u/n*100):0})}let a=new Uint8Array(u),o=0;for(let i of s)a.set(i,o),o+=i.byteLength;return a.buffer}function N(e,r){let t=0;typeof e=="string"?t=new TextEncoder().encode(e).byteLength:e instanceof ArrayBuffer?t=e.byteLength:e instanceof Blob&&(t=e.size);let n=0;return(e instanceof ReadableStream?e:new Response(e).body).pipeThrough(new TransformStream({transform(u,a){n+=u.byteLength,r({loaded:n,total:t,percent:t>0?Math.round(n/t*100):0}),a.enqueue(u)}}))}function C(e){let r={};return e.forEach((t,n)=>{r[n]=t}),r}async function W(e,r,t,n,s){if(t==="HEAD"||e.status===204||e.headers.get("content-length")==="0")return null;if(n)return e.body;let u=e.headers.get("content-type")??"",a=u.includes("application/octet-stream")||u.includes("image/")||u.includes("video/")||u.includes("audio/");if(s&&a)try{return await K(e,s)}catch(o){throw k(o.message,r)}try{return u.includes("application/json")?await e.json():u.includes("text/")?await e.text():a?await e.arrayBuffer():await e.text()}catch(o){throw k(o.message,r)}}function G(e,r,t,n){let s=Date.now();return{data:e,status:r.status,statusText:r.statusText,headers:C(r.headers),requestId:t,timing:{start:n,end:s,duration:s-n},fromCache:!1}}function ge(e){return typeof globalThis<"u"&&globalThis.Buffer?globalThis.Buffer.from(e).toString("base64"):btoa(encodeURIComponent(e).replace(/%([0-9A-F]{2})/g,(r,t)=>String.fromCharCode(parseInt(t,16))))}function z(e,r,t){if(t.type==="bearer"&&(e.Authorization=`Bearer ${t.token}`),t.type==="basic"){let n=ge(`${t.username}:${t.password}`);e.Authorization=`Basic ${n}`}t.type==="apikey"&&(t.in==="query"?r[t.key]=t.value:e[t.key]=t.value)}function X(e){return e==null?null:typeof e=="number"?{count:e,delay:300,backoff:"exponential"}:e}function Y(e,r,t){return t>=r.count||e.type==="ABORT_ERROR"?!1:r.on&&e.status?r.on.includes(e.status):!!(e.type==="NETWORK_ERROR"||e.type==="TIMEOUT_ERROR"||e.status&&e.status>=500)}async function J(e,r){let t=e.delay??300,n=e.backoff==="exponential"?t*Math.pow(2,r):t*(r+1);await new Promise(s=>setTimeout(s,n))}var E=new Map;function A(e,r){return r?.key??e}function Q(e){let r=E.get(e);return r?Date.now()>r.expiresAt?(E.delete(e),null):{...r.response,fromCache:!0}:null}function V(e,r,t){E.set(e,{response:r,expiresAt:Date.now()+t.ttl})}function Z(){E.clear()}var U=new Map;function ee(e){return U.get(e)??null}function re(e,r){U.set(e,r),r.finally(()=>U.delete(e))}function te(e,r){console.group(`[hurl] \u2192 ${r.method??"GET"} ${e}`),r.headers&&Object.keys(r.headers).length>0&&console.log("headers:",r.headers),r.query&&console.log("query:",r.query),r.body&&console.log("body:",r.body),r.timeout&&console.log("timeout:",r.timeout),r.retry&&console.log("retry:",r.retry),console.groupEnd()}function D(e){let r=e.status>=400?"\u{1F534}":e.status>=300?"\u{1F7E1}":"\u{1F7E2}";console.group(`[hurl] ${r} ${e.status} ${e.statusText} (${e.timing.duration}ms)`),console.log("requestId:",e.requestId),e.fromCache&&console.log("served from cache"),console.log("headers:",e.headers),console.log("data:",e.data),console.groupEnd()}function ne(e){console.group("[hurl] \u{1F534} Error"),console.error(e),console.groupEnd()}function Re(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():Math.random().toString(36).slice(2,10)}function me(e,r,t){let n;if(r.startsWith("http://")||r.startsWith("https://")){if(e){let u=new URL(e).origin,a=new URL(r).origin;if(u!==a)throw new Error(`Absolute URL "${r}" does not match baseUrl origin "${u}". Pass the full URL without baseUrl, or use a path-relative URL.`)}n=r}else{if(r.startsWith("//"))throw new Error("Protocol-relative URLs are not supported. Use an explicit https:// or http:// scheme.");n=e?`${e.replace(/\/$/,"")}/${r.replace(/^\//,"")}`:r}if(!t||Object.keys(t).length===0)return n;let s=new URLSearchParams;for(let[u,a]of Object.entries(t))s.set(u,String(a));return`${n}?${s.toString()}`}function he(e){return e instanceof ReadableStream||e!==null&&typeof e=="object"&&typeof e.pipe=="function"}function ye(e,r){let t={...r.headers,...e.headers},n=e.body;return n!=null&&typeof n=="object"&&!(n instanceof FormData)&&!(n instanceof Blob)&&!(n instanceof ArrayBuffer)&&!he(n)&&(t["Content-Type"]=t["Content-Type"]??"application/json"),t}function Te(e){if(e!=null)return e instanceof FormData||e instanceof Blob||e instanceof ArrayBuffer||typeof e=="string"||e instanceof ReadableStream||typeof e.pipe=="function"?e:JSON.stringify(e)}async function oe(e,r,t){let n=r.requestId??Re(),s=r.method??"GET",u=Date.now(),a=X(r.retry??t.retry),o=r.debug??t.debug??!1,i=r.throwOnError??t.throwOnError??!0,p={...t.query,...r.query},H=ye(r,t),h=r.timeout??t.timeout,b=r.auth??t.auth;b&&z(H,p,b);let R=me(t.baseUrl??"",e,Object.keys(p).length>0?p:void 0);(r.proxy??t.proxy)&&o&&console.warn("[hurl] proxy option is not supported with native fetch. Use HTTP_PROXY/HTTPS_PROXY env vars in Node.js.");let l=r.cache??t.cache,y=!!l&&!l.bypass&&s==="GET";if(y){let f=A(R,l),m=Q(f);if(m)return o&&D(m),m}let B=r.deduplicate??t.deduplicate??!1;if(B&&s==="GET"){let f=ee(R);if(f)return f}o&&te(R,{...r,method:s});let v=async f=>{let m=null,$=!1,O=new AbortController,w=null;r.signal&&(w=()=>O.abort(),r.signal.addEventListener("abort",w)),h&&(m=setTimeout(()=>{$=!0,O.abort()},h));try{let d=Te(r.body),g=r.onUploadProgress??t.onUploadProgress;d!==void 0&&g&&(r.body instanceof FormData?o&&console.warn("[hurl] onUploadProgress is not supported for FormData bodies. Use XMLHttpRequest for FormData upload progress."):d=N(d,g));let T=await fetch(R,{method:s,headers:H,body:d,signal:O.signal,redirect:r.followRedirects??!0?"follow":"manual"}),L=await W(T,n,s,r.stream??!1,r.onDownloadProgress??t.onDownloadProgress);if(!T.ok&&i)throw F({status:T.status,statusText:T.statusText,data:L,headers:C(T.headers),requestId:n,retries:f});let q=G(L,T,n,u);return y&&l&&V(A(R,l),q,l),o&&D(q),q}catch(d){let g;if(d instanceof c?g=d:d.name==="AbortError"||d.code==="ABORT_ERR"?g=$?M(h,n):j(n):g=_(d.message,n),g.retries=f,a&&Y(g,a,f))return o&&console.log(`[hurl] retrying (${f+1}/${a.count})...`),await J(a,f),v(f+1);throw o&&ne(g),g}finally{m&&clearTimeout(m),w&&r.signal&&r.signal.removeEventListener("abort",w)}},S=v(0);return B&&s==="GET"&&re(R,S),S}function x(){let e=[];return{use(r){return e.push(r),()=>{let t=e.indexOf(r);t!==-1&&e.splice(t,1)}},clear(){e.length=0},getAll(){return[...e]}}}async function se(e,r,t){let n={url:r,options:t};for(let s of e)n=await s(n.url,n.options);return n}async function ue(e,r){let t=r;for(let n of e)t=await n(t);return t}async function ie(e,r){let t=r;for(let n of e)t instanceof c&&(t=await n(t));return t}function I(e={}){let r={...e},t=x(),n=x(),s=x();async function u(o,i={}){let p=o,H=i,h=t.getAll(),b=n.getAll(),R=s.getAll();if(h.length>0){let l=await se(h,o,i);p=l.url,H=l.options}try{let l=await oe(p,H,r);return b.length>0?await ue(b,l):l}catch(l){if(l instanceof c&&R.length>0){let y=await ie(R,l);if(!(y instanceof c))return y;throw y}throw l}}return{request:u,get(o,i){return u(o,{...i,method:"GET"})},post(o,i,p){return u(o,{...p,method:"POST",body:i})},put(o,i,p){return u(o,{...p,method:"PUT",body:i})},patch(o,i,p){return u(o,{...p,method:"PATCH",body:i})},delete(o,i){return u(o,{...i,method:"DELETE"})},head(o,i){return u(o,{...i,method:"HEAD"})},options(o,i){return u(o,{...i,method:"OPTIONS"})},all(o){return Promise.all(o)},defaults:{set(o){r={...r,...o}},get(){return{...r}},reset(){r={...e}}},interceptors:{request:{use:t.use.bind(t),clear:t.clear.bind(t)},response:{use:n.use.bind(n),clear:n.clear.bind(n)},error:{use:s.use.bind(s),clear:s.clear.bind(s)}},create(o){return I({...r,...o})},extend(o){return I({...r,...o})}}}var He=I(),be=He;0&&(module.exports={HurlError,clearCache,createInstance});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var a=class extends Error{constructor(r){super(r.message),this.name="HurlError",this.type=r.type,this.status=r.status,this.statusText=r.statusText,this.data=r.data,this.headers=r.headers,this.requestId=r.requestId,this.retries=r.retries??0}};function U(e){return new a({message:`HTTP ${e.status}: ${e.statusText}`,type:"HTTP_ERROR",...e})}function $(e,r){return new a({message:e,type:"NETWORK_ERROR",requestId:r})}function v(e,r){return new a({message:`Request timed out after ${e}ms`,type:"TIMEOUT_ERROR",requestId:r})}function F(e){return new a({message:"Request was aborted",type:"ABORT_ERROR",requestId:e})}function S(e,r){return new a({message:`Failed to parse response: ${e}`,type:"PARSE_ERROR",requestId:r})}async function B(e,r){let t=e.body?.getReader(),n=parseInt(e.headers.get("content-length")??"0",10);if(!t)return e.text();let o=[],u=0;for(;;){let{done:i,value:c}=await t.read();if(i)break;o.push(c),u+=c.length,r({loaded:u,total:n,percent:n>0?Math.round(u/n*100):0})}let l=new Uint8Array(u),s=0;for(let i of o)l.set(i,s),s+=i.length;return new TextDecoder().decode(l)}function q(e){let r={};return e.forEach((t,n)=>{r[n]=t}),r}async function _(e,r,t,n){if(n==="HEAD"||e.status===204||e.headers.get("content-length")==="0")return null;let o=e.headers.get("content-type")??"";try{if(t){let l=await B(e,t);return o.includes("application/json")?JSON.parse(l):l}if(o.includes("application/json"))return await e.json();if(o.includes("text/"))return await e.text();if(o.includes("application/octet-stream")||o.includes("image/"))return await e.arrayBuffer();let u=await e.text();return u||null}catch(u){throw S(u.message,r)}}function K(e,r,t,n){let o=Date.now();return{data:e,status:r.status,statusText:r.statusText,headers:q(r.headers),requestId:t,timing:{start:n,end:o,duration:o-n},fromCache:!1}}function L(e,r,t){if(t.type==="bearer"&&(e.Authorization=`Bearer ${t.token}`),t.type==="basic"){let n=btoa(`${t.username}:${t.password}`);e.Authorization=`Basic ${n}`}t.type==="apikey"&&(t.in==="query"?r[t.key]=t.value:e[t.key]=t.value)}function M(e){return e==null?null:typeof e=="number"?{count:e,delay:300,backoff:"exponential"}:e}function j(e,r,t){return t>=r.count||e.type==="ABORT_ERROR"?!1:r.on&&e.status?r.on.includes(e.status):!!(e.type==="NETWORK_ERROR"||e.type==="TIMEOUT_ERROR"||e.status&&e.status>=500)}async function N(e,r){let t=e.delay??300,n=e.backoff==="exponential"?t*Math.pow(2,r):t*(r+1);await new Promise(o=>setTimeout(o,n))}var b=new Map;function O(e,r){return r?.key??e}function G(e){let r=b.get(e);return r?Date.now()>r.expiresAt?(b.delete(e),null):{...r.response,fromCache:!0}:null}function W(e,r,t){b.set(e,{response:r,expiresAt:Date.now()+t.ttl})}function re(){b.clear()}var I=new Map;function z(e){return I.get(e)??null}function J(e,r){I.set(e,r),r.finally(()=>I.delete(e))}function Q(e,r){console.group(`[hurl] \u2192 ${r.method??"GET"} ${e}`),r.headers&&Object.keys(r.headers).length>0&&console.log("headers:",r.headers),r.query&&console.log("query:",r.query),r.body&&console.log("body:",r.body),r.timeout&&console.log("timeout:",r.timeout),r.retry&&console.log("retry:",r.retry),console.groupEnd()}function P(e){let r=e.status>=400?"\u{1F534}":e.status>=300?"\u{1F7E1}":"\u{1F7E2}";console.group(`[hurl] ${r} ${e.status} ${e.statusText} (${e.timing.duration}ms)`),console.log("requestId:",e.requestId),e.fromCache&&console.log("served from cache"),console.log("headers:",e.headers),console.log("data:",e.data),console.groupEnd()}function V(e){console.group("[hurl] \u{1F534} Error"),console.error(e),console.groupEnd()}function te(){return Math.random().toString(36).slice(2,10)}function ne(e,r,t){let n;if(r.startsWith("http://")||r.startsWith("https://")){if(e){let u=new URL(e).origin,l=new URL(r).origin;if(u!==l)throw new Error(`Absolute URL "${r}" does not match baseUrl origin "${u}". Pass the full URL without baseUrl, or use a path-relative URL.`)}n=r}else{if(r.startsWith("//"))throw new Error("Protocol-relative URLs are not supported. Use an explicit https:// or http:// scheme.");n=e?`${e.replace(/\/$/,"")}/${r.replace(/^\//,"")}`:r}if(!t||Object.keys(t).length===0)return n;let o=new URLSearchParams;for(let[u,l]of Object.entries(t))o.set(u,String(l));return`${n}?${o.toString()}`}function se(e,r){let t={...r.headers,...e.headers},n=e.body;return n&&typeof n=="object"&&!(n instanceof FormData)&&!(n instanceof Blob)&&(t["Content-Type"]=t["Content-Type"]??"application/json"),t}function oe(e){if(e!=null)return e instanceof FormData||e instanceof Blob||e instanceof ArrayBuffer||typeof e=="string"?e:JSON.stringify(e)}function ue(e,r){if(r)return!0;let t=e;return!!(t?.name==="TimeoutError"||t?.name==="AbortError"&&t?.message?.includes("timeout"))}async function X(e,r,t){let n=r.requestId??te(),o=r.method??"GET",u=Date.now(),l=M(r.retry??t.retry),s=r.debug??t.debug??!1,i={...t.query,...r.query},c=se(r,t),y=r.timeout??t.timeout,T=r.auth??t.auth;T&&L(c,i,T);let R=ne(t.baseUrl??"",e,Object.keys(i).length>0?i:void 0),m=r.cache??t.cache,f=!!m&&!m.bypass&&o==="GET";if(f){let d=O(R,m),h=G(d);if(h)return s&&P(h),h}let H=r.deduplicate??t.deduplicate??!1;if(H&&o==="GET"){let d=z(R);if(d)return d}s&&Q(R,{...r,method:o});let C=async d=>{let h=null,D=!1,w=new AbortController;r.signal&&r.signal.addEventListener("abort",()=>w.abort()),y&&(h=setTimeout(()=>{D=!0,w.abort()},y));try{let p=await fetch(R,{method:o,headers:c,body:oe(r.body),signal:w.signal,redirect:r.followRedirects??!0?"follow":"manual"});h&&clearTimeout(h);let g=await _(p,n,r.onDownloadProgress,o);if(!p.ok)throw U({status:p.status,statusText:p.statusText,data:g,headers:q(p.headers),requestId:n,retries:d});let x=K(g,p,n,u);return f&&m&&W(O(R,m),x,m),s&&P(x),x}catch(p){h&&clearTimeout(h);let g;if(p instanceof a?g=p:p.name==="AbortError"||p.name==="TimeoutError"?g=ue(p,D)?v(y??0,n):F(n):g=$(p.message,n),g.retries=d,l&&j(g,l,d))return s&&console.log(`[hurl] retrying (${d+1}/${l.count})...`),await N(l,d),C(d+1);throw s&&V(g),g}},A=C(0);return H&&o==="GET"&&J(R,A),A}function E(){let e=[];return{use(r){return e.push(r),()=>{let t=e.indexOf(r);t!==-1&&e.splice(t,1)}},clear(){e.length=0},getAll(){return[...e]}}}async function Y(e,r,t){let n={url:r,options:t};for(let o of e)n=await o(n.url,n.options);return n}async function Z(e,r){let t=r;for(let n of e)t=await n(t);return t}async function ee(e,r){let t=r;for(let n of e)t instanceof a&&(t=await n(t));return t}function k(e={}){let r={...e},t=E(),n=E(),o=E();async function u(s,i={}){let c=s,y=i,T=t.getAll(),R=n.getAll(),m=o.getAll();if(T.length>0){let f=await Y(T,s,i);c=f.url,y=f.options}try{let f=await X(c,y,r);return R.length>0?await Z(R,f):f}catch(f){if(f instanceof a&&m.length>0){let H=await ee(m,f);if(!(H instanceof a))return H;throw H}throw f}}return{request:u,get(s,i){return u(s,{...i,method:"GET"})},post(s,i,c){return u(s,{...c,method:"POST",body:i})},put(s,i,c){return u(s,{...c,method:"PUT",body:i})},patch(s,i,c){return u(s,{...c,method:"PATCH",body:i})},delete(s,i){return u(s,{...i,method:"DELETE"})},head(s,i){return u(s,{...i,method:"HEAD"})},options(s,i){return u(s,{...i,method:"OPTIONS"})},all(s){return Promise.all(s)},defaults:{set(s){r={...r,...s}},get(){return{...r}},reset(){r={...e}}},interceptors:{request:{use:t.use.bind(t),clear:t.clear.bind(t)},response:{use:n.use.bind(n),clear:n.clear.bind(n)},error:{use:o.use.bind(o),clear:o.clear.bind(o)}},create(s){return k({...r,...s})},extend(s){return k({...r,...s})}}}var ie=k(),Ve=ie;export{a as HurlError,re as clearCache,k as createInstance,Ve as default};
1
+ var p=class extends Error{constructor(r){super(r.message),this.name="HurlError",this.type=r.type,this.status=r.status,this.statusText=r.statusText,this.data=r.data,this.headers=r.headers,this.requestId=r.requestId,this.retries=r.retries??0}};function L(e){return new p({message:`HTTP ${e.status}: ${e.statusText}`,type:"HTTP_ERROR",...e})}function F(e,r){return new p({message:e,type:"NETWORK_ERROR",requestId:r})}function _(e,r){return new p({message:`Request timed out after ${e}ms`,type:"TIMEOUT_ERROR",requestId:r})}function M(e){return new p({message:"Request was aborted",type:"ABORT_ERROR",requestId:e})}function q(e,r){return new p({message:`Failed to parse response: ${e}`,type:"PARSE_ERROR",requestId:r})}async function j(e,r){let t=e.body?.getReader(),n=parseInt(e.headers.get("content-length")??"0",10);if(!t)return new ArrayBuffer(0);let u=[],s=0;for(;;){let{done:i,value:c}=await t.read();if(i)break;u.push(c),s+=c.byteLength,r({loaded:s,total:n,percent:n>0?Math.round(s/n*100):0})}let a=new Uint8Array(s),o=0;for(let i of u)a.set(i,o),o+=i.byteLength;return a.buffer}function K(e,r){let t=0;typeof e=="string"?t=new TextEncoder().encode(e).byteLength:e instanceof ArrayBuffer?t=e.byteLength:e instanceof Blob&&(t=e.size);let n=0;return(e instanceof ReadableStream?e:new Response(e).body).pipeThrough(new TransformStream({transform(s,a){n+=s.byteLength,r({loaded:n,total:t,percent:t>0?Math.round(n/t*100):0}),a.enqueue(s)}}))}function P(e){let r={};return e.forEach((t,n)=>{r[n]=t}),r}async function N(e,r,t,n,u){if(t==="HEAD"||e.status===204||e.headers.get("content-length")==="0")return null;if(n)return e.body;let s=e.headers.get("content-type")??"",a=s.includes("application/octet-stream")||s.includes("image/")||s.includes("video/")||s.includes("audio/");if(u&&a)try{return await j(e,u)}catch(o){throw q(o.message,r)}try{return s.includes("application/json")?await e.json():s.includes("text/")?await e.text():a?await e.arrayBuffer():await e.text()}catch(o){throw q(o.message,r)}}function W(e,r,t,n){let u=Date.now();return{data:e,status:r.status,statusText:r.statusText,headers:P(r.headers),requestId:t,timing:{start:n,end:u,duration:u-n},fromCache:!1}}function ue(e){return typeof globalThis<"u"&&globalThis.Buffer?globalThis.Buffer.from(e).toString("base64"):btoa(encodeURIComponent(e).replace(/%([0-9A-F]{2})/g,(r,t)=>String.fromCharCode(parseInt(t,16))))}function G(e,r,t){if(t.type==="bearer"&&(e.Authorization=`Bearer ${t.token}`),t.type==="basic"){let n=ue(`${t.username}:${t.password}`);e.Authorization=`Basic ${n}`}t.type==="apikey"&&(t.in==="query"?r[t.key]=t.value:e[t.key]=t.value)}function z(e){return e==null?null:typeof e=="number"?{count:e,delay:300,backoff:"exponential"}:e}function X(e,r,t){return t>=r.count||e.type==="ABORT_ERROR"?!1:r.on&&e.status?r.on.includes(e.status):!!(e.type==="NETWORK_ERROR"||e.type==="TIMEOUT_ERROR"||e.status&&e.status>=500)}async function Y(e,r){let t=e.delay??300,n=e.backoff==="exponential"?t*Math.pow(2,r):t*(r+1);await new Promise(u=>setTimeout(u,n))}var E=new Map;function k(e,r){return r?.key??e}function J(e){let r=E.get(e);return r?Date.now()>r.expiresAt?(E.delete(e),null):{...r.response,fromCache:!0}:null}function Q(e,r,t){E.set(e,{response:r,expiresAt:Date.now()+t.ttl})}function ie(){E.clear()}var C=new Map;function V(e){return C.get(e)??null}function Z(e,r){C.set(e,r),r.finally(()=>C.delete(e))}function ee(e,r){console.group(`[hurl] \u2192 ${r.method??"GET"} ${e}`),r.headers&&Object.keys(r.headers).length>0&&console.log("headers:",r.headers),r.query&&console.log("query:",r.query),r.body&&console.log("body:",r.body),r.timeout&&console.log("timeout:",r.timeout),r.retry&&console.log("retry:",r.retry),console.groupEnd()}function A(e){let r=e.status>=400?"\u{1F534}":e.status>=300?"\u{1F7E1}":"\u{1F7E2}";console.group(`[hurl] ${r} ${e.status} ${e.statusText} (${e.timing.duration}ms)`),console.log("requestId:",e.requestId),e.fromCache&&console.log("served from cache"),console.log("headers:",e.headers),console.log("data:",e.data),console.groupEnd()}function re(e){console.group("[hurl] \u{1F534} Error"),console.error(e),console.groupEnd()}function ae(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():Math.random().toString(36).slice(2,10)}function le(e,r,t){let n;if(r.startsWith("http://")||r.startsWith("https://")){if(e){let s=new URL(e).origin,a=new URL(r).origin;if(s!==a)throw new Error(`Absolute URL "${r}" does not match baseUrl origin "${s}". Pass the full URL without baseUrl, or use a path-relative URL.`)}n=r}else{if(r.startsWith("//"))throw new Error("Protocol-relative URLs are not supported. Use an explicit https:// or http:// scheme.");n=e?`${e.replace(/\/$/,"")}/${r.replace(/^\//,"")}`:r}if(!t||Object.keys(t).length===0)return n;let u=new URLSearchParams;for(let[s,a]of Object.entries(t))u.set(s,String(a));return`${n}?${u.toString()}`}function ce(e){return e instanceof ReadableStream||e!==null&&typeof e=="object"&&typeof e.pipe=="function"}function pe(e,r){let t={...r.headers,...e.headers},n=e.body;return n!=null&&typeof n=="object"&&!(n instanceof FormData)&&!(n instanceof Blob)&&!(n instanceof ArrayBuffer)&&!ce(n)&&(t["Content-Type"]=t["Content-Type"]??"application/json"),t}function fe(e){if(e!=null)return e instanceof FormData||e instanceof Blob||e instanceof ArrayBuffer||typeof e=="string"||e instanceof ReadableStream||typeof e.pipe=="function"?e:JSON.stringify(e)}async function te(e,r,t){let n=r.requestId??ae(),u=r.method??"GET",s=Date.now(),a=z(r.retry??t.retry),o=r.debug??t.debug??!1,i=r.throwOnError??t.throwOnError??!0,c={...t.query,...r.query},H=pe(r,t),h=r.timeout??t.timeout,b=r.auth??t.auth;b&&G(H,c,b);let R=le(t.baseUrl??"",e,Object.keys(c).length>0?c:void 0);(r.proxy??t.proxy)&&o&&console.warn("[hurl] proxy option is not supported with native fetch. Use HTTP_PROXY/HTTPS_PROXY env vars in Node.js.");let l=r.cache??t.cache,y=!!l&&!l.bypass&&u==="GET";if(y){let f=k(R,l),m=J(f);if(m)return o&&A(m),m}let D=r.deduplicate??t.deduplicate??!1;if(D&&u==="GET"){let f=V(R);if(f)return f}o&&ee(R,{...r,method:u});let B=async f=>{let m=null,S=!1,I=new AbortController,w=null;r.signal&&(w=()=>I.abort(),r.signal.addEventListener("abort",w)),h&&(m=setTimeout(()=>{S=!0,I.abort()},h));try{let d=fe(r.body),g=r.onUploadProgress??t.onUploadProgress;d!==void 0&&g&&(r.body instanceof FormData?o&&console.warn("[hurl] onUploadProgress is not supported for FormData bodies. Use XMLHttpRequest for FormData upload progress."):d=K(d,g));let T=await fetch(R,{method:u,headers:H,body:d,signal:I.signal,redirect:r.followRedirects??!0?"follow":"manual"}),$=await N(T,n,u,r.stream??!1,r.onDownloadProgress??t.onDownloadProgress);if(!T.ok&&i)throw L({status:T.status,statusText:T.statusText,data:$,headers:P(T.headers),requestId:n,retries:f});let O=W($,T,n,s);return y&&l&&Q(k(R,l),O,l),o&&A(O),O}catch(d){let g;if(d instanceof p?g=d:d.name==="AbortError"||d.code==="ABORT_ERR"?g=S?_(h,n):M(n):g=F(d.message,n),g.retries=f,a&&X(g,a,f))return o&&console.log(`[hurl] retrying (${f+1}/${a.count})...`),await Y(a,f),B(f+1);throw o&&re(g),g}finally{m&&clearTimeout(m),w&&r.signal&&r.signal.removeEventListener("abort",w)}},v=B(0);return D&&u==="GET"&&Z(R,v),v}function x(){let e=[];return{use(r){return e.push(r),()=>{let t=e.indexOf(r);t!==-1&&e.splice(t,1)}},clear(){e.length=0},getAll(){return[...e]}}}async function ne(e,r,t){let n={url:r,options:t};for(let u of e)n=await u(n.url,n.options);return n}async function oe(e,r){let t=r;for(let n of e)t=await n(t);return t}async function se(e,r){let t=r;for(let n of e)t instanceof p&&(t=await n(t));return t}function U(e={}){let r={...e},t=x(),n=x(),u=x();async function s(o,i={}){let c=o,H=i,h=t.getAll(),b=n.getAll(),R=u.getAll();if(h.length>0){let l=await ne(h,o,i);c=l.url,H=l.options}try{let l=await te(c,H,r);return b.length>0?await oe(b,l):l}catch(l){if(l instanceof p&&R.length>0){let y=await se(R,l);if(!(y instanceof p))return y;throw y}throw l}}return{request:s,get(o,i){return s(o,{...i,method:"GET"})},post(o,i,c){return s(o,{...c,method:"POST",body:i})},put(o,i,c){return s(o,{...c,method:"PUT",body:i})},patch(o,i,c){return s(o,{...c,method:"PATCH",body:i})},delete(o,i){return s(o,{...i,method:"DELETE"})},head(o,i){return s(o,{...i,method:"HEAD"})},options(o,i){return s(o,{...i,method:"OPTIONS"})},all(o){return Promise.all(o)},defaults:{set(o){r={...r,...o}},get(){return{...r}},reset(){r={...e}}},interceptors:{request:{use:t.use.bind(t),clear:t.clear.bind(t)},response:{use:n.use.bind(n),clear:n.clear.bind(n)},error:{use:u.use.bind(u),clear:u.clear.bind(u)}},create(o){return U({...r,...o})},extend(o){return U({...r,...o})}}}var de=U(),nr=de;export{p as HurlError,ie as clearCache,U as createInstance,nr as default};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firekid/hurl",
3
- "version": "1.0.3",
4
- "description": "The modern HTTP client. Axios DX. Fetch speed. Zero dependencies.",
3
+ "version": "1.0.5",
4
+ "description": "Zero-dependency HTTP client for Node.js and edge runtimes. Built on fetch. The modern replacement for axios, request, got, node-fetch, and ky. Works on Cloudflare Workers, Vercel Edge, Deno, and Bun.",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
@@ -12,7 +12,9 @@
12
12
  "require": "./dist/index.cjs"
13
13
  }
14
14
  },
15
- "files": ["dist"],
15
+ "files": [
16
+ "dist"
17
+ ],
16
18
  "scripts": {
17
19
  "build": "tsup",
18
20
  "dev": "tsup --watch",
@@ -20,12 +22,88 @@
20
22
  "test:watch": "vitest",
21
23
  "typecheck": "tsc --noEmit"
22
24
  },
23
- "keywords": ["http", "https", "fetch", "axios", "request", "client", "typescript", "edge", "proxy", "retry"],
25
+ "keywords": [
26
+ "http",
27
+ "https",
28
+ "fetch",
29
+ "fetch-wrapper",
30
+ "fetch-api",
31
+ "axios",
32
+ "axios-alternative",
33
+ "axios-replacement",
34
+ "axios-wrapper",
35
+ "request",
36
+ "request-replacement",
37
+ "request-deprecated",
38
+ "got",
39
+ "got-alternative",
40
+ "ky",
41
+ "ky-alternative",
42
+ "node-fetch",
43
+ "node-fetch-alternative",
44
+ "wretch",
45
+ "superagent",
46
+ "http-client",
47
+ "http-request",
48
+ "https-client",
49
+ "rest-client",
50
+ "api-client",
51
+ "node",
52
+ "nodejs",
53
+ "node-http",
54
+ "typescript",
55
+ "typescript-http",
56
+ "edge-runtime",
57
+ "edge-function",
58
+ "cloudflare-workers",
59
+ "cloudflare",
60
+ "vercel-edge",
61
+ "vercel",
62
+ "deno",
63
+ "bun",
64
+ "zero-dependencies",
65
+ "zero-deps",
66
+ "no-dependencies",
67
+ "interceptors",
68
+ "request-interceptor",
69
+ "response-interceptor",
70
+ "retry",
71
+ "auto-retry",
72
+ "exponential-backoff",
73
+ "timeout",
74
+ "abort",
75
+ "abort-controller",
76
+ "auth",
77
+ "bearer-token",
78
+ "basic-auth",
79
+ "api-key",
80
+ "proxy",
81
+ "socks5",
82
+ "http-proxy",
83
+ "cache",
84
+ "request-cache",
85
+ "deduplication",
86
+ "request-dedup",
87
+ "upload-progress",
88
+ "download-progress",
89
+ "file-upload",
90
+ "multipart",
91
+ "stream",
92
+ "streaming",
93
+ "json",
94
+ "json-parser",
95
+ "modern",
96
+ "lightweight",
97
+ "minimal",
98
+ "promise",
99
+ "async-await"
100
+ ],
24
101
  "license": "MIT",
25
102
  "devDependencies": {
103
+ "@types/node": "^20.0.0",
26
104
  "tsup": "^8.0.0",
105
+ "tsx": "^4.21.0",
27
106
  "typescript": "^5.0.0",
28
- "vitest": "^1.0.0",
29
- "@types/node": "^20.0.0"
107
+ "vitest": "^1.0.0"
30
108
  }
31
- }
109
+ }