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 | await 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 | headers: {
|
55 | "Content-Type": "application/x-www-form-urlencoded"
|
56 | },
|
57 | credentials: "same-origin",
|
58 | cache: "default",
|
59 | redirect: "follow",
|
60 | referrerPolicy: "no-referrer-when-downgrade",
|
61 | mode: "cors"
|
62 | };
|
63 |
|
64 | function isFunction(value) {
|
65 | return typeof value === "function";
|
66 | }
|
67 | function immutable(record) {
|
68 | return new Proxy(record, {
|
69 | set() {
|
70 | return true;
|
71 | }
|
72 | });
|
73 | }
|
74 | function merge(initial, patches, customMerge) {
|
75 | return patches.reduce(
|
76 | (merged, patch) => Object.assign(merged, customMerge(merged, patch)),
|
77 | Object.assign( Object.create(null), initial)
|
78 | );
|
79 | }
|
80 |
|
81 | function mergeOptions(options, ...patchOptions) {
|
82 | return merge(options, patchOptions, (merged, current) => {
|
83 | const { headers } = current;
|
84 | if (headers) {
|
85 | current.headers = Object.assign({}, merged.headers || {}, headers);
|
86 | }
|
87 | return current;
|
88 | });
|
89 | }
|
90 |
|
91 | function setDefaultOptions(patchRequestOptions) {
|
92 | Object.assign(defaultOptions, mergeOptions(defaultOptions, patchRequestOptions));
|
93 | }
|
94 |
|
95 | function parseURL(baseURL, url) {
|
96 | let [_url, querystring] = `${baseURL || "/"}/${url || "/"}`.split("?");
|
97 | const [schema, isAbsoluteURL] = _url.match(/([a-z][a-z\d+\-.]*:)\/\//gi) || [];
|
98 | if (isAbsoluteURL) {
|
99 | return url;
|
100 | }
|
101 | if (schema) {
|
102 | if (!_url.startsWith(schema)) {
|
103 | return url;
|
104 | }
|
105 | _url = _url.replace(schema, "");
|
106 | }
|
107 | const paths = [];
|
108 | for (const path of _url.split("/")) {
|
109 | if (path === "..") {
|
110 | paths.pop();
|
111 | } else if (path && path !== ".") {
|
112 | paths.push(path);
|
113 | }
|
114 | }
|
115 | _url = `${schema || "/"}${paths.join("/")}`;
|
116 | if (querystring) {
|
117 | return `${_url}?${querystring}`;
|
118 | }
|
119 | return _url;
|
120 | }
|
121 |
|
122 | function createContext(options) {
|
123 | const { baseUrl = "", url = "", params = {}, headers = {} } = options;
|
124 | if (!url) {
|
125 | throw new Error("[Fatcher] URL is required.");
|
126 | }
|
127 | const [normalizedURL, querystring] = parseURL(baseUrl, url).split("?");
|
128 | if (querystring) {
|
129 | for (const [key, value] of new URLSearchParams(querystring)) {
|
130 | params[key] = value;
|
131 | }
|
132 | }
|
133 | const requestHeaders = new Headers();
|
134 | for (const [key, value] of Object.entries(headers)) {
|
135 | if (value) {
|
136 | requestHeaders.set(key, value);
|
137 | }
|
138 | }
|
139 | return { ...options, url: normalizedURL, params, requestHeaders };
|
140 | }
|
141 |
|
142 | function mergeContext(context, ...patchContext) {
|
143 | return merge(context, patchContext, (merged, current) => {
|
144 | const { headers } = current;
|
145 | if (headers) {
|
146 | current.headers = Object.assign(merged.headers || {}, headers);
|
147 | for (const [key, value] of Object.entries(headers)) {
|
148 | if (value) {
|
149 | context.requestHeaders.set(key, value);
|
150 | }
|
151 | }
|
152 | }
|
153 | return current;
|
154 | });
|
155 | }
|
156 |
|
157 | function composeMiddlewares(middlewares) {
|
158 | return function use(initialContext) {
|
159 | let currentIndex = -1;
|
160 | let response;
|
161 | let context = initialContext;
|
162 | let immutableContext = immutable(context);
|
163 | async function dispatch(index, patchContext) {
|
164 | if (index <= currentIndex) {
|
165 | return Promise.reject(
|
166 | new Error(`Middleware <${middlewares[index - 1].name}> call next() more than once.`)
|
167 | );
|
168 | }
|
169 | currentIndex = index;
|
170 | const middleware = middlewares[index];
|
171 | if (!middleware) {
|
172 | return response;
|
173 | }
|
174 | if (patchContext) {
|
175 | context = mergeContext(context, patchContext);
|
176 | immutableContext = immutable(context);
|
177 | }
|
178 | try {
|
179 | return response = await middleware.use(immutableContext, async (patch) => dispatch(index + 1, patch));
|
180 | } catch (error) {
|
181 | return Promise.reject(error);
|
182 | }
|
183 | }
|
184 | return dispatch(0);
|
185 | };
|
186 | }
|
187 |
|
188 | function fetcher() {
|
189 | return {
|
190 | name: "fatcher-middleware-fetch",
|
191 | async use(context) {
|
192 | let {
|
193 | url = "",
|
194 | requestHeaders: headers,
|
195 | payload,
|
196 | method = "GET",
|
197 | body,
|
198 | params,
|
199 | validateCode,
|
200 | ...rest
|
201 | } = context;
|
202 | const contentType = headers.get("content-type");
|
203 | if (["GET", "HEAD"].includes(method)) {
|
204 | params = Object.assign({}, params, body);
|
205 | body = null;
|
206 | } else if (payload && contentType) {
|
207 | if (contentType.includes("application/json")) {
|
208 | body = JSON.stringify(payload);
|
209 | }
|
210 | if (contentType.includes("application/x-www-form-urlencoded")) {
|
211 | body = new URLSearchParams(payload);
|
212 | }
|
213 | }
|
214 | if (Object.keys(params).length) {
|
215 | url = `${url}?${new URLSearchParams(params)}`;
|
216 | }
|
217 | const response = await fetch(url, {
|
218 | ...rest,
|
219 | headers,
|
220 | body,
|
221 | method
|
222 | });
|
223 | const { status, statusText, ok, headers: responseHeaders } = response;
|
224 | const result = {
|
225 | status,
|
226 | statusText,
|
227 | headers: responseHeaders,
|
228 | url,
|
229 | data: response
|
230 | };
|
231 | if (validateCode ? validateCode(status) : ok) {
|
232 | return result;
|
233 | }
|
234 | return Promise.reject(new FatcherError(context, response));
|
235 | }
|
236 | };
|
237 | }
|
238 |
|
239 | async function registerMiddlewares(unregisteredMiddlewares) {
|
240 | var _a;
|
241 | let middlewares = [];
|
242 | for await (const middleware of unregisteredMiddlewares) {
|
243 | if (Array.isArray(middleware)) {
|
244 | middlewares = middlewares.concat(await registerMiddlewares(middleware));
|
245 | } else {
|
246 | let current = [isFunction(middleware) ? await middleware() : middleware];
|
247 | if ((_a = current[0].presets) == null ? void 0 : _a.length) {
|
248 | current = (await registerMiddlewares(current[0].presets)).concat(current);
|
249 | }
|
250 | middlewares = middlewares.concat(current);
|
251 | }
|
252 | }
|
253 | return middlewares;
|
254 | }
|
255 |
|
256 | async function fatcher(inlineOptions = {}) {
|
257 | const options = mergeOptions(defaultOptions, inlineOptions);
|
258 | const { middlewares: customMiddlewares = [], ...rest } = options;
|
259 | const registeredMiddlewares = await registerMiddlewares([...customMiddlewares, fetcher]);
|
260 | const useMiddlewares = composeMiddlewares(registeredMiddlewares);
|
261 | const initialContext = createContext(rest);
|
262 | const result = await useMiddlewares(initialContext);
|
263 | const data = canActivate(result.data) ? result.data.body : result.data;
|
264 | return {
|
265 | ...result,
|
266 | options,
|
267 | data
|
268 | };
|
269 | }
|
270 |
|
271 | function createScopedRequest(scopedOptions = {}) {
|
272 | return function request(url, payload, inlineOptions = {}) {
|
273 | const options = mergeOptions(scopedOptions, { ...inlineOptions, url, payload });
|
274 | return fatcher(options);
|
275 | };
|
276 | }
|
277 |
|
278 | exports.FatcherError = FatcherError;
|
279 | exports.canActivate = canActivate;
|
280 | exports.chunkStreamReader = chunkStreamReader;
|
281 | exports.createScopedRequest = createScopedRequest;
|
282 | exports.fatcher = fatcher;
|
283 | exports.isAbortError = isAbortError;
|
284 | exports.isFatcherError = isFatcherError;
|
285 | exports.mergeOptions = mergeOptions;
|
286 | exports.readStreamByChunk = readStreamByChunk;
|
287 | exports.setDefaultOptions = setDefaultOptions;
|