1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', { value: true });
|
4 |
|
5 | function canActivate(data) {
|
6 | return data instanceof Response && !data.bodyUsed && !!data.body;
|
7 | }
|
8 |
|
9 | function isAbortError(error) {
|
10 | return error instanceof DOMException && error.name === "AbortError";
|
11 | }
|
12 |
|
13 | async 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 | }
|
24 | const chunkStreamReader = readStreamByChunk;
|
25 |
|
26 | class 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 |
|
49 | function isFatcherError(error) {
|
50 | return error instanceof FatcherError && error.name === "FatcherError" && error.__isFatcherError__;
|
51 | }
|
52 |
|
53 | const 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 |
|
67 | function isAbsoluteURL(url) {
|
68 | return /^http(s)?:\/\/.+/.test(url);
|
69 | }
|
70 | function 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 | }
|
77 | function 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 | }
|
89 | function isFunction(value) {
|
90 | return typeof value === "function";
|
91 | }
|
92 | function immutable(record) {
|
93 | return new Proxy(record, {
|
94 | set() {
|
95 | return true;
|
96 | }
|
97 | });
|
98 | }
|
99 | function merge(initial, patches, customMerge) {
|
100 | return patches.reduce((merged, patch) => Object.assign(merged, customMerge(merged, patch)), Object.assign( Object.create(null), initial));
|
101 | }
|
102 |
|
103 | function 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 |
|
113 | function setDefaultOptions(patchRequestOptions) {
|
114 | Object.assign(defaultOptions, mergeOptions(defaultOptions, patchRequestOptions));
|
115 | }
|
116 |
|
117 | function 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 |
|
137 | function 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 |
|
152 | function 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 |
|
181 | function 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 |
|
206 | function 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 |
|
242 | function 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 |
|
258 | async 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 |
|
273 | function createScopedRequest(scopedOptions = {}) {
|
274 | return function request(url, payload, inlineOptions = {}) {
|
275 | const options = mergeOptions(scopedOptions, { ...inlineOptions, url, payload });
|
276 | return fatcher(options);
|
277 | };
|
278 | }
|
279 |
|
280 | exports.FatcherError = FatcherError;
|
281 | exports.canActivate = canActivate;
|
282 | exports.chunkStreamReader = chunkStreamReader;
|
283 | exports.createScopedRequest = createScopedRequest;
|
284 | exports.fatcher = fatcher;
|
285 | exports.isAbortError = isAbortError;
|
286 | exports.isFatcherError = isFatcherError;
|
287 | exports.mergeOptions = mergeOptions;
|
288 | exports.readStreamByChunk = readStreamByChunk;
|
289 | exports.setDefaultOptions = setDefaultOptions;
|