UNPKG

@data-client/core

Version:

Async State Management without the Management. REST, GraphQL, SSE, Websockets, Fetch

267 lines (255 loc) 31.6 kB
import { SET_RESPONSE, FETCH, RESET } from '../actionTypes.js'; import { createSetResponse } from '../controller/actions/index.js'; import Controller from '../controller/Controller.js'; export class ResetError extends Error { constructor() { super('Aborted due to RESET'); this.name = 'ResetError'; } } /** Handles all async network dispatches * * Dedupes concurrent requests by keeping track of all fetches in flight * and returning existing promises for requests already in flight. * * Interfaces with store via a redux-compatible middleware. * * @see https://dataclient.io/docs/api/NetworkManager */ export default class NetworkManager { constructor({ dataExpiryLength = 60000, errorExpiryLength = 1000 } = {}) { this.fetching = new Map(); this.controller = new Controller(); this.middleware = controller => { this.controller = controller; return next => action => { switch (action.type) { case FETCH: this.handleFetch(action); // This is the only case that causes any state change // It's important to intercept other fetches as we don't want to trigger reducers during // render - so we need to stop 'readonly' fetches which can be triggered in render if (action.endpoint.getOptimisticResponse !== undefined && action.endpoint.sideEffect) { return next(action); } return Promise.resolve(); case SET_RESPONSE: // only set after new state is computed return next(action).then(() => { if (this.fetching.has(action.key)) { var _controller$getState$; // Note: meta *must* be set by reducer so this should be safe const error = (_controller$getState$ = controller.getState().meta[action.key]) == null ? void 0 : _controller$getState$.error; // processing errors result in state meta having error, so we should reject the promise if (error) { this.handleSet(createSetResponse(action.endpoint, { args: action.args, response: error, fetchedAt: action.meta.fetchedAt, error: true })); } else { this.handleSet(action); } } }); case RESET: { // take snapshot of rejectors at this point in time // we must use Array.from since iteration does not freeze state at this point in time const fetches = Array.from(this.fetching.values()); this.clearAll(); return next(action).then(() => { // there could be external listeners to the promise // this must happen after commit so our own rejector knows not to dispatch an error based on this for (const { reject } of fetches) { reject(new ResetError()); } }); } default: return next(action); } }; }; this.dataExpiryLength = dataExpiryLength; this.errorExpiryLength = errorExpiryLength; } /** On mount */ init() { delete this.cleanupDate; } /** Ensures all promises are completed by rejecting remaining. */ cleanup() { // ensure no dispatches after unmount // this must be reversible (done in init) so useEffect() remains symmetric this.cleanupDate = Date.now(); } /** Used by DevtoolsManager to determine whether to log an action */ skipLogging(action) { /* istanbul ignore next */ return action.type === FETCH && this.fetching.has(action.key); } allSettled() { if (this.fetching.size) return Promise.allSettled(this.fetching.values().map(({ promise }) => promise)); } /** Clear all promise state */ clearAll() { for (const k of this.fetching.keys()) { this.clear(k); } } /** Clear promise state for a given key */ clear(key) { if (this.fetching.has(key)) { this.fetching.get(key).promise.catch(() => {}); this.fetching.delete(key); } } getLastReset() { if (this.cleanupDate) return this.cleanupDate; return this.controller.getState().lastReset; } /** Called when middleware intercepts 'rdc/fetch' action. * * Will then start a promise for a key and potentially start the network * fetch. * * Uses throttle endpoints without sideEffects. This is valuable * for ensures mutation requests always go through. */ handleFetch(action) { const { resolve, reject, fetchedAt } = action.meta; const throttle = !action.endpoint.sideEffect; const deferedFetch = () => { let promise = action.endpoint(...action.args); const resolvePromise = promise => promise.then(data => { resolve(data); return data; }).catch(error => { reject(error); throw error; }); // schedule non-throttled resolutions in a microtask before set // this enables users awaiting their fetch to trigger any react updates needed to deal // with upcoming changes because of the fetch (for instance avoiding suspense if something is deleted) if (!throttle) { promise = resolvePromise(promise); } promise = promise.then(response => { let lastReset = this.getLastReset(); /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production' && isNaN(lastReset)) { console.error('state.lastReset is NaN. Only positive timestamps are valid.'); lastReset = 0; } // don't update state with promises started before last clear if (fetchedAt >= lastReset) { this.controller.resolve(action.endpoint, { args: action.args, response, fetchedAt }); } return response; }).catch(error => { const lastReset = this.getLastReset(); // don't update state with promises started before last clear if (fetchedAt >= lastReset) { this.controller.resolve(action.endpoint, { args: action.args, response: error, fetchedAt, error: true }); } throw error; }); return promise; }; if (throttle) { return this.throttle(action.key, deferedFetch, fetchedAt).then(data => resolve(data)).catch(error => reject(error)); } else { return deferedFetch().catch(() => {}); } } /** Called when middleware intercepts a set action. * * Will resolve the promise associated with set key. */ handleSet(action) { // this can still turn out to be untrue since this is async if (this.fetching.has(action.key)) { const { reject, resolve } = this.fetching.get(action.key); if (action.error) { reject(action.response); } else { resolve(action.response); } // since we're resolved we no longer need to keep track of this promise this.clear(action.key); } } /** Ensures only one request for a given key is in flight at any time * * Uses key to either retrieve in-flight promise, or if not * create a new promise and call fetch. * * Note: The new promise is not actually tied to fetch at all, * but is resolved when the expected 'receive' action is processed. * This ensures promises are resolved only once their data is processed * by the reducer. */ throttle(key, fetch, fetchedAt) { const lastReset = this.getLastReset(); let fetchMeta = this.fetching.get(key); // we're already fetching so reuse the promise // fetches after reset do not count if (fetchMeta && fetchMeta.fetchedAt > lastReset) { return fetchMeta.promise; } fetchMeta = newFetchMeta(fetchedAt); this.fetching.set(key, fetchMeta); this.idleCallback(() => { // since our real promise is resolved via the wrapReducer(), // we should just stop all errors here. // TODO: decouple this from useFetcher() (that's what's dispatching the error the resolves in here) fetch().catch(() => null); }, { timeout: 500 }); return fetchMeta.promise; } /** Calls the callback when client is not 'busy' with high priority interaction tasks * * Override for platform-specific implementations */ idleCallback(callback, options) { callback(); } } function newFetchMeta(fetchedAt) { const fetchMeta = { fetchedAt }; fetchMeta.promise = new Promise((resolve, reject) => { fetchMeta.resolve = resolve; fetchMeta.reject = reject; }); return fetchMeta; } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJTRVRfUkVTUE9OU0UiLCJGRVRDSCIsIlJFU0VUIiwiY3JlYXRlU2V0UmVzcG9uc2UiLCJDb250cm9sbGVyIiwiUmVzZXRFcnJvciIsIkVycm9yIiwiY29uc3RydWN0b3IiLCJuYW1lIiwiTmV0d29ya01hbmFnZXIiLCJkYXRhRXhwaXJ5TGVuZ3RoIiwiZXJyb3JFeHBpcnlMZW5ndGgiLCJmZXRjaGluZyIsIk1hcCIsImNvbnRyb2xsZXIiLCJtaWRkbGV3YXJlIiwibmV4dCIsImFjdGlvbiIsInR5cGUiLCJoYW5kbGVGZXRjaCIsImVuZHBvaW50IiwiZ2V0T3B0aW1pc3RpY1Jlc3BvbnNlIiwidW5kZWZpbmVkIiwic2lkZUVmZmVjdCIsIlByb21pc2UiLCJyZXNvbHZlIiwidGhlbiIsImhhcyIsImtleSIsIl9jb250cm9sbGVyJGdldFN0YXRlJCIsImVycm9yIiwiZ2V0U3RhdGUiLCJtZXRhIiwiaGFuZGxlU2V0IiwiYXJncyIsInJlc3BvbnNlIiwiZmV0Y2hlZEF0IiwiZmV0Y2hlcyIsIkFycmF5IiwiZnJvbSIsInZhbHVlcyIsImNsZWFyQWxsIiwicmVqZWN0IiwiaW5pdCIsImNsZWFudXBEYXRlIiwiY2xlYW51cCIsIkRhdGUiLCJub3ciLCJza2lwTG9nZ2luZyIsImFsbFNldHRsZWQiLCJzaXplIiwibWFwIiwicHJvbWlzZSIsImsiLCJrZXlzIiwiY2xlYXIiLCJnZXQiLCJjYXRjaCIsImRlbGV0ZSIsImdldExhc3RSZXNldCIsImxhc3RSZXNldCIsInRocm90dGxlIiwiZGVmZXJlZEZldGNoIiwicmVzb2x2ZVByb21pc2UiLCJkYXRhIiwicHJvY2VzcyIsImVudiIsIk5PREVfRU5WIiwiaXNOYU4iLCJjb25zb2xlIiwiZmV0Y2giLCJmZXRjaE1ldGEiLCJuZXdGZXRjaE1ldGEiLCJzZXQiLCJpZGxlQ2FsbGJhY2siLCJ0aW1lb3V0IiwiY2FsbGJhY2siLCJvcHRpb25zIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL21hbmFnZXIvTmV0d29ya01hbmFnZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgU0VUX1JFU1BPTlNFLCBGRVRDSCwgUkVTRVQgfSBmcm9tICcuLi9hY3Rpb25UeXBlcy5qcyc7XG5pbXBvcnQgeyBjcmVhdGVTZXRSZXNwb25zZSB9IGZyb20gJy4uL2NvbnRyb2xsZXIvYWN0aW9ucy9pbmRleC5qcyc7XG5pbXBvcnQgQ29udHJvbGxlciBmcm9tICcuLi9jb250cm9sbGVyL0NvbnRyb2xsZXIuanMnO1xuaW1wb3J0IHR5cGUge1xuICBGZXRjaEFjdGlvbixcbiAgTWFuYWdlcixcbiAgQWN0aW9uVHlwZXMsXG4gIE1pZGRsZXdhcmUsXG4gIFNldFJlc3BvbnNlQWN0aW9uLFxufSBmcm9tICcuLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBjbGFzcyBSZXNldEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICBuYW1lID0gJ1Jlc2V0RXJyb3InO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCdBYm9ydGVkIGR1ZSB0byBSRVNFVCcpO1xuICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRmV0Y2hpbmdNZXRhIHtcbiAgcHJvbWlzZTogUHJvbWlzZTxhbnk+O1xuICByZXNvbHZlOiAodmFsdWU/OiBhbnkpID0+IHZvaWQ7XG4gIHJlamVjdDogKHZhbHVlPzogYW55KSA9PiB2b2lkO1xuICBmZXRjaGVkQXQ6IG51bWJlcjtcbn1cblxuLyoqIEhhbmRsZXMgYWxsIGFzeW5jIG5ldHdvcmsgZGlzcGF0Y2hlc1xuICpcbiAqIERlZHVwZXMgY29uY3VycmVudCByZXF1ZXN0cyBieSBrZWVwaW5nIHRyYWNrIG9mIGFsbCBmZXRjaGVzIGluIGZsaWdodFxuICogYW5kIHJldHVybmluZyBleGlzdGluZyBwcm9taXNlcyBmb3IgcmVxdWVzdHMgYWxyZWFkeSBpbiBmbGlnaHQuXG4gKlxuICogSW50ZXJmYWNlcyB3aXRoIHN0b3JlIHZpYSBhIHJlZHV4LWNvbXBhdGlibGUgbWlkZGxld2FyZS5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9OZXR3b3JrTWFuYWdlclxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBOZXR3b3JrTWFuYWdlciBpbXBsZW1lbnRzIE1hbmFnZXIge1xuICBwcm90ZWN0ZWQgZmV0Y2hpbmc6IE1hcDxzdHJpbmcsIEZldGNoaW5nTWV0YT4gPSBuZXcgTWFwKCk7XG4gIGRlY2xhcmUgcmVhZG9ubHkgZGF0YUV4cGlyeUxlbmd0aDogbnVtYmVyO1xuICBkZWNsYXJlIHJlYWRvbmx5IGVycm9yRXhwaXJ5TGVuZ3RoOiBudW1iZXI7XG4gIHByb3RlY3RlZCBjb250cm9sbGVyOiBDb250cm9sbGVyID0gbmV3IENvbnRyb2xsZXIoKTtcbiAgZGVjbGFyZSBjbGVhbnVwRGF0ZT86IG51bWJlcjtcblxuICBjb25zdHJ1Y3Rvcih7IGRhdGFFeHBpcnlMZW5ndGggPSA2MDAwMCwgZXJyb3JFeHBpcnlMZW5ndGggPSAxMDAwIH0gPSB7fSkge1xuICAgIHRoaXMuZGF0YUV4cGlyeUxlbmd0aCA9IGRhdGFFeHBpcnlMZW5ndGg7XG4gICAgdGhpcy5lcnJvckV4cGlyeUxlbmd0aCA9IGVycm9yRXhwaXJ5TGVuZ3RoO1xuICB9XG5cbiAgbWlkZGxld2FyZTogTWlkZGxld2FyZSA9IGNvbnRyb2xsZXIgPT4ge1xuICAgIHRoaXMuY29udHJvbGxlciA9IGNvbnRyb2xsZXI7XG4gICAgcmV0dXJuIG5leHQgPT4gYWN0aW9uID0+IHtcbiAgICAgIHN3aXRjaCAoYWN0aW9uLnR5cGUpIHtcbiAgICAgICAgY2FzZSBGRVRDSDpcbiAgICAgICAgICB0aGlzLmhhbmRsZUZldGNoKGFjdGlvbik7XG4gICAgICAgICAgLy8gVGhpcyBpcyB0aGUgb25seSBjYXNlIHRoYXQgY2F1c2VzIGFueSBzdGF0ZSBjaGFuZ2VcbiAgICAgICAgICAvLyBJdCdzIGltcG9ydGFudCB0byBpbnRlcmNlcHQgb3RoZXIgZmV0Y2hlcyBhcyB3ZSBkb24ndCB3YW50IHRvIHRyaWdnZXIgcmVkdWNlcnMgZHVyaW5nXG4gICAgICAgICAgLy8gcmVuZGVyIC0gc28gd2UgbmVlZCB0byBzdG9wICdyZWFkb25seScgZmV0Y2hlcyB3aGljaCBjYW4gYmUgdHJpZ2dlcmVkIGluIHJlbmRlclxuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIGFjdGlvbi5lbmRwb2ludC5nZXRPcHRpbWlzdGljUmVzcG9uc2UgIT09IHVuZGVmaW5lZCAmJlxuICAgICAgICAgICAgYWN0aW9uLmVuZHBvaW50LnNpZGVFZmZlY3RcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIHJldHVybiBuZXh0KGFjdGlvbik7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICAgICAgY2FzZSBTRVRfUkVTUE9OU0U6XG4gICAgICAgICAgLy8gb25seSBzZXQgYWZ0ZXIgbmV3IHN0YXRlIGlzIGNvbXB1dGVkXG4gICAgICAgICAgcmV0dXJuIG5leHQoYWN0aW9uKS50aGVuKCgpID0+IHtcbiAgICAgICAgICAgIGlmICh0aGlzLmZldGNoaW5nLmhhcyhhY3Rpb24ua2V5KSkge1xuICAgICAgICAgICAgICAvLyBOb3RlOiBtZXRhICptdXN0KiBiZSBzZXQgYnkgcmVkdWNlciBzbyB0aGlzIHNob3VsZCBiZSBzYWZlXG4gICAgICAgICAgICAgIGNvbnN0IGVycm9yID0gY29udHJvbGxlci5nZXRTdGF0ZSgpLm1ldGFbYWN0aW9uLmtleV0/LmVycm9yO1xuICAgICAgICAgICAgICAvLyBwcm9jZXNzaW5nIGVycm9ycyByZXN1bHQgaW4gc3RhdGUgbWV0YSBoYXZpbmcgZXJyb3IsIHNvIHdlIHNob3VsZCByZWplY3QgdGhlIHByb21pc2VcbiAgICAgICAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVTZXQoXG4gICAgICAgICAgICAgICAgICBjcmVhdGVTZXRSZXNwb25zZShhY3Rpb24uZW5kcG9pbnQsIHtcbiAgICAgICAgICAgICAgICAgICAgYXJnczogYWN0aW9uLmFyZ3MsXG4gICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlOiBlcnJvcixcbiAgICAgICAgICAgICAgICAgICAgZmV0Y2hlZEF0OiBhY3Rpb24ubWV0YS5mZXRjaGVkQXQsXG4gICAgICAgICAgICAgICAgICAgIGVycm9yOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZVNldChhY3Rpb24pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgIGNhc2UgUkVTRVQ6IHtcbiAgICAgICAgICAvLyB0YWtlIHNuYXBzaG90IG9mIHJlamVjdG9ycyBhdCB0aGlzIHBvaW50IGluIHRpbWVcbiAgICAgICAgICAvLyB3ZSBtdXN0IHVzZSBBcnJheS5mcm9tIHNpbmNlIGl0ZXJhdGlvbiBkb2VzIG5vdCBmcmVlemUgc3RhdGUgYXQgdGhpcyBwb2ludCBpbiB0aW1lXG4gICAgICAgICAgY29uc3QgZmV0Y2hlcyA9IEFycmF5LmZyb20odGhpcy5mZXRjaGluZy52YWx1ZXMoKSk7XG5cbiAgICAgICAgICB0aGlzLmNsZWFyQWxsKCk7XG4gICAgICAgICAgcmV0dXJuIG5leHQoYWN0aW9uKS50aGVuKCgpID0+IHtcbiAgICAgICAgICAgIC8vIHRoZXJlIGNvdWxkIGJlIGV4dGVybmFsIGxpc3RlbmVycyB0byB0aGUgcHJvbWlzZVxuICAgICAgICAgICAgLy8gdGhpcyBtdXN0IGhhcHBlbiBhZnRlciBjb21taXQgc28gb3VyIG93biByZWplY3RvciBrbm93cyBub3QgdG8gZGlzcGF0Y2ggYW4gZXJyb3IgYmFzZWQgb24gdGhpc1xuICAgICAgICAgICAgZm9yIChjb25zdCB7IHJlamVjdCB9IG9mIGZldGNoZXMpIHtcbiAgICAgICAgICAgICAgcmVqZWN0KG5ldyBSZXNldEVycm9yKCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgcmV0dXJuIG5leHQoYWN0aW9uKTtcbiAgICAgIH1cbiAgICB9O1xuICB9O1xuXG4gIC8qKiBPbiBtb3VudCAqL1xuICBpbml0KCkge1xuICAgIGRlbGV0ZSB0aGlzLmNsZWFudXBEYXRlO1xuICB9XG5cbiAgLyoqIEVuc3VyZXMgYWxsIHByb21pc2VzIGFyZSBjb21wbGV0ZWQgYnkgcmVqZWN0aW5nIHJlbWFpbmluZy4gKi9cbiAgY2xlYW51cCgpIHtcbiAgICAvLyBlbnN1cmUgbm8gZGlzcGF0Y2hlcyBhZnRlciB1bm1vdW50XG4gICAgLy8gdGhpcyBtdXN0IGJlIHJldmVyc2libGUgKGRvbmUgaW4gaW5pdCkgc28gdXNlRWZmZWN0KCkgcmVtYWlucyBzeW1tZXRyaWNcbiAgICB0aGlzLmNsZWFudXBEYXRlID0gRGF0ZS5ub3coKTtcbiAgfVxuXG4gIC8qKiBVc2VkIGJ5IERldnRvb2xzTWFuYWdlciB0byBkZXRlcm1pbmUgd2hldGhlciB0byBsb2cgYW4gYWN0aW9uICovXG4gIHNraXBMb2dnaW5nKGFjdGlvbjogQWN0aW9uVHlwZXMpIHtcbiAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAqL1xuICAgIHJldHVybiBhY3Rpb24udHlwZSA9PT0gRkVUQ0ggJiYgdGhpcy5mZXRjaGluZy5oYXMoYWN0aW9uLmtleSk7XG4gIH1cblxuICBhbGxTZXR0bGVkKCkge1xuICAgIGlmICh0aGlzLmZldGNoaW5nLnNpemUpXG4gICAgICByZXR1cm4gUHJvbWlzZS5hbGxTZXR0bGVkKFxuICAgICAgICB0aGlzLmZldGNoaW5nLnZhbHVlcygpLm1hcCgoeyBwcm9taXNlIH0pID0+IHByb21pc2UpLFxuICAgICAgKTtcbiAgfVxuXG4gIC8qKiBDbGVhciBhbGwgcHJvbWlzZSBzdGF0ZSAqL1xuICBwcm90ZWN0ZWQgY2xlYXJBbGwoKSB7XG4gICAgZm9yIChjb25zdCBrIG9mIHRoaXMuZmV0Y2hpbmcua2V5cygpKSB7XG4gICAgICB0aGlzLmNsZWFyKGspO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBDbGVhciBwcm9taXNlIHN0YXRlIGZvciBhIGdpdmVuIGtleSAqL1xuICBwcm90ZWN0ZWQgY2xlYXIoa2V5OiBzdHJpbmcpIHtcbiAgICBpZiAodGhpcy5mZXRjaGluZy5oYXMoa2V5KSkge1xuICAgICAgKHRoaXMuZmV0Y2hpbmcuZ2V0KGtleSkgYXMgRmV0Y2hpbmdNZXRhKS5wcm9taXNlLmNhdGNoKCgpID0+IHt9KTtcbiAgICAgIHRoaXMuZmV0Y2hpbmcuZGVsZXRlKGtleSk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGdldExhc3RSZXNldCgpIHtcbiAgICBpZiAodGhpcy5jbGVhbnVwRGF0ZSkgcmV0dXJuIHRoaXMuY2xlYW51cERhdGU7XG4gICAgcmV0dXJuIHRoaXMuY29udHJvbGxlci5nZXRTdGF0ZSgpLmxhc3RSZXNldDtcbiAgfVxuXG4gIC8qKiBDYWxsZWQgd2hlbiBtaWRkbGV3YXJlIGludGVyY2VwdHMgJ3JkYy9mZXRjaCcgYWN0aW9uLlxuICAgKlxuICAgKiBXaWxsIHRoZW4gc3RhcnQgYSBwcm9taXNlIGZvciBhIGtleSBhbmQgcG90ZW50aWFsbHkgc3RhcnQgdGhlIG5ldHdvcmtcbiAgICogZmV0Y2guXG4gICAqXG4gICAqIFVzZXMgdGhyb3R0bGUgZW5kcG9pbnRzIHdpdGhvdXQgc2lkZUVmZmVjdHMuIFRoaXMgaXMgdmFsdWFibGVcbiAgICogZm9yIGVuc3VyZXMgbXV0YXRpb24gcmVxdWVzdHMgYWx3YXlzIGdvIHRocm91Z2guXG4gICAqL1xuICBwcm90ZWN0ZWQgaGFuZGxlRmV0Y2goYWN0aW9uOiBGZXRjaEFjdGlvbikge1xuICAgIGNvbnN0IHsgcmVzb2x2ZSwgcmVqZWN0LCBmZXRjaGVkQXQgfSA9IGFjdGlvbi5tZXRhO1xuICAgIGNvbnN0IHRocm90dGxlID0gIWFjdGlvbi5lbmRwb2ludC5zaWRlRWZmZWN0O1xuXG4gICAgY29uc3QgZGVmZXJlZEZldGNoID0gKCkgPT4ge1xuICAgICAgbGV0IHByb21pc2UgPSBhY3Rpb24uZW5kcG9pbnQoLi4uYWN0aW9uLmFyZ3MpO1xuICAgICAgY29uc3QgcmVzb2x2ZVByb21pc2UgPSAoXG4gICAgICAgIHByb21pc2U6IFByb21pc2U8c3RyaW5nIHwgbnVtYmVyIHwgb2JqZWN0IHwgbnVsbD4sXG4gICAgICApID0+XG4gICAgICAgIHByb21pc2VcbiAgICAgICAgICAudGhlbihkYXRhID0+IHtcbiAgICAgICAgICAgIHJlc29sdmUoZGF0YSk7XG4gICAgICAgICAgICByZXR1cm4gZGF0YTtcbiAgICAgICAgICB9KVxuICAgICAgICAgIC5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgICAgfSk7XG4gICAgICAvLyBzY2hlZHVsZSBub24tdGhyb3R0bGVkIHJlc29sdXRpb25zIGluIGEgbWljcm90YXNrIGJlZm9yZSBzZXRcbiAgICAgIC8vIHRoaXMgZW5hYmxlcyB1c2VycyBhd2FpdGluZyB0aGVpciBmZXRjaCB0byB0cmlnZ2VyIGFueSByZWFjdCB1cGRhdGVzIG5lZWRlZCB0byBkZWFsXG4gICAgICAvLyB3aXRoIHVwY29taW5nIGNoYW5nZXMgYmVjYXVzZSBvZiB0aGUgZmV0Y2ggKGZvciBpbnN0YW5jZSBhdm9pZGluZyBzdXNwZW5zZSBpZiBzb21ldGhpbmcgaXMgZGVsZXRlZClcbiAgICAgIGlmICghdGhyb3R0bGUpIHtcbiAgICAgICAgcHJvbWlzZSA9IHJlc29sdmVQcm9taXNlKHByb21pc2UpO1xuICAgICAgfVxuICAgICAgcHJvbWlzZSA9IHByb21pc2VcbiAgICAgICAgLnRoZW4ocmVzcG9uc2UgPT4ge1xuICAgICAgICAgIGxldCBsYXN0UmVzZXQgPSB0aGlzLmdldExhc3RSZXNldCgpO1xuXG4gICAgICAgICAgLyogaXN0YW5idWwgaWdub3JlIGVsc2UgKi9cbiAgICAgICAgICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyAmJiBpc05hTihsYXN0UmVzZXQpKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICAnc3RhdGUubGFzdFJlc2V0IGlzIE5hTi4gT25seSBwb3NpdGl2ZSB0aW1lc3RhbXBzIGFyZSB2YWxpZC4nLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGxhc3RSZXNldCA9IDA7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gZG9uJ3QgdXBkYXRlIHN0YXRlIHdpdGggcHJvbWlzZXMgc3RhcnRlZCBiZWZvcmUgbGFzdCBjbGVhclxuICAgICAgICAgIGlmIChmZXRjaGVkQXQgPj0gbGFzdFJlc2V0KSB7XG4gICAgICAgICAgICB0aGlzLmNvbnRyb2xsZXIucmVzb2x2ZShhY3Rpb24uZW5kcG9pbnQsIHtcbiAgICAgICAgICAgICAgYXJnczogYWN0aW9uLmFyZ3MsXG4gICAgICAgICAgICAgIHJlc3BvbnNlLFxuICAgICAgICAgICAgICBmZXRjaGVkQXQsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlO1xuICAgICAgICB9KVxuICAgICAgICAuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICAgIGNvbnN0IGxhc3RSZXNldCA9IHRoaXMuZ2V0TGFzdFJlc2V0KCk7XG4gICAgICAgICAgLy8gZG9uJ3QgdXBkYXRlIHN0YXRlIHdpdGggcHJvbWlzZXMgc3RhcnRlZCBiZWZvcmUgbGFzdCBjbGVhclxuICAgICAgICAgIGlmIChmZXRjaGVkQXQgPj0gbGFzdFJlc2V0KSB7XG4gICAgICAgICAgICB0aGlzLmNvbnRyb2xsZXIucmVzb2x2ZShhY3Rpb24uZW5kcG9pbnQsIHtcbiAgICAgICAgICAgICAgYXJnczogYWN0aW9uLmFyZ3MsXG4gICAgICAgICAgICAgIHJlc3BvbnNlOiBlcnJvcixcbiAgICAgICAgICAgICAgZmV0Y2hlZEF0LFxuICAgICAgICAgICAgICBlcnJvcjogdHJ1ZSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfSk7XG4gICAgICByZXR1cm4gcHJvbWlzZTtcbiAgICB9O1xuXG4gICAgaWYgKHRocm90dGxlKSB7XG4gICAgICByZXR1cm4gdGhpcy50aHJvdHRsZShhY3Rpb24ua2V5LCBkZWZlcmVkRmV0Y2gsIGZldGNoZWRBdClcbiAgICAgICAgLnRoZW4oZGF0YSA9PiByZXNvbHZlKGRhdGEpKVxuICAgICAgICAuY2F0Y2goZXJyb3IgPT4gcmVqZWN0KGVycm9yKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBkZWZlcmVkRmV0Y2goKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgfVxuICB9XG5cbiAgLyoqIENhbGxlZCB3aGVuIG1pZGRsZXdhcmUgaW50ZXJjZXB0cyBhIHNldCBhY3Rpb24uXG4gICAqXG4gICAqIFdpbGwgcmVzb2x2ZSB0aGUgcHJvbWlzZSBhc3NvY2lhdGVkIHdpdGggc2V0IGtleS5cbiAgICovXG4gIHByb3RlY3RlZCBoYW5kbGVTZXQoYWN0aW9uOiBTZXRSZXNwb25zZUFjdGlvbikge1xuICAgIC8vIHRoaXMgY2FuIHN0aWxsIHR1cm4gb3V0IHRvIGJlIHVudHJ1ZSBzaW5jZSB0aGlzIGlzIGFzeW5jXG4gICAgaWYgKHRoaXMuZmV0Y2hpbmcuaGFzKGFjdGlvbi5rZXkpKSB7XG4gICAgICBjb25zdCB7IHJlamVjdCwgcmVzb2x2ZSB9ID0gdGhpcy5mZXRjaGluZy5nZXQoYWN0aW9uLmtleSkgYXMgRmV0Y2hpbmdNZXRhO1xuICAgICAgaWYgKGFjdGlvbi5lcnJvcikge1xuICAgICAgICByZWplY3QoYWN0aW9uLnJlc3BvbnNlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc29sdmUoYWN0aW9uLnJlc3BvbnNlKTtcbiAgICAgIH1cblxuICAgICAgLy8gc2luY2Ugd2UncmUgcmVzb2x2ZWQgd2Ugbm8gbG9uZ2VyIG5lZWQgdG8ga2VlcCB0cmFjayBvZiB0aGlzIHByb21pc2VcbiAgICAgIHRoaXMuY2xlYXIoYWN0aW9uLmtleSk7XG4gICAgfVxuICB9XG5cbiAgLyoqIEVuc3VyZXMgb25seSBvbmUgcmVxdWVzdCBmb3IgYSBnaXZlbiBrZXkgaXMgaW4gZmxpZ2h0IGF0IGFueSB0aW1lXG4gICAqXG4gICAqIFVzZXMga2V5IHRvIGVpdGhlciByZXRyaWV2ZSBpbi1mbGlnaHQgcHJvbWlzZSwgb3IgaWYgbm90XG4gICAqIGNyZWF0ZSBhIG5ldyBwcm9taXNlIGFuZCBjYWxsIGZldGNoLlxuICAgKlxuICAgKiBOb3RlOiBUaGUgbmV3IHByb21pc2UgaXMgbm90IGFjdHVhbGx5IHRpZWQgdG8gZmV0Y2ggYXQgYWxsLFxuICAgKiBidXQgaXMgcmVzb2x2ZWQgd2hlbiB0aGUgZXhwZWN0ZWQgJ3JlY2VpdmUnIGFjdGlvbiBpcyBwcm9jZXNzZWQuXG4gICAqIFRoaXMgZW5zdXJlcyBwcm9taXNlcyBhcmUgcmVzb2x2ZWQgb25seSBvbmNlIHRoZWlyIGRhdGEgaXMgcHJvY2Vzc2VkXG4gICAqIGJ5IHRoZSByZWR1Y2VyLlxuICAgKi9cbiAgcHJvdGVjdGVkIHRocm90dGxlKFxuICAgIGtleTogc3RyaW5nLFxuICAgIGZldGNoOiAoKSA9PiBQcm9taXNlPGFueT4sXG4gICAgZmV0Y2hlZEF0OiBudW1iZXIsXG4gICk6IFByb21pc2U8YW55PiB7XG4gICAgY29uc3QgbGFzdFJlc2V0ID0gdGhpcy5nZXRMYXN0UmVzZXQoKTtcbiAgICBsZXQgZmV0Y2hNZXRhID0gdGhpcy5mZXRjaGluZy5nZXQoa2V5KTtcblxuICAgIC8vIHdlJ3JlIGFscmVhZHkgZmV0Y2hpbmcgc28gcmV1c2UgdGhlIHByb21pc2VcbiAgICAvLyBmZXRjaGVzIGFmdGVyIHJlc2V0IGRvIG5vdCBjb3VudFxuICAgIGlmIChmZXRjaE1ldGEgJiYgZmV0Y2hNZXRhLmZldGNoZWRBdCA+IGxhc3RSZXNldCkge1xuICAgICAgcmV0dXJuIGZldGNoTWV0YS5wcm9taXNlO1xuICAgIH1cblxuICAgIGZldGNoTWV0YSA9IG5ld0ZldGNoTWV0YShmZXRjaGVkQXQpO1xuICAgIHRoaXMuZmV0Y2hpbmcuc2V0KGtleSwgZmV0Y2hNZXRhKTtcblxuICAgIHRoaXMuaWRsZUNhbGxiYWNrKFxuICAgICAgKCkgPT4ge1xuICAgICAgICAvLyBzaW5jZSBvdXIgcmVhbCBwcm9taXNlIGlzIHJlc29sdmVkIHZpYSB0aGUgd3JhcFJlZHVjZXIoKSxcbiAgICAgICAgLy8gd2Ugc2hvdWxkIGp1c3Qgc3RvcCBhbGwgZXJyb3JzIGhlcmUuXG4gICAgICAgIC8vIFRPRE86IGRlY291cGxlIHRoaXMgZnJvbSB1c2VGZXRjaGVyKCkgKHRoYXQncyB3aGF0J3MgZGlzcGF0Y2hpbmcgdGhlIGVycm9yIHRoZSByZXNvbHZlcyBpbiBoZXJlKVxuICAgICAgICBmZXRjaCgpLmNhdGNoKCgpID0+IG51bGwpO1xuICAgICAgfSxcbiAgICAgIHsgdGltZW91dDogNTAwIH0sXG4gICAgKTtcblxuICAgIHJldHVybiBmZXRjaE1ldGEucHJvbWlzZTtcbiAgfVxuXG4gIC8qKiBDYWxscyB0aGUgY2FsbGJhY2sgd2hlbiBjbGllbnQgaXMgbm90ICdidXN5JyB3aXRoIGhpZ2ggcHJpb3JpdHkgaW50ZXJhY3Rpb24gdGFza3NcbiAgICpcbiAgICogT3ZlcnJpZGUgZm9yIHBsYXRmb3JtLXNwZWNpZmljIGltcGxlbWVudGF0aW9uc1xuICAgKi9cbiAgcHJvdGVjdGVkIGlkbGVDYWxsYmFjayhcbiAgICBjYWxsYmFjazogKC4uLmFyZ3M6IGFueVtdKSA9PiB2b2lkLFxuICAgIG9wdGlvbnM/OiBJZGxlUmVxdWVzdE9wdGlvbnMsXG4gICkge1xuICAgIGNhbGxiYWNrKCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gbmV3RmV0Y2hNZXRhKGZldGNoZWRBdDogbnVtYmVyKTogRmV0Y2hpbmdNZXRhIHtcbiAgY29uc3QgZmV0Y2hNZXRhID0geyBmZXRjaGVkQXQgfSBhcyBGZXRjaGluZ01ldGE7XG4gIGZldGNoTWV0YS5wcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGZldGNoTWV0YS5yZXNvbHZlID0gcmVzb2x2ZTtcbiAgICBmZXRjaE1ldGEucmVqZWN0ID0gcmVqZWN0O1xuICB9KTtcbiAgcmV0dXJuIGZldGNoTWV0YTtcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsWUFBWSxFQUFFQyxLQUFLLEVBQUVDLEtBQUssUUFBUSxtQkFBbUI7QUFDOUQsU0FBU0MsaUJBQWlCLFFBQVEsZ0NBQWdDO0FBQ2xFLE9BQU9DLFVBQVUsTUFBTSw2QkFBNkI7QUFTcEQsT0FBTyxNQUFNQyxVQUFVLFNBQVNDLEtBQUssQ0FBQztFQUdwQ0MsV0FBV0EsQ0FBQSxFQUFHO0lBQ1osS0FBSyxDQUFDLHNCQUFzQixDQUFDO0lBQUMsS0FIaENDLElBQUksR0FBRyxZQUFZO0VBSW5CO0FBQ0Y7QUFTQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxlQUFlLE1BQU1DLGNBQWMsQ0FBb0I7RUFPckRGLFdBQVdBLENBQUM7SUFBRUcsZ0JBQWdCLEdBQUcsS0FBSztJQUFFQyxpQkFBaUIsR0FBRztFQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRTtJQUFBLEtBTi9EQyxRQUFRLEdBQThCLElBQUlDLEdBQUcsQ0FBQyxDQUFDO0lBQUEsS0FHL0NDLFVBQVUsR0FBZSxJQUFJVixVQUFVLENBQUMsQ0FBQztJQUFBLEtBUW5EVyxVQUFVLEdBQWVELFVBQVUsSUFBSTtNQUNyQyxJQUFJLENBQUNBLFVBQVUsR0FBR0EsVUFBVTtNQUM1QixPQUFPRSxJQUFJLElBQUlDLE1BQU0sSUFBSTtRQUN2QixRQUFRQSxNQUFNLENBQUNDLElBQUk7VUFDakIsS0FBS2pCLEtBQUs7WUFDUixJQUFJLENBQUNrQixXQUFXLENBQUNGLE1BQU0sQ0FBQztZQUN4QjtZQUNBO1lBQ0E7WUFDQSxJQUNFQSxNQUFNLENBQUNHLFFBQVEsQ0FBQ0MscUJBQXFCLEtBQUtDLFNBQVMsSUFDbkRMLE1BQU0sQ0FBQ0csUUFBUSxDQUFDRyxVQUFVLEVBQzFCO2NBQ0EsT0FBT1AsSUFBSSxDQUFDQyxNQUFNLENBQUM7WUFD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