UNPKG

8.41 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5function canActivate(data) {
6 return data instanceof Response && !data.bodyUsed && !!data.body;
7}
8
9function isAbortError(error) {
10 return error instanceof DOMException && error.name === "AbortError";
11}
12
13async function readStreamByChunk(readableStream, callback) {
14 async function read(reader) {
15 const { value, done } = await reader.read();
16 if (done || !value) {
17 return;
18 }
19 callback(value);
20 await read(reader);
21 }
22 return read(readableStream.getReader());
23}
24const chunkStreamReader = readStreamByChunk;
25
26class FatcherError extends Error {
27 constructor(context, response) {
28 super(`[Fatcher] Fetch failed with status code ${response.status}`);
29 this.name = "FatcherError";
30 this.__isFatcherError__ = true;
31 this._response = response;
32 this._context = context;
33 }
34 toJSON() {
35 const headers = {};
36 for (const [key, value] of this._response.headers) {
37 headers[key] = value;
38 }
39 return {
40 status: this._response.status,
41 statusText: this._response.statusText,
42 context: this._context,
43 headers,
44 data: this._response.body
45 };
46 }
47}
48
49function isFatcherError(error) {
50 return error instanceof FatcherError && error.name === "FatcherError" && error.__isFatcherError__;
51}
52
53const defaultOptions = {
54 baseUrl: "/",
55 middlewares: [],
56 headers: {
57 "Content-Type": "application/x-www-form-urlencoded"
58 },
59 params: {},
60 credentials: "same-origin",
61 cache: "default",
62 redirect: "follow",
63 referrerPolicy: "no-referrer-when-downgrade",
64 mode: "cors"
65};
66
67function isAbsoluteURL(url) {
68 return /^http(s)?:\/\/.+/.test(url);
69}
70function normalize(url) {
71 let protocol;
72 return url.replace(/http(s?):\/\//, (str) => {
73 protocol = str;
74 return "";
75 }).replace(/\/\/(\/)?/g, "/").replace(/.*\S/, (str) => (protocol != null ? protocol : "") + str);
76}
77function normalizeURL(baseURL, url) {
78 if (!url) {
79 return baseURL;
80 }
81 if (isAbsoluteURL(url)) {
82 return normalize(url);
83 }
84 if (baseURL === "/") {
85 return url;
86 }
87 return normalize(`${baseURL}/${url}`);
88}
89function isFunction(value) {
90 return typeof value === "function";
91}
92function immutable(record) {
93 return new Proxy(record, {
94 set() {
95 return true;
96 }
97 });
98}
99function merge(initial, patches, customMerge) {
100 return patches.reduce((merged, patch) => Object.assign(merged, customMerge(merged, patch)), Object.assign(/* @__PURE__ */ Object.create(null), initial));
101}
102
103function mergeOptions(options, ...patchOptions) {
104 return merge(options, patchOptions, (merged, current) => {
105 const { headers } = current;
106 if (headers) {
107 current.headers = Object.assign({}, merged.headers || {}, headers);
108 }
109 return current;
110 });
111}
112
113function setDefaultOptions(patchRequestOptions) {
114 Object.assign(defaultOptions, mergeOptions(defaultOptions, patchRequestOptions));
115}
116
117function createContext(options) {
118 const { baseUrl = "", url = "", params = {}, headers = {} } = options;
119 if (!url) {
120 throw new Error("[Fatcher] URL is required.");
121 }
122 const [normalizedURL, querystring] = normalizeURL(baseUrl, url).split("?");
123 if (querystring) {
124 for (const [key, value] of new URLSearchParams(querystring)) {
125 params[key] = value;
126 }
127 }
128 const requestHeaders = new Headers();
129 for (const [key, value] of Object.entries(headers)) {
130 if (value) {
131 requestHeaders.set(key, value);
132 }
133 }
134 return { ...options, url: normalizedURL, params, requestHeaders };
135}
136
137function mergeContext(context, ...patchContext) {
138 return merge(context, patchContext, (merged, current) => {
139 const { headers } = current;
140 if (headers) {
141 current.headers = Object.assign(merged.headers || {}, headers);
142 for (const [key, value] of Object.entries(headers)) {
143 if (value) {
144 context.requestHeaders.set(key, value);
145 }
146 }
147 }
148 return current;
149 });
150}
151
152function composeMiddlewares(middlewares) {
153 return function use(initialContext) {
154 let currentIndex = -1;
155 let response;
156 let context = initialContext;
157 let immutableContext = immutable(context);
158 async function dispatch(index, patchContext) {
159 if (index <= currentIndex) {
160 return Promise.reject(new Error(`Middleware <${middlewares[index - 1].name}> call next() more than once.`));
161 }
162 currentIndex = index;
163 const middleware = middlewares[index];
164 if (!middleware) {
165 return response;
166 }
167 if (patchContext) {
168 context = mergeContext(context, patchContext);
169 immutableContext = immutable(context);
170 }
171 try {
172 return response = await middleware.use(immutableContext, async (patch) => dispatch(index + 1, patch));
173 } catch (error) {
174 return Promise.reject(error);
175 }
176 }
177 return dispatch(0);
178 };
179}
180
181function fetcher() {
182 return {
183 name: "fatcher-middleware-http-fetcher",
184 async use(context) {
185 const { url = "", requestHeaders: headers, ...rest } = context;
186 const response = await fetch(url, {
187 ...rest,
188 headers
189 });
190 const { status, statusText, ok, headers: responseHeaders } = response;
191 const result = {
192 status,
193 statusText,
194 headers: responseHeaders,
195 url,
196 data: response
197 };
198 if (ok) {
199 return result;
200 }
201 return Promise.reject(new FatcherError(context, response));
202 }
203 };
204}
205
206function payloadConsumer() {
207 return {
208 name: "fatcher-middleware-payload-consumer",
209 use(context, next) {
210 const { payload, url, requestHeaders, method, body, params = {} } = context;
211 const contentType = requestHeaders.get("content-type");
212 let normalizeUrl = url;
213 if (["GET", "HEAD"].includes(method)) {
214 if (Object.keys(params).length) {
215 normalizeUrl = `${url}?${new URLSearchParams(Object.assign({}, params, body)).toString()}`;
216 }
217 return next({
218 url: normalizeUrl,
219 body: null
220 });
221 }
222 let normalizedBody = body;
223 if (payload) {
224 if (contentType == null ? void 0 : contentType.includes("application/json")) {
225 normalizedBody = JSON.stringify(payload);
226 }
227 if (contentType == null ? void 0 : contentType.includes("application/x-www-form-urlencoded")) {
228 normalizedBody = new URLSearchParams(payload);
229 }
230 }
231 if (Object.keys(params).length) {
232 normalizeUrl = `${url}?${new URLSearchParams(params).toString()}`;
233 }
234 return next({
235 url: normalizeUrl,
236 body: normalizedBody
237 });
238 }
239 };
240}
241
242function registerMiddlewares(unregisteredMiddlewares) {
243 return unregisteredMiddlewares.reduce((total, current) => {
244 var _a;
245 let middleware;
246 if (Array.isArray(current)) {
247 middleware = registerMiddlewares(current);
248 } else {
249 middleware = [isFunction(current) ? current() : current];
250 if ((_a = middleware[0].presets) == null ? void 0 : _a.length) {
251 middleware = registerMiddlewares(middleware[0].presets).concat(middleware);
252 }
253 }
254 return total.concat(middleware);
255 }, []);
256}
257
258async function fatcher(inlineOptions = {}) {
259 const options = mergeOptions(defaultOptions, inlineOptions);
260 const { middlewares: customMiddlewares = [], ...rest } = options;
261 const registeredMiddlewares = registerMiddlewares([...customMiddlewares, payloadConsumer, fetcher]);
262 const useMiddlewares = composeMiddlewares(registeredMiddlewares);
263 const initialContext = createContext(rest);
264 const result = await useMiddlewares(initialContext);
265 const data = canActivate(result.data) ? result.data.body : result.data;
266 return {
267 ...result,
268 options,
269 data
270 };
271}
272
273function createScopedRequest(scopedOptions = {}) {
274 return function request(url, payload, inlineOptions = {}) {
275 const options = mergeOptions(scopedOptions, { ...inlineOptions, url, payload });
276 return fatcher(options);
277 };
278}
279
280exports.FatcherError = FatcherError;
281exports.canActivate = canActivate;
282exports.chunkStreamReader = chunkStreamReader;
283exports.createScopedRequest = createScopedRequest;
284exports.fatcher = fatcher;
285exports.isAbortError = isAbortError;
286exports.isFatcherError = isFatcherError;
287exports.mergeOptions = mergeOptions;
288exports.readStreamByChunk = readStreamByChunk;
289exports.setDefaultOptions = setDefaultOptions;