Skip to content

Commit 0d069bc

Browse files
committed
Update index.js
Merges developit#47
1 parent 64cf3ca commit 0d069bc

File tree

1 file changed

+100
-52
lines changed

1 file changed

+100
-52
lines changed

src/index.js

Lines changed: 100 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* @typedef Options
1717
* @property {string} [url] the URL to request
1818
* @property {'get'|'post'|'put'|'patch'|'delete'|'options'|'head'|'GET'|'POST'|'PUT'|'PATCH'|'DELETE'|'OPTIONS'|'HEAD'} [method="get"] HTTP method, case-insensitive
19-
* @property {RequestHeaders} [headers] Request headers
19+
* @property {Headers} [headers] Request headers
2020
* @property {FormData|string|object} [body] a body, optionally encoded, to send
2121
* @property {'text'|'json'|'stream'|'blob'|'arrayBuffer'|'formData'|'stream'} [responseType="json"] An encoding to use for the response
2222
* @property {Record<string,any>|URLSearchParams} [params] querystring parameters
@@ -26,16 +26,17 @@
2626
* @property {string} [xsrfCookieName] Pass an Cross-site Request Forgery prevention cookie value as a header defined by `xsrfHeaderName`
2727
* @property {string} [xsrfHeaderName] The name of a header to use for passing XSRF cookies
2828
* @property {(status: number) => boolean} [validateStatus] Override status code handling (default: 200-399 is a success)
29-
* @property {Array<(body: any, headers?: RequestHeaders) => any?>} [transformRequest] An array of transformations to apply to the outgoing request
29+
* @property {Array<(body: any, headers: Headers) => any?>} [transformRequest] An array of transformations to apply to the outgoing request
3030
* @property {string} [baseURL] a base URL from which to resolve all URLs
3131
* @property {typeof window.fetch} [fetch] Custom window.fetch implementation
32+
* @property {AbortSignal} [cancelToken] signal returned by AbortController
3233
* @property {any} [data]
3334
*/
3435

3536
/**
3637
* @public
37-
* @typedef RequestHeaders
38-
* @type {{[name: string]: string} | Headers}
38+
* @typedef Headers
39+
* @type {{[name: string]: string}}
3940
*/
4041

4142
/**
@@ -65,11 +66,17 @@
6566
*/
6667

