UNPKG

@data-client/core

Version:

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

256 lines (245 loc) 30.9 kB
import _extends from "@babel/runtime/helpers/esm/extends"; 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.fetched = Object.create(null); this.resolvers = {}; this.rejectors = {}; this.fetchedAt = {}; 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 (action.key in this.fetched) { 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: { const rejectors = _extends({}, this.rejectors); 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 k in rejectors) { rejectors[k](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 && action.key in this.fetched; } allSettled() { const fetches = Object.values(this.fetched); if (fetches.length) return Promise.allSettled(fetches); } /** Clear all promise state */ clearAll() { for (const k in this.rejectors) { this.clear(k); } } /** Clear promise state for a given key */ clear(key) { this.fetched[key].catch(() => {}); delete this.resolvers[key]; delete this.rejectors[key]; delete this.fetched[key]; delete this.fetchedAt[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 (action.key in this.fetched) { let promiseHandler; if (action.error) { promiseHandler = this.rejectors[action.key]; } else { promiseHandler = this.resolvers[action.key]; } promiseHandler(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 'recieve' 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(); // we're already fetching so reuse the promise // fetches after reset do not count if (key in this.fetched && this.fetchedAt[key] > lastReset) { return this.fetched[key]; } this.fetched[key] = new Promise((resolve, reject) => { this.resolvers[key] = resolve; this.rejectors[key] = reject; }); this.fetchedAt[key] = fetchedAt; 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 this.fetched[key]; } /** Calls the callback when client is not 'busy' with high priority interaction tasks * * Override for platform-specific implementations */ idleCallback(callback, options) { callback(); } } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJTRVRfUkVTUE9OU0UiLCJGRVRDSCIsIlJFU0VUIiwiY3JlYXRlU2V0UmVzcG9uc2UiLCJDb250cm9sbGVyIiwiUmVzZXRFcnJvciIsIkVycm9yIiwiY29uc3RydWN0b3IiLCJuYW1lIiwiTmV0d29ya01hbmFnZXIiLCJkYXRhRXhwaXJ5TGVuZ3RoIiwiZXJyb3JFeHBpcnlMZW5ndGgiLCJmZXRjaGVkIiwiT2JqZWN0IiwiY3JlYXRlIiwicmVzb2x2ZXJzIiwicmVqZWN0b3JzIiwiZmV0Y2hlZEF0IiwiY29udHJvbGxlciIsIm1pZGRsZXdhcmUiLCJuZXh0IiwiYWN0aW9uIiwidHlwZSIsImhhbmRsZUZldGNoIiwiZW5kcG9pbnQiLCJnZXRPcHRpbWlzdGljUmVzcG9uc2UiLCJ1bmRlZmluZWQiLCJzaWRlRWZmZWN0IiwiUHJvbWlzZSIsInJlc29sdmUiLCJ0aGVuIiwia2V5IiwiX2NvbnRyb2xsZXIkZ2V0U3RhdGUkIiwiZXJyb3IiLCJnZXRTdGF0ZSIsIm1ldGEiLCJoYW5kbGVTZXQiLCJhcmdzIiwicmVzcG9uc2UiLCJfZXh0ZW5kcyIsImNsZWFyQWxsIiwiayIsImluaXQiLCJjbGVhbnVwRGF0ZSIsImNsZWFudXAiLCJEYXRlIiwibm93Iiwic2tpcExvZ2dpbmciLCJhbGxTZXR0bGVkIiwiZmV0Y2hlcyIsInZhbHVlcyIsImxlbmd0aCIsImNsZWFyIiwiY2F0Y2giLCJnZXRMYXN0UmVzZXQiLCJsYXN0UmVzZXQiLCJyZWplY3QiLCJ0aHJvdHRsZSIsImRlZmVyZWRGZXRjaCIsInByb21pc2UiLCJyZXNvbHZlUHJvbWlzZSIsImRhdGEiLCJwcm9jZXNzIiwiZW52IiwiTk9ERV9FTlYiLCJpc05hTiIsImNvbnNvbGUiLCJwcm9taXNlSGFuZGxlciIsImZldGNoIiwiaWRsZUNhbGxiYWNrIiwidGltZW91dCIsImNhbGxiYWNrIiwib3B0aW9ucyJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9tYW5hZ2VyL05ldHdvcmtNYW5hZ2VyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFNFVF9SRVNQT05TRSwgRkVUQ0gsIFJFU0VUIH0gZnJvbSAnLi4vYWN0aW9uVHlwZXMuanMnO1xuaW1wb3J0IHsgY3JlYXRlU2V0UmVzcG9uc2UgfSBmcm9tICcuLi9jb250cm9sbGVyL2FjdGlvbnMvaW5kZXguanMnO1xuaW1wb3J0IENvbnRyb2xsZXIgZnJvbSAnLi4vY29udHJvbGxlci9Db250cm9sbGVyLmpzJztcbmltcG9ydCB0eXBlIHtcbiAgRmV0Y2hBY3Rpb24sXG4gIE1hbmFnZXIsXG4gIEFjdGlvblR5cGVzLFxuICBNaWRkbGV3YXJlQVBJLFxuICBNaWRkbGV3YXJlLFxuICBTZXRSZXNwb25zZUFjdGlvbixcbn0gZnJvbSAnLi4vdHlwZXMuanMnO1xuXG5leHBvcnQgY2xhc3MgUmVzZXRFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgbmFtZSA9ICdSZXNldEVycm9yJztcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICBzdXBlcignQWJvcnRlZCBkdWUgdG8gUkVTRVQnKTtcbiAgfVxufVxuXG4vKiogSGFuZGxlcyBhbGwgYXN5bmMgbmV0d29yayBkaXNwYXRjaGVzXG4gKlxuICogRGVkdXBlcyBjb25jdXJyZW50IHJlcXVlc3RzIGJ5IGtlZXBpbmcgdHJhY2sgb2YgYWxsIGZldGNoZXMgaW4gZmxpZ2h0XG4gKiBhbmQgcmV0dXJuaW5nIGV4aXN0aW5nIHByb21pc2VzIGZvciByZXF1ZXN0cyBhbHJlYWR5IGluIGZsaWdodC5cbiAqXG4gKiBJbnRlcmZhY2VzIHdpdGggc3RvcmUgdmlhIGEgcmVkdXgtY29tcGF0aWJsZSBtaWRkbGV3YXJlLlxuICpcbiAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL05ldHdvcmtNYW5hZ2VyXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE5ldHdvcmtNYW5hZ2VyIGltcGxlbWVudHMgTWFuYWdlciB7XG4gIHByb3RlY3RlZCBmZXRjaGVkOiB7IFtrOiBzdHJpbmddOiBQcm9taXNlPGFueT4gfSA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gIHByb3RlY3RlZCByZXNvbHZlcnM6IHsgW2s6IHN0cmluZ106ICh2YWx1ZT86IGFueSkgPT4gdm9pZCB9ID0ge307XG4gIHByb3RlY3RlZCByZWplY3RvcnM6IHsgW2s6IHN0cmluZ106ICh2YWx1ZT86IGFueSkgPT4gdm9pZCB9ID0ge307XG4gIHByb3RlY3RlZCBmZXRjaGVkQXQ6IHsgW2s6IHN0cmluZ106IG51bWJlciB9ID0ge307XG4gIGRlY2xhcmUgcmVhZG9ubHkgZGF0YUV4cGlyeUxlbmd0aDogbnVtYmVyO1xuICBkZWNsYXJlIHJlYWRvbmx5IGVycm9yRXhwaXJ5TGVuZ3RoOiBudW1iZXI7XG4gIHByb3RlY3RlZCBjb250cm9sbGVyOiBDb250cm9sbGVyID0gbmV3IENvbnRyb2xsZXIoKTtcbiAgZGVjbGFyZSBjbGVhbnVwRGF0ZT86IG51bWJlcjtcblxuICBjb25zdHJ1Y3Rvcih7IGRhdGFFeHBpcnlMZW5ndGggPSA2MDAwMCwgZXJyb3JFeHBpcnlMZW5ndGggPSAxMDAwIH0gPSB7fSkge1xuICAgIHRoaXMuZGF0YUV4cGlyeUxlbmd0aCA9IGRhdGFFeHBpcnlMZW5ndGg7XG4gICAgdGhpcy5lcnJvckV4cGlyeUxlbmd0aCA9IGVycm9yRXhwaXJ5TGVuZ3RoO1xuICB9XG5cbiAgbWlkZGxld2FyZTogTWlkZGxld2FyZSA9IGNvbnRyb2xsZXIgPT4ge1xuICAgIHRoaXMuY29udHJvbGxlciA9IGNvbnRyb2xsZXI7XG4gICAgcmV0dXJuIG5leHQgPT4gYWN0aW9uID0+IHtcbiAgICAgIHN3aXRjaCAoYWN0aW9uLnR5cGUpIHtcbiAgICAgICAgY2FzZSBGRVRDSDpcbiAgICAgICAgICB0aGlzLmhhbmRsZUZldGNoKGFjdGlvbik7XG4gICAgICAgICAgLy8gVGhpcyBpcyB0aGUgb25seSBjYXNlIHRoYXQgY2F1c2VzIGFueSBzdGF0ZSBjaGFuZ2VcbiAgICAgICAgICAvLyBJdCdzIGltcG9ydGFudCB0byBpbnRlcmNlcHQgb3RoZXIgZmV0Y2hlcyBhcyB3ZSBkb24ndCB3YW50IHRvIHRyaWdnZXIgcmVkdWNlcnMgZHVyaW5nXG4gICAgICAgICAgLy8gcmVuZGVyIC0gc28gd2UgbmVlZCB0byBzdG9wICdyZWFkb25seScgZmV0Y2hlcyB3aGljaCBjYW4gYmUgdHJpZ2dlcmVkIGluIHJlbmRlclxuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIGFjdGlvbi5lbmRwb2ludC5nZXRPcHRpbWlzdGljUmVzcG9uc2UgIT09IHVuZGVmaW5lZCAmJlxuICAgICAgICAgICAgYWN0aW9uLmVuZHBvaW50LnNpZGVFZmZlY3RcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIHJldHVybiBuZXh0KGFjdGlvbik7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICAgICAgY2FzZSBTRVRfUkVTUE9OU0U6XG4gICAgICAgICAgLy8gb25seSBzZXQgYWZ0ZXIgbmV3IHN0YXRlIGlzIGNvbXB1dGVkXG4gICAgICAgICAgcmV0dXJuIG5leHQoYWN0aW9uKS50aGVuKCgpID0+IHtcbiAgICAgICAgICAgIGlmIChhY3Rpb24ua2V5IGluIHRoaXMuZmV0Y2hlZCkge1xuICAgICAgICAgICAgICAvLyBOb3RlOiBtZXRhICptdXN0KiBiZSBzZXQgYnkgcmVkdWNlciBzbyB0aGlzIHNob3VsZCBiZSBzYWZlXG4gICAgICAgICAgICAgIGNvbnN0IGVycm9yID0gY29udHJvbGxlci5nZXRTdGF0ZSgpLm1ldGFbYWN0aW9uLmtleV0/LmVycm9yO1xuICAgICAgICAgICAgICAvLyBwcm9jZXNzaW5nIGVycm9ycyByZXN1bHQgaW4gc3RhdGUgbWV0YSBoYXZpbmcgZXJyb3IsIHNvIHdlIHNob3VsZCByZWplY3QgdGhlIHByb21pc2VcbiAgICAgICAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVTZXQoXG4gICAgICAgICAgICAgICAgICBjcmVhdGVTZXRSZXNwb25zZShhY3Rpb24uZW5kcG9pbnQsIHtcbiAgICAgICAgICAgICAgICAgICAgYXJnczogYWN0aW9uLmFyZ3MsXG4gICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlOiBlcnJvcixcbiAgICAgICAgICAgICAgICAgICAgZmV0Y2hlZEF0OiBhY3Rpb24ubWV0YS5mZXRjaGVkQXQsXG4gICAgICAgICAgICAgICAgICAgIGVycm9yOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZVNldChhY3Rpb24pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgIGNhc2UgUkVTRVQ6IHtcbiAgICAgICAgICBjb25zdCByZWplY3RvcnMgPSB7IC4uLnRoaXMucmVqZWN0b3JzIH07XG5cbiAgICAgICAgICB0aGlzLmNsZWFyQWxsKCk7XG4gICAgICAgICAgcmV0dXJuIG5leHQoYWN0aW9uKS50aGVuKCgpID0+IHtcbiAgICAgICAgICAgIC8vIHRoZXJlIGNvdWxkIGJlIGV4dGVybmFsIGxpc3RlbmVycyB0byB0aGUgcHJvbWlzZVxuICAgICAgICAgICAgLy8gdGhpcyBtdXN0IGhhcHBlbiBhZnRlciBjb21taXQgc28gb3VyIG93biByZWplY3RvciBrbm93cyBub3QgdG8gZGlzcGF0Y2ggYW4gZXJyb3IgYmFzZWQgb24gdGhpc1xuICAgICAgICAgICAgZm9yIChjb25zdCBrIGluIHJlamVjdG9ycykge1xuICAgICAgICAgICAgICByZWplY3RvcnNba10obmV3IFJlc2V0RXJyb3IoKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICByZXR1cm4gbmV4dChhY3Rpb24pO1xuICAgICAgfVxuICAgIH07XG4gIH07XG5cbiAgLyoqIE9uIG1vdW50ICovXG4gIGluaXQoKSB7XG4gICAgZGVsZXRlIHRoaXMuY2xlYW51cERhdGU7XG4gIH1cblxuICAvKiogRW5zdXJlcyBhbGwgcHJvbWlzZXMgYXJlIGNvbXBsZXRlZCBieSByZWplY3RpbmcgcmVtYWluaW5nLiAqL1xuICBjbGVhbnVwKCkge1xuICAgIC8vIGVuc3VyZSBubyBkaXNwYXRjaGVzIGFmdGVyIHVubW91bnRcbiAgICAvLyB0aGlzIG11c3QgYmUgcmV2ZXJzaWJsZSAoZG9uZSBpbiBpbml0KSBzbyB1c2VFZmZlY3QoKSByZW1haW5zIHN5bW1ldHJpY1xuICAgIHRoaXMuY2xlYW51cERhdGUgPSBEYXRlLm5vdygpO1xuICB9XG5cbiAgLyoqIFVzZWQgYnkgRGV2dG9vbHNNYW5hZ2VyIHRvIGRldGVybWluZSB3aGV0aGVyIHRvIGxvZyBhbiBhY3Rpb24gKi9cbiAgc2tpcExvZ2dpbmcoYWN0aW9uOiBBY3Rpb25UeXBlcykge1xuICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0ICovXG4gICAgcmV0dXJuIGFjdGlvbi50eXBlID09PSBGRVRDSCAmJiBhY3Rpb24ua2V5IGluIHRoaXMuZmV0Y2hlZDtcbiAgfVxuXG4gIGFsbFNldHRsZWQoKSB7XG4gICAgY29uc3QgZmV0Y2hlcyA9IE9iamVjdC52YWx1ZXModGhpcy5mZXRjaGVkKTtcbiAgICBpZiAoZmV0Y2hlcy5sZW5ndGgpIHJldHVybiBQcm9taXNlLmFsbFNldHRsZWQoZmV0Y2hlcyk7XG4gIH1cblxuICAvKiogQ2xlYXIgYWxsIHByb21pc2Ugc3RhdGUgKi9cbiAgcHJvdGVjdGVkIGNsZWFyQWxsKCkge1xuICAgIGZvciAoY29uc3QgayBpbiB0aGlzLnJlamVjdG9ycykge1xuICAgICAgdGhpcy5jbGVhcihrKTtcbiAgICB9XG4gIH1cblxuICAvKiogQ2xlYXIgcHJvbWlzZSBzdGF0ZSBmb3IgYSBnaXZlbiBrZXkgKi9cbiAgcHJvdGVjdGVkIGNsZWFyKGtleTogc3RyaW5nKSB7XG4gICAgdGhpcy5mZXRjaGVkW2tleV0uY2F0Y2goKCkgPT4ge30pO1xuICAgIGRlbGV0ZSB0aGlzLnJlc29sdmVyc1trZXldO1xuICAgIGRlbGV0ZSB0aGlzLnJlamVjdG9yc1trZXldO1xuICAgIGRlbGV0ZSB0aGlzLmZldGNoZWRba2V5XTtcbiAgICBkZWxldGUgdGhpcy5mZXRjaGVkQXRba2V5XTtcbiAgfVxuXG4gIHByb3RlY3RlZCBnZXRMYXN0UmVzZXQoKSB7XG4gICAgaWYgKHRoaXMuY2xlYW51cERhdGUpIHJldHVybiB0aGlzLmNsZWFudXBEYXRlO1xuICAgIHJldHVybiB0aGlzLmNvbnRyb2xsZXIuZ2V0U3RhdGUoKS5sYXN0UmVzZXQ7XG4gIH1cblxuICAvKiogQ2FsbGVkIHdoZW4gbWlkZGxld2FyZSBpbnRlcmNlcHRzICdyZGMvZmV0Y2gnIGFjdGlvbi5cbiAgICpcbiAgICogV2lsbCB0aGVuIHN0YXJ0IGEgcHJvbWlzZSBmb3IgYSBrZXkgYW5kIHBvdGVudGlhbGx5IHN0YXJ0IHRoZSBuZXR3b3JrXG4gICAqIGZldGNoLlxuICAgKlxuICAgKiBVc2VzIHRocm90dGxlIGVuZHBvaW50cyB3aXRob3V0IHNpZGVFZmZlY3RzLiBUaGlzIGlzIHZhbHVhYmxlXG4gICAqIGZvciBlbnN1cmVzIG11dGF0aW9uIHJlcXVlc3RzIGFsd2F5cyBnbyB0aHJvdWdoLlxuICAgKi9cbiAgcHJvdGVjdGVkIGhhbmRsZUZldGNoKGFjdGlvbjogRmV0Y2hBY3Rpb24pIHtcbiAgICBjb25zdCB7IHJlc29sdmUsIHJlamVjdCwgZmV0Y2hlZEF0IH0gPSBhY3Rpb24ubWV0YTtcbiAgICBjb25zdCB0aHJvdHRsZSA9ICFhY3Rpb24uZW5kcG9pbnQuc2lkZUVmZmVjdDtcblxuICAgIGNvbnN0IGRlZmVyZWRGZXRjaCA9ICgpID0+IHtcbiAgICAgIGxldCBwcm9taXNlID0gYWN0aW9uLmVuZHBvaW50KC4uLmFjdGlvbi5hcmdzKTtcbiAgICAgIGNvbnN0IHJlc29sdmVQcm9taXNlID0gKFxuICAgICAgICBwcm9taXNlOiBQcm9taXNlPHN0cmluZyB8IG51bWJlciB8IG9iamVjdCB8IG51bGw+LFxuICAgICAgKSA9PlxuICAgICAgICBwcm9taXNlXG4gICAgICAgICAgLnRoZW4oZGF0YSA9PiB7XG4gICAgICAgICAgICByZXNvbHZlKGRhdGEpO1xuICAgICAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICAgICAgfSlcbiAgICAgICAgICAuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcbiAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICAgIH0pO1xuICAgICAgLy8gc2NoZWR1bGUgbm9uLXRocm90dGxlZCByZXNvbHV0aW9ucyBpbiBhIG1pY3JvdGFzayBiZWZvcmUgc2V0XG4gICAgICAvLyB0aGlzIGVuYWJsZXMgdXNlcnMgYXdhaXRpbmcgdGhlaXIgZmV0Y2ggdG8gdHJpZ2dlciBhbnkgcmVhY3QgdXBkYXRlcyBuZWVkZWQgdG8gZGVhbFxuICAgICAgLy8gd2l0aCB1cGNvbWluZyBjaGFuZ2VzIGJlY2F1c2Ugb2YgdGhlIGZldGNoIChmb3IgaW5zdGFuY2UgYXZvaWRpbmcgc3VzcGVuc2UgaWYgc29tZXRoaW5nIGlzIGRlbGV0ZWQpXG4gICAgICBpZiAoIXRocm90dGxlKSB7XG4gICAgICAgIHByb21pc2UgPSByZXNvbHZlUHJvbWlzZShwcm9taXNlKTtcbiAgICAgIH1cbiAgICAgIHByb21pc2UgPSBwcm9taXNlXG4gICAgICAgIC50aGVuKHJlc3BvbnNlID0+IHtcbiAgICAgICAgICBsZXQgbGFzdFJlc2V0ID0gdGhpcy5nZXRMYXN0UmVzZXQoKTtcblxuICAgICAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBlbHNlICovXG4gICAgICAgICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicgJiYgaXNOYU4obGFzdFJlc2V0KSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgICAgJ3N0YXRlLmxhc3RSZXNldCBpcyBOYU4uIE9ubHkgcG9zaXRpdmUgdGltZXN0YW1wcyBhcmUgdmFsaWQuJyxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBsYXN0UmVzZXQgPSAwO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIGRvbid0IHVwZGF0ZSBzdGF0ZSB3aXRoIHByb21pc2VzIHN0YXJ0ZWQgYmVmb3JlIGxhc3QgY2xlYXJcbiAgICAgICAgICBpZiAoZmV0Y2hlZEF0ID49IGxhc3RSZXNldCkge1xuICAgICAgICAgICAgdGhpcy5jb250cm9sbGVyLnJlc29sdmUoYWN0aW9uLmVuZHBvaW50LCB7XG4gICAgICAgICAgICAgIGFyZ3M6IGFjdGlvbi5hcmdzLFxuICAgICAgICAgICAgICByZXNwb25zZSxcbiAgICAgICAgICAgICAgZmV0Y2hlZEF0LFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiByZXNwb25zZTtcbiAgICAgICAgfSlcbiAgICAgICAgLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgICBjb25zdCBsYXN0UmVzZXQgPSB0aGlzLmdldExhc3RSZXNldCgpO1xuICAgICAgICAgIC8vIGRvbid0IHVwZGF0ZSBzdGF0ZSB3aXRoIHByb21pc2VzIHN0YXJ0ZWQgYmVmb3JlIGxhc3QgY2xlYXJcbiAgICAgICAgICBpZiAoZmV0Y2hlZEF0ID49IGxhc3RSZXNldCkge1xuICAgICAgICAgICAgdGhpcy5jb250cm9sbGVyLnJlc29sdmUoYWN0aW9uLmVuZHBvaW50LCB7XG4gICAgICAgICAgICAgIGFyZ3M6IGFjdGlvbi5hcmdzLFxuICAgICAgICAgICAgICByZXNwb25zZTogZXJyb3IsXG4gICAgICAgICAgICAgIGZldGNoZWRBdCxcbiAgICAgICAgICAgICAgZXJyb3I6IHRydWUsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH0pO1xuICAgICAgcmV0dXJuIHByb21pc2U7XG4gICAgfTtcblxuICAgIGlmICh0aHJvdHRsZSkge1xuICAgICAgcmV0dXJuIHRoaXMudGhyb3R0bGUoYWN0aW9uLmtleSwgZGVmZXJlZEZldGNoLCBmZXRjaGVkQXQpXG4gICAgICAgIC50aGVuKGRhdGEgPT4gcmVzb2x2ZShkYXRhKSlcbiAgICAgICAgLmNhdGNoKGVycm9yID0+IHJlamVjdChlcnJvcikpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gZGVmZXJlZEZldGNoKCkuY2F0Y2goKCkgPT4ge30pO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBDYWxsZWQgd2hlbiBtaWRkbGV3YXJlIGludGVyY2VwdHMgYSBzZXQgYWN0aW9uLlxuICAgKlxuICAgKiBXaWxsIHJlc29sdmUgdGhlIHByb21pc2UgYXNzb2NpYXRlZCB3aXRoIHNldCBrZXkuXG4gICAqL1xuICBwcm90ZWN0ZWQgaGFuZGxlU2V0KGFjdGlvbjogU2V0UmVzcG9uc2VBY3Rpb24pIHtcbiAgICAvLyB0aGlzIGNhbiBzdGlsbCB0dXJuIG91dCB0byBiZSB1bnRydWUgc2luY2UgdGhpcyBpcyBhc3luY1xuICAgIGlmIChhY3Rpb24ua2V5IGluIHRoaXMuZmV0Y2hlZCkge1xuICAgICAgbGV0IHByb21pc2VIYW5kbGVyOiAodmFsdWU/OiBhbnkpID0+IHZvaWQ7XG4gICAgICBpZiAoYWN0aW9uLmVycm9yKSB7XG4gICAgICAgIHByb21pc2VIYW5kbGVyID0gdGhpcy5yZWplY3RvcnNbYWN0aW9uLmtleV07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBwcm9taXNlSGFuZGxlciA9IHRoaXMucmVzb2x2ZXJzW2FjdGlvbi5rZXldO1xuICAgICAgfVxuICAgICAgcHJvbWlzZUhhbmRsZXIoYWN0aW9uLnJlc3BvbnNlKTtcbiAgICAgIC8vIHNpbmNlIHdlJ3JlIHJlc29sdmVkIHdlIG5vIGxvbmdlciBuZWVkIHRvIGtlZXAgdHJhY2sgb2YgdGhpcyBwcm9taXNlXG4gICAgICB0aGlzLmNsZWFyKGFjdGlvbi5rZXkpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBFbnN1cmVzIG9ubHkgb25lIHJlcXVlc3QgZm9yIGEgZ2l2ZW4ga2V5IGlzIGluIGZsaWdodCBhdCBhbnkgdGltZVxuICAgKlxuICAgKiBVc2VzIGtleSB0byBlaXRoZXIgcmV0cmlldmUgaW4tZmxpZ2h0IHByb21pc2UsIG9yIGlmIG5vdFxuICAgKiBjcmVhdGUgYSBuZXcgcHJvbWlzZSBhbmQgY2FsbCBmZXRjaC5cbiAgICpcbiAgICogTm90ZTogVGhlIG5ldyBwcm9taXNlIGlzIG5vdCBhY3R1YWxseSB0aWVkIHRvIGZldGNoIGF0IGFsbCxcbiAgICogYnV0IGlzIHJlc29sdmVkIHdoZW4gdGhlIGV4cGVjdGVkICdyZWNpZXZlJyBhY3Rpb24gaXMgcHJvY2Vzc2VkLlxuICAgKiBUaGlzIGVuc3VyZXMgcHJvbWlzZXMgYXJlIHJlc29sdmVkIG9ubHkgb25jZSB0aGVpciBkYXRhIGlzIHByb2Nlc3NlZFxuICAgKiBieSB0aGUgcmVkdWNlci5cbiAgICovXG4gIHByb3RlY3RlZCB0aHJvdHRsZShcbiAgICBrZXk6IHN0cmluZyxcbiAgICBmZXRjaDogKCkgPT4gUHJvbWlzZTxhbnk+LFxuICAgIGZldGNoZWRBdDogbnVtYmVyLFxuICApIHtcbiAgICBjb25zdCBsYXN0UmVzZXQgPSB0aGlzLmdldExhc3RSZXNldCgpO1xuICAgIC8vIHdlJ3JlIGFscmVhZHkgZmV0Y2hpbmcgc28gcmV1c2UgdGhlIHByb21pc2VcbiAgICAvLyBmZXRjaGVzIGFmdGVyIHJlc2V0IGRvIG5vdCBjb3VudFxuICAgIGlmIChrZXkgaW4gdGhpcy5mZXRjaGVkICYmIHRoaXMuZmV0Y2hlZEF0W2tleV0gPiBsYXN0UmVzZXQpIHtcbiAgICAgIHJldHVybiB0aGlzLmZldGNoZWRba2V5XTtcbiAgICB9XG5cbiAgICB0aGlzLmZldGNoZWRba2V5XSA9IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHRoaXMucmVzb2x2ZXJzW2tleV0gPSByZXNvbHZlO1xuICAgICAgdGhpcy5yZWplY3RvcnNba2V5XSA9IHJlamVjdDtcbiAgICB9KTtcbiAgICB0aGlzLmZldGNoZWRBdFtrZXldID0gZmV0Y2hlZEF0O1xuXG4gICAgdGhpcy5pZGxlQ2FsbGJhY2soXG4gICAgICAoKSA9PiB7XG4gICAgICAgIC8vIHNpbmNlIG91ciByZWFsIHByb21pc2UgaXMgcmVzb2x2ZWQgdmlhIHRoZSB3cmFwUmVkdWNlcigpLFxuICAgICAgICAvLyB3ZSBzaG91bGQganVzdCBzdG9wIGFsbCBlcnJvcnMgaGVyZS5cbiAgICAgICAgLy8gVE9ETzogZGVjb3VwbGUgdGhpcyBmcm9tIHVzZUZldGNoZXIoKSAodGhhdCdzIHdoYXQncyBkaXNwYXRjaGluZyB0aGUgZXJyb3IgdGhlIHJlc29sdmVzIGluIGhlcmUpXG4gICAgICAgIGZldGNoKCkuY2F0Y2goKCkgPT4gbnVsbCk7XG4gICAgICB9LFxuICAgICAgeyB0aW1lb3V0OiA1MDAgfSxcbiAgICApO1xuXG4gICAgcmV0dXJuIHRoaXMuZmV0Y2hlZFtrZXldO1xuICB9XG5cbiAgLyoqIENhbGxzIHRoZSBjYWxsYmFjayB3aGVuIGNsaWVudCBpcyBub3QgJ2J1c3knIHdpdGggaGlnaCBwcmlvcml0eSBpbnRlcmFjdGlvbiB0YXNrc1xuICAgKlxuICAgKiBPdmVycmlkZSBmb3IgcGxhdGZvcm0tc3BlY2lmaWMgaW1wbGVtZW50YXRpb25zXG4gICAqL1xuICBwcm90ZWN0ZWQgaWRsZUNhbGxiYWNrKFxuICAgIGNhbGxiYWNrOiAoLi4uYXJnczogYW55W10pID0+IHZvaWQsXG4gICAgb3B0aW9ucz86IElkbGVSZXF1ZXN0T3B0aW9ucyxcbiAgKSB7XG4gICAgY2FsbGJhY2soKTtcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsWUFBWSxFQUFFQyxLQUFLLEVBQUVDLEtBQUssUUFBUSxtQkFBbUI7QUFDOUQsU0FBU0MsaUJBQWlCLFFBQVEsZ0NBQWdDO0FBQ2xFLE9BQU9DLFVBQVUsTUFBTSw2QkFBNkI7QUFVcEQsT0FBTyxNQUFNQyxVQUFVLFNBQVNDLEtBQUssQ0FBQztFQUdwQ0MsV0FBV0EsQ0FBQSxFQUFHO0lBQ1osS0FBSyxDQUFDLHNCQUFzQixDQUFDO0lBQUMsS0FIaENDLElBQUksR0FBRyxZ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