middleware.browser.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. import debugIt from "debug";
  2. import { processOptions, validateOptions } from "./_chunks/defaultOptionsValidator-DdN0wke7.js";
  3. import { isPlainObject } from "is-plain-object";
  4. function agent(opts) {
  5. return {};
  6. }
  7. const leadingSlash = /^\//, trailingSlash = /\/$/;
  8. function base(baseUrl) {
  9. const baseUri = baseUrl.replace(trailingSlash, "");
  10. return {
  11. processOptions: (options) => {
  12. if (/^https?:\/\//i.test(options.url))
  13. return options;
  14. const url = [baseUri, options.url.replace(leadingSlash, "")].join("/");
  15. return Object.assign({}, options, { url });
  16. }
  17. };
  18. }
  19. const SENSITIVE_HEADERS = ["cookie", "authorization"], hasOwn = Object.prototype.hasOwnProperty, redactKeys = (source, redacted) => {
  20. const target = {};
  21. for (const key in source)
  22. hasOwn.call(source, key) && (target[key] = redacted.indexOf(key.toLowerCase()) > -1 ? "<redacted>" : source[key]);
  23. return target;
  24. };
  25. function debug(opts = {}) {
  26. const verbose = opts.verbose, namespace = opts.namespace || "get-it", defaultLogger = debugIt(namespace), log = opts.log || defaultLogger, shortCircuit = log === defaultLogger && !debugIt.enabled(namespace);
  27. let requestId = 0;
  28. return {
  29. processOptions: (options) => (options.debug = log, options.requestId = options.requestId || ++requestId, options),
  30. onRequest: (event) => {
  31. if (shortCircuit || !event)
  32. return event;
  33. const options = event.options;
  34. if (log("[%s] HTTP %s %s", options.requestId, options.method, options.url), verbose && options.body && typeof options.body == "string" && log("[%s] Request body: %s", options.requestId, options.body), verbose && options.headers) {
  35. const headers2 = opts.redactSensitiveHeaders === !1 ? options.headers : redactKeys(options.headers, SENSITIVE_HEADERS);
  36. log("[%s] Request headers: %s", options.requestId, JSON.stringify(headers2, null, 2));
  37. }
  38. return event;
  39. },
  40. onResponse: (res, context) => {
  41. if (shortCircuit || !res)
  42. return res;
  43. const reqId = context.options.requestId;
  44. return log("[%s] Response code: %s %s", reqId, res.statusCode, res.statusMessage), verbose && res.body && log("[%s] Response body: %s", reqId, stringifyBody(res)), res;
  45. },
  46. onError: (err, context) => {
  47. const reqId = context.options.requestId;
  48. return err ? (log("[%s] ERROR: %s", reqId, err.message), err) : (log("[%s] Error encountered, but handled by an earlier middleware", reqId), err);
  49. }
  50. };
  51. }
  52. function stringifyBody(res) {
  53. return (res.headers["content-type"] || "").toLowerCase().indexOf("application/json") !== -1 ? tryFormat(res.body) : res.body;
  54. }
  55. function tryFormat(body) {
  56. try {
  57. const parsed = typeof body == "string" ? JSON.parse(body) : body;
  58. return JSON.stringify(parsed, null, 2);
  59. } catch {
  60. return body;
  61. }
  62. }
  63. function headers(_headers, opts = {}) {
  64. return {
  65. processOptions: (options) => {
  66. const existing = options.headers || {};
  67. return options.headers = opts.override ? Object.assign({}, existing, _headers) : Object.assign({}, _headers, existing), options;
  68. }
  69. };
  70. }
  71. class HttpError extends Error {
  72. constructor(res, ctx) {
  73. super();
  74. const truncatedUrl = res.url.length > 400 ? `${res.url.slice(0, 399)}\u2026` : res.url;
  75. let msg = `${res.method}-request to ${truncatedUrl} resulted in `;
  76. msg += `HTTP ${res.statusCode} ${res.statusMessage}`, this.message = msg.trim(), this.response = res, this.request = ctx.options;
  77. }
  78. }
  79. function httpErrors() {
  80. return {
  81. onResponse: (res, ctx) => {
  82. if (!(res.statusCode >= 400))
  83. return res;
  84. throw new HttpError(res, ctx);
  85. }
  86. };
  87. }
  88. function injectResponse(opts = {}) {
  89. if (typeof opts.inject != "function")
  90. throw new Error("`injectResponse` middleware requires a `inject` function");
  91. return { interceptRequest: function(prevValue, event) {
  92. const response = opts.inject(event, prevValue);
  93. if (!response)
  94. return prevValue;
  95. const options = event.context.options;
  96. return {
  97. body: "",
  98. url: options.url,
  99. method: options.method,
  100. headers: {},
  101. statusCode: 200,
  102. statusMessage: "OK",
  103. ...response
  104. };
  105. } };
  106. }
  107. const isBuffer = typeof Buffer > "u" ? () => !1 : (obj) => Buffer.isBuffer(obj), serializeTypes = ["boolean", "string", "number"];
  108. function jsonRequest() {
  109. return {
  110. processOptions: (options) => {
  111. const body = options.body;
  112. return !body || !(typeof body.pipe != "function" && !isBuffer(body) && (serializeTypes.indexOf(typeof body) !== -1 || Array.isArray(body) || isPlainObject(body))) ? options : Object.assign({}, options, {
  113. body: JSON.stringify(options.body),
  114. headers: Object.assign({}, options.headers, {
  115. "Content-Type": "application/json"
  116. })
  117. });
  118. }
  119. };
  120. }
  121. function jsonResponse(opts) {
  122. return {
  123. onResponse: (response) => {
  124. const contentType = response.headers["content-type"] || "", shouldDecode = opts && opts.force || contentType.indexOf("application/json") !== -1;
  125. return !response.body || !contentType || !shouldDecode ? response : Object.assign({}, response, { body: tryParse(response.body) });
  126. },
  127. processOptions: (options) => Object.assign({}, options, {
  128. headers: Object.assign({ Accept: "application/json" }, options.headers)
  129. })
  130. };
  131. function tryParse(body) {
  132. try {
  133. return JSON.parse(body);
  134. } catch (err) {
  135. throw err.message = `Failed to parsed response body as JSON: ${err.message}`, err;
  136. }
  137. }
  138. }
  139. function isBrowserOptions(options) {
  140. return typeof options == "object" && options !== null && !("protocol" in options);
  141. }
  142. function mtls(config = {}) {
  143. if (!config.ca)
  144. throw new Error('Required mtls option "ca" is missing');
  145. if (!config.cert)
  146. throw new Error('Required mtls option "cert" is missing');
  147. if (!config.key)
  148. throw new Error('Required mtls option "key" is missing');
  149. return {
  150. finalizeOptions: (options) => {
  151. if (isBrowserOptions(options))
  152. return options;
  153. const mtlsOpts = {
  154. cert: config.cert,
  155. key: config.key,
  156. ca: config.ca
  157. };
  158. return Object.assign({}, options, mtlsOpts);
  159. }
  160. };
  161. }
  162. let actualGlobal = {};
  163. typeof globalThis < "u" ? actualGlobal = globalThis : typeof window < "u" ? actualGlobal = window : typeof global < "u" ? actualGlobal = global : typeof self < "u" && (actualGlobal = self);
  164. var global$1 = actualGlobal;
  165. function observable(opts = {}) {
  166. const Observable = (
  167. // eslint-disable-next-line @typescript-eslint/no-explicit-any -- @TODO consider dropping checking for a global Observable since it's not on a standards track
  168. opts.implementation || global$1.Observable
  169. );
  170. if (!Observable)
  171. throw new Error(
  172. "`Observable` is not available in global scope, and no implementation was passed"
  173. );
  174. return {
  175. onReturn: (channels, context) => new Observable((observer) => (channels.error.subscribe((err) => observer.error(err)), channels.progress.subscribe(
  176. (event) => observer.next(Object.assign({ type: "progress" }, event))
  177. ), channels.response.subscribe((response) => {
  178. observer.next(Object.assign({ type: "response" }, response)), observer.complete();
  179. }), channels.request.publish(context), () => channels.abort.publish()))
  180. };
  181. }
  182. function progress() {
  183. return {
  184. onRequest: (evt) => {
  185. if (evt.adapter !== "xhr")
  186. return;
  187. const xhr = evt.request, context = evt.context;
  188. "upload" in xhr && "onprogress" in xhr.upload && (xhr.upload.onprogress = handleProgress("upload")), "onprogress" in xhr && (xhr.onprogress = handleProgress("download"));
  189. function handleProgress(stage) {
  190. return (event) => {
  191. const percent = event.lengthComputable ? event.loaded / event.total * 100 : -1;
  192. context.channels.progress.publish({
  193. stage,
  194. percent,
  195. total: event.total,
  196. loaded: event.loaded,
  197. lengthComputable: event.lengthComputable
  198. });
  199. };
  200. }
  201. }
  202. };
  203. }
  204. const promise = (options = {}) => {
  205. const PromiseImplementation = options.implementation || Promise;
  206. if (!PromiseImplementation)
  207. throw new Error("`Promise` is not available in global scope, and no implementation was passed");
  208. return {
  209. onReturn: (channels, context) => new PromiseImplementation((resolve, reject) => {
  210. const cancel = context.options.cancelToken;
  211. cancel && cancel.promise.then((reason) => {
  212. channels.abort.publish(reason), reject(reason);
  213. }), channels.error.subscribe(reject), channels.response.subscribe((response) => {
  214. resolve(options.onlyBody ? response.body : response);
  215. }), setTimeout(() => {
  216. try {
  217. channels.request.publish(context);
  218. } catch (err) {
  219. reject(err);
  220. }
  221. }, 0);
  222. })
  223. };
  224. };
  225. class Cancel {
  226. constructor(message) {
  227. this.__CANCEL__ = !0, this.message = message;
  228. }
  229. toString() {
  230. return `Cancel${this.message ? `: ${this.message}` : ""}`;
  231. }
  232. }
  233. const _CancelToken = class {
  234. constructor(executor) {
  235. if (typeof executor != "function")
  236. throw new TypeError("executor must be a function.");
  237. let resolvePromise = null;
  238. this.promise = new Promise((resolve) => {
  239. resolvePromise = resolve;
  240. }), executor((message) => {
  241. this.reason || (this.reason = new Cancel(message), resolvePromise(this.reason));
  242. });
  243. }
  244. };
  245. _CancelToken.source = () => {
  246. let cancel;
  247. return {
  248. token: new _CancelToken((can) => {
  249. cancel = can;
  250. }),
  251. cancel
  252. };
  253. };
  254. let CancelToken = _CancelToken;
  255. const isCancel = (value) => !!(value && value != null && value.__CANCEL__);
  256. promise.Cancel = Cancel;
  257. promise.CancelToken = CancelToken;
  258. promise.isCancel = isCancel;
  259. function proxy(_proxy) {
  260. if (_proxy !== !1 && (!_proxy || !_proxy.host))
  261. throw new Error("Proxy middleware takes an object of host, port and auth properties");
  262. return {
  263. processOptions: (options) => Object.assign({ proxy: _proxy }, options)
  264. };
  265. }
  266. var defaultShouldRetry = (err, attempt, options) => options.method !== "GET" && options.method !== "HEAD" ? !1 : err.isNetworkError || !1;
  267. const isStream = (stream) => stream !== null && typeof stream == "object" && typeof stream.pipe == "function";
  268. var sharedRetry = (opts) => {
  269. const maxRetries = opts.maxRetries || 5, retryDelay = opts.retryDelay || getRetryDelay, allowRetry = opts.shouldRetry;
  270. return {
  271. onError: (err, context) => {
  272. const options = context.options, max = options.maxRetries || maxRetries, shouldRetry = options.shouldRetry || allowRetry, attemptNumber = options.attemptNumber || 0;
  273. if (isStream(options.body) || !shouldRetry(err, attemptNumber, options) || attemptNumber >= max)
  274. return err;
  275. const newContext = Object.assign({}, context, {
  276. options: Object.assign({}, options, { attemptNumber: attemptNumber + 1 })
  277. });
  278. return setTimeout(() => context.channels.request.publish(newContext), retryDelay(attemptNumber)), null;
  279. }
  280. };
  281. };
  282. function getRetryDelay(attemptNum) {
  283. return 100 * Math.pow(2, attemptNum) + Math.random() * 100;
  284. }
  285. const retry = (opts = {}) => sharedRetry({ shouldRetry: defaultShouldRetry, ...opts });
  286. retry.shouldRetry = defaultShouldRetry;
  287. function encode(data) {
  288. const query = new URLSearchParams(), nest = (name, _value) => {
  289. const value = _value instanceof Set ? Array.from(_value) : _value;
  290. if (Array.isArray(value))
  291. if (value.length)
  292. for (const index in value)
  293. nest(`${name}[${index}]`, value[index]);
  294. else
  295. query.append(`${name}[]`, "");
  296. else if (typeof value == "object" && value !== null)
  297. for (const [key, obj] of Object.entries(value))
  298. nest(`${name}[${key}]`, obj);
  299. else
  300. query.append(name, value);
  301. };
  302. for (const [key, value] of Object.entries(data))
  303. nest(key, value);
  304. return query.toString();
  305. }
  306. function urlEncoded() {
  307. return {
  308. processOptions: (options) => {
  309. const body = options.body;
  310. return !body || !(typeof body.pipe != "function" && !isBuffer(body) && isPlainObject(body)) ? options : {
  311. ...options,
  312. body: encode(options.body),
  313. headers: {
  314. ...options.headers,
  315. "Content-Type": "application/x-www-form-urlencoded"
  316. }
  317. };
  318. }
  319. };
  320. }
  321. function buildKeepAlive(agent2) {
  322. return function(config = {}) {
  323. const ms = config.ms || 1e3, maxFree = config.maxFree || 256;
  324. return agent2({
  325. keepAlive: !0,
  326. keepAliveMsecs: ms,
  327. maxFreeSockets: maxFree
  328. });
  329. };
  330. }
  331. const keepAlive = buildKeepAlive(agent);
  332. export {
  333. Cancel,
  334. CancelToken,
  335. agent,
  336. base,
  337. debug,
  338. headers,
  339. httpErrors,
  340. injectResponse,
  341. jsonRequest,
  342. jsonResponse,
  343. keepAlive,
  344. mtls,
  345. observable,
  346. processOptions,
  347. progress,
  348. promise,
  349. proxy,
  350. retry,
  351. urlEncoded,
  352. validateOptions
  353. };
  354. //# sourceMappingURL=middleware.browser.js.map