@ecosy/core 0.3.0 → 0.3.2
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 +86 -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,40 @@ 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>;
|
|
61
|
+
/**
|
|
62
|
+
* Default `configs` merged into every request sent through this
|
|
63
|
+
* instance. Same shape and allowlist as {@link HttpRequest.configs}:
|
|
64
|
+
* `credentials`, `cache`, `mode`, `redirect`, `referrer`,
|
|
65
|
+
* `referrerPolicy`, `integrity`, `keepalive`, `priority`, `duplex`,
|
|
66
|
+
* plus framework extensions (`next`, `cf`, `dispatcher`). Per-call
|
|
67
|
+
* `configs` override these.
|
|
68
|
+
*/
|
|
69
|
+
configs?: Record<string, unknown>;
|
|
36
70
|
}
|
|
37
71
|
/** Storage adapter interface for reading/writing auth tokens (e.g. `localStorage`). */
|
|
38
72
|
export interface HttpStorage {
|
|
@@ -114,13 +148,23 @@ export declare class Endpoint {
|
|
|
114
148
|
* ```
|
|
115
149
|
*/
|
|
116
150
|
export declare class Http {
|
|
117
|
-
private readonly baseURL;
|
|
118
151
|
static readonly method: typeof HttpMethod;
|
|
119
152
|
static readonly Endpoint: typeof Endpoint;
|
|
120
153
|
static authTokenKey: string;
|
|
121
154
|
static authHeaderKey: string;
|
|
122
155
|
static authHeaderType: string;
|
|
123
156
|
static authDetectToken: string[];
|
|
157
|
+
/**
|
|
158
|
+
* One-shot headers merged into the very next request across all
|
|
159
|
+
* instances, then cleared. Intended for request-scoped values like
|
|
160
|
+
* CSRF tokens or correlation IDs that callers don't want to thread
|
|
161
|
+
* through every call site.
|
|
162
|
+
*
|
|
163
|
+
* Note: this is a process-wide mutable static. In concurrent async
|
|
164
|
+
* contexts (e.g. multiple tenants sharing one process) it is the
|
|
165
|
+
* caller's responsibility to ensure the set → dispatch → reset
|
|
166
|
+
* sequence is not interleaved.
|
|
167
|
+
*/
|
|
124
168
|
static extraHeaders: Record<string, string> | null;
|
|
125
169
|
static storage: HttpStorage | null;
|
|
126
170
|
private defaultHeaders;
|
|
@@ -128,7 +172,35 @@ export declare class Http {
|
|
|
128
172
|
private storage;
|
|
129
173
|
private static interceptors;
|
|
130
174
|
private interceptors;
|
|
131
|
-
|
|
175
|
+
private readonly baseURL;
|
|
176
|
+
private readonly allowedOrigins;
|
|
177
|
+
private readonly defaultConfigs;
|
|
178
|
+
/**
|
|
179
|
+
* @param init - Either a base URL string (backwards-compatible form)
|
|
180
|
+
* or an {@link HttpOptions} object. Using the object form lets
|
|
181
|
+
* callers opt in to additional origins that absolute URLs and
|
|
182
|
+
* redirect responses are permitted to reach. Any other origin is
|
|
183
|
+
* rejected before the request is sent and, for redirects, before
|
|
184
|
+
* the response body is returned.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* new Http("https://api.example.com");
|
|
189
|
+
* new Http({
|
|
190
|
+
* baseURL: "https://api.example.com",
|
|
191
|
+
* allowedOrigins: ["https://cdn.example.com"],
|
|
192
|
+
* });
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
constructor(init?: string | HttpOptions);
|
|
196
|
+
/** Whether `origin` is this instance's baseURL origin or an explicitly allowed one. */
|
|
197
|
+
private isAllowedOrigin;
|
|
198
|
+
/**
|
|
199
|
+
* If a request was sent with credentials (Authorization / Cookie) and
|
|
200
|
+
* ended up at a different origin via redirect, refuse to return the
|
|
201
|
+
* response. Defends against token exfil via server-controlled 3xx.
|
|
202
|
+
*/
|
|
203
|
+
private assertSameOriginResponse;
|
|
132
204
|
/** Register a global interceptor (applies to all `Http` instances). */
|
|
133
205
|
static on(...params: HttpInterceptorParameters): void;
|
|
134
206
|
/** Remove a global interceptor. */
|
|
@@ -153,7 +225,18 @@ export declare class Http {
|
|
|
153
225
|
getHeaders(headers?: Record<string, string>, isFormData?: boolean): {
|
|
154
226
|
[x: string]: string;
|
|
155
227
|
};
|
|
156
|
-
/**
|
|
228
|
+
/**
|
|
229
|
+
* Build the full URL from base URL, path, query string, and path params.
|
|
230
|
+
*
|
|
231
|
+
* Security rules applied here (see SECURITY audit):
|
|
232
|
+
* - Absolute URLs must use `http`/`https` and their origin must match
|
|
233
|
+
* `baseURL`'s origin or an entry in `allowedOrigins`.
|
|
234
|
+
* - Protocol-relative URLs (`//host/…`) are rejected — they silently
|
|
235
|
+
* flip the target host.
|
|
236
|
+
* - Path params (`{id}`) are URL-encoded by `interpolateURL`
|
|
237
|
+
* so a value of `"../admin"` cannot traverse the path.
|
|
238
|
+
* - Proto-pollution keys in `params` are stripped.
|
|
239
|
+
*/
|
|
157
240
|
getURL(options: HttpRequest): string;
|
|
158
241
|
/** Serialize the request body (JSON, FormData, or binary). Returns `undefined` for bodyless methods. */
|
|
159
242
|
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))d.has(r)&&(e[r]=t[r]);return e}function p(t){const e=Object.create(null);for(const r of Object.keys(t))c.has(r)||(e[r]=t[r]);return e}const u=i.getEnv("API_URL","/"),h=i.getEnv("API_AUTH_TOKEN_KEY"),g=i.getEnv("API_AUTH_HEADER_KEY"),f=i.getEnv("API_AUTH_HEADER_TYPE");var y,T;exports.HttpMethod=void 0,(y=exports.HttpMethod||(exports.HttpMethod={})).GET="GET",y.POST="POST",y.PUT="PUT",y.DELETE="DELETE",y.PATCH="PATCH",y.HEAD="HEAD",y.OPTIONS="OPTIONS",exports.HttpUpload=void 0,(T=exports.HttpUpload||(exports.HttpUpload={})).UPLOAD="UPLOAD",T.RELATED="RELATED";class E{static register(t,e){return this.registered[t]=Object.assign({},e),this}static all(){return Object.assign({},this.registered)}}E.registered={};class H{constructor(t){var e,r,s;this.defaultHeaders={"Content-Type":"application/json"},this.method=H.method,this.storage=H.storage,this.interceptors={request:[],response:[],transform:[],error:[]};const o="string"==typeof t||void 0===t?{baseURL:null!=t?t:u}:t;this.baseURL=null!==(r=null!==(e=o.baseURL)&&void 0!==e?e:u)&&void 0!==r?r:"/";const n=new Set;for(const t of null!==(s=o.allowedOrigins)&&void 0!==s?s:[]){const e=a(t);if(!e)throw new Error(`Http: invalid allowedOrigins entry: ${t}`);n.add(e)}const i=a(this.baseURL);i&&n.add(i),this.allowedOrigins=n,this.defaultConfigs=o.configs?l(o.configs):{}}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=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]&&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=p(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=h||H.authTokenKey,e=g||H.authHeaderKey,r=f||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&&/^[\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),H.extraHeaders||{});return H.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:p(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=[...H.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=Object.assign(Object.assign({},this.defaultConfigs),s.configs?l(s.configs):{}),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 d=c.headers.get("Content-Type")||"";let p=null;p=d.includes("application/json")?await c.json():await c.text();const u=[...H.interceptors.transform,...this.interceptors.transform];let h=p;for(const t of u)h=await t(h);const g=[...H.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 E=null;if(!T){E=p.error||p;const t=[...H.interceptors.error,...this.interceptors.error];for(const e of t)E=await e(E)}return{data:h,success:T,error:T?null:E,status:f.status,statusText:f.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 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=[...H.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=[...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 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=[...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(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=[...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: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 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=E,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=g,exports.API_AUTH_HEADER_TYPE=f,exports.API_AUTH_TOKEN_KEY=h,exports.DEFAULT_BASE_URL=u,exports.Endpoint=E,exports.Http=H;
|
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 t}from"./utilities/filelist.mjs";import{flatten as e}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(t){try{return new URL(t).origin}catch(t){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(t){const e=Object.create(null);for(const r of Object.keys(t))u.has(r)&&(e[r]=t[r]);return e}function p(t){const e=Object.create(null);for(const r of Object.keys(t))l.has(r)||(e[r]=t[r]);return e}const h=a("API_URL","/"),f=a("API_AUTH_TOKEN_KEY"),g=a("API_AUTH_HEADER_KEY"),y=a("API_AUTH_HEADER_TYPE");var m,T;!function(t){t.GET="GET",t.POST="POST",t.PUT="PUT",t.DELETE="DELETE",t.PATCH="PATCH",t.HEAD="HEAD",t.OPTIONS="OPTIONS"}(m||(m={})),function(t){t.UPLOAD="UPLOAD",t.RELATED="RELATED"}(T||(T={}));class O{static register(t,e){return this.registered[t]=Object.assign({},e),this}static all(){return Object.assign({},this.registered)}}O.registered={};class b{constructor(t){var e,r,s;this.defaultHeaders={"Content-Type":"application/json"},this.method=b.method,this.storage=b.storage,this.interceptors={request:[],response:[],transform:[],error:[]};const n="string"==typeof t||void 0===t?{baseURL:null!=t?t:h}:t;this.baseURL=null!==(r=null!==(e=n.baseURL)&&void 0!==e?e:h)&&void 0!==r?r:"/";const o=new Set;for(const t of null!==(s=n.allowedOrigins)&&void 0!==s?s:[]){const e=c(t);if(!e)throw new Error(`Http: invalid allowedOrigins entry: ${t}`);o.add(e)}const i=c(this.baseURL);i&&o.add(i),this.allowedOrigins=o,this.defaultConfigs=n.configs?d(n.configs):{}}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]),n=Object.keys(r).some(t=>"cookie"===t.toLowerCase()&&!!r[t]);if(!s&&!n)return;const o=c(t);if(!o)return;const i=e.url?c(e.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(...t){const[e,r]=t,s=b.interceptors[e];s.includes(r)||s.push(r),b.interceptors[e]=s}static off(...t){const[e,r]=t,s=b.interceptors[e];b.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]&&i.Primitive.isPrimitive(t[1])):"object"==typeof t&&null!==t&&Object.values(t).every(t=>i.Primitive.isPrimitive(t)))}getQuery(t){const{method:e,body:r,query:s}=t,n=e===m.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=p(n);return i.queryString.stringify(o,{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=f||b.authTokenKey,e=g||b.authHeaderKey,r=y||b.authHeaderType;if(!t||!e)return{key:e,value:""};let s="";this.storage&&(s=this.storage.getItem(t)||"");const n={key:e,value:""};return s&&/^[\x21-\x7E]+$/.test(s)&&(n.value=r?`${r} ${s}`:s),n}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),b.extraHeaders||{});return b.extraHeaders=null,s}getURL(t){const{url:e="",params:r={}}=t,s=this.getQuery(t);let o;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`);o=t.toString()}else{const t=(this.baseURL||"/").replace(/\/+$/,""),r=e.replace(/^\/+/,"");o=t?`${t}/${r}`:`/${r}`}}else o=this.baseURL||"/";if(s){const t=o.includes("?")?"&":"?";o+=`${t}${s}`}if(!o.includes("{")||!o.includes("}"))return o;const a=Array.isArray(r)?r:p(r);return o.replace(/\{([a-zA-Z0-9_.-]+)\}/g,(t,e)=>{const r=n(a,e);return null==r||"object"==typeof r?"":i.URL.encode(String(r))})}getBody(t){const{method:e,body:s}=t;if(e!==m.GET&&e!==m.HEAD&&e!==m.OPTIONS&&e!==m.DELETE)return e!==m.POST&&e!==m.PUT&&e!==m.PATCH||!r(s)?s instanceof Uint8Array||s instanceof ArrayBuffer?s:void 0!==s?JSON.stringify(s):void 0:s}async request(t){const{method:e=m.GET}=t;try{let s=Object.assign({},t);const n=[...b.interceptors.request,...this.interceptors.request];for(const t of n)s=await t(s);const o=this.getURL(s),i=this.getHeaders(s.headers||{});r(s.body)&&"Content-Type"in i&&delete i["Content-Type"];const a=Object.assign(Object.assign({},this.defaultConfigs),s.configs?d(s.configs):{}),c=await fetch(o,Object.assign(Object.assign({},a),{method:s.method||e,headers:i,body:this.getBody(s),signal:s.signal}));this.assertSameOriginResponse(o,c,i);const l=c.headers.get("Content-Type")||"";let u=null;u=l.includes("application/json")?await c.json():await c.text();const p=[...b.interceptors.transform,...this.interceptors.transform];let h=u;for(const t of p)h=await t(h);const f=[...b.interceptors.response,...this.interceptors.response];let g=c;for(const t of f)g=await t(g);const y={};g.headers.forEach((t,e)=>{y[e]=t});const m=g.ok;let T=null;if(!m){T=u.error||u;const t=[...b.interceptors.error,...this.interceptors.error];for(const e of t)T=await e(T)}return{data:h,success:m,error:m?null:T,status:g.status,statusText:g.statusText,headers:y}}catch(t){let e=t instanceof Error?t:new Error(String(t));const r=[...b.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:m.GET,url:t,query:e}))}post(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:m.POST,url:t,body:e}))}put(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:m.PUT,url:t,body:e}))}patch(t,e,r){return this.request(Object.assign(Object.assign({},r),{method:m.PATCH,url:t,body:e}))}delete(t,e){return this.request(Object.assign(Object.assign({},e),{method:m.DELETE,url:t}))}head(t,e){return this.request(Object.assign(Object.assign({},e),{method:m.HEAD,url:t}))}options(t,e){return this.request(Object.assign(Object.assign({},e),{method:m.OPTIONS,url:t}))}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((t,e)=>{i.append(`${a}[${e}]`,t,t.name)})):t(n)?(a.endsWith("[]")&&(a=a.slice(0,-2)),Array.from(n).forEach((t,e)=>{i.append(`${a}[${e}]`,t,t.name)})):i.append(a,n,n.name),null==o?void 0:o.body){const t=e(o.body);Object.entries(t).forEach(([t,e])=>{null!=e&&i.append(t,String(e))})}return(null==o?void 0:o.onProgress)&&"undefined"!=typeof XMLHttpRequest?new Promise(async t=>{try{let e=Object.assign(Object.assign({},o),{method:m.POST,url:r,body:i});const s=[...b.interceptors.request,...this.interceptors.request];for(const t of s)e=await t(e);const n=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=o.onProgress)||void 0===e||e.call(o,{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=[...b.interceptors.transform,...this.interceptors.transform];let n=r;for(const t of s)n=await t(n);const o=[...b.interceptors.response,...this.interceptors.response];let i=new Response(c.responseText,{status:c.status,statusText:c.statusText,headers:a});for(const t of o)i=await t(i);const l={};i.headers.forEach((t,e)=>{l[e]=t});const u=i.ok;t({data:n,success:u,error:u?null:n,status:i.status,statusText:i.statusText,headers:l})}catch(e){const r=[...b.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=[...b.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||m.POST,n,!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=[...b.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:m.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(t,e,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=e instanceof Uint8Array?e:new Uint8Array(e),h=new Uint8Array(l.length+u.length+p.length+d.length);return[l,u,p,d].reduce((t,e)=>(h.set(e,t),t+e.length),0),this.request({method:m.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 b(t.baseURL);return function(s,n){const o=e("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),n){case m.POST:return await r.post(o,...e);case m.PUT:return await r.put(o,...e);case m.PATCH:return await r.patch(o,...e);case m.DELETE:return await r.delete(o,...e);case m.HEAD:return await r.head(o,...e);case m.OPTIONS:return await r.options(o,...e);case T.UPLOAD:return await r.upload(o,...e);case T.RELATED:return await r.related(o,...e);default:return await r.get(o,...e)}}}}}}b.method=m,b.Endpoint=O,b.authTokenKey="access_token",b.authHeaderKey="Authorization",b.authHeaderType="Bearer",b.authDetectToken=["localStorage","sessionStorage","cookie"],b.extraHeaders=null,b.storage=null,b.interceptors={request:[],response:[],transform:[],error:[]};export{g as API_AUTH_HEADER_KEY,y as API_AUTH_HEADER_TYPE,f as API_AUTH_TOKEN_KEY,h as DEFAULT_BASE_URL,O as Endpoint,b as Http,m as HttpMethod,T 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.2",
|
|
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": {
|