sonamu 0.10.13 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/agents/agent.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
- package/dist/api/base-frame.d.ts.map +1 -1
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +3 -2
- package/dist/api/validator.d.ts.map +1 -1
- package/dist/auth/audit-log/events.d.ts.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -3
- package/dist/auth/knex-adapter.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/admin.d.ts +4 -4
- package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
- package/dist/bin/build-config.d.ts +10 -0
- package/dist/bin/build-config.d.ts.map +1 -1
- package/dist/bin/build-config.js +130 -6
- package/dist/bin/fixture.d.ts +7 -4
- package/dist/bin/fixture.d.ts.map +1 -1
- package/dist/bin/fixture.js +161 -13
- package/dist/bin/test-command.d.ts +7 -0
- package/dist/bin/test-command.d.ts.map +1 -1
- package/dist/bin/test-command.js +18 -4
- package/dist/bin/typecheck-web.d.ts +2 -0
- package/dist/bin/typecheck-web.d.ts.map +1 -0
- package/dist/bin/typecheck-web.js +36 -0
- package/dist/cache/decorator.d.ts.map +1 -1
- package/dist/cache/drivers.d.ts.map +1 -1
- package/dist/cache-control/cache-control.d.ts.map +1 -1
- package/dist/compress/compress.d.ts.map +1 -1
- package/dist/cone/cone-generator.js +1 -1
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.types.d.ts +0 -1
- package/dist/database/base-model.types.d.ts.map +1 -1
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/puri-subset.types.d.ts +0 -1
- package/dist/database/puri-subset.types.d.ts.map +1 -1
- package/dist/database/puri-wrapper.d.ts +0 -1
- package/dist/database/puri-wrapper.d.ts.map +1 -1
- package/dist/database/puri.d.ts +0 -1
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.types.d.ts +0 -1
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/transaction-context.d.ts.map +1 -1
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/dict/en.d.ts +58 -58
- package/dist/dict/en.d.ts.map +1 -1
- package/dist/dict/ko.d.ts +58 -58
- package/dist/dict/ko.d.ts.map +1 -1
- package/dist/dict/rc-keys.d.ts.map +1 -1
- package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
- package/dist/dict/sonamu-dictionary.js +2 -2
- package/dist/dict/utils.d.ts.map +1 -1
- package/dist/entity/entity-manager.d.ts +115 -113
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +25 -19
- package/dist/entity/entity-template-cone.d.ts +4 -4
- package/dist/entity/entity.d.ts +6 -1
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +8 -3
- package/dist/env.d.ts.map +1 -1
- package/dist/exceptions/so-exceptions.d.ts.map +1 -1
- package/dist/filter/types.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/logger/configure.d.ts +10 -1
- package/dist/logger/configure.d.ts.map +1 -1
- package/dist/logger/configure.js +30 -5
- package/dist/migration/migration-set.d.ts.map +1 -1
- package/dist/migration/migration-set.js +17 -6
- package/dist/migration/migrator.d.ts +12 -1
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +77 -30
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
- package/dist/migration/slack-confirm.d.ts.map +1 -1
- package/dist/naite/messaging-types.d.ts +0 -1
- package/dist/naite/messaging-types.d.ts.map +1 -1
- package/dist/naite/naite-reporter.d.ts +0 -1
- package/dist/naite/naite-reporter.d.ts.map +1 -1
- package/dist/naite/naite.d.ts.map +1 -1
- package/dist/storage/base-file.d.ts.map +1 -1
- package/dist/storage/buffered-file.d.ts.map +1 -1
- package/dist/storage/drivers.d.ts.map +1 -1
- package/dist/storage/s3-driver.d.ts.map +1 -1
- package/dist/storage/storage-manager.d.ts.map +1 -1
- package/dist/storage/uploaded-file.d.ts.map +1 -1
- package/dist/stream/ws-audience-resolver.d.ts.map +1 -1
- package/dist/stream/ws-audience.d.ts.map +1 -1
- package/dist/stream/ws-cluster-bus.d.ts.map +1 -1
- package/dist/stream/ws-delivery.d.ts.map +1 -1
- package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
- package/dist/stream/ws-presence-store.d.ts.map +1 -1
- package/dist/stream/ws-registry.d.ts.map +1 -1
- package/dist/stream/ws-telemetry-memory.d.ts.map +1 -1
- package/dist/stream/ws-telemetry.d.ts.map +1 -1
- package/dist/stream/ws.d.ts.map +1 -1
- package/dist/syncer/api-parser.js +2 -2
- package/dist/syncer/file-patterns.d.ts +17 -17
- package/dist/syncer/filesystem-dependencies.js +1 -1
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/tasks/step-wrapper.d.ts.map +1 -1
- package/dist/tasks/workflow-manager.d.ts.map +1 -1
- package/dist/template/implementations/entity.template.d.ts +2 -2
- package/dist/template/implementations/entity.template.d.ts.map +1 -1
- package/dist/template/implementations/entry-server.template.d.ts +2 -2
- package/dist/template/implementations/entry-server.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.d.ts +2 -2
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_http.template.d.ts +2 -2
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.d.ts +2 -2
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/http_validators.template.d.ts +2 -2
- package/dist/template/implementations/http_validators.template.d.ts.map +1 -1
- package/dist/template/implementations/init_types.template.d.ts +2 -2
- package/dist/template/implementations/init_types.template.d.ts.map +1 -1
- package/dist/template/implementations/model.template.d.ts +2 -2
- package/dist/template/implementations/model.template.d.ts.map +1 -1
- package/dist/template/implementations/model_test.template.d.ts +2 -2
- package/dist/template/implementations/model_test.template.d.ts.map +1 -1
- package/dist/template/implementations/queries.template.d.ts +2 -2
- package/dist/template/implementations/queries.template.d.ts.map +1 -1
- package/dist/template/implementations/sd.template.d.ts +2 -2
- package/dist/template/implementations/sd.template.d.ts.map +1 -1
- package/dist/template/implementations/sd.template.js +1 -1
- package/dist/template/implementations/services.template.d.ts +2 -2
- package/dist/template/implementations/services.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.d.ts +3 -3
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_id_all_select.template.d.ts +2 -2
- package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.d.ts +2 -2
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_search_input.template.d.ts +2 -2
- package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
- package/dist/template/template-manager.d.ts.map +1 -1
- package/dist/template/template.d.ts.map +1 -1
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/testing/_relation-graph.d.ts.map +1 -1
- package/dist/testing/data-explorer.d.ts +2 -2
- package/dist/testing/data-explorer.d.ts.map +1 -1
- package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
- package/dist/testing/faker-mappings.d.ts.map +1 -1
- package/dist/testing/fixture-generator.d.ts.map +1 -1
- package/dist/testing/fixture-generator.js +1 -1
- package/dist/testing/fixture-loader.d.ts.map +1 -1
- package/dist/testing/fixture-manager.d.ts +4 -0
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +99 -25
- package/dist/testing/naite-vitest-reporter.d.ts.map +1 -1
- package/dist/testing/parallel-db-manager.d.ts.map +1 -1
- package/dist/tooling/__tests__/helpers/sonamu-test-root.d.ts +10 -0
- package/dist/tooling/__tests__/helpers/sonamu-test-root.d.ts.map +1 -0
- package/dist/tooling/__tests__/helpers/sonamu-test-root.js +42 -0
- package/dist/tooling/cdd-service.d.ts +87 -0
- package/dist/tooling/cdd-service.d.ts.map +1 -0
- package/dist/tooling/cdd-service.js +215 -0
- package/dist/tooling/cli-tooling.d.ts +11 -0
- package/dist/tooling/cli-tooling.d.ts.map +1 -0
- package/dist/tooling/cli-tooling.js +901 -0
- package/dist/tooling/fixture-service.d.ts +29 -0
- package/dist/tooling/fixture-service.d.ts.map +1 -0
- package/dist/tooling/fixture-service.js +53 -0
- package/dist/tooling/index.d.ts +2 -0
- package/dist/tooling/index.d.ts.map +1 -0
- package/dist/tooling/index.js +2 -0
- package/dist/tooling/test-run-client.d.ts +27 -0
- package/dist/tooling/test-run-client.d.ts.map +1 -0
- package/dist/tooling/test-run-client.js +120 -0
- package/dist/tsdown.api.config.js +48 -0
- package/dist/types/types.d.ts +527 -527
- package/dist/types/types.d.ts.map +1 -1
- package/dist/ui/ai-client.d.ts.map +1 -1
- package/dist/utils/formatter.d.ts +6 -0
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +42 -11
- package/dist/utils/model.d.ts +1 -1
- package/dist/vector/chunking.d.ts.map +1 -1
- package/dist/vector/embedding.d.ts.map +1 -1
- package/package.json +13 -12
- package/src/api/sonamu.ts +2 -1
- package/src/auth/index.ts +1 -1
- package/src/bin/__tests__/zod-compiler-build.test.ts +67 -0
- package/src/bin/build-config.ts +151 -5
- package/src/bin/fixture.ts +204 -21
- package/src/bin/test-command.ts +27 -4
- package/src/bin/typecheck-web.ts +56 -0
- package/src/dict/sonamu-dictionary.ts +1 -1
- package/src/entity/__tests__/entity-manager-autoload.contract.test.ts +55 -0
- package/src/entity/__tests__/entity-manager-explicit-root-registration.contract.test.ts +107 -0
- package/src/entity/entity-manager.ts +31 -19
- package/src/entity/entity.ts +7 -2
- package/src/logger/__tests__/configure.test.ts +33 -0
- package/src/logger/configure.ts +45 -3
- package/src/migration/__tests__/migrator.test.ts +51 -0
- package/src/migration/migration-set.ts +22 -5
- package/src/migration/migrator.ts +103 -34
- package/src/syncer/api-parser.ts +1 -1
- package/src/testing/fixture-manager.ts +124 -32
- package/src/tooling/__tests__/cdd-rule-bootstrap.contract.test.ts +118 -0
- package/src/tooling/__tests__/cli-tooling-auth.contract.test.ts +72 -0
- package/src/tooling/__tests__/cli-tooling-cone-locale.contract.test.ts +150 -0
- package/src/tooling/__tests__/cli-tooling-entity-patch.contract.test.ts +131 -0
- package/src/tooling/__tests__/cli-tooling-fixture-transfer.contract.test.ts +255 -0
- package/src/tooling/__tests__/cli-tooling-i18n-update.contract.test.ts +104 -0
- package/src/tooling/__tests__/cli-tooling-scaffold.contract.test.ts +86 -0
- package/src/tooling/__tests__/cli-tooling-stub-practice.contract.test.ts +147 -0
- package/src/tooling/__tests__/default-cdd-adapter.contract.test.ts +73 -0
- package/src/tooling/__tests__/helpers/sonamu-test-root.ts +42 -0
- package/src/tooling/__tests__/test-run-client-config.contract.test.ts +154 -0
- package/src/tooling/cdd-service.ts +256 -0
- package/src/tooling/cli-tooling.ts +937 -0
- package/src/tooling/fixture-service.ts +72 -0
- package/src/tooling/index.ts +1 -0
- package/src/tooling/test-run-client.ts +196 -0
- package/src/utils/__tests__/formatter-bin.test.ts +99 -0
- package/src/utils/formatter.ts +74 -10
- package/tsdown.api.config.ts +8 -10
- package/bin/cli.js +0 -75
- package/dist/bin/cli.d.ts +0 -2
- package/dist/bin/cli.d.ts.map +0 -1
- package/dist/bin/cli.js +0 -956
- package/src/bin/cli.ts +0 -1166
package/dist/bin/cli.js
DELETED
|
@@ -1,956 +0,0 @@
|
|
|
1
|
-
import { getSonamuEnvironment, init_env } from "../env.js";
|
|
2
|
-
import { findApiRootPath, findAppRootPath, init_utils } from "../utils/utils.js";
|
|
3
|
-
import { execWithLinePrefix, init_console_util, printBuildSummary, printTaskFailed, printTaskHeader, printTaskStart, printTaskSuccess } from "../utils/console-util.js";
|
|
4
|
-
import { exists, init_fs_utils } from "../utils/fs-utils.js";
|
|
5
|
-
import { Sonamu, init_sonamu } from "../api/sonamu.js";
|
|
6
|
-
import { EntityManager, init_entity_manager } from "../entity/entity-manager.js";
|
|
7
|
-
import { SUPPORTED_PLUGIN_IDS, isValidPluginId } from "../auth/plugins/entity-definitions/index.js";
|
|
8
|
-
import { addCompanionsToEntities, generateBetterAuthEntities } from "../auth/auth-generator.js";
|
|
9
|
-
import { API_ARTIFACTS, HTTP_VALIDATOR_REGISTRY_BUILD_PATH, HTTP_VALIDATOR_REGISTRY_SOURCE_PATH, WEB_ARTIFACTS, assertApiZodCompilerBuildOutput, createApiZodCompilerBuildWrapper } from "./build-config.js";
|
|
10
|
-
import { Migrator, init_migrator } from "../migration/migrator.js";
|
|
11
|
-
import { FixtureManager, init_fixture_manager } from "../testing/fixture-manager.js";
|
|
12
|
-
import { loadBuildCompilerPolicy } from "./compiler-policy.js";
|
|
13
|
-
import { fixtureExploreCommand, fixtureFetchCommand, fixtureGenCommand } from "./fixture.js";
|
|
14
|
-
import { getMigrateRunTargets } from "./migrate-targets.js";
|
|
15
|
-
import { testCommand } from "./test-command.js";
|
|
16
|
-
import assert from "assert";
|
|
17
|
-
import path from "path";
|
|
18
|
-
import knex from "knex";
|
|
19
|
-
import chalk from "chalk";
|
|
20
|
-
import { mkdir, open, readFile, readdir, rm, writeFile } from "fs/promises";
|
|
21
|
-
import { spawn, spawnSync } from "child_process";
|
|
22
|
-
import { createRequire } from "module";
|
|
23
|
-
import process from "process";
|
|
24
|
-
import { tsicli } from "tsicli";
|
|
25
|
-
//#region src/bin/cli.ts
|
|
26
|
-
init_sonamu();
|
|
27
|
-
init_entity_manager();
|
|
28
|
-
init_env();
|
|
29
|
-
init_migrator();
|
|
30
|
-
init_fixture_manager();
|
|
31
|
-
init_console_util();
|
|
32
|
-
init_fs_utils();
|
|
33
|
-
init_utils();
|
|
34
|
-
let migrator;
|
|
35
|
-
function runMysql(args, stdio = [
|
|
36
|
-
"ignore",
|
|
37
|
-
"inherit",
|
|
38
|
-
"inherit"
|
|
39
|
-
]) {
|
|
40
|
-
const result = spawnSync("mysql", args, { stdio });
|
|
41
|
-
if (result.error) throw result.error;
|
|
42
|
-
if (result.status !== 0) throw new Error(`mysql 명령이 종료 코드 ${result.status ?? "unknown"}로 실패했습니다.`);
|
|
43
|
-
}
|
|
44
|
-
async function writeMysqlDump(args, outputPath) {
|
|
45
|
-
const output = await open(outputPath, "w");
|
|
46
|
-
try {
|
|
47
|
-
const result = spawnSync("mysqldump", args, { stdio: [
|
|
48
|
-
"ignore",
|
|
49
|
-
output.fd,
|
|
50
|
-
"inherit"
|
|
51
|
-
] });
|
|
52
|
-
if (result.error) throw result.error;
|
|
53
|
-
if (result.status !== 0) throw new Error(`mysqldump 명령이 종료 코드 ${result.status ?? "unknown"}로 실패했습니다.`);
|
|
54
|
-
} finally {
|
|
55
|
-
await output.close();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
async function runMysqlWithInput(args, inputPath) {
|
|
59
|
-
const input = await open(inputPath, "r");
|
|
60
|
-
try {
|
|
61
|
-
runMysql(args, [
|
|
62
|
-
input.fd,
|
|
63
|
-
"inherit",
|
|
64
|
-
"inherit"
|
|
65
|
-
]);
|
|
66
|
-
} finally {
|
|
67
|
-
await input.close();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function openInCode(pathToOpen) {
|
|
71
|
-
const result = spawnSync("code", [pathToOpen], { stdio: [
|
|
72
|
-
"ignore",
|
|
73
|
-
"inherit",
|
|
74
|
-
"inherit"
|
|
75
|
-
] });
|
|
76
|
-
if (result.error) throw result.error;
|
|
77
|
-
if (result.status !== 0) throw new Error(`code 명령이 종료 코드 ${result.status ?? "unknown"}로 실패했습니다.`);
|
|
78
|
-
}
|
|
79
|
-
function mysqlConnectionArgs(connection) {
|
|
80
|
-
return [
|
|
81
|
-
`-h${String(connection.host)}`,
|
|
82
|
-
`-u${String(connection.user)}`,
|
|
83
|
-
`-p${String(connection.password)}`
|
|
84
|
-
];
|
|
85
|
-
}
|
|
86
|
-
function copyToClipboard(value) {
|
|
87
|
-
const result = spawnSync("pbcopy", [], {
|
|
88
|
-
input: value,
|
|
89
|
-
stdio: [
|
|
90
|
-
"pipe",
|
|
91
|
-
"inherit",
|
|
92
|
-
"inherit"
|
|
93
|
-
]
|
|
94
|
-
});
|
|
95
|
-
if (result.error) throw result.error;
|
|
96
|
-
if (result.status !== 0) throw new Error(`pbcopy 명령이 종료 코드 ${result.status ?? "unknown"}로 실패했습니다.`);
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* CLI 옵션을 파싱하는 헬퍼 함수
|
|
100
|
-
*/
|
|
101
|
-
function parseCliOptions(argv = process.argv) {
|
|
102
|
-
const flags = /* @__PURE__ */ new Set();
|
|
103
|
-
const options = {};
|
|
104
|
-
for (let i = 0; i < argv.length; i++) {
|
|
105
|
-
const arg = argv[i];
|
|
106
|
-
if (!arg.startsWith("--")) continue;
|
|
107
|
-
if (arg.includes("=")) {
|
|
108
|
-
const [key, value] = arg.slice(2).split("=");
|
|
109
|
-
options[key] = value;
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
const nextArg = argv[i + 1];
|
|
113
|
-
if (nextArg && !nextArg.startsWith("--") && !nextArg.startsWith("-")) {
|
|
114
|
-
options[arg.slice(2)] = nextArg;
|
|
115
|
-
i++;
|
|
116
|
-
} else flags.add(arg.slice(2));
|
|
117
|
-
}
|
|
118
|
-
return {
|
|
119
|
-
flags,
|
|
120
|
-
options
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
async function bootstrap() {
|
|
124
|
-
if (![
|
|
125
|
-
"dev",
|
|
126
|
-
"build",
|
|
127
|
-
"start",
|
|
128
|
-
"skills",
|
|
129
|
-
"test"
|
|
130
|
-
].includes(process.argv[2] ?? "")) await Sonamu.init(false, false);
|
|
131
|
-
try {
|
|
132
|
-
const filteredArgv = [];
|
|
133
|
-
let skipNext = false;
|
|
134
|
-
let afterDoubleDash = false;
|
|
135
|
-
for (let i = 0; i < process.argv.length; i++) {
|
|
136
|
-
const arg = process.argv[i];
|
|
137
|
-
if (arg === "--") {
|
|
138
|
-
afterDoubleDash = true;
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
if (afterDoubleDash) continue;
|
|
142
|
-
if (skipNext) {
|
|
143
|
-
skipNext = false;
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
if (arg.startsWith("--")) {
|
|
147
|
-
if (arg.includes("=")) continue;
|
|
148
|
-
const nextArg = process.argv[i + 1];
|
|
149
|
-
if (nextArg && !nextArg.startsWith("--") && !nextArg.startsWith("-")) skipNext = true;
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
filteredArgv.push(arg);
|
|
153
|
-
}
|
|
154
|
-
const cmd = filteredArgv[2];
|
|
155
|
-
if ((cmd === "build" || cmd === "dev") && filteredArgv.length === 3) filteredArgv.push("all");
|
|
156
|
-
if (cmd === "test") return testCommand();
|
|
157
|
-
await tsicli(filteredArgv, {
|
|
158
|
-
types: {
|
|
159
|
-
"#entityId": {
|
|
160
|
-
type: "autocomplete",
|
|
161
|
-
name: "#entityId",
|
|
162
|
-
message: "Please input #entityId",
|
|
163
|
-
choices: EntityManager.getAllParentIds().map((entityId) => ({
|
|
164
|
-
title: entityId,
|
|
165
|
-
value: entityId
|
|
166
|
-
}))
|
|
167
|
-
},
|
|
168
|
-
"#recordIds": "number[]",
|
|
169
|
-
"#name": "string",
|
|
170
|
-
"#targets": {
|
|
171
|
-
type: "multiselect",
|
|
172
|
-
name: "#targets",
|
|
173
|
-
message: "Please input #targets",
|
|
174
|
-
choices: [
|
|
175
|
-
{
|
|
176
|
-
title: "Development",
|
|
177
|
-
value: "development"
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
title: "Staging",
|
|
181
|
-
value: "staging"
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
title: "Production",
|
|
185
|
-
value: "production"
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
title: "Fixture",
|
|
189
|
-
value: "fixture"
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
title: "Test",
|
|
193
|
-
value: "test"
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
args: [
|
|
199
|
-
["fixture", "init"],
|
|
200
|
-
[
|
|
201
|
-
"fixture",
|
|
202
|
-
"import",
|
|
203
|
-
"#entityId",
|
|
204
|
-
"#recordIds"
|
|
205
|
-
],
|
|
206
|
-
["fixture", "sync"],
|
|
207
|
-
["fixture", "gen"],
|
|
208
|
-
["fixture", "fetch"],
|
|
209
|
-
["fixture", "explore"],
|
|
210
|
-
["migrate", "run"],
|
|
211
|
-
[
|
|
212
|
-
"migrate",
|
|
213
|
-
"apply",
|
|
214
|
-
"#targets"
|
|
215
|
-
],
|
|
216
|
-
["migrate", "generate"],
|
|
217
|
-
["migrate", "status"],
|
|
218
|
-
[
|
|
219
|
-
"stub",
|
|
220
|
-
"practice",
|
|
221
|
-
"#name"
|
|
222
|
-
],
|
|
223
|
-
[
|
|
224
|
-
"stub",
|
|
225
|
-
"entity",
|
|
226
|
-
"#name"
|
|
227
|
-
],
|
|
228
|
-
[
|
|
229
|
-
"scaffold",
|
|
230
|
-
"model",
|
|
231
|
-
"#entityId"
|
|
232
|
-
],
|
|
233
|
-
[
|
|
234
|
-
"scaffold",
|
|
235
|
-
"model_test",
|
|
236
|
-
"#entityId"
|
|
237
|
-
],
|
|
238
|
-
[
|
|
239
|
-
"scaffold",
|
|
240
|
-
"view_list",
|
|
241
|
-
"#entityId"
|
|
242
|
-
],
|
|
243
|
-
[
|
|
244
|
-
"scaffold",
|
|
245
|
-
"view_form",
|
|
246
|
-
"#entityId"
|
|
247
|
-
],
|
|
248
|
-
[
|
|
249
|
-
"cone",
|
|
250
|
-
"gen",
|
|
251
|
-
"#entityId"
|
|
252
|
-
],
|
|
253
|
-
["sync"],
|
|
254
|
-
["build", "all"],
|
|
255
|
-
["build", "api"],
|
|
256
|
-
["build", "web"],
|
|
257
|
-
["dev", "all"],
|
|
258
|
-
["dev", "api"],
|
|
259
|
-
["dev", "web"],
|
|
260
|
-
["start"],
|
|
261
|
-
["skills", "sync"],
|
|
262
|
-
["test"],
|
|
263
|
-
["auth", "generate"],
|
|
264
|
-
["auth", "add-companions"]
|
|
265
|
-
],
|
|
266
|
-
runners: {
|
|
267
|
-
migrate_status,
|
|
268
|
-
migrate_run,
|
|
269
|
-
migrate_apply,
|
|
270
|
-
migrate_generate,
|
|
271
|
-
fixture_init,
|
|
272
|
-
fixture_import,
|
|
273
|
-
fixture_sync,
|
|
274
|
-
fixture_gen,
|
|
275
|
-
fixture_fetch,
|
|
276
|
-
fixture_explore,
|
|
277
|
-
stub_practice,
|
|
278
|
-
stub_entity,
|
|
279
|
-
scaffold_model,
|
|
280
|
-
scaffold_model_test,
|
|
281
|
-
scaffold_view_list,
|
|
282
|
-
scaffold_view_form,
|
|
283
|
-
cone_gen,
|
|
284
|
-
sync,
|
|
285
|
-
build_all,
|
|
286
|
-
build_api,
|
|
287
|
-
build_web,
|
|
288
|
-
dev_all,
|
|
289
|
-
dev_api,
|
|
290
|
-
dev_web,
|
|
291
|
-
start,
|
|
292
|
-
skills_sync: skillsMigrationNotice,
|
|
293
|
-
test: testCommand,
|
|
294
|
-
auth_generate,
|
|
295
|
-
"auth_add-companions": auth_add_companions
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
} finally {
|
|
299
|
-
await Sonamu.destroy();
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
bootstrap().finally(async () => {
|
|
303
|
-
await FixtureManager.destroy();
|
|
304
|
-
});
|
|
305
|
-
/**
|
|
306
|
-
* pnpm sync 하면 실행되는 함수입니다.
|
|
307
|
-
* 프로젝트를 싱크합니다.
|
|
308
|
-
*
|
|
309
|
-
* `--force` 옵션이 주어지면 lock을 무시하고 풀-싱크를 수행합니다.
|
|
310
|
-
* git post-merge hook이나 CI에서 매 pull 후 자동 실행할 수 있도록 노출.
|
|
311
|
-
*/
|
|
312
|
-
async function sync() {
|
|
313
|
-
const { flags } = parseCliOptions();
|
|
314
|
-
if (flags.has("force")) await Sonamu.syncer.forceSync();
|
|
315
|
-
else await Sonamu.syncer.sync();
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* 이전 생성 프로젝트의 postinstall이 업그레이드를 막지 않도록 구 명령만 안전하게 종료합니다.
|
|
319
|
-
*/
|
|
320
|
-
async function skillsMigrationNotice() {
|
|
321
|
-
console.log(chalk.yellow("'sonamu skills sync' is no longer supported."));
|
|
322
|
-
console.log(chalk.cyan("Install Sonamu skills with 'npx skills@latest add cartanova-ai/skills'."));
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* API 개발 서버를 실행하는 공통 로직입니다.
|
|
326
|
-
* dev_all과 dev_api에서 공유합니다.
|
|
327
|
-
*
|
|
328
|
-
* TypeScript를 바로 실행할 수 있도록 @sonamu-kit/ts-loader를,
|
|
329
|
-
* HMR을 지원하기 위해 @sonamu-kit/hmr-hook을 import하며,
|
|
330
|
-
* 소스맵 지원을 위해 --enable-source-maps 플래그를 포함하여 실행합니다.
|
|
331
|
-
*
|
|
332
|
-
* 이때 @sonamu-kit/ts-loader와 @sonamu-kit/hmr-hook는 sonamu가 자체적으로 가지고 있는 dependency입니다.
|
|
333
|
-
* 또한 실행에 사용하는 @sonamu-kit/hmr-runner도 마찬가지로 sonamu가 자체적으로 가지고 있는 dependency입니다.
|
|
334
|
-
* 따라서 사용자 프로젝트에서는 이 세 패키지를 직접 설치할 필요가 없습니다.
|
|
335
|
-
*/
|
|
336
|
-
function spawnApiDevServer(options) {
|
|
337
|
-
const apiRoot = findApiRootPath();
|
|
338
|
-
const entryPoint = "src/index.ts";
|
|
339
|
-
const hotRunnerBinPath = createRequire(import.meta.url).resolve("@sonamu-kit/hmr-runner/bin/run.js");
|
|
340
|
-
const serverProcess = spawn(process.execPath, [
|
|
341
|
-
hotRunnerBinPath,
|
|
342
|
-
"--clear-screen=false",
|
|
343
|
-
"--node-args=--import=sonamu/ts-loader-register",
|
|
344
|
-
"--node-args=--import=sonamu/hmr-hook-register",
|
|
345
|
-
"--node-args=--enable-source-maps",
|
|
346
|
-
"--on-key=r:restart:Restart server",
|
|
347
|
-
"--on-key=c:clear:Clear screen",
|
|
348
|
-
`--on-key=f:shell(rm ${path.join(apiRoot, "sonamu.lock")}):restart:Force restart`,
|
|
349
|
-
"--on-key=enter:shell(echo hi):Key binding test",
|
|
350
|
-
"--on-key=ctrl+f ctrl+f:shell(git pull && pnpm install && pnpm --filter sonamu build && echo 'Sonamu is now up-to-date!'):restart:Pull & install & build & restart",
|
|
351
|
-
entryPoint
|
|
352
|
-
], {
|
|
353
|
-
cwd: apiRoot,
|
|
354
|
-
stdio: "inherit",
|
|
355
|
-
env: {
|
|
356
|
-
...process.env,
|
|
357
|
-
NODE_ENV: process.env.NODE_ENV ?? "development",
|
|
358
|
-
HOT: "yes",
|
|
359
|
-
API_ROOT_PATH: apiRoot,
|
|
360
|
-
...options?.extraEnv
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
const cleanup = () => {
|
|
364
|
-
console.log(chalk.yellow("\n\n👋 Shutting down..."));
|
|
365
|
-
serverProcess.kill("SIGTERM");
|
|
366
|
-
process.exit(0);
|
|
367
|
-
};
|
|
368
|
-
process.on("SIGINT", cleanup);
|
|
369
|
-
process.on("SIGTERM", cleanup);
|
|
370
|
-
serverProcess.on("exit", (code) => {
|
|
371
|
-
if (code !== 0) {
|
|
372
|
-
console.error(chalk.red(`Server exited with code ${code}`));
|
|
373
|
-
process.exit(code || 1);
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* pnpm dev / pnpm dev all 하면 실행되는 함수입니다.
|
|
379
|
-
* 프로젝트에 대해 HMR 지원하는 개발 서버를 띄워줍니다.
|
|
380
|
-
*
|
|
381
|
-
* Sonamu.init 없이 호출될 것을 상정하여 구현되었습니다.
|
|
382
|
-
*/
|
|
383
|
-
function dev_all() {
|
|
384
|
-
const { version } = createRequire(import.meta.url)("../../package.json");
|
|
385
|
-
console.log(`🌲 Sonamu v${version}\n`);
|
|
386
|
-
spawnApiDevServer();
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* pnpm dev api 하면 실행되는 함수입니다.
|
|
390
|
-
* API 전용 개발 서버를 띄웁니다.
|
|
391
|
-
* dev_all과 거의 동일하되, 통합 웹 서버를 비활성화합니다.
|
|
392
|
-
*
|
|
393
|
-
* Sonamu.init 없이 호출될 것을 상정하여 구현되었습니다.
|
|
394
|
-
*/
|
|
395
|
-
function dev_api() {
|
|
396
|
-
console.log(chalk.yellow.bold("Starting Sonamu API-only dev server...\n"));
|
|
397
|
-
spawnApiDevServer({ extraEnv: { SONAMU_DISABLE_INTEGRATED_WEB: "yes" } });
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* pnpm dev web 하면 실행되는 함수입니다.
|
|
401
|
-
* Vite 개발 서버를 단독으로 실행합니다.
|
|
402
|
-
* -- 뒤의 인자는 Vite에 그대로 전달됩니다.
|
|
403
|
-
*
|
|
404
|
-
* Sonamu.init 없이 호출될 것을 상정하여 구현되었습니다.
|
|
405
|
-
*/
|
|
406
|
-
async function dev_web() {
|
|
407
|
-
const appRoot = findAppRootPath();
|
|
408
|
-
const webPath = path.join(appRoot, "web");
|
|
409
|
-
if (!await exists(webPath)) {
|
|
410
|
-
console.error(`web 디렉토리를 찾을 수 없습니다: ${webPath}`);
|
|
411
|
-
process.exit(1);
|
|
412
|
-
}
|
|
413
|
-
const doubleDashIndex = process.argv.indexOf("--");
|
|
414
|
-
const viteArgs = [
|
|
415
|
-
"exec",
|
|
416
|
-
"vite",
|
|
417
|
-
...doubleDashIndex !== -1 ? process.argv.slice(doubleDashIndex + 1) : []
|
|
418
|
-
];
|
|
419
|
-
console.log(chalk.yellow.bold("Starting Vite dev server...\n"));
|
|
420
|
-
const viteProcess = spawn("pnpm", viteArgs, {
|
|
421
|
-
cwd: webPath,
|
|
422
|
-
stdio: "inherit"
|
|
423
|
-
});
|
|
424
|
-
viteProcess.on("exit", (code) => {
|
|
425
|
-
process.exit(code ?? 0);
|
|
426
|
-
});
|
|
427
|
-
for (const signal of ["SIGINT", "SIGTERM"]) process.on(signal, () => {
|
|
428
|
-
viteProcess.kill(signal);
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* API 빌드 설정 파일 경로를 결정합니다.
|
|
433
|
-
* 프로젝트 루트에 `tsdown.config.ts`가 있으면 그것을, 없으면 sonamu 기본 설정을 사용합니다.
|
|
434
|
-
*/
|
|
435
|
-
async function resolveApiBuildConfigPath(apiRootPath) {
|
|
436
|
-
const localConfigPath = path.join(apiRootPath, "tsdown.config.ts");
|
|
437
|
-
try {
|
|
438
|
-
if (await exists(localConfigPath)) {
|
|
439
|
-
console.log(chalk.dim("Using tsdown.config.ts from project root..."));
|
|
440
|
-
return localConfigPath;
|
|
441
|
-
}
|
|
442
|
-
console.log(chalk.dim("Using default tsdown API config from sonamu package..."));
|
|
443
|
-
return path.join(import.meta.dirname, "..", "..", "tsdown.api.config.ts");
|
|
444
|
-
} catch (error) {
|
|
445
|
-
console.error(chalk.red("Setting up API build config failed."), error);
|
|
446
|
-
process.exit(1);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* sonamu build / sonamu build all 하면 실행되는 함수입니다.
|
|
451
|
-
* build_api + build_web의 합성입니다. Web 디렉토리가 없으면 Web 빌드를 스킵합니다.
|
|
452
|
-
*/
|
|
453
|
-
async function build_all() {
|
|
454
|
-
await build_api();
|
|
455
|
-
await build_web({ skipIfMissing: true });
|
|
456
|
-
}
|
|
457
|
-
/**
|
|
458
|
-
* pnpm build api 하면 실행되는 함수입니다.
|
|
459
|
-
* API 프로젝트만 빌드합니다.
|
|
460
|
-
*
|
|
461
|
-
* Sonamu.init 없이 호출될 것을 상정하여 구현되었습니다.
|
|
462
|
-
*/
|
|
463
|
-
async function build_api() {
|
|
464
|
-
const apiRootPath = findApiRootPath();
|
|
465
|
-
const baseConfigPath = await resolveApiBuildConfigPath(apiRootPath);
|
|
466
|
-
const policy = await loadBuildCompilerPolicy(apiRootPath);
|
|
467
|
-
const configFilePath = await createApiZodCompilerBuildWrapper({
|
|
468
|
-
apiRootPath,
|
|
469
|
-
baseConfigPath,
|
|
470
|
-
policy,
|
|
471
|
-
registryPath: path.join(apiRootPath, HTTP_VALIDATOR_REGISTRY_SOURCE_PATH)
|
|
472
|
-
});
|
|
473
|
-
const temporaryConfigPath = configFilePath === baseConfigPath ? void 0 : configFilePath;
|
|
474
|
-
const apiStartedAt = Date.now();
|
|
475
|
-
try {
|
|
476
|
-
for (const artifact of API_ARTIFACTS) {
|
|
477
|
-
const cwd = apiRootPath;
|
|
478
|
-
printTaskHeader(artifact.name, artifact.description, cwd);
|
|
479
|
-
await runBuildSteps(artifact, {
|
|
480
|
-
cwd,
|
|
481
|
-
buildCommandArgs: { configFilePath }
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
if (policy.api === "aot") {
|
|
485
|
-
const outputPath = path.join(apiRootPath, HTTP_VALIDATOR_REGISTRY_BUILD_PATH);
|
|
486
|
-
if (!await exists(outputPath)) throw new Error(`Built HTTP validator registry not found: ${outputPath}`);
|
|
487
|
-
assertApiZodCompilerBuildOutput([{
|
|
488
|
-
code: await readFile(outputPath, "utf8"),
|
|
489
|
-
path: outputPath
|
|
490
|
-
}]);
|
|
491
|
-
}
|
|
492
|
-
printBuildSummary("API", true, Date.now() - apiStartedAt);
|
|
493
|
-
} catch (e) {
|
|
494
|
-
printBuildSummary("API", false, Date.now() - apiStartedAt);
|
|
495
|
-
throw e;
|
|
496
|
-
} finally {
|
|
497
|
-
if (temporaryConfigPath !== void 0) await rm(temporaryConfigPath, { force: true });
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* pnpm build web 하면 실행되는 함수입니다.
|
|
502
|
-
* Web 프로젝트만 빌드합니다.
|
|
503
|
-
*
|
|
504
|
-
* Sonamu.init 없이 호출될 것을 상정하여 구현되었습니다.
|
|
505
|
-
*/
|
|
506
|
-
async function build_web({ skipIfMissing = false } = {}) {
|
|
507
|
-
const appRoot = findAppRootPath();
|
|
508
|
-
const webPath = path.join(appRoot, "web");
|
|
509
|
-
if (!await exists(webPath)) {
|
|
510
|
-
if (skipIfMissing) {
|
|
511
|
-
console.log(chalk.gray("Web 디렉토리가 없으므로 Web 빌드를 건너뜁니다."));
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
console.error(`web 디렉토리를 찾을 수 없습니다: ${webPath}`);
|
|
515
|
-
process.exit(1);
|
|
516
|
-
}
|
|
517
|
-
const webStartedAt = Date.now();
|
|
518
|
-
try {
|
|
519
|
-
for (const artifact of WEB_ARTIFACTS) {
|
|
520
|
-
const cwd = path.join(appRoot, artifact.projectPath);
|
|
521
|
-
printTaskHeader(artifact.name, artifact.description, cwd);
|
|
522
|
-
await runBuildSteps(artifact, {
|
|
523
|
-
cwd,
|
|
524
|
-
buildCommandArgs: {}
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
printBuildSummary("Web", true, Date.now() - webStartedAt);
|
|
528
|
-
} catch (e) {
|
|
529
|
-
printBuildSummary("Web", false, Date.now() - webStartedAt);
|
|
530
|
-
console.error(e);
|
|
531
|
-
process.exit(1);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* pre-build, build, post-build 단계를 순차적으로 실행합니다.
|
|
536
|
-
*/
|
|
537
|
-
async function runBuildSteps(artifact, options) {
|
|
538
|
-
const steps = [
|
|
539
|
-
{
|
|
540
|
-
name: "pre-build",
|
|
541
|
-
cmd: artifact.preBuildCommand?.()
|
|
542
|
-
},
|
|
543
|
-
{
|
|
544
|
-
name: "build",
|
|
545
|
-
cmd: artifact.buildCommand(options.buildCommandArgs)
|
|
546
|
-
},
|
|
547
|
-
{
|
|
548
|
-
name: "post-build",
|
|
549
|
-
cmd: artifact.postBuildCommand?.()
|
|
550
|
-
}
|
|
551
|
-
].filter((step) => step.cmd);
|
|
552
|
-
for (let i = 0; i < steps.length; i++) {
|
|
553
|
-
const step = steps[i];
|
|
554
|
-
const isLast = i === steps.length - 1;
|
|
555
|
-
try {
|
|
556
|
-
assert(step.cmd);
|
|
557
|
-
printTaskStart(step.name, step.cmd, isLast);
|
|
558
|
-
await execWithLinePrefix(step.cmd, { cwd: options.cwd });
|
|
559
|
-
printTaskSuccess(step.name, isLast);
|
|
560
|
-
} catch (e) {
|
|
561
|
-
printTaskFailed(step.name, isLast);
|
|
562
|
-
throw new Error(`${step.name} failed`, { cause: e });
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* pnpm start 하면 실행되는 함수입니다.
|
|
568
|
-
* 빌드된 프로젝트를 실행합니다.
|
|
569
|
-
*
|
|
570
|
-
* 빌드된 결과물(dist 디렉토리의 index.js 엔트리포인트)이 없다면 실행을 중단합니다.
|
|
571
|
-
* 소스맵 지원과 dotenv 지원을 포함하여 실행합니다.
|
|
572
|
-
*
|
|
573
|
-
* Sonamu.init 없이 호출될 것을 상정하여 구현되었습니다.
|
|
574
|
-
*/
|
|
575
|
-
async function start() {
|
|
576
|
-
const { version } = createRequire(import.meta.url)("../../package.json");
|
|
577
|
-
console.log(`🌲 Sonamu v${version}\n`);
|
|
578
|
-
const apiRoot = findApiRootPath();
|
|
579
|
-
const entryPoint = "dist/index.js";
|
|
580
|
-
if (!await exists(entryPoint)) {
|
|
581
|
-
console.log(chalk.red(`${entryPoint} not found. Please build your project first.`));
|
|
582
|
-
console.log(chalk.blue("Run: pnpm sonamu build"));
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
const serverProcess = spawn(process.execPath, [
|
|
586
|
-
"--enable-source-maps",
|
|
587
|
-
"-r",
|
|
588
|
-
"dotenv/config",
|
|
589
|
-
entryPoint
|
|
590
|
-
], {
|
|
591
|
-
cwd: apiRoot,
|
|
592
|
-
stdio: "inherit"
|
|
593
|
-
});
|
|
594
|
-
process.on("SIGINT", () => {
|
|
595
|
-
serverProcess.kill("SIGTERM");
|
|
596
|
-
process.exit(0);
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
async function setupMigrator() {
|
|
600
|
-
migrator = new Migrator();
|
|
601
|
-
}
|
|
602
|
-
async function setupFixtureManager() {
|
|
603
|
-
FixtureManager.init();
|
|
604
|
-
}
|
|
605
|
-
async function migrate_apply(targets) {
|
|
606
|
-
await setupMigrator();
|
|
607
|
-
await migrator.apply(targets);
|
|
608
|
-
}
|
|
609
|
-
async function migrate_run() {
|
|
610
|
-
await setupMigrator();
|
|
611
|
-
await migrator.apply(getMigrateRunTargets());
|
|
612
|
-
}
|
|
613
|
-
async function migrate_generate() {
|
|
614
|
-
await setupMigrator();
|
|
615
|
-
const { conns } = await migrator.getStatus();
|
|
616
|
-
if (!conns.some((conn) => conn.status === 0)) {
|
|
617
|
-
console.log(chalk.red("마이그레이션 파일을 생성하려면 기존 마이그레이션이 최소 하나의 DB에 모두 적용되어 있어야 합니다."));
|
|
618
|
-
for (const conn of conns) if (conn.pending.length > 0) console.log(chalk.yellow(` ${conn.name}: pending ${conn.pending.length}개`));
|
|
619
|
-
process.exit(1);
|
|
620
|
-
}
|
|
621
|
-
const count = await migrator.generatePreparedCodes();
|
|
622
|
-
if (count > 0) console.log(chalk.green(`${count}개의 마이그레이션 파일이 생성되었습니다.`));
|
|
623
|
-
}
|
|
624
|
-
async function migrate_status() {
|
|
625
|
-
await setupMigrator();
|
|
626
|
-
const status = await migrator.getStatus();
|
|
627
|
-
console.log(status);
|
|
628
|
-
}
|
|
629
|
-
async function fixture_init() {
|
|
630
|
-
const srcConfig = Sonamu.dbConfig[getSonamuEnvironment()];
|
|
631
|
-
const targets = [{
|
|
632
|
-
label: "(REMOTE) Fixture DB",
|
|
633
|
-
config: Sonamu.dbConfig.fixture
|
|
634
|
-
}, {
|
|
635
|
-
label: "(LOCAL) Testing DB",
|
|
636
|
-
config: Sonamu.dbConfig.test,
|
|
637
|
-
toSkip: (() => {
|
|
638
|
-
const remoteConn = Sonamu.dbConfig.fixture.connection;
|
|
639
|
-
const localConn = Sonamu.dbConfig.test.connection;
|
|
640
|
-
return remoteConn.host === localConn.host && remoteConn.database === localConn.database;
|
|
641
|
-
})()
|
|
642
|
-
}];
|
|
643
|
-
console.log("DUMP...");
|
|
644
|
-
const dumpFilename = `/tmp/sonamu-fixture-init-${Date.now()}.sql`;
|
|
645
|
-
const srcConn = srcConfig.connection;
|
|
646
|
-
const migrationsDump = `/tmp/sonamu-fixture-init-migrations-${Date.now()}.sql`;
|
|
647
|
-
await writeMysqlDump([
|
|
648
|
-
...mysqlConnectionArgs(srcConn),
|
|
649
|
-
"--single-transaction",
|
|
650
|
-
"-d",
|
|
651
|
-
"--no-create-db",
|
|
652
|
-
"--triggers",
|
|
653
|
-
String(srcConn.database)
|
|
654
|
-
], dumpFilename);
|
|
655
|
-
const sourceDb = knex(srcConfig);
|
|
656
|
-
const [[migrations]] = await sourceDb.raw("SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = ? AND table_name = 'knex_migrations'", [srcConn.database]);
|
|
657
|
-
if (migrations.count > 0) await writeMysqlDump([
|
|
658
|
-
...mysqlConnectionArgs(srcConn),
|
|
659
|
-
"--single-transaction",
|
|
660
|
-
"--no-create-db",
|
|
661
|
-
"--triggers",
|
|
662
|
-
String(srcConn.database),
|
|
663
|
-
"knex_migrations",
|
|
664
|
-
"knex_migrations_lock"
|
|
665
|
-
], migrationsDump);
|
|
666
|
-
for (const { label, config, toSkip } of targets) {
|
|
667
|
-
const conn = config.connection;
|
|
668
|
-
if (toSkip === true) {
|
|
669
|
-
console.log(chalk.red(`${label}: Skipped!`));
|
|
670
|
-
continue;
|
|
671
|
-
}
|
|
672
|
-
const databaseConnection = config.connection ?? {};
|
|
673
|
-
const db = knex({
|
|
674
|
-
...config,
|
|
675
|
-
connection: {
|
|
676
|
-
...databaseConnection,
|
|
677
|
-
database: void 0
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
const [[row]] = await db.raw(`SHOW DATABASES LIKE "${conn.database}"`);
|
|
681
|
-
if (row) {
|
|
682
|
-
console.log(chalk.yellow(`${label}: Database "${conn.database}" Already exists`));
|
|
683
|
-
await db.destroy();
|
|
684
|
-
continue;
|
|
685
|
-
}
|
|
686
|
-
console.log(`SYNC to ${label}...`);
|
|
687
|
-
const connectionArgs = mysqlConnectionArgs(conn);
|
|
688
|
-
const database = String(conn.database);
|
|
689
|
-
const escapedDatabase = database.replaceAll("`", "``");
|
|
690
|
-
runMysql([
|
|
691
|
-
...connectionArgs,
|
|
692
|
-
"-e",
|
|
693
|
-
`DROP DATABASE IF EXISTS \`${escapedDatabase}\``
|
|
694
|
-
]);
|
|
695
|
-
runMysql([
|
|
696
|
-
...connectionArgs,
|
|
697
|
-
"-e",
|
|
698
|
-
`CREATE DATABASE \`${escapedDatabase}\``
|
|
699
|
-
]);
|
|
700
|
-
await runMysqlWithInput([...connectionArgs, database], dumpFilename);
|
|
701
|
-
if (await exists(migrationsDump)) await runMysqlWithInput([...connectionArgs, database], migrationsDump);
|
|
702
|
-
await db.destroy();
|
|
703
|
-
}
|
|
704
|
-
await sourceDb.destroy();
|
|
705
|
-
}
|
|
706
|
-
async function fixture_import(entityId, recordIds) {
|
|
707
|
-
await setupFixtureManager();
|
|
708
|
-
await FixtureManager.importFixture(entityId, recordIds);
|
|
709
|
-
await FixtureManager.sync();
|
|
710
|
-
}
|
|
711
|
-
async function fixture_sync() {
|
|
712
|
-
await setupFixtureManager();
|
|
713
|
-
await FixtureManager.sync();
|
|
714
|
-
}
|
|
715
|
-
/**
|
|
716
|
-
* fixture gen 명령어
|
|
717
|
-
* 옵션을 process.argv에서 파싱
|
|
718
|
-
*/
|
|
719
|
-
async function fixture_gen() {
|
|
720
|
-
await fixtureGenCommand(parseOptions(process.argv));
|
|
721
|
-
}
|
|
722
|
-
/**
|
|
723
|
-
* fixture fetch 명령어
|
|
724
|
-
* 옵션을 process.argv에서 파싱
|
|
725
|
-
*/
|
|
726
|
-
async function fixture_fetch() {
|
|
727
|
-
await fixtureFetchCommand(parseOptions(process.argv));
|
|
728
|
-
}
|
|
729
|
-
/**
|
|
730
|
-
* fixture explore 명령어
|
|
731
|
-
* 옵션을 process.argv에서 파싱
|
|
732
|
-
*/
|
|
733
|
-
async function fixture_explore() {
|
|
734
|
-
await fixtureExploreCommand(parseOptions(process.argv));
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* 간단한 옵션 파서
|
|
738
|
-
*/
|
|
739
|
-
function parseOptions(argv) {
|
|
740
|
-
const options = { _: [] };
|
|
741
|
-
for (let i = 0; i < argv.length; i++) {
|
|
742
|
-
const arg = argv[i];
|
|
743
|
-
if (arg.startsWith("--")) {
|
|
744
|
-
const key = arg.slice(2);
|
|
745
|
-
if (key.includes("=")) {
|
|
746
|
-
const [k, v] = key.split("=");
|
|
747
|
-
options[k] = v;
|
|
748
|
-
} else {
|
|
749
|
-
const next = argv[i + 1];
|
|
750
|
-
if (next && !next.startsWith("--")) {
|
|
751
|
-
options[key] = next;
|
|
752
|
-
i++;
|
|
753
|
-
} else options[key] = true;
|
|
754
|
-
}
|
|
755
|
-
} else if (arg.startsWith("-")) {
|
|
756
|
-
const key = arg.slice(1);
|
|
757
|
-
const next = argv[i + 1];
|
|
758
|
-
if (next && !next.startsWith("-")) {
|
|
759
|
-
options[key] = next;
|
|
760
|
-
i++;
|
|
761
|
-
} else options[key] = true;
|
|
762
|
-
} else options._.push(arg);
|
|
763
|
-
}
|
|
764
|
-
return options;
|
|
765
|
-
}
|
|
766
|
-
async function stub_practice(name) {
|
|
767
|
-
const practiceDir = path.join(Sonamu.apiRootPath, "src", "practices");
|
|
768
|
-
const fileNames = await readdir(practiceDir);
|
|
769
|
-
const fileName = `p${await (async () => {
|
|
770
|
-
if (!await exists(practiceDir)) await mkdir(practiceDir, { recursive: true });
|
|
771
|
-
const filteredSeqs = fileNames.filter((fileName) => fileName.startsWith("p") && fileName.endsWith(".ts")).map((fileName) => {
|
|
772
|
-
const [, seqNo] = fileName.match(/^p([0-9]+)-/) ?? ["0", "0"];
|
|
773
|
-
return parseInt(seqNo);
|
|
774
|
-
}).toSorted((a, b) => b - a);
|
|
775
|
-
if (filteredSeqs.length > 0) return filteredSeqs[0];
|
|
776
|
-
return 0;
|
|
777
|
-
})() + 1}-${name}.ts`;
|
|
778
|
-
const dstPath = path.join(practiceDir, fileName);
|
|
779
|
-
await writeFile(dstPath, [
|
|
780
|
-
`import { Sonamu } from "sonamu";`,
|
|
781
|
-
"",
|
|
782
|
-
`console.clear();`,
|
|
783
|
-
`console.log("${fileName}");`,
|
|
784
|
-
"",
|
|
785
|
-
`Sonamu.runScript(async () => {`,
|
|
786
|
-
` // TODO`,
|
|
787
|
-
`});`,
|
|
788
|
-
""
|
|
789
|
-
].join("\n"));
|
|
790
|
-
openInCode(dstPath);
|
|
791
|
-
const runCode = `yarn node -r dotenv/config --enable-source-maps dist/practices/${fileName.replace(".ts", ".js")}`;
|
|
792
|
-
console.log(`${chalk.blue(runCode)} copied to clipboard.`);
|
|
793
|
-
copyToClipboard(runCode);
|
|
794
|
-
}
|
|
795
|
-
async function stub_entity(entityId) {
|
|
796
|
-
await Sonamu.syncer.createEntity({
|
|
797
|
-
entityId,
|
|
798
|
-
title: entityId
|
|
799
|
-
});
|
|
800
|
-
const { flags } = parseCliOptions();
|
|
801
|
-
const useAI = flags.has("ai");
|
|
802
|
-
if (flags.has("no-cones")) {
|
|
803
|
-
console.log(`✓ Entity '${entityId}' created without cones`);
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
const entity = EntityManager.get(entityId);
|
|
807
|
-
if (!entity) {
|
|
808
|
-
console.error(`Entity not found: ${entityId}`);
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
if (useAI) {
|
|
812
|
-
console.log(`✓ Entity '${entityId}' created`);
|
|
813
|
-
console.log(`🌟 Generating AI-powered cones...`);
|
|
814
|
-
try {
|
|
815
|
-
const configLocale = Sonamu.config.i18n?.defaultLocale;
|
|
816
|
-
const locale = configLocale === "ko" || configLocale === "en" || configLocale === "ja" ? configLocale : "ko";
|
|
817
|
-
const result = await entity.generateCones({
|
|
818
|
-
preserveExisting: false,
|
|
819
|
-
onlyEmpty: false,
|
|
820
|
-
locale
|
|
821
|
-
});
|
|
822
|
-
console.log(`✅ Done (${result.tokensUsed} tokens)`);
|
|
823
|
-
} catch (error) {
|
|
824
|
-
if (error instanceof Error && error.message.includes("ANTHROPIC_API_KEY")) {
|
|
825
|
-
console.error(`\n❌ ${error.message}`);
|
|
826
|
-
console.error(`\n💡 Remove --ai flag to use template cones instead`);
|
|
827
|
-
} else throw error;
|
|
828
|
-
}
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
console.log(`🌟 Generating template cones...`);
|
|
832
|
-
await entity.generateTemplateCones();
|
|
833
|
-
console.log(`✓ Entity '${entityId}' created with template cones`);
|
|
834
|
-
console.log(`💡 Tip: Run 'pnpm sonamu cone gen ${entityId}' to improve with AI`);
|
|
835
|
-
}
|
|
836
|
-
/**
|
|
837
|
-
* AI를 사용하여 entity의 cone 메타데이터를 생성하거나 업그레이드합니다.
|
|
838
|
-
*
|
|
839
|
-
* 옵션:
|
|
840
|
-
* - --regenerate: 전체 재생성 (기존 cone 덮어쓰기)
|
|
841
|
-
* - --locale <ko|en|ja>: 생성 언어 지정
|
|
842
|
-
* - 기본: onlyEmpty 모드 (기존 fixtureHint 보존)
|
|
843
|
-
*
|
|
844
|
-
* ANTHROPIC_API_KEY 필요 (sonamu.secret.ts 또는 환경변수)
|
|
845
|
-
*/
|
|
846
|
-
async function cone_gen(entityId) {
|
|
847
|
-
const { flags, options } = parseCliOptions();
|
|
848
|
-
if (flags.has("all") || entityId === "all") {
|
|
849
|
-
const allEntities = EntityManager.getAllEntities();
|
|
850
|
-
console.log(`🌟 Generating AI-powered cones for ${allEntities.length} entities...\n`);
|
|
851
|
-
let totalTokens = 0;
|
|
852
|
-
const errors = [];
|
|
853
|
-
for (const entity of allEntities) try {
|
|
854
|
-
console.log(`Processing ${entity.id}...`);
|
|
855
|
-
const configLocale = options.locale || Sonamu.config.i18n?.defaultLocale;
|
|
856
|
-
const locale = configLocale === "ko" || configLocale === "en" || configLocale === "ja" ? configLocale : "ko";
|
|
857
|
-
const result = await entity.generateCones({
|
|
858
|
-
preserveExisting: !flags.has("regenerate"),
|
|
859
|
-
onlyEmpty: !flags.has("regenerate"),
|
|
860
|
-
locale
|
|
861
|
-
});
|
|
862
|
-
totalTokens += result.tokensUsed;
|
|
863
|
-
console.log(` ✓ ${entity.id} (${result.tokensUsed} tokens)\n`);
|
|
864
|
-
} catch (error) {
|
|
865
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
866
|
-
errors.push(`${entity.id}: ${message}`);
|
|
867
|
-
console.error(` ✗ ${entity.id}: ${message}\n`);
|
|
868
|
-
}
|
|
869
|
-
console.log(`\n✅ Done! Total: ${totalTokens} tokens used`);
|
|
870
|
-
const estimatedCost = totalTokens * 9 / 1e6;
|
|
871
|
-
console.log(`💰 Estimated cost: ~$${estimatedCost.toFixed(4)}`);
|
|
872
|
-
if (errors.length > 0) {
|
|
873
|
-
console.error(`\n❌ Failed entities (${errors.length}):`);
|
|
874
|
-
for (const err of errors) console.error(` - ${err}`);
|
|
875
|
-
}
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
const entity = EntityManager.get(entityId);
|
|
879
|
-
if (!entity) {
|
|
880
|
-
console.error(`Entity not found: ${entityId}`);
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
const mode = flags.has("regenerate") ? "regenerating" : "generating";
|
|
884
|
-
console.log(`🌟 ${mode === "regenerating" ? "Regenerating" : "Generating"} AI-powered cones for ${entityId}...`);
|
|
885
|
-
try {
|
|
886
|
-
const configLocale = options.locale || Sonamu.config.i18n?.defaultLocale;
|
|
887
|
-
const locale = configLocale === "ko" || configLocale === "en" || configLocale === "ja" ? configLocale : "ko";
|
|
888
|
-
const result = await entity.generateCones({
|
|
889
|
-
preserveExisting: !flags.has("regenerate"),
|
|
890
|
-
onlyEmpty: !flags.has("regenerate"),
|
|
891
|
-
locale
|
|
892
|
-
});
|
|
893
|
-
console.log(`✅ Done! (${result.tokensUsed} tokens used)`);
|
|
894
|
-
const estimatedCost = result.tokensUsed * 9 / 1e6;
|
|
895
|
-
console.log(`💰 Estimated cost: ~$${estimatedCost.toFixed(4)}`);
|
|
896
|
-
} catch (error) {
|
|
897
|
-
if (error instanceof Error) if (error.message.includes("ANTHROPIC_API_KEY")) {
|
|
898
|
-
console.error(`\n❌ ${error.message}`);
|
|
899
|
-
console.error(`\n💡 To use AI-powered cone generation:`);
|
|
900
|
-
console.error(` 1. Get an API key from https://console.anthropic.com/`);
|
|
901
|
-
console.error(` 2. Add it to sonamu.secret.ts or set ANTHROPIC_API_KEY environment variable`);
|
|
902
|
-
} else if (error.message.includes("Rate limit")) {
|
|
903
|
-
console.error(`\n❌ ${error.message}`);
|
|
904
|
-
console.error(`\n💡 Please wait a moment and try again.`);
|
|
905
|
-
} else console.error(`\n❌ Failed to generate cones: ${error.message}`);
|
|
906
|
-
else console.error(`\n❌ Failed to generate cones: Unknown error`);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
async function scaffold_model(entityId) {
|
|
910
|
-
await Sonamu.syncer.generateTemplate("model", { entityId });
|
|
911
|
-
}
|
|
912
|
-
async function scaffold_model_test(entityId) {
|
|
913
|
-
await Sonamu.syncer.generateTemplate("model_test", { entityId });
|
|
914
|
-
}
|
|
915
|
-
async function scaffold_view_list(entityId) {
|
|
916
|
-
await Sonamu.syncer.generateTemplate("view_list", {
|
|
917
|
-
entityId,
|
|
918
|
-
extra: void 0
|
|
919
|
-
});
|
|
920
|
-
}
|
|
921
|
-
async function scaffold_view_form(entityId) {
|
|
922
|
-
await Sonamu.syncer.generateTemplate("view_form", { entityId });
|
|
923
|
-
}
|
|
924
|
-
async function auth_generate() {
|
|
925
|
-
const pluginsArg = process.argv.find((arg) => arg.startsWith("--plugins"));
|
|
926
|
-
const plugins = [];
|
|
927
|
-
if (pluginsArg) {
|
|
928
|
-
const pluginValue = pluginsArg.includes("=") ? pluginsArg.split("=")[1] : process.argv[process.argv.indexOf(pluginsArg) + 1];
|
|
929
|
-
if (pluginValue) {
|
|
930
|
-
const pluginIds = pluginValue.split(",").map((p) => p.trim());
|
|
931
|
-
for (const id of pluginIds) if (isValidPluginId(id)) plugins.push(id);
|
|
932
|
-
else {
|
|
933
|
-
console.log(chalk.yellow(`⚠ Unknown plugin: ${id}`));
|
|
934
|
-
console.log(chalk.dim(` Supported plugins: ${SUPPORTED_PLUGIN_IDS.join(", ")}`));
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
console.log(chalk.yellow.bold("🔐 Generating better-auth entities...\n"));
|
|
939
|
-
if (plugins.length > 0) console.log(chalk.dim(` Plugins: ${plugins.join(", ")}`));
|
|
940
|
-
await generateBetterAuthEntities({ plugins });
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* pnpm sonamu auth add-companions 하면 실행되는 함수입니다.
|
|
944
|
-
* 기존 프로젝트의 entity.json에 fixtureCompanions를 소급 추가합니다.
|
|
945
|
-
*
|
|
946
|
-
* 이미 fixtureCompanions가 있는 entity는 스킵합니다 (덮어쓰기 없음).
|
|
947
|
-
*/
|
|
948
|
-
async function auth_add_companions() {
|
|
949
|
-
console.log(chalk.yellow.bold("🔐 Adding fixtureCompanions to better-auth entities...\n"));
|
|
950
|
-
await addCompanionsToEntities();
|
|
951
|
-
console.log(chalk.bold("\n✅ Done!"));
|
|
952
|
-
}
|
|
953
|
-
//#endregion
|
|
954
|
-
export {};
|
|
955
|
-
|
|
956
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9iaW4vY2xpLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBhc3NlcnQgZnJvbSBcImFzc2VydFwiO1xuaW1wb3J0IHsgc3Bhd24sIHNwYXduU3luYyB9IGZyb20gXCJjaGlsZF9wcm9jZXNzXCI7XG5pbXBvcnQgeyBta2Rpciwgb3BlbiwgcmVhZEZpbGUsIHJlYWRkaXIsIHJtLCB3cml0ZUZpbGUgfSBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCB7IGNyZWF0ZVJlcXVpcmUgfSBmcm9tIFwibW9kdWxlXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuaW1wb3J0IHByb2Nlc3MgZnJvbSBcInByb2Nlc3NcIjtcblxuaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiO1xuaW1wb3J0IGtuZXggZnJvbSBcImtuZXhcIjtcbmltcG9ydCB7IHR5cGUgS25leCB9IGZyb20gXCJrbmV4XCI7XG5pbXBvcnQgeyB0c2ljbGkgfSBmcm9tIFwidHNpY2xpXCI7XG5cbmltcG9ydCB7IFNvbmFtdSB9IGZyb20gXCIuLi9hcGkvc29uYW11XCI7XG5pbXBvcnQgeyBhZGRDb21wYW5pb25zVG9FbnRpdGllcywgZ2VuZXJhdGVCZXR0ZXJBdXRoRW50aXRpZXMgfSBmcm9tIFwiLi4vYXV0aC9hdXRoLWdlbmVyYXRvclwiO1xuaW1wb3J0IHsgaXNWYWxpZFBsdWdpbklkLCBTVVBQT1JURURfUExVR0lOX0lEUyB9IGZyb20gXCIuLi9hdXRoL3BsdWdpbnMvZW50aXR5LWRlZmluaXRpb25zXCI7XG5pbXBvcnQgeyB0eXBlIEJldHRlckF1dGhQbHVnaW5JZCB9IGZyb20gXCIuLi9hdXRoL3BsdWdpbnMvZW50aXR5LWRlZmluaXRpb25zXCI7XG5pbXBvcnQgeyB0eXBlIFNvbmFtdURCQ29uZmlnIH0gZnJvbSBcIi4uL2RhdGFiYXNlL2RiXCI7XG5pbXBvcnQgeyBFbnRpdHlNYW5hZ2VyIH0gZnJvbSBcIi4uL2VudGl0eS9lbnRpdHktbWFuYWdlclwiO1xuaW1wb3J0IHsgZ2V0U29uYW11RW52aXJvbm1lbnQgfSBmcm9tIFwiLi4vZW52XCI7XG5pbXBvcnQgeyBNaWdyYXRvciB9IGZyb20gXCIuLi9taWdyYXRpb24vbWlncmF0b3JcIjtcbmltcG9ydCB7IEZpeHR1cmVNYW5hZ2VyIH0gZnJvbSBcIi4uL3Rlc3RpbmcvZml4dHVyZS1tYW5hZ2VyXCI7XG5pbXBvcnQge1xuICBleGVjV2l0aExpbmVQcmVmaXgsXG4gIHByaW50QnVpbGRTdW1tYXJ5LFxuICBwcmludFRhc2tGYWlsZWQsXG4gIHByaW50VGFza0hlYWRlcixcbiAgcHJpbnRUYXNrU3RhcnQsXG4gIHByaW50VGFza1N1Y2Nlc3MsXG59IGZyb20gXCIuLi91dGlscy9jb25zb2xlLXV0aWxcIjtcbmltcG9ydCB7IGV4aXN0cyB9IGZyb20gXCIuLi91dGlscy9mcy11dGlsc1wiO1xuaW1wb3J0IHsgZmluZEFwaVJvb3RQYXRoLCBmaW5kQXBwUm9vdFBhdGggfSBmcm9tIFwiLi4vdXRpbHMvdXRpbHNcIjtcbmltcG9ydCB7IEFQSV9BUlRJRkFDVFMsIFdFQl9BUlRJRkFDVFMgfSBmcm9tIFwiLi9idWlsZC1jb25maWdcIjtcbmltcG9ydCB7IHR5cGUgQnVpbGRBcnRpZmFjdCB9IGZyb20gXCIuL2J1aWxkLWNvbmZpZ1wiO1xuaW1wb3J0IHtcbiAgYXNzZXJ0QXBpWm9kQ29tcGlsZXJCdWlsZE91dHB1dCxcbiAgY3JlYXRlQXBpWm9kQ29tcGlsZXJCdWlsZFdyYXBwZXIsXG4gIEhUVFBfVkFMSURBVE9SX1JFR0lTVFJZX0JVSUxEX1BBVEgsXG4gIEhUVFBfVkFMSURBVE9SX1JFR0lTVFJZX1NPVVJDRV9QQVRILFxufSBmcm9tIFwiLi9idWlsZC1jb25maWdcIjtcbmltcG9ydCB7IGxvYWRCdWlsZENvbXBpbGVyUG9saWN5IH0gZnJvbSBcIi4vY29tcGlsZXItcG9saWN5XCI7XG5pbXBvcnQgeyBmaXh0dXJlRXhwbG9yZUNvbW1hbmQsIGZpeHR1cmVGZXRjaENvbW1hbmQsIGZpeHR1cmVHZW5Db21tYW5kIH0gZnJvbSBcIi4vZml4dHVyZVwiO1xuaW1wb3J0IHsgZ2V0TWlncmF0ZVJ1blRhcmdldHMgfSBmcm9tIFwiLi9taWdyYXRlLXRhcmdldHNcIjtcbmltcG9ydCB7IHRlc3RDb21tYW5kIH0gZnJvbSBcIi4vdGVzdC1jb21tYW5kXCI7XG5cbmxldCBtaWdyYXRvcjogTWlncmF0b3I7XG5cbmZ1bmN0aW9uIHJ1bk15c3FsKFxuICBhcmdzOiBzdHJpbmdbXSxcbiAgc3RkaW86IFtudW1iZXIgfCBcImlnbm9yZVwiLCBudW1iZXIgfCBcImluaGVyaXRcIiwgXCJpbmhlcml0XCJdID0gW1wiaWdub3JlXCIsIFwiaW5oZXJpdFwiLCBcImluaGVyaXRcIl0sXG4pIHtcbiAgY29uc3QgcmVzdWx0ID0gc3Bhd25TeW5jKFwibXlzcWxcIiwgYXJncywgeyBzdGRpbyB9KTtcbiAgaWYgKHJlc3VsdC5lcnJvcikge1xuICAgIHRocm93IHJlc3VsdC5lcnJvcjtcbiAgfVxuICBpZiAocmVzdWx0LnN0YXR1cyAhPT0gMCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgbXlzcWwg66qF66C57J20IOyiheujjCDsvZTrk5wgJHtyZXN1bHQuc3RhdHVzID8/IFwidW5rbm93blwifeuhnCDsi6TtjKjtlojsirXri4jri6QuYCk7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gd3JpdGVNeXNxbER1bXAoYXJnczogc3RyaW5nW10sIG91dHB1dFBhdGg6IHN0cmluZykge1xuICBjb25zdCBvdXRwdXQgPSBhd2FpdCBvcGVuKG91dHB1dFBhdGgsIFwid1wiKTtcbiAgdHJ5IHtcbiAgICBjb25zdCByZXN1bHQgPSBzcGF3blN5bmMoXCJteXNxbGR1bXBcIiwgYXJncywgeyBzdGRpbzogW1wiaWdub3JlXCIsIG91dHB1dC5mZCwgXCJpbmhlcml0XCJdIH0pO1xuICAgIGlmIChyZXN1bHQuZXJyb3IpIHtcbiAgICAgIHRocm93IHJlc3VsdC5lcnJvcjtcbiAgICB9XG4gICAgaWYgKHJlc3VsdC5zdGF0dXMgIT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgbXlzcWxkdW1wIOuqheugueydtCDsooXro4wg7L2U65OcICR7cmVzdWx0LnN0YXR1cyA/PyBcInVua25vd25cIn3roZwg7Iuk7Yyo7ZaI7Iq164uI64ukLmApO1xuICAgIH1cbiAgfSBmaW5hbGx5IHtcbiAgICBhd2FpdCBvdXRwdXQuY2xvc2UoKTtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBydW5NeXNxbFdpdGhJbnB1dChhcmdzOiBzdHJpbmdbXSwgaW5wdXRQYXRoOiBzdHJpbmcpIHtcbiAgY29uc3QgaW5wdXQgPSBhd2FpdCBvcGVuKGlucHV0UGF0aCwgXCJyXCIpO1xuICB0cnkge1xuICAgIHJ1bk15c3FsKGFyZ3MsIFtpbnB1dC5mZCwgXCJpbmhlcml0XCIsIFwiaW5oZXJpdFwiXSk7XG4gIH0gZmluYWxseSB7XG4gICAgYXdhaXQgaW5wdXQuY2xvc2UoKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBvcGVuSW5Db2RlKHBhdGhUb09wZW46IHN0cmluZykge1xuICBjb25zdCByZXN1bHQgPSBzcGF3blN5bmMoXCJjb2RlXCIsIFtwYXRoVG9PcGVuXSwgeyBzdGRpbzogW1wiaWdub3JlXCIsIFwiaW5oZXJpdFwiLCBcImluaGVyaXRcIl0gfSk7XG4gIGlmIChyZXN1bHQuZXJyb3IpIHtcbiAgICB0aHJvdyByZXN1bHQuZXJyb3I7XG4gIH1cbiAgaWYgKHJlc3VsdC5zdGF0dXMgIT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYGNvZGUg66qF66C57J20IOyiheujjCDsvZTrk5wgJHtyZXN1bHQuc3RhdHVzID8/IFwidW5rbm93blwifeuhnCDsi6TtjKjtlojsirXri4jri6QuYCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gbXlzcWxDb25uZWN0aW9uQXJncyhjb25uZWN0aW9uOiBLbmV4LkNvbm5lY3Rpb25Db25maWcpIHtcbiAgcmV0dXJuIFtcbiAgICBgLWgke1N0cmluZyhjb25uZWN0aW9uLmhvc3QpfWAsXG4gICAgYC11JHtTdHJpbmcoY29ubmVjdGlvbi51c2VyKX1gLFxuICAgIGAtcCR7U3RyaW5nKGNvbm5lY3Rpb24ucGFzc3dvcmQpfWAsXG4gIF07XG59XG5cbmZ1bmN0aW9uIGNvcHlUb0NsaXBib2FyZCh2YWx1ZTogc3RyaW5nKSB7XG4gIGNvbnN0IHJlc3VsdCA9IHNwYXduU3luYyhcInBiY29weVwiLCBbXSwgeyBpbnB1dDogdmFsdWUsIHN0ZGlvOiBbXCJwaXBlXCIsIFwiaW5oZXJpdFwiLCBcImluaGVyaXRcIl0gfSk7XG4gIGlmIChyZXN1bHQuZXJyb3IpIHtcbiAgICB0aHJvdyByZXN1bHQuZXJyb3I7XG4gIH1cbiAgaWYgKHJlc3VsdC5zdGF0dXMgIT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYHBiY29weSDrqoXroLnsnbQg7KKF66OMIOy9lOuTnCAke3Jlc3VsdC5zdGF0dXMgPz8gXCJ1bmtub3duXCJ966GcIOyLpO2MqO2WiOyKteuLiOuLpC5gKTtcbiAgfVxufVxuXG4vKipcbiAqIENMSSDsmLXshZjsnYQg7YyM7Iux7ZWY64qUIO2XrO2NvCDtlajsiJhcbiAqL1xuZnVuY3Rpb24gcGFyc2VDbGlPcHRpb25zKGFyZ3Y6IHN0cmluZ1tdID0gcHJvY2Vzcy5hcmd2KSB7XG4gIGNvbnN0IGZsYWdzID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gIGNvbnN0IG9wdGlvbnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyZ3YubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBhcmcgPSBhcmd2W2ldO1xuICAgIGlmICghYXJnLnN0YXJ0c1dpdGgoXCItLVwiKSkgY29udGludWU7XG5cbiAgICAvLyAtLW9wdGlvbj12YWx1ZSDtmJXsi51cbiAgICBpZiAoYXJnLmluY2x1ZGVzKFwiPVwiKSkge1xuICAgICAgY29uc3QgW2tleSwgdmFsdWVdID0gYXJnLnNsaWNlKDIpLnNwbGl0KFwiPVwiKTtcbiAgICAgIG9wdGlvbnNba2V5XSA9IHZhbHVlO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gLS1vcHRpb24gdmFsdWUg7ZiV7Iud7J247KeAIO2ZleyduFxuICAgIGNvbnN0IG5leHRBcmcgPSBhcmd2W2kgKyAxXTtcbiAgICBpZiAobmV4dEFyZyAmJiAhbmV4dEFyZy5zdGFydHNXaXRoKFwiLS1cIikgJiYgIW5leHRBcmcuc3RhcnRzV2l0aChcIi1cIikpIHtcbiAgICAgIG9wdGlvbnNbYXJnLnNsaWNlKDIpXSA9IG5leHRBcmc7XG4gICAgICBpKys7IC8vIOuLpOydjCBhcmfripQg6rCS7J2066+A66GcIOyKpO2CtVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyAtLWZsYWcg7ZiV7IudXG4gICAgICBmbGFncy5hZGQoYXJnLnNsaWNlKDIpKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4geyBmbGFncywgb3B0aW9ucyB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBib290c3RyYXAoKSB7XG4gIGNvbnN0IG5vdFRvSW5pdCA9IFtcImRldlwiLCBcImJ1aWxkXCIsIFwic3RhcnRcIiwgXCJza2lsbHNcIiwgXCJ0ZXN0XCJdLmluY2x1ZGVzKHByb2Nlc3MuYXJndlsyXSA/PyBcIlwiKTtcbiAgaWYgKCFub3RUb0luaXQpIHtcbiAgICBhd2FpdCBTb25hbXUuaW5pdChmYWxzZSwgZmFsc2UpO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyB0c2ljbGnripQg7KCV7ZmV7ZWcIOuqheugueyWtCDrp6Tsua3rp4wg7KeA7JuQ7ZWY66+A66GcLCAtLeuhnCDsi5zsnpHtlZjripQg7Ji17IWY6rO8IOq3uCDqsJLsnYQg7ZWE7YSw66eB7ZWp64uI64ukLlxuICAgIC8vIOyYteyFmCDtjIzsi7HsnYAg6rCBIHJ1bm5lciDtlajsiJjsl5DshJwg7JuQ67O4IHByb2Nlc3MuYXJnduulvCDsgqzsmqntlZjsl6wg7IiY7ZaJ7ZWp64uI64ukLlxuICAgIC8vIFwiLS1cIihiYXJlIGRvdWJsZSBkYXNoKeuKlCBwYXNzdGhyb3VnaCDqtazrtoTsnpDsnbTrr4DroZwsIOq3uCDrkqTsnZgg66qo65OgIOyduOyekOuPhCDsoJzsmbjtlanri4jri6QuXG4gICAgY29uc3QgZmlsdGVyZWRBcmd2OiBzdHJpbmdbXSA9IFtdO1xuICAgIGxldCBza2lwTmV4dCA9IGZhbHNlO1xuICAgIGxldCBhZnRlckRvdWJsZURhc2ggPSBmYWxzZTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHByb2Nlc3MuYXJndi5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgYXJnID0gcHJvY2Vzcy5hcmd2W2ldO1xuICAgICAgaWYgKGFyZyA9PT0gXCItLVwiKSB7XG4gICAgICAgIGFmdGVyRG91YmxlRGFzaCA9IHRydWU7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKGFmdGVyRG91YmxlRGFzaCkgY29udGludWU7XG4gICAgICBpZiAoc2tpcE5leHQpIHtcbiAgICAgICAgc2tpcE5leHQgPSBmYWxzZTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBpZiAoYXJnLnN0YXJ0c1dpdGgoXCItLVwiKSkge1xuICAgICAgICAvLyAtLW9wdGlvbj12YWx1ZSDtmJXsi53snYAg7J20IGFyZ+unjCDsiqTtgrVcbiAgICAgICAgaWYgKGFyZy5pbmNsdWRlcyhcIj1cIikpIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICAvLyAtLW9wdGlvbiB2YWx1ZSDtmJXsi53snbjsp4Ag7ZmV7J24OiDri6TsnYwgYXJn6rCAIC0t66GcIOyLnOyeke2VmOyngCDslYrsnLzrqbQg6rCS7J20IOyeiOuKlCDqsoNcbiAgICAgICAgY29uc3QgbmV4dEFyZyA9IHByb2Nlc3MuYXJndltpICsgMV07XG4gICAgICAgIGlmIChuZXh0QXJnICYmICFuZXh0QXJnLnN0YXJ0c1dpdGgoXCItLVwiKSAmJiAhbmV4dEFyZy5zdGFydHNXaXRoKFwiLVwiKSkge1xuICAgICAgICAgIHNraXBOZXh0ID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGZpbHRlcmVkQXJndi5wdXNoKGFyZyk7XG4gICAgfVxuXG4gICAgLy8gYnVpbGQvZGV2IOuqheugueyWtOqwgCDshJzruIzsu6Trp6jrk5wg7JeG7J20IO2YuOy2nOuQoCDrlYwgXCJhbGxcIuydhCDquLDrs7jqsJLsnLzroZwg7LaU6rCA7ZWp64uI64ukLlxuICAgIC8vIOyYiDogYHNvbmFtdSBidWlsZGAg4oaSIGBzb25hbXUgYnVpbGQgYWxsYCwgYHNvbmFtdSBkZXZgIOKGkiBgc29uYW11IGRldiBhbGxgXG4gICAgY29uc3QgY21kID0gZmlsdGVyZWRBcmd2WzJdO1xuICAgIGlmICgoY21kID09PSBcImJ1aWxkXCIgfHwgY21kID09PSBcImRldlwiKSAmJiBmaWx0ZXJlZEFyZ3YubGVuZ3RoID09PSAzKSB7XG4gICAgICBmaWx0ZXJlZEFyZ3YucHVzaChcImFsbFwiKTtcbiAgICB9XG5cbiAgICAvLyB0ZXN0IOy7pOunqOuTnOuKlCDqsIDrs4Ag7J247J6QKO2MjOydvOuqhSwgLS1wYXR0ZXJuIOuTsSnrpbwg67Cb7Jy866+A66GcIHRzaWNsaeulvCDsmrDtmoztlanri4jri6QuXG4gICAgaWYgKGNtZCA9PT0gXCJ0ZXN0XCIpIHtcbiAgICAgIHJldHVybiB0ZXN0Q29tbWFuZCgpO1xuICAgIH1cblxuICAgIGF3YWl0IHRzaWNsaShmaWx0ZXJlZEFyZ3YsIHtcbiAgICAgIHR5cGVzOiB7XG4gICAgICAgIFwiI2VudGl0eUlkXCI6IHtcbiAgICAgICAgICB0eXBlOiBcImF1dG9jb21wbGV0ZVwiLFxuICAgICAgICAgIG5hbWU6IFwiI2VudGl0eUlkXCIsXG4gICAgICAgICAgbWVzc2FnZTogXCJQbGVhc2UgaW5wdXQgI2VudGl0eUlkXCIsXG4gICAgICAgICAgY2hvaWNlczogRW50aXR5TWFuYWdlci5nZXRBbGxQYXJlbnRJZHMoKS5tYXAoKGVudGl0eUlkKSA9PiAoe1xuICAgICAgICAgICAgdGl0bGU6IGVudGl0eUlkLFxuICAgICAgICAgICAgdmFsdWU6IGVudGl0eUlkLFxuICAgICAgICAgIH0pKSxcbiAgICAgICAgfSxcbiAgICAgICAgXCIjcmVjb3JkSWRzXCI6IFwibnVtYmVyW11cIixcbiAgICAgICAgXCIjbmFtZVwiOiBcInN0cmluZ1wiLFxuICAgICAgICBcIiN0YXJnZXRzXCI6IHtcbiAgICAgICAgICB0eXBlOiBcIm11bHRpc2VsZWN0XCIsXG4gICAgICAgICAgbmFtZTogXCIjdGFyZ2V0c1wiLFxuICAgICAgICAgIG1lc3NhZ2U6IFwiUGxlYXNlIGlucHV0ICN0YXJnZXRzXCIsXG4gICAgICAgICAgY2hvaWNlczogW1xuICAgICAgICAgICAgeyB0aXRsZTogXCJEZXZlbG9wbWVudFwiLCB2YWx1ZTogXCJkZXZlbG9wbWVudFwiIH0sXG4gICAgICAgICAgICB7IHRpdGxlOiBcIlN0YWdpbmdcIiwgdmFsdWU6IFwic3RhZ2luZ1wiIH0sXG4gICAgICAgICAgICB7IHRpdGxlOiBcIlByb2R1Y3Rpb25cIiwgdmFsdWU6IFwicHJvZHVjdGlvblwiIH0sXG4gICAgICAgICAgICB7IHRpdGxlOiBcIkZpeHR1cmVcIiwgdmFsdWU6IFwiZml4dHVyZVwiIH0sXG4gICAgICAgICAgICB7IHRpdGxlOiBcIlRlc3RcIiwgdmFsdWU6IFwidGVzdFwiIH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICBhcmdzOiBbXG4gICAgICAgIFtcImZpeHR1cmVcIiwgXCJpbml0XCJdLFxuICAgICAgICBbXCJmaXh0dXJlXCIsIFwiaW1wb3J0XCIsIFwiI2VudGl0eUlkXCIsIFwiI3JlY29yZElkc1wiXSxcbiAgICAgICAgW1wiZml4dHVyZVwiLCBcInN5bmNcIl0sXG4gICAgICAgIFtcImZpeHR1cmVcIiwgXCJnZW5cIl0sXG4gICAgICAgIFtcImZpeHR1cmVcIiwgXCJmZXRjaFwiXSxcbiAgICAgICAgW1wiZml4dHVyZVwiLCBcImV4cGxvcmVcIl0sXG4gICAgICAgIFtcIm1pZ3JhdGVcIiwgXCJydW5cIl0sXG4gICAgICAgIFtcIm1pZ3JhdGVcIiwgXCJhcHBseVwiLCBcIiN0YXJnZXRzXCJdLFxuICAgICAgICBbXCJtaWdyYXRlXCIsIFwiZ2VuZXJhdGVcIl0sXG4gICAgICAgIFtcIm1pZ3JhdGVcIiwgXCJzdGF0dXNcIl0sXG4gICAgICAgIFtcInN0dWJcIiwgXCJwcmFjdGljZVwiLCBcIiNuYW1lXCJdLFxuICAgICAgICBbXCJzdHViXCIsIFwiZW50aXR5XCIsIFwiI25hbWVcIl0sXG4gICAgICAgIFtcInNjYWZmb2xkXCIsIFwibW9kZWxcIiwgXCIjZW50aXR5SWRcIl0sXG4gICAgICAgIFtcInNjYWZmb2xkXCIsIFwibW9kZWxfdGVzdFwiLCBcIiNlbnRpdHlJZFwiXSxcbiAgICAgICAgW1wic2NhZmZvbGRcIiwgXCJ2aWV3X2xpc3RcIiwgXCIjZW50aXR5SWRcIl0sXG4gICAgICAgIFtcInNjYWZmb2xkXCIsIFwidmlld19mb3JtXCIsIFwiI2VudGl0eUlkXCJdLFxuICAgICAgICBbXCJjb25lXCIsIFwiZ2VuXCIsIFwiI2VudGl0eUlkXCJdLFxuICAgICAgICBbXCJzeW5jXCJdLFxuICAgICAgICBbXCJidWlsZFwiLCBcImFsbFwiXSxcbiAgICAgICAgW1wiYnVpbGRcIiwgXCJhcGlcIl0sXG4gICAgICAgIFtcImJ1aWxkXCIsIFwid2ViXCJdLFxuICAgICAgICBbXCJkZXZcIiwgXCJhbGxcIl0sXG4gICAgICAgIFtcImRldlwiLCBcImFwaVwiXSxcbiAgICAgICAgW1wiZGV2XCIsIFwid2ViXCJdLFxuICAgICAgICBbXCJzdGFydFwiXSxcbiAgICAgICAgW1wic2tpbGxzXCIsIFwic3luY1wiXSxcbiAgICAgICAgW1widGVzdFwiXSxcbiAgICAgICAgW1wiYXV0aFwiLCBcImdlbmVyYXRlXCJdLFxuICAgICAgICBbXCJhdXRoXCIsIFwiYWRkLWNvbXBhbmlvbnNcIl0sXG4gICAgICBdLFxuICAgICAgcnVubmVyczoge1xuICAgICAgICBtaWdyYXRlX3N0YXR1cyxcbiAgICAgICAgbWlncmF0ZV9ydW4sXG4gICAgICAgIG1pZ3JhdGVfYXBwbHksXG4gICAgICAgIG1pZ3JhdGVfZ2VuZXJhdGUsXG4gICAgICAgIGZpeHR1cmVfaW5pdCxcbiAgICAgICAgZml4dHVyZV9pbXBvcnQsXG4gICAgICAgIGZpeHR1cmVfc3luYyxcbiAgICAgICAgZml4dHVyZV9nZW4sXG4gICAgICAgIGZpeHR1cmVfZmV0Y2gsXG4gICAgICAgIGZpeHR1cmVfZXhwbG9yZSxcbiAgICAgICAgc3R1Yl9wcmFjdGljZSxcbiAgICAgICAgc3R1Yl9lbnRpdHksXG4gICAgICAgIHNjYWZmb2xkX21vZGVsLFxuICAgICAgICBzY2FmZm9sZF9tb2RlbF90ZXN0LFxuICAgICAgICBzY2FmZm9sZF92aWV3X2xpc3QsXG4gICAgICAgIHNjYWZmb2xkX3ZpZXdfZm9ybSxcbiAgICAgICAgY29uZV9nZW4sXG4gICAgICAgIHN5bmMsXG4gICAgICAgIGJ1aWxkX2FsbCxcbiAgICAgICAgYnVpbGRfYXBpLFxuICAgICAgICBidWlsZF93ZWIsXG4gICAgICAgIGRldl9hbGwsXG4gICAgICAgIGRldl9hcGksXG4gICAgICAgIGRldl93ZWIsXG4gICAgICAgIHN0YXJ0LFxuICAgICAgICBza2lsbHNfc3luYzogc2tpbGxzTWlncmF0aW9uTm90aWNlLFxuICAgICAgICB0ZXN0OiB0ZXN0Q29tbWFuZCxcbiAgICAgICAgYXV0aF9nZW5lcmF0ZSxcbiAgICAgICAgXCJhdXRoX2FkZC1jb21wYW5pb25zXCI6IGF1dGhfYWRkX2NvbXBhbmlvbnMsXG4gICAgICB9LFxuICAgIH0pO1xuICB9IGZpbmFsbHkge1xuICAgIGF3YWl0IFNvbmFtdS5kZXN0cm95KCk7XG4gIH1cbn1cblxuYm9vdHN0cmFwKCkuZmluYWxseShhc3luYyAoKSA9PiB7XG4gIGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmRlc3Ryb3koKTtcbn0pO1xuXG4vKipcbiAqIHBucG0gc3luYyDtlZjrqbQg7Iuk7ZaJ65CY64qUIO2VqOyImOyeheuLiOuLpC5cbiAqIO2UhOuhnOygne2KuOulvCDsi7Htgaztlanri4jri6QuXG4gKlxuICogYC0tZm9yY2VgIOyYteyFmOydtCDso7zslrTsp4DrqbQgbG9ja+ydhCDrrLTsi5ztlZjqs6Ag7ZKALeyLse2BrOulvCDsiJjtlontlanri4jri6QuXG4gKiBnaXQgcG9zdC1tZXJnZSBob29r7J2064KYIENJ7JeQ7IScIOunpCBwdWxsIO2bhCDsnpDrj5kg7Iuk7ZaJ7ZWgIOyImCDsnojrj4TroZ0g64W47LacLlxuICovXG5hc3luYyBmdW5jdGlvbiBzeW5jKCkge1xuICBjb25zdCB7IGZsYWdzIH0gPSBwYXJzZUNsaU9wdGlvbnMoKTtcbiAgaWYgKGZsYWdzLmhhcyhcImZvcmNlXCIpKSB7XG4gICAgYXdhaXQgU29uYW11LnN5bmNlci5mb3JjZVN5bmMoKTtcbiAgfSBlbHNlIHtcbiAgICBhd2FpdCBTb25hbXUuc3luY2VyLnN5bmMoKTtcbiAgfVxufVxuXG4vKipcbiAqIOydtOyghCDsg53shLEg7ZSE66Gc7KCd7Yq47J2YIHBvc3RpbnN0YWxs7J20IOyXheq3uOugiOydtOuTnOulvCDrp4nsp4Ag7JWK64+E66GdIOq1rCDrqoXroLnrp4wg7JWI7KCE7ZWY6rKMIOyiheujjO2VqeuLiOuLpC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gc2tpbGxzTWlncmF0aW9uTm90aWNlKCkge1xuICBjb25zb2xlLmxvZyhjaGFsay55ZWxsb3coXCInc29uYW11IHNraWxscyBzeW5jJyBpcyBubyBsb25nZXIgc3VwcG9ydGVkLlwiKSk7XG4gIGNvbnNvbGUubG9nKFxuICAgIGNoYWxrLmN5YW4oXCJJbnN0YWxsIFNvbmFtdSBza2lsbHMgd2l0aCAnbnB4IHNraWxsc0BsYXRlc3QgYWRkIGNhcnRhbm92YS1haS9za2lsbHMnLlwiKSxcbiAgKTtcbn1cblxuLyoqXG4gKiBBUEkg6rCc67CcIOyEnOuyhOulvCDsi6TtlontlZjripQg6rO17Ya1IOuhnOyngeyeheuLiOuLpC5cbiAqIGRldl9hbGzqs7wgZGV2X2FwaeyXkOyEnCDqs7XsnKDtlanri4jri6QuXG4gKlxuICogVHlwZVNjcmlwdOulvCDrsJTroZwg7Iuk7ZaJ7ZWgIOyImCDsnojrj4TroZ0gQHNvbmFtdS1raXQvdHMtbG9hZGVy66W8LFxuICogSE1S7J2EIOyngOybkO2VmOq4sCDsnITtlbQgQHNvbmFtdS1raXQvaG1yLWhvb2vsnYQgaW1wb3J07ZWY66mwLFxuICog7IaM7Iqk66e1IOyngOybkOydhCDsnITtlbQgLS1lbmFibGUtc291cmNlLW1hcHMg7ZSM656Y6re466W8IO2PrO2VqO2VmOyXrCDsi6Ttlontlanri4jri6QuXG4gKlxuICog7J2065WMIEBzb25hbXUta2l0L3RzLWxvYWRlcuyZgCBAc29uYW11LWtpdC9obXItaG9va+uKlCBzb25hbXXqsIAg7J6Q7LK07KCB7Jy866GcIOqwgOyngOqzoCDsnojripQgZGVwZW5kZW5jeeyeheuLiOuLpC5cbiAqIOuYkO2VnCDsi6Ttlonsl5Ag7IKs7Jqp7ZWY64qUIEBzb25hbXUta2l0L2htci1ydW5uZXLrj4Qg66eI7LCs6rCA7KeA66GcIHNvbmFtdeqwgCDsnpDssrTsoIHsnLzroZwg6rCA7KeA6rOgIOyeiOuKlCBkZXBlbmRlbmN57J6F64uI64ukLlxuICog65Sw65287IScIOyCrOyaqeyekCDtlITroZzsoJ3tirjsl5DshJzripQg7J20IOyEuCDtjKjtgqTsp4Drpbwg7KeB7KCRIOyEpOy5mO2VoCDtlYTsmpTqsIAg7JeG7Iq164uI64ukLlxuICovXG5mdW5jdGlvbiBzcGF3bkFwaURldlNlcnZlcihvcHRpb25zPzogeyBleHRyYUVudj86IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gfSkge1xuICBjb25zdCBhcGlSb290ID0gZmluZEFwaVJvb3RQYXRoKCk7XG4gIGNvbnN0IGVudHJ5UG9pbnQgPSBcInNyYy9pbmRleC50c1wiO1xuXG4gIC8vIOydtCBzb25hbXUg7Yyo7YKk7KeA6rCAIGRlcGVuZGVuY2llc+uhnCDqsIDsp4Dqs6Ag7J6I64qUIEBzb25hbXUta2l0L2htci1ydW5uZXLsnZggYmluL3J1bi5qc+ulvCDsgqzsmqntlanri4jri6QuXG4gIC8vIOydtCDqsr3roZwoL2Jpbi9ydW4uanMp64qUIEBzb25hbXUta2l0L2htci1ydW5uZXLsnZggcGFja2FnZS5qc29u7J2YIGJpbiDtlYTrk5zsl5Ag66qF7Iuc65CY7Ja0IOyeiOuKlCDqt7jqsoPqs7wg6rCZ7Iq164uI64ukLlxuICBjb25zdCBob3RSdW5uZXJCaW5QYXRoID0gY3JlYXRlUmVxdWlyZShpbXBvcnQubWV0YS51cmwpLnJlc29sdmUoXG4gICAgXCJAc29uYW11LWtpdC9obXItcnVubmVyL2Jpbi9ydW4uanNcIixcbiAgKTtcblxuICBjb25zdCBzZXJ2ZXJQcm9jZXNzID0gc3Bhd24oXG4gICAgcHJvY2Vzcy5leGVjUGF0aCwgLy8gbm9kZVxuICAgIFtcbiAgICAgIGhvdFJ1bm5lckJpblBhdGgsIC8vIOydtOugh+qyjCDtlbTshJwgaG90LXJ1bm5lcuulvCDsi6TtlontlZjqtazsmpRcbiAgICAgIFwiLS1jbGVhci1zY3JlZW49ZmFsc2VcIiwgLy8g7J207ZWYIGhvdC1ydW5uZXLsl5Dqsowg64SY6rKo7KSEIOyduOyekOuTpOyeheuLiOuLpC5cbiAgICAgIFwiLS1ub2RlLWFyZ3M9LS1pbXBvcnQ9c29uYW11L3RzLWxvYWRlci1yZWdpc3RlclwiLCAvLyBUeXBlU2NyaXB0IOyEnO2PrO2KuOulvCDsnITtlZwg66Gc642ULFxuICAgICAgXCItLW5vZGUtYXJncz0tLWltcG9ydD1zb25hbXUvaG1yLWhvb2stcmVnaXN0ZXJcIiwgLy8gSE1S7J2EIOyngOybkO2VmOq4sCDsnITtlZwgaG9vayxcbiAgICAgIFwiLS1ub2RlLWFyZ3M9LS1lbmFibGUtc291cmNlLW1hcHNcIiwgLy8g6re466as6rOgIOyGjOyKpOuntSDsp4Dsm5DsnYQg7JyE7ZWcIO2UjOuemOq3uOyeheuLiOuLpC5cbiAgICAgIFwiLS1vbi1rZXk9cjpyZXN0YXJ0OlJlc3RhcnQgc2VydmVyXCIsIC8vIHIg64iE66W066m0IOyEnOuyhCDsnqzsi5zsnpHtlZjqsowg7ZW07KSY7JqULlxuICAgICAgXCItLW9uLWtleT1jOmNsZWFyOkNsZWFyIHNjcmVlblwiLCAvLyBjIOuIhOultOuptCDthLDrr7jrhJAg7ZmU66m07J2EIOyngOybjOykmOyalC5cbiAgICAgIGAtLW9uLWtleT1mOnNoZWxsKHJtICR7cGF0aC5qb2luKGFwaVJvb3QsIFwic29uYW11LmxvY2tcIil9KTpyZXN0YXJ0OkZvcmNlIHJlc3RhcnRgLCAvLyBmIOuIhOultOuptCBsb2NrIOygnOqxsCDtm4Qg7J6s7Iuc7J6RIOKGkiDsg4gg7ZSE66Gc7IS47Iqk6rCAIOu2gO2KuOyKpO2KuOueqeyXkOyEnCDtkoAt7Iux7YGsLiBmb3JjZSBzeW5jIENMSeulvCBzaGVsbOuhnCDrtoDrpbTrqbQg7IK07JWE7J6I64qUIOyEnOuyhOydmCB3YXRjaGVyIO2Pre2SjeydtCA6cmVzdGFydOyZgCDstqnrj4ztlbQg7IOB7YOcIOydtOyDgS5cblxuICAgICAgXCItLW9uLWtleT1lbnRlcjpzaGVsbChlY2hvIGhpKTpLZXkgYmluZGluZyB0ZXN0XCIsIC8vIGVudGVy66W8IGtleeuhnCDsk7gg7IiYIOyeiOydjOydhCDrs7TsnbTquLAg7JyE7ZWcIO2FjOyKpO2KuOyeheuLiOuLpC5cbiAgICAgIFwiLS1vbi1rZXk9Y3RybCtmIGN0cmwrZjpzaGVsbChnaXQgcHVsbCAmJiBwbnBtIGluc3RhbGwgJiYgcG5wbSAtLWZpbHRlciBzb25hbXUgYnVpbGQgJiYgZWNobyAnU29uYW11IGlzIG5vdyB1cC10by1kYXRlIScpOnJlc3RhcnQ6UHVsbCAmIGluc3RhbGwgJiBidWlsZCAmIHJlc3RhcnRcIiwgLy8gbW9kaWZpZXLsmYDsnZgg7KGw7ZWpLCDqt7jrpqzqs6Ag65GQIOqwnOydmCBjaG9yZOulvCDsgqzsmqntlaAg7IiYIOyeiOydjOydhCDrs7TsnbTquLAg7JyE7ZWcIO2FjOyKpO2KuOyeheuLiOuLpC5cbiAgICAgIGVudHJ5UG9pbnQsIC8vIOuniOyngOunieycvOuhnCDsi6TsoJwg7Iuk7ZaJ7ZWgIOyKpO2BrOumve2KuOydmCDqsr3roZzrpbwg64SY6rKo7KSN64uI64ukLlxuICAgIF0sXG4gICAge1xuICAgICAgY3dkOiBhcGlSb290LFxuICAgICAgc3RkaW86IFwiaW5oZXJpdFwiLFxuICAgICAgZW52OiB7XG4gICAgICAgIC4uLnByb2Nlc3MuZW52LFxuICAgICAgICBOT0RFX0VOVjogcHJvY2Vzcy5lbnYuTk9ERV9FTlYgPz8gXCJkZXZlbG9wbWVudFwiLFxuICAgICAgICBIT1Q6IFwieWVzXCIsIC8vIOyWmOqwgCDsnojslrTslbwgSE1S7J20IO2ZnOyEse2ZlOuQqeuLiOuLpC5cbiAgICAgICAgQVBJX1JPT1RfUEFUSDogYXBpUm9vdCwgLy8g7J20IOqyveuhnOqwgCBobXItaG9va+ydmCDro6jtirgg65SU66CJ7Yag66as6rCAIOuQqeuLiOuLpC5cbiAgICAgICAgLi4ub3B0aW9ucz8uZXh0cmFFbnYsXG4gICAgICB9LFxuICAgIH0sXG4gICk7XG5cbiAgLy8g7KKF66OMIOyymOumrFxuICBjb25zdCBjbGVhbnVwID0gKCkgPT4ge1xuICAgIGNvbnNvbGUubG9nKGNoYWxrLnllbGxvdyhcIlxcblxcbvCfkYsgU2h1dHRpbmcgZG93bi4uLlwiKSk7XG4gICAgc2VydmVyUHJvY2Vzcy5raWxsKFwiU0lHVEVSTVwiKTtcbiAgICBwcm9jZXNzLmV4aXQoMCk7XG4gIH07XG5cbiAgcHJvY2Vzcy5vbihcIlNJR0lOVFwiLCBjbGVhbnVwKTtcbiAgcHJvY2Vzcy5vbihcIlNJR1RFUk1cIiwgY2xlYW51cCk7XG5cbiAgc2VydmVyUHJvY2Vzcy5vbihcImV4aXRcIiwgKGNvZGUpID0+IHtcbiAgICBpZiAoY29kZSAhPT0gMCkge1xuICAgICAgY29uc29sZS5lcnJvcihjaGFsay5yZWQoYFNlcnZlciBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX1gKSk7XG4gICAgICBwcm9jZXNzLmV4aXQoY29kZSB8fCAxKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIHBucG0gZGV2IC8gcG5wbSBkZXYgYWxsIO2VmOuptCDsi6TtlonrkJjripQg7ZWo7IiY7J6F64uI64ukLlxuICog7ZSE66Gc7KCd7Yq47JeQIOuMgO2VtCBITVIg7KeA7JuQ7ZWY64qUIOqwnOuwnCDshJzrsoTrpbwg652E7JuM7KSN64uI64ukLlxuICpcbiAqIFNvbmFtdS5pbml0IOyXhuydtCDtmLjstpzrkKAg6rKD7J2EIOyDgeygle2VmOyXrCDqtaztmITrkJjsl4jsirXri4jri6QuXG4gKi9cbmZ1bmN0aW9uIGRldl9hbGwoKSB7XG4gIGNvbnN0IHJlcXVpcmUgPSBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCk7XG4gIGNvbnN0IHsgdmVyc2lvbiB9ID0gcmVxdWlyZShcIi4uLy4uL3BhY2thZ2UuanNvblwiKTtcbiAgY29uc29sZS5sb2coYPCfjLIgU29uYW11IHYke3ZlcnNpb259XFxuYCk7XG4gIHNwYXduQXBpRGV2U2VydmVyKCk7XG59XG5cbi8qKlxuICogcG5wbSBkZXYgYXBpIO2VmOuptCDsi6TtlonrkJjripQg7ZWo7IiY7J6F64uI64ukLlxuICogQVBJIOyghOyaqSDqsJzrsJwg7ISc67KE66W8IOudhOybgeuLiOuLpC5cbiAqIGRldl9hbGzqs7wg6rGw7J2YIOuPmeydvO2VmOuQmCwg7Ya17ZWpIOybuSDshJzrsoTrpbwg67mE7Zmc7ISx7ZmU7ZWp64uI64ukLlxuICpcbiAqIFNvbmFtdS5pbml0IOyXhuydtCDtmLjstpzrkKAg6rKD7J2EIOyDgeygle2VmOyXrCDqtaztmITrkJjsl4jsirXri4jri6QuXG4gKi9cbmZ1bmN0aW9uIGRldl9hcGkoKSB7XG4gIGNvbnNvbGUubG9nKGNoYWxrLnllbGxvdy5ib2xkKFwiU3RhcnRpbmcgU29uYW11IEFQSS1vbmx5IGRldiBzZXJ2ZXIuLi5cXG5cIikpO1xuICBzcGF3bkFwaURldlNlcnZlcih7XG4gICAgZXh0cmFFbnY6IHsgU09OQU1VX0RJU0FCTEVfSU5URUdSQVRFRF9XRUI6IFwieWVzXCIgfSxcbiAgfSk7XG59XG5cbi8qKlxuICogcG5wbSBkZXYgd2ViIO2VmOuptCDsi6TtlonrkJjripQg7ZWo7IiY7J6F64uI64ukLlxuICogVml0ZSDqsJzrsJwg7ISc67KE66W8IOuLqOuPheycvOuhnCDsi6Ttlontlanri4jri6QuXG4gKiAtLSDrkqTsnZgg7J247J6Q64qUIFZpdGXsl5Ag6re464yA66GcIOyghOuLrOuQqeuLiOuLpC5cbiAqXG4gKiBTb25hbXUuaW5pdCDsl4bsnbQg7Zi47Lac65CgIOqyg+ydhCDsg4HsoJXtlZjsl6wg6rWs7ZiE65CY7JeI7Iq164uI64ukLlxuICovXG5hc3luYyBmdW5jdGlvbiBkZXZfd2ViKCkge1xuICBjb25zdCBhcHBSb290ID0gZmluZEFwcFJvb3RQYXRoKCk7XG4gIGNvbnN0IHdlYlBhdGggPSBwYXRoLmpvaW4oYXBwUm9vdCwgXCJ3ZWJcIik7XG5cbiAgaWYgKCEoYXdhaXQgZXhpc3RzKHdlYlBhdGgpKSkge1xuICAgIGNvbnNvbGUuZXJyb3IoYHdlYiDrlJTroInthqDrpqzrpbwg7LC+7J2EIOyImCDsl4bsirXri4jri6Q6ICR7d2ViUGF0aH1gKTtcbiAgICBwcm9jZXNzLmV4aXQoMSk7XG4gIH1cblxuICAvLyAtLSDrkqTsnZgg7J247J6QIOy2lOy2nFxuICBjb25zdCBkb3VibGVEYXNoSW5kZXggPSBwcm9jZXNzLmFyZ3YuaW5kZXhPZihcIi0tXCIpO1xuICBjb25zdCBwYXNzdGhyb3VnaEFyZ3MgPSBkb3VibGVEYXNoSW5kZXggIT09IC0xID8gcHJvY2Vzcy5hcmd2LnNsaWNlKGRvdWJsZURhc2hJbmRleCArIDEpIDogW107XG5cbiAgY29uc3Qgdml0ZUFyZ3MgPSBbXCJleGVjXCIsIFwidml0ZVwiLCAuLi5wYXNzdGhyb3VnaEFyZ3NdO1xuXG4gIGNvbnNvbGUubG9nKGNoYWxrLnllbGxvdy5ib2xkKFwiU3RhcnRpbmcgVml0ZSBkZXYgc2VydmVyLi4uXFxuXCIpKTtcblxuICBjb25zdCB2aXRlUHJvY2VzcyA9IHNwYXduKFwicG5wbVwiLCB2aXRlQXJncywge1xuICAgIGN3ZDogd2ViUGF0aCxcbiAgICBzdGRpbzogXCJpbmhlcml0XCIsXG4gIH0pO1xuXG4gIHZpdGVQcm9jZXNzLm9uKFwiZXhpdFwiLCAoY29kZSkgPT4ge1xuICAgIHByb2Nlc3MuZXhpdChjb2RlID8/IDApO1xuICB9KTtcblxuICAvLyBTSUdJTlQvU0lHVEVSTSDsi5wgVml0ZSDtlITroZzshLjsiqTrpbwgZ3JhY2VmdWxseSDsooXro4ztlanri4jri6QuXG4gIGZvciAoY29uc3Qgc2lnbmFsIG9mIFtcIlNJR0lOVFwiLCBcIlNJR1RFUk1cIl0gYXMgY29uc3QpIHtcbiAgICBwcm9jZXNzLm9uKHNpZ25hbCwgKCkgPT4ge1xuICAgICAgdml0ZVByb2Nlc3Mua2lsbChzaWduYWwpO1xuICAgIH0pO1xuICB9XG59XG5cbi8qKlxuICogQVBJIOu5jOuTnCDshKTsoJUg7YyM7J28IOqyveuhnOulvCDqsrDsoJXtlanri4jri6QuXG4gKiDtlITroZzsoJ3tirgg66Oo7Yq47JeQIGB0c2Rvd24uY29uZmlnLnRzYOqwgCDsnojsnLzrqbQg6re46rKD7J2ELCDsl4bsnLzrqbQgc29uYW11IOq4sOuzuCDshKTsoJXsnYQg7IKs7Jqp7ZWp64uI64ukLlxuICovXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlQXBpQnVpbGRDb25maWdQYXRoKGFwaVJvb3RQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBsb2NhbENvbmZpZ1BhdGggPSBwYXRoLmpvaW4oYXBpUm9vdFBhdGgsIFwidHNkb3duLmNvbmZpZy50c1wiKTtcblxuICB0cnkge1xuICAgIGlmIChhd2FpdCBleGlzdHMobG9jYWxDb25maWdQYXRoKSkge1xuICAgICAgY29uc29sZS5sb2coY2hhbGsuZGltKFwiVXNpbmcgdHNkb3duLmNvbmZpZy50cyBmcm9tIHByb2plY3Qgcm9vdC4uLlwiKSk7XG4gICAgICByZXR1cm4gbG9jYWxDb25maWdQYXRoO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKGNoYWxrLmRpbShcIlVzaW5nIGRlZmF1bHQgdHNkb3duIEFQSSBjb25maWcgZnJvbSBzb25hbXUgcGFja2FnZS4uLlwiKSk7XG4gICAgcmV0dXJuIHBhdGguam9pbihpbXBvcnQubWV0YS5kaXJuYW1lLCBcIi4uXCIsIFwiLi5cIiwgXCJ0c2Rvd24uYXBpLmNvbmZpZy50c1wiKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zb2xlLmVycm9yKGNoYWxrLnJlZChcIlNldHRpbmcgdXAgQVBJIGJ1aWxkIGNvbmZpZyBmYWlsZWQuXCIpLCBlcnJvcik7XG4gICAgcHJvY2Vzcy5leGl0KDEpO1xuICB9XG59XG5cbi8qKlxuICogc29uYW11IGJ1aWxkIC8gc29uYW11IGJ1aWxkIGFsbCDtlZjrqbQg7Iuk7ZaJ65CY64qUIO2VqOyImOyeheuLiOuLpC5cbiAqIGJ1aWxkX2FwaSArIGJ1aWxkX3dlYuydmCDtlanshLHsnoXri4jri6QuIFdlYiDrlJTroInthqDrpqzqsIAg7JeG7Jy866m0IFdlYiDruYzrk5zrpbwg7Iqk7YK17ZWp64uI64ukLlxuICovXG5hc3luYyBmdW5jdGlvbiBidWlsZF9hbGwoKSB7XG4gIGF3YWl0IGJ1aWxkX2FwaSgpO1xuICBhd2FpdCBidWlsZF93ZWIoeyBza2lwSWZNaXNzaW5nOiB0cnVlIH0pO1xufVxuXG4vKipcbiAqIHBucG0gYnVpbGQgYXBpIO2VmOuptCDsi6TtlonrkJjripQg7ZWo7IiY7J6F64uI64ukLlxuICogQVBJIO2UhOuhnOygne2KuOunjCDruYzrk5ztlanri4jri6QuXG4gKlxuICogU29uYW11LmluaXQg7JeG7J20IO2YuOy2nOuQoCDqsoPsnYQg7IOB7KCV7ZWY7JesIOq1rO2YhOuQmOyXiOyKteuLiOuLpC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gYnVpbGRfYXBpKCkge1xuICBjb25zdCBhcGlSb290UGF0aCA9IGZpbmRBcGlSb290UGF0aCgpO1xuICBjb25zdCBiYXNlQ29uZmlnUGF0aCA9IGF3YWl0IHJlc29sdmVBcGlCdWlsZENvbmZpZ1BhdGgoYXBpUm9vdFBhdGgpO1xuICBjb25zdCBwb2xpY3kgPSBhd2FpdCBsb2FkQnVpbGRDb21waWxlclBvbGljeShhcGlSb290UGF0aCk7XG4gIGNvbnN0IHJlZ2lzdHJ5UGF0aCA9IHBhdGguam9pbihhcGlSb290UGF0aCwgSFRUUF9WQUxJREFUT1JfUkVHSVNUUllfU09VUkNFX1BBVEgpO1xuICBjb25zdCBjb25maWdGaWxlUGF0aCA9IGF3YWl0IGNyZWF0ZUFwaVpvZENvbXBpbGVyQnVpbGRXcmFwcGVyKHtcbiAgICBhcGlSb290UGF0aCxcbiAgICBiYXNlQ29uZmlnUGF0aCxcbiAgICBwb2xpY3ksXG4gICAgcmVnaXN0cnlQYXRoLFxuICB9KTtcbiAgY29uc3QgdGVtcG9yYXJ5Q29uZmlnUGF0aCA9IGNvbmZpZ0ZpbGVQYXRoID09PSBiYXNlQ29uZmlnUGF0aCA/IHVuZGVmaW5lZCA6IGNvbmZpZ0ZpbGVQYXRoO1xuXG4gIGNvbnN0IGFwaVN0YXJ0ZWRBdCA9IERhdGUubm93KCk7XG4gIHRyeSB7XG4gICAgZm9yIChjb25zdCBhcnRpZmFjdCBvZiBBUElfQVJUSUZBQ1RTKSB7XG4gICAgICBjb25zdCBjd2QgPSBhcGlSb290UGF0aDtcbiAgICAgIHByaW50VGFza0hlYWRlcihhcnRpZmFjdC5uYW1lLCBhcnRpZmFjdC5kZXNjcmlwdGlvbiwgY3dkKTtcblxuICAgICAgYXdhaXQgcnVuQnVpbGRTdGVwcyhhcnRpZmFjdCwgeyBjd2QsIGJ1aWxkQ29tbWFuZEFyZ3M6IHsgY29uZmlnRmlsZVBhdGggfSB9KTtcbiAgICB9XG4gICAgaWYgKHBvbGljeS5hcGkgPT09IFwiYW90XCIpIHtcbiAgICAgIGNvbnN0IG91dHB1dFBhdGggPSBwYXRoLmpvaW4oYXBpUm9vdFBhdGgsIEhUVFBfVkFMSURBVE9SX1JFR0lTVFJZX0JVSUxEX1BBVEgpO1xuICAgICAgaWYgKCEoYXdhaXQgZXhpc3RzKG91dHB1dFBhdGgpKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEJ1aWx0IEhUVFAgdmFsaWRhdG9yIHJlZ2lzdHJ5IG5vdCBmb3VuZDogJHtvdXRwdXRQYXRofWApO1xuICAgICAgfVxuICAgICAgYXNzZXJ0QXBpWm9kQ29tcGlsZXJCdWlsZE91dHB1dChbXG4gICAgICAgIHtcbiAgICAgICAgICBjb2RlOiBhd2FpdCByZWFkRmlsZShvdXRwdXRQYXRoLCBcInV0ZjhcIiksXG4gICAgICAgICAgcGF0aDogb3V0cHV0UGF0aCxcbiAgICAgICAgfSxcbiAgICAgIF0pO1xuICAgIH1cbiAgICBwcmludEJ1aWxkU3VtbWFyeShcIkFQSVwiLCB0cnVlLCBEYXRlLm5vdygpIC0gYXBpU3RhcnRlZEF0KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIHByaW50QnVpbGRTdW1tYXJ5KFwiQVBJXCIsIGZhbHNlLCBEYXRlLm5vdygpIC0gYXBpU3RhcnRlZEF0KTtcbiAgICB0aHJvdyBlO1xuICB9IGZpbmFsbHkge1xuICAgIGlmICh0ZW1wb3JhcnlDb25maWdQYXRoICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGF3YWl0IHJtKHRlbXBvcmFyeUNvbmZpZ1BhdGgsIHsgZm9yY2U6IHRydWUgfSk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogcG5wbSBidWlsZCB3ZWIg7ZWY66m0IOyLpO2WieuQmOuKlCDtlajsiJjsnoXri4jri6QuXG4gKiBXZWIg7ZSE66Gc7KCd7Yq466eMIOu5jOuTnO2VqeuLiOuLpC5cbiAqXG4gKiBTb25hbXUuaW5pdCDsl4bsnbQg7Zi47Lac65CgIOqyg+ydhCDsg4HsoJXtlZjsl6wg6rWs7ZiE65CY7JeI7Iq164uI64ukLlxuICovXG5hc3luYyBmdW5jdGlvbiBidWlsZF93ZWIoeyBza2lwSWZNaXNzaW5nID0gZmFsc2UgfSA9IHt9KSB7XG4gIGNvbnN0IGFwcFJvb3QgPSBmaW5kQXBwUm9vdFBhdGgoKTtcbiAgY29uc3Qgd2ViUGF0aCA9IHBhdGguam9pbihhcHBSb290LCBcIndlYlwiKTtcblxuICBpZiAoIShhd2FpdCBleGlzdHMod2ViUGF0aCkpKSB7XG4gICAgaWYgKHNraXBJZk1pc3NpbmcpIHtcbiAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmdyYXkoXCJXZWIg65SU66CJ7Yag66as6rCAIOyXhuycvOuvgOuhnCBXZWIg67mM65Oc66W8IOqxtOuEiOucgeuLiOuLpC5cIikpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zb2xlLmVycm9yKGB3ZWIg65SU66CJ7Yag66as66W8IOywvuydhCDsiJgg7JeG7Iq164uI64ukOiAke3dlYlBhdGh9YCk7XG4gICAgcHJvY2Vzcy5leGl0KDEpO1xuICB9XG5cbiAgY29uc3Qgd2ViU3RhcnRlZEF0ID0gRGF0ZS5ub3coKTtcbiAgdHJ5IHtcbiAgICBmb3IgKGNvbnN0IGFydGlmYWN0IG9mIFdFQl9BUlRJRkFDVFMpIHtcbiAgICAgIGNvbnN0IGN3ZCA9IHBhdGguam9pbihhcHBSb290LCBhcnRpZmFjdC5wcm9qZWN0UGF0aCk7XG4gICAgICBwcmludFRhc2tIZWFkZXIoYXJ0aWZhY3QubmFtZSwgYXJ0aWZhY3QuZGVzY3JpcHRpb24sIGN3ZCk7XG5cbiAgICAgIGF3YWl0IHJ1bkJ1aWxkU3RlcHMoYXJ0aWZhY3QsIHsgY3dkLCBidWlsZENvbW1hbmRBcmdzOiB7fSB9KTtcbiAgICB9XG4gICAgcHJpbnRCdWlsZFN1bW1hcnkoXCJXZWJcIiwgdHJ1ZSwgRGF0ZS5ub3coKSAtIHdlYlN0YXJ0ZWRBdCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBwcmludEJ1aWxkU3VtbWFyeShcIldlYlwiLCBmYWxzZSwgRGF0ZS5ub3coKSAtIHdlYlN0YXJ0ZWRBdCk7XG4gICAgY29uc29sZS5lcnJvcihlKTtcbiAgICBwcm9jZXNzLmV4aXQoMSk7XG4gIH1cbn1cblxuLyoqXG4gKiBwcmUtYnVpbGQsIGJ1aWxkLCBwb3N0LWJ1aWxkIOuLqOqzhOulvCDsiJzssKjsoIHsnLzroZwg7Iuk7ZaJ7ZWp64uI64ukLlxuICovXG5hc3luYyBmdW5jdGlvbiBydW5CdWlsZFN0ZXBzPFQ+KFxuICBhcnRpZmFjdDogQnVpbGRBcnRpZmFjdDxUPixcbiAgb3B0aW9uczogeyBjd2Q6IHN0cmluZzsgYnVpbGRDb21tYW5kQXJnczogVCB9LFxuKSB7XG4gIGNvbnN0IHN0ZXBzID0gW1xuICAgIHsgbmFtZTogXCJwcmUtYnVpbGRcIiwgY21kOiBhcnRpZmFjdC5wcmVCdWlsZENvbW1hbmQ/LigpIH0sXG4gICAgeyBuYW1lOiBcImJ1aWxkXCIsIGNtZDogYXJ0aWZhY3QuYnVpbGRDb21tYW5kKG9wdGlvbnMuYnVpbGRDb21tYW5kQXJncykgfSxcbiAgICB7IG5hbWU6IFwicG9zdC1idWlsZFwiLCBjbWQ6IGFydGlmYWN0LnBvc3RCdWlsZENvbW1hbmQ/LigpIH0sXG4gIF0uZmlsdGVyKChzdGVwKSA9PiBzdGVwLmNtZCk7XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdGVwcy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IHN0ZXAgPSBzdGVwc1tpXTtcbiAgICBjb25zdCBpc0xhc3QgPSBpID09PSBzdGVwcy5sZW5ndGggLSAxO1xuXG4gICAgdHJ5IHtcbiAgICAgIGFzc2VydChzdGVwLmNtZCk7XG4gICAgICBwcmludFRhc2tTdGFydChzdGVwLm5hbWUsIHN0ZXAuY21kLCBpc0xhc3QpO1xuICAgICAgYXdhaXQgZXhlY1dpdGhMaW5lUHJlZml4KHN0ZXAuY21kLCB7IGN3ZDogb3B0aW9ucy5jd2QgfSk7XG4gICAgICBwcmludFRhc2tTdWNjZXNzKHN0ZXAubmFtZSwgaXNMYXN0KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBwcmludFRhc2tGYWlsZWQoc3RlcC5uYW1lLCBpc0xhc3QpO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGAke3N0ZXAubmFtZX0gZmFpbGVkYCwgeyBjYXVzZTogZSB9KTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBwbnBtIHN0YXJ0IO2VmOuptCDsi6TtlonrkJjripQg7ZWo7IiY7J6F64uI64ukLlxuICog67mM65Oc65CcIO2UhOuhnOygne2KuOulvCDsi6Ttlontlanri4jri6QuXG4gKlxuICog67mM65Oc65CcIOqysOqzvOusvChkaXN0IOuUlOugie2GoOumrOydmCBpbmRleC5qcyDsl5Ttirjrpqztj6zsnbjtirgp7J20IOyXhuuLpOuptCDsi6TtlonsnYQg7KSR64uo7ZWp64uI64ukLlxuICog7IaM7Iqk66e1IOyngOybkOqzvCBkb3RlbnYg7KeA7JuQ7J2EIO2PrO2VqO2VmOyXrCDsi6Ttlontlanri4jri6QuXG4gKlxuICogU29uYW11LmluaXQg7JeG7J20IO2YuOy2nOuQoCDqsoPsnYQg7IOB7KCV7ZWY7JesIOq1rO2YhOuQmOyXiOyKteuLiOuLpC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gc3RhcnQoKSB7XG4gIGNvbnN0IHJlcXVpcmUgPSBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCk7XG4gIGNvbnN0IHsgdmVyc2lvbiB9ID0gcmVxdWlyZShcIi4uLy4uL3BhY2thZ2UuanNvblwiKTtcbiAgY29uc29sZS5sb2coYPCfjLIgU29uYW11IHYke3ZlcnNpb259XFxuYCk7XG5cbiAgY29uc3QgYXBpUm9vdCA9IGZpbmRBcGlSb290UGF0aCgpO1xuICBjb25zdCBlbnRyeVBvaW50ID0gXCJkaXN0L2luZGV4LmpzXCI7XG5cbiAgaWYgKCEoYXdhaXQgZXhpc3RzKGVudHJ5UG9pbnQpKSkge1xuICAgIGNvbnNvbGUubG9nKGNoYWxrLnJlZChgJHtlbnRyeVBvaW50fSBub3QgZm91bmQuIFBsZWFzZSBidWlsZCB5b3VyIHByb2plY3QgZmlyc3QuYCkpO1xuICAgIGNvbnNvbGUubG9nKGNoYWxrLmJsdWUoXCJSdW46IHBucG0gc29uYW11IGJ1aWxkXCIpKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCBzZXJ2ZXJQcm9jZXNzID0gc3Bhd24oXG4gICAgcHJvY2Vzcy5leGVjUGF0aCxcbiAgICBbXCItLWVuYWJsZS1zb3VyY2UtbWFwc1wiLCBcIi1yXCIsIFwiZG90ZW52L2NvbmZpZ1wiLCBlbnRyeVBvaW50XSxcbiAgICB7XG4gICAgICBjd2Q6IGFwaVJvb3QsXG4gICAgICBzdGRpbzogXCJpbmhlcml0XCIsXG4gICAgfSxcbiAgKTtcblxuICBwcm9jZXNzLm9uKFwiU0lHSU5UXCIsICgpID0+IHtcbiAgICBzZXJ2ZXJQcm9jZXNzLmtpbGwoXCJTSUdURVJNXCIpO1xuICAgIHByb2Nlc3MuZXhpdCgwKTtcbiAgfSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHNldHVwTWlncmF0b3IoKSB7XG4gIC8vIG1pZ3JhdG9yXG4gIG1pZ3JhdG9yID0gbmV3IE1pZ3JhdG9yKCk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHNldHVwRml4dHVyZU1hbmFnZXIoKSB7XG4gIEZpeHR1cmVNYW5hZ2VyLmluaXQoKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gbWlncmF0ZV9hcHBseSh0YXJnZXRzOiAoa2V5b2YgU29uYW11REJDb25maWcpW10pIHtcbiAgYXdhaXQgc2V0dXBNaWdyYXRvcigpO1xuICBhd2FpdCBtaWdyYXRvci5hcHBseSh0YXJnZXRzKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gbWlncmF0ZV9ydW4oKSB7XG4gIGF3YWl0IHNldHVwTWlncmF0b3IoKTtcbiAgYXdhaXQgbWlncmF0b3IuYXBwbHkoZ2V0TWlncmF0ZVJ1blRhcmdldHMoKSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIG1pZ3JhdGVfZ2VuZXJhdGUoKSB7XG4gIGF3YWl0IHNldHVwTWlncmF0b3IoKTtcblxuICBjb25zdCB7IGNvbm5zIH0gPSBhd2FpdCBtaWdyYXRvci5nZXRTdGF0dXMoKTtcbiAgY29uc3QgaGFzU3RhdHVzMCA9IGNvbm5zLnNvbWUoKGNvbm4pID0+IGNvbm4uc3RhdHVzID09PSAwKTtcbiAgaWYgKCFoYXNTdGF0dXMwKSB7XG4gICAgY29uc29sZS5sb2coXG4gICAgICBjaGFsay5yZWQoXG4gICAgICAgIFwi66eI7J206re466CI7J207IWYIO2MjOydvOydhCDsg53shLHtlZjroKTrqbQg6riw7KG0IOuniOydtOq3uOugiOydtOyFmOydtCDstZzshowg7ZWY64KY7J2YIERC7JeQIOuqqOuRkCDsoIHsmqnrkJjslrQg7J6I7Ja07JW8IO2VqeuLiOuLpC5cIixcbiAgICAgICksXG4gICAgKTtcbiAgICBmb3IgKGNvbnN0IGNvbm4gb2YgY29ubnMpIHtcbiAgICAgIGlmIChjb25uLnBlbmRpbmcubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zb2xlLmxvZyhjaGFsay55ZWxsb3coYCAgJHtjb25uLm5hbWV9OiBwZW5kaW5nICR7Y29ubi5wZW5kaW5nLmxlbmd0aH3qsJxgKSk7XG4gICAgICB9XG4gICAgfVxuICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgfVxuXG4gIGNvbnN0IGNvdW50ID0gYXdhaXQgbWlncmF0b3IuZ2VuZXJhdGVQcmVwYXJlZENvZGVzKCk7XG4gIGlmIChjb3VudCA+IDApIHtcbiAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihgJHtjb3VudH3qsJzsnZgg66eI7J206re466CI7J207IWYIO2MjOydvOydtCDsg53shLHrkJjsl4jsirXri4jri6QuYCkpO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIG1pZ3JhdGVfc3RhdHVzKCkge1xuICBhd2FpdCBzZXR1cE1pZ3JhdG9yKCk7XG5cbiAgY29uc3Qgc3RhdHVzID0gYXdhaXQgbWlncmF0b3IuZ2V0U3RhdHVzKCk7XG4gIC8vIHN0YXR1cztcbiAgY29uc29sZS5sb2coc3RhdHVzKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZml4dHVyZV9pbml0KCkge1xuICBjb25zdCBzcmNDb25maWcgPSBTb25hbXUuZGJDb25maWdbZ2V0U29uYW11RW52aXJvbm1lbnQoKV07XG4gIGNvbnN0IHRhcmdldHMgPSAvKiBTQUZFVFk6IENMSSDtjIzshJzsmYAg67mM65OcIOuPhOq1rCDsnoXroKUg6rOE7JW97J20IOydtCDqsJLsnZgg7YOA7J6F7J2EIOuztOyepe2VnOuLpC4gKi8gW1xuICAgIHtcbiAgICAgIGxhYmVsOiBcIihSRU1PVEUpIEZpeHR1cmUgREJcIixcbiAgICAgIGNvbmZpZzogU29uYW11LmRiQ29uZmlnLmZpeHR1cmUsXG4gICAgfSxcbiAgICB7XG4gICAgICBsYWJlbDogXCIoTE9DQUwpIFRlc3RpbmcgREJcIixcbiAgICAgIGNvbmZpZzogU29uYW11LmRiQ29uZmlnLnRlc3QsXG4gICAgICB0b1NraXA6ICgoKSA9PiB7XG4gICAgICAgIGNvbnN0IHJlbW90ZUNvbm4gPVxuICAgICAgICAgIC8qIFNBRkVUWTogQ0xJIO2MjOyEnOyZgCDruYzrk5wg64+E6rWsIOyeheugpSDqs4Tslb3snbQg7J20IOqwkuydmCDtg4DsnoXsnYQg67O07J6l7ZWc64ukLiAqLyBTb25hbXUuZGJDb25maWdcbiAgICAgICAgICAgIC5maXh0dXJlLmNvbm5lY3Rpb24gYXMgS25leC5Db25uZWN0aW9uQ29uZmlnO1xuICAgICAgICBjb25zdCBsb2NhbENvbm4gPVxuICAgICAgICAgIC8qIFNBRkVUWTogQ0xJIO2MjOyEnOyZgCDruYzrk5wg64+E6rWsIOyeheugpSDqs4Tslb3snbQg7J20IOqwkuydmCDtg4DsnoXsnYQg67O07J6l7ZWc64ukLiAqLyBTb25hbXUuZGJDb25maWdcbiAgICAgICAgICAgIC50ZXN0LmNvbm5lY3Rpb24gYXMgS25leC5Db25uZWN0aW9uQ29uZmlnO1xuICAgICAgICByZXR1cm4gcmVtb3RlQ29ubi5ob3N0ID09PSBsb2NhbENvbm4uaG9zdCAmJiByZW1vdGVDb25uLmRhdGFiYXNlID09PSBsb2NhbENvbm4uZGF0YWJhc2U7XG4gICAgICB9KSgpLFxuICAgIH0sXG4gIF0gYXMge1xuICAgIGxhYmVsOiBzdHJpbmc7XG4gICAgY29uZmlnOiBLbmV4LkNvbmZpZztcbiAgICB0b1NraXA/OiBib29sZWFuO1xuICB9W107XG5cbiAgLy8gMS4g6riw7KSAREIg7Iqk7YKk66eI66W8IOuNpO2UhFxuICBjb25zb2xlLmxvZyhcIkRVTVAuLi5cIik7XG4gIGNvbnN0IGR1bXBGaWxlbmFtZSA9IGAvdG1wL3NvbmFtdS1maXh0dXJlLWluaXQtJHtEYXRlLm5vdygpfS5zcWxgO1xuICBjb25zdCBzcmNDb25uID1cbiAgICAvKiBTQUZFVFk6IENMSSDtjIzshJzsmYAg67mM65OcIOuPhOq1rCDsnoXroKUg6rOE7JW97J20IOydtCDqsJLsnZgg7YOA7J6F7J2EIOuztOyepe2VnOuLpC4gKi8gc3JjQ29uZmlnLmNvbm5lY3Rpb24gYXMgS25leC5Db25uZWN0aW9uQ29uZmlnO1xuICBjb25zdCBtaWdyYXRpb25zRHVtcCA9IGAvdG1wL3NvbmFtdS1maXh0dXJlLWluaXQtbWlncmF0aW9ucy0ke0RhdGUubm93KCl9LnNxbGA7XG4gIGF3YWl0IHdyaXRlTXlzcWxEdW1wKFxuICAgIFtcbiAgICAgIC4uLm15c3FsQ29ubmVjdGlvbkFyZ3Moc3JjQ29ubiksXG4gICAgICBcIi0tc2luZ2xlLXRyYW5zYWN0aW9uXCIsXG4gICAgICBcIi1kXCIsXG4gICAgICBcIi0tbm8tY3JlYXRlLWRiXCIsXG4gICAgICBcIi0tdHJpZ2dlcnNcIixcbiAgICAgIFN0cmluZyhzcmNDb25uLmRhdGFiYXNlKSxcbiAgICBdLFxuICAgIGR1bXBGaWxlbmFtZSxcbiAgKTtcbiAgY29uc3Qgc291cmNlRGIgPSBrbmV4KHNyY0NvbmZpZyk7XG4gIGNvbnN0IFtbbWlncmF0aW9uc11dID0gYXdhaXQgc291cmNlRGIucmF3KFxuICAgIFwiU0VMRUNUIENPVU5UKCopIGFzIGNvdW50IEZST00gaW5mb3JtYXRpb25fc2NoZW1hLnRhYmxlcyBXSEVSRSB0YWJsZV9zY2hlbWEgPSA/IEFORCB0YWJsZV9uYW1lID0gJ2tuZXhfbWlncmF0aW9ucydcIixcbiAgICBbc3JjQ29ubi5kYXRhYmFzZV0sXG4gICk7XG4gIGlmIChtaWdyYXRpb25zLmNvdW50ID4gMCkge1xuICAgIGF3YWl0IHdyaXRlTXlzcWxEdW1wKFxuICAgICAgW1xuICAgICAgICAuLi5teXNxbENvbm5lY3Rpb25BcmdzKHNyY0Nvbm4pLFxuICAgICAgICBcIi0tc2luZ2xlLXRyYW5zYWN0aW9uXCIsXG4gICAgICAgIFwiLS1uby1jcmVhdGUtZGJcIixcbiAgICAgICAgXCItLXRyaWdnZXJzXCIsXG4gICAgICAgIFN0cmluZyhzcmNDb25uLmRhdGFiYXNlKSxcbiAgICAgICAgXCJrbmV4X21pZ3JhdGlvbnNcIixcbiAgICAgICAgXCJrbmV4X21pZ3JhdGlvbnNfbG9ja1wiLFxuICAgICAgXSxcbiAgICAgIG1pZ3JhdGlvbnNEdW1wLFxuICAgICk7XG4gIH1cblxuICAvLyAyLiDrjIDsg4FEQiDqsIHqsIHsl5Ag64yA7ZWY7JesIOyhtOyerOyXrOu2gCDtmZXsnbgg7ZuEIOu2k+q4sFxuICBmb3IgKGNvbnN0IHsgbGFiZWwsIGNvbmZpZywgdG9Ta2lwIH0gb2YgdGFyZ2V0cykge1xuICAgIGNvbnN0IGNvbm4gPVxuICAgICAgLyogU0FGRVRZOiBDTEkg7YyM7ISc7JmAIOu5jOuTnCDrj4Tqtawg7J6F66ClIOqzhOyVveydtCDsnbQg6rCS7J2YIO2DgOyeheydhCDrs7TsnqXtlZzri6QuICovIGNvbmZpZy5jb25uZWN0aW9uIGFzIEtuZXguQ29ubmVjdGlvbkNvbmZpZztcblxuICAgIGlmICh0b1NraXAgPT09IHRydWUpIHtcbiAgICAgIGNvbnNvbGUubG9nKGNoYWxrLnJlZChgJHtsYWJlbH06IFNraXBwZWQhYCkpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gU0FGRVRZOiBTb25hbXUgREIg6rWs7ISx7J2AIEtuZXgg7Jew6rKwIOq1rOyEseycvOuhnCDqsoDspp3rkJwg65KkIOydtCDrqoXroLnsl5Ag7KCE64us65Cc64ukLlxuICAgIGNvbnN0IGRhdGFiYXNlQ29ubmVjdGlvbiA9IChjb25maWcuY29ubmVjdGlvbiA/PyB7fSkgYXMgS25leC5Db25uZWN0aW9uQ29uZmlnO1xuICAgIGNvbnN0IGRiID0ga25leCh7XG4gICAgICAuLi5jb25maWcsXG4gICAgICBjb25uZWN0aW9uOiB7XG4gICAgICAgIC4uLmRhdGFiYXNlQ29ubmVjdGlvbixcbiAgICAgICAgZGF0YWJhc2U6IHVuZGVmaW5lZCxcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgY29uc3QgW1tyb3ddXSA9IGF3YWl0IGRiLnJhdyhgU0hPVyBEQVRBQkFTRVMgTElLRSBcIiR7Y29ubi5kYXRhYmFzZX1cImApO1xuICAgIGlmIChyb3cpIHtcbiAgICAgIGNvbnNvbGUubG9nKGNoYWxrLnllbGxvdyhgJHtsYWJlbH06IERhdGFiYXNlIFwiJHtjb25uLmRhdGFiYXNlfVwiIEFscmVhZHkgZXhpc3RzYCkpO1xuICAgICAgYXdhaXQgZGIuZGVzdHJveSgpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgY29uc29sZS5sb2coYFNZTkMgdG8gJHtsYWJlbH0uLi5gKTtcbiAgICBjb25zdCBjb25uZWN0aW9uQXJncyA9IG15c3FsQ29ubmVjdGlvbkFyZ3MoY29ubik7XG4gICAgY29uc3QgZGF0YWJhc2UgPSBTdHJpbmcoY29ubi5kYXRhYmFzZSk7XG4gICAgY29uc3QgZXNjYXBlZERhdGFiYXNlID0gZGF0YWJhc2UucmVwbGFjZUFsbChcImBcIiwgXCJgYFwiKTtcbiAgICBydW5NeXNxbChbLi4uY29ubmVjdGlvbkFyZ3MsIFwiLWVcIiwgYERST1AgREFUQUJBU0UgSUYgRVhJU1RTIFxcYCR7ZXNjYXBlZERhdGFiYXNlfVxcYGBdKTtcbiAgICBydW5NeXNxbChbLi4uY29ubmVjdGlvbkFyZ3MsIFwiLWVcIiwgYENSRUFURSBEQVRBQkFTRSBcXGAke2VzY2FwZWREYXRhYmFzZX1cXGBgXSk7XG4gICAgYXdhaXQgcnVuTXlzcWxXaXRoSW5wdXQoWy4uLmNvbm5lY3Rpb25BcmdzLCBkYXRhYmFzZV0sIGR1bXBGaWxlbmFtZSk7XG4gICAgaWYgKGF3YWl0IGV4aXN0cyhtaWdyYXRpb25zRHVtcCkpIHtcbiAgICAgIGF3YWl0IHJ1bk15c3FsV2l0aElucHV0KFsuLi5jb25uZWN0aW9uQXJncywgZGF0YWJhc2VdLCBtaWdyYXRpb25zRHVtcCk7XG4gICAgfVxuXG4gICAgYXdhaXQgZGIuZGVzdHJveSgpO1xuICB9XG5cbiAgYXdhaXQgc291cmNlRGIuZGVzdHJveSgpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBmaXh0dXJlX2ltcG9ydChlbnRpdHlJZDogc3RyaW5nLCByZWNvcmRJZHM6IG51bWJlcltdKSB7XG4gIGF3YWl0IHNldHVwRml4dHVyZU1hbmFnZXIoKTtcblxuICBhd2FpdCBGaXh0dXJlTWFuYWdlci5pbXBvcnRGaXh0dXJlKGVudGl0eUlkLCByZWNvcmRJZHMpO1xuICBhd2FpdCBGaXh0dXJlTWFuYWdlci5zeW5jKCk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGZpeHR1cmVfc3luYygpIHtcbiAgYXdhaXQgc2V0dXBGaXh0dXJlTWFuYWdlcigpO1xuXG4gIGF3YWl0IEZpeHR1cmVNYW5hZ2VyLnN5bmMoKTtcbn1cblxuLyoqXG4gKiBmaXh0dXJlIGdlbiDrqoXroLnslrRcbiAqIOyYteyFmOydhCBwcm9jZXNzLmFyZ3bsl5DshJwg7YyM7IuxXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGZpeHR1cmVfZ2VuKCkge1xuICBjb25zdCBvcHRpb25zID0gcGFyc2VPcHRpb25zKHByb2Nlc3MuYXJndik7XG4gIGF3YWl0IGZpeHR1cmVHZW5Db21tYW5kKG9wdGlvbnMpO1xufVxuXG4vKipcbiAqIGZpeHR1cmUgZmV0Y2gg66qF66C57Ja0XG4gKiDsmLXshZjsnYQgcHJvY2Vzcy5hcmd27JeQ7IScIO2MjOyLsVxuICovXG5hc3luYyBmdW5jdGlvbiBmaXh0dXJlX2ZldGNoKCkge1xuICBjb25zdCBvcHRpb25zID0gcGFyc2VPcHRpb25zKHByb2Nlc3MuYXJndik7XG4gIGF3YWl0IGZpeHR1cmVGZXRjaENvbW1hbmQob3B0aW9ucyk7XG59XG5cbi8qKlxuICogZml4dHVyZSBleHBsb3JlIOuqheugueyWtFxuICog7Ji17IWY7J2EIHByb2Nlc3MuYXJnduyXkOyEnCDtjIzsi7FcbiAqL1xuYXN5bmMgZnVuY3Rpb24gZml4dHVyZV9leHBsb3JlKCkge1xuICBjb25zdCBvcHRpb25zID0gcGFyc2VPcHRpb25zKHByb2Nlc3MuYXJndik7XG4gIGF3YWl0IGZpeHR1cmVFeHBsb3JlQ29tbWFuZChvcHRpb25zKTtcbn1cblxuLyoqXG4gKiDqsITri6jtlZwg7Ji17IWYIO2MjOyEnFxuICovXG5mdW5jdGlvbiBwYXJzZU9wdGlvbnMoXG4gIGFyZ3Y6IHN0cmluZ1tdLFxuKTogUmVjb3JkPHN0cmluZywgc3RyaW5nIHwgYm9vbGVhbiB8IHN0cmluZ1tdPiAmIHsgXzogc3RyaW5nW10gfSB7XG4gIGNvbnN0IG9wdGlvbnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IGJvb2xlYW4gfCBzdHJpbmdbXT4gJiB7IF86IHN0cmluZ1tdIH0gPSB7IF86IFtdIH07XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBhcmd2Lmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgYXJnID0gYXJndltpXTtcblxuICAgIGlmIChhcmcuc3RhcnRzV2l0aChcIi0tXCIpKSB7XG4gICAgICBjb25zdCBrZXkgPSBhcmcuc2xpY2UoMik7XG5cbiAgICAgIGlmIChrZXkuaW5jbHVkZXMoXCI9XCIpKSB7XG4gICAgICAgIGNvbnN0IFtrLCB2XSA9IGtleS5zcGxpdChcIj1cIik7XG4gICAgICAgIG9wdGlvbnNba10gPSB2O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgbmV4dCA9IGFyZ3ZbaSArIDFdO1xuICAgICAgICBpZiAobmV4dCAmJiAhbmV4dC5zdGFydHNXaXRoKFwiLS1cIikpIHtcbiAgICAgICAgICBvcHRpb25zW2tleV0gPSBuZXh0O1xuICAgICAgICAgIGkrKztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBvcHRpb25zW2tleV0gPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChhcmcuc3RhcnRzV2l0aChcIi1cIikpIHtcbiAgICAgIGNvbnN0IGtleSA9IGFyZy5zbGljZSgxKTtcbiAgICAgIGNvbnN0IG5leHQgPSBhcmd2W2kgKyAxXTtcblxuICAgICAgaWYgKG5leHQgJiYgIW5leHQuc3RhcnRzV2l0aChcIi1cIikpIHtcbiAgICAgICAgb3B0aW9uc1trZXldID0gbmV4dDtcbiAgICAgICAgaSsrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb3B0aW9uc1trZXldID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgb3B0aW9ucy5fLnB1c2goYXJnKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gb3B0aW9ucztcbn1cblxuYXN5bmMgZnVuY3Rpb24gc3R1Yl9wcmFjdGljZShuYW1lOiBzdHJpbmcpIHtcbiAgY29uc3QgcHJhY3RpY2VEaXIgPSBwYXRoLmpvaW4oU29uYW11LmFwaVJvb3RQYXRoLCBcInNyY1wiLCBcInByYWN0aWNlc1wiKTtcbiAgY29uc3QgZmlsZU5hbWVzID0gYXdhaXQgcmVhZGRpcihwcmFjdGljZURpcik7XG5cbiAgY29uc3QgbWF4U2VxTm8gPSBhd2FpdCAoYXN5bmMgKCkgPT4ge1xuICAgIGlmICghKGF3YWl0IGV4aXN0cyhwcmFjdGljZURpcikpKSB7XG4gICAgICBhd2FpdCBta2RpcihwcmFjdGljZURpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfVxuXG4gICAgY29uc3QgZmlsdGVyZWRTZXFzID0gZmlsZU5hbWVzXG4gICAgICAuZmlsdGVyKChmaWxlTmFtZSkgPT4gZmlsZU5hbWUuc3RhcnRzV2l0aChcInBcIikgJiYgZmlsZU5hbWUuZW5kc1dpdGgoXCIudHNcIikpXG4gICAgICAubWFwKChmaWxlTmFtZSkgPT4ge1xuICAgICAgICBjb25zdCBbLCBzZXFOb10gPSBmaWxlTmFtZS5tYXRjaCgvXnAoWzAtOV0rKS0vKSA/PyBbXCIwXCIsIFwiMFwiXTtcbiAgICAgICAgcmV0dXJuIHBhcnNlSW50KHNlcU5vKTtcbiAgICAgIH0pXG4gICAgICAudG9Tb3J0ZWQoKGEsIGIpID0+IGIgLSBhKTtcblxuICAgIGlmIChmaWx0ZXJlZFNlcXMubGVuZ3RoID4gMCkge1xuICAgICAgcmV0dXJuIGZpbHRlcmVkU2Vxc1swXTtcbiAgICB9XG5cbiAgICByZXR1cm4gMDtcbiAgfSkoKTtcblxuICBjb25zdCBjdXJyZW50U2VxTm8gPSBtYXhTZXFObyArIDE7XG4gIGNvbnN0IGZpbGVOYW1lID0gYHAke2N1cnJlbnRTZXFOb30tJHtuYW1lfS50c2A7XG4gIGNvbnN0IGRzdFBhdGggPSBwYXRoLmpvaW4ocHJhY3RpY2VEaXIsIGZpbGVOYW1lKTtcblxuICBjb25zdCBjb2RlID0gW1xuICAgIGBpbXBvcnQgeyBTb25hbXUgfSBmcm9tIFwic29uYW11XCI7YCxcbiAgICBcIlwiLFxuICAgIGBjb25zb2xlLmNsZWFyKCk7YCxcbiAgICBgY29uc29sZS5sb2coXCIke2ZpbGVOYW1lfVwiKTtgLFxuICAgIFwiXCIsXG4gICAgYFNvbmFtdS5ydW5TY3JpcHQoYXN5bmMgKCkgPT4ge2AsXG4gICAgYCAvLyBUT0RPYCxcbiAgICBgfSk7YCxcbiAgICBcIlwiLFxuICBdLmpvaW4oXCJcXG5cIik7XG4gIGF3YWl0IHdyaXRlRmlsZShkc3RQYXRoLCBjb2RlKTtcblxuICBvcGVuSW5Db2RlKGRzdFBhdGgpO1xuXG4gIGNvbnN0IHJ1bkNvZGUgPSBgeWFybiBub2RlIC1yIGRvdGVudi9jb25maWcgLS1lbmFibGUtc291cmNlLW1hcHMgZGlzdC9wcmFjdGljZXMvJHtmaWxlTmFtZS5yZXBsYWNlKFxuICAgIFwiLnRzXCIsXG4gICAgXCIuanNcIixcbiAgKX1gO1xuICBjb25zb2xlLmxvZyhgJHtjaGFsay5ibHVlKHJ1bkNvZGUpfSBjb3BpZWQgdG8gY2xpcGJvYXJkLmApO1xuICBjb3B5VG9DbGlwYm9hcmQocnVuQ29kZSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHN0dWJfZW50aXR5KGVudGl0eUlkOiBzdHJpbmcpIHtcbiAgYXdhaXQgU29uYW11LnN5bmNlci5jcmVhdGVFbnRpdHkoeyBlbnRpdHlJZCwgdGl0bGU6IGVudGl0eUlkIH0pO1xuXG4gIGNvbnN0IHsgZmxhZ3MgfSA9IHBhcnNlQ2xpT3B0aW9ucygpO1xuICBjb25zdCB1c2VBSSA9IGZsYWdzLmhhcyhcImFpXCIpO1xuICBjb25zdCBub0NvbmVzID0gZmxhZ3MuaGFzKFwibm8tY29uZXNcIik7XG5cbiAgLy8gLS1uby1jb25lczogY29uZSDsg53shLEg7Iqk7YK1XG4gIGlmIChub0NvbmVzKSB7XG4gICAgY29uc29sZS5sb2coYOKckyBFbnRpdHkgJyR7ZW50aXR5SWR9JyBjcmVhdGVkIHdpdGhvdXQgY29uZXNgKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCBlbnRpdHkgPSBFbnRpdHlNYW5hZ2VyLmdldChlbnRpdHlJZCk7XG4gIGlmICghZW50aXR5KSB7XG4gICAgY29uc29sZS5lcnJvcihgRW50aXR5IG5vdCBmb3VuZDogJHtlbnRpdHlJZH1gKTtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyAtLWFpOiBMTE3snLzroZwgY29uZSDsg53shLFcbiAgaWYgKHVzZUFJKSB7XG4gICAgY29uc29sZS5sb2coYOKckyBFbnRpdHkgJyR7ZW50aXR5SWR9JyBjcmVhdGVkYCk7XG4gICAgY29uc29sZS5sb2coYPCfjJ8gR2VuZXJhdGluZyBBSS1wb3dlcmVkIGNvbmVzLi4uYCk7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNvbmZpZ0xvY2FsZSA9IFNvbmFtdS5jb25maWcuaTE4bj8uZGVmYXVsdExvY2FsZTtcbiAgICAgIGNvbnN0IGxvY2FsZSA9XG4gICAgICAgIGNvbmZpZ0xvY2FsZSA9PT0gXCJrb1wiIHx8IGNvbmZpZ0xvY2FsZSA9PT0gXCJlblwiIHx8IGNvbmZpZ0xvY2FsZSA9PT0gXCJqYVwiXG4gICAgICAgICAgPyBjb25maWdMb2NhbGVcbiAgICAgICAgICA6IFwia29cIjtcblxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZW50aXR5LmdlbmVyYXRlQ29uZXMoe1xuICAgICAgICBwcmVzZXJ2ZUV4aXN0aW5nOiBmYWxzZSxcbiAgICAgICAgb25seUVtcHR5OiBmYWxzZSxcbiAgICAgICAgbG9jYWxlLFxuICAgICAgfSk7XG5cbiAgICAgIGNvbnNvbGUubG9nKGDinIUgRG9uZSAoJHtyZXN1bHQudG9rZW5zVXNlZH0gdG9rZW5zKWApO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvciAmJiBlcnJvci5tZXNzYWdlLmluY2x1ZGVzKFwiQU5USFJPUElDX0FQSV9LRVlcIikpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgXFxu4p2MICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgICAgICAgY29uc29sZS5lcnJvcihgXFxu8J+SoSBSZW1vdmUgLS1haSBmbGFnIHRvIHVzZSB0ZW1wbGF0ZSBjb25lcyBpbnN0ZWFkYCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8g6riw67O4OiDthZztlIzrpr8gY29uZSDsnpDrj5kg7IOd7ISxXG4gIC8vIExMTSDsl4bsnbQgZmFrZXItbWFwcGluZ3MudHPrpbwg7Zmc7Jqp7ZWY7JesIOq4sOuzuCBjb25lIOuplO2DgOuNsOydtO2EsOulvCDsg53shLHtlanri4jri6QuXG4gIC8vIOydtOulvCDthrXtlbQgQU5USFJPUElDX0FQSV9LRVnqsIAg7JeG7Ja064+EIFNvbmFtdeulvCDsgqzsmqntlaAg7IiYIOyeiOycvOupsCxcbiAgLy8g7IOd7ISx65CcIO2FnO2UjOumvyBjb25l7J2AIOuCmOykkeyXkCAnY29uZSBnZW4nIOuqheugueyWtOuhnCBBSeulvCDthrXtlbQg7JeF6re466CI7J2065Oc7ZWgIOyImCDsnojsirXri4jri6QuXG4gIGNvbnNvbGUubG9nKGDwn4yfIEdlbmVyYXRpbmcgdGVtcGxhdGUgY29uZXMuLi5gKTtcbiAgYXdhaXQgZW50aXR5LmdlbmVyYXRlVGVtcGxhdGVDb25lcygpO1xuICBjb25zb2xlLmxvZyhg4pyTIEVudGl0eSAnJHtlbnRpdHlJZH0nIGNyZWF0ZWQgd2l0aCB0ZW1wbGF0ZSBjb25lc2ApO1xuICBjb25zb2xlLmxvZyhg8J+SoSBUaXA6IFJ1biAncG5wbSBzb25hbXUgY29uZSBnZW4gJHtlbnRpdHlJZH0nIHRvIGltcHJvdmUgd2l0aCBBSWApO1xufVxuXG4vKipcbiAqIEFJ66W8IOyCrOyaqe2VmOyXrCBlbnRpdHnsnZggY29uZSDrqZTtg4DrjbDsnbTthLDrpbwg7IOd7ISx7ZWY6rGw64KYIOyXheq3uOugiOydtOuTnO2VqeuLiOuLpC5cbiAqXG4gKiDsmLXshZg6XG4gKiAtIC0tcmVnZW5lcmF0ZTog7KCE7LK0IOyerOyDneyEsSAo6riw7KG0IGNvbmUg642u7Ja07JOw6riwKVxuICogLSAtLWxvY2FsZSA8a298ZW58amE+OiDsg53shLEg7Ja47Ja0IOyngOyglVxuICogLSDquLDrs7g6IG9ubHlFbXB0eSDrqqjrk5wgKOq4sOyhtCBmaXh0dXJlSGludCDrs7TsobQpXG4gKlxuICogQU5USFJPUElDX0FQSV9LRVkg7ZWE7JqUIChzb25hbXUuc2VjcmV0LnRzIOuYkOuKlCDtmZjqsr3rs4DsiJgpXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGNvbmVfZ2VuKGVudGl0eUlkOiBzdHJpbmcpIHtcbiAgY29uc3QgeyBmbGFncywgb3B0aW9ucyB9ID0gcGFyc2VDbGlPcHRpb25zKCk7XG5cbiAgLy8gLS1hbGwg7Ji17IWYOiDrqqjrk6AgZW50aXR5IGNvbmUg7IOd7ISxXG4gIGlmIChmbGFncy5oYXMoXCJhbGxcIikgfHwgZW50aXR5SWQgPT09IFwiYWxsXCIpIHtcbiAgICBjb25zdCBhbGxFbnRpdGllcyA9IEVudGl0eU1hbmFnZXIuZ2V0QWxsRW50aXRpZXMoKTtcbiAgICBjb25zb2xlLmxvZyhg8J+MnyBHZW5lcmF0aW5nIEFJLXBvd2VyZWQgY29uZXMgZm9yICR7YWxsRW50aXRpZXMubGVuZ3RofSBlbnRpdGllcy4uLlxcbmApO1xuXG4gICAgbGV0IHRvdGFsVG9rZW5zID0gMDtcbiAgICBjb25zdCBlcnJvcnM6IHN0cmluZ1tdID0gW107XG5cbiAgICBmb3IgKGNvbnN0IGVudGl0eSBvZiBhbGxFbnRpdGllcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc29sZS5sb2coYFByb2Nlc3NpbmcgJHtlbnRpdHkuaWR9Li4uYCk7XG4gICAgICAgIGNvbnN0IGNvbmZpZ0xvY2FsZSA9IG9wdGlvbnMubG9jYWxlIHx8IFNvbmFtdS5jb25maWcuaTE4bj8uZGVmYXVsdExvY2FsZTtcbiAgICAgICAgY29uc3QgbG9jYWxlID1cbiAgICAgICAgICBjb25maWdMb2NhbGUgPT09IFwia29cIiB8fCBjb25maWdMb2NhbGUgPT09IFwiZW5cIiB8fCBjb25maWdMb2NhbGUgPT09IFwiamFcIlxuICAgICAgICAgICAgPyBjb25maWdMb2NhbGVcbiAgICAgICAgICAgIDogXCJrb1wiO1xuXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGVudGl0eS5nZW5lcmF0ZUNvbmVzKHtcbiAgICAgICAgICBwcmVzZXJ2ZUV4aXN0aW5nOiAhZmxhZ3MuaGFzKFwicmVnZW5lcmF0ZVwiKSxcbiAgICAgICAgICBvbmx5RW1wdHk6ICFmbGFncy5oYXMoXCJyZWdlbmVyYXRlXCIpLFxuICAgICAgICAgIGxvY2FsZSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdG90YWxUb2tlbnMgKz0gcmVzdWx0LnRva2Vuc1VzZWQ7XG4gICAgICAgIGNvbnNvbGUubG9nKGAgIOKckyAke2VudGl0eS5pZH0gKCR7cmVzdWx0LnRva2Vuc1VzZWR9IHRva2VucylcXG5gKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGNvbnN0IG1lc3NhZ2UgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFwiVW5rbm93biBlcnJvclwiO1xuICAgICAgICBlcnJvcnMucHVzaChgJHtlbnRpdHkuaWR9OiAke21lc3NhZ2V9YCk7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYCAg4pyXICR7ZW50aXR5LmlkfTogJHttZXNzYWdlfVxcbmApO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKGBcXG7inIUgRG9uZSEgVG90YWw6ICR7dG90YWxUb2tlbnN9IHRva2VucyB1c2VkYCk7XG4gICAgY29uc3QgZXN0aW1hdGVkQ29zdCA9ICh0b3RhbFRva2VucyAqIDkpIC8gMV8wMDBfMDAwO1xuICAgIGNvbnNvbGUubG9nKGDwn5KwIEVzdGltYXRlZCBjb3N0OiB+JCR7ZXN0aW1hdGVkQ29zdC50b0ZpeGVkKDQpfWApO1xuXG4gICAgaWYgKGVycm9ycy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGBcXG7inYwgRmFpbGVkIGVudGl0aWVzICgke2Vycm9ycy5sZW5ndGh9KTpgKTtcbiAgICAgIGZvciAoY29uc3QgZXJyIG9mIGVycm9ycykge1xuICAgICAgICBjb25zb2xlLmVycm9yKGAgIC0gJHtlcnJ9YCk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIOuLqOydvCBlbnRpdHkgY29uZSDsg53shLFcbiAgY29uc3QgZW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQoZW50aXR5SWQpO1xuICBpZiAoIWVudGl0eSkge1xuICAgIGNvbnNvbGUuZXJyb3IoYEVudGl0eSBub3QgZm91bmQ6ICR7ZW50aXR5SWR9YCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgbW9kZSA9IGZsYWdzLmhhcyhcInJlZ2VuZXJhdGVcIikgPyBcInJlZ2VuZXJhdGluZ1wiIDogXCJnZW5lcmF0aW5nXCI7XG4gIGNvbnNvbGUubG9nKFxuICAgIGDwn4yfICR7bW9kZSA9PT0gXCJyZWdlbmVyYXRpbmdcIiA/IFwiUmVnZW5lcmF0aW5nXCIgOiBcIkdlbmVyYXRpbmdcIn0gQUktcG93ZXJlZCBjb25lcyBmb3IgJHtlbnRpdHlJZH0uLi5gLFxuICApO1xuXG4gIHRyeSB7XG4gICAgY29uc3QgY29uZmlnTG9jYWxlID0gb3B0aW9ucy5sb2NhbGUgfHwgU29uYW11LmNvbmZpZy5pMThuPy5kZWZhdWx0TG9jYWxlO1xuICAgIGNvbnN0IGxvY2FsZSA9XG4gICAgICBjb25maWdMb2NhbGUgPT09IFwia29cIiB8fCBjb25maWdMb2NhbGUgPT09IFwiZW5cIiB8fCBjb25maWdMb2NhbGUgPT09IFwiamFcIiA/IGNvbmZpZ0xvY2FsZSA6IFwia29cIjtcblxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGVudGl0eS5nZW5lcmF0ZUNvbmVzKHtcbiAgICAgIHByZXNlcnZlRXhpc3Rpbmc6ICFmbGFncy5oYXMoXCJyZWdlbmVyYXRlXCIpLFxuICAgICAgb25seUVtcHR5OiAhZmxhZ3MuaGFzKFwicmVnZW5lcmF0ZVwiKSxcbiAgICAgIGxvY2FsZSxcbiAgICB9KTtcblxuICAgIGNvbnNvbGUubG9nKGDinIUgRG9uZSEgKCR7cmVzdWx0LnRva2Vuc1VzZWR9IHRva2VucyB1c2VkKWApO1xuXG4gICAgLy8g7Yag7YGwIOu5hOyaqSDqs4TsgrAgKOuMgOueteyggeyduCDstpTsoJUpXG4gICAgLy8gQ2xhdWRlIFNvbm5ldCA0LjU6IGlucHV0ICQzL00gdG9rZW5zLCBvdXRwdXQgJDE1L00gdG9rZW5zXG4gICAgLy8g6rCE64uo7ZWY6rKMIO2Pieq3oCAkOS9NIHRva2Vuc+uhnCDqs4TsgrBcbiAgICBjb25zdCBlc3RpbWF0ZWRDb3N0ID0gKHJlc3VsdC50b2tlbnNVc2VkICogOSkgLyAxXzAwMF8wMDA7XG4gICAgY29uc29sZS5sb2coYPCfkrAgRXN0aW1hdGVkIGNvc3Q6IH4kJHtlc3RpbWF0ZWRDb3N0LnRvRml4ZWQoNCl9YCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgIGlmIChlcnJvci5tZXNzYWdlLmluY2x1ZGVzKFwiQU5USFJPUElDX0FQSV9LRVlcIikpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgXFxu4p2MICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgICAgICAgY29uc29sZS5lcnJvcihgXFxu8J+SoSBUbyB1c2UgQUktcG93ZXJlZCBjb25lIGdlbmVyYXRpb246YCk7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYCAgIDEuIEdldCBhbiBBUEkga2V5IGZyb20gaHR0cHM6Ly9jb25zb2xlLmFudGhyb3BpYy5jb20vYCk7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgYCAgIDIuIEFkZCBpdCB0byBzb25hbXUuc2VjcmV0LnRzIG9yIHNldCBBTlRIUk9QSUNfQVBJX0tFWSBlbnZpcm9ubWVudCB2YXJpYWJsZWAsXG4gICAgICAgICk7XG4gICAgICB9IGVsc2UgaWYgKGVycm9yLm1lc3NhZ2UuaW5jbHVkZXMoXCJSYXRlIGxpbWl0XCIpKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYFxcbuKdjCAke2Vycm9yLm1lc3NhZ2V9YCk7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYFxcbvCfkqEgUGxlYXNlIHdhaXQgYSBtb21lbnQgYW5kIHRyeSBhZ2Fpbi5gKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYFxcbuKdjCBGYWlsZWQgdG8gZ2VuZXJhdGUgY29uZXM6ICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgY29uc29sZS5lcnJvcihgXFxu4p2MIEZhaWxlZCB0byBnZW5lcmF0ZSBjb25lczogVW5rbm93biBlcnJvcmApO1xuICAgIH1cbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBzY2FmZm9sZF9tb2RlbChlbnRpdHlJZDogc3RyaW5nKSB7XG4gIGF3YWl0IFNvbmFtdS5zeW5jZXIuZ2VuZXJhdGVUZW1wbGF0ZShcIm1vZGVsXCIsIHtcbiAgICBlbnRpdHlJZCxcbiAgfSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHNjYWZmb2xkX21vZGVsX3Rlc3QoZW50aXR5SWQ6IHN0cmluZykge1xuICBhd2FpdCBTb25hbXUuc3luY2VyLmdlbmVyYXRlVGVtcGxhdGUoXCJtb2RlbF90ZXN0XCIsIHtcbiAgICBlbnRpdHlJZCxcbiAgfSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHNjYWZmb2xkX3ZpZXdfbGlzdChlbnRpdHlJZDogc3RyaW5nKSB7XG4gIGF3YWl0IFNvbmFtdS5zeW5jZXIuZ2VuZXJhdGVUZW1wbGF0ZShcInZpZXdfbGlzdFwiLCB7XG4gICAgZW50aXR5SWQsXG4gICAgZXh0cmE6IHVuZGVmaW5lZCxcbiAgfSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHNjYWZmb2xkX3ZpZXdfZm9ybShlbnRpdHlJZDogc3RyaW5nKSB7XG4gIGF3YWl0IFNvbmFtdS5zeW5jZXIuZ2VuZXJhdGVUZW1wbGF0ZShcInZpZXdfZm9ybVwiLCB7XG4gICAgZW50aXR5SWQsXG4gIH0pO1xufVxuXG5hc3luYyBmdW5jdGlvbiBhdXRoX2dlbmVyYXRlKCkge1xuICAvLyAtLXBsdWdpbnMg7Ji17IWYIO2MjOyLsVxuICBjb25zdCBwbHVnaW5zQXJnID0gcHJvY2Vzcy5hcmd2LmZpbmQoKGFyZykgPT4gYXJnLnN0YXJ0c1dpdGgoXCItLXBsdWdpbnNcIikpO1xuICBjb25zdCBwbHVnaW5zOiBCZXR0ZXJBdXRoUGx1Z2luSWRbXSA9IFtdO1xuXG4gIGlmIChwbHVnaW5zQXJnKSB7XG4gICAgY29uc3QgcGx1Z2luVmFsdWUgPSBwbHVnaW5zQXJnLmluY2x1ZGVzKFwiPVwiKVxuICAgICAgPyBwbHVnaW5zQXJnLnNwbGl0KFwiPVwiKVsxXVxuICAgICAgOiBwcm9jZXNzLmFyZ3ZbcHJvY2Vzcy5hcmd2LmluZGV4T2YocGx1Z2luc0FyZykgKyAxXTtcblxuICAgIGlmIChwbHVnaW5WYWx1ZSkge1xuICAgICAgY29uc3QgcGx1Z2luSWRzID0gcGx1Z2luVmFsdWUuc3BsaXQoXCIsXCIpLm1hcCgocCkgPT4gcC50cmltKCkpO1xuXG4gICAgICBmb3IgKGNvbnN0IGlkIG9mIHBsdWdpbklkcykge1xuICAgICAgICBpZiAoaXNWYWxpZFBsdWdpbklkKGlkKSkge1xuICAgICAgICAgIHBsdWdpbnMucHVzaChpZCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc29sZS5sb2coY2hhbGsueWVsbG93KGDimqAgVW5rbm93biBwbHVnaW46ICR7aWR9YCkpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmRpbShgICBTdXBwb3J0ZWQgcGx1Z2luczogJHtTVVBQT1JURURfUExVR0lOX0lEUy5qb2luKFwiLCBcIil9YCkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgY29uc29sZS5sb2coY2hhbGsueWVsbG93LmJvbGQoXCLwn5SQIEdlbmVyYXRpbmcgYmV0dGVyLWF1dGggZW50aXRpZXMuLi5cXG5cIikpO1xuXG4gIGlmIChwbHVnaW5zLmxlbmd0aCA+IDApIHtcbiAgICBjb25zb2xlLmxvZyhjaGFsay5kaW0oYCAgUGx1Z2luczogJHtwbHVnaW5zLmpvaW4oXCIsIFwiKX1gKSk7XG4gIH1cblxuICBhd2FpdCBnZW5lcmF0ZUJldHRlckF1dGhFbnRpdGllcyh7IHBsdWdpbnMgfSk7XG59XG5cbi8qKlxuICogcG5wbSBzb25hbXUgYXV0aCBhZGQtY29tcGFuaW9ucyDtlZjrqbQg7Iuk7ZaJ65CY64qUIO2VqOyImOyeheuLiOuLpC5cbiAqIOq4sOyhtCDtlITroZzsoJ3tirjsnZggZW50aXR5Lmpzb27sl5AgZml4dHVyZUNvbXBhbmlvbnPrpbwg7IaM6riJIOy2lOqwgO2VqeuLiOuLpC5cbiAqXG4gKiDsnbTrr7ggZml4dHVyZUNvbXBhbmlvbnPqsIAg7J6I64qUIGVudGl0eeuKlCDsiqTtgrXtlanri4jri6QgKOuNruyWtOyTsOq4sCDsl4bsnYwpLlxuICovXG5hc3luYyBmdW5jdGlvbiBhdXRoX2FkZF9jb21wYW5pb25zKCkge1xuICBjb25zb2xlLmxvZyhjaGFsay55ZWxsb3cuYm9sZChcIvCflJAgQWRkaW5nIGZpeHR1cmVDb21wYW5pb25zIHRvIGJldHRlci1hdXRoIGVudGl0aWVzLi4uXFxuXCIpKTtcbiAgYXdhaXQgYWRkQ29tcGFuaW9uc1RvRW50aXRpZXMoKTtcbiAgY29uc29sZS5sb2coY2hhbGsuYm9sZChcIlxcbuKchSBEb25lIVwiKSk7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7WUFZc0M7b0JBS2tCO1NBQ1g7Y0FDRztxQkFDVztrQkFRN0I7Y0FDWTtXQUN1QjtBQWNqRSxJQUFJO0FBRUosU0FBUyxTQUNQLE1BQ0EsUUFBNEQ7Q0FBQztDQUFVO0NBQVc7QUFBUyxHQUMzRjtDQUNBLE1BQU0sU0FBUyxVQUFVLFNBQVMsTUFBTSxFQUFFLE1BQU0sQ0FBQztDQUNqRCxJQUFJLE9BQU8sT0FDVCxNQUFNLE9BQU87Q0FFZixJQUFJLE9BQU8sV0FBVyxHQUNwQixNQUFNLElBQUksTUFBTSxtQkFBbUIsT0FBTyxVQUFVLFVBQVUsVUFBVTtBQUU1RTtBQUVBLGVBQWUsZUFBZSxNQUFn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
|