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.
Files changed (233) hide show
  1. package/dist/ai/agents/agent.d.ts.map +1 -1
  2. package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
  3. package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
  4. package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
  5. package/dist/api/base-frame.d.ts.map +1 -1
  6. package/dist/api/decorators.d.ts.map +1 -1
  7. package/dist/api/sonamu.d.ts.map +1 -1
  8. package/dist/api/sonamu.js +3 -2
  9. package/dist/api/validator.d.ts.map +1 -1
  10. package/dist/auth/audit-log/events.d.ts.map +1 -1
  11. package/dist/auth/index.d.ts +1 -1
  12. package/dist/auth/index.d.ts.map +1 -1
  13. package/dist/auth/index.js +3 -3
  14. package/dist/auth/knex-adapter.d.ts.map +1 -1
  15. package/dist/auth/plugins/wrappers/admin.d.ts +4 -4
  16. package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
  17. package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -1
  18. package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -1
  19. package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -1
  20. package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -1
  21. package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -1
  22. package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
  23. package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -1
  24. package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
  25. package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
  26. package/dist/bin/build-config.d.ts +10 -0
  27. package/dist/bin/build-config.d.ts.map +1 -1
  28. package/dist/bin/build-config.js +130 -6
  29. package/dist/bin/fixture.d.ts +7 -4
  30. package/dist/bin/fixture.d.ts.map +1 -1
  31. package/dist/bin/fixture.js +161 -13
  32. package/dist/bin/test-command.d.ts +7 -0
  33. package/dist/bin/test-command.d.ts.map +1 -1
  34. package/dist/bin/test-command.js +18 -4
  35. package/dist/bin/typecheck-web.d.ts +2 -0
  36. package/dist/bin/typecheck-web.d.ts.map +1 -0
  37. package/dist/bin/typecheck-web.js +36 -0
  38. package/dist/cache/decorator.d.ts.map +1 -1
  39. package/dist/cache/drivers.d.ts.map +1 -1
  40. package/dist/cache-control/cache-control.d.ts.map +1 -1
  41. package/dist/compress/compress.d.ts.map +1 -1
  42. package/dist/cone/cone-generator.js +1 -1
  43. package/dist/database/base-model.d.ts.map +1 -1
  44. package/dist/database/base-model.types.d.ts +0 -1
  45. package/dist/database/base-model.types.d.ts.map +1 -1
  46. package/dist/database/db.d.ts.map +1 -1
  47. package/dist/database/puri-subset.types.d.ts +0 -1
  48. package/dist/database/puri-subset.types.d.ts.map +1 -1
  49. package/dist/database/puri-wrapper.d.ts +0 -1
  50. package/dist/database/puri-wrapper.d.ts.map +1 -1
  51. package/dist/database/puri.d.ts +0 -1
  52. package/dist/database/puri.d.ts.map +1 -1
  53. package/dist/database/puri.types.d.ts +0 -1
  54. package/dist/database/puri.types.d.ts.map +1 -1
  55. package/dist/database/transaction-context.d.ts.map +1 -1
  56. package/dist/database/upsert-builder.d.ts.map +1 -1
  57. package/dist/dict/en.d.ts +58 -58
  58. package/dist/dict/en.d.ts.map +1 -1
  59. package/dist/dict/ko.d.ts +58 -58
  60. package/dist/dict/ko.d.ts.map +1 -1
  61. package/dist/dict/rc-keys.d.ts.map +1 -1
  62. package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
  63. package/dist/dict/sonamu-dictionary.js +2 -2
  64. package/dist/dict/utils.d.ts.map +1 -1
  65. package/dist/entity/entity-manager.d.ts +115 -113
  66. package/dist/entity/entity-manager.d.ts.map +1 -1
  67. package/dist/entity/entity-manager.js +25 -19
  68. package/dist/entity/entity-template-cone.d.ts +4 -4
  69. package/dist/entity/entity.d.ts +6 -1
  70. package/dist/entity/entity.d.ts.map +1 -1
  71. package/dist/entity/entity.js +8 -3
  72. package/dist/env.d.ts.map +1 -1
  73. package/dist/exceptions/so-exceptions.d.ts.map +1 -1
  74. package/dist/filter/types.d.ts.map +1 -1
  75. package/dist/index.js +6 -6
  76. package/dist/logger/configure.d.ts +10 -1
  77. package/dist/logger/configure.d.ts.map +1 -1
  78. package/dist/logger/configure.js +30 -5
  79. package/dist/migration/migration-set.d.ts.map +1 -1
  80. package/dist/migration/migration-set.js +17 -6
  81. package/dist/migration/migrator.d.ts +12 -1
  82. package/dist/migration/migrator.d.ts.map +1 -1
  83. package/dist/migration/migrator.js +77 -30
  84. package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
  85. package/dist/migration/slack-confirm.d.ts.map +1 -1
  86. package/dist/naite/messaging-types.d.ts +0 -1
  87. package/dist/naite/messaging-types.d.ts.map +1 -1
  88. package/dist/naite/naite-reporter.d.ts +0 -1
  89. package/dist/naite/naite-reporter.d.ts.map +1 -1
  90. package/dist/naite/naite.d.ts.map +1 -1
  91. package/dist/storage/base-file.d.ts.map +1 -1
  92. package/dist/storage/buffered-file.d.ts.map +1 -1
  93. package/dist/storage/drivers.d.ts.map +1 -1
  94. package/dist/storage/s3-driver.d.ts.map +1 -1
  95. package/dist/storage/storage-manager.d.ts.map +1 -1
  96. package/dist/storage/uploaded-file.d.ts.map +1 -1
  97. package/dist/stream/ws-audience-resolver.d.ts.map +1 -1
  98. package/dist/stream/ws-audience.d.ts.map +1 -1
  99. package/dist/stream/ws-cluster-bus.d.ts.map +1 -1
  100. package/dist/stream/ws-delivery.d.ts.map +1 -1
  101. package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
  102. package/dist/stream/ws-presence-store.d.ts.map +1 -1
  103. package/dist/stream/ws-registry.d.ts.map +1 -1
  104. package/dist/stream/ws-telemetry-memory.d.ts.map +1 -1
  105. package/dist/stream/ws-telemetry.d.ts.map +1 -1
  106. package/dist/stream/ws.d.ts.map +1 -1
  107. package/dist/syncer/api-parser.js +2 -2
  108. package/dist/syncer/file-patterns.d.ts +17 -17
  109. package/dist/syncer/filesystem-dependencies.js +1 -1
  110. package/dist/syncer/syncer.d.ts.map +1 -1
  111. package/dist/tasks/step-wrapper.d.ts.map +1 -1
  112. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  113. package/dist/template/implementations/entity.template.d.ts +2 -2
  114. package/dist/template/implementations/entity.template.d.ts.map +1 -1
  115. package/dist/template/implementations/entry-server.template.d.ts +2 -2
  116. package/dist/template/implementations/entry-server.template.d.ts.map +1 -1
  117. package/dist/template/implementations/generated.template.d.ts +2 -2
  118. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  119. package/dist/template/implementations/generated_http.template.d.ts +2 -2
  120. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  121. package/dist/template/implementations/generated_sso.template.d.ts +2 -2
  122. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  123. package/dist/template/implementations/http_validators.template.d.ts +2 -2
  124. package/dist/template/implementations/http_validators.template.d.ts.map +1 -1
  125. package/dist/template/implementations/init_types.template.d.ts +2 -2
  126. package/dist/template/implementations/init_types.template.d.ts.map +1 -1
  127. package/dist/template/implementations/model.template.d.ts +2 -2
  128. package/dist/template/implementations/model.template.d.ts.map +1 -1
  129. package/dist/template/implementations/model_test.template.d.ts +2 -2
  130. package/dist/template/implementations/model_test.template.d.ts.map +1 -1
  131. package/dist/template/implementations/queries.template.d.ts +2 -2
  132. package/dist/template/implementations/queries.template.d.ts.map +1 -1
  133. package/dist/template/implementations/sd.template.d.ts +2 -2
  134. package/dist/template/implementations/sd.template.d.ts.map +1 -1
  135. package/dist/template/implementations/sd.template.js +1 -1
  136. package/dist/template/implementations/services.template.d.ts +2 -2
  137. package/dist/template/implementations/services.template.d.ts.map +1 -1
  138. package/dist/template/implementations/view_form.template.d.ts +3 -3
  139. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  140. package/dist/template/implementations/view_id_all_select.template.d.ts +2 -2
  141. package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -1
  142. package/dist/template/implementations/view_list.template.d.ts +2 -2
  143. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  144. package/dist/template/implementations/view_search_input.template.d.ts +2 -2
  145. package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
  146. package/dist/template/template-manager.d.ts.map +1 -1
  147. package/dist/template/template.d.ts.map +1 -1
  148. package/dist/template/zod-converter.d.ts.map +1 -1
  149. package/dist/testing/_relation-graph.d.ts.map +1 -1
  150. package/dist/testing/data-explorer.d.ts +2 -2
  151. package/dist/testing/data-explorer.d.ts.map +1 -1
  152. package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
  153. package/dist/testing/faker-mappings.d.ts.map +1 -1
  154. package/dist/testing/fixture-generator.d.ts.map +1 -1
  155. package/dist/testing/fixture-generator.js +1 -1
  156. package/dist/testing/fixture-loader.d.ts.map +1 -1
  157. package/dist/testing/fixture-manager.d.ts +4 -0
  158. package/dist/testing/fixture-manager.d.ts.map +1 -1
  159. package/dist/testing/fixture-manager.js +99 -25
  160. package/dist/testing/naite-vitest-reporter.d.ts.map +1 -1
  161. package/dist/testing/parallel-db-manager.d.ts.map +1 -1
  162. package/dist/tooling/__tests__/helpers/sonamu-test-root.d.ts +10 -0
  163. package/dist/tooling/__tests__/helpers/sonamu-test-root.d.ts.map +1 -0
  164. package/dist/tooling/__tests__/helpers/sonamu-test-root.js +42 -0
  165. package/dist/tooling/cdd-service.d.ts +87 -0
  166. package/dist/tooling/cdd-service.d.ts.map +1 -0
  167. package/dist/tooling/cdd-service.js +215 -0
  168. package/dist/tooling/cli-tooling.d.ts +11 -0
  169. package/dist/tooling/cli-tooling.d.ts.map +1 -0
  170. package/dist/tooling/cli-tooling.js +901 -0
  171. package/dist/tooling/fixture-service.d.ts +29 -0
  172. package/dist/tooling/fixture-service.d.ts.map +1 -0
  173. package/dist/tooling/fixture-service.js +53 -0
  174. package/dist/tooling/index.d.ts +2 -0
  175. package/dist/tooling/index.d.ts.map +1 -0
  176. package/dist/tooling/index.js +2 -0
  177. package/dist/tooling/test-run-client.d.ts +27 -0
  178. package/dist/tooling/test-run-client.d.ts.map +1 -0
  179. package/dist/tooling/test-run-client.js +120 -0
  180. package/dist/tsdown.api.config.js +48 -0
  181. package/dist/types/types.d.ts +527 -527
  182. package/dist/types/types.d.ts.map +1 -1
  183. package/dist/ui/ai-client.d.ts.map +1 -1
  184. package/dist/utils/formatter.d.ts +6 -0
  185. package/dist/utils/formatter.d.ts.map +1 -1
  186. package/dist/utils/formatter.js +42 -11
  187. package/dist/utils/model.d.ts +1 -1
  188. package/dist/vector/chunking.d.ts.map +1 -1
  189. package/dist/vector/embedding.d.ts.map +1 -1
  190. package/package.json +13 -12
  191. package/src/api/sonamu.ts +2 -1
  192. package/src/auth/index.ts +1 -1
  193. package/src/bin/__tests__/zod-compiler-build.test.ts +67 -0
  194. package/src/bin/build-config.ts +151 -5
  195. package/src/bin/fixture.ts +204 -21
  196. package/src/bin/test-command.ts +27 -4
  197. package/src/bin/typecheck-web.ts +56 -0
  198. package/src/dict/sonamu-dictionary.ts +1 -1
  199. package/src/entity/__tests__/entity-manager-autoload.contract.test.ts +55 -0
  200. package/src/entity/__tests__/entity-manager-explicit-root-registration.contract.test.ts +107 -0
  201. package/src/entity/entity-manager.ts +31 -19
  202. package/src/entity/entity.ts +7 -2
  203. package/src/logger/__tests__/configure.test.ts +33 -0
  204. package/src/logger/configure.ts +45 -3
  205. package/src/migration/__tests__/migrator.test.ts +51 -0
  206. package/src/migration/migration-set.ts +22 -5
  207. package/src/migration/migrator.ts +103 -34
  208. package/src/syncer/api-parser.ts +1 -1
  209. package/src/testing/fixture-manager.ts +124 -32
  210. package/src/tooling/__tests__/cdd-rule-bootstrap.contract.test.ts +118 -0
  211. package/src/tooling/__tests__/cli-tooling-auth.contract.test.ts +72 -0
  212. package/src/tooling/__tests__/cli-tooling-cone-locale.contract.test.ts +150 -0
  213. package/src/tooling/__tests__/cli-tooling-entity-patch.contract.test.ts +131 -0
  214. package/src/tooling/__tests__/cli-tooling-fixture-transfer.contract.test.ts +255 -0
  215. package/src/tooling/__tests__/cli-tooling-i18n-update.contract.test.ts +104 -0
  216. package/src/tooling/__tests__/cli-tooling-scaffold.contract.test.ts +86 -0
  217. package/src/tooling/__tests__/cli-tooling-stub-practice.contract.test.ts +147 -0
  218. package/src/tooling/__tests__/default-cdd-adapter.contract.test.ts +73 -0
  219. package/src/tooling/__tests__/helpers/sonamu-test-root.ts +42 -0
  220. package/src/tooling/__tests__/test-run-client-config.contract.test.ts +154 -0
  221. package/src/tooling/cdd-service.ts +256 -0
  222. package/src/tooling/cli-tooling.ts +937 -0
  223. package/src/tooling/fixture-service.ts +72 -0
  224. package/src/tooling/index.ts +1 -0
  225. package/src/tooling/test-run-client.ts +196 -0
  226. package/src/utils/__tests__/formatter-bin.test.ts +99 -0
  227. package/src/utils/formatter.ts +74 -10
  228. package/tsdown.api.config.ts +8 -10
  229. package/bin/cli.js +0 -75
  230. package/dist/bin/cli.d.ts +0 -2
  231. package/dist/bin/cli.d.ts.map +0 -1
  232. package/dist/bin/cli.js +0 -956
  233. 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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