/**
- * Pick out useful axios response fields.
- * @param {AxiosResponse} res
+ * Pick out useful got response fields.
+ * @param {GotResponse} res
* @returns {Object}
*/
-const axiosResponseLogData = (res) => {
+const gotResponseLogData = (res) => {
const data = pick(res, [
- 'status',
- 'statusText',
+ 'statusCode',
+ 'statusMessage',
'headers',
- 'elapsedTimeMs',
- 'data',
+ 'body',
]);
- if (data.data) {
- data.data = logTruncate(data.data, 100);
+ if (typeof res.body === 'string') {
+ data.body = logTruncate(data.body, 100);
+ } else if (res.body instanceof Buffer) {
+ data.body = `<Buffer ${res.body.byteLength} bytes>`;
+ }
+ data.elapsedTimeMs = res?.timings?.phases?.total;
+ if (res?.redirectUrls?.length) {
+ data.redirectUrls = res.redirectUrls;
+ }
+ if (res?.retryCount) {
+ data.retryCount = res.retryCount;
}
return data;
};
}
-/**
- * Encodes single-level object as form data string.
- * @param {Object} data
- */
-const formData = (data) => {
- const formData = new URLSearchParams();
- Object.entries(data).forEach(([name, value]) => formData.set(name, value));
- return formData.toString();
-};
-
-
module.exports = {
fileScope,
- axiosResponseLogData,
+ gotResponseLogData,
logTruncate,
pick,
setSymmetricDifference,
properURLComponentName,
- formData,
};
\ No newline at end of file
'use strict';
-const axios = require('axios');
const { mf2 } = require('microformats-parser');
const { parse: parseLinkHeader } = require('@squeep/web-linking');
const { Iconv } = require('iconv');
const { version: packageVersion, name: packageName } = require('../package.json');
-const { performance } = require('perf_hooks');
const { randomBytes, createHash } = require('crypto');
const { promisify } = require('util');
const randomBytesAsync = promisify(randomBytes);
/**
* @param {Console} logger
* @param {Object} options
+ * @param {Number=} options.timeout
* @param {Object=} options.userAgent
* @param {String=} options.userAgent.product
* @param {String=} options.userAgent.version
constructor(logger, options = {}) {
this.logger = logger;
this.options = options;
- this.axios = axios.create({
- headers: {
- [Enum.Header.UserAgent]: Communication._userAgentString(options.userAgent),
- [Enum.Header.Accept]: 'text/html, text/*;q=0.9, application/xhtml+xml;q=0.8, application/xml;q=0.8, */*;q=0.1',
- },
- });
- this.axios.interceptors.request.use((request) => {
- request.startTimestampMs = performance.now();
- return request;
- });
- this.axios.interceptors.response.use((response) => {
- response.elapsedTimeMs = performance.now() - response.config.startTimestampMs;
- return response;
- });
+
+ this._defaultAccept = options?.defaultAccept || 'text/html, text/*;q=0.9, application/xhtml+xml;q=0.8, application/xml;q=0.8, */*;q=0.1';
+ this._jsonAccept = options?.jsonAccept || [Enum.ContentType.ApplicationJson, Enum.ContentType.Any + ';q=0.1'].join(', ');
+
+ this.Got = undefined;
+ this.got = this._init; // Do the dynamic import on first attempt to use client.
+ }
+
+
+ /**
+ * Do a little dance to support this ESM client.
+ */
+ async _init(...args) {
+ if (!this.Got) {
+ // For some reason eslint is confused about import being supported here.
+ // eslint-disable-next-line
+ this.Got = await import('got');
+ this.got = this.Got.got.extend({
+ headers: {
+ [Enum.Header.UserAgent]: Communication._userAgentString(this.options.userAgent),
+ [Enum.Header.Accept]: this._defaultAccept,
+ },
+ timeout: {
+ request: this.options.timeout || 120000,
+ },
+ hooks: {
+ beforeRetry: [
+ this._onRetry,
+ ],
+ },
+ });
+ }
+ if (args.length) {
+ return this.got(...args);
+ }
+ }
+
+
+ /**
+ * Take notes on transient retries.
+ * @param {*} error
+ * @param {*} retryCount
+ */
+ _onRetry(error, retryCount) {
+ const _scope = _fileScope('_onRetry');
+ this.logger.debug(_scope, 'retry', { retryCount, error });
}
}
- /**
- * Valid response statuses.
- * Allow 401 as a workaround for one specific client which return such on
- * its client identifier endpoint when not yet authenticated.
- * @param {Number} status
- * @returns {Boolean}
- */
- static _validateStatus(status) {
- return (status >= 200 && status < 300) || status == 401;
- }
-
-
- /**
- * A request config skeleton.
- * @param {String} method
- * @param {URL} urlObj
- * @param {String=} body
- * @param {Object=} params
- * @param {Object=} headers
- * @returns {Object}
- */
- static _axiosConfig(method, urlObj, body, params = {}, headers = {}) {
- const config = {
- method,
- url: `${urlObj.origin}${urlObj.pathname}`,
- params: urlObj.searchParams,
- headers,
- ...(body && { data: body }),
- // Setting this does not appear to be enough to keep axios from parsing JSON response into object
- responseType: 'text',
- // So force the matter by eliding all response transformations
- transformResponse: [ (res) => res ],
-
- validateStatus: Communication._validateStatus,
- };
- Object.entries(params).map(([k, v]) => config.params.set(k, v));
- return config;
- }
-
-
/**
* Isolate the base of a url.
* mf2 parser needs this so that relative links can be made absolute.
mediaType: mediaType.toLowerCase() || defaultContentType,
params: params.reduce((obj, param) => {
const [field, value] = param.split('=');
- const isQuoted = value && value.charAt(0) === '"' && value.charAt(value.length - 1) === '"';
+ const isQuoted = value?.startsWith('"') && value?.endsWith('"');
obj[field.toLowerCase()] = isQuoted ? value.slice(1, value.length - 1) : value;
return obj;
}, {}),
};
let response;
try {
- const fetchMicroformatConfig = Communication._axiosConfig('GET', urlObj);
- response = await this.axios(fetchMicroformatConfig);
+ const fetchMicroformatConfig = {
+ method: 'GET',
+ url: urlObj,
+ responseType: 'buffer',
+ };
+ response = await this.got(fetchMicroformatConfig);
} catch (e) {
this.logger.error(_scope, 'microformat request failed', { error: e, ...logInfoData });
return;
}
- logInfoData.response = common.axiosResponseLogData(response);
+ logInfoData.response = common.gotResponseLogData(response);
// Normalize to utf8.
- let body = response.data;
+ let body;
const contentType = Communication._parseContentType(response.headers[Enum.Header.ContentType.toLowerCase()]);
+ // If a charset was specified, and it's not utf8ish, attempt to transliterate it to utf8.
const nonUTF8Charset = !/utf-*8/i.test(contentType.params.charset) && contentType.params.charset;
if (nonUTF8Charset) {
try {
const iconv = new Iconv(nonUTF8Charset, 'utf-8//translit//ignore');
- body = iconv.convert(body).toString('utf8');
+ body = iconv.convert(response.body).toString('utf8');
} catch (e) {
// istanbul ignore next
this.logger.error(_scope, 'iconv conversion error', { error: e, ...logInfoData });
// Try to carry on, maybe the encoding will work anyhow...
}
+ } else {
+ body = response.body.toString('utf8');
}
let microformat = {};
};
let response;
try {
- const fetchJSONConfig = Communication._axiosConfig('GET', urlObj, undefined, undefined, {
- [Enum.Header.Accept]: [Enum.ContentType.ApplicationJson, Enum.ContentType.Any + ';q=0.1'].join(', '),
- });
- response = await this.axios(fetchJSONConfig);
+ const fetchJSONConfig = {
+ method: 'GET',
+ url: urlObj,
+ headers: {
+ [Enum.Header.Accept]: this._jsonAccept,
+ },
+ responseType: 'json',
+ };
+ response = await this.got(fetchJSONConfig);
} catch (e) {
this.logger.error(_scope, 'json request failed', { error: e, ...logInfoData });
return;
}
- logInfoData.response = common.axiosResponseLogData(response);
+ logInfoData.response = common.gotResponseLogData(response);
- let data;
- try {
- data = JSON.parse(response.data);
- } catch (e) {
- this.logger.error(_scope, 'json parsing failed', { error: e, ...logInfoData });
- }
-
- return data;
+ return response.body;
}
async redeemCode(urlObj, code, codeVerifier, clientId, redirectURI) {
const _scope = _fileScope('redeemCode');
- const formData = common.formData({
- 'grant_type': 'authorization_code',
- code,
- 'client_id': clientId,
- 'redirect_uri': redirectURI,
- 'code_verifier': codeVerifier,
- });
-
- const postRedeemCodeConfig = Communication._axiosConfig('POST', urlObj, formData, {}, {
- [Enum.Header.ContentType]: Enum.ContentType.ApplicationForm,
- [Enum.Header.Accept]: `${Enum.ContentType.ApplicationJson}, ${Enum.ContentType.Any};q=0.1`,
- });
+ const postRedeemCodeConfig = {
+ url: urlObj,
+ method: 'POST',
+ headers: {
+ [Enum.Header.Accept]: this._jsonAccept,
+ },
+ form: {
+ 'grant_type': 'authorization_code',
+ code,
+ 'client_id': clientId,
+ 'redirect_uri': redirectURI,
+ 'code_verifier': codeVerifier,
+ },
+ responseType: 'json',
+ };
try {
- const response = await this.axios(postRedeemCodeConfig);
- try {
- return JSON.parse(response.data);
- } catch (e) {
- this.logger.error(_scope, 'failed to parse json', { error: e, response });
- throw e;
- }
+ const response = await this.got(postRedeemCodeConfig);
+ return response.body;
} catch (e) {
this.logger.error(_scope, 'redeem code request failed', { error: e, url: urlObj.href });
return;
/**
- * Deprecated method name.
+ * Deprecated method name alias.
* @see redeemCode
* @param {URL} urlObj
* @param {String} code
async introspectToken(introspectionUrlObj, authorizationHeader, token) {
const _scope = _fileScope('introspectToken');
- const formData = common.formData({ token });
- const postIntrospectConfig = Communication._axiosConfig('POST', introspectionUrlObj, formData, {}, {
- [Enum.Header.Authorization]: authorizationHeader,
- [Enum.Header.ContentType]: Enum.ContentType.ApplicationForm,
- [Enum.Header.Accept]: `${Enum.ContentType.ApplicationJson}, ${Enum.ContentType.Any};q=0.1`,
- });
- delete postIntrospectConfig.validateStatus; // only accept success
+ const postIntrospectConfig = {
+ url: introspectionUrlObj,
+ method: 'POST',
+ headers: {
+ [Enum.Header.Authorization]: authorizationHeader,
+ [Enum.Header.Accept]: this._jsonAccept,
+ },
+ form: {
+ token,
+ },
+ responseType: 'json',
+ };
- let tokenInfo;
try {
- const response = await this.axios(postIntrospectConfig);
+ const response = await this.got(postIntrospectConfig);
this.logger.debug(_scope, 'response', { response });
// check status
try {
- tokenInfo = JSON.parse(response.data);
const {
active,
me,
scope,
exp,
iat,
- } = tokenInfo;
+ } = response.body;
+
+ if (![true, false].includes(active)) {
+ throw new RangeError('missing required response field "active"');
+ }
return {
active,
...(iat && { iat: Number(iat) }),
};
} catch (e) {
- this.logger.error(_scope, 'failed to parse json', { error: e, response });
+ this.logger.error(_scope, 'failed to parse json', { error: e, response: common.gotResponseLogData(response) });
throw e;
}
} catch (e) {
const _scope = _fileScope('deliverTicket');
try {
- const ticketPayload = {
- ticket,
- resource: resourceUrlObj.href,
- subject: subjectUrlObj.href,
+ const ticketConfig = {
+ method: 'POST',
+ url: ticketEndpointUrlObj,
+ form: {
+ ticket,
+ resource: resourceUrlObj.href,
+ subject: subjectUrlObj.href,
+ },
};
- const ticketConfig = Communication._axiosConfig('POST', ticketEndpointUrlObj, ticketPayload, {}, {
- [Enum.Header.ContentType]: Enum.ContentType.ApplicationForm,
- });
- return await this.axios(ticketConfig);
+ return await this.got(ticketConfig);
} catch (e) {
this.logger.error(_scope, 'ticket delivery request failed', { error: e, url: ticketEndpointUrlObj.href });
throw e;
"license": "ISC",
"dependencies": {
"@squeep/web-linking": "^1.0.7",
- "axios": "^1.4.0",
+ "got": "^13.0.0",
"iconv": "^3.0.1",
"ip-address": "^8.1.0",
"microformats-parser": "^1.4.1"
"node": ">= 8"
}
},
+ "node_modules/@sindresorhus/is": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.4.0.tgz",
+ "integrity": "sha512-Ggh6E9AnMpiNXlbXfFUcWE9qm408rL8jDi7+PMBBx7TMbwEmiqAiSmZ+zydYwxcJLqPGNDoLc9mXDuMDBZg0sA==",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
"node_modules/@sinonjs/commons": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
"node": ">=14.0"
}
},
+ "node_modules/@szmarczak/http-timer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
+ "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+ "dependencies": {
+ "defer-to-connect": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/@types/http-cache-semantics": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
+ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
+ },
"node_modules/acorn": {
"version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
- },
- "node_modules/axios": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
- "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
- "dependencies": {
- "follow-redirects": "^1.15.0",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
- }
- },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
+ "node_modules/cacheable-lookup": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
+ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==",
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/cacheable-request": {
+ "version": "10.2.10",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.10.tgz",
+ "integrity": "sha512-v6WB+Epm/qO4Hdlio/sfUn69r5Shgh39SsE9DSd4bIezP0mblOlObI+I0kUEM7J0JFc+I7pSeMeYaOYtX1N/VQ==",
+ "dependencies": {
+ "@types/http-cache-semantics": "^4.0.1",
+ "get-stream": "^6.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "keyv": "^4.5.2",
+ "mimic-response": "^4.0.0",
+ "normalize-url": "^8.0.0",
+ "responselike": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
"node_modules/caching-transform": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dependencies": {
- "delayed-stream": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"node": ">=0.10.0"
}
},
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "node_modules/defer-to-connect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
"engines": {
- "node": ">=0.4.0"
+ "node": ">=10"
}
},
"node_modules/diff": {
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
- "node_modules/follow-redirects": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
- "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/RubenVerborgh"
- }
- ],
- "engines": {
- "node": ">=4.0"
- },
- "peerDependenciesMeta": {
- "debug": {
- "optional": true
- }
- }
- },
"node_modules/foreground-child": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
"node": ">=8.0.0"
}
},
- "node_modules/form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- },
+ "node_modules/form-data-encoder": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",
+ "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==",
"engines": {
- "node": ">= 6"
+ "node": ">= 14.17"
}
},
"node_modules/fromentries": {
"node": ">=8.0.0"
}
},
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/got": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz",
+ "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==",
+ "dependencies": {
+ "@sindresorhus/is": "^5.2.0",
+ "@szmarczak/http-timer": "^5.0.1",
+ "cacheable-lookup": "^7.0.0",
+ "cacheable-request": "^10.2.8",
+ "decompress-response": "^6.0.0",
+ "form-data-encoder": "^2.1.2",
+ "get-stream": "^6.0.1",
+ "http2-wrapper": "^2.1.10",
+ "lowercase-keys": "^3.0.0",
+ "p-cancelable": "^3.0.0",
+ "responselike": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
+ "node_modules/http-cache-semantics": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+ },
+ "node_modules/http2-wrapper": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz",
+ "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
"node_modules/iconv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/iconv/-/iconv-3.0.1.tgz",
"node": ">=4"
}
},
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+ },
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==",
"dev": true
},
+ "node_modules/keyv": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz",
+ "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lowercase-keys": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"node": ">=10"
}
},
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "node_modules/mimic-response": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
+ "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==",
"engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dependencies": {
- "mime-db": "1.52.0"
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
- "engines": {
- "node": ">= 0.6"
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": {
"node": ">=0.10.0"
}
},
+ "node_modules/normalize-url": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
+ "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/nyc": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
"node": ">= 0.4.0"
}
},
+ "node_modules/p-cancelable": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"node": ">=8"
}
},
- "node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
"node_modules/pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
}
]
},
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/resolve-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+ },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"node": ">=4"
}
},
+ "node_modules/responselike": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
+ "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
+ "dependencies": {
+ "lowercase-keys": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"fastq": "^1.6.0"
}
},
+ "@sindresorhus/is": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.4.0.tgz",
+ "integrity": "sha512-Ggh6E9AnMpiNXlbXfFUcWE9qm408rL8jDi7+PMBBx7TMbwEmiqAiSmZ+zydYwxcJLqPGNDoLc9mXDuMDBZg0sA=="
+ },
"@sinonjs/commons": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/@squeep/web-linking/-/web-linking-1.0.7.tgz",
"integrity": "sha512-9d3QijrWc/WNE7p/K7NLUHbmPf92CURRqfzDLV0cGqYNA4QWAPfzwC8hxWpdUkUnep3KakvLKK60l0kEBMM3ag=="
},
+ "@szmarczak/http-timer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
+ "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+ "requires": {
+ "defer-to-connect": "^2.0.1"
+ }
+ },
+ "@types/http-cache-semantics": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
+ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
+ },
"acorn": {
"version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
- "asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
- },
- "axios": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
- "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
- "requires": {
- "follow-redirects": "^1.15.0",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
- }
- },
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
+ "cacheable-lookup": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
+ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w=="
+ },
+ "cacheable-request": {
+ "version": "10.2.10",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.10.tgz",
+ "integrity": "sha512-v6WB+Epm/qO4Hdlio/sfUn69r5Shgh39SsE9DSd4bIezP0mblOlObI+I0kUEM7J0JFc+I7pSeMeYaOYtX1N/VQ==",
+ "requires": {
+ "@types/http-cache-semantics": "^4.0.1",
+ "get-stream": "^6.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "keyv": "^4.5.2",
+ "mimic-response": "^4.0.0",
+ "normalize-url": "^8.0.0",
+ "responselike": "^3.0.0"
+ }
+ },
"caching-transform": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "requires": {
- "delayed-stream": "~1.0.0"
- }
- },
"commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"dev": true
},
+ "decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "requires": {
+ "mimic-response": "^3.1.0"
+ },
+ "dependencies": {
+ "mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+ }
+ }
+ },
"deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"strip-bom": "^4.0.0"
}
},
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+ "defer-to-connect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
},
"diff": {
"version": "5.0.0",
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
- "follow-redirects": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
- "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
- },
"foreground-child": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
"signal-exit": "^3.0.2"
}
},
- "form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- }
+ "form-data-encoder": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",
+ "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="
},
"fromentries": {
"version": "1.3.2",
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"dev": true
},
+ "get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+ },
"glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"type-fest": "^0.20.2"
}
},
+ "got": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz",
+ "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==",
+ "requires": {
+ "@sindresorhus/is": "^5.2.0",
+ "@szmarczak/http-timer": "^5.0.1",
+ "cacheable-lookup": "^7.0.0",
+ "cacheable-request": "^10.2.8",
+ "decompress-response": "^6.0.0",
+ "form-data-encoder": "^2.1.2",
+ "get-stream": "^6.0.1",
+ "http2-wrapper": "^2.1.10",
+ "lowercase-keys": "^3.0.0",
+ "p-cancelable": "^3.0.0",
+ "responselike": "^3.0.0"
+ }
+ },
"graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
+ "http-cache-semantics": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+ },
+ "http2-wrapper": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz",
+ "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==",
+ "requires": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.2.0"
+ }
+ },
"iconv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/iconv/-/iconv-3.0.1.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
+ "json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+ },
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==",
"dev": true
},
+ "keyv": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz",
+ "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==",
+ "requires": {
+ "json-buffer": "3.0.1"
+ }
+ },
"levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"is-unicode-supported": "^0.1.0"
}
},
+ "lowercase-keys": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="
+ },
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"parse5": "^6.0.0"
}
},
- "mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
- },
- "mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "requires": {
- "mime-db": "1.52.0"
- }
+ "mimic-response": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
+ "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="
},
"minimatch": {
"version": "3.1.2",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
},
+ "normalize-url": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
+ "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw=="
+ },
"nyc": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
"integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==",
"dev": true
},
+ "p-cancelable": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="
+ },
"p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"fromentries": "^1.2.0"
}
},
- "proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true
},
+ "quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
+ },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
+ "resolve-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+ },
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
+ "responselike": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
+ "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
+ "requires": {
+ "lowercase-keys": "^3.0.0"
+ }
+ },
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"license": "ISC",
"dependencies": {
"@squeep/web-linking": "^1.0.7",
- "axios": "^1.4.0",
+ "got": "^13.0.0",
"iconv": "^3.0.1",
"ip-address": "^8.1.0",
"microformats-parser": "^1.4.1"
});
}); // logTruncate
- describe('axiosResponseLogData', function () {
+ describe('gotResponseLogData', function () {
it('covers', function () {
const response = {
- status: 200,
- statusText: 'OK',
+ statusCode: 200,
+ statusMessage: 'OK',
headers: {
'Content-Type': 'text/plain',
},
otherData: 'blah',
- data: 'Old Mother West Wind had stopped to talk with the Slender Fir Tree. "I\'ve just come across the Green Meadows," said Old Mother West Wind, “and there I saw the Best Thing in the World.”',
+ timings: {
+ phases: {
+ total: 89,
+ },
+ },
+ retryCount: 0,
+ redirectUrls: [],
+ body: 'Old Mother West Wind had stopped to talk with the Slender Fir Tree. "I\'ve just come across the Green Meadows," said Old Mother West Wind, “and there I saw the Best Thing in the World.”',
+ };
+ const expected = {
+ statusCode: 200,
+ statusMessage: 'OK',
+ elapsedTimeMs: 89,
+ headers: {
+ 'Content-Type': 'text/plain',
+ },
+ body: 'Old Mother West Wind had stopped to talk with the Slender Fir Tree. "I\'ve just come across the Green... (184 bytes)',
+ };
+ const result = common.gotResponseLogData(response);
+ assert.deepStrictEqual(result, expected);
+ });
+ it('covers no body', function () {
+ const response = {
+ statusCode: 200,
+ statusMessage: 'OK',
+ headers: {
+ 'Content-Type': 'text/plain',
+ },
+ timings: {
+ phases: {
+ total: 89,
+ },
+ },
+ retryCount: 1,
};
const expected = {
- status: 200,
- statusText: 'OK',
+ statusCode: 200,
+ statusMessage: 'OK',
headers: {
'Content-Type': 'text/plain',
},
- data: 'Old Mother West Wind had stopped to talk with the Slender Fir Tree. "I\'ve just come across the Green... (184 bytes)',
+ elapsedTimeMs: 89,
+ retryCount: 1,
+ };
+ const result = common.gotResponseLogData(response);
+ assert.deepStrictEqual(result, expected);
+ });
+ it('covers json', function () {
+ const response = {
+ statusCode: 200,
+ statusMessage: 'OK',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ timings: {
+ phases: {
+ total: 89,
+ },
+ },
+ body: {
+ foo: 'bar',
+ },
+ redirectUrls: ['https://redirect.example.com/'],
+ };
+ const expected = {
+ statusCode: 200,
+ statusMessage: 'OK',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ elapsedTimeMs: 89,
+ body: {
+ foo: 'bar',
+ },
+ redirectUrls: ['https://redirect.example.com/'],
};
- const result = common.axiosResponseLogData(response);
+ const result = common.gotResponseLogData(response);
assert.deepStrictEqual(result, expected);
});
- it('covers no data', function () {
+ it('covers buffer', function () {
const response = {
- status: 200,
- statusText: 'OK',
+ statusCode: 200,
+ statusMessage: 'OK',
headers: {
'Content-Type': 'text/plain',
},
+ timings: {
+ phases: {
+ total: 89,
+ },
+ },
+ body: Buffer.from('ᘛ⁐̤ᕐᐷ'),
};
const expected = {
- status: 200,
- statusText: 'OK',
+ statusCode: 200,
+ statusMessage: 'OK',
headers: {
'Content-Type': 'text/plain',
},
+ elapsedTimeMs: 89,
+ body: '<Buffer 14 bytes>',
};
- const result = common.axiosResponseLogData(response);
+ const result = common.gotResponseLogData(response);
assert.deepStrictEqual(result, expected);
});
- }); // axiosResponseLogData
+ }); // gotResponseLogData
describe('setSymmetricDifference', function () {
it('covers difference', function () {
});
}); // properURLComponentName
- describe('formData', function () {
- it('covers', function () {
- const result = common.formData({
- key: 'value',
- foo: 'bar',
- });
- assert.strictEqual(result, 'key=value&foo=bar');
- });
- }); // formData
-
}); // common
\ No newline at end of file
describe('Communication', function () {
let communication, options;
- beforeEach(function () {
+ beforeEach(async function () {
options = {};
communication = new Communication(stubLogger, options);
+ await communication._init();
stubLogger._reset();
- sinon.stub(communication, 'axios');
+ sinon.stub(communication, 'got');
});
afterEach(function () {
sinon.restore();
communication = new Communication(stubLogger);
});
- describe('Axios timing coverage', function () {
- const request = {};
- const response = {
- config: request,
- };
- it('tags request', function () {
- communication.axios.interceptors.request.handlers[0].fulfilled(request);
- assert(request.startTimestampMs);
+ describe('_init', function () {
+ it('covers first use', async function () {
+ await communication._init({});
+ assert(communication.got.called);
});
- it('tags response', function () {
- communication.axios.interceptors.response.handlers[0].fulfilled(response);
- assert(response.elapsedTimeMs);
+ }); // _init
+
+ describe('_onRetry', function () {
+ it('covers', function () {
+ communication._onRetry(new Error('oh no'), 1);
+ assert(communication.logger.debug.called);
});
- }); // Axios timing coverage
+ }); // _onRetry
describe('_challengeFromVerifier', function () {
it('covers', function () {
});
}); // userAgentString
- describe('Axios Configurations', function () {
- let requestUrl, expectedUrl;
- beforeEach(function () {
- requestUrl = 'https://example.com/client_id';
- expectedUrl = 'https://example.com/client_id';
- });
- it('_axiosConfig', function () {
- const method = 'GET';
- const contentType = 'text/plain';
- const body = undefined;
- const params = {
- 'extra_parameter': 'foobar',
- };
- const urlObj = new URL(requestUrl);
- const expectedUrlObj = new URL(`${requestUrl}?extra_parameter=foobar`);
- const expected = {
- method,
- url: 'https://example.com/client_id',
- headers: {
- 'Content-Type': 'text/plain',
- },
- params: expectedUrlObj.searchParams,
- responseType: 'text',
- validateStatus: Communication._validateStatus,
- };
- const result = Communication._axiosConfig(method, urlObj, body, params, {
- 'Content-Type': contentType,
- });
- delete result.transformResponse;
- assert.deepStrictEqual(result, expected);
- });
- it('_axiosConfig covers defaults', function () {
- const method = 'OPTIONS';
- const urlObj = new URL(requestUrl);
- const expectedUrlObj = new URL(requestUrl);
- const expected = {
- method,
- url: expectedUrl,
- headers: {},
- params: expectedUrlObj.searchParams,
- responseType: 'text',
- validateStatus: Communication._validateStatus,
- };
- const result = Communication._axiosConfig(method, urlObj);
- delete result.transformResponse;
- assert.deepStrictEqual(result, expected);
- });
- it('covers data', function () {
- const method = 'POST';
- const body = Buffer.from('some data');
- const params = {};
- const urlObj = new URL(requestUrl);
- const expected = {
- method,
- url: 'https://example.com/client_id',
- data: body,
- headers: {},
- params: urlObj.searchParams,
- responseType: 'text',
- validateStatus: Communication._validateStatus,
- };
- const result = Communication._axiosConfig(method, urlObj, body, params, {});
- delete result.transformResponse;
- assert.deepStrictEqual(result, expected);
- });
- it('covers null response transform', function () {
- const urlObj = new URL(requestUrl);
- const result = Communication._axiosConfig('GET', urlObj, undefined, {}, {});
- result.transformResponse[0]();
- });
-
- describe('_validateStatus', function () {
- it('allows normal valid', function () {
- const result = Communication._validateStatus(200);
- assert.strictEqual(result, true);
- });
- it('allows unauthorized', function () {
- const result = Communication._validateStatus(401);
- assert.strictEqual(result, true);
- });
- it('rejects invalid', function () {
- const result = Communication._validateStatus(400);
- assert.strictEqual(result, false);
- });
- }); // _validateStatus
- }); // Axios Configurations
-
describe('_baseUrlString', function () {
it('covers no path', function () {
const urlObj = new URL('https://example.com');
headers: {
link: '<https://example.com/>; rel="self", <https://hub.example.com/>;rel="hub"',
},
- data: {},
+ body: {},
}
});
it('covers', function () {
urlObj = new URL('https://thuza.ratfeathers.com/');
response = {
headers: Object.assign({}, testData.linkHeaders),
- data: testData.hCardHtml,
+ body: Buffer.from(testData.hCardHtml),
};
});
it('covers', async function () {
- response.data = testData.hCardHtml;
- communication.axios.resolves(response);
+ response.body = testData.hCardHtml;
+ communication.got.resolves(response);
expected = {
rels: {
'authorization_endpoint': ['https://ia.squeep.com/auth'],
result = await communication.fetchMicroformat(urlObj);
assert.deepStrictEqual(result, expected);
});
- it('covers axios error', async function () {
- communication.axios.rejects(new Error('blah'));
+ it('covers got error', async function () {
+ communication.got.rejects(new Error('blah'));
expected = undefined;
result = await communication.fetchMicroformat(urlObj);
assert.deepStrictEqual(result, expected);
});
it('covers non-parsable content', async function () {
- response.data = 'some bare text';
+ response.body = 'some bare text';
response.headers = {};
- communication.axios.resolves(response);
+ communication.got.resolves(response);
expected = {
items: [],
rels: {},
});
it('covers non-utf8 content', async function () {
response.headers['content-type'] = 'text/html; charset=ASCII';
- communication.axios.resolves(response);
+ communication.got.resolves(response);
expected = {
rels: {
'authorization_endpoint': ['https://ia.squeep.com/auth'],
urlObj = new URL('https://thuza.ratfeathers.com/');
response = {
headers: Object.assign({}, testData.linkHeaders),
- data: testData.hCardHtml,
+ body: testData.hCardHtml,
};
});
it('covers', async function () {
- communication.axios.resolves(response);
+ communication.got.resolves(response);
expected = { foo: 'bar', baz: 123 };
- response.data = JSON.stringify(expected);
+ response.body = expected;
result = await communication.fetchJSON(urlObj);
assert.deepStrictEqual(result, expected);
});
- it('covers axios error', async function () {
- communication.axios.rejects(new Error('blah'));
+ it('covers got error', async function () {
+ communication.got.rejects(new Error('blah'));
expected = undefined;
result = await communication.fetchJSON(urlObj);
assert.deepStrictEqual(result, expected);
});
it('covers non-parsable content', async function () {
- response.data = 'some bare text';
+ response.body = 'some bare text';
response.headers = {};
- communication.axios.resolves(response);
+ const error = new Error('oh no');
+ response.request = { options: { url: new URL('https://example.com/') } };
+ communication.got.rejects(new communication.Got.ParseError(error, response));
expected = undefined;
result = await communication.fetchJSON(urlObj);
urlObj = new URL('https://thuza.ratfeathers.com/');
response = {
headers: {},
- data: testData.multiMF2Html,
+ body: testData.multiMF2Html,
};
});
it('covers', async function () {
- communication.axios.resolves(response);
+ communication.got.resolves(response);
expected = {
items: [{
properties: {
assert.deepStrictEqual(result, expected);
});
it('covers failed fetch', async function () {
- communication.axios.rejects();
+ communication.got.rejects();
expected = undefined;
result = await communication.fetchClientIdentifier(urlObj);
assert.deepStrictEqual(result, expected);
});
it('covers no h-app data', async function () {
- response.data = testData.noneMF2Html;
- communication.axios.resolves(response);
+ response.body = testData.noneMF2Html;
+ communication.got.resolves(response);
expected = {
items: [],
rels: {},
urlObj = new URL('https://thuza.ratfeathers.com/');
response = {
headers: {},
- data: testData.hCardHtml,
+ body: testData.hCardHtml,
};
sinon.stub(communication, 'fetchJSON');
});
describe('legacy without indieauth-metadata', function () {
it('covers', async function () {
- communication.axios.resolves(response);
+ communication.got.resolves(response);
expected = {
name: 'Thuza',
photo: 'https://thuza.ratfeathers.com/image.png',
assert.deepStrictEqual(result, expected);
});
it('covers multiple hCards', async function () {
- response.data = testData.multiMF2Html;
- communication.axios.resolves(response);
+ response.body = testData.multiMF2Html;
+ communication.got.resolves(response);
expected = {
email: undefined,
name: 'Thuza',
assert.deepStrictEqual(result, expected);
});
it('covers failed fetch', async function () {
- communication.axios.rejects();
+ communication.got.rejects();
expected = {
email: undefined,
name: undefined,
});
});
it('covers', async function () {
- response.data = testData.hCardMetadataHtml;
- communication.axios.resolves(response);
+ response.body = testData.hCardMetadataHtml;
+ communication.got.resolves(response);
communication.fetchJSON.resolves({
'issuer': 'https://ia.squeep.com/',
'authorization_endpoint': 'https://ia.squeep.com/auth',
assert.deepStrictEqual(result, expected);
});
it('covers metadata missing fields', async function () {
- response.data = testData.hCardMetadataHtml;
- communication.axios.resolves(response);
+ response.body = testData.hCardMetadataHtml;
+ communication.got.resolves(response);
communication.fetchJSON.resolves({
'issuer': 'https://ia.squeep.com/',
});
});
it('covers metadata response failure', async function () {
const jsonError = new Error('oh no');
- response.data = testData.hCardMetadataHtml;
- communication.axios
+ response.body = testData.hCardMetadataHtml;
+ communication.got
.onCall(0).resolves(response)
.onCall(1).rejects(jsonError);
communication.fetchJSON.restore();
let expected, urlObj, code, codeVerifier, clientId, redirectURI;
beforeEach(function () {
urlObj = new URL('https://example.com/auth');
- code = Buffer.allocUnsafe(42).toString('base64').replace('/', '_').replace('+', '-');
- codeVerifier = Buffer.allocUnsafe(42).toString('base64').replace('/', '_').replace('+', '-');
+ code = Buffer.allocUnsafe(42).toString('base64url');
+ codeVerifier = Buffer.allocUnsafe(42).toString('base64url');
clientId = 'https://example.com/';
redirectURI = 'https://example.com/_ia';
});
it('covers', async function () {
- communication.axios.resolves({
- data: '{"me":"https://profile.example.com/"}',
+ communication.got.resolves({
+ body: {
+ me: 'https://profile.example.com/',
+ },
});
expected = {
me: 'https://profile.example.com/',
assert.deepStrictEqual(result, expected);
});
it('covers deprecated method name', async function () {
- communication.axios.resolves({
- data: '{"me":"https://profile.example.com/"}',
+ communication.got.resolves({
+ body: {
+ me: 'https://profile.example.com/',
+ },
});
expected = {
me: 'https://profile.example.com/',
assert.deepStrictEqual(result, expected);
});
it('covers failure', async function () {
- communication.axios.resolves('Not a JSON payload.');
+ const error = new Error('oh no');
+ const response = {
+ request: {
+ options: {
+ url: new URL('https://example.com'),
+ },
+ },
+ };
+ const parseError = new communication.Got.ParseError(error, response);
+ communication.got.rejects(parseError);
const result = await communication.redeemCode(urlObj, code, codeVerifier, clientId, redirectURI);
});
it('covers success active', async function () {
const nowEpoch = Math.ceil(Date.now() / 1000);
- communication.axios.resolves({
- data: JSON.stringify({
+ communication.got.resolves({
+ body: {
active: true,
me: 'https://profile.example.com/',
'client_id': 'https://app.example.com/',
scope: 'create profile email',
exp: nowEpoch + 86400,
iat: nowEpoch,
- }),
+ },
});
const result = await communication.introspectToken(introspectionUrlObj, authenticationHeader, token);
assert.strictEqual(result.active, true);
});
it('covers success inactive', async function () {
- communication.axios.resolves({
- data: JSON.stringify({
+ communication.got.resolves({
+ body: {
active: false,
- }),
+ },
});
const result = await communication.introspectToken(introspectionUrlObj, authenticationHeader, token);
assert.strictEqual(result.active, false);
});
it('covers failure', async function () {
- communication.axios.resolves('what kind of response is this?');
+ communication.got.resolves({ body: 'what kind of response is this?' });
await assert.rejects(() => communication.introspectToken(introspectionUrlObj, authenticationHeader, token));
});
}); // introspectToken
ticket = 'XXXThisIsATicketXXX';
});
it('covers success', async function () {
- const expected = { data: 'blah', statusCode: 200 };
- communication.axios.resolves(expected);
+ const expected = { body: 'blah', statusCode: 200 };
+ communication.got.resolves(expected);
const result = await communication.deliverTicket(ticketEndpointUrlObj, resourceUrlObj, subjectUrlObj, ticket);
assert.deepStrictEqual(result, expected);
});
it('covers failure', async function () {
const expectedException = new Error('oh no');
- communication.axios.rejects(expectedException);
+ communication.got.rejects(expectedException);
await assert.rejects(() => communication.deliverTicket(ticketEndpointUrlObj, resourceUrlObj, subjectUrlObj, ticket), expectedException);
});
}); // deliverTicket