@data-client/core
Version:
Async State Management without the Management. REST, GraphQL, SSE, Websockets, Fetch
426 lines (397 loc) • 66.2 kB
JavaScript
import { ExpiryStatus, MemoCache, isEntity, denormalize, validateQueryKey } from '@data-client/normalizr';
import AbortOptimistic from './AbortOptimistic.js';
import { createUnsubscription, createSubscription } from './actions/createSubscription.js';
import { createExpireAll, createFetch, createInvalidate, createInvalidateAll, createReset, createSet, createSetResponse } from './actions/index.js';
import ensurePojo from './ensurePojo.js';
import { ImmortalGCPolicy } from '../state/GCPolicy.js';
import { initialState } from '../state/reducer/createReducer.js';
import selectMeta from '../state/selectMeta.js';
const unsetDispatch = action => {
throw new Error(`Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.`);
};
const unsetState = () => {
// This is only the value until it is set by the DataProvider
/* istanbul ignore next */
return initialState;
};
/**
* Imperative control of Reactive Data Client store
* @see https://dataclient.io/docs/api/Controller
*/
export default class Controller {
/**
* Dispatches an action to Reactive Data Client reducer.
*
* @see https://dataclient.io/docs/api/Controller#dispatch
*/
/**
* Gets the latest state snapshot that is fully committed.
*
* This can be useful for imperative use-cases like event handlers.
* This should *not* be used to render; instead useSuspense() or useCache()
* @see https://dataclient.io/docs/api/Controller#getState
*/
/**
* Singleton to maintain referential equality between calls
*/
/**
* Handles garbage collection
*/
constructor({
dispatch = unsetDispatch,
getState = unsetState,
memo = new MemoCache(),
gcPolicy = new ImmortalGCPolicy()
} = {}) {
/*************** Action Dispatchers ***************/
/**
* Fetches the endpoint with given args, updating the Reactive Data Client cache with the response or error upon completion.
* @see https://dataclient.io/docs/api/Controller#fetch
*/
this.fetch = (endpoint, ...args) => {
const action = createFetch(endpoint, {
args
});
this.dispatch(action);
if (endpoint.schema) {
return action.meta.promise.then(input => denormalize(endpoint.schema, input, {}, args));
}
return action.meta.promise;
};
/**
* Fetches only if endpoint is considered 'stale'; otherwise returns undefined
* @see https://dataclient.io/docs/api/Controller#fetchIfStale
*/
this.fetchIfStale = (endpoint, ...args) => {
const {
data,
expiresAt,
expiryStatus
} = this.getResponse(endpoint, ...args, this.getState());
if (expiryStatus !== ExpiryStatus.Invalid && Date.now() <= expiresAt) return data;
return this.fetch(endpoint, ...args);
};
/**
* Forces refetching and suspense on useSuspense with the same Endpoint and parameters.
* @see https://dataclient.io/docs/api/Controller#invalidate
*/
this.invalidate = (endpoint, ...args) => args[0] !== null ? this.dispatch(createInvalidate(endpoint, {
args: args
})) : Promise.resolve();
/**
* Forces refetching and suspense on useSuspense on all matching endpoint result keys.
* @see https://dataclient.io/docs/api/Controller#invalidateAll
* @returns Promise that resolves when invalidation is commited.
*/
this.invalidateAll = options => this.dispatch(createInvalidateAll(key => options.testKey(key)));
/**
* Sets all matching endpoint result keys to be STALE.
* @see https://dataclient.io/docs/api/Controller#expireAll
* @returns Promise that resolves when expiry is commited. *NOT* fetch promise
*/
this.expireAll = options => this.dispatch(createExpireAll(key => options.testKey(key)));
/**
* Resets the entire Reactive Data Client cache. All inflight requests will not resolve.
* @see https://dataclient.io/docs/api/Controller#resetEntireStore
*/
this.resetEntireStore = () => this.dispatch(createReset());
/**
* Sets response for the Endpoint and args.
* @see https://dataclient.io/docs/api/Controller#setResponse
*/
this.setResponse = (endpoint, ...rest) => {
const response = rest[rest.length - 1];
const action = createSetResponse(endpoint, {
args: rest.slice(0, rest.length - 1),
response
});
return this.dispatch(action);
};
/**
* Sets an error response for the Endpoint and args.
* @see https://dataclient.io/docs/api/Controller#setError
*/
this.setError = (endpoint, ...rest) => {
const response = rest[rest.length - 1];
const action = createSetResponse(endpoint, {
args: rest.slice(0, rest.length - 1),
response,
error: true
});
return this.dispatch(action);
};
/**
* Resolves an inflight fetch.
* @see https://dataclient.io/docs/api/Controller#resolve
*/
this.resolve = (endpoint, meta) => {
return this.dispatch(createSetResponse(endpoint, meta));
};
/**
* Marks a new subscription to a given Endpoint.
* @see https://dataclient.io/docs/api/Controller#subscribe
*/
this.subscribe = (endpoint, ...args) => args[0] !== null ? this.dispatch(createSubscription(endpoint, {
args: args
})) : Promise.resolve();
/**
* Marks completion of subscription to a given Endpoint.
* @see https://dataclient.io/docs/api/Controller#unsubscribe
*/
this.unsubscribe = (endpoint, ...args) => args[0] !== null ? this.dispatch(createUnsubscription(endpoint, {
args: args
})) : Promise.resolve();
/*************** More ***************/
/* TODO:
abort = <E extends EndpointInterface>(
endpoint: E,
...args: readonly [...Parameters<E>]
): Promise<void>
*/
/**
* Gets a snapshot (https://dataclient.io/docs/api/Snapshot)
* @see https://dataclient.io/docs/api/Controller#snapshot
*/
this.snapshot = (state, fetchedAt) => {
return new Snapshot(this, state, fetchedAt);
};
this._dispatch = dispatch;
this.getState = getState;
this.memo = memo;
this.gcPolicy = gcPolicy;
}
// TODO: drop when drop support for destructuring (0.14 and below)
set dispatch(dispatch) {
/* istanbul ignore next */
this._dispatch = dispatch;
}
// TODO: drop when drop support for destructuring (0.14 and below)
get dispatch() {
return this._dispatch;
}
bindMiddleware({
dispatch,
getState
}) {
this._dispatch = dispatch;
this.getState = getState;
}
/**
* Sets value for the Queryable and args.
* @see https://dataclient.io/docs/api/Controller#set
*/
set(schema, ...rest) {
const value = rest[rest.length - 1];
const action = createSet(schema, {
args: rest.slice(0, rest.length - 1),
value
});
// TODO: reject with error if this fails in reducer
return this.dispatch(action);
}
/**
* Gets the error, if any, for a given endpoint. Returns undefined for no errors.
* @see https://dataclient.io/docs/api/Controller#getError
*/
getError(endpoint, ...rest) {
if (rest[0] === null) return;
const state = rest[rest.length - 1];
// this is typescript generics breaking
const args = rest.slice(0, rest.length - 1);
const key = endpoint.key(...args);
const meta = selectMeta(state, key);
const error = state.endpoints[key];
if (error !== undefined && (meta == null ? void 0 : meta.errorPolicy) === 'soft') return;
return meta == null ? void 0 : meta.error;
}
/**
* Gets the (globally referentially stable) response for a given endpoint/args pair from state given.
* @see https://dataclient.io/docs/api/Controller#getResponse
*/
getResponse(endpoint, ...rest) {
// TODO: breaking: only return data
return this.getResponseMeta(endpoint, ...rest);
}
/**
* Gets the (globally referentially stable) response for a given endpoint/args pair from state given.
* @see https://dataclient.io/docs/api/Controller#getResponseMeta
*/
getResponseMeta(endpoint, ...rest) {
const state = rest[rest.length - 1];
// this is typescript generics breaking
const args = rest.slice(0, rest.length - 1)
// handle FormData
.map(ensurePojo);
const isActive = args.length !== 1 || args[0] !== null;
const key = isActive ? endpoint.key(...args) : '';
const cacheEndpoints = isActive ? state.endpoints[key] : undefined;
const schema = endpoint.schema;
const meta = selectMeta(state, key);
let expiresAt = meta == null ? void 0 : meta.expiresAt;
// if we have no endpoint entry, and our endpoint has a schema - try querying the store
const shouldQuery = cacheEndpoints === undefined && schema !== undefined;
const input = shouldQuery ?
// nothing in endpoints cache, so try querying if we have a schema to do so
this.memo.buildQueryKey(schema, args, state.entities, state.indexes, key) : cacheEndpoints;
if (!isActive) {
// when not active simply return the query input without denormalizing
return {
data: input,
expiryStatus: ExpiryStatus.Valid,
expiresAt: Infinity,
countRef: () => () => undefined
};
}
let isInvalid = false;
if (shouldQuery) {
isInvalid = !validateQueryKey(input);
// endpoint without entities
} else if (!schema || !schemaHasEntity(schema)) {
return {
data: cacheEndpoints,
expiryStatus: meta != null && meta.invalidated ? ExpiryStatus.Invalid : cacheEndpoints && !endpoint.invalidIfStale ? ExpiryStatus.Valid : ExpiryStatus.InvalidIfStale,
expiresAt: expiresAt || 0,
countRef: this.gcPolicy.createCountRef({
key
})
};
}
// second argument is false if any entities are missing
const {
data,
paths
} = this.memo.denormalize(schema, input, state.entities, args);
// note: isInvalid can only be true if shouldQuery is true
if (!expiresAt && isInvalid) expiresAt = 1;
return this.getSchemaResponse(data, key, paths, state.entityMeta, expiresAt, endpoint.invalidIfStale || isInvalid, meta);
}
/**
* Queries the store for a Querable schema
* @see https://dataclient.io/docs/api/Controller#get
*/
get(schema, ...rest) {
const state = rest[rest.length - 1];
// this is typescript generics breaking
const args = rest.slice(0, rest.length - 1).map(ensurePojo);
return this.memo.query(schema, args, state.entities, state.indexes);
}
/**
* Queries the store for a Querable schema; providing related metadata
* @see https://dataclient.io/docs/api/Controller#getQueryMeta
*/
getQueryMeta(schema, ...rest) {
const state = rest[rest.length - 1];
// this is typescript generics breaking
const args = rest.slice(0, rest.length - 1).map(ensurePojo);
// TODO: breaking: Switch back to this.memo.query(schema, args, state.entities as any, state.indexes) to do
// this logic
const input = this.memo.buildQueryKey(schema, args, state.entities, state.indexes, JSON.stringify(args));
if (!input) {
return {
data: undefined,
countRef: () => () => undefined
};
}
const {
data,
paths
} = this.memo.denormalize(schema, input, state.entities, args);
return {
data: typeof data === 'symbol' ? undefined : data,
countRef: this.gcPolicy.createCountRef({
paths
})
};
}
getSchemaResponse(data, key, paths, entityMeta, expiresAt, invalidIfStale, meta = {}) {
const invalidDenormalize = typeof data === 'symbol';
// fallback to entity expiry time
if (!expiresAt) {
expiresAt = entityExpiresAt(paths, entityMeta);
}
// https://dataclient.io/docs/concepts/expiry-policy#expiry-status
// we don't track the difference between stale or fresh because that is tied to triggering
// conditions
const expiryStatus = meta != null && meta.invalidated || invalidDenormalize && !(meta != null && meta.error) ? ExpiryStatus.Invalid : invalidDenormalize || invalidIfStale ? ExpiryStatus.InvalidIfStale : ExpiryStatus.Valid;
return {
data,
expiryStatus,
expiresAt,
countRef: this.gcPolicy.createCountRef({
key,
paths
})
};
}
}
// benchmark: https://www.measurethat.net/Benchmarks/Show/24691/0/min-reducer-vs-imperative-with-paths
// earliest expiry dictates age
function entityExpiresAt(paths, entityMeta) {
let expiresAt = Infinity;
for (const {
pk,
key
} of paths) {
var _entityMeta$key;
const entityExpiry = (_entityMeta$key = entityMeta[key]) == null || (_entityMeta$key = _entityMeta$key[pk]) == null ? void 0 : _entityMeta$key.expiresAt;
// expiresAt will always resolve to false with any comparison
if (entityExpiry < expiresAt) expiresAt = entityExpiry;
}
return expiresAt;
}
/** Determine whether the schema has any entities.
*
* Without entities, denormalization is not needed, and results should not be queried.
*/
function schemaHasEntity(schema) {
if (isEntity(schema)) return true;
if (Array.isArray(schema)) return schema.length !== 0 && schemaHasEntity(schema[0]);
if (schema && (typeof schema === 'object' || typeof schema === 'function')) {
const nestedSchema = 'schema' in schema ? schema.schema : schema;
if (typeof nestedSchema === 'function') {
return schemaHasEntity(nestedSchema);
}
return Object.values(nestedSchema).some(x => schemaHasEntity(x));
}
return false;
}
class Snapshot {
constructor(controller, state, fetchedAt = 0) {
this.state = void 0;
this.controller = void 0;
this.fetchedAt = void 0;
this.abort = Snapshot.abort;
this.state = state;
this.controller = controller;
this.fetchedAt = fetchedAt;
}
/*************** Data Access ***************/
/** @see https://dataclient.io/docs/api/Snapshot#getResponse */
getResponse(endpoint, ...args) {
return this.controller.getResponse(endpoint, ...args, this.state);
}
/** @see https://dataclient.io/docs/api/Snapshot#getResponseMeta */
getResponseMeta(endpoint, ...args) {
return this.controller.getResponseMeta(endpoint, ...args, this.state);
}
/** @see https://dataclient.io/docs/api/Snapshot#getError */
getError(endpoint, ...args) {
return this.controller.getError(endpoint, ...args, this.state);
}
/**
* Retrieved memoized value for any Querable schema
* @see https://dataclient.io/docs/api/Snapshot#get
*/
get(schema, ...args) {
return this.controller.get(schema, ...args, this.state);
}
/**
* Queries the store for a Querable schema; providing related metadata
* @see https://dataclient.io/docs/api/Snapshot#getQueryMeta
*/
getQueryMeta(schema, ...args) {
return this.controller.getQueryMeta(schema, ...args, this.state);
}
}
Snapshot.abort = new AbortOptimistic();
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJFeHBpcnlTdGF0dXMiLCJNZW1vQ2FjaGUiLCJpc0VudGl0eSIsImRlbm9ybWFsaXplIiwidmFsaWRhdGVRdWVyeUtleSIsIkFib3J0T3B0aW1pc3RpYyIsImNyZWF0ZVVuc3Vic2NyaXB0aW9uIiwiY3JlYXRlU3Vic2NyaXB0aW9uIiwiY3JlYXRlRXhwaXJlQWxsIiwiY3JlYXRlRmV0Y2giLCJjcmVhdGVJbnZhbGlkYXRlIiwiY3JlYXRlSW52YWxpZGF0ZUFsbCIsImNyZWF0ZVJlc2V0IiwiY3JlYXRlU2V0IiwiY3JlYXRlU2V0UmVzcG9uc2UiLCJlbnN1cmVQb2pvIiwiSW1tb3J0YWxHQ1BvbGljeSIsImluaXRpYWxTdGF0ZSIsInNlbGVjdE1ldGEiLCJ1bnNldERpc3BhdGNoIiwiYWN0aW9uIiwiRXJyb3IiLCJ1bnNldFN0YXRlIiwiQ29udHJvbGxlciIsImNvbnN0cnVjdG9yIiwiZGlzcGF0Y2giLCJnZXRTdGF0ZSIsIm1lbW8iLCJnY1BvbGljeSIsImZldGNoIiwiZW5kcG9pbnQiLCJhcmdzIiwic2NoZW1hIiwibWV0YSIsInByb21pc2UiLCJ0aGVuIiwiaW5wdXQiLCJmZXRjaElmU3RhbGUiLCJkYXRhIiwiZXhwaXJlc0F0IiwiZXhwaXJ5U3RhdHVzIiwiZ2V0UmVzcG9uc2UiLCJJbnZhbGlkIiwiRGF0ZSIsIm5vdyIsImludmFsaWRhdGUiLCJQcm9taXNlIiwicmVzb2x2ZSIsImludmFsaWRhdGVBbGwiLCJvcHRpb25zIiwia2V5IiwidGVzdEtleSIsImV4cGlyZUFsbCIsInJlc2V0RW50aXJlU3RvcmUiLCJzZXRSZXNwb25zZSIsInJlc3QiLCJyZXNwb25zZSIsImxlbmd0aCIsInNsaWNlIiwic2V0RXJyb3IiLCJlcnJvciIsInN1YnNjcmliZSIsInVuc3Vic2NyaWJlIiwic25hcHNob3QiLCJzdGF0ZSIsImZldGNoZWRBdCIsIlNuYXBzaG90IiwiX2Rpc3BhdGNoIiwiYmluZE1pZGRsZXdhcmUiLCJzZXQiLCJ2YWx1ZSIsImdldEVycm9yIiwiZW5kcG9pbnRzIiwidW5kZWZpbmVkIiwiZXJyb3JQb2xpY3kiLCJnZXRSZXNwb25zZU1ldGEiLCJtYXAiLCJpc0FjdGl2ZSIsImNhY2hlRW5kcG9pbnRzIiwic2hvdWxkUXVlcnkiLCJidWlsZFF1ZXJ5S2V5IiwiZW50aXRpZXMiLCJpbmRleGVzIiwiVmFsaWQiLCJJbmZpbml0eSIsImNvdW50UmVmIiwiaXNJbnZhbGlkIiwic2NoZW1hSGFzRW50aXR5IiwiaW52YWxpZGF0ZWQiLCJpbnZhbGlkSWZTdGFsZSIsIkludmFsaWRJZlN0YWxlIiwiY3JlYXRlQ291bnRSZWYiLCJwYXRocyIsImdldFNjaGVtYVJlc3BvbnNlIiwiZW50aXR5TWV0YSIsImdldCIsInF1ZXJ5IiwiZ2V0UXVlcnlNZXRhIiwiSlNPTiIsInN0cmluZ2lmeSIsImludmFsaWREZW5vcm1hbGl6ZSIsImVudGl0eUV4cGlyZXNBdCIsInBrIiwiX2VudGl0eU1ldGEka2V5IiwiZW50aXR5RXhwaXJ5IiwiQXJyYXkiLCJpc0FycmF5IiwibmVzdGVkU2NoZW1hIiwiT2JqZWN0IiwidmFsdWVzIiwic29tZSIsIngiLCJjb250cm9sbGVyIiwiYWJvcnQiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvY29udHJvbGxlci9Db250cm9sbGVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHtcbiAgRXJyb3JUeXBlcyxcbiAgU25hcHNob3RJbnRlcmZhY2UsXG4gIFNjaGVtYSxcbiAgRGVub3JtYWxpemUsXG4gIFF1ZXJ5YWJsZSxcbiAgU2NoZW1hQXJncyxcbn0gZnJvbSAnQGRhdGEtY2xpZW50L25vcm1hbGl6cic7XG5pbXBvcnQge1xuICBFeHBpcnlTdGF0dXMsXG4gIEVuZHBvaW50SW50ZXJmYWNlLFxuICBGZXRjaEZ1bmN0aW9uLFxuICBSZXNvbHZlVHlwZSxcbiAgRGVub3JtYWxpemVOdWxsYWJsZSxcbiAgRW50aXR5UGF0aCxcbiAgTWVtb0NhY2hlLFxuICBpc0VudGl0eSxcbiAgZGVub3JtYWxpemUsXG4gIHZhbGlkYXRlUXVlcnlLZXksXG59IGZyb20gJ0BkYXRhLWNsaWVudC9ub3JtYWxpenInO1xuXG5pbXBvcnQgQWJvcnRPcHRpbWlzdGljIGZyb20gJy4vQWJvcnRPcHRpbWlzdGljLmpzJztcbmltcG9ydCB7XG4gIGNyZWF0ZVVuc3Vic2NyaXB0aW9uLFxuICBjcmVhdGVTdWJzY3JpcHRpb24sXG59IGZyb20gJy4vYWN0aW9ucy9jcmVhdGVTdWJzY3JpcHRpb24uanMnO1xuaW1wb3J0IHtcbiAgY3JlYXRlRXhwaXJlQWxsLFxuICBjcmVhdGVGZXRjaCxcbiAgY3JlYXRlSW52YWxpZGF0ZSxcbiAgY3JlYXRlSW52YWxpZGF0ZUFsbCxcbiAgY3JlYXRlUmVzZXQsXG4gIGNyZWF0ZVNldCxcbiAgY3JlYXRlU2V0UmVzcG9uc2UsXG59IGZyb20gJy4vYWN0aW9ucy9pbmRleC5qcyc7XG5pbXBvcnQgZW5zdXJlUG9qbyBmcm9tICcuL2Vuc3VyZVBvam8uanMnO1xuaW1wb3J0IHR5cGUgeyBFbmRwb2ludFVwZGF0ZUZ1bmN0aW9uIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBSZWR1eE1pZGRsZXdhcmVBUEkgfSBmcm9tICcuLi9tYW5hZ2VyL2FwcGx5TWFuYWdlci5qcyc7XG5pbXBvcnQgdHlwZSB7IEdDSW50ZXJmYWNlIH0gZnJvbSAnLi4vc3RhdGUvR0NQb2xpY3kuanMnO1xuaW1wb3J0IHsgSW1tb3J0YWxHQ1BvbGljeSB9IGZyb20gJy4uL3N0YXRlL0dDUG9saWN5LmpzJztcbmltcG9ydCB7IGluaXRpYWxTdGF0ZSB9IGZyb20gJy4uL3N0YXRlL3JlZHVjZXIvY3JlYXRlUmVkdWNlci5qcyc7XG5pbXBvcnQgc2VsZWN0TWV0YSBmcm9tICcuLi9zdGF0ZS9zZWxlY3RNZXRhLmpzJztcbmltcG9ydCB0eXBlIHsgQWN0aW9uVHlwZXMsIERpc3BhdGNoLCBTdGF0ZSB9IGZyb20gJy4uL3R5cGVzLmpzJztcblxuZXhwb3J0IHR5cGUgR2VuZXJpY0Rpc3BhdGNoID0gKHZhbHVlOiBhbnkpID0+IFByb21pc2U8dm9pZD47XG5leHBvcnQgdHlwZSBEYXRhQ2xpZW50RGlzcGF0Y2ggPSAodmFsdWU6IEFjdGlvblR5cGVzKSA9PiBQcm9taXNlPHZvaWQ+O1xuXG5leHBvcnQgaW50ZXJmYWNlIENvbnRyb2xsZXJDb25zdHJ1Y3RvclByb3BzPFxuICBEIGV4dGVuZHMgR2VuZXJpY0Rpc3BhdGNoID0gRGF0YUNsaWVudERpc3BhdGNoLFxuPiB7XG4gIGRpc3BhdGNoPzogRDtcbiAgZ2V0U3RhdGU/OiAoKSA9PiBTdGF0ZTx1bmtub3duPjtcbiAgbWVtbz86IFBpY2s8TWVtb0NhY2hlLCAnZGVub3JtYWxpemUnIHwgJ3F1ZXJ5JyB8ICdidWlsZFF1ZXJ5S2V5Jz47XG4gIGdjUG9saWN5PzogR0NJbnRlcmZhY2U7XG59XG5cbmNvbnN0IHVuc2V0RGlzcGF0Y2ggPSAoYWN0aW9uOiB1bmtub3duKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIHRocm93IG5ldyBFcnJvcihcbiAgICBgRGlzcGF0Y2hpbmcgd2hpbGUgY29uc3RydWN0aW5nIHlvdXIgbWlkZGxld2FyZSBpcyBub3QgYWxsb3dlZC4gYCArXG4gICAgICBgT3RoZXIgbWlkZGxld2FyZSB3b3VsZCBub3QgYmUgYXBwbGllZCB0byB0aGlzIGRpc3BhdGNoLmAsXG4gICk7XG59O1xuY29uc3QgdW5zZXRTdGF0ZSA9ICgpOiBTdGF0ZTx1bmtub3duPiA9PiB7XG4gIC8vIFRoaXMgaXMgb25seSB0aGUgdmFsdWUgdW50aWwgaXQgaXMgc2V0IGJ5IHRoZSBEYXRhUHJvdmlkZXJcbiAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cbiAgcmV0dXJuIGluaXRpYWxTdGF0ZTtcbn07XG5cbi8qKlxuICogSW1wZXJhdGl2ZSBjb250cm9sIG9mIFJlYWN0aXZlIERhdGEgQ2xpZW50IHN0b3JlXG4gKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9Db250cm9sbGVyXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIENvbnRyb2xsZXI8XG4gIC8vIE5PVEU6IFdlIHRlbXBsYXRlIG9uIGVudGlyZSBkaXNwYXRjaCwgc28gd2UgY2FuIGJlIGNvbnRyYXZhcmlhbnQgb24gQWN0aW9uVHlwZXNcbiAgRCBleHRlbmRzIEdlbmVyaWNEaXNwYXRjaCA9IERhdGFDbGllbnREaXNwYXRjaCxcbj4ge1xuICAvKipcbiAgICogRGlzcGF0Y2hlcyBhbiBhY3Rpb24gdG8gUmVhY3RpdmUgRGF0YSBDbGllbnQgcmVkdWNlci5cbiAgICpcbiAgICogQHNlZSBodHRwczovL2RhdGFjbGllbnQuaW8vZG9jcy9hcGkvQ29udHJvbGxlciNkaXNwYXRjaFxuICAgKi9cbiAgZGVjbGFyZSBwcm90ZWN0ZWQgX2Rpc3BhdGNoOiBEO1xuICAvKipcbiAgICogR2V0cyB0aGUgbGF0ZXN0IHN0YXRlIHNuYXBzaG90IHRoYXQgaXMgZnVsbHkgY29tbWl0dGVkLlxuICAgKlxuICAgKiBUaGlzIGNhbiBiZSB1c2VmdWwgZm9yIGltcGVyYXRpdmUgdXNlLWNhc2VzIGxpa2UgZXZlbnQgaGFuZGxlcnMuXG4gICAqIFRoaXMgc2hvdWxkICpub3QqIGJlIHVzZWQgdG8gcmVuZGVyOyBpbnN0ZWFkIHVzZVN1c3BlbnNlKCkgb3IgdXNlQ2FjaGUoKVxuICAgKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9Db250cm9sbGVyI2dldFN0YXRlXG4gICAqL1xuICBkZWNsYXJlIGdldFN0YXRlOiAoKSA9PiBTdGF0ZTx1bmtub3duPjtcbiAgLyoqXG4gICAqIFNpbmdsZXRvbiB0byBtYWludGFpbiByZWZlcmVudGlhbCBlcXVhbGl0eSBiZXR3ZWVuIGNhbGxzXG4gICAqL1xuICBkZWNsYXJlIHJlYWRvbmx5IG1lbW86IFBpY2s8XG4gICAgTWVtb0NhY2hlLFxuICAgICdkZW5vcm1hbGl6ZScgfCAncXVlcnknIHwgJ2J1aWxkUXVlcnlLZXknXG4gID47XG5cbiAgLyoqXG4gICAqIEhhbmRsZXMgZ2FyYmFnZSBjb2xsZWN0aW9uXG4gICAqL1xuICBkZWNsYXJlIHJlYWRvbmx5IGdjUG9saWN5OiBHQ0ludGVyZmFjZTtcblxuICBjb25zdHJ1Y3Rvcih7XG4gICAgZGlzcGF0Y2ggPSB1bnNldERpc3BhdGNoIGFzIGFueSxcbiAgICBnZXRTdGF0ZSA9IHVuc2V0U3RhdGUsXG4gICAgbWVtbyA9IG5ldyBNZW1vQ2FjaGUoKSxcbiAgICBnY1BvbGljeSA9IG5ldyBJbW1vcnRhbEdDUG9saWN5KCksXG4gIH06IENvbnRyb2xsZXJDb25zdHJ1Y3RvclByb3BzPEQ+ID0ge30pIHtcbiAgICB0aGlzLl9kaXNwYXRjaCA9IGRpc3BhdGNoO1xuICAgIHRoaXMuZ2V0U3RhdGUgPSBnZXRTdGF0ZTtcbiAgICB0aGlzLm1lbW8gPSBtZW1vO1xuICAgIHRoaXMuZ2NQb2xpY3kgPSBnY1BvbGljeTtcbiAgfVxuXG4gIC8vIFRPRE86IGRyb3Agd2hlbiBkcm9wIHN1cHBvcnQgZm9yIGRlc3RydWN0dXJpbmcgKDAuMTQgYW5kIGJlbG93KVxuICBzZXQgZGlzcGF0Y2goZGlzcGF0Y2g6IEQpIHtcbiAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAqL1xuICAgIHRoaXMuX2Rpc3BhdGNoID0gZGlzcGF0Y2g7XG4gIH1cblxuICAvLyBUT0RPOiBkcm9wIHdoZW4gZHJvcCBzdXBwb3J0IGZvciBkZXN0cnVjdHVyaW5nICgwLjE0IGFuZCBiZWxvdylcbiAgZ2V0IGRpc3BhdGNoKCk6IEQge1xuICAgIHJldHVybiB0aGlzLl9kaXNwYXRjaDtcbiAgfVxuXG4gIGJpbmRNaWRkbGV3YXJlKHtcbiAgICBkaXNwYXRjaCxcbiAgICBnZXRTdGF0ZSxcbiAgfToge1xuICAgIGRpc3BhdGNoOiBEO1xuICAgIGdldFN0YXRlOiBSZWR1eE1pZGRsZXdhcmVBUElbJ2dldFN0YXRlJ107XG4gIH0pIHtcbiAgICB0aGlzLl9kaXNwYXRjaCA9IGRpc3BhdGNoO1xuICAgIHRoaXMuZ2V0U3RhdGUgPSBnZXRTdGF0ZTtcbiAgfVxuXG4gIC8qKioqKioqKioqKioqKiogQWN0aW9uIERpc3BhdGNoZXJzICoqKioqKioqKioqKioqKi9cblxuICAvKipcbiAgICogRmV0Y2hlcyB0aGUgZW5kcG9pbnQgd2l0aCBnaXZlbiBhcmdzLCB1cGRhdGluZyB0aGUgUmVhY3RpdmUgRGF0YSBDbGllbnQgY2FjaGUgd2l0aCB0aGUgcmVzcG9uc2Ugb3IgZXJyb3IgdXBvbiBjb21wbGV0aW9uLlxuICAgKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9Db250cm9sbGVyI2ZldGNoXG4gICAqL1xuICBmZXRjaCA9IDxcbiAgICBFIGV4dGVuZHMgRW5kcG9pbnRJbnRlcmZhY2UgJiB7IHVwZGF0ZT86IEVuZHBvaW50VXBkYXRlRnVuY3Rpb248RT4gfSxcbiAgPihcbiAgICBlbmRwb2ludDogRSxcbiAgICAuLi5hcmdzOiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPl1cbiAgKTogRVsnc2NoZW1hJ10gZXh0ZW5kcyB1bmRlZmluZWQgfCBudWxsID8gUmV0dXJuVHlwZTxFPlxuICA6IFByb21pc2U8RGVub3JtYWxpemU8RVsnc2NoZW1hJ10+PiA9PiB7XG4gICAgY29uc3QgYWN0aW9uID0gY3JlYXRlRmV0Y2goZW5kcG9pbnQsIHtcbiAgICAgIGFyZ3MsXG4gICAgfSk7XG4gICAgdGhpcy5kaXNwYXRjaChhY3Rpb24pO1xuXG4gICAgaWYgKGVuZHBvaW50LnNjaGVtYSkge1xuICAgICAgcmV0dXJuIGFjdGlvbi5tZXRhLnByb21pc2UudGhlbihpbnB1dCA9PlxuICAgICAgICBkZW5vcm1hbGl6ZShlbmRwb2ludC5zY2hlbWEsIGlucHV0LCB7fSwgYXJncyksXG4gICAgICApIGFzIGFueTtcbiAgICB9XG4gICAgcmV0dXJuIGFjdGlvbi5tZXRhLnByb21pc2UgYXMgYW55O1xuICB9O1xuXG4gIC8qKlxuICAgKiBGZXRjaGVzIG9ubHkgaWYgZW5kcG9pbnQgaXMgY29uc2lkZXJlZCAnc3RhbGUnOyBvdGhlcndpc2UgcmV0dXJucyB1bmRlZmluZWRcbiAgICogQHNlZSBodHRwczovL2RhdGFjbGllbnQuaW8vZG9jcy9hcGkvQ29udHJvbGxlciNmZXRjaElmU3RhbGVcbiAgICovXG4gIGZldGNoSWZTdGFsZSA9IDxcbiAgICBFIGV4dGVuZHMgRW5kcG9pbnRJbnRlcmZhY2UgJiB7IHVwZGF0ZT86IEVuZHBvaW50VXBkYXRlRnVuY3Rpb248RT4gfSxcbiAgPihcbiAgICBlbmRwb2ludDogRSxcbiAgICAuLi5hcmdzOiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPl1cbiAgKTogRVsnc2NoZW1hJ10gZXh0ZW5kcyB1bmRlZmluZWQgfCBudWxsID8gUmV0dXJuVHlwZTxFPiB8IFJlc29sdmVUeXBlPEU+XG4gIDogUHJvbWlzZTxEZW5vcm1hbGl6ZTxFWydzY2hlbWEnXT4+IHwgRGVub3JtYWxpemU8RVsnc2NoZW1hJ10+ID0+IHtcbiAgICBjb25zdCB7IGRhdGEsIGV4cGlyZXNBdCwgZXhwaXJ5U3RhdHVzIH0gPSB0aGlzLmdldFJlc3BvbnNlKFxuICAgICAgZW5kcG9pbnQsXG4gICAgICAuLi5hcmdzLFxuICAgICAgdGhpcy5nZXRTdGF0ZSgpLFxuICAgICk7XG4gICAgaWYgKGV4cGlyeVN0YXR1cyAhPT0gRXhwaXJ5U3RhdHVzLkludmFsaWQgJiYgRGF0ZS5ub3coKSA8PSBleHBpcmVzQXQpXG4gICAgICByZXR1cm4gZGF0YSBhcyBhbnk7XG4gICAgcmV0dXJuIHRoaXMuZmV0Y2goZW5kcG9pbnQsIC4uLmFyZ3MpO1xuICB9O1xuXG4gIC8qKlxuICAgKiBGb3JjZXMgcmVmZXRjaGluZyBhbmQgc3VzcGVuc2Ugb24gdXNlU3VzcGVuc2Ugd2l0aCB0aGUgc2FtZSBFbmRwb2ludCBhbmQgcGFyYW1ldGVycy5cbiAgICogQHNlZSBodHRwczovL2RhdGFjbGllbnQuaW8vZG9jcy9hcGkvQ29udHJvbGxlciNpbnZhbGlkYXRlXG4gICAqL1xuICBpbnZhbGlkYXRlID0gPEUgZXh0ZW5kcyBFbmRwb2ludEludGVyZmFjZT4oXG4gICAgZW5kcG9pbnQ6IEUsXG4gICAgLi4uYXJnczogcmVhZG9ubHkgWy4uLlBhcmFtZXRlcnM8RT5dIHwgcmVhZG9ubHkgW251bGxdXG4gICk6IFByb21pc2U8dm9pZD4gPT5cbiAgICBhcmdzWzBdICE9PSBudWxsID9cbiAgICAgIHRoaXMuZGlzcGF0Y2goXG4gICAgICAgIGNyZWF0ZUludmFsaWRhdGUoZW5kcG9pbnQsIHtcbiAgICAgICAgICBhcmdzOiBhcmdzIGFzIFBhcmFtZXRlcnM8RT4sXG4gICAgICAgIH0pLFxuICAgICAgKVxuICAgIDogUHJvbWlzZS5yZXNvbHZlKCk7XG5cbiAgLyoqXG4gICAqIEZvcmNlcyByZWZldGNoaW5nIGFuZCBzdXNwZW5zZSBvbiB1c2VTdXNwZW5zZSBvbiBhbGwgbWF0Y2hpbmcgZW5kcG9pbnQgcmVzdWx0IGtleXMuXG4gICAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL0NvbnRyb2xsZXIjaW52YWxpZGF0ZUFsbFxuICAgKiBAcmV0dXJucyBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiBpbnZhbGlkYXRpb24gaXMgY29tbWl0ZWQuXG4gICAqL1xuICBpbnZhbGlkYXRlQWxsID0gKG9wdGlvbnM6IHsgdGVzdEtleTogKGtleTogc3RyaW5nKSA9PiBib29sZWFuIH0pID0+XG4gICAgdGhpcy5kaXNwYXRjaChjcmVhdGVJbnZhbGlkYXRlQWxsKChrZXk6IHN0cmluZykgPT4gb3B0aW9ucy50ZXN0S2V5KGtleSkpKTtcblxuICAvKipcbiAgICogU2V0cyBhbGwgbWF0Y2hpbmcgZW5kcG9pbnQgcmVzdWx0IGtleXMgdG8gYmUgU1RBTEUuXG4gICAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL0NvbnRyb2xsZXIjZXhwaXJlQWxsXG4gICAqIEByZXR1cm5zIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIGV4cGlyeSBpcyBjb21taXRlZC4gKk5PVCogZmV0Y2ggcHJvbWlzZVxuICAgKi9cbiAgZXhwaXJlQWxsID0gKG9wdGlvbnM6IHsgdGVzdEtleTogKGtleTogc3RyaW5nKSA9PiBib29sZWFuIH0pID0+XG4gICAgdGhpcy5kaXNwYXRjaChjcmVhdGVFeHBpcmVBbGwoKGtleTogc3RyaW5nKSA9PiBvcHRpb25zLnRlc3RLZXkoa2V5KSkpO1xuXG4gIC8qKlxuICAgKiBSZXNldHMgdGhlIGVudGlyZSBSZWFjdGl2ZSBEYXRhIENsaWVudCBjYWNoZS4gQWxsIGluZmxpZ2h0IHJlcXVlc3RzIHdpbGwgbm90IHJlc29sdmUuXG4gICAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL0NvbnRyb2xsZXIjcmVzZXRFbnRpcmVTdG9yZVxuICAgKi9cbiAgcmVzZXRFbnRpcmVTdG9yZSA9ICgpOiBQcm9taXNlPHZvaWQ+ID0+IHRoaXMuZGlzcGF0Y2goY3JlYXRlUmVzZXQoKSk7XG5cbiAgLyoqXG4gICAqIFNldHMgdmFsdWUgZm9yIHRoZSBRdWVyeWFibGUgYW5kIGFyZ3MuXG4gICAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL0NvbnRyb2xsZXIjc2V0XG4gICAqL1xuICBzZXQ8UyBleHRlbmRzIFF1ZXJ5YWJsZT4oXG4gICAgc2NoZW1hOiBTLFxuICAgIC4uLnJlc3Q6IHJlYWRvbmx5IFsuLi5TY2hlbWFBcmdzPFM+LCAocHJldmlvdXNWYWx1ZTogRGVub3JtYWxpemU8Uz4pID0+IHt9XVxuICApOiBQcm9taXNlPHZvaWQ+O1xuXG4gIHNldDxTIGV4dGVuZHMgUXVlcnlhYmxlPihcbiAgICBzY2hlbWE6IFMsXG4gICAgLi4ucmVzdDogcmVhZG9ubHkgWy4uLlNjaGVtYUFyZ3M8Uz4sIHt9XVxuICApOiBQcm9taXNlPHZvaWQ+O1xuXG4gIHNldDxTIGV4dGVuZHMgUXVlcnlhYmxlPihcbiAgICBzY2hlbWE6IFMsXG4gICAgLi4ucmVzdDogcmVhZG9ubHkgWy4uLlNjaGVtYUFyZ3M8Uz4sIGFueV1cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgdmFsdWUgPSByZXN0W3Jlc3QubGVuZ3RoIC0gMV07XG4gICAgY29uc3QgYWN0aW9uID0gY3JlYXRlU2V0KHNjaGVtYSwge1xuICAgICAgYXJnczogcmVzdC5zbGljZSgwLCByZXN0Lmxlbmd0aCAtIDEpIGFzIFNjaGVtYUFyZ3M8Uz4sXG4gICAgICB2YWx1ZSxcbiAgICB9KTtcbiAgICAvLyBUT0RPOiByZWplY3Qgd2l0aCBlcnJvciBpZiB0aGlzIGZhaWxzIGluIHJlZHVjZXJcbiAgICByZXR1cm4gdGhpcy5kaXNwYXRjaChhY3Rpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgcmVzcG9uc2UgZm9yIHRoZSBFbmRwb2ludCBhbmQgYXJncy5cbiAgICogQHNlZSBodHRwczovL2RhdGFjbGllbnQuaW8vZG9jcy9hcGkvQ29udHJvbGxlciNzZXRSZXNwb25zZVxuICAgKi9cbiAgc2V0UmVzcG9uc2UgPSA8XG4gICAgRSBleHRlbmRzIEVuZHBvaW50SW50ZXJmYWNlICYge1xuICAgICAgdXBkYXRlPzogRW5kcG9pbnRVcGRhdGVGdW5jdGlvbjxFPjtcbiAgICB9LFxuICA+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIC4uLnJlc3Q6IHJlYWRvbmx5IFsuLi5QYXJhbWV0ZXJzPEU+LCBhbnldXG4gICk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgIGNvbnN0IHJlc3BvbnNlOiBSZXNvbHZlVHlwZTxFPiA9IHJlc3RbcmVzdC5sZW5ndGggLSAxXTtcbiAgICBjb25zdCBhY3Rpb24gPSBjcmVhdGVTZXRSZXNwb25zZShlbmRwb2ludCwge1xuICAgICAgYXJnczogcmVzdC5zbGljZSgwLCByZXN0Lmxlbmd0aCAtIDEpIGFzIFBhcmFtZXRlcnM8RT4sXG4gICAgICByZXNwb25zZSxcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcy5kaXNwYXRjaChhY3Rpb24pO1xuICB9O1xuXG4gIC8qKlxuICAgKiBTZXRzIGFuIGVycm9yIHJlc3BvbnNlIGZvciB0aGUgRW5kcG9pbnQgYW5kIGFyZ3MuXG4gICAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL0NvbnRyb2xsZXIjc2V0RXJyb3JcbiAgICovXG4gIHNldEVycm9yID0gPFxuICAgIEUgZXh0ZW5kcyBFbmRwb2ludEludGVyZmFjZSAmIHtcbiAgICAgIHVwZGF0ZT86IEVuZHBvaW50VXBkYXRlRnVuY3Rpb248RT47XG4gICAgfSxcbiAgPihcbiAgICBlbmRwb2ludDogRSxcbiAgICAuLi5yZXN0OiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPiwgRXJyb3JdXG4gICk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgIGNvbnN0IHJlc3BvbnNlOiBFcnJvciA9IHJlc3RbcmVzdC5sZW5ndGggLSAxXTtcbiAgICBjb25zdCBhY3Rpb24gPSBjcmVhdGVTZXRSZXNwb25zZShlbmRwb2ludCwge1xuICAgICAgYXJnczogcmVzdC5zbGljZSgwLCByZXN0Lmxlbmd0aCAtIDEpIGFzIFBhcmFtZXRlcnM8RT4sXG4gICAgICByZXNwb25zZSxcbiAgICAgIGVycm9yOiB0cnVlLFxuICAgIH0pO1xuICAgIHJldHVybiB0aGlzLmRpc3BhdGNoKGFjdGlvbik7XG4gIH07XG5cbiAgLyoqXG4gICAqIFJlc29sdmVzIGFuIGluZmxpZ2h0IGZldGNoLlxuICAgKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9Db250cm9sbGVyI3Jlc29sdmVcbiAgICovXG4gIHJlc29sdmUgPSA8XG4gICAgRSBleHRlbmRzIEVuZHBvaW50SW50ZXJmYWNlICYge1xuICAgICAgdXBkYXRlPzogRW5kcG9pbnRVcGRhdGVGdW5jdGlvbjxFPjtcbiAgICB9LFxuICA+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIG1ldGE6XG4gICAgICB8IHtcbiAgICAgICAgICBhcmdzOiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPl07XG4gICAgICAgICAgcmVzcG9uc2U6IEVycm9yO1xuICAgICAgICAgIGZldGNoZWRBdDogbnVtYmVyO1xuICAgICAgICAgIGVycm9yOiB0cnVlO1xuICAgICAgICB9XG4gICAgICB8IHtcbiAgICAgICAgICBhcmdzOiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPl07XG4gICAgICAgICAgcmVzcG9uc2U6IGFueTtcbiAgICAgICAgICBmZXRjaGVkQXQ6IG51bWJlcjtcbiAgICAgICAgICBlcnJvcj86IGZhbHNlIHwgdW5kZWZpbmVkO1xuICAgICAgICB9LFxuICApOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICByZXR1cm4gdGhpcy5kaXNwYXRjaChjcmVhdGVTZXRSZXNwb25zZShlbmRwb2ludCwgbWV0YSBhcyBhbnkpKTtcbiAgfTtcblxuICAvKipcbiAgICogTWFya3MgYSBuZXcgc3Vic2NyaXB0aW9uIHRvIGEgZ2l2ZW4gRW5kcG9pbnQuXG4gICAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL0NvbnRyb2xsZXIjc3Vic2NyaWJlXG4gICAqL1xuICBzdWJzY3JpYmUgPSA8XG4gICAgRSBleHRlbmRzIEVuZHBvaW50SW50ZXJmYWNlPFxuICAgICAgRmV0Y2hGdW5jdGlvbixcbiAgICAgIFNjaGVtYSB8IHVuZGVmaW5lZCxcbiAgICAgIHVuZGVmaW5lZCB8IGZhbHNlXG4gICAgPixcbiAgPihcbiAgICBlbmRwb2ludDogRSxcbiAgICAuLi5hcmdzOiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPl0gfCByZWFkb25seSBbbnVsbF1cbiAgKTogUHJvbWlzZTx2b2lkPiA9PlxuICAgIGFyZ3NbMF0gIT09IG51bGwgP1xuICAgICAgdGhpcy5kaXNwYXRjaChcbiAgICAgICAgY3JlYXRlU3Vic2NyaXB0aW9uKGVuZHBvaW50LCB7XG4gICAgICAgICAgYXJnczogYXJncyBhcyBQYXJhbWV0ZXJzPEU+LFxuICAgICAgICB9KSxcbiAgICAgIClcbiAgICA6IFByb21pc2UucmVzb2x2ZSgpO1xuXG4gIC8qKlxuICAgKiBNYXJrcyBjb21wbGV0aW9uIG9mIHN1YnNjcmlwdGlvbiB0byBhIGdpdmVuIEVuZHBvaW50LlxuICAgKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9Db250cm9sbGVyI3Vuc3Vic2NyaWJlXG4gICAqL1xuICB1bnN1YnNjcmliZSA9IDxcbiAgICBFIGV4dGVuZHMgRW5kcG9pbnRJbnRlcmZhY2U8XG4gICAgICBGZXRjaEZ1bmN0aW9uLFxuICAgICAgU2NoZW1hIHwgdW5kZWZpbmVkLFxuICAgICAgdW5kZWZpbmVkIHwgZmFsc2VcbiAgICA+LFxuICA+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIC4uLmFyZ3M6IHJlYWRvbmx5IFsuLi5QYXJhbWV0ZXJzPEU+XSB8IHJlYWRvbmx5IFtudWxsXVxuICApOiBQcm9taXNlPHZvaWQ+ID0+XG4gICAgYXJnc1swXSAhPT0gbnVsbCA/XG4gICAgICB0aGlzLmRpc3BhdGNoKFxuICAgICAgICBjcmVhdGVVbnN1YnNjcmlwdGlvbihlbmRwb2ludCwge1xuICAgICAgICAgIGFyZ3M6IGFyZ3MgYXMgUGFyYW1ldGVyczxFPixcbiAgICAgICAgfSksXG4gICAgICApXG4gICAgOiBQcm9taXNlLnJlc29sdmUoKTtcblxuICAvKioqKioqKioqKioqKioqIE1vcmUgKioqKioqKioqKioqKioqL1xuXG4gIC8qIFRPRE86XG4gIGFib3J0ID0gPEUgZXh0ZW5kcyBFbmRwb2ludEludGVyZmFjZT4oXG4gICAgZW5kcG9pbnQ6IEUsXG4gICAgLi4uYXJnczogcmVhZG9ubHkgWy4uLlBhcmFtZXRlcnM8RT5dXG4gICk6IFByb21pc2U8dm9pZD5cbiAgKi9cblxuICAvKipcbiAgICogR2V0cyBhIHNuYXBzaG90IChodHRwczovL2RhdGFjbGllbnQuaW8vZG9jcy9hcGkvU25hcHNob3QpXG4gICAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL0NvbnRyb2xsZXIjc25hcHNob3RcbiAgICovXG4gIHNuYXBzaG90ID0gKHN0YXRlOiBTdGF0ZTx1bmtub3duPiwgZmV0Y2hlZEF0PzogbnVtYmVyKTogU25hcHNob3Q8dW5rbm93bj4gPT4ge1xuICAgIHJldHVybiBuZXcgU25hcHNob3QodGhpcywgc3RhdGUsIGZldGNoZWRBdCk7XG4gIH07XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIGVycm9yLCBpZiBhbnksIGZvciBhIGdpdmVuIGVuZHBvaW50LiBSZXR1cm5zIHVuZGVmaW5lZCBmb3Igbm8gZXJyb3JzLlxuICAgKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9Db250cm9sbGVyI2dldEVycm9yXG4gICAqL1xuICBnZXRFcnJvcjxFIGV4dGVuZHMgRW5kcG9pbnRJbnRlcmZhY2U+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIC4uLnJlc3Q6XG4gICAgICB8IHJlYWRvbmx5IFtudWxsLCBTdGF0ZTx1bmtub3duPl1cbiAgICAgIHwgcmVhZG9ubHkgWy4uLlBhcmFtZXRlcnM8RT4sIFN0YXRlPHVua25vd24+XVxuICApOiBFcnJvclR5cGVzIHwgdW5kZWZpbmVkO1xuXG4gIGdldEVycm9yPEUgZXh0ZW5kcyBQaWNrPEVuZHBvaW50SW50ZXJmYWNlLCAna2V5Jz4+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIC4uLnJlc3Q6XG4gICAgICB8IHJlYWRvbmx5IFtudWxsLCBTdGF0ZTx1bmtub3duPl1cbiAgICAgIHwgcmVhZG9ubHkgWy4uLlBhcmFtZXRlcnM8RVsna2V5J10+LCBTdGF0ZTx1bmtub3duPl1cbiAgKTogRXJyb3JUeXBlcyB8IHVuZGVmaW5lZDtcblxuICBnZXRFcnJvcihcbiAgICBlbmRwb2ludDogRW5kcG9pbnRJbnRlcmZhY2UsXG4gICAgLi4ucmVzdDogcmVhZG9ubHkgWy4uLnVua25vd25bXSwgU3RhdGU8dW5rbm93bj5dXG4gICk6IEVycm9yVHlwZXMgfCB1bmRlZmluZWQge1xuICAgIGlmIChyZXN0WzBdID09PSBudWxsKSByZXR1cm47XG4gICAgY29uc3Qgc3RhdGUgPSByZXN0W3Jlc3QubGVuZ3RoIC0gMV0gYXMgU3RhdGU8dW5rbm93bj47XG4gICAgLy8gdGhpcyBpcyB0eXBlc2NyaXB0IGdlbmVyaWNzIGJyZWFraW5nXG4gICAgY29uc3QgYXJnczogYW55ID0gcmVzdC5zbGljZSgwLCByZXN0Lmxlbmd0aCAtIDEpO1xuICAgIGNvbnN0IGtleSA9IGVuZHBvaW50LmtleSguLi5hcmdzKTtcblxuICAgIGNvbnN0IG1ldGEgPSBzZWxlY3RNZXRhKHN0YXRlLCBrZXkpO1xuICAgIGNvbnN0IGVycm9yID0gc3RhdGUuZW5kcG9pbnRzW2tleV07XG5cbiAgICBpZiAoZXJyb3IgIT09IHVuZGVmaW5lZCAmJiBtZXRhPy5lcnJvclBvbGljeSA9PT0gJ3NvZnQnKSByZXR1cm47XG5cbiAgICByZXR1cm4gbWV0YT8uZXJyb3IgYXMgYW55O1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIChnbG9iYWxseSByZWZlcmVudGlhbGx5IHN0YWJsZSkgcmVzcG9uc2UgZm9yIGEgZ2l2ZW4gZW5kcG9pbnQvYXJncyBwYWlyIGZyb20gc3RhdGUgZ2l2ZW4uXG4gICAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL0NvbnRyb2xsZXIjZ2V0UmVzcG9uc2VcbiAgICovXG4gIGdldFJlc3BvbnNlPEUgZXh0ZW5kcyBFbmRwb2ludEludGVyZmFjZT4oXG4gICAgZW5kcG9pbnQ6IEUsXG4gICAgLi4ucmVzdDpcbiAgICAgIHwgcmVhZG9ubHkgW251bGwsIFN0YXRlPHVua25vd24+XVxuICAgICAgfCByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPiwgU3RhdGU8dW5rbm93bj5dXG4gICk6IHtcbiAgICBkYXRhOiBEZW5vcm1hbGl6ZU51bGxhYmxlPEVbJ3NjaGVtYSddPjtcbiAgICBleHBpcnlTdGF0dXM6IEV4cGlyeVN0YXR1cztcbiAgICBleHBpcmVzQXQ6IG51bWJlcjtcbiAgICBjb3VudFJlZjogKCkgPT4gKCkgPT4gdm9pZDtcbiAgfTtcblxuICBnZXRSZXNwb25zZTxcbiAgICBFIGV4dGVuZHMgUGljazxFbmRwb2ludEludGVyZmFjZSwgJ2tleScgfCAnc2NoZW1hJyB8ICdpbnZhbGlkSWZTdGFsZSc+LFxuICA+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIC4uLnJlc3Q6IHJlYWRvbmx5IFtcbiAgICAgIC4uLihyZWFkb25seSBbLi4uUGFyYW1ldGVyczxFWydrZXknXT5dIHwgcmVhZG9ubHkgW251bGxdKSxcbiAgICAgIFN0YXRlPHVua25vd24+LFxuICAgIF1cbiAgKToge1xuICAgIGRhdGE6IERlbm9ybWFsaXplTnVsbGFibGU8RVsnc2NoZW1hJ10+O1xuICAgIGV4cGlyeVN0YXR1czogRXhwaXJ5U3RhdHVzO1xuICAgIGV4cGlyZXNBdDogbnVtYmVyO1xuICAgIGNvdW50UmVmOiAoKSA9PiAoKSA9PiB2b2lkO1xuICB9O1xuXG4gIGdldFJlc3BvbnNlKFxuICAgIGVuZHBvaW50OiBFbmRwb2ludEludGVyZmFjZSxcbiAgICAuLi5yZXN0OiByZWFkb25seSBbLi4udW5rbm93bltdLCBTdGF0ZTx1bmtub3duPl1cbiAgKToge1xuICAgIGRhdGE6IHVua25vd247XG4gICAgZXhwaXJ5U3RhdHVzOiBFeHBpcnlTdGF0dXM7XG4gICAgZXhwaXJlc0F0OiBudW1iZXI7XG4gICAgY291bnRSZWY6ICgpID0+ICgpID0+IHZvaWQ7XG4gIH0ge1xuICAgIC8vIFRPRE86IGJyZWFraW5nOiBvbmx5IHJldHVybiBkYXRhXG4gICAgcmV0dXJuIHRoaXMuZ2V0UmVzcG9uc2VNZXRhKGVuZHBvaW50LCAuLi5yZXN0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXRzIHRoZSAoZ2xvYmFsbHkgcmVmZXJlbnRpYWxseSBzdGFibGUpIHJlc3BvbnNlIGZvciBhIGdpdmVuIGVuZHBvaW50L2FyZ3MgcGFpciBmcm9tIHN0YXRlIGdpdmVuLlxuICAgKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9Db250cm9sbGVyI2dldFJlc3BvbnNlTWV0YVxuICAgKi9cbiAgZ2V0UmVzcG9uc2VNZXRhPEUgZXh0ZW5kcyBFbmRwb2ludEludGVyZmFjZT4oXG4gICAgZW5kcG9pbnQ6IEUsXG4gICAgLi4ucmVzdDpcbiAgICAgIHwgcmVhZG9ubHkgW251bGwsIFN0YXRlPHVua25vd24+XVxuICAgICAgfCByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPiwgU3RhdGU8dW5rbm93bj5dXG4gICk6IHtcbiAgICBkYXRhOiBEZW5vcm1hbGl6ZU51bGxhYmxlPEVbJ3NjaGVtYSddPjtcbiAgICBleHBpcnlTdGF0dXM6IEV4cGlyeVN0YXR1cztcbiAgICBleHBpcmVzQXQ6IG51bWJlcjtcbiAgICBjb3VudFJlZjogKCkgPT4gKCkgPT4gdm9pZDtcbiAgfTtcblxuICBnZXRSZXNwb25zZU1ldGE8XG4gICAgRSBleHRlbmRzIFBpY2s8RW5kcG9pbnRJbnRlcmZhY2UsICdrZXknIHwgJ3NjaGVtYScgfCAnaW52YWxpZElmU3RhbGUnPixcbiAgPihcbiAgICBlbmRwb2ludDogRSxcbiAgICAuLi5yZXN0OiByZWFkb25seSBbXG4gICAgICAuLi4ocmVhZG9ubHkgWy4uLlBhcmFtZXRlcnM8RVsna2V5J10+XSB8IHJlYWRvbmx5IFtudWxsXSksXG4gICAgICBTdGF0ZTx1bmtub3duPixcbiAgICBdXG4gICk6IHtcbiAgICBkYXRhOiBEZW5vcm1hbGl6ZU51bGxhYmxlPEVbJ3NjaGVtYSddPjtcbiAgICBleHBpcnlTdGF0dXM6IEV4cGlyeVN0YXR1cztcbiAgICBleHBpcmVzQXQ6IG51bWJlcjtcbiAgICBjb3VudFJlZjogKCkgPT4gKCkgPT4gdm9pZDtcbiAgfTtcblxuICBnZXRSZXNwb25zZU1ldGEoXG4gICAgZW5kcG9pbnQ6IEVuZHBvaW50SW50ZXJmYWNlLFxuICAgIC4uLnJlc3Q6IHJlYWRvbmx5IFsuLi51bmtub3duW10sIFN0YXRlPHVua25vd24+XVxuICApOiB7XG4gICAgZGF0YTogdW5rbm93bjtcbiAgICBleHBpcnlTdGF0dXM6IEV4cGlyeVN0YXR1cztcbiAgICBleHBpcmVzQXQ6IG51bWJlcjtcbiAgICBjb3VudFJlZjogKCkgPT4gKCkgPT4gdm9pZDtcbiAgfSB7XG4gICAgY29uc3Qgc3RhdGUgPSByZXN0W3Jlc3QubGVuZ3RoIC0gMV0gYXMgU3RhdGU8dW5rbm93bj47XG4gICAgLy8gdGhpcyBpcyB0eXBlc2NyaXB0IGdlbmVyaWNzIGJyZWFraW5nXG4gICAgY29uc3QgYXJnczogYW55ID0gcmVzdFxuICAgICAgLnNsaWNlKDAsIHJlc3QubGVuZ3RoIC0gMSlcbiAgICAgIC8vIGhhbmRsZSBGb3JtRGF0YVxuICAgICAgLm1hcChlbnN1cmVQb2pvKTtcbiAgICBjb25zdCBpc0FjdGl2ZSA9IGFyZ3MubGVuZ3RoICE9PSAxIHx8IGFyZ3NbMF0gIT09IG51bGw7XG4gICAgY29uc3Qga2V5ID0gaXNBY3RpdmUgPyBlbmRwb2ludC5rZXkoLi4uYXJncykgOiAnJztcbiAgICBjb25zdCBjYWNoZUVuZHBvaW50cyA9IGlzQWN0aXZlID8gc3RhdGUuZW5kcG9pbnRzW2tleV0gOiB1bmRlZmluZWQ7XG4gICAgY29uc3Qgc2NoZW1hID0gZW5kcG9pbnQuc2NoZW1hO1xuICAgIGNvbnN0IG1ldGEgPSBzZWxlY3RNZXRhKHN0YXRlLCBrZXkpO1xuICAgIGxldCBleHBpcmVzQXQgPSBtZXRhPy5leHBpcmVzQXQ7XG4gICAgLy8gaWYgd2UgaGF2ZSBubyBlbmRwb2ludCBlbnRyeSwgYW5kIG91ciBlbmRwb2ludCBoYXMgYSBzY2hlbWEgLSB0cnkgcXVlcnlpbmcgdGhlIHN0b3JlXG4gICAgY29uc3Qgc2hvdWxkUXVlcnkgPSBjYWNoZUVuZHBvaW50cyA9PT0gdW5kZWZpbmVkICYmIHNjaGVtYSAhPT0gdW5kZWZpbmVkO1xuXG4gICAgY29uc3QgaW5wdXQgPVxuICAgICAgc2hvdWxkUXVlcnkgP1xuICAgICAgICAvLyBub3RoaW5nIGluIGVuZHBvaW50cyBjYWNoZSwgc28gdHJ5IHF1ZXJ5aW5nIGlmIHdlIGhhdmUgYSBzY2hlbWEgdG8gZG8gc29cbiAgICAgICAgdGhpcy5tZW1vLmJ1aWxkUXVlcnlLZXkoXG4gICAgICAgICAgc2NoZW1hLFxuICAgICAgICAgIGFyZ3MsXG4gICAgICAgICAgc3RhdGUuZW50aXRpZXMgYXMgYW55LFxuICAgICAgICAgIHN0YXRlLmluZGV4ZXMsXG4gICAgICAgICAga2V5LFxuICAgICAgICApXG4gICAgICA6IGNhY2hlRW5kcG9pbnRzO1xuXG4gICAgaWYgKCFpc0FjdGl2ZSkge1xuICAgICAgLy8gd2hlbiBub3QgYWN0aXZlIHNpbXBseSByZXR1cm4gdGhlIHF1ZXJ5IGlucHV0IHdpdGhvdXQgZGVub3JtYWxpemluZ1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZGF0YTogaW5wdXQgYXMgYW55LFxuICAgICAgICBleHBpcnlTdGF0dXM6IEV4cGlyeVN0YXR1cy5WYWxpZCxcbiAgICAgICAgZXhwaXJlc0F0OiBJbmZpbml0eSxcbiAgICAgICAgY291bnRSZWY6ICgpID0+ICgpID0+IHVuZGVmaW5lZCxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgbGV0IGlzSW52YWxpZCA9IGZhbHNlO1xuICAgIGlmIChzaG91bGRRdWVyeSkge1xuICAgICAgaXNJbnZhbGlkID0gIXZhbGlkYXRlUXVlcnlLZXkoaW5wdXQpO1xuICAgICAgLy8gZW5kcG9pbnQgd2l0aG91dCBlbnRpdGllc1xuICAgIH0gZWxzZSBpZiAoIXNjaGVtYSB8fCAhc2NoZW1hSGFzRW50aXR5KHNjaGVtYSkpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGRhdGE6IGNhY2hlRW5kcG9pbnRzLFxuICAgICAgICBleHBpcnlTdGF0dXM6XG4gICAgICAgICAgbWV0YT8uaW52YWxpZGF0ZWQgPyBFeHBpcnlTdGF0dXMuSW52YWxpZFxuICAgICAgICAgIDogY2FjaGVFbmRwb2ludHMgJiYgIWVuZHBvaW50LmludmFsaWRJZlN0YWxlID8gRXhwaXJ5U3RhdHVzLlZhbGlkXG4gICAgICAgICAgOiBFeHBpcnlTdGF0dXMuSW52YWxpZElmU3RhbGUsXG4gICAgICAgIGV4cGlyZXNBdDogZXhwaXJlc0F0IHx8IDAsXG4gICAgICAgIGNvdW50UmVmOiB0aGlzLmdjUG9saWN5LmNyZWF0ZUNvdW50UmVmKHsga2V5IH0pLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBzZWNvbmQgYXJndW1lbnQgaXMgZmFsc2UgaWYgYW55IGVudGl0aWVzIGFyZSBtaXNzaW5nXG5cbiAgICBjb25zdCB7IGRhdGEsIHBhdGhzIH0gPSB0aGlzLm1lbW8uZGVub3JtYWxpemUoXG4gICAgICBzY2hlbWEsXG4gICAgICBpbnB1dCxcbiAgICAgIHN0YXRlLmVudGl0aWVzLFxuICAgICAgYXJncyxcbiAgICApIGFzIHsgZGF0YTogYW55OyBwYXRoczogRW50aXR5UGF0aFtdIH07XG5cbiAgICAvLyBub3RlOiBpc0ludmFsaWQgY2FuIG9ubHkgYmUgdHJ1ZSBpZiBzaG91bGRRdWVyeSBpcyB0cnVlXG4gICAgaWYgKCFleHBpcmVzQXQgJiYgaXNJbnZhbGlkKSBleHBpcmVzQXQgPSAxO1xuXG4gICAgcmV0dXJuIHRoaXMuZ2V0U2NoZW1hUmVzcG9uc2UoXG4gICAgICBkYXRhLFxuICAgICAga2V5LFxuICAgICAgcGF0aHMsXG4gICAgICBzdGF0ZS5lbnRpdHlNZXRhLFxuICAgICAgZXhwaXJlc0F0LFxuICAgICAgZW5kcG9pbnQuaW52YWxpZElmU3RhbGUgfHwgaXNJbnZhbGlkLFxuICAgICAgbWV0YSxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFF1ZXJpZXMgdGhlIHN0b3JlIGZvciBhIFF1ZXJhYmxlIHNjaGVtYVxuICAgKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9Db250cm9sbGVyI2dldFxuICAgKi9cbiAgZ2V0PFMgZXh0ZW5kcyBRdWVyeWFibGU+KFxuICAgIHNjaGVtYTogUyxcbiAgICAuLi5yZXN0OiByZWFkb25seSBbXG4gICAgICAuLi5TY2hlbWFBcmdzPFM+LFxuICAgICAgUGljazxTdGF0ZTx1bmtub3duPiwgJ2VudGl0aWVzJyB8ICdlbnRpdHlNZXRhJz4sXG4gICAgXVxuICApOiBEZW5vcm1hbGl6ZU51bGxhYmxlPFM+IHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBzdGF0ZSA9IHJlc3RbcmVzdC5sZW5ndGggLSAxXSBhcyBTdGF0ZTxhbnk+O1xuICAgIC8vIHRoaXMgaXMgdHlwZXNjcmlwdCBnZW5lcmljcyBicmVha2luZ1xuICAgIGNvbnN0IGFyZ3M6IGFueSA9IHJlc3RcbiAgICAgIC5zbGljZSgwLCByZXN0Lmxlbmd0aCAtIDEpXG4gICAgICAubWFwKGVuc3VyZVBvam8pIGFzIFNjaGVtYUFyZ3M8Uz47XG5cbiAgICByZXR1cm4gdGhpcy5tZW1vLnF1ZXJ5KHNjaGVtYSwgYXJncywgc3RhdGUuZW50aXRpZXMgYXMgYW55LCBzdGF0ZS5pbmRleGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBRdWVyaWVzIHRoZSBzdG9yZSBmb3IgYSBRdWVyYWJsZSBzY2hlbWE7IHByb3ZpZGluZyByZWxhdGVkIG1ldGFkYXRhXG4gICAqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL0NvbnRyb2xsZXIjZ2V0UXVlcnlNZXRhXG4gICAqL1xuICBnZXRRdWVyeU1ldGE8UyBleHRlbmRzIFF1ZXJ5YWJsZT4oXG4gICAgc2NoZW1hOiBTLFxuICAgIC4uLnJlc3Q6IHJlYWRvbmx5IFtcbiAgICAgIC4uLlNjaGVtYUFyZ3M8Uz4sXG4gICAgICBQaWNrPFN0YXRlPHVua25vd24+LCAnZW50aXRpZXMnIHwgJ2VudGl0eU1ldGEnPixcbiAgICBdXG4gICk6IHtcbiAgICBkYXRhOiBEZW5vcm1hbGl6ZU51bGxhYmxlPFM+IHwgdW5kZWZpbmVkO1xuICAgIGNvdW50UmVmOiAoKSA9PiAoKSA9PiB2b2lkO1xuICB9IHtcbiAgICBjb25zdCBzdGF0ZSA9IHJlc3RbcmVzdC5sZW5ndGggLSAxXSBhcyBTdGF0ZTxhbnk+O1xuICAgIC8vIHRoaXMgaXMgdHlwZXNjcmlwdCBnZW5lcmljcyBicmVha2luZ1xuICAgIGNvbnN0IGFyZ3M6IGFueSA9IHJlc3RcbiAgICAgIC5zbGljZSgwLCByZXN0Lmxlbmd0aCAtIDEpXG4gICAgICAubWFwKGVuc3VyZVBvam8pIGFzIFNjaGVtYUFyZ3M8Uz47XG5cbiAgICAvLyBUT0RPOiBicmVha2luZzogU3dpdGNoIGJhY2sgdG8gdGhpcy5tZW1vLnF1ZXJ5KHNjaGVtYSwgYXJncywgc3RhdGUuZW50aXRpZXMgYXMgYW55LCBzdGF0ZS5pbmRleGVzKSB0byBkb1xuICAgIC8vIHRoaXMgbG9naWNcbiAgICBjb25zdCBpbnB1dCA9IHRoaXMubWVtby5idWlsZFF1ZXJ5S2V5KFxuICAgICAgc2NoZW1hLFxuICAgICAgYXJncyxcbiAgICAgIHN0YXRlLmVudGl0aWVzIGFzIGFueSxcbiAgICAgIHN0YXRlLmluZGV4ZXMsXG4gICAgICBKU09OLnN0cmluZ2lmeShhcmdzKSxcbiAgICApO1xuXG4gICAgaWYgKCFpbnB1dCkge1xuICAgICAgcmV0dXJuIHsgZGF0YTogdW5kZWZpbmVkLCBjb3VudFJlZjogKCkgPT4gKCkgPT4gdW5kZWZpbmVkIH07XG4gICAgfVxuXG4gICAgY29uc3QgeyBkYXRhLCBwYXRocyB9ID0gdGhpcy5tZW1vLmRlbm9ybWFsaXplKFxuICAgICAgc2NoZW1hLFxuICAgICAgaW5wdXQsXG4gICAgICBzdGF0ZS5lbnRpdGllcyxcbiAgICAgIGFyZ3MsXG4gICAgKTtcbiAgICByZXR1cm4ge1xuICAgICAgZGF0YTogdHlwZW9mIGRhdGEgPT09ICdzeW1ib2wnID8gdW5kZWZpbmVkIDogKGRhdGEgYXMgYW55KSxcbiAgICAgIGNvdW50UmVmOiB0aGlzLmdjUG9saWN5LmNyZWF0ZUNvdW50UmVmKHsgcGF0aHMgfSksXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0U2NoZW1hUmVzcG9uc2U8VD4oXG4gICAgZGF0YTogVCxcbiAgICBrZXk6IHN0cmluZyxcbiAgICBwYXRoczogRW50aXR5UGF0aFtdLFxuICAgIGVudGl0eU1ldGE6IFN0YXRlPHVua25vd24+WydlbnRpdHlNZXRhJ10sXG4gICAgZXhwaXJlc0F0OiBudW1iZXIsXG4gICAgaW52YWxpZElmU3RhbGU6IGJvb2xlYW4sXG4gICAgbWV0YTogeyBlcnJvcj86IHVua25vd247IGludmFsaWRhdGVkPzogdW5rbm93biB9ID0ge30sXG4gICk6IHtcbiAgICBkYXRhOiBUO1xuICAgIGV4cGlyeVN0YXR1czogRXhwaXJ5U3RhdHVzO1xuICAgIGV4cGlyZXNBdDogbnVtYmVyO1xuICAgIGNvdW50UmVmOiAoKSA9PiAoKSA9PiB2b2lkO1xuICB9IHtcbiAgICBjb25zdCBpbnZhbGlkRGVub3JtYWxpemUgPSB0eXBlb2YgZGF0YSA9PT0gJ3N5bWJvbCc7XG5cbiAgICAvLyBmYWxsYmFjayB0byBlbnRpdHkgZXhwaXJ5IHRpbWVcbiAgICBpZiAoIWV4cGlyZXNBdCkge1xuICAgICAgZXhwaXJlc0F0ID0gZW50aXR5RXhwaXJlc0F0KHBhdGhzLCBlbnRpdHlNZXRhKTtcbiAgICB9XG5cbiAgICAvLyBodHRwczovL2RhdGFjbGllbnQuaW8vZG9jcy9jb25jZXB0cy9leHBpcnktcG9saWN5I2V4cGlyeS1zdGF0dXNcbiAgICAvLyB3ZSBkb24ndCB0cmFjayB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHN0YWxlIG9yIGZyZXNoIGJlY2F1c2UgdGhhdCBpcyB0aWVkIHRvIHRyaWdnZXJpbmdcbiAgICAvLyBjb25kaXRpb25zXG4gICAgY29uc3QgZXhwaXJ5U3RhdHVzID1cbiAgICAgIG1ldGE/LmludmFsaWRhdGVkIHx8IChpbnZhbGlkRGVub3JtYWxpemUgJiYgIW1ldGE/LmVycm9yKSA/XG4gICAgICAgIEV4cGlyeVN0YXR1cy5JbnZhbGlkXG4gICAgICA6IGludmFsaWREZW5vcm1hbGl6ZSB8fCBpbnZhbGlkSWZTdGFsZSA/IEV4cGlyeVN0YXR1cy5JbnZhbGlkSWZTdGFsZVxuICAgICAgOiBFeHBpcnlTdGF0dXMuVmFsaWQ7XG5cbiAgICByZXR1cm4ge1xuICAgICAgZGF0YSxcbiAgICAgIGV4cGlyeVN0YXR1cyxcbiAgICAgIGV4cGlyZXNBdCxcbiAgICAgIGNvdW50UmVmOiB0aGlzLmdjUG9saWN5LmNyZWF0ZUNvdW50UmVmKHsga2V5LCBwYXRocyB9KSxcbiAgICB9O1xuICB9XG59XG5cbi8vIGJlbmNobWFyazogaHR0cHM6Ly93d3cubWVhc3VyZXRoYXQubmV0L0JlbmNobWFya3MvU2hvdy8yNDY5MS8wL21pbi1yZWR1Y2VyLXZzLWltcGVyYXRpdmUtd2l0aC1wYXRoc1xuLy8gZWFybGllc3QgZXhwaXJ5IGRpY3RhdGVzIGFnZVxuZnVuY3Rpb24gZW50aXR5RXhwaXJlc0F0KFxuICBwYXRoczogRW50aXR5UGF0aFtdLFxuICBlbnRpdHlNZXRhOiB7XG4gICAgcmVhZG9ubHkgW2VudGl0eUtleTogc3RyaW5nXToge1xuICAgICAgcmVhZG9ubHkgW3BrOiBzdHJpbmddOiB7XG4gICAgICAgIHJlYWRvbmx5IGRhdGU6IG51bWJlcjtcbiAgICAgICAgcmVhZG9ubHkgZXhwaXJlc0F0OiBudW1iZXI7XG4gICAgICAgIHJlYWRvbmx5IGZldGNoZWRBdDogbnVtYmVyOyAvLyBUaGlzIGlzIG9ubHkgdGhlIHZhbHVlIHVudGlsIGl0IGlzIHNldCBieSB0aGUgRGF0YVByb3ZpZGVyXG4gICAgICB9O1xuICAgIH07XG4gIH0sXG4pIHtcbiAgbGV0IGV4cGlyZXNBdCA9IEluZmluaXR5O1xuICBmb3IgKGNvbnN0IHsgcGssIGtleSB9IG9mIHBhdGhzKSB7XG4gICAgY29uc3QgZW50aXR5RXhwaXJ5ID0gZW50aXR5TWV0YVtrZXldPy5bcGtdPy5leHBpcmVzQXQ7XG4gICAgLy8gZXhwaXJlc0F0IHdpbGwgYWx3YXlzIHJlc29sdmUgdG8gZmFsc2Ugd2l0aCBhbnkgY29tcGFyaXNvblxuICAgIGlmIChlbnRpdHlFeHBpcnkgPCBleHBpcmVzQXQpIGV4cGlyZXNBdCA9IGVudGl0eUV4cGlyeTtcbiAgfVxuICByZXR1cm4gZXhwaXJlc0F0O1xufVxuXG4vKiogRGV0ZXJtaW5lIHdoZXRoZXIgdGhlIHNjaGVtYSBoYXMgYW55IGVudGl0aWVzLlxuICpcbiAqIFdpdGhvdXQgZW50aXRpZXMsIGRlbm9ybWFsaXphdGlvbiBpcyBub3QgbmVlZGVkLCBhbmQgcmVzdWx0cyBzaG91bGQgbm90IGJlIHF1ZXJpZWQuXG4gKi9cbmZ1bmN0aW9uIHNjaGVtYUhhc0VudGl0eShzY2hlbWE6IFNjaGVtYSk6IGJvb2xlYW4ge1xuICBpZiAoaXNFbnRpdHkoc2NoZW1hKSkgcmV0dXJuIHRydWU7XG4gIGlmIChBcnJheS5pc0FycmF5KHNjaGVtYSkpXG4gICAgcmV0dXJuIHNjaGVtYS5sZW5ndGggIT09IDAgJiYgc2NoZW1hSGFzRW50aXR5KHNjaGVtYVswXSk7XG4gIGlmIChzY2hlbWEgJiYgKHR5cGVvZiBzY2hlbWEgPT09ICdvYmplY3QnIHx8IHR5cGVvZiBzY2hlbWEgPT09ICdmdW5jdGlvbicpKSB7XG4gICAgY29uc3QgbmVzdGVkU2NoZW1hID1cbiAgICAgICdzY2hlbWEnIGluIHNjaGVtYSA/IChzY2hlbWEuc2NoZW1hIGFzIFJlY29yZDxzdHJpbmcsIFNjaGVtYT4pIDogc2NoZW1hO1xuICAgIGlmICh0eXBlb2YgbmVzdGVkU2NoZW1hID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICByZXR1cm4gc2NoZW1hSGFzRW50aXR5KG5lc3RlZFNjaGVtYSk7XG4gICAgfVxuICAgIHJldHVybiBPYmplY3QudmFsdWVzKG5lc3RlZFNjaGVtYSkuc29tZSh4ID0+IHNjaGVtYUhhc0VudGl0eSh4KSk7XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG5leHBvcnQgdHlwZSB7IEVycm9yVHlwZXMgfTtcblxuY2xhc3MgU25hcHNob3Q8VCA9IHVua25vd24+IGltcGxlbWVudHMgU25hcHNob3RJbnRlcmZhY2Uge1xuICBzdGF0aWMgcmVhZG9ubHkgYWJvcnQgPSBuZXcgQWJvcnRPcHRpbWlzdGljKCk7XG5cbiAgcHJpdmF0ZSBzdGF0ZTogU3RhdGU8VD47XG4gIHByaXZhdGUgY29udHJvbGxlcjogQ29udHJvbGxlcjtcbiAgcmVhZG9ubHkgZmV0Y2hlZEF0OiBudW1iZXI7XG4gIHJlYWRvbmx5IGFib3J0ID0gU25hcHNob3QuYWJvcnQ7XG5cbiAgY29uc3RydWN0b3IoY29udHJvbGxlcjogQ29udHJvbGxlciwgc3RhdGU6IFN0YXRlPFQ+LCBmZXRjaGVkQXQgPSAwKSB7XG4gICAgdGhpcy5zdGF0ZSA9IHN0YXRlO1xuICAgIHRoaXMuY29udHJvbGxlciA9IGNvbnRyb2xsZXI7XG4gICAgdGhpcy5mZXRjaGVkQXQgPSBmZXRjaGVkQXQ7XG4gIH1cblxuICAvKioqKioqKioqKioqKioqIERhdGEgQWNjZXNzICoqKioqKioqKioqKioqKi9cbiAgLyoqIEBzZWUgaHR0cHM6Ly9kYXRhY2xpZW50LmlvL2RvY3MvYXBpL1NuYXBzaG90I2dldFJlc3BvbnNlICovXG4gIGdldFJlc3BvbnNlPEUgZXh0ZW5kcyBFbmRwb2ludEludGVyZmFjZT4oXG4gICAgZW5kcG9pbnQ6IEUsXG4gICAgLi4uYXJnczogcmVhZG9ubHkgW251bGxdXG4gICk6IHtcbiAgICBkYXRhOiBEZW5vcm1hbGl6ZU51bGxhYmxlPEVbJ3NjaGVtYSddPjtcbiAgICBleHBpcnlTdGF0dXM6IEV4cGlyeVN0YXR1cztcbiAgICBleHBpcmVzQXQ6IG51bWJlcjtcbiAgfTtcblxuICBnZXRSZXNwb25zZTxFIGV4dGVuZHMgRW5kcG9pbnRJbnRlcmZhY2U+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIC4uLmFyZ3M6IHJlYWRvbmx5IFsuLi5QYXJhbWV0ZXJzPEU+XVxuICApOiB7XG4gICAgZGF0YTogRGVub3JtYWxpemVOdWxsYWJsZTxFWydzY2hlbWEnXT47XG4gICAgZXhwaXJ5U3RhdHVzOiBFeHBpcnlTdGF0dXM7XG4gICAgZXhwaXJlc0F0OiBudW1iZXI7XG4gIH07XG5cbiAgZ2V0UmVzcG9uc2U8XG4gICAgRSBleHRlbmRzIFBpY2s8RW5kcG9pbnRJbnRlcmZhY2UsICdrZXknIHwgJ3NjaGVtYScgfCAnaW52YWxpZElmU3RhbGUnPixcbiAgPihcbiAgICBlbmRwb2ludDogRSxcbiAgICAuLi5hcmdzOiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFWydrZXknXT5dIHwgcmVhZG9ubHkgW251bGxdXG4gICk6IHtcbiAgICBkYXRhOiBEZW5vcm1hbGl6ZU51bGxhYmxlPEVbJ3NjaGVtYSddPjtcbiAgICBleHBpcnlTdGF0dXM6IEV4cGlyeVN0YXR1cztcbiAgICBleHBpcmVzQXQ6IG51bWJlcjtcbiAgfTtcblxuICBnZXRSZXNwb25zZTxcbiAgICBFIGV4dGVuZHMgUGljazxFbmRwb2ludEludGVyZmFjZSwgJ2tleScgfCAnc2NoZW1hJyB8ICdpbnZhbGlkSWZTdGFsZSc+LFxuICA+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIC4uLmFyZ3M6IHJlYWRvbmx5IFsuLi5QYXJhbWV0ZXJzPEVbJ2tleSddPl0gfCByZWFkb25seSBbbnVsbF1cbiAgKToge1xuICAgIGRhdGE6IERlbm9ybWFsaXplTnVsbGFibGU8RVsnc2NoZW1hJ10+O1xuICAgIGV4cGlyeVN0YXR1czogRXhwaXJ5U3RhdHVzO1xuICAgIGV4cGlyZXNBdDogbnVtYmVyO1xuICB9IHtcbiAgICByZXR1cm4gdGhpcy5jb250cm9sbGVyLmdldFJlc3BvbnNlKGVuZHBvaW50LCAuLi5hcmdzLCB0aGlzLnN0YXRlKTtcbiAgfVxuXG4gIC8qKiBAc2VlIGh0dHBzOi8vZGF0YWNsaWVudC5pby9kb2NzL2FwaS9TbmFwc2hvdCNnZXRSZXNwb25zZU1ldGEgKi9cbiAgZ2V0UmVzcG9uc2VNZXRhPEUgZXh0ZW5kcyBFbmRwb2ludEludGVyZmFjZT4oXG4gICAgZW5kcG9pbnQ6IEUsXG4gICAgLi4uYXJnczogcmVhZG9ubHkgW251bGxdXG4gICk6IHtcbiAgICBkYXRhOiBEZW5vcm1hbGl6ZU51bGxhYmxlPEVbJ3NjaGVtYSddPjtcbiAgICBleHBpcnlTdGF0dXM6IEV4cGlyeVN0YXR1cztcbiAgICBleHBpcmVzQXQ6IG51bWJlcjtcbiAgfTtcblxuICBnZXRSZXNwb25zZU1ldGE8RSBleHRlbmRzIEVuZHBvaW50SW50ZXJmYWNlPihcbiAgICBlbmRwb2ludDogRSxcbiAgICAuLi5hcmdzOiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPl1cbiAgKToge1xuICAgIGRhdGE6IERlbm9ybWFsaXplTnVsbGFibGU8RVsnc2NoZW1hJ10+O1xuICAgIGV4cGlyeVN0YXR1czogRXhwaXJ5U3RhdHVzO1xuICAgIGV4cGlyZXNBdDogbnVtYmVyO1xuICB9O1xuXG4gIGdldFJlc3BvbnNlTWV0YTxcbiAgICBFIGV4dGVuZHMgUGljazxFbmRwb2ludEludGVyZmFjZSwgJ2tleScgfCAnc2NoZW1hJyB8ICdpbnZhbGlkSWZTdGFsZSc+LFxuICA+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIC4uLmFyZ3M6IHJlYWRvbmx5IFsuLi5QYXJhbWV0ZXJzPEVbJ2tleSddPl0gfCByZWFkb25seSBbbnVsbF1cbiAgKToge1xuICAgIGRhdGE6IERlbm9ybWFsaXplTnVsbGFibGU8RVsnc2NoZW1hJ10+O1xuICAgIGV4cGlyeVN0YXR1czogRXhwaXJ5U3RhdHVzO1xuICAgIGV4cGlyZXNBdDogbnVtYmVyO1xuICB9O1xuXG4gIGdldFJlc3BvbnNlTWV0YTxcbiAgICBFIGV4dGVuZHMgUGljazxFbmRwb2ludEludGVyZmFjZSwgJ2tleScgfCAnc2NoZW1hJyB8ICdpbnZhbGlkSWZTdGFsZSc+LFxuICA+KFxuICAgIGVuZHBvaW50OiBFLFxuICAgIC4uLmFyZ3M6IHJlYWRvbmx5IFsuLi5QYXJhbWV0ZXJzPEVbJ2tleSddPl0gfCByZWFkb25seSBbbnVsbF1cbiAgKToge1xuICAgIGRhdGE6IERlbm9ybWFsaXplTnVsbGFibGU8RVsnc2NoZW1hJ10+O1xuICAgIGV4cGlyeVN0YXR1czogRXhwaXJ5U3RhdHVzO1xuICAgIGV4cGlyZXNBdDogbnVtYmVyO1xuICB9IHtcbiAgICByZXR1cm4gdGhpcy5jb250cm9sbGVyLmdldFJlc3BvbnNlTWV0YShlbmRwb2ludCwgLi4uYXJncywgdGhpcy5zdGF0ZSk7XG4gIH1cblxuICAvKiogQHNlZSBodHRwczovL2RhdGFjbGllbnQuaW8vZG9jcy9hcGkvU25hcHNob3QjZ2V0RXJyb3IgKi9cbiAgZ2V0RXJyb3I8RSBleHRlbmRzIEVuZHBvaW50SW50ZXJmYWNlPihcbiAgICBlbmRwb2ludDogRSxcbiAgICAuLi5hcmdzOiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFPl0gfCByZWFkb25seSBbbnVsbF1cbiAgKTogRXJyb3JUeXBlcyB8IHVuZGVmaW5lZDtcblxuICBnZXRFcnJvcjxFIGV4dGVuZHMgUGljazxFbmRwb2ludEludGVyZmFjZSwgJ2tleSc+PihcbiAgICBlbmRwb2ludDogRSxcbiAgICAuLi5hcmdzOiByZWFkb25seSBbLi4uUGFyYW1ldGVyczxFWydrZXknXT5dIHwgcmVhZG9ubHkgW251bGxdXG4gICk6IEVycm9yVHlwZXMgfCB1bmRlZmluZWQ7X