UNPKG

@hasan-akbari/advanced-http-client

Version:

Advanced Angular HttpClient with cache, inflight dedup, rate limit, debounce, queueing, batching, retry/backoff, timeout, logging.

138 lines 29.1 kB
import { Injectable } from '@angular/core'; import { of, throwError, timer } from 'rxjs'; import { catchError, finalize, retryWhen, scan, shareReplay, switchMap, timeout, tap } from 'rxjs/operators'; import { MemoryCache, stableStringify } from './request-cache'; import { RequestQueue } from './request-queue'; import { RequestBatcher } from './request-batcher'; import { toHeaders, toParams, backoffDelay } from './utils'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common/http"; export class AdvancedHttpClientService { constructor(http) { this.http = http; this.cache = new MemoryCache(); this.inflight = new Map(); this.lastSent = new Map(); this.queue = new RequestQueue(4); this.batcher = new RequestBatcher(); this.cleanupHandle = setInterval(() => this.cache.cleanup(), 60000); } send(endpoint, payload, options = {}) { const method = options.method ?? 'GET'; const body = options.body ?? (method === 'GET' || method === 'HEAD' || method === 'OPTIONS' ? undefined : payload); const paramsObj = options.params ?? {}; const params = toParams(paramsObj); const headers = toHeaders(options.headers); const cacheKey = `${method} ${endpoint} :: ${stableStringify({ params: paramsObj, body })}`; const raw = !!options.raw; const inflightKey = !raw ? (options.batch?.enabled ? `${cacheKey} :: payload=${stableStringify(payload)}` : cacheKey) : undefined; if (options.cacheDurationMs && options.cacheDurationMs > 0) { const c = this.cache.get(cacheKey); if (c !== undefined) return of(c); } if (!raw) { const ex = this.inflight.get(inflightKey); if (ex) return ex; } const logEnabled = !!options.log?.enabled; const logLevel = options.log?.level ?? 'basic'; const log = logEnabled ? { key: inflightKey ?? cacheKey, method, endpoint, startedAt: Date.now() } : undefined; let stream = this.http.request(method, endpoint, { body, headers, params, observe: 'body', responseType: 'json' }); if (options.timeoutMs) { stream = stream.pipe(timeout(options.timeoutMs)); } if (options.retry?.attempts) { stream = stream.pipe(retryWhen(errs => errs.pipe(scan((acc, err) => { const attempts = options.retry?.attempts ?? 0; const should = options.retry?.shouldRetry?.(err) ?? true; if (!should || acc >= attempts) throw err; return acc + 1; }, 0), switchMap(attempt => timer(backoffDelay(attempt, options.retry?.baseDelayMs ?? 250, options.retry?.backoff ?? 'exponential', options.retry?.maxDelayMs)))))); } stream = stream.pipe(tap(res => { if (options.cacheDurationMs && options.cacheDurationMs > 0) this.cache.set(cacheKey, res, options.cacheDurationMs); if (logEnabled && logLevel !== 'none') { log.status = 'ok'; log.finishedAt = Date.now(); if (logLevel === 'verbose') log.meta = { size: JSON.stringify(res).length }; options.log?.sendToServer?.(log); if (options.debug) console.debug('[HTTP]', log); } }), catchError(err => { if (options.retry?.fallbackValue !== undefined) { const fv = typeof options.retry.fallbackValue === 'function' ? options.retry.fallbackValue() : options.retry.fallbackValue; return of(fv); } if (logEnabled && logLevel !== 'none') { log.status = 'error'; log.finishedAt = Date.now(); log.error = err; options.log?.sendToServer?.(log); if (options.debug) console.error('[HTTP]', log); } return throwError(() => err); }), finalize(() => { if (!raw && inflightKey) this.inflight.delete(inflightKey); if (!raw) this.lastSent.set(cacheKey, Date.now()); })); if (!raw) { stream = stream.pipe(shareReplay({ bufferSize: 1, refCount: true })); } const debounceMs = options.debounceMs ?? 0; const minIntervalMs = options.rateLimitMs ?? 0; const last = this.lastSent.get(cacheKey) ?? 0; const needDelay = minIntervalMs > 0 ? Math.max(0, minIntervalMs - (Date.now() - last)) : 0; const totalDelay = Math.max(debounceMs, needDelay); const exec$ = (options.queue?.enabled) ? this.queue.execute(() => stream, options.queue.priority ?? 'normal', options.queue.concurrency ?? (options.queue.mode === 'parallel' ? 4 : 1)) : stream; let scheduled$; if (totalDelay > 0) { scheduled$ = timer(totalDelay).pipe(switchMap(() => exec$)); } else { scheduled$ = exec$; } let out$; if (options.batch?.enabled && !raw) { const bKey = options.batch.key ?? `${method}:${endpoint}`; let batchStream = this.batcher.enqueue(bKey, payload, options.batch, (combined, ep) => { const epWithQuery = (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') && Array.isArray(combined) ? `${ep}?${combined.map((v) => `id=${encodeURIComponent(v)}`).join('&')}` : ep; const bodyForMethod = (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') ? undefined : combined; return this.http.request(method, epWithQuery, { body: bodyForMethod, headers, params, observe: 'body', responseType: 'json' }); }, endpoint); if (options.timeoutMs) { batchStream = batchStream.pipe(timeout(options.timeoutMs)); } out$ = batchStream.pipe(shareReplay({ bufferSize: 1, refCount: true })); } else { out$ = scheduled$; } if (!raw && inflightKey) { this.inflight.set(inflightKey, out$); } return out$; } get(endpoint, params, options = {}) { return this.send(endpoint, undefined, { ...options, method: 'GET', params }); } post(endpoint, body, options = {}) { return this.send(endpoint, body, { ...options, method: 'POST', body }); } put(endpoint, body, options = {}) { return this.send(endpoint, body, { ...options, method: 'PUT', body }); } patch(endpoint, body, options = {}) { return this.send(endpoint, body, { ...options, method: 'PATCH', body }); } delete(endpoint, body, options = {}) { return this.send(endpoint, body, { ...options, method: 'DELETE', body }); } head(endpoint, params, options = {}) { return this.send(endpoint, undefined, { ...options, method: 'HEAD', params }); } options(endpoint, params, options = {}) { return this.send(endpoint, undefined, { ...options, method: 'OPTIONS', params }); } clearCacheByKey(method, endpoint, paramsOrBody) { const key = `${method} ${endpoint} :: ${stableStringify(paramsOrBody ?? {})}`; this.cache.delete(key); } clearCacheByEndpoint(endpoint) { for (const k of this.cache.keys()) if (k.includes(` ${endpoint} :: `)) this.cache.delete(k); } clearAllCache() { this.cache.clear(); } ngOnDestroy() { clearInterval(this.cleanupHandle); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AdvancedHttpClientService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AdvancedHttpClientService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AdvancedHttpClientService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.HttpClient }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWR2YW5jZWQtaHR0cC1jbGllbnQuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL2FkdmFuY2VkLWh0dHAtY2xpZW50L3NyYy9saWIvYWR2YW5jZWQtaHR0cC1jbGllbnQuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTNDLE9BQU8sRUFBYyxFQUFFLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN6RCxPQUFPLEVBQUUsVUFBVSxFQUFTLFFBQVEsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3BILE9BQU8sRUFBRSxXQUFXLEVBQUUsZUFBZSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxZQUFZLEVBQUUsTUFBTSxTQUFTLENBQUM7OztBQUk1RCxNQUFNLE9BQU8seUJBQXlCO0lBT3BDLFlBQW9CLElBQWU7UUFBZixTQUFJLEdBQUosSUFBSSxDQUFXO1FBTjNCLFVBQUssR0FBQyxJQUFJLFdBQVcsRUFBTyxDQUFDO1FBQzdCLGFBQVEsR0FBQyxJQUFJLEdBQUcsRUFBMEIsQ0FBQztRQUMzQyxhQUFRLEdBQUMsSUFBSSxHQUFHLEVBQWlCLENBQUM7UUFDbEMsVUFBSyxHQUFDLElBQUksWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzFCLFlBQU8sR0FBQyxJQUFJLGNBQWMsRUFBRSxDQUFDO1FBQzdCLGtCQUFhLEdBQUMsV0FBVyxDQUFDLEdBQUUsRUFBRSxD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RUFBQyxNQUFNLEVBQUMsQ0FBQyxDQUFDLENBQUEsQ0FBQztJQUN4TCxPQUFPLENBQUksUUFBZSxFQUFDLE1BQTBCLEVBQUMsVUFBc0QsRUFBRSxJQUFFLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBSSxRQUFRLEVBQUMsU0FBUyxFQUFDLEVBQUMsR0FBRyxPQUFPLEVBQUMsTUFBTSxFQUFDLFNBQVMsRUFBQyxNQUFNLEVBQUMsQ0FBQyxDQUFDLENBQUEsQ0FBQztJQUU5TCxlQUFlLENBQUMsTUFBaUIsRUFBQyxRQUFlLEVBQUMsWUFBaUIsSUFBRSxNQUFNLEdBQUcsR0FBQyxHQUFHLE1BQU0sSUFBSSxRQUFRLE9BQU8sZUFBZSxDQUFDLFlBQVksSUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUEsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQSxDQUFDO0lBQ3ZLLG9CQUFvQixDQUFDLFFBQWUsSUFBRSxLQUFJLE1BQU0sQ0FBQyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFO1FBQUMsSUFBRyxDQUFDLENBQUMsUUFBUSxDQUFDLElBQUksUUFBUSxNQUFNLENBQUM7WUFBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBLENBQUM7SUFDL0gsYUFBYSxLQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQSxDQUFDO0lBQ3BDLFdBQVcsS0FBRyxhQUFhLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUEsQ0FBQzsrR0EvRXRDLHlCQUF5QjttSEFBekIseUJBQXlCLGNBRGQsTUFBTTs7NEZBQ2pCLHlCQUF5QjtrQkFEckMsVUFBVTttQkFBQyxFQUFDLFVBQVUsRUFBQyxNQUFNLEVBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBIdHRwQ2xpZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgb2YsIHRocm93RXJyb3IsIHRpbWVyIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBjYXRjaEVycm9yLCBkZWxheSwgZmluYWxpemUsIHJldHJ5V2hlbiwgc2Nhbiwgc2hhcmVSZXBsYXksIHN3aXRjaE1hcCwgdGltZW91dCwgdGFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgTWVtb3J5Q2FjaGUsIHN0YWJsZVN0cmluZ2lmeSB9IGZyb20gJy4vcmVxdWVzdC1jYWNoZSc7XG5pbXBvcnQgeyBSZXF1ZXN0UXVldWUgfSBmcm9tICcuL3JlcXVlc3QtcXVldWUnO1xuaW1wb3J0IHsgUmVxdWVzdEJhdGNoZXIgfSBmcm9tICcuL3JlcXVlc3QtYmF0Y2hlcic7XG5pbXBvcnQgeyB0b0hlYWRlcnMsIHRvUGFyYW1zLCBiYWNrb2ZmRGVsYXkgfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7IFNlbmRPcHRpb25zLCBIdHRwTWV0aG9kIH0gZnJvbSAnLi90eXBlcyc7XG5cbkBJbmplY3RhYmxlKHtwcm92aWRlZEluOidyb290J30pXG5leHBvcnQgY2xhc3MgQWR2YW5jZWRIdHRwQ2xpZW50U2VydmljZXtcbiAgcHJpdmF0ZSBjYWNoZT1uZXcgTWVtb3J5Q2FjaGU8YW55PigpO1xuICBwcml2YXRlIGluZmxpZ2h0PW5ldyBNYXA8c3RyaW5nLE9ic2VydmFibGU8YW55Pj4oKTtcbiAgcHJpdmF0ZSBsYXN0U2VudD1uZXcgTWFwPHN0cmluZyxudW1iZXI+KCk7XG4gIHByaXZhdGUgcXVldWU9bmV3IFJlcXVlc3RRdWV1ZSg0KTtcbiAgcHJpdmF0ZSBiYXRjaGVyPW5ldyBSZXF1ZXN0QmF0Y2hlcigpO1xuICBwcml2YXRlIGNsZWFudXBIYW5kbGU9c2V0SW50ZXJ2YWwoKCk9PnRoaXMuY2FjaGUuY2xlYW51cCgpLDYwMDAwKTtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBodHRwOkh0dHBDbGllbnQpe31cblxuICBzZW5kPFQ9YW55PihlbmRwb2ludDpzdHJpbmcscGF5bG9hZD86YW55LG9wdGlvbnM6U2VuZE9wdGlvbnM8VD49e30pOk9ic2VydmFibGU8VD57XG4gICAgY29uc3QgbWV0aG9kOkh0dHBNZXRob2Q9b3B0aW9ucy5tZXRob2Q/PydHRVQnO1xuICAgIGNvbnN0IGJvZHk9b3B0aW9ucy5ib2R5Pz8obWV0aG9kPT09J0dFVCd8fG1ldGhvZD09PSdIRUFEJ3x8bWV0aG9kPT09J09QVElPTlMnP3VuZGVmaW5lZDpwYXlsb2FkKTtcbiAgICBjb25zdCBwYXJhbXNPYmo9b3B0aW9ucy5wYXJhbXM/P3t9OyBjb25zdCBwYXJhbXM9dG9QYXJhbXMocGFyYW1zT2JqKTsgY29uc3QgaGVhZGVycz10b0hlYWRlcnMob3B0aW9ucy5oZWFkZXJzKTtcbiAgICBjb25zdCBjYWNoZUtleT1gJHttZXRob2R9ICR7ZW5kcG9pbnR9IDo6ICR7c3RhYmxlU3RyaW5naWZ5KHtwYXJhbXM6cGFyYW1zT2JqLGJvZHl9KX1gO1xuICAgIGNvbnN0IHJhdz0hIW9wdGlvbnMucmF3O1xuICAgIGNvbnN0IGluZmxpZ2h0S2V5PSFyYXcgPyAob3B0aW9ucy5iYXRjaD8uZW5hYmxlZD9gJHtjYWNoZUtleX0gOjogcGF5bG9hZD0ke3N0YWJsZVN0cmluZ2lmeShwYXlsb2FkKX1gOmNhY2hlS2V5KSA6IHVuZGVmaW5lZDtcblxuICAgIGlmKG9wdGlvbnMuY2FjaGVEdXJhdGlvbk1zJiZvcHRpb25zLmNhY2hlRHVyYXRpb25Ncz4wKXtjb25zdCBjPXRoaXMuY2FjaGUuZ2V0KGNhY2hlS2V5KTtpZihjIT09dW5kZWZpbmVkKXJldHVybiBvZihjIGFzIFQpO30gXG4gICAgaWYoIXJhdyl7Y29uc3QgZXg9dGhpcy5pbmZsaWdodC5nZXQoaW5mbGlnaHRLZXkhKTtpZihleClyZXR1cm4gZXggYXMgT2JzZXJ2YWJsZTxUPjt9XG5cbiAgICBjb25zdCBsb2dFbmFibGVkPSEhb3B0aW9ucy5sb2c/LmVuYWJsZWQ7Y29uc3QgbG9nTGV2ZWw9b3B0aW9ucy5sb2c/LmxldmVsPz8nYmFzaWMnO1xuICAgIGNvbnN0IGxvZzphbnk9bG9nRW5hYmxlZD97a2V5OmluZmxpZ2h0S2V5Pz9jYWNoZUtleSxtZXRob2QsZW5kcG9pbnQsc3RhcnRlZEF0OkRhdGUubm93KCl9OnVuZGVmaW5lZDtcblxuICAgIGxldCBzdHJlYW09dGhpcy5odHRwLnJlcXVlc3Q8VD4obWV0aG9kLGVuZHBvaW50LHtib2R5LGhlYWRlcnMscGFyYW1zLG9ic2VydmU6J2JvZHknLHJlc3BvbnNlVHlwZTonanNvbid9KTtcbiAgICBpZihvcHRpb25zLnRpbWVvdXRNcyl7c3RyZWFtPXN0cmVhbS5waXBlKHRpbWVvdXQob3B0aW9ucy50aW1lb3V0TXMpKTt9XG4gICAgaWYob3B0aW9ucy5yZXRyeT8uYXR0ZW1wdHMpe1xuICAgICAgc3RyZWFtPXN0cmVhbS5waXBlKHJldHJ5V2hlbihlcnJzPT5lcnJzLnBpcGUoXG4gICAgICAgIHNjYW4oKGFjYyxlcnIpPT57Y29uc3QgYXR0ZW1wdHM9b3B0aW9ucy5yZXRyeT8uYXR0ZW1wdHM/PzA7Y29uc3Qgc2hvdWxkPW9wdGlvbnMucmV0cnk/LnNob3VsZFJldHJ5Py4oZXJyKT8/dHJ1ZTtpZighc2hvdWxkfHxhY2M+PWF0dGVtcHRzKXRocm93IGVycjtyZXR1cm4gYWNjKzE7fSwwKSxcbiAgICAgICAgc3dpdGNoTWFwKGF0dGVtcHQ9PnRpbWVyKGJhY2tvZmZEZWxheShhdHRlbXB0LG9wdGlvbnMucmV0cnk/LmJhc2VEZWxheU1zPz8yNTAsb3B0aW9ucy5yZXRyeT8uYmFja29mZj8/J2V4cG9uZW50aWFsJyxvcHRpb25zLnJldHJ5Py5tYXhEZWxheU1zKSkpXG4gICAgICApKSk7XG4gICAgfVxuICAgIHN0cmVhbT1zdHJlYW0ucGlwZShcbiAgICAgIHRhcChyZXM9PntpZihvcHRpb25zLmNhY2hlRHVyYXRpb25NcyYmb3B0aW9ucy5jYWNoZUR1cmF0aW9uTXM+MCl0aGlzLmNhY2hlLnNldChjYWNoZUtleSxyZXMsb3B0aW9ucy5jYWNoZUR1cmF0aW9uTXMpO1xuICAgICAgICBpZihsb2dFbmFibGVkJiZsb2dMZXZlbCE9PSdub25lJyl7bG9nLnN0YXR1cz0nb2snO2xvZy5maW5pc2hlZEF0PURhdGUubm93KCk7aWYobG9nTGV2ZWw9PT0ndmVyYm9zZScpbG9nLm1ldGE9e3NpemU6SlNPTi5zdHJpbmdpZnkocmVzKS5sZW5ndGh9O29wdGlvbnMubG9nPy5zZW5kVG9TZXJ2ZXI/Lihsb2cpO2lmKG9wdGlvbnMuZGVidWcpY29uc29sZS5kZWJ1ZygnW0hUVFBdJyxsb2cpO30gfSksXG4gICAgICBjYXRjaEVycm9yKGVycj0+e2lmKG9wdGlvbnMucmV0cnk/LmZhbGxiYWNrVmFsdWUhPT11bmRlZmluZWQpe2NvbnN0IGZ2PXR5cGVvZiBvcHRpb25zLnJldHJ5LmZhbGxiYWNrVmFsdWU9PT0nZnVuY3Rpb24nPyhvcHRpb25zLnJldHJ5LmZhbGxiYWNrVmFsdWUgYXMgYW55KSgpOm9wdGlvbnMucmV0cnkuZmFsbGJhY2tWYWx1ZTtyZXR1cm4gb2YoZnYgYXMgVCk7fSBcbiAgICAgICAgaWYobG9nRW5hYmxlZCYmbG9nTGV2ZWwhPT0nbm9uZScpe2xvZy5zdGF0dXM9J2Vycm9yJztsb2cuZmluaXNoZWRBdD1EYXRlLm5vdygpO2xvZy5lcnJvcj1lcnI7b3B0aW9ucy5sb2c/LnNlbmRUb1NlcnZlcj8uKGxvZyk7aWYob3B0aW9ucy5kZWJ1Zyljb25zb2xlLmVycm9yKCdbSFRUUF0nLGxvZyk7fSByZXR1cm4gdGhyb3dFcnJvcigoKT0+ZXJyKTt9ICksXG4gICAgICBmaW5hbGl6ZSgoKT0+e2lmKCFyYXcmJmluZmxpZ2h0S2V5KXRoaXMuaW5mbGlnaHQuZGVsZXRlKGluZmxpZ2h0S2V5KTtpZighcmF3KXRoaXMubGFzdFNlbnQuc2V0KGNhY2hlS2V5LERhdGUubm93KCkpO30pXG4gICAgKTtcbiAgICBpZighcmF3KXtzdHJlYW09c3RyZWFtLnBpcGUoc2hhcmVSZXBsYXkoe2J1ZmZlclNpemU6MSxyZWZDb3VudDp0cnVlfSkpO31cblxuICAgIGNvbnN0IGRlYm91bmNlTXM9b3B0aW9ucy5kZWJvdW5jZU1zPz8wO2NvbnN0IG1pbkludGVydmFsTXM9b3B0aW9ucy5yYXRlTGltaXRNcz8/MDtcbiAgICBjb25zdCBsYXN0PXRoaXMubGFzdFNlbnQuZ2V0KGNhY2hlS2V5KT8/MDtjb25zdCBuZWVkRGVsYXk9bWluSW50ZXJ2YWxNcz4wP01hdGgubWF4KDAsbWluSW50ZXJ2YWxNcy0oRGF0ZS5ub3coKS1sYXN0KSk6MDtcbiAgICBjb25zdCB0b3RhbERlbGF5PU1hdGgubWF4KGRlYm91bmNlTXMsbmVlZERlbGF5KTtcblxuICAgIGNvbnN0IGV4ZWMkPShvcHRpb25zLnF1ZXVlPy5lbmFibGVkKT90aGlzLnF1ZXVlLmV4ZWN1dGUoKCk9PnN0cmVhbSxvcHRpb25zLnF1ZXVlLnByaW9yaXR5Pz8nbm9ybWFsJyxvcHRpb25zLnF1ZXVlLmNvbmN1cnJlbmN5Pz8ob3B0aW9ucy5xdWV1ZS5tb2RlPT09J3BhcmFsbGVsJz80OjEpKTpzdHJlYW07XG4gICAgbGV0IHNjaGVkdWxlZCQ6T2JzZXJ2YWJsZTxUPjtcbiAgICBpZih0b3RhbERlbGF5PjApe1xuICAgICAgc2NoZWR1bGVkJD10aW1lcih0b3RhbERlbGF5KS5waXBlKHN3aXRjaE1hcCgoKT0+ZXhlYyQgYXMgT2JzZXJ2YWJsZTxUPikpO1xuICAgIH1lbHNle3NjaGVkdWxlZCQ9ZXhlYyQ7fVxuXG4gICAgbGV0IG91dCQ6T2JzZXJ2YWJsZTxUPjtcbiAgICBpZihvcHRpb25zLmJhdGNoPy5lbmFibGVkICYmICFyYXcpe1xuICAgICAgY29uc3QgYktleT1vcHRpb25zLmJhdGNoLmtleT8/YCR7bWV0aG9kfToke2VuZHBvaW50fWA7XG4gICAgICBsZXQgYmF0Y2hTdHJlYW0gPSB0aGlzLmJhdGNoZXIuZW5xdWV1ZTxUPihiS2V5LHBheWxvYWQsb3B0aW9ucy5iYXRjaCwoY29tYmluZWQsZXApPT57XG4gICAgICAgIGNvbnN0IGVwV2l0aFF1ZXJ5PShtZXRob2Q9PT0nR0VUJ3x8bWV0aG9kPT09J0hFQUQnfHxtZXRob2Q9PT0nT1BUSU9OUycpJiZBcnJheS5pc0FycmF5KGNvbWJpbmVkKVxuICAgICAgICAgID8gYCR7ZXB9PyR7Y29tYmluZWQubWFwKCh2OmFueSk9PmBpZD0ke2VuY29kZVVSSUNvbXBvbmVudCh2KX1gKS5qb2luKCcmJyl9YFxuICAgICAgICAgIDogZXA7XG4gICAgICAgIGNvbnN0IGJvZHlGb3JNZXRob2Q9KG1ldGhvZD09PSdHRVQnfHxtZXRob2Q9PT0nSEVBRCd8fG1ldGhvZD09PSdPUFRJT05TJyk/dW5kZWZpbmVkOmNvbWJpbmVkO1xuICAgICAgICByZXR1cm4gdGhpcy5odHRwLnJlcXVlc3Q8VD4obWV0aG9kLGVwV2l0aFF1ZXJ5LHtib2R5OmJvZHlGb3JNZXRob2QsaGVhZGVycyxwYXJhbXMsb2JzZXJ2ZTonYm9keScscmVzcG9uc2VUeXBlOidqc29uJ30pO1xuICAgICAgfSxlbmRwb2ludCk7XG4gICAgICBpZihvcHRpb25zLnRpbWVvdXRNcyl7YmF0Y2hTdHJlYW09YmF0Y2hTdHJlYW0ucGlwZSh0aW1lb3V0KG9wdGlvbnMudGltZW91dE1zKSk7fVxuICAgICAgb3V0JD1iYXRjaFN0cmVhbS5waXBlKHNoYXJlUmVwbGF5KHtidWZmZXJTaXplOjEscmVmQ291bnQ6dHJ1ZX0pKTtcbiAgICB9ZWxzZXtvdXQkPXNjaGVkdWxlZCQ7fVxuXG4gICAgaWYoIXJhdyYmaW5mbGlnaHRLZXkpe3RoaXMuaW5mbGlnaHQuc2V0KGluZmxpZ2h0S2V5LG91dCQpO30gXG4gICAgcmV0dXJuIG91dCQ7XG4gIH1cblxuICBnZXQ8VD4oZW5kcG9pbnQ6c3RyaW5nLHBhcmFtcz86UmVjb3JkPHN0cmluZyxhbnk+LG9wdGlvbnM6T21pdDxTZW5kT3B0aW9uczxUPiwnbWV0aG9kJ3wncGFyYW1zJz49e30pe3JldHVybiB0aGlzLnNlbmQ8VD4oZW5kcG9pbnQsdW5kZWZpbmVkLHsuLi5vcHRpb25zLG1ldGhvZDonR0VUJyxwYXJhbXN9KTt9XG4gIHBvc3Q8VD4oZW5kcG9pbnQ6c3RyaW5nLGJvZHk/OmFueSxvcHRpb25zOk9taXQ8U2VuZE9wdGlvbnM8VD4sJ21ldGhvZCd8J2JvZHknPj17fSl7cmV0dXJuIHRoaXMuc2VuZDxUPihlbmRwb2ludCxib2R5LHsuLi5vcHRpb25zLG1ldGhvZDonUE9TVCcsYm9keX0pO31cbiAgcHV0PFQ+KGVuZHBvaW50OnN0cmluZyxib2R5Pzphbnksb3B0aW9uczpPbWl0PFNlbmRPcHRpb25zPFQ+LCdtZXRob2QnfCdib2R5Jz49e30pe3JldHVybiB0aGlzLnNlbmQ8VD4oZW5kcG9pbnQsYm9keSx7Li4ub3B0aW9ucyxtZXRob2Q6J1BVVCcsYm9keX0pO31cbiAgcGF0Y2g8VD4oZW5kcG9pbnQ6c3RyaW5nLGJvZHk/OmFueSxvcHRpb25zOk9taXQ8U2VuZE9wdGlvbnM8VD4sJ21ldGhvZCd8J2JvZHknPj17fSl7cmV0dXJuIHRoaXMuc2VuZDxUPihlbmRwb2ludCxib2R5LHsuLi5vcHRpb25zLG1ldGhvZDonUEFUQ0gnLGJvZHl9KTt9XG4gIGRlbGV0ZTxUPihlbmRwb2ludDpzdHJpbmcsYm9keT86YW55LG9wdGlvbnM6T21pdDxTZW5kT3B0aW9uczxUPiwnbWV0aG9kJ3wnYm9keSc+PXt9KXtyZXR1cm4gdGhpcy5zZW5kPFQ+KGVuZHBvaW50LGJvZHksey4uLm9wdGlvbnMsbWV0aG9kOidERUxFVEUnLGJvZHl9KTt9XG4gIGhlYWQ8VD4oZW5kcG9pbnQ6c3RyaW5nLHBhcmFtcz86UmVjb3JkPHN0cmluZyxhbnk+LG9wdGlvbnM6T21pdDxTZW5kT3B0aW9uczxUPiwnbWV0aG9kJ3wncGFyYW1zJ3wnYm9keSc+PXt9KXtyZXR1cm4gdGhpcy5zZW5kPFQ+KGVuZHBvaW50LHVuZGVmaW5lZCx7Li4ub3B0aW9ucyxtZXRob2Q6J0hFQUQnLHBhcmFtc30pO31cbiAgb3B0aW9uczxUPihlbmRwb2ludDpzdHJpbmcscGFyYW1zPzpSZWNvcmQ8c3RyaW5nLGFueT4sb3B0aW9uczpPbWl0PFNlbmRPcHRpb25zPFQ+LCdtZXRob2QnfCdwYXJhbXMnfCdib2R5Jz49e30pe3JldHVybiB0aGlzLnNlbmQ8VD4oZW5kcG9pbnQsdW5kZWZpbmVkLHsuLi5vcHRpb25zLG1ldGhvZDonT1BUSU9OUycscGFyYW1zfSk7fVxuXG4gIGNsZWFyQ2FjaGVCeUtleShtZXRob2Q6SHR0cE1ldGhvZCxlbmRwb2ludDpzdHJpbmcscGFyYW1zT3JCb2R5Pzphbnkpe2NvbnN0IGtleT1gJHttZXRob2R9ICR7ZW5kcG9pbnR9IDo6ICR7c3RhYmxlU3RyaW5naWZ5KHBhcmFtc09yQm9keT8/e30pfWA7dGhpcy5jYWNoZS5kZWxldGUoa2V5KTt9IFxuICBjbGVhckNhY2hlQnlFbmRwb2ludChlbmRwb2ludDpzdHJpbmcpe2Zvcihjb25zdCBrIG9mIHRoaXMuY2FjaGUua2V5cygpKWlmKGsuaW5jbHVkZXMoYCAke2VuZHBvaW50fSA6OiBgKSl0aGlzLmNhY2hlLmRlbGV0ZShrKTt9IFxuICBjbGVhckFsbENhY2hlKCl7dGhpcy5jYWNoZS5jbGVhcigpO31cbiAgbmdPbkRlc3Ryb3koKXtjbGVhckludGVydmFsKHRoaXMuY2xlYW51cEhhbmRsZSk7fSBcbn0iXX0=