package.dist.src.internal.pubsub-client.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sdk Show documentation
Show all versions of sdk Show documentation
Client SDK for Momento services
The newest version!
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PubsubClient = void 0;
const generated_types_1 = require("@gomomento/generated-types");
var grpcPubsub = generated_types_1.pubsub.cache_client.pubsub;
// older versions of node don't have the global util variables https://github.com/nodejs/node/issues/20365
const headers_interceptor_1 = require("./grpc/headers-interceptor");
const cache_service_error_mapper_1 = require("../errors/cache-service-error-mapper");
const grpc_js_1 = require("@grpc/grpc-js");
const package_json_1 = require("../../package.json");
const middlewares_interceptor_1 = require("./grpc/middlewares-interceptor");
const __1 = require("../");
const utils_1 = require("@gomomento/sdk-core/dist/src/internal/utils");
const AbstractPubsubClient_1 = require("@gomomento/sdk-core/dist/src/internal/clients/pubsub/AbstractPubsubClient");
const grpc_channel_options_1 = require("./grpc/grpc-channel-options");
const retry_interceptor_1 = require("./grpc/retry-interceptor");
class PubsubClient extends AbstractPubsubClient_1.AbstractPubsubClient {
// private static readonly RST_STREAM_NO_ERROR_MESSAGE =
// 'Received RST_STREAM with code 0';
/**
* @param {TopicClientProps} props
*/
constructor(props) {
super(props.configuration.getLoggerFactory(), props.configuration.getLoggerFactory().getLogger(PubsubClient.name), new cache_service_error_mapper_1.CacheServiceErrorMapper(props.configuration.getThrowOnErrors()));
this.credentialProvider = props.credentialProvider;
this.unaryRequestTimeoutMs = PubsubClient.DEFAULT_REQUEST_TIMEOUT_MS;
this.getLogger().debug(`Creating topic client using endpoint: '${this.credentialProvider.getCacheEndpoint()}'`);
const topicGrpcConfig = props.configuration
.getTransportStrategy()
.getGrpcConfig();
// NOTE: This is hard-coded for now but we may want to expose it via TopicConfiguration in the
// future, as we do with some of the other clients.
const grpcConfig = new __1.StaticGrpcConfiguration({
deadlineMillis: this.unaryRequestTimeoutMs,
maxSessionMemoryMb: PubsubClient.DEFAULT_MAX_SESSION_MEMORY_MB,
keepAlivePermitWithoutCalls: topicGrpcConfig.getKeepAlivePermitWithoutCalls(),
keepAliveTimeMs: topicGrpcConfig.getKeepAliveTimeMS(),
keepAliveTimeoutMs: topicGrpcConfig.getKeepAliveTimeoutMS(),
});
const channelOptions = (0, grpc_channel_options_1.grpcChannelOptionsFromGrpcConfig)(grpcConfig);
this.getLogger().debug(`Creating pubsub client with channel options: ${JSON.stringify(channelOptions, null, 2)}`);
this.client = new grpcPubsub.PubsubClient(this.credentialProvider.getCacheEndpoint(), this.credentialProvider.isCacheEndpointSecure()
? grpc_js_1.ChannelCredentials.createSsl()
: grpc_js_1.ChannelCredentials.createInsecure(), channelOptions);
const headers = [
new headers_interceptor_1.Header('Authorization', this.credentialProvider.getAuthToken()),
new headers_interceptor_1.Header('agent', `nodejs:topic:${package_json_1.version}`),
new headers_interceptor_1.Header('runtime-version', `nodejs:${process.versions.node}`),
];
this.unaryInterceptors = PubsubClient.initializeUnaryInterceptors(headers, props.configuration, this.unaryRequestTimeoutMs);
this.streamingInterceptors =
PubsubClient.initializeStreamingInterceptors(headers);
}
getEndpoint() {
const endpoint = this.credentialProvider.getCacheEndpoint();
this.getLogger().debug(`Using cache endpoint: ${endpoint}`);
return endpoint;
}
async sendPublish(cacheName, topicName, value) {
const topicValue = new grpcPubsub._TopicValue();
if (typeof value === 'string') {
topicValue.text = value;
}
else {
topicValue.binary = value;
}
const request = new grpcPubsub._PublishRequest({
cache_name: cacheName,
topic: topicName,
value: topicValue,
});
return await new Promise((resolve, reject) => {
this.client.Publish(request, {
interceptors: this.unaryInterceptors,
}, (err, resp) => {
if (resp) {
resolve(new __1.TopicPublish.Success());
}
else {
this.getCacheServiceErrorMapper().resolveOrRejectError({
err: err,
errorResponseFactoryFn: e => new __1.TopicPublish.Error(e),
resolveFn: resolve,
rejectFn: reject,
});
}
});
});
}
/**
* @remark This method is responsible for restarting the stream if it ends unexpectedly.
* Since we return a single subscription object to the user, we need to update it with the
* unsubscribe function should we restart the stream. This is why we pass the subscription
* state and subscription object to this method.
*
* Handling a cache not exists requires special care as well. In the most likely case,
* when the subscription starts and the cache does not exist, we receive an error immediately.
* We return an error from the subscribe method and do immediately unsubscribe. In a distinct,
* unlikely but possible case, the user deletes the cache while the stream is running. In this
* case we already returned a subscription object to the user, so we instead cancel the stream and
* propagate an error to the user via the error handler.
*/
sendSubscribe(options) {
const request = new grpcPubsub._SubscriptionRequest({
cache_name: options.cacheName,
topic: options.topicName,
resume_at_topic_sequence_number: options.subscriptionState.resumeAtTopicSequenceNumber,
});
this.getLogger().trace('Subscribing to topic with resume_at_topic_sequence_number: %s', options.subscriptionState.resumeAtTopicSequenceNumber);
const call = this.client.Subscribe(request, {
interceptors: this.streamingInterceptors,
});
options.subscriptionState.setSubscribed();
// Allow the caller to cancel the stream.
// Note that because we restart the stream on error or stream end,
// we need to ensure we keep the same subscription object. That way
// stream restarts are transparent to the caller.
options.subscriptionState.unsubscribeFn = () => {
call.cancel();
};
return new Promise((resolve, _reject) => {
const prepareCallbackOptions = {
...options,
resolve,
};
call.on('data', this.prepareDataCallback(prepareCallbackOptions));
call.on('error', this.prepareErrorCallback(prepareCallbackOptions));
call.on('end', this.prepareEndCallback(prepareCallbackOptions));
});
}
prepareDataCallback(options) {
return (resp) => {
if (options.firstMessage) {
options.resolve(options.subscription);
}
options.firstMessage = false;
if (resp.item) {
const sequenceNumber = resp.item.topic_sequence_number;
options.subscriptionState.lastTopicSequenceNumber = sequenceNumber;
this.getLogger().trace('Received an item on subscription stream; topic: %s; sequence number: %s', (0, utils_1.truncateString)(options.topicName), sequenceNumber);
if (resp.item.value.text) {
options.onItem(new __1.TopicItem(resp.item.value.text, sequenceNumber, {
tokenId: resp.item.publisher_id,
}));
}
else if (resp.item.value.binary) {
options.onItem(new __1.TopicItem(resp.item.value.binary, sequenceNumber, {
tokenId: resp.item.publisher_id,
}));
}
else {
this.getLogger().error('Received subscription item with unknown type; topic: %s', (0, utils_1.truncateString)(options.topicName));
options.onError(new __1.TopicSubscribe.Error(new __1.UnknownError('Unknown item value type')), options.subscription);
}
}
else if (resp.heartbeat) {
this.getLogger().trace('Received heartbeat from subscription stream; topic: %s', (0, utils_1.truncateString)(options.topicName));
options.onHeartbeat(new __1.TopicHeartbeat());
}
else if (resp.discontinuity) {
this.getLogger().trace('Received discontinuity from subscription stream; topic: %s', (0, utils_1.truncateString)(options.topicName));
options.onDiscontinuity(new __1.TopicDiscontinuity(resp.discontinuity.last_topic_sequence, resp.discontinuity.new_topic_sequence));
}
else {
this.getLogger().error('Received unknown subscription item; topic: %s', (0, utils_1.truncateString)(options.topicName));
options.onError(new __1.TopicSubscribe.Error(new __1.UnknownError('Unknown item type')), options.subscription);
}
};
}
prepareErrorCallback(options) {
return (err) => {
// When the caller unsubscribes, we may get a follow on error, which we ignore.
if (!options.subscriptionState.isSubscribed) {
return;
}
const serviceError = err;
this.getLogger().trace(`Subscription encountered an error: ${serviceError.code}: ${serviceError.message}: ${serviceError.details}`);
const shouldReconnectSubscription =
// previously, we were only attempting a reconnect on this one very specific case, but our current expectation is that
// we should err on the side of retrying. This may become a sort of "deny list" of error types to *not* retry on
// in the future, but for now we will be aggressive about retrying.
// // serviceError.code === Status.INTERNAL &&
// // serviceError.details === PubsubClient.RST_STREAM_NO_ERROR_MESSAGE;
true;
const momentoError = new __1.TopicSubscribe.Error(this.getCacheServiceErrorMapper().convertError(serviceError));
this.handleSubscribeError(options, momentoError, shouldReconnectSubscription);
};
}
static initializeUnaryInterceptors(headers, configuration, requestTimeoutMs) {
return [
(0, middlewares_interceptor_1.middlewaresInterceptor)(configuration.getLoggerFactory(), [], {}),
headers_interceptor_1.HeaderInterceptor.createHeadersInterceptor(headers),
retry_interceptor_1.RetryInterceptor.createRetryInterceptor({
clientName: 'PubSubClient',
loggerFactory: configuration.getLoggerFactory(),
overallRequestTimeoutMs: requestTimeoutMs,
}),
];
}
// TODO https://github.com/momentohq/client-sdk-nodejs/issues/349
// decide on streaming interceptors and middlewares
static initializeStreamingInterceptors(headers) {
return [headers_interceptor_1.HeaderInterceptor.createHeadersInterceptor(headers)];
}
}
exports.PubsubClient = PubsubClient;
PubsubClient.DEFAULT_REQUEST_TIMEOUT_MS = 5 * 1000;
PubsubClient.DEFAULT_MAX_SESSION_MEMORY_MB = 256;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVic3ViLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9pbnRlcm5hbC9wdWJzdWItY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLGdFQUFrRDtBQUNsRCxJQUFPLFVBQVUsR0FBRyx3QkFBTSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUM7QUFDL0MsMEdBQTBHO0FBQzFHLG9FQUFxRTtBQUNyRSxxRkFBNkU7QUFDN0UsMkNBQTRFO0FBQzVFLHFEQUEyQztBQUMzQyw0RUFBc0U7QUFDdEUsMkJBVWE7QUFDYix1RUFBMkU7QUFDM0Usb0hBSW1GO0FBR25GLHNFQUE2RTtBQUM3RSxnRUFBMEQ7QUFFMUQsTUFBYSxZQUFhLFNBQVEsMkNBQWtDO0lBU2xFLHdEQUF3RDtJQUN4RCx1Q0FBdUM7SUFFdkM7O09BRUc7SUFDSCxZQUFZLEtBQTBCO1FBQ3BDLEtBQUssQ0FDSCxLQUFLLENBQUMsYUFBYSxDQUFDLGdCQUFnQixFQUFFLEVBQ3RDLEtBQUssQ0FBQyxhQUFhLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxFQUNuRSxJQUFJLG9EQUF1QixDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxDQUNwRSxDQUFDO1FBQ0YsSUFBSSxDQUFDLGtCQUFrQixHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztRQUNuRCxJQUFJLENBQUMscUJBQXFCLEdBQUcsWUFBWSxDQUFDLDBCQUEwQixDQUFDO1FBQ3JFLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxLQUFLLENBQ3BCLDBDQUEwQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsZ0JBQWdCLEVBQUUsR0FBRyxDQUN4RixDQUFDO1FBRUYsTUFBTSxlQUFlLEdBQTJCLEtBQUssQ0FBQyxhQUFhO2FBQ2hFLG9CQUFvQixFQUFFO2FBQ3RCLGFBQWEsRUFBRSxDQUFDO1FBRW5CLDhGQUE4RjtR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QUMsb0JBQW9CLENBQ3ZCLE9BQU8sRUFDUCxZQUFZLEVBQ1osMkJBQTJCLENBQzVCLENBQUM7UUFDSixDQUFDLENBQUM7SUFDSixDQUFDO0lBRU8sTUFBTSxDQUFDLDJCQUEyQixDQUN4QyxPQUFpQixFQUNqQixhQUFpQyxFQUNqQyxnQkFBd0I7UUFFeEIsT0FBTztZQUNMLElBQUEsZ0RBQXNCLEVBQUMsYUFBYSxDQUFDLGdCQUFnQixFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztZQUNoRSx1Q0FBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxPQUFPLENBQUM7WUFDbkQsb0NBQWdCLENBQUMsc0JBQXNCLENBQUM7Z0JBQ3RDLFVBQVUsRUFBRSxjQUFjO2dCQUMxQixhQUFhLEVBQUUsYUFBYSxDQUFDLGdCQUFnQixFQUFFO2dCQUMvQyx1QkFBdUIsRUFBRSxnQkFBZ0I7YUFDMUMsQ0FBQztTQUNILENBQUM7SUFDSixDQUFDO0lBRUQsaUVBQWlFO0lBQ2pFLG1EQUFtRDtJQUMzQyxNQUFNLENBQUMsK0JBQStCLENBQzVDLE9BQWlCO1FBRWpCLE9BQU8sQ0FBQyx1Q0FBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQy9ELENBQUM7O0FBdlNILG9DQXdTQztBQXBTeUIsdUNBQTBCLEdBQVcsQ0FBQyxHQUFHLElBQUksQ0FBQztBQUM5QywwQ0FBNkIsR0FBVyxHQUFHLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge3B1YnN1Yn0gZnJvbSAnQGdvbW9tZW50by9nZW5lcmF0ZWQtdHlwZXMnO1xuaW1wb3J0IGdycGNQdWJzdWIgPSBwdWJzdWIuY2FjaGVfY2xpZW50LnB1YnN1Yjtcbi8vIG9sZGVyIHZlcnNpb25zIG9mIG5vZGUgZG9uJ3QgaGF2ZSB0aGUgZ2xvYmFsIHV0aWwgdmFyaWFibGVzIGh0dHBzOi8vZ2l0aHViLmNvbS9ub2RlanMvbm9kZS9pc3N1ZXMvMjAzNjVcbmltcG9ydCB7SGVhZGVyLCBIZWFkZXJJbnRlcmNlcHRvcn0gZnJvbSAnLi9ncnBjL2hlYWRlcnMtaW50ZXJjZXB0b3InO1xuaW1wb3J0IHtDYWNoZVNlcnZpY2VFcnJvck1hcHBlcn0gZnJvbSAnLi4vZXJyb3JzL2NhY2hlLXNlcnZpY2UtZXJyb3ItbWFwcGVyJztcbmltcG9ydCB7Q2hhbm5lbENyZWRlbnRpYWxzLCBJbnRlcmNlcHRvciwgU2VydmljZUVycm9yfSBmcm9tICdAZ3JwYy9ncnBjLWpzJztcbmltcG9ydCB7dmVyc2lvbn0gZnJvbSAnLi4vLi4vcGFja2FnZS5qc29uJztcbmltcG9ydCB7bWlkZGxld2FyZXNJbnRlcmNlcHRvcn0gZnJvbSAnLi9ncnBjL21pZGRsZXdhcmVzLWludGVyY2VwdG9yJztcbmltcG9ydCB7XG4gIENyZWRlbnRpYWxQcm92aWRlcixcbiAgU3RhdGljR3JwY0NvbmZpZ3VyYXRpb24sXG4gIFRvcGljRGlzY29udGludWl0eSxcbiAgVG9waWNIZWFydGJlYXQsXG4gIFRvcGljR3JwY0NvbmZpZ3VyYXRpb24sXG4gIFRvcGljSXRlbSxcbiAgVG9waWNQdWJsaXNoLFxuICBUb3BpY1N1YnNjcmliZSxcbiAgVW5rbm93bkVycm9yLFxufSBmcm9tICcuLi8nO1xuaW1wb3J0IHt0cnVuY2F0ZVN0cmluZ30gZnJvbSAnQGdvbW9tZW50by9zZGstY29yZS9kaXN0L3NyYy9pbnRlcm5hbC91dGlscyc7XG5pbXBvcnQge1xuICBBYnN0cmFjdFB1YnN1YkNsaWVudCxcbiAgU2VuZFN1YnNjcmliZU9wdGlvbnMsXG4gIFByZXBhcmVTdWJzY3JpYmVDYWxsYmFja09wdGlvbnMsXG59IGZyb20gJ0Bnb21vbWVudG8vc2RrLWNvcmUvZGlzdC9zcmMvaW50ZXJuYWwvY2xpZW50cy9wdWJzdWIvQWJzdHJhY3RQdWJzdWJDbGllbnQnO1xuaW1wb3J0IHtUb3BpY0NvbmZpZ3VyYXRpb259IGZyb20gJy4uL2NvbmZpZy90b3BpYy1jb25maWd1cmF0aW9uJztcbmltcG9ydCB7VG9waWNDbGllbnRBbGxQcm9wc30gZnJvbSAnLi90b3BpYy1jbGllbnQtYWxsLXByb3BzJztcbmltcG9ydCB7Z3JwY0NoYW5uZWxPcHRpb25zRnJvbUdycGNDb25maWd9IGZyb20gJy4vZ3JwYy9ncnBjLWNoYW5uZWwtb3B0aW9ucyc7XG5pbXBvcnQge1JldHJ5SW50ZXJjZXB0b3J9IGZyb20gJy4vZ3JwYy9yZXRyeS1pbnRlcmNlcHRvcic7XG5cbmV4cG9ydCBjbGFzcyBQdWJzdWJDbGllbnQgZXh0ZW5kcyBBYnN0cmFjdFB1YnN1YkNsaWVudDxTZXJ2aWNlRXJyb3I+IHtcbiAgcHJpdmF0ZSByZWFkb25seSBjbGllbnQ6IGdycGNQdWJzdWIuUHVic3ViQ2xpZW50O1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY3JlZGVudGlhbFByb3ZpZGVyOiBDcmVkZW50aWFsUHJvdmlkZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgdW5hcnlSZXF1ZXN0VGltZW91dE1zOiBudW1iZXI7XG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IERFRkFVTFRfUkVRVUVTVF9USU1FT1VUX01TOiBudW1iZXIgPSA1ICogMTAwMDtcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgREVGQVVMVF9NQVhfU0VTU0lPTl9NRU1PUllfTUI6IG51bWJlciA9IDI1NjtcbiAgcHJpdmF0ZSByZWFkb25seSB1bmFyeUludGVyY2VwdG9yczogSW50ZXJjZXB0b3JbXTtcbiAgcHJpdmF0ZSByZWFkb25seSBzdHJlYW1pbmdJbnRlcmNlcHRvcnM6IEludGVyY2VwdG9yW107XG5cbiAgLy8gcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgUlNUX1NUUkVBTV9OT19FUlJPUl9NRVNTQUdFID1cbiAgLy8gICAnUmVjZWl2ZWQgUlNUX1NUUkVBTSB3aXRoIGNvZGUgMCc7XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7VG9waWNDbGllbnRQcm9wc30gcHJvcHNcbiAgICovXG4gIGNvbnN0cnVjdG9yKHByb3BzOiBUb3BpY0NsaWVudEFsbFByb3BzKSB7XG4gICAgc3VwZXIoXG4gICAgICBwcm9wcy5jb25maWd1cmF0aW9uLmdldExvZ2dlckZhY3RvcnkoKSxcbiAgICAgIHByb3BzLmNvbmZpZ3VyYXRpb24uZ2V0TG9nZ2VyRmFjdG9yeSgpLmdldExvZ2dlcihQdWJzdWJDbGllbnQubmFtZSksXG4gICAgICBuZXcgQ2FjaGVTZXJ2aWNlRXJyb3JNYXBwZXIocHJvcHMuY29uZmlndXJhdGlvbi5nZXRUaHJvd09uRXJyb3JzKCkpXG4gICAgKTtcbiAgICB0aGlzLmNyZWRlbnRpYWxQcm92aWRlciA9IHByb3BzLmNyZWRlbnRpYWxQcm92aWRlcjtcbiAgICB0aGlzLnVuYXJ5UmVxdWVzdFRpbWVvdXRNcyA9IFB1YnN1YkNsaWVudC5ERUZBVUxUX1JFUVVFU1RfVElNRU9VVF9NUztcbiAgICB0aGlzLmdldExvZ2dlcigpLmRlYnVnKFxuICAgICAgYENyZWF0aW5nIHRvcGljIGNsaWVudCB1c2luZyBlbmRwb2ludDogJyR7dGhpcy5jcmVkZW50aWFsUHJvdmlkZXIuZ2V0Q2FjaGVFbmRwb2ludCgpfSdgXG4gICAgKTtcblxuICAgIGNvbnN0IHRvcGljR3JwY0NvbmZpZzogVG9waWNHcnBjQ29uZmlndXJhdGlvbiA9IHByb3BzLmNvbmZpZ3VyYXRpb25cbiAgICAgIC5nZXRUcmFuc3BvcnRTdHJhdGVneSgpXG4gICAgICAuZ2V0R3JwY0NvbmZpZygpO1xuXG4gICAgLy8gTk9URTogVGhpcyBpcyBoYXJkLWNvZGVkIGZvciBub3cgYnV0IHdlIG1heSB3YW50IHRvIGV4cG9zZSBpdCB2aWEgVG9waWNDb25maWd1cmF0aW9uIGluIHRoZVxuICAgIC8vIGZ1dHVyZSwgYXMgd2UgZG8gd2l0aCBzb21lIG9mIHRoZSBvdGhlciBjbGllbnRzLlxuICAgIGNvbnN0IGdycGNDb25maWcgPSBuZXcgU3RhdGljR3JwY0NvbmZpZ3VyYXRpb24oe1xuICAgICAgZGVhZGxpbmVNaWxsaXM6IHRoaXMudW5hcnlSZXF1ZXN0VGltZW91dE1zLFxuICAgICAgbWF4U2Vzc2lvbk1lbW9yeU1iOiBQdWJzdWJDbGllbnQuREVGQVVMVF9NQVhfU0VTU0lPTl9NRU1PUllfTUIsXG4gICAgICBrZWVwQWxpdmVQZXJtaXRXaXRob3V0Q2FsbHM6XG4gICAgICAgIHRvcGljR3JwY0NvbmZpZy5nZXRLZWVwQWxpdmVQZXJtaXRXaXRob3V0Q2FsbHMoKSxcbiAgICAgIGtlZXBBbGl2ZVRpbWVNczogdG9waWNHcnBjQ29uZmlnLmdldEtlZXBBbGl2ZVRpbWVNUygpLFxuICAgICAga2VlcEFsaXZlVGltZW91dE1zOiB0b3BpY0dycGNDb25maWcuZ2V0S2VlcEFsaXZlVGltZW91dE1TKCksXG4gICAgfSk7XG5cbiAgICBjb25zdCBjaGFubmVsT3B0aW9ucyA9IGdycGNDaGFubmVsT3B0aW9uc0Zyb21HcnBjQ29uZmlnKGdycGNDb25maWcpO1xuXG4gICAgdGhpcy5nZXRMb2dnZXIoKS5kZWJ1ZyhcbiAgICAgIGBDcmVhdGluZyBwdWJzdWIgY2xpZW50IHdpdGggY2hhbm5lbCBvcHRpb25zOiAke0pTT04uc3RyaW5naWZ5KFxuICAgICAgICBjaGFubmVsT3B0aW9ucyxcbiAgICAgICAgbnVsbCxcbiAgICAgICAgMlxuICAgICAgKX1gXG4gICAgKTtcblxuICAgIHRoaXMuY2xpZW50ID0gbmV3IGdycGNQdWJzdWIuUHVic3ViQ2xpZW50KFxuICAgICAgdGhpcy5jcmVkZW50aWFsUHJvdmlkZXIuZ2V0Q2FjaGVFbmRwb2ludCgpLFxuICAgICAgdGhpcy5jcmVkZW50aWFsUHJvdmlkZXIuaXNDYWNoZUVuZHBvaW50U2VjdXJlKClcbiAgICAgICAgPyBDaGFubmVsQ3JlZGVudGlhbHMuY3JlYXRlU3NsKClcbiAgICAgICAgOiBDaGFubmVsQ3JlZGVudGlhbHMuY3JlYXRlSW5zZWN1cmUoKSxcbiAgICAgIGNoYW5uZWxPcHRpb25zXG4gICAgKTtcblxuICAgIGNvbnN0IGhlYWRlcnM6IEhlYWRlcltdID0gW1xuICAgICAgbmV3IEhlYWRlcignQXV0aG9yaXphdGlvbicsIHRoaXMuY3JlZGVudGlhbFByb3ZpZGVyLmdldEF1dGhUb2tlbigpKSxcbiAgICAgIG5ldyBIZWFkZXIoJ2FnZW50JywgYG5vZGVqczp0b3BpYzoke3ZlcnNpb259YCksXG4gICAgICBuZXcgSGVhZGVyKCdydW50aW1lLXZlcnNpb24nLCBgbm9kZWpzOiR7cHJvY2Vzcy52ZXJzaW9ucy5ub2RlfWApLFxuICAgIF07XG4gICAgdGhpcy51bmFyeUludGVyY2VwdG9ycyA9IFB1YnN1YkNsaWVudC5pbml0aWFsaXplVW5hcnlJbnRlcmNlcHRvcnMoXG4gICAgICBoZWFkZXJzLFxuICAgICAgcHJvcHMuY29uZmlndXJhdGlvbixcbiAgICAgIHRoaXMudW5hcnlSZXF1ZXN0VGltZW91dE1zXG4gICAgKTtcbiAgICB0aGlzLnN0cmVhbWluZ0ludGVyY2VwdG9ycyA9XG4gICAgICBQdWJzdWJDbGllbnQuaW5pdGlhbGl6ZVN0cmVhbWluZ0ludGVyY2VwdG9ycyhoZWFkZXJzKTtcbiAgfVxuXG4gIHB1YmxpYyBnZXRFbmRwb2ludCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IGVuZHBvaW50ID0gdGhpcy5jcmVkZW50aWFsUHJvdmlkZXIuZ2V0Q2FjaGVFbmRwb2ludCgpO1xuICAgIHRoaXMuZ2V0TG9nZ2VyKCkuZGVidWcoYFVzaW5nIGNhY2hlIGVuZHBvaW50OiAke2VuZHBvaW50fWApO1xuICAgIHJldHVybiBlbmRwb2ludDtcbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyBzZW5kUHVibGlzaChcbiAgICBjYWNoZU5hbWU6IHN0cmluZyxcbiAgICB0b3BpY05hbWU6IHN0cmluZyxcbiAgICB2YWx1ZTogc3RyaW5nIHwgVWludDhBcnJheVxuICApOiBQcm9taXNlPFRvcGljUHVibGlzaC5SZXNwb25zZT4ge1xuICAgIGNvbnN0IHRvcGljVmFsdWUgPSBuZXcgZ3JwY1B1YnN1Yi5fVG9waWNWYWx1ZSgpO1xuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICB0b3BpY1ZhbHVlLnRleHQgPSB2YWx1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgdG9waWNWYWx1ZS5iaW5hcnkgPSB2YWx1ZTtcbiAgICB9XG5cbiAgICBjb25zdCByZXF1ZXN0ID0gbmV3IGdycGNQdWJzdWIuX1B1Ymxpc2hSZXF1ZXN0KHtcbiAgICAgIGNhY2hlX25hbWU6IGNhY2hlTmFtZSxcbiAgICAgIHRvcGljOiB0b3BpY05hbWUsXG4gICAgICB2YWx1ZTogdG9waWNWYWx1ZSxcbiAgICB9KTtcblxuICAgIHJldHVybiBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICB0aGlzLmNsaWVudC5QdWJsaXNoKFxuICAgICAgICByZXF1ZXN0LFxuICAgICAgICB7XG4gICAgICAgICAgaW50ZXJjZXB0b3JzOiB0aGlzLnVuYXJ5SW50ZXJjZXB0b3JzLFxuICAgICAgICB9LFxuICAgICAgICAoZXJyLCByZXNwKSA9PiB7XG4gICAgICAgICAgaWYgKHJlc3ApIHtcbiAgICAgICAgICAgIHJlc29sdmUobmV3IFRvcGljUHVibGlzaC5TdWNjZXNzKCkpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmdldENhY2hlU2VydmljZUVycm9yTWFwcGVyKCkucmVzb2x2ZU9yUmVqZWN0RXJyb3Ioe1xuICAgICAgICAgICAgICBlcnI6IGVycixcbiAgICAgICAgICAgICAgZXJyb3JSZXNwb25zZUZhY3RvcnlGbjogZSA9PiBuZXcgVG9waWNQdWJsaXNoLkVycm9yKGUpLFxuICAgICAgICAgICAgICByZXNvbHZlRm46IHJlc29sdmUsXG4gICAgICAgICAgICAgIHJlamVjdEZuOiByZWplY3QsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQHJlbWFyayBUaGlzIG1ldGhvZCBpcyByZXNwb25zaWJsZSBmb3IgcmVzdGFydGluZyB0aGUgc3RyZWFtIGlmIGl0IGVuZHMgdW5leHBlY3RlZGx5LlxuICAgKiBTaW5jZSB3ZSByZXR1cm4gYSBzaW5nbGUgc3Vic2NyaXB0aW9uIG9iamVjdCB0byB0aGUgdXNlciwgd2UgbmVlZCB0byB1cGRhdGUgaXQgd2l0aCB0aGVcbiAgICogdW5zdWJzY3JpYmUgZnVuY3Rpb24gc2hvdWxkIHdlIHJlc3RhcnQgdGhlIHN0cmVhbS4gVGhpcyBpcyB3aHkgd2UgcGFzcyB0aGUgc3Vic2NyaXB0aW9uXG4gICAqIHN0YXRlIGFuZCBzdWJzY3JpcHRpb24gb2JqZWN0IHRvIHRoaXMgbWV0aG9kLlxuICAgKlxuICAgKiBIYW5kbGluZyBhIGNhY2hlIG5vdCBleGlzdHMgcmVxdWlyZXMgc3BlY2lhbCBjYXJlIGFzIHdlbGwuIEluIHRoZSBtb3N0IGxpa2VseSBjYXNlLFxuICAgKiB3aGVuIHRoZSBzdWJzY3JpcHRpb24gc3RhcnRzIGFuZCB0aGUgY2FjaGUgZG9lcyBub3QgZXhpc3QsIHdlIHJlY2VpdmUgYW4gZXJyb3IgaW1tZWRpYXRlbHkuXG4gICAqIFdlIHJldHVybiBhbiBlcnJvciBmcm9tIHRoZSBzdWJzY3JpYmUgbWV0aG9kIGFuZCBkbyBpbW1lZGlhdGVseSB1bnN1YnNjcmliZS4gSW4gYSBkaXN0aW5jdCxcbiAgICogdW5saWtlbHkgYnV0IHBvc3NpYmxlIGNhc2UsIHRoZSB1c2VyIGRlbGV0ZXMgdGhlIGNhY2hlIHdoaWxlIHRoZSBzdHJlYW0gaXMgcnVubmluZy4gSW4gdGhpc1xuICAgKiBjYXNlIHdlIGFscmVhZHkgcmV0dXJuZWQgYSBzdWJzY3JpcHRpb24gb2JqZWN0IHRvIHRoZSB1c2VyLCBzbyB3ZSBpbnN0ZWFkIGNhbmNlbCB0aGUgc3RyZWFtIGFuZFxuICAgKiBwcm9wYWdhdGUgYW4gZXJyb3IgdG8gdGhlIHVzZXIgdmlhIHRoZSBlcnJvciBoYW5kbGVyLlxuICAgKi9cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIHNlbmRTdWJzY3JpYmUoXG4gICAgb3B0aW9uczogU2VuZFN1YnNjcmliZU9wdGlvbnNcbiAgKTogUHJvbWlzZTxUb3BpY1N1YnNjcmliZS5SZXNwb25zZT4ge1xuICAgIGNvbnN0IHJlcXVlc3QgPSBuZXcgZ3JwY1B1YnN1Yi5fU3Vic2NyaXB0aW9uUmVxdWVzdCh7XG4gICAgICBjYWNoZV9uYW1lOiBvcHRpb25zLmNhY2hlTmFtZSxcbiAgICAgIHRvcGljOiBvcHRpb25zLnRvcGljTmFtZSxcbiAgICAgIHJlc3VtZV9hdF90b3BpY19zZXF1ZW5jZV9udW1iZXI6XG4gICAgICAgIG9wdGlvbnMuc3Vic2NyaXB0aW9uU3RhdGUucmVzdW1lQXRUb3BpY1NlcXVlbmNlTnVtYmVyLFxuICAgIH0pO1xuXG4gICAgdGhpcy5nZXRMb2dnZXIoKS50cmFjZShcbiAgICAgICdTdWJzY3JpYmluZyB0byB0b3BpYyB3aXRoIHJlc3VtZV9hdF90b3BpY19zZXF1ZW5jZV9udW1iZXI6ICVzJyxcbiAgICAgIG9wdGlvbnMuc3Vic2NyaXB0aW9uU3RhdGUucmVzdW1lQXRUb3BpY1NlcXVlbmNlTnVtYmVyXG4gICAgKTtcbiAgICBjb25zdCBjYWxsID0gdGhpcy5jbGllbnQuU3Vic2NyaWJlKHJlcXVlc3QsIHtcbiAgICAgIGludGVyY2VwdG9yczogdGhpcy5zdHJlYW1pbmdJbnRlcmNlcHRvcnMsXG4gICAgfSk7XG4gICAgb3B0aW9ucy5zdWJzY3JpcHRpb25TdGF0ZS5zZXRTdWJzY3JpYmVkKCk7XG5cbiAgICAvLyBBbGxvdyB0aGUgY2FsbGVyIHRvIGNhbmNlbCB0aGUgc3RyZWFtLlxuICAgIC8vIE5vdGUgdGhhdCBiZWNhdXNlIHdlIHJlc3RhcnQgdGhlIHN0cmVhbSBvbiBlcnJvciBvciBzdHJlYW0gZW5kLFxuICAgIC8vIHdlIG5lZWQgdG8gZW5zdXJlIHdlIGtlZXAgdGhlIHNhbWUgc3Vic2NyaXB0aW9uIG9iamVjdC4gVGhhdCB3YXlcbiAgICAvLyBzdHJlYW0gcmVzdGFydHMgYXJlIHRyYW5zcGFyZW50IHRvIHRoZSBjYWxsZXIuXG4gICAgb3B0aW9ucy5zdWJzY3JpcHRpb25TdGF0ZS51bnN1YnNjcmliZUZuID0gKCkgPT4ge1xuICAgICAgY2FsbC5jYW5jZWwoKTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCBfcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBwcmVwYXJlQ2FsbGJhY2tPcHRpb25zOiBQcmVwYXJlU3Vic2NyaWJlQ2FsbGJhY2tPcHRpb25zID0ge1xuICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgICByZXNvbHZlLFxuICAgICAgfTtcbiAgICAgIGNhbGwub24oJ2RhdGEnLCB0aGlzLnByZXBhcmVEYXRhQ2FsbGJhY2socHJlcGFyZUNhbGxiYWNrT3B0aW9ucykpO1xuICAgICAgY2FsbC5vbignZXJyb3InLCB0aGlzLnByZXBhcmVFcnJvckNhbGxiYWNrKHByZXBhcmVDYWxsYmFja09wdGlvbnMpKTtcbiAgICAgIGNhbGwub24oJ2VuZCcsIHRoaXMucHJlcGFyZUVuZENhbGxiYWNrKHByZXBhcmVDYWxsYmFja09wdGlvbnMpKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgcHJlcGFyZURhdGFDYWxsYmFjayhcbiAgICBvcHRpb25zOiBQcmVwYXJlU3Vic2NyaWJlQ2FsbGJhY2tPcHRpb25zXG4gICk6IChyZXNwOiBncnBjUHVic3ViLl9TdWJzY3JpcHRpb25JdGVtKSA9PiB2b2lkIHtcbiAgICByZXR1cm4gKHJlc3A6IGdycGNQdWJzdWIuX1N1YnNjcmlwdGlvbkl0ZW0pID0+IHtcbiAgICAgIGlmIChvcHRpb25zLmZpcnN0TWVzc2FnZSkge1xuICAgICAgICBvcHRpb25zLnJlc29sdmUob3B0aW9ucy5zdWJzY3JpcHRpb24pO1xuICAgICAgfVxuICAgICAgb3B0aW9ucy5maXJzdE1lc3NhZ2UgPSBmYWxzZTtcblxuICAgICAgaWYgKHJlc3AuaXRlbSkge1xuICAgICAgICBjb25zdCBzZXF1ZW5jZU51bWJlciA9IHJlc3AuaXRlbS50b3BpY19zZXF1ZW5jZV9udW1iZXI7XG4gICAgICAgIG9wdGlvbnMuc3Vic2NyaXB0aW9uU3RhdGUubGFzdFRvcGljU2VxdWVuY2VOdW1iZXIgPSBzZXF1ZW5jZU51bWJlcjtcbiAgICAgICAgdGhpcy5nZXRMb2dnZXIoKS50cmFjZShcbiAgICAgICAgICAnUmVjZWl2ZWQgYW4gaXRlbSBvbiBzdWJzY3JpcHRpb24gc3RyZWFtOyB0b3BpYzogJXM7IHNlcXVlbmNlIG51bWJlcjogJXMnLFxuICAgICAgICAgIHRydW5jYXRlU3RyaW5nKG9wdGlvbnMudG9waWNOYW1lKSxcbiAgICAgICAgICBzZXF1ZW5jZU51bWJlclxuICAgICAgICApO1xuICAgICAgICBpZiAocmVzcC5pdGVtLnZhbHVlLnRleHQpIHtcbiAgICAgICAgICBvcHRpb25zLm9uSXRlbShcbiAgICAgICAgICAgIG5ldyBUb3BpY0l0ZW0ocmVzcC5pdGVtLnZhbHVlLnRleHQsIHNlcXVlbmNlTnVtYmVyLCB7XG4gICAgICAgICAgICAgIHRva2VuSWQ6IHJlc3AuaXRlbS5wdWJsaXNoZXJfaWQsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICk7XG4gICAgICAgIH0gZWxzZSBpZiAocmVzcC5pdGVtLnZhbHVlLmJpbmFyeSkge1xuICAgICAgICAgIG9wdGlvbnMub25JdGVtKFxuICAgICAgICAgICAgbmV3IFRvcGljSXRlbShyZXNwLml0ZW0udmFsdWUuYmluYXJ5LCBzZXF1ZW5jZU51bWJlciwge1xuICAgICAgICAgICAgICB0b2tlbklkOiByZXNwLml0ZW0ucHVibGlzaGVyX2lkLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMuZ2V0TG9nZ2VyKCkuZXJyb3IoXG4gICAgICAgICAgICAnUmVjZWl2ZWQgc3Vic2NyaXB0aW9uIGl0ZW0gd2l0aCB1bmtub3duIHR5cGU7IHRvcGljOiAlcycsXG4gICAgICAgICAgICB0cnVuY2F0ZVN0cmluZyhvcHRpb25zLnRvcGljTmFtZSlcbiAgICAgICAgICApO1xuICAgICAgICAgIG9wdGlvbnMub25FcnJvcihcbiAgICAgICAgICAgIG5ldyBUb3BpY1N1YnNjcmliZS5FcnJvcihcbiAgICAgICAgICAgICAgbmV3IFVua25vd25FcnJvcignVW5rbm93biBpdGVtIHZhbHVlIHR5cGUnKVxuICAgICAgICAgICAgKSxcbiAgICAgICAgICAgIG9wdGlvbnMuc3Vic2NyaXB0aW9uXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChyZXNwLmhlYXJ0YmVhdCkge1xuICAgICAgICB0aGlzLmdldExvZ2dlcigpLnRyYWNlKFxuICAgICAgICAgICdSZWNlaXZlZCBoZWFydGJlYXQgZnJvbSBzdWJzY3JpcHRpb24gc3RyZWFtOyB0b3BpYzogJXMnLFxuICAgICAgICAgIHRydW5jYXRlU3RyaW5nKG9wdGlvbnMudG9waWNOYW1lKVxuICAgICAgICApO1xuICAgICAgICBvcHRpb25zLm9uSGVhcnRiZWF0KG5ldyBUb3BpY0hlYXJ0YmVhdCgpKTtcbiAgICAgIH0gZWxzZSBpZiAocmVzcC5kaXNjb250aW51aXR5KSB7XG4gICAgICAgIHRoaXMuZ2V0TG9nZ2VyKCkudHJhY2UoXG4gICAgICAgICAgJ1JlY2VpdmVkIGRpc2NvbnRpbnVpdHkgZnJvbSBzdWJzY3JpcHRpb24gc3RyZWFtOyB0b3BpYzogJXMnLFxuICAgICAgICAgIHRydW5jYXRlU3RyaW5nKG9wdGlvbnMudG9waWNOYW1lKVxuICAgICAgICApO1xuICAgICAgICBvcHRpb25zLm9uRGlzY29udGludWl0eShcbiAgICAgICAgICBuZXcgVG9waWNEaXNjb250aW51aXR5KFxuICAgICAgICAgICAgcmVzcC5kaXNjb250aW51aXR5Lmxhc3RfdG9waWNfc2VxdWVuY2UsXG4gICAgICAgICAgICByZXNwLmRpc2NvbnRpbnVpdHkubmV3X3RvcGljX3NlcXVlbmNlXG4gICAgICAgICAgKVxuICAgICAgICApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5nZXRMb2dnZXIoKS5lcnJvcihcbiAgICAgICAgICAnUmVjZWl2ZWQgdW5rbm93biBzdWJzY3JpcHRpb24gaXRlbTsgdG9waWM6ICVzJyxcbiAgICAgICAgICB0cnVuY2F0ZVN0cmluZyhvcHRpb25zLnRvcGljTmFtZSlcbiAgICAgICAgKTtcbiAgICAgICAgb3B0aW9ucy5vbkVycm9yKFxuICAgICAgICAgIG5ldyBUb3BpY1N1YnNjcmliZS5FcnJvcihuZXcgVW5rbm93bkVycm9yKCdVbmtub3duIGl0ZW0gdHlwZScpKSxcbiAgICAgICAgICBvcHRpb25zLnN1YnNjcmlwdGlvblxuICAgICAgICApO1xuICAgICAgfVxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIHByZXBhcmVFcnJvckNhbGxiYWNrKFxuICAgIG9wdGlvbnM6IFByZXBhcmVTdWJzY3JpYmVDYWxsYmFja09wdGlvbnNcbiAgKTogKGVycjogRXJyb3IpID0+IHZvaWQge1xuICAgIHJldHVybiAoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgLy8gV2hlbiB0aGUgY2FsbGVyIHVuc3Vic2NyaWJlcywgd2UgbWF5IGdldCBhIGZvbGxvdyBvbiBlcnJvciwgd2hpY2ggd2UgaWdub3JlLlxuICAgICAgaWYgKCFvcHRpb25zLnN1YnNjcmlwdGlvblN0YXRlLmlzU3Vic2NyaWJlZCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHNlcnZpY2VFcnJvciA9IGVyciBhcyB1bmtub3duIGFzIFNlcnZpY2VFcnJvcjtcbiAgICAgIHRoaXMuZ2V0TG9nZ2VyKCkudHJhY2UoXG4gICAgICAgIGBTdWJzY3JpcHRpb24gZW5jb3VudGVyZWQgYW4gZXJyb3I6ICR7c2VydmljZUVycm9yLmNvZGV9OiAke3NlcnZpY2VFcnJvci5tZXNzYWdlfTogJHtzZXJ2aWNlRXJyb3IuZGV0YWlsc31gXG4gICAgICApO1xuICAgICAgY29uc3Qgc2hvdWxkUmVjb25uZWN0U3Vic2NyaXB0aW9uID1cbiAgICAgICAgLy8gcHJldmlvdXNseSwgd2Ugd2VyZSBvbmx5IGF0dGVtcHRpbmcgYSByZWNvbm5lY3Qgb24gdGhpcyBvbmUgdmVyeSBzcGVjaWZpYyBjYXNlLCBidXQgb3VyIGN1cnJlbnQgZXhwZWN0YXRpb24gaXMgdGhhdFxuICAgICAgICAvLyB3ZSBzaG91bGQgZXJyIG9uIHRoZSBzaWRlIG9mIHJldHJ5aW5nLiBUaGlzIG1heSBiZWNvbWUgYSBzb3J0IG9mIFwiZGVueSBsaXN0XCIgb2YgZXJyb3IgdHlwZXMgdG8gKm5vdCogcmV0cnkgb25cbiAgICAgICAgLy8gaW4gdGhlIGZ1dHVyZSwgYnV0IGZvciBub3cgd2Ugd2lsbCBiZSBhZ2dyZXNzaXZlIGFib3V0IHJldHJ5aW5nLlxuICAgICAgICAvLyAvLyBzZXJ2aWNlRXJyb3IuY29kZSA9PT0gU3RhdHVzLklOVEVSTkFMICYmXG4gICAgICAgIC8vICAvLyBzZXJ2aWNlRXJyb3IuZGV0YWlscyA9PT0gUHVic3ViQ2xpZW50LlJTVF9TVFJFQU1fTk9fRVJST1JfTUVTU0FHRTtcbiAgICAgICAgdHJ1ZTtcbiAgICAgIGNvbnN0IG1vbWVudG9FcnJvciA9IG5ldyBUb3BpY1N1YnNjcmliZS5FcnJvcihcbiAgICAgICAgdGhpcy5nZXRDYWNoZVNlcnZpY2VFcnJvck1hcHBlcigpLmNvbnZlcnRFcnJvcihzZXJ2aWNlRXJyb3IpXG4gICAgICApO1xuICAgICAgdGhpcy5oYW5kbGVTdWJzY3JpYmVFcnJvcihcbiAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgbW9tZW50b0Vycm9yLFxuICAgICAgICBzaG91bGRSZWNvbm5lY3RTdWJzY3JpcHRpb25cbiAgICAgICk7XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGluaXRpYWxpemVVbmFyeUludGVyY2VwdG9ycyhcbiAgICBoZWFkZXJzOiBIZWFkZXJbXSxcbiAgICBjb25maWd1cmF0aW9uOiBUb3BpY0NvbmZpZ3VyYXRpb24sXG4gICAgcmVxdWVzdFRpbWVvdXRNczogbnVtYmVyXG4gICk6IEludGVyY2VwdG9yW10ge1xuICAgIHJldHVybiBbXG4gICAgICBtaWRkbGV3YXJlc0ludGVyY2VwdG9yKGNvbmZpZ3VyYXRpb24uZ2V0TG9nZ2VyRmFjdG9yeSgpLCBbXSwge30pLFxuICAgICAgSGVhZGVySW50ZXJjZXB0b3IuY3JlYXRlSGVhZGVyc0ludGVyY2VwdG9yKGhlYWRlcnMpLFxuICAgICAgUmV0cnlJbnRlcmNlcHRvci5jcmVhdGVSZXRyeUludGVyY2VwdG9yKHtcbiAgICAgICAgY2xpZW50TmFtZTogJ1B1YlN1YkNsaWVudCcsXG4gICAgICAgIGxvZ2dlckZhY3Rvcnk6IGNvbmZpZ3VyYXRpb24uZ2V0TG9nZ2VyRmFjdG9yeSgpLFxuICAgICAgICBvdmVyYWxsUmVxdWVzdFRpbWVvdXRNczogcmVxdWVzdFRpbWVvdXRNcyxcbiAgICAgIH0pLFxuICAgIF07XG4gIH1cblxuICAvLyBUT0RPIGh0dHBzOi8vZ2l0aHViLmNvbS9tb21lbnRvaHEvY2xpZW50LXNkay1ub2RlanMvaXNzdWVzLzM0OVxuICAvLyBkZWNpZGUgb24gc3RyZWFtaW5nIGludGVyY2VwdG9ycyBhbmQgbWlkZGxld2FyZXNcbiAgcHJpdmF0ZSBzdGF0aWMgaW5pdGlhbGl6ZVN0cmVhbWluZ0ludGVyY2VwdG9ycyhcbiAgICBoZWFkZXJzOiBIZWFkZXJbXVxuICApOiBJbnRlcmNlcHRvcltdIHtcbiAgICByZXR1cm4gW0hlYWRlckludGVyY2VwdG9yLmNyZWF0ZUhlYWRlcnNJbnRlcmNlcHRvcihoZWFkZXJzKV07XG4gIH1cbn1cbiJdfQ==