@neo-one/server
Version:
NEO•ONE Server that NEO•ONE CLI and other clients connect to.
160 lines (158 loc) • 26.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const http_1 = require("@neo-one/http");
const server_client_1 = require("@neo-one/server-client");
const server_grpc_1 = require("@neo-one/server-grpc");
const server_plugin_1 = require("@neo-one/server-plugin");
const utils_1 = require("@neo-one/utils");
const http = tslib_1.__importStar(require("http"));
const koa_1 = tslib_1.__importDefault(require("koa"));
const koa_router_1 = tslib_1.__importDefault(require("koa-router"));
const mali_1 = tslib_1.__importDefault(require("mali"));
const path = tslib_1.__importStar(require("path"));
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const errors_1 = require("./errors");
const middleware_1 = require("./middleware");
const PluginManager_1 = require("./PluginManager");
const PortAllocator_1 = require("./PortAllocator");
const PLUGIN_PATH = 'plugin';
class Server {
static init$({ monitor, binary, serverConfig, }) {
const dataPath$ = serverConfig.config$.pipe(operators_1.map((config) => config.paths.data), operators_1.distinctUntilChanged());
const portAllocator$ = rxjs_1.combineLatest([
dataPath$,
serverConfig.config$.pipe(operators_1.map((config) => config.ports), operators_1.distinctUntilChanged()),
]).pipe(operators_1.switchMap(([dataPath, ports]) => rxjs_1.defer(async () => PortAllocator_1.PortAllocator.create({
monitor,
dataPath,
portMin: ports.min,
portMax: ports.max,
}))));
const pluginManager$ = rxjs_1.combineLatest([
dataPath$,
portAllocator$,
serverConfig.config$.pipe(operators_1.map((config) => config.httpServer.port), operators_1.distinctUntilChanged()),
]).pipe(operators_1.switchMap(([dataPath, portAllocator, httpServerPort]) => rxjs_1.defer(async () => {
const pluginManager = new PluginManager_1.PluginManager({
monitor,
binary,
portAllocator,
dataPath: path.resolve(dataPath, PLUGIN_PATH),
httpServerPort,
});
await pluginManager.init();
return pluginManager;
})));
return rxjs_1.combineLatest([dataPath$, pluginManager$]).pipe(operators_1.switchMap(([dataPath, pluginManager]) => {
const server = new Server({
monitor,
serverConfig,
dataPath,
pluginManager,
});
return server.start$().pipe(operators_1.map(() => server), operators_1.distinctUntilChanged());
}));
}
constructor({ monitor, serverConfig, dataPath, pluginManager, }) {
this.monitor = monitor.at('server');
this.serverConfig = serverConfig;
this.dataPath = dataPath;
this.pluginManager = pluginManager;
this.mutableServerDebug = {};
}
start$() {
const grpc$ = this.serverConfig.config$.pipe(operators_1.map((config) => config.server), operators_1.distinctUntilChanged(), operators_1.mergeScan((prevApp, serverConfig) => rxjs_1.defer(async () => {
if (prevApp === undefined) {
const manager = new server_client_1.ServerManager({
dataPath: this.dataPath,
serverVersion: server_plugin_1.VERSION,
});
this.mutableServerDebug.pidPath = manager.pidPath;
const pid = await manager.checkAlive(serverConfig.port);
if (pid === undefined) {
this.mutableServerDebug.pid = process.pid;
await manager.writeVersionPID(process.pid, server_plugin_1.VERSION);
}
else if (pid !== process.pid) {
throw new errors_1.ServerRunningError(pid);
}
}
else {
await prevApp.close().catch((error) => {
this.monitor.logError({
name: 'grpc_app_close_error',
message: 'Failed to close previous app',
error,
});
});
}
const app = new mali_1.default(server_grpc_1.proto);
app.silent = false;
app.on('error', (error, ctx) => {
let monitor = this.monitor;
if (ctx != undefined && ctx.state != undefined && ctx.state.monitor != undefined) {
({ monitor } = ctx.state);
}
monitor.logError({
name: 'grpc_server_request_uncaught_error',
message: 'Uncaught grpc error.',
error,
});
});
app.use(middleware_1.context({ monitor: this.monitor }));
app.use(middleware_1.services({ server: this }));
this.mutableServerDebug.port = serverConfig.port;
app.start(`0.0.0.0:${serverConfig.port}`);
this.monitor.log({
name: 'server_listen',
message: `GRPC Server listening on 0.0.0.0:${serverConfig.port}.`,
level: 'info',
});
return app;
}), undefined, 1), utils_1.finalize(async (app) => {
if (app !== undefined) {
await app.close();
}
}));
const app$ = new rxjs_1.BehaviorSubject((() => {
const app = new koa_1.default();
app.silent = true;
app.on('error', http_1.onError({ monitor: this.monitor }));
const router = new koa_router_1.default();
const rpcMiddleware = middleware_1.rpc({ server: this });
router.use(http_1.context({ monitor: this.monitor }));
router.use(http_1.cors).post(rpcMiddleware.name, rpcMiddleware.path, rpcMiddleware.middleware);
app.use(router.routes());
app.use(http_1.cors);
app.use(router.allowedMethods());
return app;
})());
const http$ = this.serverConfig.config$.pipe(operators_1.switchMap((config) => http_1.createServer$(this.monitor, app$, rxjs_1.of(60000), { host: '0.0.0.0', port: config.httpServer.port }, () => http.createServer())));
return rxjs_1.combineLatest([grpc$, http$]);
}
async reset() {
await this.pluginManager.reset();
}
getDebug() {
return [
['Server Config Path', this.serverConfig.configPath],
['Data Path', this.dataPath],
['Server', { type: 'describe', table: this.getServerDebug() }],
['Plugin Manager', { type: 'describe', table: this.pluginManager.getDebug() }],
];
}
getServerDebug() {
return [
['Port', this.getValue(this.mutableServerDebug.port)],
['Process ID', this.getValue(this.mutableServerDebug.pid)],
['Process ID Path', this.getValue(this.mutableServerDebug.pidPath)],
];
}
getValue(value) {
return value === undefined ? 'Not Set' : `${value}`;
}
}
exports.Server = Server;
//# sourceMappingURL=data:application/json;charset=utf8;base64,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QUFVLENBQVU7WUFDN0QsQ0FBQyxXQUFXLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBVTtZQUNyQyxDQUFDLFFBQVEsRUFBRSxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxjQUFjLEVBQUUsRUFBRSxDQUFVO1lBQ3ZFLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQVU7U0FDeEYsQ0FBQztJQUNKLENBQUM7SUFFTyxjQUFjO1FBQ3BCLE9BQU87WUFDTCxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsQ0FBVTtZQUM5RCxDQUFDLFlBQVksRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLENBQUMsQ0FBVTtZQUNuRSxDQUFDLGlCQUFpQixFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxDQUFVO1NBQzdFLENBQUM7SUFDSixDQUFDO0lBRU8sUUFBUSxDQUFDLEtBQXVCO1FBQ3RDLE9BQU8sS0FBSyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxHQUFHLEtBQUssRUFBRSxDQUFDO0lBQ3RELENBQUM7Q0FDRjtBQTNPRCx3QkEyT0MiLCJmaWxlIjoibmVvLW9uZS1zZXJ2ZXIvc3JjL1NlcnZlci5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNvbnRleHQgYXMgaHR0cENvbnRleHQsIGNvcnMsIGNyZWF0ZVNlcnZlciQsIG9uRXJyb3IgYXMgYXBwT25FcnJvciB9IGZyb20gJ0BuZW8tb25lL2h0dHAnO1xuaW1wb3J0IHsgTW9uaXRvciB9IGZyb20gJ0BuZW8tb25lL21vbml0b3InO1xuaW1wb3J0IHsgU2VydmVyQ29uZmlnLCBTZXJ2ZXJNYW5hZ2VyIH0gZnJvbSAnQG5lby1vbmUvc2VydmVyLWNsaWVudCc7XG5pbXBvcnQgeyBwcm90byB9IGZyb20gJ0BuZW8tb25lL3NlcnZlci1ncnBjJztcbmltcG9ydCB7IEJpbmFyeSwgQ29uZmlnLCBEZXNjcmliZVRhYmxlLCBWRVJTSU9OIH0gZnJvbSAnQG5lby1vbmUvc2VydmVyLXBsdWdpbic7XG5pbXBvcnQgeyBmaW5hbGl6ZSB9IGZyb20gJ0BuZW8tb25lL3V0aWxzJztcbmltcG9ydCAqIGFzIGh0dHAgZnJvbSAnaHR0cCc7XG5pbXBvcnQgQXBwbGljYXRpb24gZnJvbSAna29hJztcbmltcG9ydCBSb3V0ZXIgZnJvbSAna29hLXJvdXRlcic7XG5pbXBvcnQgTWFsaSwgeyBDb250ZXh0IH0gZnJvbSAnbWFsaSc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBjb21iaW5lTGF0ZXN0LCBkZWZlciwgT2JzZXJ2YWJsZSwgb2YgYXMgX29mIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBkaXN0aW5jdFVudGlsQ2hhbmdlZCwgbWFwLCBtZXJnZVNjYW4sIHN3aXRjaE1hcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IFNlcnZlclJ1bm5pbmdFcnJvciB9IGZyb20gJy4vZXJyb3JzJztcbmltcG9ydCB7IGNvbnRleHQsIHJwYywgc2VydmljZXMgYXMgc2VydmljZXNNaWRkbGV3YXJlIH0gZnJvbSAnLi9taWRkbGV3YXJlJztcbmltcG9ydCB7IFBsdWdpbk1hbmFnZXIgfSBmcm9tICcuL1BsdWdpbk1hbmFnZXInO1xuaW1wb3J0IHsgUG9ydEFsbG9jYXRvciB9IGZyb20gJy4vUG9ydEFsbG9jYXRvcic7XG5cbmNvbnN0IFBMVUdJTl9QQVRIID0gJ3BsdWdpbic7XG5cbmV4cG9ydCBjbGFzcyBTZXJ2ZXIge1xuICBwdWJsaWMgc3RhdGljIGluaXQkKHtcbiAgICBtb25pdG9yLFxuICAgIGJpbmFyeSxcbiAgICBzZXJ2ZXJDb25maWcsXG4gIH06IHtcbiAgICByZWFkb25seSBtb25pdG9yOiBNb25pdG9yO1xuICAgIHJlYWRvbmx5IGJpbmFyeTogQmluYXJ5O1xuICAgIHJlYWRvbmx5IHNlcnZlckNvbmZpZzogQ29uZmlnPFNlcnZlckNvbmZpZz47XG4gIH0pOiBPYnNlcnZhYmxlPFNlcnZlcj4ge1xuICAgIGNvbnN0IGRhdGFQYXRoJCA9IHNlcnZlckNvbmZpZy5jb25maWckLnBpcGUoXG4gICAgICBtYXAoKGNvbmZpZykgPT4gY29uZmlnLnBhdGhzLmRhdGEpLFxuICAgICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKSxcbiAgICApO1xuXG4gICAgY29uc3QgcG9ydEFsbG9jYXRvciQgPSBjb21iaW5lTGF0ZXN0KFtcbiAgICAgIGRhdGFQYXRoJCxcbiAgICAgIHNlcnZlckNvbmZpZy5jb25maWckLnBpcGUoXG4gICAgICAgIG1hcCgoY29uZmlnKSA9PiBjb25maWcucG9ydHMpLFxuICAgICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgpLFxuICAgICAgKSxcbiAgICBdKS5waXBlKFxuICAgICAgc3dpdGNoTWFwKChbZGF0YVBhdGgsIHBvcnRzXSkgPT5cbiAgICAgICAgZGVmZXIoYXN5bmMgKCkgPT5cbiAgICAgICAgICBQb3J0QWxsb2NhdG9yLmNyZWF0ZSh7XG4gICAgICAgICAgICBtb25pdG9yLFxuICAgICAgICAgICAgZGF0YVBhdGgsXG4gICAgICAgICAgICBwb3J0TWluOiBwb3J0cy5taW4sXG4gICAgICAgICAgICBwb3J0TWF4OiBwb3J0cy5tYXgsXG4gICAgICAgICAgfSksXG4gICAgICAgICksXG4gICAgICApLFxuICAgICk7XG5cbiAgICBjb25zdCBwbHVnaW5NYW5hZ2VyJCA9IGNvbWJpbmVMYXRlc3QoW1xuICAgICAgZGF0YVBhdGgkLFxuICAgICAgcG9ydEFsbG9jYXRvciQsXG4gICAgICBzZXJ2ZXJDb25maWcuY29uZmlnJC5waXBlKFxuICAgICAgICBtYXAoKGNvbmZpZykgPT4gY29uZmlnLmh0dHBTZXJ2ZXIucG9ydCksXG4gICAgICAgIGRpc3RpbmN0VW50aWxDaGFuZ2VkKCksXG4gICAgICApLFxuICAgIF0pLnBpcGUoXG4gICAgICBzd2l0Y2hNYXAoKFtkYXRhUGF0aCwgcG9ydEFsbG9jYXRvciwgaHR0cFNlcnZlclBvcnRdKSA9PlxuICAgICAgICBkZWZlcihhc3luYyAoKSA9PiB7XG4gICAgICAgICAgY29uc3QgcGx1Z2luTWFuYWdlciA9IG5ldyBQbHVnaW5NYW5hZ2VyKHtcbiAgICAgICAgICAgIG1vbml0b3IsXG4gICAgICAgICAgICBiaW5hcnksXG4gICAgICAgICAgICBwb3J0QWxsb2NhdG9yLFxuICAgICAgICAgICAgZGF0YVBhdGg6IHBhdGgucmVzb2x2ZShkYXRhUGF0aCwgUExVR0lOX1BBVEgpLFxuICAgICAgICAgICAgaHR0cFNlcnZlclBvcnQsXG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICBhd2FpdCBwbHVnaW5NYW5hZ2VyLmluaXQoKTtcblxuICAgICAgICAgIHJldHVybiBwbHVnaW5NYW5hZ2VyO1xuICAgICAgICB9KSxcbiAgICAgICksXG4gICAgKTtcblxuICAgIHJldHVybiBjb21iaW5lTGF0ZXN0KFtkYXRhUGF0aCQsIHBsdWdpbk1hbmFnZXIkXSkucGlwZShcbiAgICAgIHN3aXRjaE1hcCgoW2RhdGFQYXRoLCBwbHVnaW5NYW5hZ2VyXSkgPT4ge1xuICAgICAgICBjb25zdCBzZXJ2ZXIgPSBuZXcgU2VydmVyKHtcbiAgICAgICAgICBtb25pdG9yLFxuICAgICAgICAgIHNlcnZlckNvbmZpZyxcbiAgICAgICAgICBkYXRhUGF0aCxcbiAgICAgICAgICBwbHVnaW5NYW5hZ2VyLFxuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gc2VydmVyLnN0YXJ0JCgpLnBpcGUoXG4gICAgICAgICAgbWFwKCgpID0+IHNlcnZlciksXG4gICAgICAgICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKSxcbiAgICAgICAgKTtcbiAgICAgIH0pLFxuICAgICk7XG4gIH1cblxuICBwdWJsaWMgcmVhZG9ubHkgc2VydmVyQ29uZmlnOiBDb25maWc8U2VydmVyQ29uZmlnPjtcbiAgcHVibGljIHJlYWRvbmx5IGRhdGFQYXRoOiBzdHJpbmc7XG4gIHB1YmxpYyByZWFkb25seSBwbHVnaW5NYW5hZ2VyOiBQbHVnaW5NYW5hZ2VyO1xuICBwcml2YXRlIHJlYWRvbmx5IG1vbml0b3I6IE1vbml0b3I7XG4gIHByaXZhdGUgcmVhZG9ubHkgbXV0YWJsZVNlcnZlckRlYnVnOiB7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lIHJlYWRvbmx5LWtleXdvcmRcbiAgICBwb3J0PzogbnVtYmVyO1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZSByZWFkb25seS1rZXl3b3JkXG4gICAgcGlkPzogbnVtYmVyO1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZSByZWFkb25seS1rZXl3b3JkXG4gICAgcGlkUGF0aD86IHN0cmluZztcbiAgfTtcblxuICBwdWJsaWMgY29uc3RydWN0b3Ioe1xuICAgIG1vbml0b3IsXG4gICAgc2VydmVyQ29uZmlnLFxuICAgIGRhdGFQYXRoLFxuICAgIHBsdWdpbk1hbmFnZXIsXG4gIH06IHtcbiAgICByZWFkb25seSBtb25pdG9yOiBNb25pdG9yO1xuICAgIHJlYWRvbmx5IHNlcnZlckNvbmZpZzogQ29uZmlnPFNlcnZlckNvbmZpZz47XG4gICAgcmVhZG9ubHkgZGF0YVBhdGg6IHN0cmluZztcbiAgICByZWFkb25seSBwbHVnaW5NYW5hZ2VyOiBQbHVnaW5NYW5hZ2VyO1xuICB9KSB7XG4gICAgdGhpcy5tb25pdG9yID0gbW9uaXRvci5hdCgnc2VydmVyJyk7XG4gICAgdGhpcy5zZXJ2ZXJDb25maWcgPSBzZXJ2ZXJDb25maWc7XG4gICAgdGhpcy5kYXRhUGF0aCA9IGRhdGFQYXRoO1xuICAgIHRoaXMucGx1Z2luTWFuYWdlciA9IHBsdWdpbk1hbmFnZXI7XG5cbiAgICB0aGlzLm11dGFibGVTZXJ2ZXJEZWJ1ZyA9IHt9O1xuICB9XG5cbiAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lIG5vLWFueVxuICBwdWJsaWMgc3RhcnQkKCk6IE9ic2VydmFibGU8YW55PiB7XG4gICAgY29uc3QgZ3JwYyQgPSB0aGlzLnNlcnZlckNvbmZpZy5jb25maWckLnBpcGUoXG4gICAgICBtYXAoKGNvbmZpZykgPT4gY29uZmlnLnNlcnZlciksXG4gICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgpLFxuICAgICAgbWVyZ2VTY2FuPFNlcnZlckNvbmZpZ1snc2VydmVyJ10sIE1hbGkgfCB1bmRlZmluZWQ+KFxuICAgICAgICAocHJldkFwcCwgc2VydmVyQ29uZmlnKSA9PlxuICAgICAgICAgIGRlZmVyKGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIGlmIChwcmV2QXBwID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgY29uc3QgbWFuYWdlciA9IG5ldyBTZXJ2ZXJNYW5hZ2VyKHtcbiAgICAgICAgICAgICAgICBkYXRhUGF0aDogdGhpcy5kYXRhUGF0aCxcbiAgICAgICAgICAgICAgICBzZXJ2ZXJWZXJzaW9uOiBWRVJTSU9OLFxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgdGhpcy5tdXRhYmxlU2VydmVyRGVidWcucGlkUGF0aCA9IG1hbmFnZXIucGlkUGF0aDtcblxuICAgICAgICAgICAgICBjb25zdCBwaWQgPSBhd2FpdCBtYW5hZ2VyLmNoZWNrQWxpdmUoc2VydmVyQ29uZmlnLnBvcnQpO1xuICAgICAgICAgICAgICBpZiAocGlkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLm11dGFibGVTZXJ2ZXJEZWJ1Zy5waWQgPSBwcm9jZXNzLnBpZDtcblxuICAgICAgICAgICAgICAgIGF3YWl0IG1hbmFnZXIud3JpdGVWZXJzaW9uUElEKHByb2Nlc3MucGlkLCBWRVJTSU9OKTtcbiAgICAgICAgICAgICAgfSBlbHNlIGlmIChwaWQgIT09IHByb2Nlc3MucGlkKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IFNlcnZlclJ1bm5pbmdFcnJvcihwaWQpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBhd2FpdCBwcmV2QXBwLmNsb3NlKCkuY2F0Y2goKGVycm9yOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMubW9uaXRvci5sb2dFcnJvcih7XG4gICAgICAgICAgICAgICAgICBuYW1lOiAnZ3JwY19hcHBfY2xvc2VfZXJyb3InLFxuICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ0ZhaWxlZCB0byBjbG9zZSBwcmV2aW91cyBhcHAnLFxuICAgICAgICAgICAgICAgICAgZXJyb3IsXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgYXBwID0gbmV3IE1hbGkocHJvdG8pO1xuICAgICAgICAgICAgYXBwLnNpbGVudCA9IGZhbHNlO1xuICAgICAgICAgICAgYXBwLm9uKCdlcnJvcicsIChlcnJvcjogRXJyb3IsIGN0eDogQ29udGV4dCB8IHVuZGVmaW5lZCB8IG51bGwpID0+IHtcbiAgICAgICAgICAgICAgbGV0IG1vbml0b3IgPSB0aGlzLm1vbml0b3I7XG4gICAgICAgICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgICAgICAgaWYgKGN0eCAhPSB1bmRlZmluZWQgJiYgKGN0eCBhcyBhbnkpLnN0YXRlICE9IHVuZGVmaW5lZCAmJiAoY3R4IGFzIGFueSkuc3RhdGUubW9uaXRvciAhPSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgICAgICAgICAgKHsgbW9uaXRvciB9ID0gKGN0eCBhcyBhbnkpLnN0YXRlKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBtb25pdG9yLmxvZ0Vycm9yKHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ3JwY19zZXJ2ZXJfcmVxdWVzdF91bmNhdWdodF9lcnJvcicsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogJ1VuY2F1Z2h0IGdycGMgZXJyb3IuJyxcbiAgICAgICAgICAgICAgICBlcnJvcixcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGFwcC51c2UoY29udGV4dCh7IG1vbml0b3I6IHRoaXMubW9uaXRvciB9KSk7XG4gICAgICAgICAgICBhcHAudXNlKHNlcnZpY2VzTWlkZGxld2FyZSh7IHNlcnZlcjogdGhpcyB9KSk7XG4gICAgICAgICAgICB0aGlzLm11dGFibGVTZXJ2ZXJEZWJ1Zy5wb3J0ID0gc2VydmVyQ29uZmlnLnBvcnQ7XG4gICAgICAgICAgICBhcHAuc3RhcnQoYDAuMC4wLjA6JHtzZXJ2ZXJDb25maWcucG9ydH1gKTtcbiAgICAgICAgICAgIHRoaXMubW9uaXRvci5sb2coe1xuICAgICAgICAgICAgICBuYW1lOiAnc2VydmVyX2xpc3RlbicsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IGBHUlBDIFNlcnZlciBsaXN0ZW5pbmcgb24gMC4wLjAuMDoke3NlcnZlckNvbmZpZy5wb3J0fS5gLFxuICAgICAgICAgICAgICBsZXZlbDogJ2luZm8nLFxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHJldHVybiBhcHA7XG4gICAgICAgICAgfSksXG4gICAgICAgIHVuZGVmaW5lZCxcbiAgICAgICAgMSxcbiAgICAgICksXG4gICAgICBmaW5hbGl6ZTxNYWxpIHwgdW5kZWZpbmVkPihhc3luYyAoYXBwKSA9PiB7XG4gICAgICAgIGlmIChhcHAgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIGF3YWl0IGFwcC5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgY29uc3QgYXBwJCA9IG5ldyBCZWhhdmlvclN1YmplY3QoXG4gICAgICAoKCkgPT4ge1xuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgIGNvbnN0IGFwcCA9IG5ldyBBcHBsaWNhdGlvbjxhbnksIHt9PigpO1xuICAgICAgICBhcHAuc2lsZW50ID0gdHJ1ZTtcblxuICAgICAgICBhcHAub24oJ2Vycm9yJywgYXBwT25FcnJvcih7IG1vbml0b3I6IHRoaXMubW9uaXRvciB9KSk7XG5cbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICBjb25zdCByb3V0ZXIgPSBuZXcgUm91dGVyPGFueSwge30+KCk7XG5cbiAgICAgICAgY29uc3QgcnBjTWlkZGxld2FyZSA9IHJwYyh7IHNlcnZlcjogdGhpcyB9KTtcbiAgICAgICAgcm91dGVyLnVzZShodHRwQ29udGV4dCh7IG1vbml0b3I6IHRoaXMubW9uaXRvciB9KSk7XG4gICAgICAgIHJvdXRlci51c2UoY29ycykucG9zdChycGNNaWRkbGV3YXJlLm5hbWUsIHJwY01pZGRsZXdhcmUucGF0aCwgcnBjTWlkZGxld2FyZS5taWRkbGV3YXJlKTtcblxuICAgICAgICBhcHAudXNlKHJvdXRlci5yb3V0ZXMoKSk7XG4gICAgICAgIGFwcC51c2UoY29ycyk7XG4gICAgICAgIGFwcC51c2Uocm91dGVyLmFsbG93ZWRNZXRob2RzKCkpO1xuXG4gICAgICAgIHJldHVybiBhcHA7XG4gICAgICB9KSgpLFxuICAgICk7XG5cbiAgICBjb25zdCBodHRwJCA9IHRoaXMuc2VydmVyQ29uZmlnLmNvbmZpZyQucGlwZShcbiAgICAgIHN3aXRjaE1hcCgoY29uZmlnKSA9PlxuICAgICAgICBjcmVhdGVTZXJ2ZXIkKHRoaXMubW9uaXRvciwgYXBwJCwgX29mKDYwMDAwKSwgeyBob3N0OiAnMC4wLjAuMCcsIHBvcnQ6IGNvbmZpZy5odHRwU2VydmVyLnBvcnQgfSwgKCkgPT5cbiAgICAgICAgICBodHRwLmNyZWF0ZVNlcnZlcigpLFxuICAgICAgICApLFxuICAgICAgKSxcbiAgICApO1xuXG4gICAgcmV0dXJuIGNvbWJpbmVMYXRlc3QoW2dycGMkLCBodHRwJF0pO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIHJlc2V0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMucGx1Z2luTWFuYWdlci5yZXNldCgpO1xuICB9XG5cbiAgcHVibGljIGdldERlYnVnKCk6IERlc2NyaWJlVGFibGUge1xuICAgIHJldHVybiBbXG4gICAgICBbJ1NlcnZlciBDb25maWcgUGF0aCcsIHRoaXMuc2VydmVyQ29uZmlnLmNvbmZpZ1BhdGhdIGFzIGNvbnN0LFxuICAgICAgWydEYXRhIFBhdGgnLCB0aGlzLmRhdGFQYXRoXSBhcyBjb25zdCxcbiAgICAgIFsnU2VydmVyJywgeyB0eXBlOiAnZGVzY3JpYmUnLCB0YWJsZTogdGhpcy5nZXRTZXJ2ZXJEZWJ1ZygpIH1dIGFzIGNvbnN0LFxuICAgICAgWydQbHVnaW4gTWFuYWdlcicsIHsgdHlwZTogJ2Rlc2NyaWJlJywgdGFibGU6IHRoaXMucGx1Z2luTWFuYWdlci5nZXREZWJ1ZygpIH1dIGFzIGNvbnN0LFxuICAgIF07XG4gIH1cblxuICBwcml2YXRlIGdldFNlcnZlckRlYnVnKCk6IERlc2NyaWJlVGFibGUge1xuICAgIHJldHVybiBbXG4gICAgICBbJ1BvcnQnLCB0aGlzLmdldFZhbHVlKHRoaXMubXV0YWJsZVNlcnZlckRlYnVnLnBvcnQpXSBhcyBjb25zdCxcbiAgICAgIFsnUHJvY2VzcyBJRCcsIHRoaXMuZ2V0VmFsdWUodGhpcy5tdXRhYmxlU2VydmVyRGVidWcucGlkKV0gYXMgY29uc3QsXG4gICAgICBbJ1Byb2Nlc3MgSUQgUGF0aCcsIHRoaXMuZ2V0VmFsdWUodGhpcy5tdXRhYmxlU2VydmVyRGVidWcucGlkUGF0aCldIGFzIGNvbnN0LFxuICAgIF07XG4gIH1cblxuICBwcml2YXRlIGdldFZhbHVlKHZhbHVlPzogc3RyaW5nIHwgbnVtYmVyKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IHVuZGVmaW5lZCA/ICdOb3QgU2V0JyA6IGAke3ZhbHVlfWA7XG4gIH1cbn1cbiJdfQ==