@ecosy/core 0.3.0 → 0.3.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/http.d.ts +76 -3
- package/dist/http.js +1 -1
- package/dist/http.mjs +1 -1
- package/package.json +1 -1
package/dist/http.d.ts
CHANGED
|
@@ -33,6 +33,31 @@ export interface HttpRequest<Body = unknown, Params = Record<string, unknown>, Q
|
|
|
33
33
|
query?: Query;
|
|
34
34
|
params?: Params;
|
|
35
35
|
signal?: AbortSignal;
|
|
36
|
+
/**
|
|
37
|
+
* Pass-through bag for `fetch`'s `RequestInit` options that the
|
|
38
|
+
* library does not manage itself (`credentials`, `cache`, `mode`,
|
|
39
|
+
* `redirect`, `referrer`, `referrerPolicy`, `integrity`, `keepalive`,
|
|
40
|
+
* `priority`, `duplex`) plus framework extensions (`next` on Next.js,
|
|
41
|
+
* `cf` on Cloudflare Workers, `dispatcher` on undici).
|
|
42
|
+
*
|
|
43
|
+
* Lib-level fields (`method`, `headers`, `body`, `signal`) cannot be
|
|
44
|
+
* overridden here — they are applied after this bag is spread.
|
|
45
|
+
* Unknown keys are silently dropped, so a compromised caller cannot
|
|
46
|
+
* smuggle arbitrary fields into `fetch`.
|
|
47
|
+
*/
|
|
48
|
+
configs?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
/** Constructor options for {@link Http}. */
|
|
51
|
+
export interface HttpOptions {
|
|
52
|
+
/** Base URL prepended to every relative request path. */
|
|
53
|
+
baseURL?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Additional origins (besides `baseURL`'s origin) that absolute URLs
|
|
56
|
+
* and redirect responses are permitted to reach. Any other origin is
|
|
57
|
+
* rejected before the request is sent and, for redirects, before the
|
|
58
|
+
* response body is returned.
|
|
59
|
+
*/
|
|
60
|
+
allowedOrigins?: ReadonlyArray<string>;
|
|
36
61
|
}
|
|
37
62
|
/** Storage adapter interface for reading/writing auth tokens (e.g. `localStorage`). */
|
|
38
63
|
export interface HttpStorage {
|
|
@@ -114,13 +139,23 @@ export declare class Endpoint {
|
|
|
114
139
|
* ```
|
|
115
140
|
*/
|
|
116
141
|
export declare class Http {
|
|
117
|
-
private readonly baseURL;
|
|
118
142
|
static readonly method: typeof HttpMethod;
|
|
119
143
|
static readonly Endpoint: typeof Endpoint;
|
|
120
144
|
static authTokenKey: string;
|
|
121
145
|
static authHeaderKey: string;
|
|
122
146
|
static authHeaderType: string;
|
|
123
147
|
static authDetectToken: string[];
|
|
148
|
+
/**
|
|
149
|
+
* One-shot headers merged into the very next request across all
|
|
150
|
+
* instances, then cleared. Intended for request-scoped values like
|
|
151
|
+
* CSRF tokens or correlation IDs that callers don't want to thread
|
|
152
|
+
* through every call site.
|
|
153
|
+
*
|
|
154
|
+
* Note: this is a process-wide mutable static. In concurrent async
|
|
155
|
+
* contexts (e.g. multiple tenants sharing one process) it is the
|
|
156
|
+
* caller's responsibility to ensure the set → dispatch → reset
|
|
157
|
+
* sequence is not interleaved.
|
|
158
|
+
*/
|
|
124
159
|
static extraHeaders: Record<string, string> | null;
|
|
125
160
|
static storage: HttpStorage | null;
|
|
126
161
|
private defaultHeaders;
|
|
@@ -128,7 +163,34 @@ export declare class Http {
|
|
|
128
163
|
private storage;
|
|
129
164
|
private static interceptors;
|
|
130
165
|
private interceptors;
|
|
131
|
-
|
|
166
|
+
private readonly baseURL;
|
|
167
|
+
private readonly allowedOrigins;
|
|
168
|
+
/**
|
|
169
|
+
* @param init - Either a base URL string (backwards-compatible form)
|
|
170
|
+
* or an {@link HttpOptions} object. Using the object form lets
|
|
171
|
+
* callers opt in to additional origins that absolute URLs and
|
|
172
|
+
* redirect responses are permitted to reach. Any other origin is
|
|
173
|
+
* rejected before the request is sent and, for redirects, before
|
|
174
|
+
* the response body is returned.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* new Http("https://api.example.com");
|
|
179
|
+
* new Http({
|
|
180
|
+
* baseURL: "https://api.example.com",
|
|
181
|
+
* allowedOrigins: ["https://cdn.example.com"],
|
|
182
|
+
* });
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
constructor(init?: string | HttpOptions);
|
|
186
|
+
/** Whether `origin` is this instance's baseURL origin or an explicitly allowed one. */
|
|
187
|
+
private isAllowedOrigin;
|
|
188
|
+
/**
|
|
189
|
+
* If a request was sent with credentials (Authorization / Cookie) and
|
|
190
|
+
* ended up at a different origin via redirect, refuse to return the
|
|
191
|
+
* response. Defends against token exfil via server-controlled 3xx.
|
|
192
|
+
*/
|
|
193
|
+
private assertSameOriginResponse;
|
|
132
194
|
/** Register a global interceptor (applies to all `Http` instances). */
|
|
133
195
|
static on(...params: HttpInterceptorParameters): void;
|
|
134
196
|
/** Remove a global interceptor. */
|
|
@@ -153,7 +215,18 @@ export declare class Http {
|
|
|
153
215
|
getHeaders(headers?: Record<string, string>, isFormData?: boolean): {
|
|
154
216
|
[x: string]: string;
|
|
155
217
|
};
|
|
156
|
-
/**
|
|
218
|
+
/**
|
|
219
|
+
* Build the full URL from base URL, path, query string, and path params.
|
|
220
|
+
*
|
|
221
|
+
* Security rules applied here (see SECURITY audit):
|
|
222
|
+
* - Absolute URLs must use `http`/`https` and their origin must match
|
|
223
|
+
* `baseURL`'s origin or an entry in `allowedOrigins`.
|
|
224
|
+
* - Protocol-relative URLs (`//host/…`) are rejected — they silently
|
|
225
|
+
* flip the target host.
|
|
226
|
+
* - Path params (`{id}`) are URL-encoded by `interpolateURL`
|
|
227
|
+
* so a value of `"../admin"` cannot traverse the path.
|
|
228
|
+
* - Proto-pollution keys in `params` are stripped.
|
|
229
|
+
*/
|
|
157
230
|
getURL(options: HttpRequest): string;
|
|
158
231
|
/** Serialize the request body (JSON, FormData, or binary). Returns `undefined` for bodyless methods. */
|
|
159
232
|
getBody(options: HttpRequest): string | FormData | Uint8Array | ArrayBuffer | undefined;
|
package/dist/http.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var t=require("./utilities/filelist.js"),e=require("./utilities/flatten.js"),r=require("./utilities/formdata.js"),s=require("./utilities/sanitize-mime.js"),o=require("./serialize.js"),n=require("./env.js");const a=n.getEnv("API_URL","/"),i=n.getEnv("API_AUTH_TOKEN_KEY"),c=n.getEnv("API_AUTH_HEADER_KEY"),p=n.getEnv("API_AUTH_HEADER_TYPE");var d,u;exports.HttpMethod=void 0,(d=exports.HttpMethod||(exports.HttpMethod={})).GET="GET",d.POST="POST",d.PUT="PUT",d.DELETE="DELETE",d.PATCH="PATCH",d.HEAD="HEAD",d.OPTIONS="OPTIONS",exports.HttpUpload=void 0,(u=exports.HttpUpload||(exports.HttpUpload={})).UPLOAD="UPLOAD",u.RELATED="RELATED";class l{static register(t,e){return this.registered[t]=Object.assign({},e),this}static all(){return Object.assign({},this.registered)}}l.registered={};class h{constructor(t=a){this.baseURL=t,this.defaultHeaders={"Content-Type":"application/json"},this.method=h.method,this.storage=h.storage,this.interceptors={request:[],response:[],transform:[],error:[]}}static on(...t){const[e,r]=t,s=h.interceptors[e];s.includes(r)||s.push(r),h.interceptors[e]=s}static off(...t){const[e,r]=t,s=h.interceptors[e];h.interceptors[e]=s.filter(t=>t!==r)}setStorage(t){this.storage=t}isValidQuery(t){return"string"==typeof t||t instanceof URLSearchParams||(Array.isArray(t)?t.every(t=>Array.isArray(t)&&2===t.length&&"string"==typeof t[0]&&o.Serialize.Primitive.isPrimitive(t[1])):"object"==typeof t&&null!==t&&Object.values(t).every(t=>o.Serialize.Primitive.isPrimitive(t)))}getQuery(t){const{method:e,body:r,query:s}=t,n=e===exports.HttpMethod.GET&&this.isValidQuery(r)?r:s;return n&&"object"==typeof n?n instanceof URLSearchParams?n.toString():o.Serialize.queryString.stringify(n,{skipNull:!0,skipEmptyString:!0}):"string"==typeof n?n:""}addHeaders(t){this.defaultHeaders=Object.assign(Object.assign({},this.defaultHeaders),t)}on(...t){const[e,r]=t,s=this.interceptors[e];return s.includes(r)||s.push(r),this.interceptors[e]=s,this}off(...t){const[e,r]=t,s=this.interceptors[e];return this.interceptors[e]=s.filter(t=>t!==r),this}getToken(){const t=i||h.authTokenKey,e=c||h.authHeaderKey,r=p||h.authHeaderType;if(!t||!e)return{key:e,value:""};let s="";this.storage&&(s=this.storage.getItem(t)||"");const o={key:e,value:""};return s&&(o.value=s,r&&(o.value=`${r} ${s}`)),o}getHeaders(t={},e){const r=this.getToken();r.key in t&&t[r.key]||(t[r.key]=r.value),e&&"Content-Type"in t?delete t["Content-Type"]:t["Content-Type"]=t["Content-Type"]||this.defaultHeaders["Content-Type"]||"application/json";const s=Object.assign(Object.assign(Object.assign({},this.defaultHeaders),t),h.extraHeaders||{});return h.extraHeaders=null,s}getURL(t){var e;const{url:r,params:s={}}=t,n=this.getQuery(t);let a=r;if(r)if(r.match(/^https?:\/\//))a=r;else{a=`${null===(e=this.baseURL)||void 0===e?void 0:e.replace(/\/+$/,"")}/${r.replace(/^\/+/,"")}`}if(n){const t=a.includes("?")?"&":"?";a+=`${t}${n}`}return o.Serialize.interpolate(a,s)}getBody(t){const{method:e,body:s}=t;if(e!==exports.HttpMethod.GET&&e!==exports.HttpMethod.HEAD&&e!==exports.HttpMethod.OPTIONS&&e!==exports.HttpMethod.DELETE)return e!==exports.HttpMethod.POST&&e!==exports.HttpMethod.PUT&&e!==exports.HttpMethod.PATCH||!r.isFormData(s)?s instanceof Uint8Array||s instanceof ArrayBuffer?s:void 0!==s?JSON.stringify(s):void 0:s}async request(t){const{method:e=exports.HttpMethod.GET}=t;try{let s=Object.assign({},t);const o=[...h.interceptors.request,...this.interceptors.request];for(const t of o)s=await t(s);const n=this.getURL(s),a=this.getHeaders(s.headers||{});r.isFormData(s.body)&&"Content-Type"in a&&delete a["Content-Type"];const i=await fetch(n,{method:s.method||e,headers:a,body:this.getBody(s),signal:s.signal}),c=i.headers.get("Content-Type")||"";let p=null;p=c.includes("application/json")?await i.json():await i.text();const d=[...h.interceptors.transform,...this.interceptors.transform];let u=p;for(const t of d)u=await t(u);const l=[...h.interceptors.response,...this.interceptors.response];let g=i;for(const t of l)g=await t(g);const y={};g.headers.forEach((t,e)=>{y[e]=t});const T=g.ok;let f=null;if(!T){f=p.error||p;const t=[...h.interceptors.error,...this.interceptors.error];for(const e of t)f=await e(f)}return{data:u,success:T,error:T?null:f,status:g.status,statusText:g.statusText,headers:y}}catch(t){let e=t instanceof Error?t:new Error(String(t));const r=[...h.interceptors.error,...this.interceptors.error];for(const t of r)e=await t(e);return{data:null,status:0,statusText:"Error",headers:{},error:e,success:!1}}}get(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:exports.HttpMethod.GET,url:t,query:e}))}post(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:exports.HttpMethod.POST,url:t,body:e}))}put(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:exports.HttpMethod.PUT,url:t,body:e}))}patch(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:exports.HttpMethod.PATCH,url:t,body:e}))}delete(t,e){return this.request(Object.assign(Object.assign({},e),{method:exports.HttpMethod.DELETE,url:t}))}head(t,e){return this.request(Object.assign(Object.assign({},e),{method:exports.HttpMethod.HEAD,url:t}))}options(t,e){return this.request(Object.assign(Object.assign({},e),{method:exports.HttpMethod.OPTIONS,url:t}))}upload(s,o,n){const a=(null==n?void 0:n.body)?r.objectToFormData(n.body):new FormData;let i=(null==n?void 0:n.name)||"file";if(Array.isArray(o)?(i.endsWith("[]")&&(i=i.slice(0,-2)),o.forEach((t,e)=>{a.append(`${i}[${e}]`,t,t.name)})):t.isFileList(o)?(i.endsWith("[]")&&(i=i.slice(0,-2)),Array.from(o).forEach((t,e)=>{a.append(`${i}[${e}]`,t,t.name)})):a.append(i,o,o.name),null==n?void 0:n.body){const t=e.flatten(n.body);Object.entries(t).forEach(([t,e])=>{null!=e&&a.append(t,String(e))})}return(null==n?void 0:n.onProgress)&&"undefined"!=typeof XMLHttpRequest?new Promise(async t=>{try{let e=Object.assign(Object.assign({},n),{method:exports.HttpMethod.POST,url:s,body:a});const r=[...h.interceptors.request,...this.interceptors.request];for(const t of r)e=await t(e);const o=this.getURL(e),i=this.getHeaders(e.headers||{},!0),c=new XMLHttpRequest;c.upload.addEventListener("progress",t=>{var e;if(t.lengthComputable){const r=Math.round(t.loaded/t.total*100);null===(e=n.onProgress)||void 0===e||e.call(n,{loaded:t.loaded,total:t.total,percentage:r})}}),c.addEventListener("load",async()=>{try{const e=c.getResponseHeader("Content-Type")||"";let r=null;r=e.includes("application/json")?JSON.parse(c.responseText):c.responseText;const s=[...h.interceptors.transform,...this.interceptors.transform];let o=r;for(const t of s)o=await t(o);const n=[...h.interceptors.response,...this.interceptors.response];let a=new Response(c.responseText,{status:c.status,statusText:c.statusText,headers:i});for(const t of n)a=await t(a);const p={};a.headers.forEach((t,e)=>{p[e]=t});const d=a.ok;t({data:o,success:d,error:d?null:o,status:a.status,statusText:a.statusText,headers:p})}catch(e){const r=[...h.interceptors.error,...this.interceptors.error];for(const t of r)t(e);t({data:null,status:0,statusText:"Error",headers:{},error:e,success:!1})}}),c.addEventListener("error",e=>{const r=[...h.interceptors.error,...this.interceptors.error];for(const t of r)t(e);t({data:null,status:0,statusText:"Error",headers:{},error:e,success:!1})}),c.addEventListener("abort",()=>{t({data:null,status:0,statusText:"Aborted",headers:{},error:null,success:!1})}),c.open(e.method||exports.HttpMethod.POST,o,!0),Object.entries(i).forEach(([t,e])=>{"content-type"!==(null==t?void 0:t.toLowerCase())&&e&&c.setRequestHeader(t,e)}),c.send(e.body)}catch(e){const r=[...h.interceptors.error,...this.interceptors.error];for(const t of r)t(e);t({data:null,status:0,statusText:"Error",headers:{},error:e,success:!1})}}):this.request({method:exports.HttpMethod.POST,url:s,body:a,headers:null==n?void 0:n.headers,params:null==n?void 0:n.params,signal:null==n?void 0:n.signal,query:null==n?void 0:n.query})}related(t,e,r){const o=s.sanitizeMime(r.contentType),n=s.sanitizeMime(r.metadataMimeType||"application/json"),a=`----related-boundary-${Date.now()}-${Math.random().toString(36).slice(2)}`,i=JSON.stringify(r.metadata),c=new TextEncoder,p=c.encode(`--${a}\r\nContent-Type: ${n}; charset=UTF-8\r\n\r\n`+i+"\r\n"),d=c.encode(`--${a}\r\nContent-Type: ${o}\r\nContent-Transfer-Encoding: binary\r\n\r\n`),u=c.encode(`\r\n--${a}--`),l=e instanceof Uint8Array?e:new Uint8Array(e),h=new Uint8Array(p.length+d.length+l.length+u.length);return[p,d,l,u].reduce((t,e)=>(h.set(e,t),t+e.length),0),this.request({method:exports.HttpMethod.POST,url:t,body:h,headers:Object.assign(Object.assign({},r.headers||{}),{"Content-Type":`multipart/related; boundary=${a}`}),params:r.params,signal:r.signal,query:r.query})}static createFactory(t={}){const r=t.http||new h(t.baseURL);return function(s,o){const n=e.flatten("function"==typeof t.endpoint?t.endpoint():t.endpoint||{})[s];return{key:s,fn:async(...e)=>{const s="function"==typeof t.storage?await t.storage():t.storage;switch(s&&r.setStorage(s),o){case exports.HttpMethod.POST:return await r.post(n,...e);case exports.HttpMethod.PUT:return await r.put(n,...e);case exports.HttpMethod.PATCH:return await r.patch(n,...e);case exports.HttpMethod.DELETE:return await r.delete(n,...e);case exports.HttpMethod.HEAD:return await r.head(n,...e);case exports.HttpMethod.OPTIONS:return await r.options(n,...e);case exports.HttpUpload.UPLOAD:return await r.upload(n,...e);case exports.HttpUpload.RELATED:return await r.related(n,...e);default:return await r.get(n,...e)}}}}}}h.method=exports.HttpMethod,h.Endpoint=l,h.authTokenKey="access_token",h.authHeaderKey="Authorization",h.authHeaderType="Bearer",h.authDetectToken=["localStorage","sessionStorage","cookie"],h.extraHeaders=null,h.storage=null,h.interceptors={request:[],response:[],transform:[],error:[]},exports.API_AUTH_HEADER_KEY=c,exports.API_AUTH_HEADER_TYPE=p,exports.API_AUTH_TOKEN_KEY=i,exports.DEFAULT_BASE_URL=a,exports.Endpoint=l,exports.Http=h;
|
|
1
|
+
"use strict";var t=require("./utilities/filelist.js"),e=require("./utilities/flatten.js"),r=require("./utilities/formdata.js"),s=require("./utilities/get.js"),o=require("./utilities/sanitize-mime.js"),n=require("./serialize.js"),i=require("./env.js");function a(t){try{return new URL(t).origin}catch(t){return null}}const c=new Set(["__proto__","constructor","prototype"]),d=new Set(["credentials","cache","mode","redirect","referrer","referrerPolicy","integrity","keepalive","priority","duplex","window","next","cf","dispatcher"]);function l(t){const e=Object.create(null);for(const r of Object.keys(t))c.has(r)||(e[r]=t[r]);return e}const p=i.getEnv("API_URL","/"),u=i.getEnv("API_AUTH_TOKEN_KEY"),h=i.getEnv("API_AUTH_HEADER_KEY"),g=i.getEnv("API_AUTH_HEADER_TYPE");var f,y;exports.HttpMethod=void 0,(f=exports.HttpMethod||(exports.HttpMethod={})).GET="GET",f.POST="POST",f.PUT="PUT",f.DELETE="DELETE",f.PATCH="PATCH",f.HEAD="HEAD",f.OPTIONS="OPTIONS",exports.HttpUpload=void 0,(y=exports.HttpUpload||(exports.HttpUpload={})).UPLOAD="UPLOAD",y.RELATED="RELATED";class T{static register(t,e){return this.registered[t]=Object.assign({},e),this}static all(){return Object.assign({},this.registered)}}T.registered={};class E{constructor(t){var e,r,s,o;this.defaultHeaders={"Content-Type":"application/json"},this.method=E.method,this.storage=E.storage,this.interceptors={request:[],response:[],transform:[],error:[]};const n="string"==typeof t||void 0===t?{baseURL:null!=t?t:p}:{baseURL:null!==(e=t.baseURL)&&void 0!==e?e:p,allowedOrigins:t.allowedOrigins};this.baseURL=null!==(s=null!==(r=n.baseURL)&&void 0!==r?r:p)&&void 0!==s?s:"/";const i=new Set;for(const t of null!==(o=n.allowedOrigins)&&void 0!==o?o:[]){const e=a(t);if(!e)throw new Error(`Http: invalid allowedOrigins entry: ${t}`);i.add(e)}const c=a(this.baseURL);c&&i.add(c),this.allowedOrigins=i}isAllowedOrigin(t){return 0===this.allowedOrigins.size||this.allowedOrigins.has(t)}assertSameOriginResponse(t,e,r){const s=Object.keys(r).some(t=>"authorization"===t.toLowerCase()&&!!r[t]),o=Object.keys(r).some(t=>"cookie"===t.toLowerCase()&&!!r[t]);if(!s&&!o)return;const n=a(t);if(!n)return;const i=e.url?a(e.url):null;if(i&&i!==n&&!this.isAllowedOrigin(i))throw new Error(`Http: credentialed request was redirected from ${n} to untrusted origin ${i}`)}static on(...t){const[e,r]=t,s=E.interceptors[e];s.includes(r)||s.push(r),E.interceptors[e]=s}static off(...t){const[e,r]=t,s=E.interceptors[e];E.interceptors[e]=s.filter(t=>t!==r)}setStorage(t){this.storage=t}isValidQuery(t){return"string"==typeof t||t instanceof URLSearchParams||(Array.isArray(t)?t.every(t=>Array.isArray(t)&&2===t.length&&"string"==typeof t[0]&&n.Serialize.Primitive.isPrimitive(t[1])):"object"==typeof t&&null!==t&&Object.values(t).every(t=>n.Serialize.Primitive.isPrimitive(t)))}getQuery(t){const{method:e,body:r,query:s}=t,o=e===exports.HttpMethod.GET&&this.isValidQuery(r)?r:s;if(!o||"object"!=typeof o)return"string"==typeof o?o:"";if(o instanceof URLSearchParams)return o.toString();const i=l(o);return n.Serialize.queryString.stringify(i,{skipNull:!0,skipEmptyString:!0})}addHeaders(t){this.defaultHeaders=Object.assign(Object.assign({},this.defaultHeaders),t)}on(...t){const[e,r]=t,s=this.interceptors[e];return s.includes(r)||s.push(r),this.interceptors[e]=s,this}off(...t){const[e,r]=t,s=this.interceptors[e];return this.interceptors[e]=s.filter(t=>t!==r),this}getToken(){const t=u||E.authTokenKey,e=h||E.authHeaderKey,r=g||E.authHeaderType;if(!t||!e)return{key:e,value:""};let s="";this.storage&&(s=this.storage.getItem(t)||"");const o={key:e,value:""};return s&&/^[\x21-\x7E]+$/.test(s)&&(o.value=r?`${r} ${s}`:s),o}getHeaders(t={},e){const r=this.getToken();r.key in t&&t[r.key]||(t[r.key]=r.value),e&&"Content-Type"in t?delete t["Content-Type"]:t["Content-Type"]=t["Content-Type"]||this.defaultHeaders["Content-Type"]||"application/json";const s=Object.assign(Object.assign(Object.assign({},this.defaultHeaders),t),E.extraHeaders||{});return E.extraHeaders=null,s}getURL(t){const{url:e="",params:r={}}=t,o=this.getQuery(t);let i;if(e){if(e.startsWith("//"))throw new Error(`Http: protocol-relative URLs are not allowed: ${e}`);if(/^[a-z][a-z0-9+.-]*:/i.test(e)){let t;try{t=new URL(e)}catch(t){throw new Error(`Http: invalid absolute URL: ${e}`)}if("http:"!==t.protocol&&"https:"!==t.protocol)throw new Error(`Http: unsupported URL scheme: ${t.protocol}`);if(!this.isAllowedOrigin(t.origin))throw new Error(`Http: URL origin '${t.origin}' is not in allowedOrigins`);i=t.toString()}else{const t=(this.baseURL||"/").replace(/\/+$/,""),r=e.replace(/^\/+/,"");i=t?`${t}/${r}`:`/${r}`}}else i=this.baseURL||"/";if(o){const t=i.includes("?")?"&":"?";i+=`${t}${o}`}if(!i.includes("{")||!i.includes("}"))return i;const a=Array.isArray(r)?r:l(r);return i.replace(/\{([a-zA-Z0-9_.-]+)\}/g,(t,e)=>{const r=s.get(a,e);return null==r||"object"==typeof r?"":n.Serialize.URL.encode(String(r))})}getBody(t){const{method:e,body:s}=t;if(e!==exports.HttpMethod.GET&&e!==exports.HttpMethod.HEAD&&e!==exports.HttpMethod.OPTIONS&&e!==exports.HttpMethod.DELETE)return e!==exports.HttpMethod.POST&&e!==exports.HttpMethod.PUT&&e!==exports.HttpMethod.PATCH||!r.isFormData(s)?s instanceof Uint8Array||s instanceof ArrayBuffer?s:void 0!==s?JSON.stringify(s):void 0:s}async request(t){const{method:e=exports.HttpMethod.GET}=t;try{let s=Object.assign({},t);const o=[...E.interceptors.request,...this.interceptors.request];for(const t of o)s=await t(s);const n=this.getURL(s),i=this.getHeaders(s.headers||{});r.isFormData(s.body)&&"Content-Type"in i&&delete i["Content-Type"];const a={};if(s.configs)for(const t of Object.keys(s.configs))d.has(t)&&(a[t]=s.configs[t]);const c=await fetch(n,Object.assign(Object.assign({},a),{method:s.method||e,headers:i,body:this.getBody(s),signal:s.signal}));this.assertSameOriginResponse(n,c,i);const l=c.headers.get("Content-Type")||"";let p=null;p=l.includes("application/json")?await c.json():await c.text();const u=[...E.interceptors.transform,...this.interceptors.transform];let h=p;for(const t of u)h=await t(h);const g=[...E.interceptors.response,...this.interceptors.response];let f=c;for(const t of g)f=await t(f);const y={};f.headers.forEach((t,e)=>{y[e]=t});const T=f.ok;let H=null;if(!T){H=p.error||p;const t=[...E.interceptors.error,...this.interceptors.error];for(const e of t)H=await e(H)}return{data:h,success:T,error:T?null:H,status:f.status,statusText:f.statusText,headers:y}}catch(t){let e=t instanceof Error?t:new Error(String(t));const r=[...E.interceptors.error,...this.interceptors.error];for(const t of r)e=await t(e);return{data:null,status:0,statusText:"Error",headers:{},error:e,success:!1}}}get(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:exports.HttpMethod.GET,url:t,query:e}))}post(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:exports.HttpMethod.POST,url:t,body:e}))}put(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:exports.HttpMethod.PUT,url:t,body:e}))}patch(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:exports.HttpMethod.PATCH,url:t,body:e}))}delete(t,e){return this.request(Object.assign(Object.assign({},e),{method:exports.HttpMethod.DELETE,url:t}))}head(t,e){return this.request(Object.assign(Object.assign({},e),{method:exports.HttpMethod.HEAD,url:t}))}options(t,e){return this.request(Object.assign(Object.assign({},e),{method:exports.HttpMethod.OPTIONS,url:t}))}upload(s,o,n){const i=(null==n?void 0:n.body)?r.objectToFormData(n.body):new FormData;let a=(null==n?void 0:n.name)||"file";if(Array.isArray(o)?(a.endsWith("[]")&&(a=a.slice(0,-2)),o.forEach((t,e)=>{i.append(`${a}[${e}]`,t,t.name)})):t.isFileList(o)?(a.endsWith("[]")&&(a=a.slice(0,-2)),Array.from(o).forEach((t,e)=>{i.append(`${a}[${e}]`,t,t.name)})):i.append(a,o,o.name),null==n?void 0:n.body){const t=e.flatten(n.body);Object.entries(t).forEach(([t,e])=>{null!=e&&i.append(t,String(e))})}return(null==n?void 0:n.onProgress)&&"undefined"!=typeof XMLHttpRequest?new Promise(async t=>{try{let e=Object.assign(Object.assign({},n),{method:exports.HttpMethod.POST,url:s,body:i});const r=[...E.interceptors.request,...this.interceptors.request];for(const t of r)e=await t(e);const o=this.getURL(e),a=this.getHeaders(e.headers||{},!0),c=new XMLHttpRequest;c.upload.addEventListener("progress",t=>{var e;if(t.lengthComputable){const r=Math.round(t.loaded/t.total*100);null===(e=n.onProgress)||void 0===e||e.call(n,{loaded:t.loaded,total:t.total,percentage:r})}}),c.addEventListener("load",async()=>{try{const e=c.getResponseHeader("Content-Type")||"";let r=null;r=e.includes("application/json")?JSON.parse(c.responseText):c.responseText;const s=[...E.interceptors.transform,...this.interceptors.transform];let o=r;for(const t of s)o=await t(o);const n=[...E.interceptors.response,...this.interceptors.response];let i=new Response(c.responseText,{status:c.status,statusText:c.statusText,headers:a});for(const t of n)i=await t(i);const d={};i.headers.forEach((t,e)=>{d[e]=t});const l=i.ok;t({data:o,success:l,error:l?null:o,status:i.status,statusText:i.statusText,headers:d})}catch(e){const r=[...E.interceptors.error,...this.interceptors.error];for(const t of r)t(e);t({data:null,status:0,statusText:"Error",headers:{},error:e,success:!1})}}),c.addEventListener("error",e=>{const r=[...E.interceptors.error,...this.interceptors.error];for(const t of r)t(e);t({data:null,status:0,statusText:"Error",headers:{},error:e,success:!1})}),c.addEventListener("abort",()=>{t({data:null,status:0,statusText:"Aborted",headers:{},error:null,success:!1})}),c.open(e.method||exports.HttpMethod.POST,o,!0),Object.entries(a).forEach(([t,e])=>{"content-type"!==(null==t?void 0:t.toLowerCase())&&e&&c.setRequestHeader(t,e)}),c.send(e.body)}catch(e){const r=[...E.interceptors.error,...this.interceptors.error];for(const t of r)t(e);t({data:null,status:0,statusText:"Error",headers:{},error:e,success:!1})}}):this.request({method:exports.HttpMethod.POST,url:s,body:i,headers:null==n?void 0:n.headers,params:null==n?void 0:n.params,signal:null==n?void 0:n.signal,query:null==n?void 0:n.query})}related(t,e,r){const s=o.sanitizeMime(r.contentType),n=o.sanitizeMime(r.metadataMimeType||"application/json"),i=`----related-boundary-${Date.now()}-${Math.random().toString(36).slice(2)}`,a=JSON.stringify(r.metadata),c=new TextEncoder,d=c.encode(`--${i}\r\nContent-Type: ${n}; charset=UTF-8\r\n\r\n`+a+"\r\n"),l=c.encode(`--${i}\r\nContent-Type: ${s}\r\nContent-Transfer-Encoding: binary\r\n\r\n`),p=c.encode(`\r\n--${i}--`),u=e instanceof Uint8Array?e:new Uint8Array(e),h=new Uint8Array(d.length+l.length+u.length+p.length);return[d,l,u,p].reduce((t,e)=>(h.set(e,t),t+e.length),0),this.request({method:exports.HttpMethod.POST,url:t,body:h,headers:Object.assign(Object.assign({},r.headers||{}),{"Content-Type":`multipart/related; boundary=${i}`}),params:r.params,signal:r.signal,query:r.query})}static createFactory(t={}){const r=t.http||new E(t.baseURL);return function(s,o){const n=e.flatten("function"==typeof t.endpoint?t.endpoint():t.endpoint||{})[s];return{key:s,fn:async(...e)=>{const s="function"==typeof t.storage?await t.storage():t.storage;switch(s&&r.setStorage(s),o){case exports.HttpMethod.POST:return await r.post(n,...e);case exports.HttpMethod.PUT:return await r.put(n,...e);case exports.HttpMethod.PATCH:return await r.patch(n,...e);case exports.HttpMethod.DELETE:return await r.delete(n,...e);case exports.HttpMethod.HEAD:return await r.head(n,...e);case exports.HttpMethod.OPTIONS:return await r.options(n,...e);case exports.HttpUpload.UPLOAD:return await r.upload(n,...e);case exports.HttpUpload.RELATED:return await r.related(n,...e);default:return await r.get(n,...e)}}}}}}E.method=exports.HttpMethod,E.Endpoint=T,E.authTokenKey="access_token",E.authHeaderKey="Authorization",E.authHeaderType="Bearer",E.authDetectToken=["localStorage","sessionStorage","cookie"],E.extraHeaders=null,E.storage=null,E.interceptors={request:[],response:[],transform:[],error:[]},exports.API_AUTH_HEADER_KEY=h,exports.API_AUTH_HEADER_TYPE=g,exports.API_AUTH_TOKEN_KEY=u,exports.DEFAULT_BASE_URL=p,exports.Endpoint=T,exports.Http=E;
|
package/dist/http.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isFileList as e}from"./utilities/filelist.mjs";import{flatten as t}from"./utilities/flatten.mjs";import{isFormData as r,objectToFormData as s}from"./utilities/formdata.mjs";import{sanitizeMime as n}from"./utilities/sanitize-mime.mjs";import{Serialize as o}from"./serialize.mjs";import{getEnv as a}from"./env.mjs";const i=a("API_URL","/"),c=a("API_AUTH_TOKEN_KEY"),u=a("API_AUTH_HEADER_KEY"),d=a("API_AUTH_HEADER_TYPE");var l,p;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE",e.PATCH="PATCH",e.HEAD="HEAD",e.OPTIONS="OPTIONS"}(l||(l={})),function(e){e.UPLOAD="UPLOAD",e.RELATED="RELATED"}(p||(p={}));class h{static register(e,t){return this.registered[e]=Object.assign({},t),this}static all(){return Object.assign({},this.registered)}}h.registered={};class f{constructor(e=i){this.baseURL=e,this.defaultHeaders={"Content-Type":"application/json"},this.method=f.method,this.storage=f.storage,this.interceptors={request:[],response:[],transform:[],error:[]}}static on(...e){const[t,r]=e,s=f.interceptors[t];s.includes(r)||s.push(r),f.interceptors[t]=s}static off(...e){const[t,r]=e,s=f.interceptors[t];f.interceptors[t]=s.filter(e=>e!==r)}setStorage(e){this.storage=e}isValidQuery(e){return"string"==typeof e||e instanceof URLSearchParams||(Array.isArray(e)?e.every(e=>Array.isArray(e)&&2===e.length&&"string"==typeof e[0]&&o.Primitive.isPrimitive(e[1])):"object"==typeof e&&null!==e&&Object.values(e).every(e=>o.Primitive.isPrimitive(e)))}getQuery(e){const{method:t,body:r,query:s}=e,n=t===l.GET&&this.isValidQuery(r)?r:s;return n&&"object"==typeof n?n instanceof URLSearchParams?n.toString():o.queryString.stringify(n,{skipNull:!0,skipEmptyString:!0}):"string"==typeof n?n:""}addHeaders(e){this.defaultHeaders=Object.assign(Object.assign({},this.defaultHeaders),e)}on(...e){const[t,r]=e,s=this.interceptors[t];return s.includes(r)||s.push(r),this.interceptors[t]=s,this}off(...e){const[t,r]=e,s=this.interceptors[t];return this.interceptors[t]=s.filter(e=>e!==r),this}getToken(){const e=c||f.authTokenKey,t=u||f.authHeaderKey,r=d||f.authHeaderType;if(!e||!t)return{key:t,value:""};let s="";this.storage&&(s=this.storage.getItem(e)||"");const n={key:t,value:""};return s&&(n.value=s,r&&(n.value=`${r} ${s}`)),n}getHeaders(e={},t){const r=this.getToken();r.key in e&&e[r.key]||(e[r.key]=r.value),t&&"Content-Type"in e?delete e["Content-Type"]:e["Content-Type"]=e["Content-Type"]||this.defaultHeaders["Content-Type"]||"application/json";const s=Object.assign(Object.assign(Object.assign({},this.defaultHeaders),e),f.extraHeaders||{});return f.extraHeaders=null,s}getURL(e){var t;const{url:r,params:s={}}=e,n=this.getQuery(e);let a=r;if(r)if(r.match(/^https?:\/\//))a=r;else{a=`${null===(t=this.baseURL)||void 0===t?void 0:t.replace(/\/+$/,"")}/${r.replace(/^\/+/,"")}`}if(n){const e=a.includes("?")?"&":"?";a+=`${e}${n}`}return o.interpolate(a,s)}getBody(e){const{method:t,body:s}=e;if(t!==l.GET&&t!==l.HEAD&&t!==l.OPTIONS&&t!==l.DELETE)return t!==l.POST&&t!==l.PUT&&t!==l.PATCH||!r(s)?s instanceof Uint8Array||s instanceof ArrayBuffer?s:void 0!==s?JSON.stringify(s):void 0:s}async request(e){const{method:t=l.GET}=e;try{let s=Object.assign({},e);const n=[...f.interceptors.request,...this.interceptors.request];for(const e of n)s=await e(s);const o=this.getURL(s),a=this.getHeaders(s.headers||{});r(s.body)&&"Content-Type"in a&&delete a["Content-Type"];const i=await fetch(o,{method:s.method||t,headers:a,body:this.getBody(s),signal:s.signal}),c=i.headers.get("Content-Type")||"";let u=null;u=c.includes("application/json")?await i.json():await i.text();const d=[...f.interceptors.transform,...this.interceptors.transform];let l=u;for(const e of d)l=await e(l);const p=[...f.interceptors.response,...this.interceptors.response];let h=i;for(const e of p)h=await e(h);const y={};h.headers.forEach((e,t)=>{y[t]=e});const g=h.ok;let T=null;if(!g){T=u.error||u;const e=[...f.interceptors.error,...this.interceptors.error];for(const t of e)T=await t(T)}return{data:l,success:g,error:g?null:T,status:h.status,statusText:h.statusText,headers:y}}catch(e){let t=e instanceof Error?e:new Error(String(e));const r=[...f.interceptors.error,...this.interceptors.error];for(const e of r)t=await e(t);return{data:null,status:0,statusText:"Error",headers:{},error:t,success:!1}}}get(e,t,r){return this.request(Object.assign(Object.assign({},r),{method:l.GET,url:e,query:t}))}post(e,t,r){return this.request(Object.assign(Object.assign({},r),{method:l.POST,url:e,body:t}))}put(e,t,r){return this.request(Object.assign(Object.assign({},r),{method:l.PUT,url:e,body:t}))}patch(e,t,r){return this.request(Object.assign(Object.assign({},r),{method:l.PATCH,url:e,body:t}))}delete(e,t){return this.request(Object.assign(Object.assign({},t),{method:l.DELETE,url:e}))}head(e,t){return this.request(Object.assign(Object.assign({},t),{method:l.HEAD,url:e}))}options(e,t){return this.request(Object.assign(Object.assign({},t),{method:l.OPTIONS,url:e}))}upload(r,n,o){const a=(null==o?void 0:o.body)?s(o.body):new FormData;let i=(null==o?void 0:o.name)||"file";if(Array.isArray(n)?(i.endsWith("[]")&&(i=i.slice(0,-2)),n.forEach((e,t)=>{a.append(`${i}[${t}]`,e,e.name)})):e(n)?(i.endsWith("[]")&&(i=i.slice(0,-2)),Array.from(n).forEach((e,t)=>{a.append(`${i}[${t}]`,e,e.name)})):a.append(i,n,n.name),null==o?void 0:o.body){const e=t(o.body);Object.entries(e).forEach(([e,t])=>{null!=t&&a.append(e,String(t))})}return(null==o?void 0:o.onProgress)&&"undefined"!=typeof XMLHttpRequest?new Promise(async e=>{try{let t=Object.assign(Object.assign({},o),{method:l.POST,url:r,body:a});const s=[...f.interceptors.request,...this.interceptors.request];for(const e of s)t=await e(t);const n=this.getURL(t),i=this.getHeaders(t.headers||{},!0),c=new XMLHttpRequest;c.upload.addEventListener("progress",e=>{var t;if(e.lengthComputable){const r=Math.round(e.loaded/e.total*100);null===(t=o.onProgress)||void 0===t||t.call(o,{loaded:e.loaded,total:e.total,percentage:r})}}),c.addEventListener("load",async()=>{try{const t=c.getResponseHeader("Content-Type")||"";let r=null;r=t.includes("application/json")?JSON.parse(c.responseText):c.responseText;const s=[...f.interceptors.transform,...this.interceptors.transform];let n=r;for(const e of s)n=await e(n);const o=[...f.interceptors.response,...this.interceptors.response];let a=new Response(c.responseText,{status:c.status,statusText:c.statusText,headers:i});for(const e of o)a=await e(a);const u={};a.headers.forEach((e,t)=>{u[t]=e});const d=a.ok;e({data:n,success:d,error:d?null:n,status:a.status,statusText:a.statusText,headers:u})}catch(t){const r=[...f.interceptors.error,...this.interceptors.error];for(const e of r)e(t);e({data:null,status:0,statusText:"Error",headers:{},error:t,success:!1})}}),c.addEventListener("error",t=>{const r=[...f.interceptors.error,...this.interceptors.error];for(const e of r)e(t);e({data:null,status:0,statusText:"Error",headers:{},error:t,success:!1})}),c.addEventListener("abort",()=>{e({data:null,status:0,statusText:"Aborted",headers:{},error:null,success:!1})}),c.open(t.method||l.POST,n,!0),Object.entries(i).forEach(([e,t])=>{"content-type"!==(null==e?void 0:e.toLowerCase())&&t&&c.setRequestHeader(e,t)}),c.send(t.body)}catch(t){const r=[...f.interceptors.error,...this.interceptors.error];for(const e of r)e(t);e({data:null,status:0,statusText:"Error",headers:{},error:t,success:!1})}}):this.request({method:l.POST,url:r,body:a,headers:null==o?void 0:o.headers,params:null==o?void 0:o.params,signal:null==o?void 0:o.signal,query:null==o?void 0:o.query})}related(e,t,r){const s=n(r.contentType),o=n(r.metadataMimeType||"application/json"),a=`----related-boundary-${Date.now()}-${Math.random().toString(36).slice(2)}`,i=JSON.stringify(r.metadata),c=new TextEncoder,u=c.encode(`--${a}\r\nContent-Type: ${o}; charset=UTF-8\r\n\r\n`+i+"\r\n"),d=c.encode(`--${a}\r\nContent-Type: ${s}\r\nContent-Transfer-Encoding: binary\r\n\r\n`),p=c.encode(`\r\n--${a}--`),h=t instanceof Uint8Array?t:new Uint8Array(t),f=new Uint8Array(u.length+d.length+h.length+p.length);return[u,d,h,p].reduce((e,t)=>(f.set(t,e),e+t.length),0),this.request({method:l.POST,url:e,body:f,headers:Object.assign(Object.assign({},r.headers||{}),{"Content-Type":`multipart/related; boundary=${a}`}),params:r.params,signal:r.signal,query:r.query})}static createFactory(e={}){const r=e.http||new f(e.baseURL);return function(s,n){const o=t("function"==typeof e.endpoint?e.endpoint():e.endpoint||{})[s];return{key:s,fn:async(...t)=>{const s="function"==typeof e.storage?await e.storage():e.storage;switch(s&&r.setStorage(s),n){case l.POST:return await r.post(o,...t);case l.PUT:return await r.put(o,...t);case l.PATCH:return await r.patch(o,...t);case l.DELETE:return await r.delete(o,...t);case l.HEAD:return await r.head(o,...t);case l.OPTIONS:return await r.options(o,...t);case p.UPLOAD:return await r.upload(o,...t);case p.RELATED:return await r.related(o,...t);default:return await r.get(o,...t)}}}}}}f.method=l,f.Endpoint=h,f.authTokenKey="access_token",f.authHeaderKey="Authorization",f.authHeaderType="Bearer",f.authDetectToken=["localStorage","sessionStorage","cookie"],f.extraHeaders=null,f.storage=null,f.interceptors={request:[],response:[],transform:[],error:[]};export{u as API_AUTH_HEADER_KEY,d as API_AUTH_HEADER_TYPE,c as API_AUTH_TOKEN_KEY,i as DEFAULT_BASE_URL,h as Endpoint,f as Http,l as HttpMethod,p as HttpUpload};
|
|
1
|
+
import{isFileList as e}from"./utilities/filelist.mjs";import{flatten as t}from"./utilities/flatten.mjs";import{isFormData as r,objectToFormData as s}from"./utilities/formdata.mjs";import{get as n}from"./utilities/get.mjs";import{sanitizeMime as o}from"./utilities/sanitize-mime.mjs";import{Serialize as i}from"./serialize.mjs";import{getEnv as a}from"./env.mjs";function c(e){try{return new URL(e).origin}catch(e){return null}}const l=new Set(["__proto__","constructor","prototype"]),u=new Set(["credentials","cache","mode","redirect","referrer","referrerPolicy","integrity","keepalive","priority","duplex","window","next","cf","dispatcher"]);function d(e){const t=Object.create(null);for(const r of Object.keys(e))l.has(r)||(t[r]=e[r]);return t}const p=a("API_URL","/"),h=a("API_AUTH_TOKEN_KEY"),f=a("API_AUTH_HEADER_KEY"),g=a("API_AUTH_HEADER_TYPE");var y,m;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE",e.PATCH="PATCH",e.HEAD="HEAD",e.OPTIONS="OPTIONS"}(y||(y={})),function(e){e.UPLOAD="UPLOAD",e.RELATED="RELATED"}(m||(m={}));class T{static register(e,t){return this.registered[e]=Object.assign({},t),this}static all(){return Object.assign({},this.registered)}}T.registered={};class O{constructor(e){var t,r,s,n;this.defaultHeaders={"Content-Type":"application/json"},this.method=O.method,this.storage=O.storage,this.interceptors={request:[],response:[],transform:[],error:[]};const o="string"==typeof e||void 0===e?{baseURL:null!=e?e:p}:{baseURL:null!==(t=e.baseURL)&&void 0!==t?t:p,allowedOrigins:e.allowedOrigins};this.baseURL=null!==(s=null!==(r=o.baseURL)&&void 0!==r?r:p)&&void 0!==s?s:"/";const i=new Set;for(const e of null!==(n=o.allowedOrigins)&&void 0!==n?n:[]){const t=c(e);if(!t)throw new Error(`Http: invalid allowedOrigins entry: ${e}`);i.add(t)}const a=c(this.baseURL);a&&i.add(a),this.allowedOrigins=i}isAllowedOrigin(e){return 0===this.allowedOrigins.size||this.allowedOrigins.has(e)}assertSameOriginResponse(e,t,r){const s=Object.keys(r).some(e=>"authorization"===e.toLowerCase()&&!!r[e]),n=Object.keys(r).some(e=>"cookie"===e.toLowerCase()&&!!r[e]);if(!s&&!n)return;const o=c(e);if(!o)return;const i=t.url?c(t.url):null;if(i&&i!==o&&!this.isAllowedOrigin(i))throw new Error(`Http: credentialed request was redirected from ${o} to untrusted origin ${i}`)}static on(...e){const[t,r]=e,s=O.interceptors[t];s.includes(r)||s.push(r),O.interceptors[t]=s}static off(...e){const[t,r]=e,s=O.interceptors[t];O.interceptors[t]=s.filter(e=>e!==r)}setStorage(e){this.storage=e}isValidQuery(e){return"string"==typeof e||e instanceof URLSearchParams||(Array.isArray(e)?e.every(e=>Array.isArray(e)&&2===e.length&&"string"==typeof e[0]&&i.Primitive.isPrimitive(e[1])):"object"==typeof e&&null!==e&&Object.values(e).every(e=>i.Primitive.isPrimitive(e)))}getQuery(e){const{method:t,body:r,query:s}=e,n=t===y.GET&&this.isValidQuery(r)?r:s;if(!n||"object"!=typeof n)return"string"==typeof n?n:"";if(n instanceof URLSearchParams)return n.toString();const o=d(n);return i.queryString.stringify(o,{skipNull:!0,skipEmptyString:!0})}addHeaders(e){this.defaultHeaders=Object.assign(Object.assign({},this.defaultHeaders),e)}on(...e){const[t,r]=e,s=this.interceptors[t];return s.includes(r)||s.push(r),this.interceptors[t]=s,this}off(...e){const[t,r]=e,s=this.interceptors[t];return this.interceptors[t]=s.filter(e=>e!==r),this}getToken(){const e=h||O.authTokenKey,t=f||O.authHeaderKey,r=g||O.authHeaderType;if(!e||!t)return{key:t,value:""};let s="";this.storage&&(s=this.storage.getItem(e)||"");const n={key:t,value:""};return s&&/^[\x21-\x7E]+$/.test(s)&&(n.value=r?`${r} ${s}`:s),n}getHeaders(e={},t){const r=this.getToken();r.key in e&&e[r.key]||(e[r.key]=r.value),t&&"Content-Type"in e?delete e["Content-Type"]:e["Content-Type"]=e["Content-Type"]||this.defaultHeaders["Content-Type"]||"application/json";const s=Object.assign(Object.assign(Object.assign({},this.defaultHeaders),e),O.extraHeaders||{});return O.extraHeaders=null,s}getURL(e){const{url:t="",params:r={}}=e,s=this.getQuery(e);let o;if(t){if(t.startsWith("//"))throw new Error(`Http: protocol-relative URLs are not allowed: ${t}`);if(/^[a-z][a-z0-9+.-]*:/i.test(t)){let e;try{e=new URL(t)}catch(e){throw new Error(`Http: invalid absolute URL: ${t}`)}if("http:"!==e.protocol&&"https:"!==e.protocol)throw new Error(`Http: unsupported URL scheme: ${e.protocol}`);if(!this.isAllowedOrigin(e.origin))throw new Error(`Http: URL origin '${e.origin}' is not in allowedOrigins`);o=e.toString()}else{const e=(this.baseURL||"/").replace(/\/+$/,""),r=t.replace(/^\/+/,"");o=e?`${e}/${r}`:`/${r}`}}else o=this.baseURL||"/";if(s){const e=o.includes("?")?"&":"?";o+=`${e}${s}`}if(!o.includes("{")||!o.includes("}"))return o;const a=Array.isArray(r)?r:d(r);return o.replace(/\{([a-zA-Z0-9_.-]+)\}/g,(e,t)=>{const r=n(a,t);return null==r||"object"==typeof r?"":i.URL.encode(String(r))})}getBody(e){const{method:t,body:s}=e;if(t!==y.GET&&t!==y.HEAD&&t!==y.OPTIONS&&t!==y.DELETE)return t!==y.POST&&t!==y.PUT&&t!==y.PATCH||!r(s)?s instanceof Uint8Array||s instanceof ArrayBuffer?s:void 0!==s?JSON.stringify(s):void 0:s}async request(e){const{method:t=y.GET}=e;try{let s=Object.assign({},e);const n=[...O.interceptors.request,...this.interceptors.request];for(const e of n)s=await e(s);const o=this.getURL(s),i=this.getHeaders(s.headers||{});r(s.body)&&"Content-Type"in i&&delete i["Content-Type"];const a={};if(s.configs)for(const e of Object.keys(s.configs))u.has(e)&&(a[e]=s.configs[e]);const c=await fetch(o,Object.assign(Object.assign({},a),{method:s.method||t,headers:i,body:this.getBody(s),signal:s.signal}));this.assertSameOriginResponse(o,c,i);const l=c.headers.get("Content-Type")||"";let d=null;d=l.includes("application/json")?await c.json():await c.text();const p=[...O.interceptors.transform,...this.interceptors.transform];let h=d;for(const e of p)h=await e(h);const f=[...O.interceptors.response,...this.interceptors.response];let g=c;for(const e of f)g=await e(g);const y={};g.headers.forEach((e,t)=>{y[t]=e});const m=g.ok;let T=null;if(!m){T=d.error||d;const e=[...O.interceptors.error,...this.interceptors.error];for(const t of e)T=await t(T)}return{data:h,success:m,error:m?null:T,status:g.status,statusText:g.statusText,headers:y}}catch(e){let t=e instanceof Error?e:new Error(String(e));const r=[...O.interceptors.error,...this.interceptors.error];for(const e of r)t=await e(t);return{data:null,status:0,statusText:"Error",headers:{},error:t,success:!1}}}get(e,t,r){return this.request(Object.assign(Object.assign({},r),{method:y.GET,url:e,query:t}))}post(e,t,r){return this.request(Object.assign(Object.assign({},r),{method:y.POST,url:e,body:t}))}put(e,t,r){return this.request(Object.assign(Object.assign({},r),{method:y.PUT,url:e,body:t}))}patch(e,t,r){return this.request(Object.assign(Object.assign({},r),{method:y.PATCH,url:e,body:t}))}delete(e,t){return this.request(Object.assign(Object.assign({},t),{method:y.DELETE,url:e}))}head(e,t){return this.request(Object.assign(Object.assign({},t),{method:y.HEAD,url:e}))}options(e,t){return this.request(Object.assign(Object.assign({},t),{method:y.OPTIONS,url:e}))}upload(r,n,o){const i=(null==o?void 0:o.body)?s(o.body):new FormData;let a=(null==o?void 0:o.name)||"file";if(Array.isArray(n)?(a.endsWith("[]")&&(a=a.slice(0,-2)),n.forEach((e,t)=>{i.append(`${a}[${t}]`,e,e.name)})):e(n)?(a.endsWith("[]")&&(a=a.slice(0,-2)),Array.from(n).forEach((e,t)=>{i.append(`${a}[${t}]`,e,e.name)})):i.append(a,n,n.name),null==o?void 0:o.body){const e=t(o.body);Object.entries(e).forEach(([e,t])=>{null!=t&&i.append(e,String(t))})}return(null==o?void 0:o.onProgress)&&"undefined"!=typeof XMLHttpRequest?new Promise(async e=>{try{let t=Object.assign(Object.assign({},o),{method:y.POST,url:r,body:i});const s=[...O.interceptors.request,...this.interceptors.request];for(const e of s)t=await e(t);const n=this.getURL(t),a=this.getHeaders(t.headers||{},!0),c=new XMLHttpRequest;c.upload.addEventListener("progress",e=>{var t;if(e.lengthComputable){const r=Math.round(e.loaded/e.total*100);null===(t=o.onProgress)||void 0===t||t.call(o,{loaded:e.loaded,total:e.total,percentage:r})}}),c.addEventListener("load",async()=>{try{const t=c.getResponseHeader("Content-Type")||"";let r=null;r=t.includes("application/json")?JSON.parse(c.responseText):c.responseText;const s=[...O.interceptors.transform,...this.interceptors.transform];let n=r;for(const e of s)n=await e(n);const o=[...O.interceptors.response,...this.interceptors.response];let i=new Response(c.responseText,{status:c.status,statusText:c.statusText,headers:a});for(const e of o)i=await e(i);const l={};i.headers.forEach((e,t)=>{l[t]=e});const u=i.ok;e({data:n,success:u,error:u?null:n,status:i.status,statusText:i.statusText,headers:l})}catch(t){const r=[...O.interceptors.error,...this.interceptors.error];for(const e of r)e(t);e({data:null,status:0,statusText:"Error",headers:{},error:t,success:!1})}}),c.addEventListener("error",t=>{const r=[...O.interceptors.error,...this.interceptors.error];for(const e of r)e(t);e({data:null,status:0,statusText:"Error",headers:{},error:t,success:!1})}),c.addEventListener("abort",()=>{e({data:null,status:0,statusText:"Aborted",headers:{},error:null,success:!1})}),c.open(t.method||y.POST,n,!0),Object.entries(a).forEach(([e,t])=>{"content-type"!==(null==e?void 0:e.toLowerCase())&&t&&c.setRequestHeader(e,t)}),c.send(t.body)}catch(t){const r=[...O.interceptors.error,...this.interceptors.error];for(const e of r)e(t);e({data:null,status:0,statusText:"Error",headers:{},error:t,success:!1})}}):this.request({method:y.POST,url:r,body:i,headers:null==o?void 0:o.headers,params:null==o?void 0:o.params,signal:null==o?void 0:o.signal,query:null==o?void 0:o.query})}related(e,t,r){const s=o(r.contentType),n=o(r.metadataMimeType||"application/json"),i=`----related-boundary-${Date.now()}-${Math.random().toString(36).slice(2)}`,a=JSON.stringify(r.metadata),c=new TextEncoder,l=c.encode(`--${i}\r\nContent-Type: ${n}; charset=UTF-8\r\n\r\n`+a+"\r\n"),u=c.encode(`--${i}\r\nContent-Type: ${s}\r\nContent-Transfer-Encoding: binary\r\n\r\n`),d=c.encode(`\r\n--${i}--`),p=t instanceof Uint8Array?t:new Uint8Array(t),h=new Uint8Array(l.length+u.length+p.length+d.length);return[l,u,p,d].reduce((e,t)=>(h.set(t,e),e+t.length),0),this.request({method:y.POST,url:e,body:h,headers:Object.assign(Object.assign({},r.headers||{}),{"Content-Type":`multipart/related; boundary=${i}`}),params:r.params,signal:r.signal,query:r.query})}static createFactory(e={}){const r=e.http||new O(e.baseURL);return function(s,n){const o=t("function"==typeof e.endpoint?e.endpoint():e.endpoint||{})[s];return{key:s,fn:async(...t)=>{const s="function"==typeof e.storage?await e.storage():e.storage;switch(s&&r.setStorage(s),n){case y.POST:return await r.post(o,...t);case y.PUT:return await r.put(o,...t);case y.PATCH:return await r.patch(o,...t);case y.DELETE:return await r.delete(o,...t);case y.HEAD:return await r.head(o,...t);case y.OPTIONS:return await r.options(o,...t);case m.UPLOAD:return await r.upload(o,...t);case m.RELATED:return await r.related(o,...t);default:return await r.get(o,...t)}}}}}}O.method=y,O.Endpoint=T,O.authTokenKey="access_token",O.authHeaderKey="Authorization",O.authHeaderType="Bearer",O.authDetectToken=["localStorage","sessionStorage","cookie"],O.extraHeaders=null,O.storage=null,O.interceptors={request:[],response:[],transform:[],error:[]};export{f as API_AUTH_HEADER_KEY,g as API_AUTH_HEADER_TYPE,h as API_AUTH_TOKEN_KEY,p as DEFAULT_BASE_URL,T as Endpoint,O as Http,y as HttpMethod,m as HttpUpload};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecosy/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A modular, tree-shakable collection of essential utilities, serialization primitives, and event-driven patterns for modern TypeScript applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|