@data-client/core
Version:
Async State Management without the Management. REST, GraphQL, SSE, Websockets, Fetch
267 lines (255 loc) • 31.6 kB
JavaScript
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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