6768
/**
68-
* @public
69-
* @param {Options} [defaults = {}]
70-
* @returns {redaxios}
69+
* @typedef CancelToken
70+
* @type {{ (executor: Function): AbortSignal; source(): { token: AbortSignal; cancel: () => void; }; }}
7171
*/
72-
function create(defaults) {
72+
73+
/**
74+
* @typedef CancelTokenSourceMethod
75+
* @type {() => { token: AbortSignal, cancel: () => void }}
76+
*/
77+
78+
/** */
79+
export default (function create(/** @type {Options} */ defaults) {
7380
defaults = defaults || {};
7481

7582
/**
@@ -109,21 +116,25 @@ function create(defaults) {
109116
* @param {(...args: Args[]) => R} fn
110117
* @returns {(array: Args[]) => R}
111118
*/
112-
redaxios.spread = (fn) => /** @type {any} */ (fn.apply.bind(fn, fn));
119+
redaxios.spread = function (fn) {
120+
return function (results) {
121+
return fn.apply(this, results);
122+
};
123+
};
124+
// 3b smaller:
125+
// redaxios.spread = (fn) => /** @type {any} */ (fn.apply.bind(fn, fn));
113126

114127
/**
115128
* @private
116-
* @template T, U
117-
* @param {T} opts
118-
* @param {U} [overrides]
129+
* @param {Record<string,any>} opts
130+
* @param {Record<string,any>} [overrides]
119131
* @param {boolean} [lowerCase]
120-
* @returns {{} & (T | U)}
132+
* @returns {Partial<opts>}
121133
*/
122134
function deepMerge(opts, overrides, lowerCase) {
123-
let out = /** @type {any} */ ({}),
135+
let out = {},
124136
i;
125137
if (Array.isArray(opts)) {
126-
// @ts-ignore
127138
return opts.concat(overrides);
128139
}
129140
for (i in opts) {
@@ -133,78 +144,113 @@ function create(defaults) {
133144
for (i in overrides) {
134145
const key = lowerCase ? i.toLowerCase() : i;
135146
const value = /** @type {any} */ (overrides)[i];
136-
out[key] = key in out && typeof value == 'object' ? deepMerge(out[key], value, key == 'headers') : value;
147+
out[key] = key in out && typeof value == 'object' ? deepMerge(out[key], value, key === 'headers') : value;
137148
}
138149
return out;
139150
}
140151

152+
/**
153+
* CancelToken
154+
* @private
155+
* @param {Function} executor
156+
* @returns {AbortSignal}
157+
*/
158+
function CancelToken(executor) {
159+
if (typeof executor !== 'function') {
160+
throw new TypeError('executor must be a function.');
161+
}
162+
163+
const ac = new AbortController();
164+
165+
executor(ac.abort.bind(ac));
166+
167+
return ac.signal;
168+
}
169+
170+
/**
171+
* @private
172+
* @type {CancelTokenSourceMethod}
173+
* @returns
174+
*/
175+
CancelToken.source = () => {
176+
const ac = new AbortController();
177+
178+
return {
179+
token: ac.signal,
180+
cancel: ac.abort.bind(ac)
181+
};
182+
};
183+
141184
/**
142185
* Issues a request.
143186
* @public
144187
* @template T
145-
* @param {string | Options} urlOrConfig
146-
* @param {Options} [config = {}]
147-
* @param {any} [_method] (internal)
148-
* @param {any} [data] (internal)
149-
* @param {never} [_undefined] (internal)
188+
* @param {string | Options} url
189+
* @param {Options} [config]
190+
* @param {any} [_method]
191+
* @param {any} [_data]
150192
* @returns {Promise<Response<T>>}
151193
*/
152-
function redaxios(urlOrConfig, config, _method, data, _undefined) {
153-
let url = /** @type {string} */ (typeof urlOrConfig != 'string' ? (config = urlOrConfig).url : urlOrConfig);
194+
function redaxios(url, config, _method, _data) {
195+
if (typeof url !== 'string') {
196+
config = url;
197+
url = config.url;
198+
}
154199

155200
const response = /** @type {Response<any>} */ ({ config });
156201

157202
/** @type {Options} */
158203
const options = deepMerge(defaults, config);
159204

160-
/** @type {RequestHeaders} */
205+
/** @type {Headers} */
161206
const customHeaders = {};
162207

163-
data = data || options.data;
208+
let data = _data || options.data;
164209

165210
(options.transformRequest || []).map((f) => {
166211
data = f(data, options.headers) || data;
167212
});
168213

169-
if (options.auth) {
170-
customHeaders.authorization = options.auth;
171-
}
172-
173-
if (data && typeof data === 'object' && typeof data.append !== 'function' && typeof data.text !== 'function') {
214+
if (data && typeof data === 'object' && typeof data.append !== 'function') {
174215
data = JSON.stringify(data);
175216
customHeaders['content-type'] = 'application/json';
176217
}
177218

178-
try {
179-
// @ts-ignore providing the cookie name without header name is nonsensical anyway
180-
customHeaders[options.xsrfHeaderName] = decodeURIComponent(
181-
// @ts-ignore accessing match()[2] throws for no match, which is intentional
182-
document.cookie.match(RegExp('(^|; )' + options.xsrfCookieName + '=([^;]*)'))[2]
183-
);
184-
} catch (e) {}
219+
const m =
220+
typeof document !== 'undefined' && document.cookie.match(RegExp('(^|; )' + options.xsrfCookieName + '=([^;]*)'));
221+
if (m) customHeaders[options.xsrfHeaderName] = m[2];
222+
223+
if (options.auth) {
224+
customHeaders.authorization = options.auth;
225+
}
185226

186227
if (options.baseURL) {
187-
url = url.replace(/^(?!.*\/\/)\/?/, options.baseURL + '/');
228+
url = url.replace(/^(?!.*\/\/)\/?(.*)$/, options.baseURL + '/$1');
188229
}
189230

190231
if (options.params) {
191-
url +=
192-
(~url.indexOf('?') ? '&' : '?') +
193-
(options.paramsSerializer ? options.paramsSerializer(options.params) : new URLSearchParams(options.params));
232+
const divider = ~url.indexOf('?') ? '&' : '?';
233+
const query = options.paramsSerializer
234+
? options.paramsSerializer(options.params)
235+
: new URLSearchParams(options.params);
236+
url += divider + query;
194237
}
195238

196239
const fetchFunc = options.fetch || fetch;
197240

198241
return fetchFunc(url, {
199-
method: (_method || options.method || 'get').toUpperCase(),
242+
method: _method || options.method,
200243
body: data,
201244
headers: deepMerge(options.headers, customHeaders, true),
202-
credentials: options.withCredentials ? 'include' : _undefined
245+
credentials: options.withCredentials ? 'include' : 'same-origin',
246+
signal: options.cancelToken
203247
}).then((res) => {
204248
for (const i in res) {
205249
if (typeof res[i] != 'function') response[i] = res[i];
206250
}
207251

252+
const ok = options.validateStatus ? options.validateStatus(res.status) : res.ok;
253+
208254
if (options.responseType == 'stream') {
209255
response.data = res.body;
210256
return response;
@@ -217,18 +263,22 @@ function create(defaults) {
217263
response.data = JSON.parse(data);
218264
})
219265
.catch(Object)
220-
.then(() => {
221-
const ok = options.validateStatus ? options.validateStatus(res.status) : res.ok;
222-
return ok ? response : Promise.reject(response);
223-
});
266+
.then(() => (ok ? response : Promise.reject(response)));
224267
});
225268
}
226269

227270
/**
228271
* @public
229-
* @type {AbortController}
272+
* @type {CancelToken}
273+
*/
274+
redaxios.CancelToken = CancelToken;
275+
276+
/**
277+
* @public
278+
* @param {DOMError} e
279+
* @returns {boolean}
230280
*/
231-
redaxios.CancelToken = /** @type {any} */ (typeof AbortController == 'function' ? AbortController : Object);
281+
redaxios.isCancel = (e) => e.name === 'AbortError';
232282

233283
/**
234284
* @public
@@ -242,6 +292,4 @@ function create(defaults) {
242292
redaxios.create = create;
243293

244294
return redaxios;
245-
}
246-
247-
export default create();
295+
})();

0 commit comments

Comments
 (0)