All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.dist.src.internal.pubsub-client.js Maven / Gradle / Ivy

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");
const utils_2 = require("@gomomento/sdk-core/dist/src/utils");
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,
            sequence_page: options.subscriptionState.lastTopicSequencePage,
        });
        this.getLogger().trace('Subscribing to topic with resume_at_topic_sequence_number %s and sequence_page %s', options.subscriptionState.resumeAtTopicSequenceNumber, options.subscriptionState.resumeAtTopicSequencePage);
        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;
                const sequencePage = resp.item.sequence_page;
                options.subscriptionState.lastTopicSequenceNumber = sequenceNumber;
                options.subscriptionState.lastTopicSequencePage = sequencePage;
                this.getLogger().trace('Received an item on subscription stream; topic: %s; sequence number: %s; sequence page: %s', (0, utils_1.truncateString)(options.topicName), sequenceNumber, sequencePage);
                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) {
                const topicDiscontinuity = new __1.TopicDiscontinuity(resp.discontinuity.last_topic_sequence, resp.discontinuity.new_topic_sequence, resp.discontinuity.new_sequence_page);
                this.getLogger().trace('Received discontinuity from subscription stream; topic: %s; %s', (0, utils_1.truncateString)(options.topicName), topicDiscontinuity.toString());
                options.subscriptionState.lastTopicSequenceNumber =
                    resp.discontinuity.new_topic_sequence;
                options.subscriptionState.lastTopicSequencePage =
                    resp.discontinuity.new_sequence_page;
                options.onDiscontinuity(topicDiscontinuity);
            }
            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 = (0, utils_2.secondsToMilliseconds)(5);
