summaryrefslogtreecommitdiff
path: root/includes/external/addressbook/node_modules/cacheable-request/dist/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'includes/external/addressbook/node_modules/cacheable-request/dist/index.js')
-rw-r--r--includes/external/addressbook/node_modules/cacheable-request/dist/index.js275
1 files changed, 275 insertions, 0 deletions
diff --git a/includes/external/addressbook/node_modules/cacheable-request/dist/index.js b/includes/external/addressbook/node_modules/cacheable-request/dist/index.js
new file mode 100644
index 0000000..3ab3ccc
--- /dev/null
+++ b/includes/external/addressbook/node_modules/cacheable-request/dist/index.js
@@ -0,0 +1,275 @@
+import EventEmitter from 'node:events';
+import urlLib from 'node:url';
+import crypto from 'node:crypto';
+import stream, { PassThrough as PassThroughStream } from 'node:stream';
+import normalizeUrl from 'normalize-url';
+import getStream from 'get-stream';
+import CachePolicy from 'http-cache-semantics';
+import Response from 'responselike';
+import Keyv from 'keyv';
+import mimicResponse from 'mimic-response';
+import { CacheError, RequestError } from './types.js';
+class CacheableRequest {
+ constructor(cacheRequest, cacheAdapter) {
+ this.hooks = new Map();
+ this.request = () => (options, cb) => {
+ let url;
+ if (typeof options === 'string') {
+ url = normalizeUrlObject(urlLib.parse(options));
+ options = {};
+ }
+ else if (options instanceof urlLib.URL) {
+ url = normalizeUrlObject(urlLib.parse(options.toString()));
+ options = {};
+ }
+ else {
+ const [pathname, ...searchParts] = (options.path ?? '').split('?');
+ const search = searchParts.length > 0
+ ? `?${searchParts.join('?')}`
+ : '';
+ url = normalizeUrlObject({ ...options, pathname, search });
+ }
+ options = {
+ headers: {},
+ method: 'GET',
+ cache: true,
+ strictTtl: false,
+ automaticFailover: false,
+ ...options,
+ ...urlObjectToRequestOptions(url),
+ };
+ options.headers = Object.fromEntries(entries(options.headers).map(([key, value]) => [key.toLowerCase(), value]));
+ const ee = new EventEmitter();
+ const normalizedUrlString = normalizeUrl(urlLib.format(url), {
+ stripWWW: false,
+ removeTrailingSlash: false,
+ stripAuthentication: false,
+ });
+ let key = `${options.method}:${normalizedUrlString}`;
+ // POST, PATCH, and PUT requests may be cached, depending on the response
+ // cache-control headers. As a result, the body of the request should be
+ // added to the cache key in order to avoid collisions.
+ if (options.body && options.method !== undefined && ['POST', 'PATCH', 'PUT'].includes(options.method)) {
+ if (options.body instanceof stream.Readable) {
+ // Streamed bodies should completely skip the cache because they may
+ // or may not be hashable and in either case the stream would need to
+ // close before the cache key could be generated.
+ options.cache = false;
+ }
+ else {
+ key += `:${crypto.createHash('md5').update(options.body).digest('hex')}`;
+ }
+ }
+ let revalidate = false;
+ let madeRequest = false;
+ const makeRequest = (options_) => {
+ madeRequest = true;
+ let requestErrored = false;
+ let requestErrorCallback = () => { };
+ const requestErrorPromise = new Promise(resolve => {
+ requestErrorCallback = () => {
+ if (!requestErrored) {
+ requestErrored = true;
+ resolve();
+ }
+ };
+ });
+ const handler = async (response) => {
+ if (revalidate) {
+ response.status = response.statusCode;
+ const revalidatedPolicy = CachePolicy.fromObject(revalidate.cachePolicy).revalidatedPolicy(options_, response);
+ if (!revalidatedPolicy.modified) {
+ response.resume();
+ await new Promise(resolve => {
+ // Skipping 'error' handler cause 'error' event should't be emitted for 304 response
+ response
+ .once('end', resolve);
+ });
+ const headers = convertHeaders(revalidatedPolicy.policy.responseHeaders());
+ response = new Response({ statusCode: revalidate.statusCode, headers, body: revalidate.body, url: revalidate.url });
+ response.cachePolicy = revalidatedPolicy.policy;
+ response.fromCache = true;
+ }
+ }
+ if (!response.fromCache) {
+ response.cachePolicy = new CachePolicy(options_, response, options_);
+ response.fromCache = false;
+ }
+ let clonedResponse;
+ if (options_.cache && response.cachePolicy.storable()) {
+ clonedResponse = cloneResponse(response);
+ (async () => {
+ try {
+ const bodyPromise = getStream.buffer(response);
+ await Promise.race([
+ requestErrorPromise,
+ new Promise(resolve => response.once('end', resolve)),
+ new Promise(resolve => response.once('close', resolve)), // eslint-disable-line no-promise-executor-return
+ ]);
+ const body = await bodyPromise;
+ let value = {
+ url: response.url,
+ statusCode: response.fromCache ? revalidate.statusCode : response.statusCode,
+ body,
+ cachePolicy: response.cachePolicy.toObject(),
+ };
+ let ttl = options_.strictTtl ? response.cachePolicy.timeToLive() : undefined;
+ if (options_.maxTtl) {
+ ttl = ttl ? Math.min(ttl, options_.maxTtl) : options_.maxTtl;
+ }
+ if (this.hooks.size > 0) {
+ /* eslint-disable no-await-in-loop */
+ for (const key_ of this.hooks.keys()) {
+ value = await this.runHook(key_, value, response);
+ }
+ /* eslint-enable no-await-in-loop */
+ }
+ await this.cache.set(key, value, ttl);
+ }
+ catch (error) {
+ ee.emit('error', new CacheError(error));
+ }
+ })();
+ }
+ else if (options_.cache && revalidate) {
+ (async () => {
+ try {
+ await this.cache.delete(key);
+ }
+ catch (error) {
+ ee.emit('error', new CacheError(error));
+ }
+ })();
+ }
+ ee.emit('response', clonedResponse ?? response);
+ if (typeof cb === 'function') {
+ cb(clonedResponse ?? response);
+ }
+ };
+ try {
+ const request_ = this.cacheRequest(options_, handler);
+ request_.once('error', requestErrorCallback);
+ request_.once('abort', requestErrorCallback);
+ request_.once('destroy', requestErrorCallback);
+ ee.emit('request', request_);
+ }
+ catch (error) {
+ ee.emit('error', new RequestError(error));
+ }
+ };
+ (async () => {
+ const get = async (options_) => {
+ await Promise.resolve();
+ const cacheEntry = options_.cache ? await this.cache.get(key) : undefined;
+ if (typeof cacheEntry === 'undefined' && !options_.forceRefresh) {
+ makeRequest(options_);
+ return;
+ }
+ const policy = CachePolicy.fromObject(cacheEntry.cachePolicy);
+ if (policy.satisfiesWithoutRevalidation(options_) && !options_.forceRefresh) {
+ const headers = convertHeaders(policy.responseHeaders());
+ const response = new Response({ statusCode: cacheEntry.statusCode, headers, body: cacheEntry.body, url: cacheEntry.url });
+ response.cachePolicy = policy;
+ response.fromCache = true;
+ ee.emit('response', response);
+ if (typeof cb === 'function') {
+ cb(response);
+ }
+ }
+ else if (policy.satisfiesWithoutRevalidation(options_) && Date.now() >= policy.timeToLive() && options_.forceRefresh) {
+ await this.cache.delete(key);
+ options_.headers = policy.revalidationHeaders(options_);
+ makeRequest(options_);
+ }
+ else {
+ revalidate = cacheEntry;
+ options_.headers = policy.revalidationHeaders(options_);
+ makeRequest(options_);
+ }
+ };
+ const errorHandler = (error) => ee.emit('error', new CacheError(error));
+ if (this.cache instanceof Keyv) {
+ const cachek = this.cache;
+ cachek.once('error', errorHandler);
+ ee.on('error', () => cachek.removeListener('error', errorHandler));
+ ee.on('response', () => cachek.removeListener('error', errorHandler));
+ }
+ try {
+ await get(options);
+ }
+ catch (error) {
+ if (options.automaticFailover && !madeRequest) {
+ makeRequest(options);
+ }
+ ee.emit('error', new CacheError(error));
+ }
+ })();
+ return ee;
+ };
+ this.addHook = (name, fn) => {
+ if (!this.hooks.has(name)) {
+ this.hooks.set(name, fn);
+ }
+ };
+ this.removeHook = (name) => this.hooks.delete(name);
+ this.getHook = (name) => this.hooks.get(name);
+ this.runHook = async (name, ...args) => this.hooks.get(name)?.(...args);
+ if (cacheAdapter instanceof Keyv) {
+ this.cache = cacheAdapter;
+ }
+ else if (typeof cacheAdapter === 'string') {
+ this.cache = new Keyv({
+ uri: cacheAdapter,
+ namespace: 'cacheable-request',
+ });
+ }
+ else {
+ this.cache = new Keyv({
+ store: cacheAdapter,
+ namespace: 'cacheable-request',
+ });
+ }
+ this.request = this.request.bind(this);
+ this.cacheRequest = cacheRequest;
+ }
+}
+const entries = Object.entries;
+const cloneResponse = (response) => {
+ const clone = new PassThroughStream({ autoDestroy: false });
+ mimicResponse(response, clone);
+ return response.pipe(clone);
+};
+const urlObjectToRequestOptions = (url) => {
+ const options = { ...url };
+ options.path = `${url.pathname || '/'}${url.search || ''}`;
+ delete options.pathname;
+ delete options.search;
+ return options;
+};
+const normalizeUrlObject = (url) =>
+// If url was parsed by url.parse or new URL:
+// - hostname will be set
+// - host will be hostname[:port]
+// - port will be set if it was explicit in the parsed string
+// Otherwise, url was from request options:
+// - hostname or host may be set
+// - host shall not have port encoded
+({
+ protocol: url.protocol,
+ auth: url.auth,
+ hostname: url.hostname || url.host || 'localhost',
+ port: url.port,
+ pathname: url.pathname,
+ search: url.search,
+});
+const convertHeaders = (headers) => {
+ const result = [];
+ for (const name of Object.keys(headers)) {
+ result[name.toLowerCase()] = headers[name];
+ }
+ return result;
+};
+export default CacheableRequest;
+export * from './types.js';
+export const onResponse = 'onResponse';
+//# sourceMappingURL=index.js.map \ No newline at end of file