UNPKG

@zhengxs/http

Version:

A lightweight cross-platform http request library

6 lines (5 loc) 18.5 kB
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("./registry.cjs"),x=r=>r instanceof Error?r:new Error(r);class p extends Error{}class d extends p{status;headers;error;code;param;type;constructor(e,t,n,s){super(`${d.makeMessage(e,t,n)}`),this.status=e,this.headers=s;const o=t;this.error=o,this.code=o?.code,this.param=o?.param,this.type=o?.type}static makeMessage(e,t,n){const s=t?.message?typeof t.message=="string"?t.message:JSON.stringify(t.message):t?JSON.stringify(t):n;return e&&s?`${e} ${s}`:e?`${e} status code (no body)`:s||"(no status code or body)"}static generate(e,t,n,s){if(!e)return new S({cause:x(t)});const o=t?.error;return e===400?new W(e,o,n,s):e===401?new J(e,o,n,s):e===403?new _(e,o,n,s):e===404?new K(e,o,n,s):e===409?new v(e,o,n,s):e===422?new G(e,o,n,s):e===429?new Q(e,o,n,s):e>=500?new V(e,o,n,s):new d(e,o,n,s)}}class q extends d{status=void 0;constructor({message:e}={}){super(void 0,void 0,e||"Request was aborted.",void 0)}}class S extends d{status=void 0;constructor({message:e,cause:t}){super(void 0,void 0,e||"Connection error.",void 0),t&&(this.cause=t)}}class N extends S{constructor({message:e}={}){super({message:e??"Request timed out."})}}class W extends d{status=400}class J extends d{status=401}class _ extends d{status=403}class K extends d{status=404}class v extends d{status=409}class G extends d{status=422}class Q extends d{status=429}class V extends d{}class w{constructor(e,t){this.iterator=e,this.controller=t}controller;static fromSSEResponse(e,t){let n=!1;const s=new z;async function*o(){if(!e.body)throw t.abort(),new p("Attempted to iterate over a response with no body");const i=new R,c=L(e.body);for await(const u of c)for(const f of i.decode(u)){const l=s.decode(f);l&&(yield l)}for(const u of i.flush()){const f=s.decode(u);f&&(yield f)}}async function*a(){if(n)throw new Error("Cannot iterate over a consumed stream, use `.tee()` to split the stream.");n=!0;let i=!1;try{for await(const c of o())if(!i){if(c.data.startsWith("[DONE]")){i=!0;continue}if(c.event===null){let u;try{u=JSON.parse(c.data)}catch(f){throw console.error("Could not parse message into JSON:",c.data),console.error("From chunk:",c.raw),f}if(u&&u.error)throw new d(void 0,u.error,void 0,void 0);yield u}}i=!0}catch(c){if(c instanceof Error&&c.name==="AbortError")return;throw c}finally{i||t.abort()}}return new w(a,t)}static fromReadableStream(e,t){let n=!1;async function*s(){const a=new R,i=L(e);for await(const c of i)for(const u of a.decode(c))yield u;for(const c of a.flush())yield c}async function*o(){if(n)throw new Error("Cannot iterate over a consumed stream, use `.tee()` to split the stream.");n=!0;let a=!1;try{for await(const i of s())a||i&&(yield JSON.parse(i));a=!0}catch(i){if(i instanceof Error&&i.name==="AbortError")return;throw i}finally{a||t.abort()}}return new w(o,t)}[Symbol.asyncIterator](){return this.iterator()}tee(){const e=[],t=[],n=this.iterator(),s=o=>({next:()=>{if(o.length===0){const a=n.next();e.push(a),t.push(a)}return o.shift()}});return[new w(()=>s(e),this.controller),new w(()=>s(t),this.controller)]}toReadableStream(){const e=this;let t;const n=new TextEncoder;return new ReadableStream({async start(){t=e[Symbol.asyncIterator]()},async pull(s){try{const{value:o,done:a}=await t.next();if(a)return s.close();const i=n.encode(JSON.stringify(o)+` `);s.enqueue(i)}catch(o){s.error(o)}},async cancel(){await t.return?.()}})}}class z{data;event;chunks;constructor(){this.event=null,this.data=[],this.chunks=[]}decode(e){if(e.endsWith("\r")&&(e=e.substring(0,e.length-1)),!e){if(!this.event&&!this.data.length)return null;const a={event:this.event,data:this.data.join(` `),raw:this.chunks};return this.event=null,this.data=[],this.chunks=[],a}if(this.chunks.push(e),e.startsWith(":"))return null;const[t,n,s]=oe(e,":");let o=s;return o.startsWith(" ")&&(o=o.substring(1)),t==="event"?this.event=o:t==="data"&&this.data.push(o),null}}class R{static NEWLINE_CHARS=new Set([` `,"\r","\v","\f","","","","…","\u2028","\u2029"]);static NEWLINE_REGEXP=/\r\n|[\n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029]/g;buffer;trailingCR;textDecoder;constructor(){this.buffer=[],this.trailingCR=!1}decode(e){let t=this.decodeText(e);if(this.trailingCR&&(t="\r"+t,this.trailingCR=!1),t.endsWith("\r")&&(this.trailingCR=!0,t=t.slice(0,-1)),!t)return[];const n=R.NEWLINE_CHARS.has(t[t.length-1]||"");let s=t.split(R.NEWLINE_REGEXP);return s.length===1&&!n?(this.buffer.push(s[0]),[]):(this.buffer.length>0&&(s=[this.buffer.join("")+s[0],...s.slice(1)],this.buffer=[]),n||(this.buffer=[s.pop()||""]),s)}decodeText(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof Buffer<"u"){if(e instanceof Buffer)return e.toString();if(e instanceof Uint8Array)return Buffer.from(e).toString();throw new p(`Unexpected: received non-Uint8Array (${e.constructor.name}) stream chunk in an environment with a global "Buffer" defined, which this library assumes to be Node. Please report this error.`)}if(typeof TextDecoder<"u"){if(e instanceof Uint8Array||e instanceof ArrayBuffer)return this.textDecoder??=new TextDecoder("utf8"),this.textDecoder.decode(e);throw new p(`Unexpected: received non-Uint8Array/ArrayBuffer (${e.constructor.name}) in a web platform. Please report this error.`)}throw new p("Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.")}flush(){if(!this.buffer.length&&!this.trailingCR)return[];const e=[this.buffer.join("")];return this.buffer=[],this.trailingCR=!1,e}}function oe(r,e){const t=r.indexOf(e);return t!==-1?[r.substring(0,t),e,r.substring(t+e.length)]:[r,"",""]}function L(r){if(r[Symbol.asyncIterator])return r;const e=r.getReader();return{async next(){try{const t=await e.read();return t?.done&&e.releaseLock(),t}catch(t){throw e.releaseLock(),t}},async return(){const t=e.cancel();return e.releaseLock(),await t,{done:!0,value:void 0}},[Symbol.asyncIterator](){return this}}}const j=r=>r!=null&&typeof r=="object"&&typeof r.url=="string"&&typeof r.blob=="function",X=r=>r!=null&&typeof r=="object"&&typeof r.name=="string"&&typeof r.lastModified=="number"&&D(r),D=r=>r!=null&&typeof r=="object"&&typeof r.size=="number"&&typeof r.type=="string"&&typeof r.text=="function"&&typeof r.slice=="function"&&typeof r.arrayBuffer=="function",B=r=>X(r)||j(r)||y.isFsReadStream(r);async function Y(r,e,t={}){if(r=await r,j(r)){const s=await r.blob();return e||=new URL(r.url).pathname.split(/[\\/]/).pop()??"unknown_file",new y.File([s],e,t)}const n=await ie(r);if(e||=ce(r)??"unknown_file",!t.type){const s=n[0]?.type;typeof s=="string"&&(t={...t,type:s})}return new y.File(n,e,t)}async function ie(r){const e=[];if(typeof r=="string"||ArrayBuffer.isView(r)||r instanceof ArrayBuffer)e.push(r);else if(D(r))e.push(await r.arrayBuffer());else if(ue(r))for await(const t of r)e.push(t);else throw new Error(`Unexpected data type: ${typeof r}; constructor: ${r?.constructor?.name}; props: ${ae(r)}`);return e}function ae(r){return`[${Object.getOwnPropertyNames(r).map(t=>`"${t}"`).join(", ")}]`}function ce(r){return O(r.name)||O(r.filename)||O(r.path)?.split(/[\\/]/).pop()}const O=r=>{if(typeof r=="string")return r;if(typeof Buffer<"u"&&r instanceof Buffer)return String(r)},ue=r=>r!=null&&typeof r=="object"&&typeof r[Symbol.asyncIterator]=="function",I=r=>r&&typeof r=="object"&&r.body&&r[Symbol.toStringTag]==="MultipartBody",fe=async r=>{if(!k(r.body))return r;const e=await H(r.body);return y.getMultipartRequestOptions(e,r)},le=async r=>{const e=await H(r.body);return y.getMultipartRequestOptions(e,r)},H=async r=>{const e=new FormData;return await Promise.all(Object.entries(r||{}).map(([t,n])=>U(e,t,n))),e},k=r=>{if(B(r))return!0;if(Array.isArray(r))return r.some(k);if(r&&typeof r=="object"){for(const e in r)if(k(r[e]))return!0}return!1},U=async(r,e,t)=>{if(t!==void 0){if(t==null)throw new TypeError(`Received null for "${e}"; to pass null in FormData, you must use the string 'null'`);if(typeof t=="string"||typeof t=="number"||typeof t=="boolean")r.append(e,String(t));else if(B(t)){const n=await Y(t);r.append(e,n)}else if(Array.isArray(t))await Promise.all(t.map(n=>U(r,e+"[]",n)));else if(typeof t=="object")await Promise.all(Object.entries(t).map(([n,s])=>U(r,`${e}[${n}]`,s)));else throw new TypeError(`Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${t} instead`)}},C=r=>{try{return JSON.parse(r)}catch{return}},Z=r=>new Promise(e=>setTimeout(e,r));function $(r){if(!r)return!0;for(const e in r)return!1;return!0}function ee(r,e){return Object.prototype.hasOwnProperty.call(r,e)}function m(r,...e){typeof process<"u"&&process.env.DEBUG==="true"&&console.log(`DINGTALK:DEBUG:${r}`,...e)}const te=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,r=>{const e=Math.random()*16|0;return(r==="x"?e:e&3|8).toString(16)}),de=new RegExp("^(?:[a-z]+:)?//","i"),re=r=>de.test(r),he={method:!0,path:!0,query:!0,body:!0,headers:!0,duplex:!0,maxRetries:!0,stream:!0,timeout:!0,httpAgent:!0,signal:!0,idempotencyKey:!0,__binaryResponse:!0},pe=r=>typeof r=="object"&&r!==null&&!$(r)&&Object.keys(r).every(e=>ee(he,e)),T=r=>new Proxy(Object.fromEntries(r.entries()),{get(e,t){const n=t.toString();return e[n.toLowerCase()]||e[n]}}),P=(r,e)=>{if(typeof e!="number"||!Number.isInteger(e))throw new p(`${r} must be an integer`);if(e<0)throw new p(`${r} must be a positive integer`);return e};async function F(r){const{response:e}=r;if(r.options.stream)return m("response",e.status,e.url,e.headers,e.body),w.fromSSEResponse(e,r.controller);if(e.status===204)return null;if(r.options.__binaryResponse)return e;if(e.headers.get("content-type")?.includes("application/json")){const s=await e.json();return m("response",e.status,e.url,e.headers,s),s}const n=await e.text();return m("response",e.status,e.url,e.headers,n),n}class E extends Promise{constructor(e,t=F){super(n=>{n(null)}),this.responsePromise=e,this.parseResponse=t}parsedPromise;_thenUnwrap(e){return new E(this.responsePromise,async t=>e(await this.parseResponse(t)))}asResponse(){return this.responsePromise.then(e=>e.response)}async withResponse(){const[e,t]=await Promise.all([this.parse(),this.asResponse()]);return{data:e,response:t}}parse(){return this.parsedPromise||(this.parsedPromise=this.responsePromise.then(this.parseResponse)),this.parsedPromise}then(e,t){return this.parse().then(e,t)}catch(e){return this.parse().catch(e)}finally(e){return this.parse().finally(e)}}class M{baseURL;maxRetries;timeout;httpAgent;fetch;idempotencyHeader;constructor({baseURL:e,maxRetries:t=2,timeout:n=6e5,httpAgent:s,fetch:o}){this.baseURL=e,this.maxRetries=P("maxRetries",t),this.timeout=P("timeout",n),this.httpAgent=s,this.fetch=o??y.fetch}authHeaders(e){return{}}async defaultHeaders(e){const t=await this.authHeaders(e);return{Accept:"application/json","Content-Type":"application/json","User-Agent":this.getUserAgent(),...t}}defaultQuery(){}validateHeaders(e,t){}defaultIdempotencyKey(){return`stainless-node-retry-${te()}`}get(e,t){return this.methodRequest("get",e,t)}post(e,t){return this.methodRequest("post",e,t)}patch(e,t){return this.methodRequest("patch",e,t)}put(e,t){return this.methodRequest("put",e,t)}delete(e,t){return this.methodRequest("delete",e,t)}methodRequest(e,t,n){return this.request(Promise.resolve(n).then(s=>({method:e,path:t,...s})))}getAPIList(e,t,n){return this.requestAPIList(t,{method:"get",path:e,...n})}calculateContentLength(e){if(typeof e=="string"){if(typeof Buffer<"u")return Buffer.byteLength(e,"utf8").toString();if(typeof TextEncoder<"u")return new TextEncoder().encode(e).length.toString()}return null}async buildRequest(e){const{method:t,path:n,query:s,headers:o={}}=e,a=I(e.body)?e.body.body:e.body?JSON.stringify(e.body,null,2):null,i=this.calculateContentLength(a),c=this.buildURL(n,s);"timeout"in e&&P("timeout",e.timeout);const u=e.timeout??this.timeout,f=e.httpAgent??this.httpAgent??y.getDefaultAgent(c),l=u+1e3;typeof f?.options?.timeout=="number"&&l>(f.options.timeout??0)&&(f.options.timeout=l),this.idempotencyHeader&&t!=="get"&&(e.idempotencyKey||(e.idempotencyKey=this.defaultIdempotencyKey()),o[this.idempotencyHeader]=e.idempotencyKey);const g=await this.defaultHeaders(e),h={...i&&{"Content-Length":i},...g,...o};I(e.body)&&y.kind!=="node"&&delete h["Content-Type"],Object.keys(h).forEach(A=>h[A]===null&&delete h[A]);const b={method:t,...a&&{body:a},headers:h,duplex:e.duplex,...f&&{agent:f},signal:e.signal??null};return this.validateHeaders(h,o),{req:b,url:c,timeout:u}}async prepareRequest(e,t){}parseHeaders(e){return e?Symbol.iterator in e?Object.fromEntries(Array.from(e).map(t=>[...t])):{...e}:{}}makeStatusError(e,t,n,s){return d.generate(e,t,n,s)}request(e,t=null){return new E(this.makeRequest(e,t))}async makeRequest(e,t){const n=await e;t==null&&(t=n.maxRetries??this.maxRetries);const{req:s,url:o,timeout:a}=await this.buildRequest(n);if(await this.prepareRequest(s,{url:o,options:n}),m("request",o,n,s.headers),n.signal?.aborted)throw new q;const i=new AbortController,c=await this.fetchWithTimeout(o,s,a,i).catch(x);if(c instanceof Error){if(n.signal?.aborted)throw new q;if(t)return this.retryRequest(n,t);throw c.name==="AbortError"?new N:new S({cause:c})}const u=T(c.headers);if(!c.ok){if(t&&this.shouldRetry(c))return this.retryRequest(n,t,u);const f=await c.text().catch(b=>x(b).message),l=C(f),g=l?void 0:f;throw m("response",c.status,o,u,g),this.makeStatusError(c.status,l,g,u)}return{response:c,options:n,controller:i}}simple(e,t){const n=Promise.resolve(t).then(s=>({method:"get",path:e,...s}));return new E(this.makeSimpleRequest(n))}async makeSimpleRequest(e,t){const n=await e;t==null&&(t=n.maxRetries??this.maxRetries);const s=I(n.body)?n.body.body:n.body?JSON.stringify(n.body,null,2):null,o=this.buildURL(n.path,n.query);"timeout"in n&&P("timeout",n.timeout);const a=n.timeout??this.timeout,i=n.httpAgent??this.httpAgent??y.getDefaultAgent(o),c=a+1e3;typeof i?.options?.timeout=="number"&&c>(i.options.timeout??0)&&(i.options.timeout=c);const u={method:n.method||"get",...s&&{body:s},headers:n.headers,...i&&{agent:i},signal:n.signal??null};m("request",o,n,u.headers);const f=new AbortController,l=await this.fetchWithTimeout(o,u,a,f).catch(x);if(l instanceof Error)throw u.signal?.aborted?new q:l.name==="AbortError"?new N:new S({cause:l});const g=T(l.headers);if(!l.ok){const h=await l.text().catch(se=>x(se).message),b=C(h),A=b?void 0:h;throw m("response",l.status,o,g,A),this.makeStatusError(l.status,b,A,g)}return{response:l,options:n,controller:f}}requestAPIList(e,t){const n=this.makeRequest(t,null);return new ne(this,n,e)}buildURL(e,t){const n=re(e)?new URL(e):new URL(this.baseURL+(this.baseURL.endsWith("/")&&e.startsWith("/")?e.slice(1):e)),s=this.defaultQuery();return $(s)||(t={...s,...t}),t&&(n.search=this.stringifyQuery(t)),n.toString()}stringifyQuery(e){return Object.entries(e).filter(([t,n])=>typeof n<"u").map(([t,n])=>{if(typeof n=="string"||typeof n=="number"||typeof n=="boolean")return`${encodeURIComponent(t)}=${encodeURIComponent(n)}`;if(n===null)return`${encodeURIComponent(t)}=`;throw new p(`Cannot stringify type ${typeof n}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`)}).join("&")}async fetchWithTimeout(e,t,n,s){const{signal:o,...a}=t||{};o&&o.addEventListener("abort",()=>s.abort());const i=setTimeout(()=>s.abort(),n);return this.getRequestClient().fetch.call(void 0,e,{signal:s.signal,...a}).finally(()=>{clearTimeout(i)})}getRequestClient(){return{fetch:this.fetch}}shouldRetry(e){const t=e.headers.get("x-should-retry");return t==="true"?!0:t==="false"?!1:e.status===408||e.status===409||e.status===429||e.status>=500}async retryRequest(e,t,n){let s;const o=n?.["retry-after"];if(o){const a=parseInt(o);Number.isNaN(a)?s=Date.parse(o)-Date.now():s=a*1e3}if(!s||!Number.isInteger(s)||s<=0||s>60*1e3){const a=e.maxRetries??this.maxRetries;s=this.calculateDefaultRetryTimeoutMillis(t,a)}return await Z(s),this.makeRequest(e,t-1)}calculateDefaultRetryTimeoutMillis(e,t){const o=t-e,a=Math.min(.5*Math.pow(2,o),8),i=1-Math.random()*.25;return a*i*1e3}getUserAgent(){return`${this.constructor.name}/JS`}static create(e,t){return new M({baseURL:e,...t})}}class ye{#e;options;response;body;constructor(e,t,n,s){this.#e=e,this.options=s,this.response=t,this.body=n}hasNextPage(){return this.getPaginatedItems().length?this.nextPageInfo()!=null:!1}async getNextPage(){const e=this.nextPageInfo();if(!e)throw new p("No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`.");const t={...this.options};if("params"in e)t.query={...t.query,...e.params};else if("url"in e){const n=[...Object.entries(t.query||{}),...e.url.searchParams.entries()];for(const[s,o]of n)e.url.searchParams.set(s,o);t.query=void 0,t.path=e.url.toString()}return await this.#e.requestAPIList(this.constructor,t)}async*iterPages(){let e=this;for(yield e;e.hasNextPage();)e=await e.getNextPage(),yield e}async*[Symbol.asyncIterator](){for await(const e of this.iterPages())for(const t of e.getPaginatedItems())yield t}}class ne extends E{constructor(e,t,n){super(t,async s=>new n(e,s.response,await F(s),s.options))}async*[Symbol.asyncIterator](){const e=await this;for await(const t of e)yield t}}exports.APIClient=M;exports.APIConnectionError=S;exports.APIConnectionTimeoutError=N;exports.APIError=d;exports.APIPromise=E;exports.APIUserAbortError=q;exports.AbstractPage=ye;exports.AuthenticationError=J;exports.BadRequestError=W;exports.ConflictError=v;exports.HttpException=p;exports.InternalServerError=V;exports.LineDecoder=R;exports.NotFoundError=K;exports.PagePromise=ne;exports.PermissionDeniedError=_;exports.RateLimitError=Q;exports.SSEDecoder=z;exports.Stream=w;exports.UnprocessableEntityError=G;exports.castToError=x;exports.createForm=H;exports.createResponseHeaders=T;exports.debug=m;exports.defaultParseResponse=F;exports.hasOwn=ee;exports.isAbsoluteURL=re;exports.isBlobLike=D;exports.isEmptyObj=$;exports.isFileLike=X;exports.isMultipartBody=I;exports.isRequestOptions=pe;exports.isResponseLike=j;exports.isUploadable=B;exports.maybeMultipartFormRequestOptions=fe;exports.multipartFormRequestOptions=le;exports.readableStreamAsyncIterable=L;exports.safeJSON=C;exports.sleep=Z;exports.toFile=Y;exports.uuid4=te;exports.validatePositiveInteger=P; //# sourceMappingURL=index.cjs.map