PubsubClient.DEFAULT_MAX_SESSION_MEMORY_MB = 256;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVic3ViLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9pbnRlcm5hbC9wdWJzdWItY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLGdFQUFrRDtBQUNsRCxJQUFPLFVBQVUsR0FBRyx3QkFBTSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUM7QUFDL0MsMEdBQTBHO0FBQzFHLG9FQUFxRTtBQUNyRSxxRkFBNkU7QUFDN0UsMkNBQTRFO0FBQzVFLHFEQUEyQztBQUMzQyw0RUFBc0U7QUFDdEUsMkJBVWE7QUFDYix1RUFBMkU7QUFDM0Usb0hBSW1GO0FBR25GLHNFQUE2RTtBQUM3RSxnRUFBMEQ7QUFDMUQsOERBQXlFO0FBRXpFLE1BQWEsWUFBYSxTQUFRLDJDQUFrQztJQVVsRSx3REFBd0Q7SUFDeEQsdUNBQXVDO0lBRXZDOztPQUVHO0lBQ0gsWUFBWSxLQUEwQjtRQUNwQyxLQUFLLENBQ0gsS0FBSyxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsRUFBRSxFQUN0QyxLQUFLLENBQUMsYUFBYSxDQUFDLGdCQUFnQixFQUFFLENBQUMsU0FBUyxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsRUFDbkUsSUFBSSxvREFBdUIsQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDLGdCQUFnQixFQUFFLENBQUMsQ0FDcEUsQ0FBQztRQUNGLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxLQUFLLENBQUMsa0JBQWtCLENBQUM7UUFDbkQsSUFBSSxDQUFDLHFCQUFxQixHQUFHLFlBQVksQ0FBQywwQkFBMEIsQ0FBQztRQUNyRSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUMsS0FBSyxDQUNwQiwwQ0FBMEMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLGdCQUFnQixFQUFFLEdBQUcsQ0FDeEYsQ0FBQztRQUVGLE1BQU0sZUFBZSxHQUEyQixLQUFLLENBQUMsYUFBYTthQUNoRSxvQkFBb0IsRUFBRTthQUN0QixhQUFhLEVBQUUsQ0FBQztR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QkFBK0IsQ0FDNUMsT0FBaUI7UUFFakIsT0FBTyxDQUFDLHVDQUFpQixDQUFDLHdCQUF3QixDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDL0QsQ0FBQzs7QUFsVEgsb0NBbVRDO0FBL1N5Qix1Q0FBMEIsR0FDaEQsSUFBQSw2QkFBcUIsRUFBQyxDQUFDLENBQUMsQ0FBQztBQUNILDBDQUE2QixHQUFXLEdBQUcsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7cHVic3VifSBmcm9tICdAZ29tb21lbnRvL2dlbmVyYXRlZC10eXBlcyc7XG5pbXBvcnQgZ3JwY1B1YnN1YiA9IHB1YnN1Yi5jYWNoZV9jbGllbnQucHVic3ViO1xuLy8gb2xkZXIgdmVyc2lvbnMgb2Ygbm9kZSBkb24ndCBoYXZlIHRoZSBnbG9iYWwgdXRpbCB2YXJpYWJsZXMgaHR0cHM6Ly9naXRodWIuY29tL25vZGVqcy9ub2RlL2lzc3Vlcy8yMDM2NVxuaW1wb3J0IHtIZWFkZXIsIEhlYWRlckludGVyY2VwdG9yfSBmcm9tICcuL2dycGMvaGVhZGVycy1pbnRlcmNlcHRvcic7XG5pbXBvcnQge0NhY2hlU2VydmljZUVycm9yTWFwcGVyfSBmcm9tICcuLi9lcnJvcnMvY2FjaGUtc2VydmljZS1lcnJvci1tYXBwZXInO1xuaW1wb3J0IHtDaGFubmVsQ3JlZGVudGlhbHMsIEludGVyY2VwdG9yLCBTZXJ2aWNlRXJyb3J9IGZyb20gJ0BncnBjL2dycGMtanMnO1xuaW1wb3J0IHt2ZXJzaW9ufSBmcm9tICcuLi8uLi9wYWNrYWdlLmpzb24nO1xuaW1wb3J0IHttaWRkbGV3YXJlc0ludGVyY2VwdG9yfSBmcm9tICcuL2dycGMvbWlkZGxld2FyZXMtaW50ZXJjZXB0b3InO1xuaW1wb3J0IHtcbiAgQ3JlZGVudGlhbFByb3ZpZGVyLFxuICBTdGF0aWNHcnBjQ29uZmlndXJhdGlvbixcbiAgVG9waWNEaXNjb250aW51aXR5LFxuICBUb3BpY0hlYXJ0YmVhdCxcbiAgVG9waWNHcnBjQ29uZmlndXJhdGlvbixcbiAgVG9waWNJdGVtLFxuICBUb3BpY1B1Ymxpc2gsXG4gIFRvcGljU3Vic2NyaWJlLFxuICBVbmtub3duRXJyb3IsXG59IGZyb20gJy4uLyc7XG5pbXBvcnQge3RydW5jYXRlU3RyaW5nfSBmcm9tICdAZ29tb21lbnRvL3Nkay1jb3JlL2Rpc3Qvc3JjL2ludGVybmFsL3V0aWxzJztcbmltcG9ydCB7XG4gIEFic3RyYWN0UHVic3ViQ2xpZW50LFxuICBTZW5kU3Vic2NyaWJlT3B0aW9ucyxcbiAgUHJlcGFyZVN1YnNjcmliZUNhbGxiYWNrT3B0aW9ucyxcbn0gZnJvbSAnQGdvbW9tZW50by9zZGstY29yZS9kaXN0L3NyYy9pbnRlcm5hbC9jbGllbnRzL3B1YnN1Yi9BYnN0cmFjdFB1YnN1YkNsaWVudCc7XG5pbXBvcnQge1RvcGljQ29uZmlndXJhdGlvbn0gZnJvbSAnLi4vY29uZmlnL3RvcGljLWNvbmZpZ3VyYXRpb24nO1xuaW1wb3J0IHtUb3BpY0NsaWVudEFsbFByb3BzfSBmcm9tICcuL3RvcGljLWNsaWVudC1hbGwtcHJvcHMnO1xuaW1wb3J0IHtncnBjQ2hhbm5lbE9wdGlvbnNGcm9tR3JwY0NvbmZpZ30gZnJvbSAnLi9ncnBjL2dycGMtY2hhbm5lbC1vcHRpb25zJztcbmltcG9ydCB7UmV0cnlJbnRlcmNlcHRvcn0gZnJvbSAnLi9ncnBjL3JldHJ5LWludGVyY2VwdG9yJztcbmltcG9ydCB7c2Vjb25kc1RvTWlsbGlzZWNvbmRzfSBmcm9tICdAZ29tb21lbnRvL3Nkay1jb3JlL2Rpc3Qvc3JjL3V0aWxzJztcblxuZXhwb3J0IGNsYXNzIFB1YnN1YkNsaWVudCBleHRlbmRzIEFic3RyYWN0UHVic3ViQ2xpZW50PFNlcnZpY2VFcnJvcj4ge1xuICBwcml2YXRlIHJlYWRvbmx5IGNsaWVudDogZ3JwY1B1YnN1Yi5QdWJzdWJDbGllbnQ7XG4gIHByb3RlY3RlZCByZWFkb25seSBjcmVkZW50aWFsUHJvdmlkZXI6IENyZWRlbnRpYWxQcm92aWRlcjtcbiAgcHJpdmF0ZSByZWFkb25seSB1bmFyeVJlcXVlc3RUaW1lb3V0TXM6IG51bWJlcjtcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgREVGQVVMVF9SRVFVRVNUX1RJTUVPVVRfTVM6IG51bWJlciA9XG4gICAgc2Vjb25kc1RvTWlsbGlzZWNvbmRzKDUpO1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBERUZBVUxUX01BWF9TRVNTSU9OX01FTU9SWV9NQjogbnVtYmVyID0gMjU2O1xuICBwcml2YXRlIHJlYWRvbmx5IHVuYXJ5SW50ZXJjZXB0b3JzOiBJbnRlcmNlcHRvcltdO1xuICBwcml2YXRlIHJlYWRvbmx5IHN0cmVhbWluZ0ludGVyY2VwdG9yczogSW50ZXJjZXB0b3JbXTtcblxuICAvLyBwcml2YXRlIHN0YXRpYyByZWFkb25seSBSU1RfU1RSRUFNX05PX0VSUk9SX01FU1NBR0UgPVxuICAvLyAgICdSZWNlaXZlZCBSU1RfU1RSRUFNIHdpdGggY29kZSAwJztcblxuICAvKipcbiAgICogQHBhcmFtIHtUb3BpY0NsaWVudFByb3BzfSBwcm9wc1xuICAgKi9cbiAgY29uc3RydWN0b3IocHJvcHM6IFRvcGljQ2xpZW50QWxsUHJvcHMpIHtcbiAgICBzdXBlcihcbiAgICAgIHByb3BzLmNvbmZpZ3VyYXRpb24uZ2V0TG9nZ2VyRmFjdG9yeSgpLFxuICAgICAgcHJvcHMuY29uZmlndXJhdGlvbi5nZXRMb2dnZXJGYWN0b3J5KCkuZ2V0TG9nZ2VyKFB1YnN1YkNsaWVudC5uYW1lKSxcbiAgICAgIG5ldyBDYWNoZVNlcnZpY2VFcnJvck1hcHBlcihwcm9wcy5jb25maWd1cmF0aW9uLmdldFRocm93T25FcnJvcnMoKSlcbiAgICApO1xuICAgIHRoaXMuY3JlZGVudGlhbFByb3ZpZGVyID0gcHJvcHMuY3JlZGVudGlhbFByb3ZpZGVyO1xuICAgIHRoaXMudW5hcnlSZXF1ZXN0VGltZW91dE1zID0gUHVic3ViQ2xpZW50LkRFRkFVTFRfUkVRVUVTVF9USU1FT1VUX01TO1xuICAgIHRoaXMuZ2V0TG9nZ2VyKCkuZGVidWcoXG4gICAgICBgQ3JlYXRpbmcgdG9waWMgY2xpZW50IHVzaW5nIGVuZHBvaW50OiAnJHt0aGlzLmNyZWRlbnRpYWxQcm92aWRlci5nZXRDYWNoZUVuZHBvaW50KCl9J2BcbiAgICApO1xuXG4gICAgY29uc3QgdG9waWNHcnBjQ29uZmlnOiBUb3BpY0dycGNDb25maWd1cmF0aW9uID0gcHJvcHMuY29uZmlndXJhdGlvblxuICAgICAgLmdldFRyYW5zcG9ydFN0cmF0ZWd5KClcbiAgICAgIC5nZXRHcnBjQ29uZmlnKCk7XG5cbiAgICAvLyBOT1RFOiBUaGlzIGlzIGhhcmQtY29kZWQgZm9yIG5vdyBidXQgd2UgbWF5IHdhbnQgdG8gZXhwb3NlIGl0IHZpYSBUb3BpY0NvbmZpZ3VyYXRpb24gaW4gdGhlXG4gICAgLy8gZnV0dXJlLCBhcyB3ZSBkbyB3aXRoIHNvbWUgb2YgdGhlIG90aGVyIGNsaWVudHMuXG4gICAgY29uc3QgZ3JwY0NvbmZpZyA9IG5ldyBTdGF0aWNHcnBjQ29uZmlndXJhdGlvbih7XG4gICAgICBkZWFkbGluZU1pbGxpczogdGhpcy51bmFyeVJlcXVlc3RUaW1lb3V0TXMsXG4gICAgICBtYXhTZXNzaW9uTWVtb3J5TWI6IFB1YnN1YkNsaWVudC5ERUZBVUxUX01BWF9TRVNTSU9OX01FTU9SWV9NQixcbiAgICAgIGtlZXBBbGl2ZVBlcm1pdFdpdGhvdXRDYWxsczpcbiAgICAgICAgdG9waWNHcnBjQ29uZmlnLmdldEtlZXBBbGl2ZVBlcm1pdFdpdGhvdXRDYWxscygpLFxuICAgICAga2VlcEFsaXZlVGltZU1zOiB0b3BpY0dycGNDb25maWcuZ2V0S2VlcEFsaXZlVGltZU1TKCksXG4gICAgICBrZWVwQWxpdmVUaW1lb3V0TXM6IHRvcGljR3JwY0NvbmZpZy5nZXRLZWVwQWxpdmVUaW1lb3V0TVMoKSxcbiAgICB9KTtcblxuICAgIGNvbnN0IGNoYW5uZWxPcHRpb25zID0gZ3JwY0NoYW5uZWxPcHRpb25zRnJvbUdycGNDb25maWcoZ3JwY0NvbmZpZyk7XG5cbiAgICB0aGlzLmdldExvZ2dlcigpLmRlYnVnKFxuICAgICAgYENyZWF0aW5nIHB1YnN1YiBjbGllbnQgd2l0aCBjaGFubmVsIG9wdGlvbnM6ICR7SlNPTi5zdHJpbmdpZnkoXG4gICAgICAgIGNoYW5uZWxPcHRpb25zLFxuICAgICAgICBudWxsLFxuICAgICAgICAyXG4gICAgICApfWBcbiAgICApO1xuXG4gICAgdGhpcy5jbGllbnQgPSBuZXcgZ3JwY1B1YnN1Yi5QdWJzdWJDbGllbnQoXG4gICAgICB0aGlzLmNyZWRlbnRpYWxQcm92aWRlci5nZXRDYWNoZUVuZHBvaW50KCksXG4gICAgICB0aGlzLmNyZWRlbnRpYWxQcm92aWRlci5pc0NhY2hlRW5kcG9pbnRTZWN1cmUoKVxuICAgICAgICA/IENoYW5uZWxDcmVkZW50aWFscy5jcmVhdGVTc2woKVxuICAgICAgICA6IENoYW5uZWxDcmVkZW50aWFscy5jcmVhdGVJbnNlY3VyZSgpLFxuICAgICAgY2hhbm5lbE9wdGlvbnNcbiAgICApO1xuXG4gICAgY29uc3QgaGVhZGVyczogSGVhZGVyW10gPSBbXG4gICAgICBuZXcgSGVhZGVyKCdBdXRob3JpemF0aW9uJywgdGhpcy5jcmVkZW50aWFsUHJvdmlkZXIuZ2V0QXV0aFRva2VuKCkpLFxuICAgICAgbmV3IEhlYWRlcignYWdlbnQnLCBgbm9kZWpzOnRvcGljOiR7dmVyc2lvbn1gKSxcbiAgICAgIG5ldyBIZWFkZXIoJ3J1bnRpbWUtdmVyc2lvbicsIGBub2RlanM6JHtwcm9jZXNzLnZlcnNpb25zLm5vZGV9YCksXG4gICAgXTtcbiAgICB0aGlzLnVuYXJ5SW50ZXJjZXB0b3JzID0gUHVic3ViQ2xpZW50LmluaXRpYWxpemVVbmFyeUludGVyY2VwdG9ycyhcbiAgICAgIGhlYWRlcnMsXG4gICAgICBwcm9wcy5jb25maWd1cmF0aW9uLFxuICAgICAgdGhpcy51bmFyeVJlcXVlc3RUaW1lb3V0TXNcbiAgICApO1xuICAgIHRoaXMuc3RyZWFtaW5nSW50ZXJjZXB0b3JzID1cbiAgICAgIFB1YnN1YkNsaWVudC5pbml0aWFsaXplU3RyZWFtaW5nSW50ZXJjZXB0b3JzKGhlYWRlcnMpO1xuICB9XG5cbiAgcHVibGljIGdldEVuZHBvaW50KCk6IHN0cmluZyB7XG4gICAgY29uc3QgZW5kcG9pbnQgPSB0aGlzLmNyZWRlbnRpYWxQcm92aWRlci5nZXRDYWNoZUVuZHBvaW50KCk7XG4gICAgdGhpcy5nZXRMb2dnZXIoKS5kZWJ1ZyhgVXNpbmcgY2FjaGUgZW5kcG9pbnQ6ICR7ZW5kcG9pbnR9YCk7XG4gICAgcmV0dXJuIGVuZHBvaW50O1xuICB9XG5cbiAgcHJvdGVjdGVkIGFzeW5jIHNlbmRQdWJsaXNoKFxuICAgIGNhY2hlTmFtZTogc3RyaW5nLFxuICAgIHRvcGljTmFtZTogc3RyaW5nLFxuICAgIHZhbHVlOiBzdHJpbmcgfCBVaW50OEFycmF5XG4gICk6IFByb21pc2U8VG9waWNQdWJsaXNoLlJlc3BvbnNlPiB7XG4gICAgY29uc3QgdG9waWNWYWx1ZSA9IG5ldyBncnBjUHVic3ViLl9Ub3BpY1ZhbHVlKCk7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHRvcGljVmFsdWUudGV4dCA9IHZhbHVlO1xuICAgIH0gZWxzZSB7XG4gICAgICB0b3BpY1ZhbHVlLmJpbmFyeSA9IHZhbHVlO1xuICAgIH1cblxuICAgIGNvbnN0IHJlcXVlc3QgPSBuZXcgZ3JwY1B1YnN1Yi5fUHVibGlzaFJlcXVlc3Qoe1xuICAgICAgY2FjaGVfbmFtZTogY2FjaGVOYW1lLFxuICAgICAgdG9waWM6IHRvcGljTmFtZSxcbiAgICAgIHZhbHVlOiB0b3BpY1ZhbHVlLFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHRoaXMuY2xpZW50LlB1Ymxpc2goXG4gICAgICAgIHJlcXVlc3QsXG4gICAgICAgIHtcbiAgICAgICAgICBpbnRlcmNlcHRvcnM6IHRoaXMudW5hcnlJbnRlcmNlcHRvcnMsXG4gICAgICAgIH0sXG4gICAgICAgIChlcnIsIHJlc3ApID0+IHtcbiAgICAgICAgICBpZiAocmVzcCkge1xuICAgICAgICAgICAgcmVzb2x2ZShuZXcgVG9waWNQdWJsaXNoLlN1Y2Nlc3MoKSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuZ2V0Q2FjaGVTZXJ2aWNlRXJyb3JNYXBwZXIoKS5yZXNvbHZlT3JSZWplY3RFcnJvcih7XG4gICAgICAgICAgICAgIGVycjogZXJyLFxuICAgICAgICAgICAgICBlcnJvclJlc3BvbnNlRmFjdG9yeUZuOiBlID0+IG5ldyBUb3BpY1B1Ymxpc2guRXJyb3IoZSksXG4gICAgICAgICAgICAgIHJlc29sdmVGbjogcmVzb2x2ZSxcbiAgICAgICAgICAgICAgcmVqZWN0Rm46IHJlamVjdCxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcmVtYXJrIFRoaXMgbWV0aG9kIGlzIHJlc3BvbnNpYmxlIGZvciByZXN0YXJ0aW5nIHRoZSBzdHJlYW0gaWYgaXQgZW5kcyB1bmV4cGVjdGVkbHkuXG4gICAqIFNpbmNlIHdlIHJldHVybiBhIHNpbmdsZSBzdWJzY3JpcHRpb24gb2JqZWN0IHRvIHRoZSB1c2VyLCB3ZSBuZWVkIHRvIHVwZGF0ZSBpdCB3aXRoIHRoZVxuICAgKiB1bnN1YnNjcmliZSBmdW5jdGlvbiBzaG91bGQgd2UgcmVzdGFydCB0aGUgc3RyZWFtLiBUaGlzIGlzIHdoeSB3ZSBwYXNzIHRoZSBzdWJzY3JpcHRpb25cbiAgICogc3RhdGUgYW5kIHN1YnNjcmlwdGlvbiBvYmplY3QgdG8gdGhpcyBtZXRob2QuXG4gICAqXG4gICAqIEhhbmRsaW5nIGEgY2FjaGUgbm90IGV4aXN0cyByZXF1aXJlcyBzcGVjaWFsIGNhcmUgYXMgd2VsbC4gSW4gdGhlIG1vc3QgbGlrZWx5IGNhc2UsXG4gICAqIHdoZW4gdGhlIHN1YnNjcmlwdGlvbiBzdGFydHMgYW5kIHRoZSBjYWNoZSBkb2VzIG5vdCBleGlzdCwgd2UgcmVjZWl2ZSBhbiBlcnJvciBpbW1lZGlhdGVseS5cbiAgICogV2UgcmV0dXJuIGFuIGVycm9yIGZyb20gdGhlIHN1YnNjcmliZSBtZXRob2QgYW5kIGRvIGltbWVkaWF0ZWx5IHVuc3Vic2NyaWJlLiBJbiBhIGRpc3RpbmN0LFxuICAgKiB1bmxpa2VseSBidXQgcG9zc2libGUgY2FzZSwgdGhlIHVzZXIgZGVsZXRlcyB0aGUgY2FjaGUgd2hpbGUgdGhlIHN0cmVhbSBpcyBydW5uaW5nLiBJbiB0aGlzXG4gICAqIGNhc2Ugd2UgYWxyZWFkeSByZXR1cm5lZCBhIHN1YnNjcmlwdGlvbiBvYmplY3QgdG8gdGhlIHVzZXIsIHNvIHdlIGluc3RlYWQgY2FuY2VsIHRoZSBzdHJlYW0gYW5kXG4gICAqIHByb3BhZ2F0ZSBhbiBlcnJvciB0byB0aGUgdXNlciB2aWEgdGhlIGVycm9yIGhhbmRsZXIuXG4gICAqL1xuICBwcm90ZWN0ZWQgb3ZlcnJpZGUgc2VuZFN1YnNjcmliZShcbiAgICBvcHRpb25zOiBTZW5kU3Vic2NyaWJlT3B0aW9uc1xuICApOiBQcm9taXNlPFRvcGljU3Vic2NyaWJlLlJlc3BvbnNlPiB7XG4gICAgY29uc3QgcmVxdWVzdCA9IG5ldyBncnBjUHVic3ViLl9TdWJzY3JpcHRpb25SZXF1ZXN0KHtcbiAgICAgIGNhY2hlX25hbWU6IG9wdGlvbnMuY2FjaGVOYW1lLFxuICAgICAgdG9waWM6IG9wdGlvbnMudG9waWNOYW1lLFxuICAgICAgcmVzdW1lX2F0X3RvcGljX3NlcXVlbmNlX251bWJlcjpcbiAgICAgICAgb3B0aW9ucy5zdWJzY3JpcHRpb25TdGF0ZS5yZXN1bWVBdFRvcGljU2VxdWVuY2VOdW1iZXIsXG4gICAgICBzZXF1ZW5jZV9wYWdlOiBvcHRpb25zLnN1YnNjcmlwdGlvblN0YXRlLmxhc3RUb3BpY1NlcXVlbmNlUGFnZSxcbiAgICB9KTtcblxuICAgIHRoaXMuZ2V0TG9nZ2VyKCkudHJhY2UoXG4gICAgICAnU3Vic2NyaWJpbmcgdG8gdG9waWMgd2l0aCByZXN1bWVfYXRfdG9waWNfc2VxdWVuY2VfbnVtYmVyICVzIGFuZCBzZXF1ZW5jZV9wYWdlICVzJyxcbiAgICAgIG9wdGlvbnMuc3Vic2NyaXB0aW9uU3RhdGUucmVzdW1lQXRUb3BpY1NlcXVlbmNlTnVtYmVyLFxuICAgICAgb3B0aW9ucy5zdWJzY3JpcHRpb25TdGF0ZS5yZXN1bWVBdFRvcGljU2VxdWVuY2VQYWdlXG4gICAgKTtcbiAgICBjb25zdCBjYWxsID0gdGhpcy5jbGllbnQuU3Vic2NyaWJlKHJlcXVlc3QsIHtcbiAgICAgIGludGVyY2VwdG9yczogdGhpcy5zdHJlYW1pbmdJbnRlcmNlcHRvcnMsXG4gICAgfSk7XG4gICAgb3B0aW9ucy5zdWJzY3JpcHRpb25TdGF0ZS5zZXRTdWJzY3JpYmVkKCk7XG5cbiAgICAvLyBBbGxvdyB0aGUgY2FsbGVyIHRvIGNhbmNlbCB0aGUgc3RyZWFtLlxuICAgIC8vIE5vdGUgdGhhdCBiZWNhdXNlIHdlIHJlc3RhcnQgdGhlIHN0cmVhbSBvbiBlcnJvciBvciBzdHJlYW0gZW5kLFxuICAgIC8vIHdlIG5lZWQgdG8gZW5zdXJlIHdlIGtlZXAgdGhlIHNhbWUgc3Vic2NyaXB0aW9uIG9iamVjdC4gVGhhdCB3YXlcbiAgICAvLyBzdHJlYW0gcmVzdGFydHMgYXJlIHRyYW5zcGFyZW50IHRvIHRoZSBjYWxsZXIuXG4gICAgb3B0aW9ucy5zdWJzY3JpcHRpb25TdGF0ZS51bnN1YnNjcmliZUZuID0gKCkgPT4ge1xuICAgICAgY2FsbC5jYW5jZWwoKTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCBfcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBwcmVwYXJlQ2FsbGJhY2tPcHRpb25zOiBQcmVwYXJlU3Vic2NyaWJlQ2FsbGJhY2tPcHRpb25zID0ge1xuICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgICByZXNvbHZlLFxuICAgICAgfTtcbiAgICAgIGNhbGwub24oJ2RhdGEnLCB0aGlzLnByZXBhcmVEYXRhQ2FsbGJhY2socHJlcGFyZUNhbGxiYWNrT3B0aW9ucykpO1xuICAgICAgY2FsbC5vbignZXJyb3InLCB0aGlzLnByZXBhcmVFcnJvckNhbGxiYWNrKHByZXBhcmVDYWxsYmFja09wdGlvbnMpKTtcbiAgICAgIGNhbGwub24oJ2VuZCcsIHRoaXMucHJlcGFyZUVuZENhbGxiYWNrKHByZXBhcmVDYWxsYmFja09wdGlvbnMpKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgcHJlcGFyZURhdGFDYWxsYmFjayhcbiAgICBvcHRpb25zOiBQcmVwYXJlU3Vic2NyaWJlQ2FsbGJhY2tPcHRpb25zXG4gICk6IChyZXNwOiBncnBjUHVic3ViLl9TdWJzY3JpcHRpb25JdGVtKSA9PiB2b2lkIHtcbiAgICByZXR1cm4gKHJlc3A6IGdycGNQdWJzdWIuX1N1YnNjcmlwdGlvbkl0ZW0pID0+IHtcbiAgICAgIGlmIChvcHRpb25zLmZpcnN0TWVzc2FnZSkge1xuICAgICAgICBvcHRpb25zLnJlc29sdmUob3B0aW9ucy5zdWJzY3JpcHRpb24pO1xuICAgICAgfVxuICAgICAgb3B0aW9ucy5maXJzdE1lc3NhZ2UgPSBmYWxzZTtcblxuICAgICAgaWYgKHJlc3AuaXRlbSkge1xuICAgICAgICBjb25zdCBzZXF1ZW5jZU51bWJlciA9IHJlc3AuaXRlbS50b3BpY19zZXF1ZW5jZV9udW1iZXI7XG4gICAgICAgIGNvbnN0IHNlcXVlbmNlUGFnZSA9IHJlc3AuaXRlbS5zZXF1ZW5jZV9wYWdlO1xuICAgICAgICBvcHRpb25zLnN1YnNjcmlwdGlvblN0YXRlLmxhc3RUb3BpY1NlcXVlbmNlTnVtYmVyID0gc2VxdWVuY2VOdW1iZXI7XG4gICAgICAgIG9wdGlvbnMuc3Vic2NyaXB0aW9uU3RhdGUubGFzdFRvcGljU2VxdWVuY2VQYWdlID0gc2VxdWVuY2VQYWdlO1xuICAgICAgICB0aGlzLmdldExvZ2dlcigpLnRyYWNlKFxuICAgICAgICAgICdSZWNlaXZlZCBhbiBpdGVtIG9uIHN1YnNjcmlwdGlvbiBzdHJlYW07IHRvcGljOiAlczsgc2VxdWVuY2UgbnVtYmVyOiAlczsgc2VxdWVuY2UgcGFnZTogJXMnLFxuICAgICAgICAgIHRydW5jYXRlU3RyaW5nKG9wdGlvbnMudG9waWNOYW1lKSxcbiAgICAgICAgICBzZXF1ZW5jZU51bWJlcixcbiAgICAgICAgICBzZXF1ZW5jZVBhZ2VcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKHJlc3AuaXRlbS52YWx1ZS50ZXh0KSB7XG4gICAgICAgICAgb3B0aW9ucy5vbkl0ZW0oXG4gICAgICAgICAgICBuZXcgVG9waWNJdGVtKHJlc3AuaXRlbS52YWx1ZS50ZXh0LCBzZXF1ZW5jZU51bWJlciwge1xuICAgICAgICAgICAgICB0b2tlbklkOiByZXNwLml0ZW0ucHVibGlzaGVyX2lkLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICApO1xuICAgICAgICB9IGVsc2UgaWYgKHJlc3AuaXRlbS52YWx1ZS5iaW5hcnkpIHtcbiAgICAgICAgICBvcHRpb25zLm9uSXRlbShcbiAgICAgICAgICAgIG5ldyBUb3BpY0l0ZW0ocmVzcC5pdGVtLnZhbHVlLmJpbmFyeSwgc2VxdWVuY2VOdW1iZXIsIHtcbiAgICAgICAgICAgICAgdG9rZW5JZDogcmVzcC5pdGVtLnB1Ymxpc2hlcl9pZCxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLmdldExvZ2dlcigpLmVycm9yKFxuICAgICAgICAgICAgJ1JlY2VpdmVkIHN1YnNjcmlwdGlvbiBpdGVtIHdpdGggdW5rbm93biB0eXBlOyB0b3BpYzogJXMnLFxuICAgICAgICAgICAgdHJ1bmNhdGVTdHJpbmcob3B0aW9ucy50b3BpY05hbWUpXG4gICAgICAgICAgKTtcbiAgICAgICAgICBvcHRpb25zLm9uRXJyb3IoXG4gICAgICAgICAgICBuZXcgVG9waWNTdWJzY3JpYmUuRXJyb3IoXG4gICAgICAgICAgICAgIG5ldyBVbmtub3duRXJyb3IoJ1Vua25vd24gaXRlbSB2YWx1ZSB0eXBlJylcbiAgICAgICAgICAgICksXG4gICAgICAgICAgICBvcHRpb25zLnN1YnNjcmlwdGlvblxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAocmVzcC5oZWFydGJlYXQpIHtcbiAgICAgICAgdGhpcy5nZXRMb2dnZXIoKS50cmFjZShcbiAgICAgICAgICAnUmVjZWl2ZWQgaGVhcnRiZWF0IGZyb20gc3Vic2NyaXB0aW9uIHN0cmVhbTsgdG9waWM6ICVzJyxcbiAgICAgICAgICB0cnVuY2F0ZVN0cmluZyhvcHRpb25zLnRvcGljTmFtZSlcbiAgICAgICAgKTtcbiAgICAgICAgb3B0aW9ucy5vbkhlYXJ0YmVhdChuZXcgVG9waWNIZWFydGJlYXQoKSk7XG4gICAgICB9IGVsc2UgaWYgKHJlc3AuZGlzY29udGludWl0eSkge1xuICAgICAgICBjb25zdCB0b3BpY0Rpc2NvbnRpbnVpdHkgPSBuZXcgVG9waWNEaXNjb250aW51aXR5KFxuICAgICAgICAgIHJlc3AuZGlzY29udGludWl0eS5sYXN0X3RvcGljX3NlcXVlbmNlLFxuICAgICAgICAgIHJlc3AuZGlzY29udGludWl0eS5uZXdfdG9waWNfc2VxdWVuY2UsXG4gICAgICAgICAgcmVzcC5kaXNjb250aW51aXR5Lm5ld19zZXF1ZW5jZV9wYWdlXG4gICAgICAgICk7XG4gICAgICAgIHRoaXMuZ2V0TG9nZ2VyKCkudHJhY2UoXG4gICAgICAgICAgJ1JlY2VpdmVkIGRpc2NvbnRpbnVpdHkgZnJvbSBzdWJzY3JpcHRpb24gc3RyZWFtOyB0b3BpYzogJXM7ICVzJyxcbiAgICAgICAgICB0cnVuY2F0ZVN0cmluZyhvcHRpb25zLnRvcGljTmFtZSksXG4gICAgICAgICAgdG9waWNEaXNjb250aW51aXR5LnRvU3RyaW5nKClcbiAgICAgICAgKTtcbiAgICAgICAgb3B0aW9ucy5zdWJzY3JpcHRpb25TdGF0ZS5sYXN0VG9waWNTZXF1ZW5jZU51bWJlciA9XG4gICAgICAgICAgcmVzcC5kaXNjb250aW51aXR5Lm5ld190b3BpY19zZXF1ZW5jZTtcbiAgICAgICAgb3B0aW9ucy5zdWJzY3JpcHRpb25TdGF0ZS5sYXN0VG9waWNTZXF1ZW5jZVBhZ2UgPVxuICAgICAgICAgIHJlc3AuZGlzY29udGludWl0eS5uZXdfc2VxdWVuY2VfcGFnZTtcbiAgICAgICAgb3B0aW9ucy5vbkRpc2NvbnRpbnVpdHkodG9waWNEaXNjb250aW51aXR5KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuZ2V0TG9nZ2VyKCkuZXJyb3IoXG4gICAgICAgICAgJ1JlY2VpdmVkIHVua25vd24gc3Vic2NyaXB0aW9uIGl0ZW07IHRvcGljOiAlcycsXG4gICAgICAgICAgdHJ1bmNhdGVTdHJpbmcob3B0aW9ucy50b3BpY05hbWUpXG4gICAgICAgICk7XG4gICAgICAgIG9wdGlvbnMub25FcnJvcihcbiAgICAgICAgICBuZXcgVG9waWNTdWJzY3JpYmUuRXJyb3IobmV3IFVua25vd25FcnJvcignVW5rbm93biBpdGVtIHR5cGUnKSksXG4gICAgICAgICAgb3B0aW9ucy5zdWJzY3JpcHRpb25cbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBwcmVwYXJlRXJyb3JDYWxsYmFjayhcbiAgICBvcHRpb25zOiBQcmVwYXJlU3Vic2NyaWJlQ2FsbGJhY2tPcHRpb25zXG4gICk6IChlcnI6IEVycm9yKSA9PiB2b2lkIHtcbiAgICByZXR1cm4gKGVycjogRXJyb3IpID0+IHtcbiAgICAgIC8vIFdoZW4gdGhlIGNhbGxlciB1bnN1YnNjcmliZXMsIHdlIG1heSBnZXQgYSBmb2xsb3cgb24gZXJyb3IsIHdoaWNoIHdlIGlnbm9yZS5cbiAgICAgIGlmICghb3B0aW9ucy5zdWJzY3JpcHRpb25TdGF0ZS5pc1N1YnNjcmliZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBzZXJ2aWNlRXJyb3IgPSBlcnIgYXMgdW5rbm93biBhcyBTZXJ2aWNlRXJyb3I7XG4gICAgICB0aGlzLmdldExvZ2dlcigpLnRyYWNlKFxuICAgICAgICBgU3Vic2NyaXB0aW9uIGVuY291bnRlcmVkIGFuIGVycm9yOiAke3NlcnZpY2VFcnJvci5jb2RlfTogJHtzZXJ2aWNlRXJyb3IubWVzc2FnZX06ICR7c2VydmljZUVycm9yLmRldGFpbHN9YFxuICAgICAgKTtcbiAgICAgIGNvbnN0IHNob3VsZFJlY29ubmVjdFN1YnNjcmlwdGlvbiA9XG4gICAgICAgIC8vIHByZXZpb3VzbHksIHdlIHdlcmUgb25seSBhdHRlbXB0aW5nIGEgcmVjb25uZWN0IG9uIHRoaXMgb25lIHZlcnkgc3BlY2lmaWMgY2FzZSwgYnV0IG91ciBjdXJyZW50IGV4cGVjdGF0aW9uIGlzIHRoYXRcbiAgICAgICAgLy8gd2Ugc2hvdWxkIGVyciBvbiB0aGUgc2lkZSBvZiByZXRyeWluZy4gVGhpcyBtYXkgYmVjb21lIGEgc29ydCBvZiBcImRlbnkgbGlzdFwiIG9mIGVycm9yIHR5cGVzIHRvICpub3QqIHJldHJ5IG9uXG4gICAgICAgIC8vIGluIHRoZSBmdXR1cmUsIGJ1dCBmb3Igbm93IHdlIHdpbGwgYmUgYWdncmVzc2l2ZSBhYm91dCByZXRyeWluZy5cbiAgICAgICAgLy8gLy8gc2VydmljZUVycm9yLmNvZGUgPT09IFN0YXR1cy5JTlRFUk5BTCAmJlxuICAgICAgICAvLyAgLy8gc2VydmljZUVycm9yLmRldGFpbHMgPT09IFB1YnN1YkNsaWVudC5SU1RfU1RSRUFNX05PX0VSUk9SX01FU1NBR0U7XG4gICAgICAgIHRydWU7XG4gICAgICBjb25zdCBtb21lbnRvRXJyb3IgPSBuZXcgVG9waWNTdWJzY3JpYmUuRXJyb3IoXG4gICAgICAgIHRoaXMuZ2V0Q2FjaGVTZXJ2aWNlRXJyb3JNYXBwZXIoKS5jb252ZXJ0RXJyb3Ioc2VydmljZUVycm9yKVxuICAgICAgKTtcbiAgICAgIHRoaXMuaGFuZGxlU3Vic2NyaWJlRXJyb3IoXG4gICAgICAgIG9wdGlvbnMsXG4gICAgICAgIG1vbWVudG9FcnJvcixcbiAgICAgICAgc2hvdWxkUmVjb25uZWN0U3Vic2NyaXB0aW9uXG4gICAgICApO1xuICAgIH07XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBpbml0aWFsaXplVW5hcnlJbnRlcmNlcHRvcnMoXG4gICAgaGVhZGVyczogSGVhZGVyW10sXG4gICAgY29uZmlndXJhdGlvbjogVG9waWNDb25maWd1cmF0aW9uLFxuICAgIHJlcXVlc3RUaW1lb3V0TXM6IG51bWJlclxuICApOiBJbnRlcmNlcHRvcltdIHtcbiAgICByZXR1cm4gW1xuICAgICAgbWlkZGxld2FyZXNJbnRlcmNlcHRvcihjb25maWd1cmF0aW9uLmdldExvZ2dlckZhY3RvcnkoKSwgW10sIHt9KSxcbiAgICAgIEhlYWRlckludGVyY2VwdG9yLmNyZWF0ZUhlYWRlcnNJbnRlcmNlcHRvcihoZWFkZXJzKSxcbiAgICAgIFJldHJ5SW50ZXJjZXB0b3IuY3JlYXRlUmV0cnlJbnRlcmNlcHRvcih7XG4gICAgICAgIGNsaWVudE5hbWU6ICdQdWJTdWJDbGllbnQnLFxuICAgICAgICBsb2dnZXJGYWN0b3J5OiBjb25maWd1cmF0aW9uLmdldExvZ2dlckZhY3RvcnkoKSxcbiAgICAgICAgb3ZlcmFsbFJlcXVlc3RUaW1lb3V0TXM6IHJlcXVlc3RUaW1lb3V0TXMsXG4gICAgICB9KSxcbiAgICBdO1xuICB9XG5cbiAgLy8gVE9ETyBodHRwczovL2dpdGh1Yi5jb20vbW9tZW50b2hxL2NsaWVudC1zZGstbm9kZWpzL2lzc3Vlcy8zNDlcbiAgLy8gZGVjaWRlIG9uIHN0cmVhbWluZyBpbnRlcmNlcHRvcnMgYW5kIG1pZGRsZXdhcmVzXG4gIHByaXZhdGUgc3RhdGljIGluaXRpYWxpemVTdHJlYW1pbmdJbnRlcmNlcHRvcnMoXG4gICAgaGVhZGVyczogSGVhZGVyW11cbiAgKTogSW50ZXJjZXB0b3JbXSB7XG4gICAgcmV0dXJuIFtIZWFkZXJJbnRlcmNlcHRvci5jcmVhdGVIZWFkZXJzSW50ZXJjZXB0b3IoaGVhZGVycyldO1xuICB9XG59XG4iXX0=




© 2015 - 2025 Weber Informatics LLC | Privacy Policy