sonamu 0.7.53 → 0.8.0

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 (271) hide show
  1. package/dist/api/config.d.ts +9 -1
  2. package/dist/api/config.d.ts.map +1 -1
  3. package/dist/api/config.js +1 -1
  4. package/dist/api/sonamu.d.ts +21 -1
  5. package/dist/api/sonamu.d.ts.map +1 -1
  6. package/dist/api/sonamu.js +159 -65
  7. package/dist/auth/plugins/entity-definitions/anonymous.d.ts +10 -0
  8. package/dist/auth/plugins/entity-definitions/anonymous.d.ts.map +1 -0
  9. package/dist/auth/plugins/entity-definitions/anonymous.js +23 -0
  10. package/dist/auth/plugins/entity-definitions/api-key.d.ts +9 -0
  11. package/dist/auth/plugins/entity-definitions/api-key.d.ts.map +1 -0
  12. package/dist/auth/plugins/entity-definitions/api-key.js +199 -0
  13. package/dist/auth/plugins/entity-definitions/index.d.ts +6 -0
  14. package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
  15. package/dist/auth/plugins/entity-definitions/index.js +20 -2
  16. package/dist/auth/plugins/entity-definitions/jwt.d.ts +9 -0
  17. package/dist/auth/plugins/entity-definitions/jwt.d.ts.map +1 -0
  18. package/dist/auth/plugins/entity-definitions/jwt.js +67 -0
  19. package/dist/auth/plugins/entity-definitions/organization.d.ts +9 -0
  20. package/dist/auth/plugins/entity-definitions/organization.d.ts.map +1 -0
  21. package/dist/auth/plugins/entity-definitions/organization.js +424 -0
  22. package/dist/auth/plugins/entity-definitions/passkey.d.ts +10 -0
  23. package/dist/auth/plugins/entity-definitions/passkey.d.ts.map +1 -0
  24. package/dist/auth/plugins/entity-definitions/passkey.js +129 -0
  25. package/dist/auth/plugins/entity-definitions/sso.d.ts +10 -0
  26. package/dist/auth/plugins/entity-definitions/sso.d.ts.map +1 -0
  27. package/dist/auth/plugins/entity-definitions/sso.js +110 -0
  28. package/dist/auth/plugins/entity-definitions/types.d.ts +1 -1
  29. package/dist/auth/plugins/entity-definitions/types.d.ts.map +1 -1
  30. package/dist/auth/plugins/entity-definitions/types.js +1 -1
  31. package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
  32. package/dist/auth/plugins/wrappers/admin.js +2 -4
  33. package/dist/auth/plugins/wrappers/anonymous.d.ts +18 -0
  34. package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -0
  35. package/dist/auth/plugins/wrappers/anonymous.js +26 -0
  36. package/dist/auth/plugins/wrappers/api-key.d.ts +18 -0
  37. package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -0
  38. package/dist/auth/plugins/wrappers/api-key.js +38 -0
  39. package/dist/auth/plugins/wrappers/index.d.ts +6 -0
  40. package/dist/auth/plugins/wrappers/index.d.ts.map +1 -1
  41. package/dist/auth/plugins/wrappers/index.js +7 -1
  42. package/dist/auth/plugins/wrappers/jwt.d.ts +18 -0
  43. package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -0
  44. package/dist/auth/plugins/wrappers/jwt.js +30 -0
  45. package/dist/auth/plugins/wrappers/organization.d.ts +18 -0
  46. package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -0
  47. package/dist/auth/plugins/wrappers/organization.js +67 -0
  48. package/dist/auth/plugins/wrappers/passkey.d.ts +18 -0
  49. package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -0
  50. package/dist/auth/plugins/wrappers/passkey.js +32 -0
  51. package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
  52. package/dist/auth/plugins/wrappers/phone-number.js +2 -4
  53. package/dist/auth/plugins/wrappers/sso.d.ts +853 -0
  54. package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -0
  55. package/dist/auth/plugins/wrappers/sso.js +36 -0
  56. package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
  57. package/dist/auth/plugins/wrappers/two-factor.js +2 -4
  58. package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
  59. package/dist/auth/plugins/wrappers/username.js +2 -4
  60. package/dist/bin/build-config.d.ts +2 -2
  61. package/dist/bin/build-config.js +6 -7
  62. package/dist/bin/cli.js +417 -32
  63. package/dist/bin/fixture.d.ts +27 -0
  64. package/dist/bin/fixture.d.ts.map +1 -0
  65. package/dist/bin/fixture.js +245 -0
  66. package/dist/cache/decorator.d.ts +4 -3
  67. package/dist/cache/decorator.d.ts.map +1 -1
  68. package/dist/cache/decorator.js +5 -4
  69. package/dist/cone/cone-generator.d.ts +33 -0
  70. package/dist/cone/cone-generator.d.ts.map +1 -0
  71. package/dist/cone/cone-generator.js +286 -0
  72. package/dist/database/_batch_update.d.ts.map +1 -1
  73. package/dist/database/_batch_update.js +16 -2
  74. package/dist/database/puri-subset.test-d.js +1 -1
  75. package/dist/database/puri-subset.types.d.ts +1 -1
  76. package/dist/database/puri-subset.types.d.ts.map +1 -1
  77. package/dist/database/puri-subset.types.js +1 -1
  78. package/dist/database/puri.d.ts +4 -0
  79. package/dist/database/puri.d.ts.map +1 -1
  80. package/dist/database/puri.js +20 -2
  81. package/dist/database/upsert-builder.d.ts.map +1 -1
  82. package/dist/database/upsert-builder.js +19 -3
  83. package/dist/dict/en.d.ts +15 -0
  84. package/dist/dict/en.d.ts.map +1 -1
  85. package/dist/dict/en.js +2 -1
  86. package/dist/dict/ko.d.ts +15 -0
  87. package/dist/dict/ko.d.ts.map +1 -1
  88. package/dist/dict/ko.js +2 -1
  89. package/dist/dict/rc-keys.d.ts +28 -0
  90. package/dist/dict/rc-keys.d.ts.map +1 -1
  91. package/dist/dict/rc-keys.js +31 -1
  92. package/dist/dict/sd.d.ts.map +1 -1
  93. package/dist/dict/sd.js +20 -4
  94. package/dist/entity/entity-manager.d.ts +298 -2
  95. package/dist/entity/entity-manager.d.ts.map +1 -1
  96. package/dist/entity/entity-manager.js +4 -1
  97. package/dist/entity/entity-template-cone.d.ts +14 -0
  98. package/dist/entity/entity-template-cone.d.ts.map +1 -0
  99. package/dist/entity/entity-template-cone.js +222 -0
  100. package/dist/entity/entity.d.ts +47 -2
  101. package/dist/entity/entity.d.ts.map +1 -1
  102. package/dist/entity/entity.js +161 -14
  103. package/dist/ssr/renderer.js +3 -3
  104. package/dist/syncer/api-parser.js +12 -1
  105. package/dist/syncer/checksum.d.ts +0 -14
  106. package/dist/syncer/checksum.d.ts.map +1 -1
  107. package/dist/syncer/checksum.js +1 -23
  108. package/dist/syncer/syncer-actions.d.ts.map +1 -1
  109. package/dist/syncer/syncer-actions.js +8 -2
  110. package/dist/syncer/syncer.d.ts +1 -1
  111. package/dist/syncer/syncer.d.ts.map +1 -1
  112. package/dist/syncer/syncer.js +17 -10
  113. package/dist/tasks/workflow-manager.d.ts +13 -1
  114. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  115. package/dist/tasks/workflow-manager.js +18 -1
  116. package/dist/template/entity-converter.js +4 -4
  117. package/dist/template/helpers.d.ts +10 -0
  118. package/dist/template/helpers.d.ts.map +1 -1
  119. package/dist/template/helpers.js +48 -1
  120. package/dist/template/implementations/entry-server.template.d.ts +1 -1
  121. package/dist/template/implementations/entry-server.template.js +7 -2
  122. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  123. package/dist/template/implementations/generated.template.js +5 -1
  124. package/dist/template/implementations/generated_http.template.d.ts +1 -0
  125. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  126. package/dist/template/implementations/generated_http.template.js +6 -2
  127. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  128. package/dist/template/implementations/generated_sso.template.js +29 -8
  129. package/dist/template/implementations/queries.template.d.ts.map +1 -1
  130. package/dist/template/implementations/queries.template.js +9 -1
  131. package/dist/template/implementations/sd.template.d.ts +1 -1
  132. package/dist/template/implementations/sd.template.d.ts.map +1 -1
  133. package/dist/template/implementations/sd.template.js +28 -4
  134. package/dist/template/implementations/services.template.d.ts.map +1 -1
  135. package/dist/template/implementations/services.template.js +12 -12
  136. package/dist/template/implementations/view_form.template.d.ts +11 -7
  137. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  138. package/dist/template/implementations/view_form.template.js +97 -87
  139. package/dist/template/implementations/view_list.template.d.ts +3 -3
  140. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  141. package/dist/template/implementations/view_list.template.js +115 -109
  142. package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
  143. package/dist/template/implementations/view_search_input.template.js +18 -14
  144. package/dist/template/zod-converter.d.ts.map +1 -1
  145. package/dist/template/zod-converter.js +95 -7
  146. package/dist/testing/_relation-graph.js +1 -1
  147. package/dist/testing/data-explorer.d.ts +61 -0
  148. package/dist/testing/data-explorer.d.ts.map +1 -0
  149. package/dist/testing/data-explorer.js +274 -0
  150. package/dist/testing/faker-mappings.d.ts +20 -0
  151. package/dist/testing/faker-mappings.d.ts.map +1 -0
  152. package/dist/testing/faker-mappings.js +421 -0
  153. package/dist/testing/fixture-generator.d.ts +161 -0
  154. package/dist/testing/fixture-generator.d.ts.map +1 -0
  155. package/dist/testing/fixture-generator.js +954 -0
  156. package/dist/testing/fixture-manager.d.ts +6 -1
  157. package/dist/testing/fixture-manager.d.ts.map +1 -1
  158. package/dist/testing/fixture-manager.js +72 -4
  159. package/dist/testing/index.d.ts +3 -0
  160. package/dist/testing/index.d.ts.map +1 -1
  161. package/dist/testing/index.js +4 -1
  162. package/dist/types/types.d.ts +1520 -26
  163. package/dist/types/types.d.ts.map +1 -1
  164. package/dist/types/types.js +136 -22
  165. package/dist/ui/ai-client.d.ts.map +1 -1
  166. package/dist/ui/ai-client.js +9 -4
  167. package/dist/ui/api.d.ts.map +1 -1
  168. package/dist/ui/api.js +303 -24
  169. package/dist/ui-web/assets/index-CsUr-_pV.js +254 -0
  170. package/dist/ui-web/assets/index-T42zzs1K.css +1 -0
  171. package/dist/ui-web/index.html +2 -2
  172. package/dist/utils/fs-utils.d.ts +2 -1
  173. package/dist/utils/fs-utils.d.ts.map +1 -1
  174. package/dist/utils/fs-utils.js +14 -3
  175. package/package.json +19 -11
  176. package/src/api/config.ts +12 -1
  177. package/src/api/sonamu.ts +179 -65
  178. package/src/auth/plugins/entity-definitions/anonymous.ts +17 -0
  179. package/src/auth/plugins/entity-definitions/api-key.ts +93 -0
  180. package/src/auth/plugins/entity-definitions/index.ts +18 -0
  181. package/src/auth/plugins/entity-definitions/jwt.ts +35 -0
  182. package/src/auth/plugins/entity-definitions/organization.ts +215 -0
  183. package/src/auth/plugins/entity-definitions/passkey.ts +64 -0
  184. package/src/auth/plugins/entity-definitions/sso.ts +62 -0
  185. package/src/auth/plugins/entity-definitions/types.ts +11 -1
  186. package/src/auth/plugins/wrappers/admin.ts +1 -3
  187. package/src/auth/plugins/wrappers/anonymous.ts +30 -0
  188. package/src/auth/plugins/wrappers/api-key.ts +42 -0
  189. package/src/auth/plugins/wrappers/index.ts +6 -0
  190. package/src/auth/plugins/wrappers/jwt.ts +34 -0
  191. package/src/auth/plugins/wrappers/organization.ts +73 -0
  192. package/src/auth/plugins/wrappers/passkey.ts +36 -0
  193. package/src/auth/plugins/wrappers/phone-number.ts +1 -3
  194. package/src/auth/plugins/wrappers/sso.ts +40 -0
  195. package/src/auth/plugins/wrappers/two-factor.ts +1 -3
  196. package/src/auth/plugins/wrappers/username.ts +1 -3
  197. package/src/bin/build-config.ts +6 -6
  198. package/src/bin/cli.ts +452 -31
  199. package/src/bin/fixture.ts +302 -0
  200. package/src/cache/decorator.ts +4 -3
  201. package/src/cone/cone-generator.ts +363 -0
  202. package/src/database/_batch_update.ts +11 -0
  203. package/src/database/puri-subset.test-d.ts +13 -13
  204. package/src/database/puri-subset.types.ts +1 -1
  205. package/src/database/puri.ts +43 -1
  206. package/src/database/upsert-builder.ts +16 -2
  207. package/src/dict/en.ts +1 -0
  208. package/src/dict/ko.ts +1 -0
  209. package/src/dict/rc-keys.ts +32 -0
  210. package/src/dict/sd.ts +23 -3
  211. package/src/entity/entity-manager.ts +4 -0
  212. package/src/entity/entity-template-cone.ts +298 -0
  213. package/src/entity/entity.ts +189 -13
  214. package/src/shared/app.shared.ts.txt +5 -0
  215. package/src/shared/web.shared.ts.txt +9 -5
  216. package/src/skills/project/README.md +21 -0
  217. package/src/skills/project/architecture.md +373 -0
  218. package/src/skills/project/business-logic.md +270 -0
  219. package/src/skills/project/requirements.md +160 -0
  220. package/src/skills/sonamu/SKILL.md +168 -3
  221. package/src/skills/sonamu/api.md +102 -0
  222. package/src/skills/sonamu/database.md +220 -1
  223. package/src/skills/sonamu/entity-relations.md +89 -1
  224. package/src/skills/sonamu/fixture-cli.md +501 -0
  225. package/src/skills/sonamu/frontend.md +214 -0
  226. package/src/skills/sonamu/i18n.md +95 -0
  227. package/src/skills/sonamu/model.md +153 -0
  228. package/src/skills/sonamu/project-init.md +178 -8
  229. package/src/skills/sonamu/scaffolding.md +112 -0
  230. package/src/skills/sonamu/subset.md +9 -3
  231. package/src/skills/sonamu/testing.md +287 -2
  232. package/src/skills/sonamu/workflow.md +70 -5
  233. package/src/ssr/renderer.ts +2 -2
  234. package/src/syncer/api-parser.ts +12 -0
  235. package/src/syncer/checksum.ts +0 -38
  236. package/src/syncer/syncer-actions.ts +7 -1
  237. package/src/syncer/syncer.ts +16 -5
  238. package/src/tasks/workflow-manager.ts +29 -8
  239. package/src/template/entity-converter.ts +3 -3
  240. package/src/template/helpers.ts +49 -0
  241. package/src/template/implementations/entry-server.template.ts +1 -1
  242. package/src/template/implementations/generated.template.ts +4 -0
  243. package/src/template/implementations/generated_http.template.ts +1 -0
  244. package/src/template/implementations/generated_sso.template.ts +40 -11
  245. package/src/template/implementations/queries.template.ts +8 -0
  246. package/src/template/implementations/sd.template.ts +22 -3
  247. package/src/template/implementations/services.template.ts +11 -10
  248. package/src/template/implementations/view_form.template.ts +111 -101
  249. package/src/template/implementations/view_list.template.ts +120 -119
  250. package/src/template/implementations/view_search_input.template.ts +17 -13
  251. package/src/template/zod-converter.ts +103 -6
  252. package/src/testing/_relation-graph.ts +1 -1
  253. package/src/testing/data-explorer.ts +427 -0
  254. package/src/testing/faker-mappings.ts +434 -0
  255. package/src/testing/fixture-generator.ts +1166 -0
  256. package/src/testing/fixture-manager.ts +91 -6
  257. package/src/testing/index.ts +3 -0
  258. package/src/types/types.ts +222 -26
  259. package/src/ui/ai-client.ts +9 -1
  260. package/src/ui/api.ts +429 -23
  261. package/src/utils/fs-utils.ts +14 -1
  262. package/dist/template/implementations/view_enums_select.template.d.ts +0 -17
  263. package/dist/template/implementations/view_enums_select.template.d.ts.map +0 -1
  264. package/dist/template/implementations/view_enums_select.template.js +0 -62
  265. package/dist/template/implementations/view_id_async_select.template.d.ts +0 -17
  266. package/dist/template/implementations/view_id_async_select.template.d.ts.map +0 -1
  267. package/dist/template/implementations/view_id_async_select.template.js +0 -125
  268. package/dist/ui-web/assets/index-Bd_2AkLb.css +0 -1
  269. package/dist/ui-web/assets/index-BpSbhQWo.js +0 -225
  270. package/src/template/implementations/view_enums_select.template.ts +0 -65
  271. package/src/template/implementations/view_id_async_select.template.ts +0 -139
@@ -0,0 +1,954 @@
1
+ import chalk from "chalk";
2
+ import { isBelongsToOneRelationProp, isOneToOneRelationProp, isRelationProp } from "../types/types.js";
3
+ import { DataExplorer } from "./data-explorer.js";
4
+ import { fakerMappings } from "./faker-mappings.js";
5
+ import { FixtureManager } from "./fixture-manager.js";
6
+ export class FixtureGenerator {
7
+ sourceDb;
8
+ targetDbName;
9
+ entityManager;
10
+ dataExplorer;
11
+ locale;
12
+ mappings;
13
+ llmCache = new Map();
14
+ options;
15
+ constructor(sourceDb, // FixtureManager.insertFixtures가 dbName 문자열을 받기 때문에 직접 사용하지 않습니다
16
+ // 미래 확장성을 위해 API 시그니처에는 포함시켰습니다
17
+ _targetDb, targetDbName, entityManager, options){
18
+ this.sourceDb = sourceDb;
19
+ this.targetDbName = targetDbName;
20
+ this.entityManager = entityManager;
21
+ this.dataExplorer = new DataExplorer(sourceDb, entityManager);
22
+ this.locale = options?.locale || "ko";
23
+ this.mappings = fakerMappings;
24
+ this.options = {
25
+ locale: options?.locale || "ko",
26
+ useLLM: options?.useLLM || false,
27
+ enableLLMCache: options?.enableLLMCache !== false,
28
+ llmModel: options?.llmModel || "claude-sonnet-4-5"
29
+ };
30
+ }
31
+ /**
32
+ * Fixture 생성 (단일)
33
+ * @returns 생성된 fixture 데이터 (메모리 상)
34
+ */ async generate(entityName, overrides = {}, context = this.createContext()) {
35
+ const entity = this.entityManager.get(entityName);
36
+ const tempId = `${entityName}#temp#${Date.now()}`; // 임시 ID
37
+ // 각 prop별 값 생성
38
+ const fixture = {};
39
+ for (const prop of entity.props){
40
+ // Virtual prop은 스킵
41
+ if ("virtual" in prop && prop.virtual) {
42
+ continue;
43
+ }
44
+ // override가 있으면 사용
45
+ if (prop.name in overrides) {
46
+ fixture[prop.name] = overrides[prop.name];
47
+ continue;
48
+ }
49
+ // cone에서 생성 전략 확인
50
+ const cone = prop.cone;
51
+ // 1. Relation prop 처리
52
+ if (isRelationProp(prop)) {
53
+ const relationValue = await this.generateRelationValue(entity, prop, context);
54
+ // BelongsToOne, OneToOne(hasJoinColumn)의 경우 foreign key 컬럼명으로 저장
55
+ if (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop) && prop.hasJoinColumn) {
56
+ fixture[`${prop.name}_id`] = relationValue;
57
+ } else {
58
+ fixture[prop.name] = relationValue;
59
+ }
60
+ continue;
61
+ }
62
+ // 2. fixtureGenerator 사용
63
+ if (cone?.fixtureGenerator) {
64
+ fixture[prop.name] = await this.executeGenerator(cone.fixtureGenerator, prop, entity);
65
+ continue;
66
+ }
67
+ // 2.5. fixtureHint + LLM 사용
68
+ if (cone?.fixtureHint && this.options.useLLM) {
69
+ try {
70
+ fixture[prop.name] = await this.generateWithLLM(cone.fixtureHint, prop, entity);
71
+ continue;
72
+ } catch (error) {
73
+ console.warn(`[FixtureGenerator] LLM generation failed for ${entity.id}.${prop.name}, falling back to default`, error instanceof Error ? error.message : error);
74
+ // fallback: fixtureDefault 또는 기본값으로 계속
75
+ }
76
+ }
77
+ // 3. fixtureDefault 사용
78
+ if (cone?.fixtureDefault !== undefined) {
79
+ fixture[prop.name] = cone.fixtureDefault;
80
+ continue;
81
+ }
82
+ // 4. 타입별 기본 생성
83
+ fixture[prop.name] = await this.generateDefaultValue(prop, entity);
84
+ }
85
+ // 5. password 필드 암호화
86
+ if ("password" in fixture && fixture.password && typeof fixture.password === "string") {
87
+ const bcrypt = await import("bcrypt");
88
+ fixture.password = await bcrypt.hash(fixture.password, 10);
89
+ }
90
+ context.fixtures.set(tempId, fixture);
91
+ return fixture;
92
+ }
93
+ /**
94
+ * Relation 값 생성 + 자동 Import
95
+ */ async generateRelationValue(entity, prop, context) {
96
+ if (!isRelationProp(prop)) {
97
+ throw new Error(`FixtureGenerator: ${entity.id}.${prop.name} is not a relation prop`);
98
+ }
99
+ // BelongsToOne, OneToOne(hasJoinColumn)만 처리
100
+ if (!isBelongsToOneRelationProp(prop) && !(isOneToOneRelationProp(prop) && prop.hasJoinColumn)) {
101
+ return null;
102
+ }
103
+ const cone = prop.cone;
104
+ const dataSource = cone?.dataSource;
105
+ // DataExplorer로 참조 데이터 조회 (sourceDb)
106
+ // 관계 체인을 따라가기 위해 exploreWithRelations 사용
107
+ if (dataSource) {
108
+ const cacheKey = `${prop.with}:${JSON.stringify(dataSource)}`;
109
+ if (!context.referenceCache.has(cacheKey)) {
110
+ const exploreResult = await this.dataExplorer.exploreWithRelations(prop.with, {
111
+ strategy: dataSource.strategy,
112
+ limit: dataSource.config?.limit || 10,
113
+ includeRelations: true,
114
+ maxDepth: 3,
115
+ ...dataSource.config
116
+ });
117
+ context.referenceCache.set(cacheKey, exploreResult.main.records);
118
+ // 조회한 데이터와 관계된 모든 엔티티를 targetDb에 import
119
+ await this.importExploreResult(exploreResult, context);
120
+ }
121
+ const candidates = context.referenceCache.get(cacheKey);
122
+ if (candidates && candidates.length > 0) {
123
+ // 랜덤하게 하나 선택
124
+ const selected = candidates[Math.floor(Math.random() * candidates.length)];
125
+ return selected.id;
126
+ }
127
+ }
128
+ // dataSource가 없을 때 자동으로 fixture DB에서 조회 시도
129
+ // 관계 체인을 따라가기 위해 exploreWithRelations 사용
130
+ const autoKey = `${prop.with}:auto`;
131
+ if (!context.referenceCache.has(autoKey)) {
132
+ // fixture DB(sourceDb)에서 자동 조회 (관계 포함)
133
+ const autoExploreResult = await this.dataExplorer.exploreWithRelations(prop.with, {
134
+ strategy: "random",
135
+ limit: 10,
136
+ includeRelations: true,
137
+ maxDepth: 3
138
+ });
139
+ context.referenceCache.set(autoKey, autoExploreResult.main.records);
140
+ // 조회한 데이터와 관계된 모든 엔티티를 targetDb에 import
141
+ if (autoExploreResult.main.records.length > 0) {
142
+ await this.importExploreResult(autoExploreResult, context);
143
+ }
144
+ }
145
+ const autoCandidates = context.referenceCache.get(autoKey);
146
+ if (autoCandidates && autoCandidates.length > 0) {
147
+ // 랜덤하게 하나 선택
148
+ const selected = autoCandidates[Math.floor(Math.random() * autoCandidates.length)];
149
+ return selected.id;
150
+ }
151
+ // 참조 데이터가 없으면 null 반환 (nullable인 경우)
152
+ if (prop.nullable) {
153
+ return null;
154
+ }
155
+ // nullable이 아니고 데이터도 없으면 에러
156
+ throw new Error(`FixtureGenerator: ${entity.id}.${prop.name}에 필요한 ${prop.with} 데이터가 없습니다. ` + `먼저 ${prop.with}를 생성하거나 cone.dataSource를 설정하세요.`);
157
+ }
158
+ /**
159
+ * ExploreWithRelations 결과를 targetDb에 import
160
+ *
161
+ * 관계 체인을 따라간 결과(main + related)를 모두 import합니다.
162
+ * 의존성 순서는 FixtureManager.insertFixtures가 자동으로 처리합니다.
163
+ */ async importExploreResult(exploreResult, context) {
164
+ const allFixtureRecords = [];
165
+ // 1. Related entities import (Company, Department 등)
166
+ for (const [entityId, records] of exploreResult.related.entries()){
167
+ const entity = this.entityManager.get(entityId);
168
+ const recordsToImport = [];
169
+ console.log(chalk.cyan(`Importing related entity: ${entityId} (${records.length} records)`));
170
+ for (const record of records){
171
+ const recordKey = `${entityId}#${record.id}`;
172
+ if (!context.importedRecords.has(recordKey)) {
173
+ recordsToImport.push(record);
174
+ context.importedRecords.add(recordKey);
175
+ }
176
+ }
177
+ if (recordsToImport.length > 0) {
178
+ for (const record of recordsToImport){
179
+ console.log(chalk.gray(` - Processing ${entityId} record:`, JSON.stringify(record).slice(0, 100)));
180
+ const fixtureRecords = await FixtureManager.createFixtureRecord(entity, record, {
181
+ _db: this.sourceDb,
182
+ singleRecord: true
183
+ });
184
+ allFixtureRecords.push(...fixtureRecords);
185
+ }
186
+ }
187
+ }
188
+ // 2. Main entity import (Employee 등)
189
+ const mainEntity = this.entityManager.get(exploreResult.main.entityId);
190
+ const mainRecordsToImport = [];
191
+ console.log(chalk.cyan(`Importing main entity: ${exploreResult.main.entityId} (${exploreResult.main.records.length} records)`));
192
+ for (const record of exploreResult.main.records){
193
+ const recordKey = `${exploreResult.main.entityId}#${record.id}`;
194
+ if (!context.importedRecords.has(recordKey)) {
195
+ mainRecordsToImport.push(record);
196
+ context.importedRecords.add(recordKey);
197
+ }
198
+ }
199
+ if (mainRecordsToImport.length > 0) {
200
+ for (const record of mainRecordsToImport){
201
+ console.log(chalk.gray(` - Processing ${exploreResult.main.entityId} record:`, JSON.stringify(record).slice(0, 100)));
202
+ const fixtureRecords = await FixtureManager.createFixtureRecord(mainEntity, record, {
203
+ _db: this.sourceDb,
204
+ singleRecord: true
205
+ });
206
+ allFixtureRecords.push(...fixtureRecords);
207
+ }
208
+ }
209
+ // 3. 모든 fixture를 한 번에 삽입 (의존성 순서 자동 처리)
210
+ if (allFixtureRecords.length > 0) {
211
+ await FixtureManager.insertFixtures(this.targetDbName, allFixtureRecords);
212
+ console.log(chalk.green(`Auto-imported ${exploreResult.main.entityId} with relations: ` + `${exploreResult.main.records.length} main + ${exploreResult.related.size} related entities`));
213
+ }
214
+ }
215
+ /**
216
+ * fixtureGenerator 실행 (Faker.js만 지원)
217
+ *
218
+ * faker.* 형식의 표현식을 안전하게 파싱하여 실행합니다.
219
+ * 예: "faker.internet.email()" → faker.internet.email()
220
+ * 예: "faker.lorem.words(3)" → faker.lorem.words(3)
221
+ */ async executeGenerator(generator, prop, entity) {
222
+ // Faker.js 표현식만 지원
223
+ if (generator.startsWith("faker.")) {
224
+ // username이나 name 필드는 한국어 faker 사용
225
+ const isNameField = prop.name === "username" || prop.name === "name";
226
+ const fakerModule = await import("@faker-js/faker");
227
+ const faker = isNameField ? fakerModule.fakerKO : fakerModule.faker;
228
+ const expr = generator.slice(6); // "faker." 제거
229
+ try {
230
+ // 함수 경로와 인자 파싱
231
+ const match = expr.match(/^([\w.]+)(?:\((.*?)\))?$/);
232
+ if (!match) {
233
+ throw new Error(`FixtureGenerator: Invalid faker expression for ${prop.name}: ${generator}`);
234
+ }
235
+ const [, path, argsStr] = match;
236
+ const parts = path.split(".");
237
+ // faker 객체에서 함수 찾기
238
+ let fn = faker;
239
+ for (const part of parts){
240
+ if (typeof fn === "object" && fn !== null && part in fn) {
241
+ fn = fn[part];
242
+ } else {
243
+ throw new Error(`FixtureGenerator: Invalid faker path for ${prop.name}: faker.${path}`);
244
+ }
245
+ }
246
+ // 함수가 아니면 에러
247
+ if (typeof fn !== "function") {
248
+ throw new Error(`FixtureGenerator: faker.${path} is not a function (for ${prop.name})`);
249
+ }
250
+ // 인자 파싱 (JSON 형식만 지원)
251
+ let args = [];
252
+ if (argsStr?.trim()) {
253
+ try {
254
+ // JSON 배열로 파싱 시도
255
+ const parsed = JSON.parse(`[${argsStr}]`);
256
+ args = Array.isArray(parsed) ? parsed : [
257
+ parsed
258
+ ];
259
+ } catch {
260
+ // 숫자나 문자열 단일 인자 처리
261
+ const trimmed = argsStr.trim();
262
+ if (!Number.isNaN(Number(trimmed))) {
263
+ args = [
264
+ Number(trimmed)
265
+ ];
266
+ } else if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
267
+ args = [
268
+ trimmed.slice(1, -1)
269
+ ];
270
+ } else {
271
+ throw new Error(`FixtureGenerator: Cannot parse arguments for ${prop.name}: ${argsStr}`);
272
+ }
273
+ }
274
+ }
275
+ return fn(...args);
276
+ } catch (error) {
277
+ console.log(chalk.yellow(`Failed to execute generator "${generator}" for ${prop.name}, falling back to default:`), error);
278
+ return this.generateDefaultValue(prop, entity);
279
+ }
280
+ }
281
+ // faker 이외의 표현식은 지원하지 않음
282
+ console.log(chalk.yellow(`Unsupported generator expression for ${prop.name}: ${generator}. Only faker.* expressions are supported. Using default value.`));
283
+ return this.generateDefaultValue(prop, entity);
284
+ }
285
+ /**
286
+ * 필드의 타입과 이름을 분석하여 적절한 기본값을 생성합니다.
287
+ *
288
+ * 우선순위:
289
+ * 1. 필드명 패턴 매칭 (salary, budget 등 의미있는 데이터)
290
+ * 2. 특수 케이스 (Department name 등 도메인 지식)
291
+ * 3. 배열 타입 (JSON 배열)
292
+ * 4. Enum 타입
293
+ * 5. 타입별 기본값
294
+ */ async generateDefaultValue(prop, entity) {
295
+ const fakerModule = await import("@faker-js/faker");
296
+ const faker = fakerModule.faker;
297
+ const fakerKO = fakerModule.fakerKO;
298
+ const fakerJA = fakerModule.fakerJA;
299
+ const localeFaker = this.locale === "ko" ? fakerKO : this.locale === "ja" ? fakerJA : faker;
300
+ /**
301
+ * 1. 필드명에서 의미를 추론하여 현실적인 데이터를 생성합니다.
302
+ * 예: salary → 30M~150M (한국 연봉 범위)
303
+ * budget → 10M~500M (프로젝트 예산 범위)
304
+ */ const localeMappings = this.mappings[this.locale] || this.mappings.en;
305
+ const normalizedName = prop.name.toLowerCase().replace(/_/g, "");
306
+ for (const [pattern, config] of Object.entries(localeMappings.field_patterns)){
307
+ if (normalizedName.includes(pattern.toLowerCase())) {
308
+ try {
309
+ return await this.executeFakerExpression(config.faker, prop);
310
+ } catch (error) {
311
+ console.log(chalk.yellow(`Failed to execute field pattern "${pattern}" for ${prop.name}, falling back:`), error);
312
+ break;
313
+ }
314
+ }
315
+ }
316
+ /**
317
+ * 2. Department name은 한국어 부서명 목록에서 선택합니다.
318
+ * 고유성을 위해 70% 확률로 prefix/suffix를 추가합니다.
319
+ */ if (entity?.id === "Department" && prop.name === "name") {
320
+ const departments = [
321
+ "개발팀",
322
+ "기획팀",
323
+ "마케팅팀",
324
+ "영업팀",
325
+ "인사팀",
326
+ "총무팀",
327
+ "재무팀",
328
+ "회계팀",
329
+ "법무팀",
330
+ "디자인팀",
331
+ "IT팀",
332
+ "고객지원팀",
333
+ "품질관리팀",
334
+ "연구개발팀",
335
+ "생산팀",
336
+ "구매팀",
337
+ "물류팀"
338
+ ];
339
+ const prefixes = [
340
+ "신규",
341
+ "통합",
342
+ "전략",
343
+ "글로벌",
344
+ "디지털",
345
+ "핵심"
346
+ ];
347
+ const suffixes = [
348
+ "1팀",
349
+ "2팀",
350
+ "3팀",
351
+ "A팀",
352
+ "B팀",
353
+ "본부",
354
+ "센터",
355
+ "그룹"
356
+ ];
357
+ const dept = faker.helpers.arrayElement(departments);
358
+ const random = Math.random();
359
+ if (random > 0.7) {
360
+ const prefix = faker.helpers.arrayElement(prefixes);
361
+ return `${prefix} ${dept}`;
362
+ }
363
+ if (random > 0.4) {
364
+ const suffix = faker.helpers.arrayElement(suffixes);
365
+ return `${dept} ${suffix}`;
366
+ }
367
+ return dept;
368
+ }
369
+ /**
370
+ * 3. JSON 타입이면서 배열인 경우 (SonamuFile[], string[] 등)
371
+ * 필드명 패턴을 보고 적절한 배열 데이터를 생성합니다.
372
+ */ if (prop.type === "json" && "id" in prop && prop.id) {
373
+ if (prop.id.endsWith("[]")) {
374
+ return this.generateArrayValue(prop, entity, faker, localeFaker);
375
+ }
376
+ }
377
+ /** 4. Enum 타입은 정의된 값 중 하나를 랜덤 선택합니다 */ if (prop.type === "enum") {
378
+ let enumValues = [];
379
+ if ("enum" in prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
380
+ enumValues = prop.enum;
381
+ } else if ("id" in prop && prop.id && entity?.enumLabels?.[prop.id]) {
382
+ enumValues = Object.keys(entity.enumLabels[prop.id]);
383
+ }
384
+ if (enumValues.length > 0) {
385
+ return faker.helpers.arrayElement(enumValues);
386
+ }
387
+ return prop.nullable ? null : "UNKNOWN";
388
+ }
389
+ if (prop.type === "enum[]") {
390
+ let enumValues = [];
391
+ if ("enum" in prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
392
+ enumValues = prop.enum;
393
+ } else if ("id" in prop && prop.id && entity?.enumLabels?.[prop.id]) {
394
+ enumValues = Object.keys(entity.enumLabels[prop.id]);
395
+ }
396
+ if (enumValues.length > 0) {
397
+ return [
398
+ faker.helpers.arrayElement(enumValues)
399
+ ];
400
+ }
401
+ return [];
402
+ }
403
+ /**
404
+ * 5. Vector 타입은 현재 지원하지 않으므로 null을 반환합니다.
405
+ * 향후 AI embedding 생성 기능 추가 시 구현 예정입니다.
406
+ */ if (prop.type === "vector" || prop.type === "vector[]" || prop.type === "tsvector") {
407
+ return null;
408
+ }
409
+ /** 6. 타입별 기본 Faker 표현식을 실행합니다 */ const typeDefault = localeMappings.type_defaults[prop.type];
410
+ if (typeDefault) {
411
+ try {
412
+ return await this.executeFakerExpression(typeDefault.faker, prop);
413
+ } catch (error) {
414
+ console.log(chalk.yellow(`Failed to execute type default for ${prop.type}, using fallback:`, error));
415
+ }
416
+ }
417
+ /** 7. 매핑되지 않은 타입은 기본 Faker 함수로 처리합니다 */ switch(prop.type){
418
+ case "string":
419
+ case "string[]":
420
+ return faker.lorem.words(3);
421
+ case "integer":
422
+ return faker.number.int({
423
+ min: 1,
424
+ max: 1000
425
+ });
426
+ case "integer[]":
427
+ return [
428
+ faker.number.int({
429
+ min: 1,
430
+ max: 1000
431
+ })
432
+ ];
433
+ case "bigInteger":
434
+ return faker.number.bigInt({
435
+ min: 1n,
436
+ max: 1000n
437
+ });
438
+ case "bigInteger[]":
439
+ return [
440
+ faker.number.bigInt({
441
+ min: 1n,
442
+ max: 1000n
443
+ })
444
+ ];
445
+ case "number":
446
+ case "numeric":
447
+ return faker.number.float({
448
+ min: 0,
449
+ max: 1000
450
+ });
451
+ case "number[]":
452
+ case "numeric[]":
453
+ return [
454
+ faker.number.float({
455
+ min: 0,
456
+ max: 1000
457
+ })
458
+ ];
459
+ case "boolean":
460
+ return faker.datatype.boolean();
461
+ case "boolean[]":
462
+ return [
463
+ faker.datatype.boolean()
464
+ ];
465
+ case "date":
466
+ case "date[]":
467
+ return faker.date.past();
468
+ case "json":
469
+ return {};
470
+ case "uuid":
471
+ case "uuid[]":
472
+ return faker.string.uuid();
473
+ default:
474
+ return null;
475
+ }
476
+ }
477
+ /**
478
+ * 배열 타입의 값을 생성합니다.
479
+ *
480
+ * 타입 ID와 필드명 패턴을 분석하여 적절한 배열 데이터를 생성합니다.
481
+ * 예: image_urls → [{url, name, mime_type}, ...]
482
+ * tag_ids → [1, 23, 45]
483
+ */ generateArrayValue(prop, _entity, faker, _localeFaker) {
484
+ const count = faker.number.int({
485
+ min: 1,
486
+ max: 3
487
+ });
488
+ /** SonamuFile[]은 Sonamu 내장 타입으로 구조가 정해져 있습니다 */ if ("id" in prop && prop.id === "SonamuFile[]") {
489
+ return Array.from({
490
+ length: count
491
+ }, ()=>({
492
+ url: faker.image.url(),
493
+ name: faker.system.fileName(),
494
+ mime_type: faker.helpers.arrayElement([
495
+ "image/jpeg",
496
+ "image/png",
497
+ "image/gif",
498
+ "application/pdf"
499
+ ])
500
+ }));
501
+ }
502
+ /** 필드명에서 배열의 용도를 추론합니다 */ const normalizedName = prop.name.toLowerCase().replace(/_/g, "");
503
+ if (normalizedName.includes("url") || normalizedName.includes("image")) {
504
+ return Array.from({
505
+ length: count
506
+ }, ()=>faker.internet.url());
507
+ }
508
+ if (normalizedName.includes("id") && normalizedName.endsWith("s")) {
509
+ return Array.from({
510
+ length: count
511
+ }, ()=>faker.number.int({
512
+ min: 1,
513
+ max: 100
514
+ }));
515
+ }
516
+ if (normalizedName.includes("tag") || normalizedName.includes("name")) {
517
+ return Array.from({
518
+ length: count
519
+ }, ()=>faker.lorem.word());
520
+ }
521
+ /** 패턴 매칭되지 않으면 빈 배열을 반환합니다 */ return [];
522
+ }
523
+ /**
524
+ * JSON 매핑의 Faker 표현식을 파싱하여 실행합니다.
525
+ *
526
+ * 표현식 예시:
527
+ * - "faker.internet.email()" → 인자 없음
528
+ * - "faker.number.int({ min: 1, max: 100 })" → JSON 인자
529
+ * - "{}" → 리터럴 값 (JSON.parse)
530
+ *
531
+ * fakerKO, fakerJA도 지원하여 다국어 데이터를 생성합니다.
532
+ */ async executeFakerExpression(expression, prop) {
533
+ const fakerModule = await import("@faker-js/faker");
534
+ const faker = fakerModule.faker;
535
+ const fakerKO = fakerModule.fakerKO;
536
+ const fakerJA = fakerModule.fakerJA;
537
+ /** Faker 표현식이 아닌 리터럴 값은 JSON으로 파싱합니다 */ if (!expression.startsWith("faker")) {
538
+ try {
539
+ return JSON.parse(expression);
540
+ } catch {
541
+ return expression;
542
+ }
543
+ }
544
+ /** 표현식에서 Faker 객체와 경로를 추출합니다 */ const match = expression.match(/^(faker|fakerKO|fakerJA)\.(.*?)$/);
545
+ if (!match) {
546
+ throw new Error(`Invalid faker expression: ${expression}`);
547
+ }
548
+ const [, fakerName, expr] = match;
549
+ const selectedFaker = fakerName === "fakerKO" ? fakerKO : fakerName === "fakerJA" ? fakerJA : faker;
550
+ const funcMatch = expr.match(/^([\w.]+)(?:\((.*?)\))?$/);
551
+ if (!funcMatch) {
552
+ throw new Error(`Invalid faker expression for ${prop.name}: ${expression}`);
553
+ }
554
+ const [, path, argsStr] = funcMatch;
555
+ const parts = path.split(".");
556
+ /** 점 표기법(dot notation)으로 Faker 함수를 찾아갑니다 */ let fn = selectedFaker;
557
+ for (const part of parts){
558
+ if (typeof fn === "object" && fn !== null && part in fn) {
559
+ fn = fn[part];
560
+ } else {
561
+ throw new Error(`Invalid faker path for ${prop.name}: ${fakerName}.${path}`);
562
+ }
563
+ }
564
+ if (typeof fn !== "function") {
565
+ throw new Error(`${fakerName}.${path} is not a function (for ${prop.name})`);
566
+ }
567
+ /** 함수 인자를 JSON으로 파싱합니다 */ let args = [];
568
+ if (argsStr?.trim()) {
569
+ try {
570
+ const parsed = JSON.parse(`[${argsStr}]`);
571
+ args = Array.isArray(parsed) ? parsed : [
572
+ parsed
573
+ ];
574
+ } catch {
575
+ /** JSON 파싱 실패 시 단순 숫자/문자열로 시도합니다 */ const trimmed = argsStr.trim();
576
+ if (!Number.isNaN(Number(trimmed))) {
577
+ args = [
578
+ Number(trimmed)
579
+ ];
580
+ } else if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
581
+ args = [
582
+ trimmed.slice(1, -1)
583
+ ];
584
+ } else {
585
+ throw new Error(`Cannot parse arguments for ${prop.name}: ${argsStr}`);
586
+ }
587
+ }
588
+ }
589
+ return fn(...args);
590
+ }
591
+ /**
592
+ * fixtureHint를 LLM에게 전달하여 현실적인 테스트 데이터를 생성합니다.
593
+ *
594
+ * faker.js로는 생성하기 어려운 복잡한 텍스트(자기소개, 설명문 등)를
595
+ * LLM을 활용하여 생성합니다. 동일한 hint에 대한 중복 호출을 방지하기 위해
596
+ * 캐싱을 기본으로 지원합니다 (LLM API 비용 절감).
597
+ *
598
+ * ai 패키지는 dynamic import로 불러오므로, useLLM이 false인 경우
599
+ * 의존성이 설치되지 않아도 fixture 생성이 정상 동작합니다.
600
+ */ async generateWithLLM(fixtureHint, prop, entity) {
601
+ const cacheKey = `${entity.id}:${prop.name}:${fixtureHint}`;
602
+ if (this.options.enableLLMCache && this.llmCache.has(cacheKey)) {
603
+ return this.llmCache.get(cacheKey);
604
+ }
605
+ const apiKey = this.getApiKey();
606
+ const { createAnthropic } = await import("@ai-sdk/anthropic");
607
+ const { generateText } = await import("ai");
608
+ const { text } = await generateText({
609
+ model: createAnthropic({
610
+ apiKey
611
+ })(this.options.llmModel || "claude-sonnet-4-5"),
612
+ prompt: this.buildLLMPrompt(fixtureHint, prop, entity)
613
+ });
614
+ const value = this.parseLLMResponse(text, prop.type);
615
+ if (this.options.enableLLMCache) {
616
+ this.llmCache.set(cacheKey, value);
617
+ }
618
+ return value;
619
+ }
620
+ buildLLMPrompt(hint, prop, entity) {
621
+ const locale = this.options.locale || "ko";
622
+ const language = locale === "ko" ? "Korean" : locale === "ja" ? "Japanese" : "English";
623
+ let prompt = `Generate test data for ${entity.id}.${prop.name} (type: ${prop.type})
624
+
625
+ Requirement: ${hint}
626
+
627
+ Rules:
628
+ - Return ONLY the value, no explanation or markdown
629
+ - Use ${language} language if applicable
630
+ - Format: ${this.getExpectedFormat(prop.type)}`;
631
+ // enum 타입인 경우 가능한 값 목록 추가
632
+ if (prop.type === "enum" || prop.type === "enum[]") {
633
+ let enumValues = [];
634
+ if ("enum" in prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
635
+ enumValues = prop.enum;
636
+ } else if ("id" in prop && prop.id && entity?.enumLabels?.[prop.id]) {
637
+ enumValues = Object.keys(entity.enumLabels[prop.id]);
638
+ }
639
+ if (enumValues.length > 0) {
640
+ prompt += `\n- IMPORTANT: Choose ONLY from these allowed values: ${enumValues.join(", ")}`;
641
+ }
642
+ }
643
+ prompt += `\n\nExample: ${this.getExampleForType(prop.type, locale)}`;
644
+ return prompt;
645
+ }
646
+ parseLLMResponse(text, propType) {
647
+ const cleaned = text.trim();
648
+ // 배열 타입 처리
649
+ if (propType.endsWith("[]")) {
650
+ try {
651
+ const parsed = JSON.parse(cleaned);
652
+ const baseType = propType.slice(0, -2); // "integer[]" -> "integer"
653
+ if (Array.isArray(parsed)) {
654
+ return parsed.map((item)=>{
655
+ // null/undefined는 타입별 기본값으로
656
+ if (item === null || item === undefined) {
657
+ return this.getDefaultValueForType(baseType);
658
+ }
659
+ // 객체는 JSON.stringify 후 파싱 (json 타입인 경우)
660
+ if (typeof item === "object") {
661
+ return baseType === "json" ? item : this.parseScalarValue(JSON.stringify(item), baseType);
662
+ }
663
+ // primitive 값은 문자열로 변환 후 파싱
664
+ return this.parseScalarValue(String(item), baseType);
665
+ });
666
+ }
667
+ // 단일 값이 온 경우 배열로 감싸기
668
+ if (parsed === null || parsed === undefined) {
669
+ return [
670
+ this.getDefaultValueForType(baseType)
671
+ ];
672
+ }
673
+ return [
674
+ this.parseScalarValue(String(parsed), baseType)
675
+ ];
676
+ } catch {
677
+ return [];
678
+ }
679
+ }
680
+ return this.parseScalarValue(cleaned, propType);
681
+ }
682
+ getDefaultValueForType(propType) {
683
+ switch(propType){
684
+ case "integer":
685
+ return 0;
686
+ case "bigInteger":
687
+ return 0n;
688
+ case "float":
689
+ case "number":
690
+ case "numeric":
691
+ return 0;
692
+ case "boolean":
693
+ return false;
694
+ case "date":
695
+ return new Date();
696
+ case "json":
697
+ return {};
698
+ case "uuid":
699
+ return "00000000-0000-0000-0000-000000000000";
700
+ default:
701
+ return "";
702
+ }
703
+ }
704
+ parseScalarValue(text, propType) {
705
+ const cleaned = text.trim();
706
+ switch(propType){
707
+ case "integer":
708
+ {
709
+ const num = parseInt(cleaned, 10);
710
+ return Number.isNaN(num) ? 0 : num;
711
+ }
712
+ case "bigInteger":
713
+ {
714
+ try {
715
+ return BigInt(cleaned);
716
+ } catch {
717
+ return 0n;
718
+ }
719
+ }
720
+ case "float":
721
+ case "number":
722
+ case "numeric":
723
+ {
724
+ const num = parseFloat(cleaned);
725
+ return Number.isNaN(num) ? 0 : num;
726
+ }
727
+ case "boolean":
728
+ return cleaned.toLowerCase() === "true";
729
+ case "date":
730
+ {
731
+ const date = new Date(cleaned);
732
+ return Number.isNaN(date.getTime()) ? new Date() : date;
733
+ }
734
+ case "json":
735
+ try {
736
+ return JSON.parse(cleaned);
737
+ } catch {
738
+ return cleaned;
739
+ }
740
+ case "uuid":
741
+ case "enum":
742
+ return cleaned;
743
+ default:
744
+ return cleaned;
745
+ }
746
+ }
747
+ /**
748
+ * Sonamu.secret을 우선으로 하고, 없으면 환경변수에서 API 키를 읽습니다.
749
+ *
750
+ * Sonamu.secret은 프로젝트별 설정(sonamu.config.ts)이므로 더 높은 우선순위를 가지며,
751
+ * 환경변수는 개발 환경이나 CI/CD에서 fallback으로 사용됩니다.
752
+ */ getApiKey() {
753
+ let apiKey;
754
+ try {
755
+ const { Sonamu } = require("../api");
756
+ apiKey = Sonamu.secret?.anthropic_api_key;
757
+ } catch {
758
+ // Sonamu가 초기화되지 않은 경우 (테스트 환경 등)
759
+ }
760
+ if (!apiKey) {
761
+ apiKey = process.env.ANTHROPIC_API_KEY;
762
+ }
763
+ if (!apiKey) {
764
+ throw new Error("ANTHROPIC_API_KEY not found. Set it in environment variables or Sonamu.secret.anthropic_api_key");
765
+ }
766
+ return apiKey;
767
+ }
768
+ getExpectedFormat(propType) {
769
+ // 배열 타입 처리
770
+ if (propType.endsWith("[]")) {
771
+ const baseType = propType.slice(0, -2);
772
+ const baseFormat = this.getScalarFormat(baseType);
773
+ return `JSON array of ${baseFormat} (e.g., [${this.getExampleForType(baseType, "en")}, ...])`;
774
+ }
775
+ return this.getScalarFormat(propType);
776
+ }
777
+ getScalarFormat(propType) {
778
+ switch(propType){
779
+ case "integer":
780
+ case "bigInteger":
781
+ return "integer numbers";
782
+ case "float":
783
+ case "number":
784
+ case "numeric":
785
+ return "decimal numbers";
786
+ case "boolean":
787
+ return "booleans (true or false)";
788
+ case "date":
789
+ return "ISO 8601 date strings";
790
+ case "json":
791
+ return "valid JSON object or array";
792
+ case "uuid":
793
+ return "UUID strings";
794
+ case "enum":
795
+ return "one of the allowed enum values";
796
+ default:
797
+ return "plain text strings";
798
+ }
799
+ }
800
+ getExampleForType(propType, locale) {
801
+ // 배열 타입 처리
802
+ if (propType.endsWith("[]")) {
803
+ const baseType = propType.slice(0, -2);
804
+ const baseExample = this.getScalarExample(baseType, locale);
805
+ return `[${baseExample}]`;
806
+ }
807
+ return this.getScalarExample(propType, locale);
808
+ }
809
+ getScalarExample(propType, locale) {
810
+ const isKorean = locale === "ko";
811
+ switch(propType){
812
+ case "integer":
813
+ case "bigInteger":
814
+ return "42";
815
+ case "float":
816
+ case "number":
817
+ case "numeric":
818
+ return "3.14";
819
+ case "boolean":
820
+ return "true";
821
+ case "date":
822
+ return "2024-01-01";
823
+ case "json":
824
+ return '{"key": "value"}';
825
+ case "uuid":
826
+ return "550e8400-e29b-41d4-a716-446655440000";
827
+ case "enum":
828
+ return "ENUM_VALUE";
829
+ default:
830
+ return isKorean ? "안녕하세요" : "Hello";
831
+ }
832
+ }
833
+ /**
834
+ * LLM 캐시 통계를 반환합니다.
835
+ */ getLLMCacheStats() {
836
+ return {
837
+ size: this.llmCache.size,
838
+ enabled: this.options.enableLLMCache
839
+ };
840
+ }
841
+ /**
842
+ * LLM 캐시를 초기화합니다.
843
+ */ clearLLMCache() {
844
+ this.llmCache.clear();
845
+ }
846
+ /**
847
+ * 컨텍스트 생성
848
+ */ createContext() {
849
+ return {
850
+ fixtures: new Map(),
851
+ referenceCache: new Map(),
852
+ importedRecords: new Set()
853
+ };
854
+ }
855
+ /**
856
+ * 배치 생성 및 자동 저장
857
+ *
858
+ * 1. 각 spec별로 fixture 생성 (메모리)
859
+ * 2. FixtureRecord로 변환
860
+ * 3. FixtureManager.insertFixtures()로 targetDb에 저장
861
+ *
862
+ * @returns 저장된 fixture 데이터 (실제 DB ID 포함)
863
+ */ async generateBatch(specs) {
864
+ const context = this.createContext();
865
+ const generatedFixtures = [];
866
+ // 1. 각 spec별로 fixture 생성
867
+ for (const spec of specs){
868
+ for(let i = 0; i < spec.count; i++){
869
+ const fixture = await this.generate(spec.entity, spec.overrides || {}, context);
870
+ generatedFixtures.push({
871
+ entity: spec.entity,
872
+ data: fixture
873
+ });
874
+ }
875
+ }
876
+ // 2. FixtureRecord로 변환
877
+ const fixtureRecords = [];
878
+ for (const { entity: entityName, data } of generatedFixtures){
879
+ const entity = this.entityManager.get(entityName);
880
+ // 임시 ID 생성 (targetDb에 INSERT 후 실제 ID를 받음)
881
+ const tempId = Math.floor(Math.random() * 1000000);
882
+ const records = await FixtureManager.createFixtureRecord(entity, {
883
+ ...data,
884
+ id: tempId
885
+ }, {
886
+ singleRecord: true
887
+ });
888
+ fixtureRecords.push(...records);
889
+ }
890
+ // 3. targetDb에 삽입 (FixtureManager가 의존성 정렬 처리)
891
+ const results = await FixtureManager.insertFixtures(this.targetDbName, fixtureRecords);
892
+ console.log(chalk.green(`Generated and saved ${results.length} fixtures to ${this.targetDbName}`));
893
+ return results;
894
+ }
895
+ /**
896
+ * 실제 DB(sourceDb)에서 데이터를 조회하여 fixture DB(targetDb)에 import합니다.
897
+ *
898
+ * 1. DataExplorer로 sourceDb에서 데이터 조회 (관련 데이터 포함)
899
+ * 2. FixtureRecord로 변환
900
+ * 3. targetDb에 삽입
901
+ *
902
+ * @param entityName - 조회할 entity 이름
903
+ * @param options - 조회 옵션 (strategy, limit, includeRelations 등)
904
+ * @returns 저장된 fixture 데이터 (실제 DB ID 포함)
905
+ *
906
+ * @example
907
+ * // 프로덕션 DB에서 User 10명 + 관련 Employee, Department 가져오기
908
+ * await generator.importFromSource("User", {
909
+ * strategy: "sample",
910
+ * limit: 10,
911
+ * includeRelations: true,
912
+ * maxDepth: 2
913
+ * });
914
+ */ async importFromSource(entityName, options) {
915
+ console.log(chalk.blue(`Importing ${entityName} from source DB with options: ${JSON.stringify({
916
+ strategy: options.strategy,
917
+ limit: options.limit,
918
+ includeRelations: options.includeRelations,
919
+ maxDepth: options.maxDepth
920
+ })}`));
921
+ // 1. DataExplorer로 sourceDb에서 데이터 조회 (관련 데이터 포함)
922
+ const exploreResult = await this.dataExplorer.exploreWithRelations(entityName, options);
923
+ console.log(chalk.cyan(`Found ${exploreResult.main.records.length} ${entityName} records and ${exploreResult.related.size} related entities`));
924
+ // 2. FixtureRecord로 변환
925
+ const fixtureRecords = [];
926
+ // 메인 entity의 records를 FixtureRecord로 변환
927
+ const mainEntity = this.entityManager.get(entityName);
928
+ for (const record of exploreResult.main.records){
929
+ const records = await FixtureManager.createFixtureRecord(mainEntity, record, {
930
+ _db: this.sourceDb,
931
+ singleRecord: true
932
+ });
933
+ fixtureRecords.push(...records);
934
+ }
935
+ // 관련 entity의 records를 FixtureRecord로 변환
936
+ for (const [relatedEntityName, relatedRecords] of exploreResult.related.entries()){
937
+ const relatedEntity = this.entityManager.get(relatedEntityName);
938
+ for (const record of relatedRecords){
939
+ const records = await FixtureManager.createFixtureRecord(relatedEntity, record, {
940
+ _db: this.sourceDb,
941
+ singleRecord: true
942
+ });
943
+ fixtureRecords.push(...records);
944
+ }
945
+ console.log(chalk.gray(` - ${relatedEntityName}: ${relatedRecords.length} records`));
946
+ }
947
+ // 3. targetDb에 삽입 (FixtureManager가 의존성 정렬 처리)
948
+ const results = await FixtureManager.insertFixtures(this.targetDbName, fixtureRecords);
949
+ console.log(chalk.green(`Successfully imported ${results.length} records to ${this.targetDbName} (${exploreResult.main.records.length} ${entityName} + ${results.length - exploreResult.main.records.length} related)`));
950
+ return results;
951
+ }
952
+ }
953
+
954
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0aW5nL2ZpeHR1cmUtZ2VuZXJhdG9yLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjaGFsayBmcm9tIFwiY2hhbGtcIjtcbmltcG9ydCB0eXBlIHsgS25leCB9IGZyb20gXCJrbmV4XCI7XG5pbXBvcnQgdHlwZSB7IEVudGl0eSB9IGZyb20gXCIuLi9lbnRpdHkvZW50aXR5XCI7XG5pbXBvcnQgdHlwZSB7IEVudGl0eU1hbmFnZXIgfSBmcm9tIFwiLi4vZW50aXR5L2VudGl0eS1tYW5hZ2VyXCI7XG5pbXBvcnQgdHlwZSB7IEVudGl0eVByb3AsIEZpeHR1cmVJbXBvcnRSZXN1bHQsIEZpeHR1cmVSZWNvcmQgfSBmcm9tIFwiLi4vdHlwZXMvdHlwZXNcIjtcbmltcG9ydCB7IGlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wLCBpc09uZVRvT25lUmVsYXRpb25Qcm9wLCBpc1JlbGF0aW9uUHJvcCB9IGZyb20gXCIuLi90eXBlcy90eXBlc1wiO1xuaW1wb3J0IHtcbiAgRGF0YUV4cGxvcmVyLFxuICB0eXBlIEV4cGxvcmVXaXRoUmVsYXRpb25zT3B0aW9ucyxcbiAgdHlwZSBFeHBsb3JlV2l0aFJlbGF0aW9uc1Jlc3VsdCxcbn0gZnJvbSBcIi4vZGF0YS1leHBsb3JlclwiO1xuaW1wb3J0IHsgdHlwZSBGYWtlck1hcHBpbmdzLCBmYWtlck1hcHBpbmdzIH0gZnJvbSBcIi4vZmFrZXItbWFwcGluZ3NcIjtcbmltcG9ydCB7IEZpeHR1cmVNYW5hZ2VyIH0gZnJvbSBcIi4vZml4dHVyZS1tYW5hZ2VyXCI7XG5cbmV4cG9ydCB0eXBlIExvY2FsZSA9IFwia29cIiB8IFwiZW5cIiB8IFwiamFcIjtcblxuZXhwb3J0IHR5cGUgRml4dHVyZUdlbmVyYXRvck9wdGlvbnMgPSB7XG4gIGxvY2FsZT86IExvY2FsZTtcbiAgdXNlTExNPzogYm9vbGVhbjtcbiAgZW5hYmxlTExNQ2FjaGU/OiBib29sZWFuO1xuICBsbG1Nb2RlbD86IHN0cmluZztcbn07XG5cbmV4cG9ydCB0eXBlIEdlbmVyYXRvckNvbnRleHQgPSB7XG4gIC8qKiDsg53shLEg7KSR7J24IGZpeHR1cmXrk6QgKOuplOuqqOumrCDsg4EpICovXG4gIGZpeHR1cmVzOiBNYXA8c3RyaW5nLCBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj47XG5cbiAgLyoqIOywuOyhsCDrjbDsnbTthLAg7LqQ7IucIChEYXRhRXhwbG9yZXIg6rKw6rO8KSAqL1xuICByZWZlcmVuY2VDYWNoZTogTWFwPHN0cmluZywgUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXT47XG5cbiAgLyoqIOydtOuvuCBpbXBvcnTrkJwg66CI7L2U65Oc66W8IOy2lOygge2VmOyXrCDspJHrs7UgaW1wb3J066W8IOuwqeyngO2VqeuLiOuLpCAqL1xuICBpbXBvcnRlZFJlY29yZHM6IFNldDxzdHJpbmc+OyAvLyBcIlVzZXIjMTIzXCJcbn07XG5cbmV4cG9ydCBjbGFzcyBGaXh0dXJlR2VuZXJhdG9yIHtcbiAgcHJpdmF0ZSBkYXRhRXhwbG9yZXI6IERhdGFFeHBsb3JlcjtcbiAgcHJpdmF0ZSBsb2NhbGU6IExvY2FsZTtcbiAgcHJpdmF0ZSBtYXBwaW5nczogRmFrZXJNYXBwaW5ncztcbiAgcHJpdmF0ZSBsbG1DYWNoZTogTWFwPHN0cmluZywgdW5rbm93bj4gPSBuZXcgTWFwKCk7XG4gIHByaXZhdGUgb3B0aW9uczogRml4dHVyZUdlbmVyYXRvck9wdGlvbnM7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBzb3VyY2VEYjogS25leCxcbiAgICAvLyBGaXh0dXJlTWFuYWdlci5pbnNlcnRGaXh0dXJlc+qwgCBkYk5hbWUg66y47J6Q7Je07J2EIOuwm+q4sCDrlYzrrLjsl5Ag7KeB7KCRIOyCrOyaqe2VmOyngCDslYrsirXri4jri6RcbiAgICAvLyDrr7jrnpgg7ZmV7J6l7ISx7J2EIOychO2VtCBBUEkg7Iuc6re464uI7LKY7JeQ64qUIO2PrO2VqOyLnOy8sOyKteuLiOuLpFxuICAgIF90YXJnZXREYjogS25leCxcbiAgICBwcml2YXRlIHRhcmdldERiTmFtZTogXCJmaXh0dXJlXCIgfCBcInRlc3RcIiB8IFwicHJvZHVjdGlvbl9tYXN0ZXJcIixcbiAgICBwcml2YXRlIGVudGl0eU1hbmFnZXI6IHR5cGVvZiBFbnRpdHlNYW5hZ2VyLFxuICAgIG9wdGlvbnM/OiBGaXh0dXJlR2VuZXJhdG9yT3B0aW9ucyxcbiAgKSB7XG4gICAgdGhpcy5kYXRhRXhwbG9yZXIgPSBuZXcgRGF0YUV4cGxvcmVyKHNvdXJjZURiLCBlbnRpdHlNYW5hZ2VyKTtcbiAgICB0aGlzLmxvY2FsZSA9IG9wdGlvbnM/LmxvY2FsZSB8fCBcImtvXCI7XG4gICAgdGhpcy5tYXBwaW5ncyA9IGZha2VyTWFwcGluZ3M7XG4gICAgdGhpcy5vcHRpb25zID0ge1xuICAgICAgbG9jYWxlOiBvcHRpb25zPy5sb2NhbGUgfHwgXCJrb1wiLFxuICAgICAgdXNlTExNOiBvcHRpb25zPy51c2VMTE0gfHwgZmFsc2UsXG4gICAgICBlbmFibGVMTE1DYWNoZTogb3B0aW9ucz8uZW5hYmxlTExNQ2FjaGUgIT09IGZhbHNlLFxuICAgICAgbGxtTW9kZWw6IG9wdGlvbnM/LmxsbU1vZGVsIHx8IFwiY2xhdWRlLXNvbm5ldC00LTVcIixcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEZpeHR1cmUg7IOd7ISxICjri6jsnbwpXG4gICAqIEByZXR1cm5zIOyDneyEseuQnCBmaXh0dXJlIOuNsOydtO2EsCAo66mU66qo66asIOyDgSlcbiAgICovXG4gIGFzeW5jIGdlbmVyYXRlKFxuICAgIGVudGl0eU5hbWU6IHN0cmluZyxcbiAgICBvdmVycmlkZXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge30sXG4gICAgY29udGV4dDogR2VuZXJhdG9yQ29udGV4dCA9IHRoaXMuY3JlYXRlQ29udGV4dCgpLFxuICApOiBQcm9taXNlPFJlY29yZDxzdHJpbmcsIHVua25vd24+PiB7XG4gICAgY29uc3QgZW50aXR5ID0gdGhpcy5lbnRpdHlNYW5hZ2VyLmdldChlbnRpdHlOYW1lKTtcbiAgICBjb25zdCB0ZW1wSWQgPSBgJHtlbnRpdHlOYW1lfSN0ZW1wIyR7RGF0ZS5ub3coKX1gOyAvLyDsnoTsi5wgSURcblxuICAgIC8vIOqwgSBwcm9w67OEIOqwkiDsg53shLFcbiAgICBjb25zdCBmaXh0dXJlOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuXG4gICAgZm9yIChjb25zdCBwcm9wIG9mIGVudGl0eS5wcm9wcykge1xuICAgICAgLy8gVmlydHVhbCBwcm9w7J2AIOyKpO2CtVxuICAgICAgaWYgKFwidmlydHVhbFwiIGluIHByb3AgJiYgcHJvcC52aXJ0dWFsKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICAvLyBvdmVycmlkZeqwgCDsnojsnLzrqbQg7IKs7JqpXG4gICAgICBpZiAocHJvcC5uYW1lIGluIG92ZXJyaWRlcykge1xuICAgICAgICBmaXh0dXJlW3Byb3AubmFtZV0gPSBvdmVycmlkZXNbcHJvcC5uYW1lXTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIGNvbmXsl5DshJwg7IOd7ISxIOyghOuetSDtmZXsnbhcbiAgICAgIGNvbnN0IGNvbmUgPSBwcm9wLmNvbmU7XG5cbiAgICAgIC8vIDEuIFJlbGF0aW9uIHByb3Ag7LKY66asXG4gICAgICBpZiAoaXNSZWxhdGlvblByb3AocHJvcCkpIHtcbiAgICAgICAgY29uc3QgcmVsYXRpb25WYWx1ZSA9IGF3YWl0IHRoaXMuZ2VuZXJhdGVSZWxhdGlvblZhbHVlKGVudGl0eSwgcHJvcCwgY29udGV4dCk7XG4gICAgICAgIC8vIEJlbG9uZ3NUb09uZSwgT25lVG9PbmUoaGFzSm9pbkNvbHVtbinsnZgg6rK97JqwIGZvcmVpZ24ga2V5IOy7rOufvOuqheycvOuhnCDsoIDsnqVcbiAgICAgICAgaWYgKFxuICAgICAgICAgIGlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wKHByb3ApIHx8XG4gICAgICAgICAgKGlzT25lVG9PbmVSZWxhdGlvblByb3AocHJvcCkgJiYgcHJvcC5oYXNKb2luQ29sdW1uKVxuICAgICAgICApIHtcbiAgICAgICAgICBmaXh0dXJlW2Ake3Byb3AubmFtZX1faWRgXSA9IHJlbGF0aW9uVmFsdWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gcmVsYXRpb25WYWx1ZTtcbiAgICAgICAgfVxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gMi4gZml4dHVyZUdlbmVyYXRvciDsgqzsmqlcbiAgICAgIGlmIChjb25lPy5maXh0dXJlR2VuZXJhdG9yKSB7XG4gICAgICAgIGZpeHR1cmVbcHJvcC5uYW1lXSA9IGF3YWl0IHRoaXMuZXhlY3V0ZUdlbmVyYXRvcihcbiAgICAgICAgICBjb25lLmZpeHR1cmVHZW5lcmF0b3IgYXMgc3RyaW5nLFxuICAgICAgICAgIHByb3AsXG4gICAgICAgICAgZW50aXR5LFxuICAgICAgICApO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gMi41LiBmaXh0dXJlSGludCArIExMTSDsgqzsmqlcbiAgICAgIGlmIChjb25lPy5maXh0dXJlSGludCAmJiB0aGlzLm9wdGlvbnMudXNlTExNKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gYXdhaXQgdGhpcy5nZW5lcmF0ZVdpdGhMTE0oY29uZS5maXh0dXJlSGludCwgcHJvcCwgZW50aXR5KTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgICBgW0ZpeHR1cmVHZW5lcmF0b3JdIExMTSBnZW5lcmF0aW9uIGZhaWxlZCBmb3IgJHtlbnRpdHkuaWR9LiR7cHJvcC5uYW1lfSwgZmFsbGluZyBiYWNrIHRvIGRlZmF1bHRgLFxuICAgICAgICAgICAgZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBlcnJvcixcbiAgICAgICAgICApO1xuICAgICAgICAgIC8vIGZhbGxiYWNrOiBmaXh0dXJlRGVmYXVsdCDrmJDripQg6riw67O46rCS7Jy866GcIOqzhOyGjVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIDMuIGZpeHR1cmVEZWZhdWx0IOyCrOyaqVxuICAgICAgaWYgKGNvbmU/LmZpeHR1cmVEZWZhdWx0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gY29uZS5maXh0dXJlRGVmYXVsdDtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIDQuIO2DgOyeheuzhCDquLDrs7gg7IOd7ISxXG4gICAgICBmaXh0dXJlW3Byb3AubmFtZV0gPSBhd2FpdCB0aGlzLmdlbmVyYXRlRGVmYXVsdFZhbHVlKHByb3AsIGVudGl0eSk7XG4gICAgfVxuXG4gICAgLy8gNS4gcGFzc3dvcmQg7ZWE65OcIOyVlO2YuO2ZlFxuICAgIGlmIChcInBhc3N3b3JkXCIgaW4gZml4dHVyZSAmJiBmaXh0dXJlLnBhc3N3b3JkICYmIHR5cGVvZiBmaXh0dXJlLnBhc3N3b3JkID09PSBcInN0cmluZ1wiKSB7XG4gICAgICBjb25zdCBiY3J5cHQgPSBhd2FpdCBpbXBvcnQoXCJiY3J5cHRcIik7XG4gICAgICBmaXh0dXJlLnBhc3N3b3JkID0gYXdhaXQgYmNyeXB0Lmhhc2goZml4dHVyZS5wYXNzd29yZCwgMTApO1xuICAgIH1cblxuICAgIGNvbnRleHQuZml4dHVyZXMuc2V0KHRlbXBJZCwgZml4dHVyZSk7XG4gICAgcmV0dXJuIGZpeHR1cmU7XG4gIH1cblxuICAvKipcbiAgICogUmVsYXRpb24g6rCSIOyDneyEsSArIOyekOuPmSBJbXBvcnRcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVSZWxhdGlvblZhbHVlKFxuICAgIGVudGl0eTogRW50aXR5LFxuICAgIHByb3A6IEVudGl0eVByb3AsXG4gICAgY29udGV4dDogR2VuZXJhdG9yQ29udGV4dCxcbiAgKTogUHJvbWlzZTxudW1iZXIgfCBudWxsPiB7XG4gICAgaWYgKCFpc1JlbGF0aW9uUHJvcChwcm9wKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGaXh0dXJlR2VuZXJhdG9yOiAke2VudGl0eS5pZH0uJHtwcm9wLm5hbWV9IGlzIG5vdCBhIHJlbGF0aW9uIHByb3BgKTtcbiAgICB9XG5cbiAgICAvLyBCZWxvbmdzVG9PbmUsIE9uZVRvT25lKGhhc0pvaW5Db2x1bW4p66eMIOyymOumrFxuICAgIGlmIChcbiAgICAgICFpc0JlbG9uZ3NUb09uZVJlbGF0aW9uUHJvcChwcm9wKSAmJlxuICAgICAgIShpc09uZVRvT25lUmVsYXRpb25Qcm9wKHByb3ApICYmIHByb3AuaGFzSm9pbkNvbHVtbilcbiAgICApIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IGNvbmUgPSBwcm9wLmNvbmU7XG4gICAgY29uc3QgZGF0YVNvdXJjZSA9IGNvbmU/LmRhdGFTb3VyY2U7XG5cbiAgICAvLyBEYXRhRXhwbG9yZXLroZwg7LC47KGwIOuNsOydtO2EsCDsobDtmowgKHNvdXJjZURiKVxuICAgIC8vIOq0gOqzhCDssrTsnbjsnYQg65Sw65286rCA6riwIOychO2VtCBleHBsb3JlV2l0aFJlbGF0aW9ucyDsgqzsmqlcbiAgICBpZiAoZGF0YVNvdXJjZSkge1xuICAgICAgY29uc3QgY2FjaGVLZXkgPSBgJHtwcm9wLndpdGh9OiR7SlNPTi5zdHJpbmdpZnkoZGF0YVNvdXJjZSl9YDtcblxuICAgICAgaWYgKCFjb250ZXh0LnJlZmVyZW5jZUNhY2hlLmhhcyhjYWNoZUtleSkpIHtcbiAgICAgICAgY29uc3QgZXhwbG9yZVJlc3VsdCA9IGF3YWl0IHRoaXMuZGF0YUV4cGxvcmVyLmV4cGxvcmVXaXRoUmVsYXRpb25zKHByb3Aud2l0aCwge1xuICAgICAgICAgIHN0cmF0ZWd5OiBkYXRhU291cmNlLnN0cmF0ZWd5LFxuICAgICAgICAgIGxpbWl0OlxuICAgICAgICAgICAgKChkYXRhU291cmNlLmNvbmZpZyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCk/LmxpbWl0IGFzXG4gICAgICAgICAgICAgIHwgbnVtYmVyXG4gICAgICAgICAgICAgIHwgdW5kZWZpbmVkKSB8fCAxMCxcbiAgICAgICAgICBpbmNsdWRlUmVsYXRpb25zOiB0cnVlLFxuICAgICAgICAgIG1heERlcHRoOiAzLFxuICAgICAgICAgIC4uLihkYXRhU291cmNlLmNvbmZpZyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCksXG4gICAgICAgIH0pO1xuICAgICAgICBjb250ZXh0LnJlZmVyZW5jZUNhY2hlLnNldChjYWNoZUtleSwgZXhwbG9yZVJlc3VsdC5tYWluLnJlY29yZHMpO1xuXG4gICAgICAgIC8vIOyhsO2ajO2VnCDrjbDsnbTthLDsmYAg6rSA6rOE65CcIOuqqOuToCDsl5Tti7Dti7DrpbwgdGFyZ2V0RGLsl5AgaW1wb3J0XG4gICAgICAgIGF3YWl0IHRoaXMuaW1wb3J0RXhwbG9yZVJlc3VsdChleHBsb3JlUmVzdWx0LCBjb250ZXh0KTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgY2FuZGlkYXRlcyA9IGNvbnRleHQucmVmZXJlbmNlQ2FjaGUuZ2V0KGNhY2hlS2V5KTtcbiAgICAgIGlmIChjYW5kaWRhdGVzICYmIGNhbmRpZGF0ZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAvLyDrnpzrjaTtlZjqsowg7ZWY64KYIOyEoO2DnVxuICAgICAgICBjb25zdCBzZWxlY3RlZCA9IGNhbmRpZGF0ZXNbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogY2FuZGlkYXRlcy5sZW5ndGgpXTtcbiAgICAgICAgcmV0dXJuIHNlbGVjdGVkLmlkIGFzIG51bWJlcjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBkYXRhU291cmNl6rCAIOyXhuydhCDrlYwg7J6Q64+Z7Jy866GcIGZpeHR1cmUgRELsl5DshJwg7KGw7ZqMIOyLnOuPhFxuICAgIC8vIOq0gOqzhCDssrTsnbjsnYQg65Sw65286rCA6riwIOychO2VtCBleHBsb3JlV2l0aFJlbGF0aW9ucyDsgqzsmqlcbiAgICBjb25zdCBhdXRvS2V5ID0gYCR7cHJvcC53aXRofTphdXRvYDtcbiAgICBpZiAoIWNvbnRleHQucmVmZXJlbmNlQ2FjaGUuaGFzKGF1dG9LZXkpKSB7XG4gICAgICAvLyBmaXh0dXJlIERCKHNvdXJjZURiKeyXkOyEnCDsnpDrj5kg7KGw7ZqMICjqtIDqs4Qg7Y+s7ZWoKVxuICAgICAgY29uc3QgYXV0b0V4cGxvcmVSZXN1bHQgPSBhd2FpdCB0aGlzLmRhdGFFeHBsb3Jlci5leHBsb3JlV2l0aFJlbGF0aW9ucyhwcm9wLndpdGgsIHtcbiAgICAgICAgc3RyYXRlZ3k6IFwicmFuZG9tXCIsXG4gICAgICAgIGxpbWl0OiAxMCxcbiAgICAgICAgaW5jbHVkZVJlbGF0aW9uczogdHJ1ZSxcbiAgICAgICAgbWF4RGVwdGg6IDMsXG4gICAgICB9KTtcbiAgICAgIGNvbnRleHQucmVmZXJlbmNlQ2FjaGUuc2V0KGF1dG9LZXksIGF1dG9FeHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcyk7XG5cbiAgICAgIC8vIOyhsO2ajO2VnCDrjbDsnbTthLDsmYAg6rSA6rOE65CcIOuqqOuToCDsl5Tti7Dti7DrpbwgdGFyZ2V0RGLsl5AgaW1wb3J0XG4gICAgICBpZiAoYXV0b0V4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgYXdhaXQgdGhpcy5pbXBvcnRFeHBsb3JlUmVzdWx0KGF1dG9FeHBsb3JlUmVzdWx0LCBjb250ZXh0KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBhdXRvQ2FuZGlkYXRlcyA9IGNvbnRleHQucmVmZXJlbmNlQ2FjaGUuZ2V0KGF1dG9LZXkpO1xuICAgIGlmIChhdXRvQ2FuZGlkYXRlcyAmJiBhdXRvQ2FuZGlkYXRlcy5sZW5ndGggPiAwKSB7XG4gICAgICAvLyDrnpzrjaTtlZjqsowg7ZWY64KYIOyEoO2DnVxuICAgICAgY29uc3Qgc2VsZWN0ZWQgPSBhdXRvQ2FuZGlkYXRlc1tNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBhdXRvQ2FuZGlkYXRlcy5sZW5ndGgpXTtcbiAgICAgIHJldHVybiBzZWxlY3RlZC5pZCBhcyBudW1iZXI7XG4gICAgfVxuXG4gICAgLy8g7LC47KGwIOuNsOydtO2EsOqwgCDsl4bsnLzrqbQgbnVsbCDrsJjtmZggKG51bGxhYmxl7J24IOqyveyasClcbiAgICBpZiAocHJvcC5udWxsYWJsZSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgLy8gbnVsbGFibGXsnbQg7JWE64uI6rOgIOuNsOydtO2EsOuPhCDsl4bsnLzrqbQg7JeQ65+sXG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEZpeHR1cmVHZW5lcmF0b3I6ICR7ZW50aXR5LmlkfS4ke3Byb3AubmFtZX3sl5Ag7ZWE7JqU7ZWcICR7cHJvcC53aXRofSDrjbDsnbTthLDqsIAg7JeG7Iq164uI64ukLiBgICtcbiAgICAgICAgYOuovOyggCAke3Byb3Aud2l0aH3rpbwg7IOd7ISx7ZWY6rGw64KYIGNvbmUuZGF0YVNvdXJjZeulvCDshKTsoJXtlZjshLjsmpQuYCxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4cGxvcmVXaXRoUmVsYXRpb25zIOqysOqzvOulvCB0YXJnZXREYuyXkCBpbXBvcnRcbiAgICpcbiAgICog6rSA6rOEIOyytOyduOydhCDrlLDrnbzqsIQg6rKw6rO8KG1haW4gKyByZWxhdGVkKeulvCDrqqjrkZAgaW1wb3J07ZWp64uI64ukLlxuICAgKiDsnZjsobTshLEg7Iic7ISc64qUIEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVz6rCAIOyekOuPmeycvOuhnCDsspjrpqztlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGltcG9ydEV4cGxvcmVSZXN1bHQoXG4gICAgZXhwbG9yZVJlc3VsdDogRXhwbG9yZVdpdGhSZWxhdGlvbnNSZXN1bHQsXG4gICAgY29udGV4dDogR2VuZXJhdG9yQ29udGV4dCxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgYWxsRml4dHVyZVJlY29yZHM6IEZpeHR1cmVSZWNvcmRbXSA9IFtdO1xuXG4gICAgLy8gMS4gUmVsYXRlZCBlbnRpdGllcyBpbXBvcnQgKENvbXBhbnksIERlcGFydG1lbnQg65OxKVxuICAgIGZvciAoY29uc3QgW2VudGl0eUlkLCByZWNvcmRzXSBvZiBleHBsb3JlUmVzdWx0LnJlbGF0ZWQuZW50cmllcygpKSB7XG4gICAgICBjb25zdCBlbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KGVudGl0eUlkKTtcbiAgICAgIGNvbnN0IHJlY29yZHNUb0ltcG9ydDogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSA9IFtdO1xuXG4gICAgICBjb25zb2xlLmxvZyhjaGFsay5jeWFuKGBJbXBvcnRpbmcgcmVsYXRlZCBlbnRpdHk6ICR7ZW50aXR5SWR9ICgke3JlY29yZHMubGVuZ3RofSByZWNvcmRzKWApKTtcblxuICAgICAgZm9yIChjb25zdCByZWNvcmQgb2YgcmVjb3Jkcykge1xuICAgICAgICBjb25zdCByZWNvcmRLZXkgPSBgJHtlbnRpdHlJZH0jJHtyZWNvcmQuaWR9YDtcbiAgICAgICAgaWYgKCFjb250ZXh0LmltcG9ydGVkUmVjb3Jkcy5oYXMocmVjb3JkS2V5KSkge1xuICAgICAgICAgIHJlY29yZHNUb0ltcG9ydC5wdXNoKHJlY29yZCk7XG4gICAgICAgICAgY29udGV4dC5pbXBvcnRlZFJlY29yZHMuYWRkKHJlY29yZEtleSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKHJlY29yZHNUb0ltcG9ydC5sZW5ndGggPiAwKSB7XG4gICAgICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIHJlY29yZHNUb0ltcG9ydCkge1xuICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgY2hhbGsuZ3JheShgICAtIFByb2Nlc3NpbmcgJHtlbnRpdHlJZH0gcmVjb3JkOmAsIEpTT04uc3RyaW5naWZ5KHJlY29yZCkuc2xpY2UoMCwgMTAwKSksXG4gICAgICAgICAgKTtcbiAgICAgICAgICBjb25zdCBmaXh0dXJlUmVjb3JkcyA9IGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmNyZWF0ZUZpeHR1cmVSZWNvcmQoXG4gICAgICAgICAgICBlbnRpdHksXG4gICAgICAgICAgICByZWNvcmQgYXMgeyBpZDogbnVtYmVyIHwgc3RyaW5nOyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuIHwgbnVsbCB9LFxuICAgICAgICAgICAgeyBfZGI6IHRoaXMuc291cmNlRGIsIHNpbmdsZVJlY29yZDogdHJ1ZSB9LFxuICAgICAgICAgICk7XG4gICAgICAgICAgYWxsRml4dHVyZVJlY29yZHMucHVzaCguLi5maXh0dXJlUmVjb3Jkcyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyAyLiBNYWluIGVudGl0eSBpbXBvcnQgKEVtcGxveWVlIOuTsSlcbiAgICBjb25zdCBtYWluRW50aXR5ID0gdGhpcy5lbnRpdHlNYW5hZ2VyLmdldChleHBsb3JlUmVzdWx0Lm1haW4uZW50aXR5SWQpO1xuICAgIGNvbnN0IG1haW5SZWNvcmRzVG9JbXBvcnQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+W10gPSBbXTtcblxuICAgIGNvbnNvbGUubG9nKFxuICAgICAgY2hhbGsuY3lhbihcbiAgICAgICAgYEltcG9ydGluZyBtYWluIGVudGl0eTogJHtleHBsb3JlUmVzdWx0Lm1haW4uZW50aXR5SWR9ICgke2V4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzLmxlbmd0aH0gcmVjb3JkcylgLFxuICAgICAgKSxcbiAgICApO1xuXG4gICAgZm9yIChjb25zdCByZWNvcmQgb2YgZXhwbG9yZVJlc3VsdC5tYWluLnJlY29yZHMpIHtcbiAgICAgIGNvbnN0IHJlY29yZEtleSA9IGAke2V4cGxvcmVSZXN1bHQubWFpbi5lbnRpdHlJZH0jJHtyZWNvcmQuaWR9YDtcbiAgICAgIGlmICghY29udGV4dC5pbXBvcnRlZFJlY29yZHMuaGFzKHJlY29yZEtleSkpIHtcbiAgICAgICAgbWFpblJlY29yZHNUb0ltcG9ydC5wdXNoKHJlY29yZCk7XG4gICAgICAgIGNvbnRleHQuaW1wb3J0ZWRSZWNvcmRzLmFkZChyZWNvcmRLZXkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChtYWluUmVjb3Jkc1RvSW1wb3J0Lmxlbmd0aCA+IDApIHtcbiAgICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIG1haW5SZWNvcmRzVG9JbXBvcnQpIHtcbiAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgY2hhbGsuZ3JheShcbiAgICAgICAgICAgIGAgIC0gUHJvY2Vzc2luZyAke2V4cGxvcmVSZXN1bHQubWFpbi5lbnRpdHlJZH0gcmVjb3JkOmAsXG4gICAgICAgICAgICBKU09OLnN0cmluZ2lmeShyZWNvcmQpLnNsaWNlKDAsIDEwMCksXG4gICAgICAgICAgKSxcbiAgICAgICAgKTtcbiAgICAgICAgY29uc3QgZml4dHVyZVJlY29yZHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5jcmVhdGVGaXh0dXJlUmVjb3JkKFxuICAgICAgICAgIG1haW5FbnRpdHksXG4gICAgICAgICAgcmVjb3JkIGFzIHsgaWQ6IG51bWJlciB8IHN0cmluZzsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwgfSxcbiAgICAgICAgICB7IF9kYjogdGhpcy5zb3VyY2VEYiwgc2luZ2xlUmVjb3JkOiB0cnVlIH0sXG4gICAgICAgICk7XG4gICAgICAgIGFsbEZpeHR1cmVSZWNvcmRzLnB1c2goLi4uZml4dHVyZVJlY29yZHMpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIDMuIOuqqOuToCBmaXh0dXJl66W8IO2VnCDrsojsl5Ag7IK97J6FICjsnZjsobTshLEg7Iic7IScIOyekOuPmSDsspjrpqwpXG4gICAgaWYgKGFsbEZpeHR1cmVSZWNvcmRzLmxlbmd0aCA+IDApIHtcbiAgICAgIGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVzKHRoaXMudGFyZ2V0RGJOYW1lLCBhbGxGaXh0dXJlUmVjb3Jkcyk7XG5cbiAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICBjaGFsay5ncmVlbihcbiAgICAgICAgICBgQXV0by1pbXBvcnRlZCAke2V4cGxvcmVSZXN1bHQubWFpbi5lbnRpdHlJZH0gd2l0aCByZWxhdGlvbnM6IGAgK1xuICAgICAgICAgICAgYCR7ZXhwbG9yZVJlc3VsdC5tYWluLnJlY29yZHMubGVuZ3RofSBtYWluICsgJHtleHBsb3JlUmVzdWx0LnJlbGF0ZWQuc2l6ZX0gcmVsYXRlZCBlbnRpdGllc2AsXG4gICAgICAgICksXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBmaXh0dXJlR2VuZXJhdG9yIOyLpO2WiSAoRmFrZXIuanPrp4wg7KeA7JuQKVxuICAgKlxuICAgKiBmYWtlci4qIO2YleyLneydmCDtkZztmITsi53snYQg7JWI7KCE7ZWY6rKMIO2MjOyLse2VmOyXrCDsi6Ttlontlanri4jri6QuXG4gICAqIOyYiDogXCJmYWtlci5pbnRlcm5ldC5lbWFpbCgpXCIg4oaSIGZha2VyLmludGVybmV0LmVtYWlsKClcbiAgICog7JiIOiBcImZha2VyLmxvcmVtLndvcmRzKDMpXCIg4oaSIGZha2VyLmxvcmVtLndvcmRzKDMpXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGV4ZWN1dGVHZW5lcmF0b3IoXG4gICAgZ2VuZXJhdG9yOiBzdHJpbmcsXG4gICAgcHJvcDogRW50aXR5UHJvcCxcbiAgICBlbnRpdHk6IEVudGl0eSxcbiAgKTogUHJvbWlzZTx1bmtub3duPiB7XG4gICAgLy8gRmFrZXIuanMg7ZGc7ZiE7Iud66eMIOyngOybkFxuICAgIGlmIChnZW5lcmF0b3Iuc3RhcnRzV2l0aChcImZha2VyLlwiKSkge1xuICAgICAgLy8gdXNlcm5hbWXsnbTrgpggbmFtZSDtlYTrk5zripQg7ZWc6rWt7Ja0IGZha2VyIOyCrOyaqVxuICAgICAgY29uc3QgaXNOYW1lRmllbGQgPSBwcm9wLm5hbWUgPT09IFwidXNlcm5hbWVcIiB8fCBwcm9wLm5hbWUgPT09IFwibmFtZVwiO1xuICAgICAgY29uc3QgZmFrZXJNb2R1bGUgPSBhd2FpdCBpbXBvcnQoXCJAZmFrZXItanMvZmFrZXJcIik7XG4gICAgICBjb25zdCBmYWtlciA9IGlzTmFtZUZpZWxkID8gZmFrZXJNb2R1bGUuZmFrZXJLTyA6IGZha2VyTW9kdWxlLmZha2VyO1xuICAgICAgY29uc3QgZXhwciA9IGdlbmVyYXRvci5zbGljZSg2KTsgLy8gXCJmYWtlci5cIiDsoJzqsbBcblxuICAgICAgdHJ5IHtcbiAgICAgICAgLy8g7ZWo7IiYIOqyveuhnOyZgCDsnbjsnpAg7YyM7IuxXG4gICAgICAgIGNvbnN0IG1hdGNoID0gZXhwci5tYXRjaCgvXihbXFx3Ll0rKSg/OlxcKCguKj8pXFwpKT8kLyk7XG4gICAgICAgIGlmICghbWF0Y2gpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRml4dHVyZUdlbmVyYXRvcjogSW52YWxpZCBmYWtlciBleHByZXNzaW9uIGZvciAke3Byb3AubmFtZX06ICR7Z2VuZXJhdG9yfWAsXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IFssIHBhdGgsIGFyZ3NTdHJdID0gbWF0Y2g7XG4gICAgICAgIGNvbnN0IHBhcnRzID0gcGF0aC5zcGxpdChcIi5cIik7XG5cbiAgICAgICAgLy8gZmFrZXIg6rCd7LK07JeQ7IScIO2VqOyImCDssL7quLBcbiAgICAgICAgbGV0IGZuOiB1bmtub3duID0gZmFrZXI7XG4gICAgICAgIGZvciAoY29uc3QgcGFydCBvZiBwYXJ0cykge1xuICAgICAgICAgIGlmICh0eXBlb2YgZm4gPT09IFwib2JqZWN0XCIgJiYgZm4gIT09IG51bGwgJiYgcGFydCBpbiBmbikge1xuICAgICAgICAgICAgZm4gPSAoZm4gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pW3BhcnRdO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZpeHR1cmVHZW5lcmF0b3I6IEludmFsaWQgZmFrZXIgcGF0aCBmb3IgJHtwcm9wLm5hbWV9OiBmYWtlci4ke3BhdGh9YCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8g7ZWo7IiY6rCAIOyVhOuLiOuptCDsl5Drn6xcbiAgICAgICAgaWYgKHR5cGVvZiBmbiAhPT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGaXh0dXJlR2VuZXJhdG9yOiBmYWtlci4ke3BhdGh9IGlzIG5vdCBhIGZ1bmN0aW9uIChmb3IgJHtwcm9wLm5hbWV9KWApO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8g7J247J6QIO2MjOyLsSAoSlNPTiDtmJXsi53rp4wg7KeA7JuQKVxuICAgICAgICBsZXQgYXJnczogdW5rbm93bltdID0gW107XG4gICAgICAgIGlmIChhcmdzU3RyPy50cmltKCkpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gSlNPTiDrsLDsl7TroZwg7YyM7IuxIOyLnOuPhFxuICAgICAgICAgICAgY29uc3QgcGFyc2VkID0gSlNPTi5wYXJzZShgWyR7YXJnc1N0cn1dYCkgYXMgdW5rbm93bjtcbiAgICAgICAgICAgIGFyZ3MgPSBBcnJheS5pc0FycmF5KHBhcnNlZCkgPyBwYXJzZWQgOiBbcGFyc2VkXTtcbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIC8vIOyIq+yekOuCmCDrrLjsnpDsl7Qg64uo7J28IOyduOyekCDsspjrpqxcbiAgICAgICAgICAgIGNvbnN0IHRyaW1tZWQgPSBhcmdzU3RyLnRyaW0oKTtcbiAgICAgICAgICAgIGlmICghTnVtYmVyLmlzTmFOKE51bWJlcih0cmltbWVkKSkpIHtcbiAgICAgICAgICAgICAgYXJncyA9IFtOdW1iZXIodHJpbW1lZCldO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgICAgICAgKHRyaW1tZWQuc3RhcnRzV2l0aCgnXCInKSAmJiB0cmltbWVkLmVuZHNXaXRoKCdcIicpKSB8fFxuICAgICAgICAgICAgICAodHJpbW1lZC5zdGFydHNXaXRoKFwiJ1wiKSAmJiB0cmltbWVkLmVuZHNXaXRoKFwiJ1wiKSlcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICBhcmdzID0gW3RyaW1tZWQuc2xpY2UoMSwgLTEpXTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICBgRml4dHVyZUdlbmVyYXRvcjogQ2Fubm90IHBhcnNlIGFyZ3VtZW50cyBmb3IgJHtwcm9wLm5hbWV9OiAke2FyZ3NTdHJ9YCxcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gZm4oLi4uYXJncyk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICBjaGFsay55ZWxsb3coXG4gICAgICAgICAgICBgRmFpbGVkIHRvIGV4ZWN1dGUgZ2VuZXJhdG9yIFwiJHtnZW5lcmF0b3J9XCIgZm9yICR7cHJvcC5uYW1lfSwgZmFsbGluZyBiYWNrIHRvIGRlZmF1bHQ6YCxcbiAgICAgICAgICApLFxuICAgICAgICAgIGVycm9yLFxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gdGhpcy5nZW5lcmF0ZURlZmF1bHRWYWx1ZShwcm9wLCBlbnRpdHkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIGZha2VyIOydtOyZuOydmCDtkZztmITsi53snYAg7KeA7JuQ7ZWY7KeAIOyViuydjFxuICAgIGNvbnNvbGUubG9nKFxuICAgICAgY2hhbGsueWVsbG93KFxuICAgICAgICBgVW5zdXBwb3J0ZWQgZ2VuZXJhdG9yIGV4cHJlc3Npb24gZm9yICR7cHJvcC5uYW1lfTogJHtnZW5lcmF0b3J9LiBPbmx5IGZha2VyLiogZXhwcmVzc2lvbnMgYXJlIHN1cHBvcnRlZC4gVXNpbmcgZGVmYXVsdCB2YWx1ZS5gLFxuICAgICAgKSxcbiAgICApO1xuICAgIHJldHVybiB0aGlzLmdlbmVyYXRlRGVmYXVsdFZhbHVlKHByb3AsIGVudGl0eSk7XG4gIH1cblxuICAvKipcbiAgICog7ZWE65Oc7J2YIO2DgOyeheqzvCDsnbTrpoTsnYQg67aE7ISd7ZWY7JesIOyggeygiO2VnCDquLDrs7jqsJLsnYQg7IOd7ISx7ZWp64uI64ukLlxuICAgKlxuICAgKiDsmrDshKDsiJzsnIQ6XG4gICAqIDEuIO2VhOuTnOuqhSDtjKjthLQg66ek7LmtIChzYWxhcnksIGJ1ZGdldCDrk7Eg7J2Y66+47J6I64qUIOuNsOydtO2EsClcbiAgICogMi4g7Yq57IiYIOy8gOydtOyKpCAoRGVwYXJ0bWVudCBuYW1lIOuTsSDrj4TrqZTsnbgg7KeA7IudKVxuICAgKiAzLiDrsLDsl7Qg7YOA7J6FIChKU09OIOuwsOyXtClcbiAgICogNC4gRW51bSDtg4DsnoVcbiAgICogNS4g7YOA7J6F67OEIOq4sOuzuOqwklxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBnZW5lcmF0ZURlZmF1bHRWYWx1ZShwcm9wOiBFbnRpdHlQcm9wLCBlbnRpdHk/OiBFbnRpdHkpOiBQcm9taXNlPHVua25vd24+IHtcbiAgICBjb25zdCBmYWtlck1vZHVsZSA9IGF3YWl0IGltcG9ydChcIkBmYWtlci1qcy9mYWtlclwiKTtcbiAgICBjb25zdCBmYWtlciA9IGZha2VyTW9kdWxlLmZha2VyO1xuICAgIGNvbnN0IGZha2VyS08gPSBmYWtlck1vZHVsZS5mYWtlcktPO1xuICAgIGNvbnN0IGZha2VySkEgPSBmYWtlck1vZHVsZS5mYWtlckpBO1xuXG4gICAgY29uc3QgbG9jYWxlRmFrZXIgPSB0aGlzLmxvY2FsZSA9PT0gXCJrb1wiID8gZmFrZXJLTyA6IHRoaXMubG9jYWxlID09PSBcImphXCIgPyBmYWtlckpBIDogZmFrZXI7XG5cbiAgICAvKipcbiAgICAgKiAxLiDtlYTrk5zrqoXsl5DshJwg7J2Y66+466W8IOy2lOuhoO2VmOyXrCDtmITsi6TsoIHsnbgg642w7J207YSw66W8IOyDneyEse2VqeuLiOuLpC5cbiAgICAgKiDsmIg6IHNhbGFyeSDihpIgMzBNfjE1ME0gKO2VnOq1rSDsl7DrtIkg67KU7JyEKVxuICAgICAqICAgICBidWRnZXQg4oaSIDEwTX41MDBNICjtlITroZzsoJ3tirgg7JiI7IKwIOuylOychClcbiAgICAgKi9cbiAgICBjb25zdCBsb2NhbGVNYXBwaW5ncyA9IHRoaXMubWFwcGluZ3NbdGhpcy5sb2NhbGVdIHx8IHRoaXMubWFwcGluZ3MuZW47XG4gICAgY29uc3Qgbm9ybWFsaXplZE5hbWUgPSBwcm9wLm5hbWUudG9Mb3dlckNhc2UoKS5yZXBsYWNlKC9fL2csIFwiXCIpO1xuXG4gICAgZm9yIChjb25zdCBbcGF0dGVybiwgY29uZmlnXSBvZiBPYmplY3QuZW50cmllcyhsb2NhbGVNYXBwaW5ncy5maWVsZF9wYXR0ZXJucykpIHtcbiAgICAgIGlmIChub3JtYWxpemVkTmFtZS5pbmNsdWRlcyhwYXR0ZXJuLnRvTG93ZXJDYXNlKCkpKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZXhlY3V0ZUZha2VyRXhwcmVzc2lvbihjb25maWcuZmFrZXIsIHByb3ApO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgY2hhbGsueWVsbG93KFxuICAgICAgICAgICAgICBgRmFpbGVkIHRvIGV4ZWN1dGUgZmllbGQgcGF0dGVybiBcIiR7cGF0dGVybn1cIiBmb3IgJHtwcm9wLm5hbWV9LCBmYWxsaW5nIGJhY2s6YCxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgICBlcnJvcixcbiAgICAgICAgICApO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogMi4gRGVwYXJ0bWVudCBuYW1l7J2AIO2VnOq1reyWtCDrtoDshJzrqoUg66qp66Gd7JeQ7IScIOyEoO2Dne2VqeuLiOuLpC5cbiAgICAgKiDqs6DsnKDshLHsnYQg7JyE7ZW0IDcwJSDtmZXrpaDroZwgcHJlZml4L3N1ZmZpeOulvCDstpTqsIDtlanri4jri6QuXG4gICAgICovXG4gICAgaWYgKGVudGl0eT8uaWQgPT09IFwiRGVwYXJ0bWVudFwiICYmIHByb3AubmFtZSA9PT0gXCJuYW1lXCIpIHtcbiAgICAgIGNvbnN0IGRlcGFydG1lbnRzID0gW1xuICAgICAgICBcIuqwnOuwnO2MgFwiLFxuICAgICAgICBcIuq4sO2aje2MgFwiLFxuICAgICAgICBcIuuniOy8gO2Mhe2MgFwiLFxuICAgICAgICBcIuyYgeyXhe2MgFwiLFxuICAgICAgICBcIuyduOyCrO2MgFwiLFxuICAgICAgICBcIuy0neustO2MgFwiLFxuICAgICAgICBcIuyerOustO2MgFwiLFxuICAgICAgICBcIu2ajOqzhO2MgFwiLFxuICAgICAgICBcIuuyleustO2MgFwiLFxuICAgICAgICBcIuuUlOyekOyduO2MgFwiLFxuICAgICAgICBcIklU7YyAXCIsXG4gICAgICAgIFwi6rOg6rCd7KeA7JuQ7YyAXCIsXG4gICAgICAgIFwi7ZKI7KeI6rSA66as7YyAXCIsXG4gICAgICAgIFwi7Jew6rWs6rCc67Cc7YyAXCIsXG4gICAgICAgIFwi7IOd7IKw7YyAXCIsXG4gICAgICAgIFwi6rWs66ek7YyAXCIsXG4gICAgICAgIFwi66y866WY7YyAXCIsXG4gICAgICBdO1xuICAgICAgY29uc3QgcHJlZml4ZXMgPSBbXCLsi6Dqt5xcIiwgXCLthrXtlalcIiwgXCLsoITrnrVcIiwgXCLquIDroZzrsoxcIiwgXCLrlJTsp4DthLhcIiwgXCLtlbXsi6xcIl07XG4gICAgICBjb25zdCBzdWZmaXhlcyA9IFtcIjHtjIBcIiwgXCIy7YyAXCIsIFwiM+2MgFwiLCBcIkHtjIBcIiwgXCJC7YyAXCIsIFwi67O467aAXCIsIFwi7IS87YSwXCIsIFwi6re466O5XCJdO1xuXG4gICAgICBjb25zdCBkZXB0ID0gZmFrZXIuaGVscGVycy5hcnJheUVsZW1lbnQoZGVwYXJ0bWVudHMpO1xuXG4gICAgICBjb25zdCByYW5kb20gPSBNYXRoLnJhbmRvbSgpO1xuICAgICAgaWYgKHJhbmRvbSA+IDAuNykge1xuICAgICAgICBjb25zdCBwcmVmaXggPSBmYWtlci5oZWxwZXJzLmFycmF5RWxlbWVudChwcmVmaXhlcyk7XG4gICAgICAgIHJldHVybiBgJHtwcmVmaXh9ICR7ZGVwdH1gO1xuICAgICAgfVxuICAgICAgaWYgKHJhbmRvbSA+IDAuNCkge1xuICAgICAgICBjb25zdCBzdWZmaXggPSBmYWtlci5oZWxwZXJzLmFycmF5RWxlbWVudChzdWZmaXhlcyk7XG4gICAgICAgIHJldHVybiBgJHtkZXB0fSAke3N1ZmZpeH1gO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGRlcHQ7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogMy4gSlNPTiDtg4DsnoXsnbTrqbTshJwg67Cw7Je07J24IOqyveyasCAoU29uYW11RmlsZVtdLCBzdHJpbmdbXSDrk7EpXG4gICAgICog7ZWE65Oc66qFIO2MqO2EtOydhCDrs7Tqs6Ag7KCB7KCI7ZWcIOuwsOyXtCDrjbDsnbTthLDrpbwg7IOd7ISx7ZWp64uI64ukLlxuICAgICAqL1xuICAgIGlmIChwcm9wLnR5cGUgPT09IFwianNvblwiICYmIFwiaWRcIiBpbiBwcm9wICYmIHByb3AuaWQpIHtcbiAgICAgIGlmIChwcm9wLmlkLmVuZHNXaXRoKFwiW11cIikpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2VuZXJhdGVBcnJheVZhbHVlKHByb3AsIGVudGl0eSwgZmFrZXIsIGxvY2FsZUZha2VyKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogNC4gRW51bSDtg4DsnoXsnYAg7KCV7J2Y65CcIOqwkiDspJEg7ZWY64KY66W8IOuenOuNpCDshKDtg53tlanri4jri6QgKi9cbiAgICBpZiAocHJvcC50eXBlID09PSBcImVudW1cIikge1xuICAgICAgbGV0IGVudW1WYWx1ZXM6IHN0cmluZ1tdID0gW107XG5cbiAgICAgIGlmIChcImVudW1cIiBpbiBwcm9wICYmIEFycmF5LmlzQXJyYXkocHJvcC5lbnVtKSAmJiBwcm9wLmVudW0ubGVuZ3RoID4gMCkge1xuICAgICAgICBlbnVtVmFsdWVzID0gcHJvcC5lbnVtO1xuICAgICAgfSBlbHNlIGlmIChcImlkXCIgaW4gcHJvcCAmJiBwcm9wLmlkICYmIGVudGl0eT8uZW51bUxhYmVscz8uW3Byb3AuaWRdKSB7XG4gICAgICAgIGVudW1WYWx1ZXMgPSBPYmplY3Qua2V5cyhlbnRpdHkuZW51bUxhYmVsc1twcm9wLmlkXSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbnVtVmFsdWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIGZha2VyLmhlbHBlcnMuYXJyYXlFbGVtZW50KGVudW1WYWx1ZXMpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHByb3AubnVsbGFibGUgPyBudWxsIDogXCJVTktOT1dOXCI7XG4gICAgfVxuXG4gICAgaWYgKHByb3AudHlwZSA9PT0gXCJlbnVtW11cIikge1xuICAgICAgbGV0IGVudW1WYWx1ZXM6IHN0cmluZ1tdID0gW107XG5cbiAgICAgIGlmIChcImVudW1cIiBpbiBwcm9wICYmIEFycmF5LmlzQXJyYXkocHJvcC5lbnVtKSAmJiBwcm9wLmVudW0ubGVuZ3RoID4gMCkge1xuICAgICAgICBlbnVtVmFsdWVzID0gcHJvcC5lbnVtO1xuICAgICAgfSBlbHNlIGlmIChcImlkXCIgaW4gcHJvcCAmJiBwcm9wLmlkICYmIGVudGl0eT8uZW51bUxhYmVscz8uW3Byb3AuaWRdKSB7XG4gICAgICAgIGVudW1WYWx1ZXMgPSBPYmplY3Qua2V5cyhlbnRpdHkuZW51bUxhYmVsc1twcm9wLmlkXSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbnVtVmFsdWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIFtmYWtlci5oZWxwZXJzLmFycmF5RWxlbWVudChlbnVtVmFsdWVzKV07XG4gICAgICB9XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogNS4gVmVjdG9yIO2DgOyeheydgCDtmITsnqwg7KeA7JuQ7ZWY7KeAIOyViuycvOuvgOuhnCBudWxs7J2EIOuwmO2ZmO2VqeuLiOuLpC5cbiAgICAgKiDtlqXtm4QgQUkgZW1iZWRkaW5nIOyDneyEsSDquLDriqUg7LaU6rCAIOyLnCDqtaztmIQg7JiI7KCV7J6F64uI64ukLlxuICAgICAqL1xuICAgIGlmIChwcm9wLnR5cGUgPT09IFwidmVjdG9yXCIgfHwgcHJvcC50eXBlID09PSBcInZlY3RvcltdXCIgfHwgcHJvcC50eXBlID09PSBcInRzdmVjdG9yXCIpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIC8qKiA2LiDtg4DsnoXrs4Qg6riw67O4IEZha2VyIO2RnO2YhOyLneydhCDsi6Ttlontlanri4jri6QgKi9cbiAgICBjb25zdCB0eXBlRGVmYXVsdCA9IGxvY2FsZU1hcHBpbmdzLnR5cGVfZGVmYXVsdHNbcHJvcC50eXBlXTtcbiAgICBpZiAodHlwZURlZmF1bHQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmV4ZWN1dGVGYWtlckV4cHJlc3Npb24odHlwZURlZmF1bHQuZmFrZXIsIHByb3ApO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgY2hhbGsueWVsbG93KGBGYWlsZWQgdG8gZXhlY3V0ZSB0eXBlIGRlZmF1bHQgZm9yICR7cHJvcC50eXBlfSwgdXNpbmcgZmFsbGJhY2s6YCwgZXJyb3IpLFxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8qKiA3LiDrp6TtlZHrkJjsp4Ag7JWK7J2AIO2DgOyeheydgCDquLDrs7ggRmFrZXIg7ZWo7IiY66GcIOyymOumrO2VqeuLiOuLpCAqL1xuICAgIHN3aXRjaCAocHJvcC50eXBlKSB7XG4gICAgICBjYXNlIFwic3RyaW5nXCI6XG4gICAgICBjYXNlIFwic3RyaW5nW11cIjpcbiAgICAgICAgcmV0dXJuIGZha2VyLmxvcmVtLndvcmRzKDMpO1xuICAgICAgY2FzZSBcImludGVnZXJcIjpcbiAgICAgICAgcmV0dXJuIGZha2VyLm51bWJlci5pbnQoeyBtaW46IDEsIG1heDogMTAwMCB9KTtcbiAgICAgIGNhc2UgXCJpbnRlZ2VyW11cIjpcbiAgICAgICAgcmV0dXJuIFtmYWtlci5udW1iZXIuaW50KHsgbWluOiAxLCBtYXg6IDEwMDAgfSldO1xuICAgICAgY2FzZSBcImJpZ0ludGVnZXJcIjpcbiAgICAgICAgcmV0dXJuIGZha2VyLm51bWJlci5iaWdJbnQoeyBtaW46IDFuLCBtYXg6IDEwMDBuIH0pO1xuICAgICAgY2FzZSBcImJpZ0ludGVnZXJbXVwiOlxuICAgICAgICByZXR1cm4gW2Zha2VyLm51bWJlci5iaWdJbnQoeyBtaW46IDFuLCBtYXg6IDEwMDBuIH0pXTtcbiAgICAgIGNhc2UgXCJudW1iZXJcIjpcbiAgICAgIGNhc2UgXCJudW1lcmljXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5udW1iZXIuZmxvYXQoeyBtaW46IDAsIG1heDogMTAwMCB9KTtcbiAgICAgIGNhc2UgXCJudW1iZXJbXVwiOlxuICAgICAgY2FzZSBcIm51bWVyaWNbXVwiOlxuICAgICAgICByZXR1cm4gW2Zha2VyLm51bWJlci5mbG9hdCh7IG1pbjogMCwgbWF4OiAxMDAwIH0pXTtcbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5kYXRhdHlwZS5ib29sZWFuKCk7XG4gICAgICBjYXNlIFwiYm9vbGVhbltdXCI6XG4gICAgICAgIHJldHVybiBbZmFrZXIuZGF0YXR5cGUuYm9vbGVhbigpXTtcbiAgICAgIGNhc2UgXCJkYXRlXCI6XG4gICAgICBjYXNlIFwiZGF0ZVtdXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5kYXRlLnBhc3QoKTtcbiAgICAgIGNhc2UgXCJqc29uXCI6XG4gICAgICAgIHJldHVybiB7fTtcbiAgICAgIGNhc2UgXCJ1dWlkXCI6XG4gICAgICBjYXNlIFwidXVpZFtdXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5zdHJpbmcudXVpZCgpO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIOuwsOyXtCDtg4DsnoXsnZgg6rCS7J2EIOyDneyEse2VqeuLiOuLpC5cbiAgICpcbiAgICog7YOA7J6FIElE7JmAIO2VhOuTnOuqhSDtjKjthLTsnYQg67aE7ISd7ZWY7JesIOyggeygiO2VnCDrsLDsl7Qg642w7J207YSw66W8IOyDneyEse2VqeuLiOuLpC5cbiAgICog7JiIOiBpbWFnZV91cmxzIOKGkiBbe3VybCwgbmFtZSwgbWltZV90eXBlfSwgLi4uXVxuICAgKiAgICAgdGFnX2lkcyDihpIgWzEsIDIzLCA0NV1cbiAgICovXG4gIHByaXZhdGUgZ2VuZXJhdGVBcnJheVZhbHVlKFxuICAgIHByb3A6IEVudGl0eVByb3AsXG4gICAgX2VudGl0eTogRW50aXR5IHwgdW5kZWZpbmVkLFxuICAgIGZha2VyOiB0eXBlb2YgaW1wb3J0KFwiQGZha2VyLWpzL2Zha2VyXCIpLmZha2VyLFxuICAgIF9sb2NhbGVGYWtlcjogdHlwZW9mIGltcG9ydChcIkBmYWtlci1qcy9mYWtlclwiKS5mYWtlcixcbiAgKTogdW5rbm93bltdIHtcbiAgICBjb25zdCBjb3VudCA9IGZha2VyLm51bWJlci5pbnQoeyBtaW46IDEsIG1heDogMyB9KTtcblxuICAgIC8qKiBTb25hbXVGaWxlW13snYAgU29uYW11IOuCtOyepSDtg4DsnoXsnLzroZwg6rWs7KGw6rCAIOygle2VtOyguCDsnojsirXri4jri6QgKi9cbiAgICBpZiAoXCJpZFwiIGluIHByb3AgJiYgcHJvcC5pZCA9PT0gXCJTb25hbXVGaWxlW11cIikge1xuICAgICAgcmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGNvdW50IH0sICgpID0+ICh7XG4gICAgICAgIHVybDogZmFrZXIuaW1hZ2UudXJsKCksXG4gICAgICAgIG5hbWU6IGZha2VyLnN5c3RlbS5maWxlTmFtZSgpLFxuICAgICAgICBtaW1lX3R5cGU6IGZha2VyLmhlbHBlcnMuYXJyYXlFbGVtZW50KFtcbiAgICAgICAgICBcImltYWdlL2pwZWdcIixcbiAgICAgICAgICBcImltYWdlL3BuZ1wiLFxuICAgICAgICAgIFwiaW1hZ2UvZ2lmXCIsXG4gICAgICAgICAgXCJhcHBsaWNhdGlvbi9wZGZcIixcbiAgICAgICAgXSksXG4gICAgICB9KSk7XG4gICAgfVxuXG4gICAgLyoqIO2VhOuTnOuqheyXkOyEnCDrsLDsl7TsnZgg7Jqp64+E66W8IOy2lOuhoO2VqeuLiOuLpCAqL1xuICAgIGNvbnN0IG5vcm1hbGl6ZWROYW1lID0gcHJvcC5uYW1lLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgvXy9nLCBcIlwiKTtcblxuICAgIGlmIChub3JtYWxpemVkTmFtZS5pbmNsdWRlcyhcInVybFwiKSB8fCBub3JtYWxpemVkTmFtZS5pbmNsdWRlcyhcImltYWdlXCIpKSB7XG4gICAgICByZXR1cm4gQXJyYXkuZnJvbSh7IGxlbmd0aDogY291bnQgfSwgKCkgPT4gZmFrZXIuaW50ZXJuZXQudXJsKCkpO1xuICAgIH1cblxuICAgIGlmIChub3JtYWxpemVkTmFtZS5pbmNsdWRlcyhcImlkXCIpICYmIG5vcm1hbGl6ZWROYW1lLmVuZHNXaXRoKFwic1wiKSkge1xuICAgICAgcmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGNvdW50IH0sICgpID0+IGZha2VyLm51bWJlci5pbnQoeyBtaW46IDEsIG1heDogMTAwIH0pKTtcbiAgICB9XG5cbiAgICBpZiAobm9ybWFsaXplZE5hbWUuaW5jbHVkZXMoXCJ0YWdcIikgfHwgbm9ybWFsaXplZE5hbWUuaW5jbHVkZXMoXCJuYW1lXCIpKSB7XG4gICAgICByZXR1cm4gQXJyYXkuZnJvbSh7IGxlbmd0aDogY291bnQgfSwgKCkgPT4gZmFrZXIubG9yZW0ud29yZCgpKTtcbiAgICB9XG5cbiAgICAvKiog7Yyo7YS0IOunpOy5reuQmOyngCDslYrsnLzrqbQg67mIIOuwsOyXtOydhCDrsJjtmZjtlanri4jri6QgKi9cbiAgICByZXR1cm4gW107XG4gIH1cblxuICAvKipcbiAgICogSlNPTiDrp6TtlZHsnZggRmFrZXIg7ZGc7ZiE7Iud7J2EIO2MjOyLse2VmOyXrCDsi6Ttlontlanri4jri6QuXG4gICAqXG4gICAqIO2RnO2YhOyLnSDsmIjsi5w6XG4gICAqIC0gXCJmYWtlci5pbnRlcm5ldC5lbWFpbCgpXCIg4oaSIOyduOyekCDsl4bsnYxcbiAgICogLSBcImZha2VyLm51bWJlci5pbnQoeyBtaW46IDEsIG1heDogMTAwIH0pXCIg4oaSIEpTT04g7J247J6QXG4gICAqIC0gXCJ7fVwiIOKGkiDrpqzthLDrn7Qg6rCSIChKU09OLnBhcnNlKVxuICAgKlxuICAgKiBmYWtlcktPLCBmYWtlckpB64+EIOyngOybkO2VmOyXrCDri6Tqta3slrQg642w7J207YSw66W8IOyDneyEse2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZXhlY3V0ZUZha2VyRXhwcmVzc2lvbihleHByZXNzaW9uOiBzdHJpbmcsIHByb3A6IEVudGl0eVByb3ApOiBQcm9taXNlPHVua25vd24+IHtcbiAgICBjb25zdCBmYWtlck1vZHVsZSA9IGF3YWl0IGltcG9ydChcIkBmYWtlci1qcy9mYWtlclwiKTtcbiAgICBjb25zdCBmYWtlciA9IGZha2VyTW9kdWxlLmZha2VyO1xuICAgIGNvbnN0IGZha2VyS08gPSBmYWtlck1vZHVsZS5mYWtlcktPO1xuICAgIGNvbnN0IGZha2VySkEgPSBmYWtlck1vZHVsZS5mYWtlckpBO1xuXG4gICAgLyoqIEZha2VyIO2RnO2YhOyLneydtCDslYTri4wg66as7YSw65+0IOqwkuydgCBKU09O7Jy866GcIO2MjOyLse2VqeuLiOuLpCAqL1xuICAgIGlmICghZXhwcmVzc2lvbi5zdGFydHNXaXRoKFwiZmFrZXJcIikpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBKU09OLnBhcnNlKGV4cHJlc3Npb24pO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIHJldHVybiBleHByZXNzaW9uO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8qKiDtkZztmITsi53sl5DshJwgRmFrZXIg6rCd7LK07JmAIOqyveuhnOulvCDstpTstpztlanri4jri6QgKi9cbiAgICBjb25zdCBtYXRjaCA9IGV4cHJlc3Npb24ubWF0Y2goL14oZmFrZXJ8ZmFrZXJLT3xmYWtlckpBKVxcLiguKj8pJC8pO1xuICAgIGlmICghbWF0Y2gpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBmYWtlciBleHByZXNzaW9uOiAke2V4cHJlc3Npb259YCk7XG4gICAgfVxuXG4gICAgY29uc3QgWywgZmFrZXJOYW1lLCBleHByXSA9IG1hdGNoO1xuICAgIGNvbnN0IHNlbGVjdGVkRmFrZXIgPVxuICAgICAgZmFrZXJOYW1lID09PSBcImZha2VyS09cIiA/IGZha2VyS08gOiBmYWtlck5hbWUgPT09IFwiZmFrZXJKQVwiID8gZmFrZXJKQSA6IGZha2VyO1xuXG4gICAgY29uc3QgZnVuY01hdGNoID0gZXhwci5tYXRjaCgvXihbXFx3Ll0rKSg/OlxcKCguKj8pXFwpKT8kLyk7XG4gICAgaWYgKCFmdW5jTWF0Y2gpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBmYWtlciBleHByZXNzaW9uIGZvciAke3Byb3AubmFtZX06ICR7ZXhwcmVzc2lvbn1gKTtcbiAgICB9XG5cbiAgICBjb25zdCBbLCBwYXRoLCBhcmdzU3RyXSA9IGZ1bmNNYXRjaDtcbiAgICBjb25zdCBwYXJ0cyA9IHBhdGguc3BsaXQoXCIuXCIpO1xuXG4gICAgLyoqIOygkCDtkZzquLDrspUoZG90IG5vdGF0aW9uKeycvOuhnCBGYWtlciDtlajsiJjrpbwg7LC+7JWE6rCR64uI64ukICovXG4gICAgbGV0IGZuOiB1bmtub3duID0gc2VsZWN0ZWRGYWtlcjtcbiAgICBmb3IgKGNvbnN0IHBhcnQgb2YgcGFydHMpIHtcbiAgICAgIGlmICh0eXBlb2YgZm4gPT09IFwib2JqZWN0XCIgJiYgZm4gIT09IG51bGwgJiYgcGFydCBpbiBmbikge1xuICAgICAgICBmbiA9IChmbiBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPilbcGFydF07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZmFrZXIgcGF0aCBmb3IgJHtwcm9wLm5hbWV9OiAke2Zha2VyTmFtZX0uJHtwYXRofWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmICh0eXBlb2YgZm4gIT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGAke2Zha2VyTmFtZX0uJHtwYXRofSBpcyBub3QgYSBmdW5jdGlvbiAoZm9yICR7cHJvcC5uYW1lfSlgKTtcbiAgICB9XG5cbiAgICAvKiog7ZWo7IiYIOyduOyekOulvCBKU09O7Jy866GcIO2MjOyLse2VqeuLiOuLpCAqL1xuICAgIGxldCBhcmdzOiB1bmtub3duW10gPSBbXTtcbiAgICBpZiAoYXJnc1N0cj8udHJpbSgpKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKGBbJHthcmdzU3RyfV1gKSBhcyB1bmtub3duO1xuICAgICAgICBhcmdzID0gQXJyYXkuaXNBcnJheShwYXJzZWQpID8gcGFyc2VkIDogW3BhcnNlZF07XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLyoqIEpTT04g7YyM7IuxIOyLpO2MqCDsi5wg64uo7IicIOyIq+yekC/rrLjsnpDsl7TroZwg7Iuc64+E7ZWp64uI64ukICovXG4gICAgICAgIGNvbnN0IHRyaW1tZWQgPSBhcmdzU3RyLnRyaW0oKTtcbiAgICAgICAgaWYgKCFOdW1iZXIuaXNOYU4oTnVtYmVyKHRyaW1tZWQpKSkge1xuICAgICAgICAgIGFyZ3MgPSBbTnVtYmVyKHRyaW1tZWQpXTtcbiAgICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgICAodHJpbW1lZC5zdGFydHNXaXRoKCdcIicpICYmIHRyaW1tZWQuZW5kc1dpdGgoJ1wiJykpIHx8XG4gICAgICAgICAgKHRyaW1tZWQuc3RhcnRzV2l0aChcIidcIikgJiYgdHJpbW1lZC5lbmRzV2l0aChcIidcIikpXG4gICAgICAgICkge1xuICAgICAgICAgIGFyZ3MgPSBbdHJpbW1lZC5zbGljZSgxLCAtMSldO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2Fubm90IHBhcnNlIGFyZ3VtZW50cyBmb3IgJHtwcm9wLm5hbWV9OiAke2FyZ3NTdHJ9YCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZm4oLi4uYXJncyk7XG4gIH1cblxuICAvKipcbiAgICogZml4dHVyZUhpbnTrpbwgTExN7JeQ6rKMIOyghOuLrO2VmOyXrCDtmITsi6TsoIHsnbgg7YWM7Iqk7Yq4IOuNsOydtO2EsOulvCDsg53shLHtlanri4jri6QuXG4gICAqXG4gICAqIGZha2VyLmpz66Gc64qUIOyDneyEse2VmOq4sCDslrTroKTsmrQg67O17J6h7ZWcIO2FjeyKpO2KuCjsnpDquLDshozqsJwsIOyEpOuqheusuCDrk7Ep66W8XG4gICAqIExMTeydhCDtmZzsmqntlZjsl6wg7IOd7ISx7ZWp64uI64ukLiDrj5nsnbztlZwgaGludOyXkCDrjIDtlZwg7KSR67O1IO2YuOy2nOydhCDrsKnsp4DtlZjquLAg7JyE7ZW0XG4gICAqIOy6kOyLseydhCDquLDrs7jsnLzroZwg7KeA7JuQ7ZWp64uI64ukIChMTE0gQVBJIOu5hOyaqSDsoIjqsJApLlxuICAgKlxuICAgKiBhaSDtjKjtgqTsp4DripQgZHluYW1pYyBpbXBvcnTroZwg67aI65+s7Jik66+A66GcLCB1c2VMTE3snbQgZmFsc2Xsnbgg6rK97JqwXG4gICAqIOydmOyhtOyEseydtCDshKTsuZjrkJjsp4Ag7JWK7JWE64+EIGZpeHR1cmUg7IOd7ISx7J20IOygleyDgSDrj5nsnpHtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdlbmVyYXRlV2l0aExMTShcbiAgICBmaXh0dXJlSGludDogc3RyaW5nLFxuICAgIHByb3A6IEVudGl0eVByb3AsXG4gICAgZW50aXR5OiBFbnRpdHksXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIGNvbnN0IGNhY2hlS2V5ID0gYCR7ZW50aXR5LmlkfToke3Byb3AubmFtZX06JHtmaXh0dXJlSGludH1gO1xuICAgIGlmICh0aGlzLm9wdGlvbnMuZW5hYmxlTExNQ2FjaGUgJiYgdGhpcy5sbG1DYWNoZS5oYXMoY2FjaGVLZXkpKSB7XG4gICAgICByZXR1cm4gdGhpcy5sbG1DYWNoZS5nZXQoY2FjaGVLZXkpO1xuICAgIH1cblxuICAgIGNvbnN0IGFwaUtleSA9IHRoaXMuZ2V0QXBpS2V5KCk7XG4gICAgY29uc3QgeyBjcmVhdGVBbnRocm9waWMgfSA9IGF3YWl0IGltcG9ydChcIkBhaS1zZGsvYW50aHJvcGljXCIpO1xuICAgIGNvbnN0IHsgZ2VuZXJhdGVUZXh0IH0gPSBhd2FpdCBpbXBvcnQoXCJhaVwiKTtcblxuICAgIGNvbnN0IHsgdGV4dCB9ID0gYXdhaXQgZ2VuZXJhdGVUZXh0KHtcbiAgICAgIG1vZGVsOiBjcmVhdGVBbnRocm9waWMoeyBhcGlLZXkgfSkodGhpcy5vcHRpb25zLmxsbU1vZGVsIHx8IFwiY2xhdWRlLXNvbm5ldC00LTVcIiksXG4gICAgICBwcm9tcHQ6IHRoaXMuYnVpbGRMTE1Qcm9tcHQoZml4dHVyZUhpbnQsIHByb3AsIGVudGl0eSksXG4gICAgfSk7XG5cbiAgICBjb25zdCB2YWx1ZSA9IHRoaXMucGFyc2VMTE1SZXNwb25zZSh0ZXh0LCBwcm9wLnR5cGUpO1xuICAgIGlmICh0aGlzLm9wdGlvbnMuZW5hYmxlTExNQ2FjaGUpIHtcbiAgICAgIHRoaXMubGxtQ2FjaGUuc2V0KGNhY2hlS2V5LCB2YWx1ZSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG5cbiAgcHJpdmF0ZSBidWlsZExMTVByb21wdChoaW50OiBzdHJpbmcsIHByb3A6IEVudGl0eVByb3AsIGVudGl0eTogRW50aXR5KTogc3RyaW5nIHtcbiAgICBjb25zdCBsb2NhbGUgPSB0aGlzLm9wdGlvbnMubG9jYWxlIHx8IFwia29cIjtcbiAgICBjb25zdCBsYW5ndWFnZSA9IGxvY2FsZSA9PT0gXCJrb1wiID8gXCJLb3JlYW5cIiA6IGxvY2FsZSA9PT0gXCJqYVwiID8gXCJKYXBhbmVzZVwiIDogXCJFbmdsaXNoXCI7XG5cbiAgICBsZXQgcHJvbXB0ID0gYEdlbmVyYXRlIHRlc3QgZGF0YSBmb3IgJHtlbnRpdHkuaWR9LiR7cHJvcC5uYW1lfSAodHlwZTogJHtwcm9wLnR5cGV9KVxuXG5SZXF1aXJlbWVudDogJHtoaW50fVxuXG5SdWxlczpcbi0gUmV0dXJuIE9OTFkgdGhlIHZhbHVlLCBubyBleHBsYW5hdGlvbiBvciBtYXJrZG93blxuLSBVc2UgJHtsYW5ndWFnZX0gbGFuZ3VhZ2UgaWYgYXBwbGljYWJsZVxuLSBGb3JtYXQ6ICR7dGhpcy5nZXRFeHBlY3RlZEZvcm1hdChwcm9wLnR5cGUpfWA7XG5cbiAgICAvLyBlbnVtIO2DgOyeheyduCDqsr3smrAg6rCA64ql7ZWcIOqwkiDrqqnroZ0g7LaU6rCAXG4gICAgaWYgKHByb3AudHlwZSA9PT0gXCJlbnVtXCIgfHwgcHJvcC50eXBlID09PSBcImVudW1bXVwiKSB7XG4gICAgICBsZXQgZW51bVZhbHVlczogc3RyaW5nW10gPSBbXTtcblxuICAgICAgaWYgKFwiZW51bVwiIGluIHByb3AgJiYgQXJyYXkuaXNBcnJheShwcm9wLmVudW0pICYmIHByb3AuZW51bS5sZW5ndGggPiAwKSB7XG4gICAgICAgIGVudW1WYWx1ZXMgPSBwcm9wLmVudW07XG4gICAgICB9IGVsc2UgaWYgKFwiaWRcIiBpbiBwcm9wICYmIHByb3AuaWQgJiYgZW50aXR5Py5lbnVtTGFiZWxzPy5bcHJvcC5pZF0pIHtcbiAgICAgICAgZW51bVZhbHVlcyA9IE9iamVjdC5rZXlzKGVudGl0eS5lbnVtTGFiZWxzW3Byb3AuaWRdKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGVudW1WYWx1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgICBwcm9tcHQgKz0gYFxcbi0gSU1QT1JUQU5UOiBDaG9vc2UgT05MWSBmcm9tIHRoZXNlIGFsbG93ZWQgdmFsdWVzOiAke2VudW1WYWx1ZXMuam9pbihcIiwgXCIpfWA7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcHJvbXB0ICs9IGBcXG5cXG5FeGFtcGxlOiAke3RoaXMuZ2V0RXhhbXBsZUZvclR5cGUocHJvcC50eXBlLCBsb2NhbGUpfWA7XG5cbiAgICByZXR1cm4gcHJvbXB0O1xuICB9XG5cbiAgcHJpdmF0ZSBwYXJzZUxMTVJlc3BvbnNlKHRleHQ6IHN0cmluZywgcHJvcFR5cGU6IHN0cmluZyk6IHVua25vd24ge1xuICAgIGNvbnN0IGNsZWFuZWQgPSB0ZXh0LnRyaW0oKTtcblxuICAgIC8vIOuwsOyXtCDtg4DsnoUg7LKY66asXG4gICAgaWYgKHByb3BUeXBlLmVuZHNXaXRoKFwiW11cIikpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHBhcnNlZCA9IEpTT04ucGFyc2UoY2xlYW5lZCk7XG4gICAgICAgIGNvbnN0IGJhc2VUeXBlID0gcHJvcFR5cGUuc2xpY2UoMCwgLTIpOyAvLyBcImludGVnZXJbXVwiIC0+IFwiaW50ZWdlclwiXG5cbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkocGFyc2VkKSkge1xuICAgICAgICAgIHJldHVybiBwYXJzZWQubWFwKChpdGVtKSA9PiB7XG4gICAgICAgICAgICAvLyBudWxsL3VuZGVmaW5lZOuKlCDtg4DsnoXrs4Qg6riw67O46rCS7Jy866GcXG4gICAgICAgICAgICBpZiAoaXRlbSA9PT0gbnVsbCB8fCBpdGVtID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0RGVmYXVsdFZhbHVlRm9yVHlwZShiYXNlVHlwZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyDqsJ3ssrTripQgSlNPTi5zdHJpbmdpZnkg7ZuEIO2MjOyLsSAoanNvbiDtg4DsnoXsnbgg6rK97JqwKVxuICAgICAgICAgICAgaWYgKHR5cGVvZiBpdGVtID09PSBcIm9iamVjdFwiKSB7XG4gICAgICAgICAgICAgIHJldHVybiBiYXNlVHlwZSA9PT0gXCJqc29uXCJcbiAgICAgICAgICAgICAgICA/IGl0ZW1cbiAgICAgICAgICAgICAgICA6IHRoaXMucGFyc2VTY2FsYXJWYWx1ZShKU09OLnN0cmluZ2lmeShpdGVtKSwgYmFzZVR5cGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gcHJpbWl0aXZlIOqwkuydgCDrrLjsnpDsl7TroZwg67OA7ZmYIO2bhCDtjIzsi7FcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnBhcnNlU2NhbGFyVmFsdWUoU3RyaW5nKGl0ZW0pLCBiYXNlVHlwZSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyDri6jsnbwg6rCS7J20IOyYqCDqsr3smrAg67Cw7Je066GcIOqwkOyLuOq4sFxuICAgICAgICBpZiAocGFyc2VkID09PSBudWxsIHx8IHBhcnNlZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcmV0dXJuIFt0aGlzLmdldERlZmF1bHRWYWx1ZUZvclR5cGUoYmFzZVR5cGUpXTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gW3RoaXMucGFyc2VTY2FsYXJWYWx1ZShTdHJpbmcocGFyc2VkKSwgYmFzZVR5cGUpXTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMucGFyc2VTY2FsYXJWYWx1ZShjbGVhbmVkLCBwcm9wVHlwZSk7XG4gIH1cblxuICBwcml2YXRlIGdldERlZmF1bHRWYWx1ZUZvclR5cGUocHJvcFR5cGU6IHN0cmluZyk6IHVua25vd24ge1xuICAgIHN3aXRjaCAocHJvcFR5cGUpIHtcbiAgICAgIGNhc2UgXCJpbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiAwO1xuICAgICAgY2FzZSBcImJpZ0ludGVnZXJcIjpcbiAgICAgICAgcmV0dXJuIDBuO1xuICAgICAgY2FzZSBcImZsb2F0XCI6XG4gICAgICBjYXNlIFwibnVtYmVyXCI6XG4gICAgICBjYXNlIFwibnVtZXJpY1wiOlxuICAgICAgICByZXR1cm4gMDtcbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIGNhc2UgXCJkYXRlXCI6XG4gICAgICAgIHJldHVybiBuZXcgRGF0ZSgpO1xuICAgICAgY2FzZSBcImpzb25cIjpcbiAgICAgICAgcmV0dXJuIHt9O1xuICAgICAgY2FzZSBcInV1aWRcIjpcbiAgICAgICAgcmV0dXJuIFwiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwXCI7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gXCJcIjtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHBhcnNlU2NhbGFyVmFsdWUodGV4dDogc3RyaW5nLCBwcm9wVHlwZTogc3RyaW5nKTogdW5rbm93biB7XG4gICAgY29uc3QgY2xlYW5lZCA9IHRleHQudHJpbSgpO1xuXG4gICAgc3dpdGNoIChwcm9wVHlwZSkge1xuICAgICAgY2FzZSBcImludGVnZXJcIjoge1xuICAgICAgICBjb25zdCBudW0gPSBwYXJzZUludChjbGVhbmVkLCAxMCk7XG4gICAgICAgIHJldHVybiBOdW1iZXIuaXNOYU4obnVtKSA/IDAgOiBudW07XG4gICAgICB9XG4gICAgICBjYXNlIFwiYmlnSW50ZWdlclwiOiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmV0dXJuIEJpZ0ludChjbGVhbmVkKTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgcmV0dXJuIDBuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjYXNlIFwiZmxvYXRcIjpcbiAgICAgIGNhc2UgXCJudW1iZXJcIjpcbiAgICAgIGNhc2UgXCJudW1lcmljXCI6IHtcbiAgICAgICAgY29uc3QgbnVtID0gcGFyc2VGbG9hdChjbGVhbmVkKTtcbiAgICAgICAgcmV0dXJuIE51bWJlci5pc05hTihudW0pID8gMCA6IG51bTtcbiAgICAgIH1cbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBjbGVhbmVkLnRvTG93ZXJDYXNlKCkgPT09IFwidHJ1ZVwiO1xuICAgICAgY2FzZSBcImRhdGVcIjoge1xuICAgICAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoY2xlYW5lZCk7XG4gICAgICAgIHJldHVybiBOdW1iZXIuaXNOYU4oZGF0ZS5nZXRUaW1lKCkpID8gbmV3IERhdGUoKSA6IGRhdGU7XG4gICAgICB9XG4gICAgICBjYXNlIFwianNvblwiOlxuICAgICAgICB0cnkge1xuICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGNsZWFuZWQpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICByZXR1cm4gY2xlYW5lZDtcbiAgICAgICAgfVxuICAgICAgY2FzZSBcInV1aWRcIjpcbiAgICAgIGNhc2UgXCJlbnVtXCI6XG4gICAgICAgIHJldHVybiBjbGVhbmVkO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIGNsZWFuZWQ7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNvbmFtdS5zZWNyZXTsnYQg7Jqw7ISg7Jy866GcIO2VmOqzoCwg7JeG7Jy866m0IO2ZmOqyveuzgOyImOyXkOyEnCBBUEkg7YKk66W8IOydveyKteuLiOuLpC5cbiAgICpcbiAgICogU29uYW11LnNlY3JldOydgCDtlITroZzsoJ3tirjrs4Qg7ISk7KCVKHNvbmFtdS5jb25maWcudHMp7J2066+A66GcIOuNlCDrhpLsnYAg7Jqw7ISg7Iic7JyE66W8IOqwgOyngOupsCxcbiAgICog7ZmY6rK967OA7IiY64qUIOqwnOuwnCDtmZjqsr3snbTrgpggQ0kvQ0Tsl5DshJwgZmFsbGJhY2vsnLzroZwg7IKs7Jqp65Cp64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBnZXRBcGlLZXkoKTogc3RyaW5nIHtcbiAgICBsZXQgYXBpS2V5OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgeyBTb25hbXUgfSA9IHJlcXVpcmUoXCIuLi9hcGlcIik7XG4gICAgICBhcGlLZXkgPSBTb25hbXUuc2VjcmV0Py5hbnRocm9waWNfYXBpX2tleTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIFNvbmFtdeqwgCDstIjquLDtmZTrkJjsp4Ag7JWK7J2AIOqyveyasCAo7YWM7Iqk7Yq4IO2ZmOqyvSDrk7EpXG4gICAgfVxuXG4gICAgaWYgKCFhcGlLZXkpIHtcbiAgICAgIGFwaUtleSA9IHByb2Nlc3MuZW52LkFOVEhST1BJQ19BUElfS0VZO1xuICAgIH1cblxuICAgIGlmICghYXBpS2V5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIFwiQU5USFJPUElDX0FQSV9LRVkgbm90IGZvdW5kLiBTZXQgaXQgaW4gZW52aXJvbm1lbnQgdmFyaWFibGVzIG9yIFNvbmFtdS5zZWNyZXQuYW50aHJvcGljX2FwaV9rZXlcIixcbiAgICAgICk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGFwaUtleTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0RXhwZWN0ZWRGb3JtYXQocHJvcFR5cGU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgLy8g67Cw7Je0IO2DgOyehSDsspjrpqxcbiAgICBpZiAocHJvcFR5cGUuZW5kc1dpdGgoXCJbXVwiKSkge1xuICAgICAgY29uc3QgYmFzZVR5cGUgPSBwcm9wVHlwZS5zbGljZSgwLCAtMik7XG4gICAgICBjb25zdCBiYXNlRm9ybWF0ID0gdGhpcy5nZXRTY2FsYXJGb3JtYXQoYmFzZVR5cGUpO1xuICAgICAgcmV0dXJuIGBKU09OIGFycmF5IG9mICR7YmFzZUZvcm1hdH0gKGUuZy4sIFske3RoaXMuZ2V0RXhhbXBsZUZvclR5cGUoYmFzZVR5cGUsIFwiZW5cIil9LCAuLi5dKWA7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuZ2V0U2NhbGFyRm9ybWF0KHByb3BUeXBlKTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0U2NhbGFyRm9ybWF0KHByb3BUeXBlOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHN3aXRjaCAocHJvcFR5cGUpIHtcbiAgICAgIGNhc2UgXCJpbnRlZ2VyXCI6XG4gICAgICBjYXNlIFwiYmlnSW50ZWdlclwiOlxuICAgICAgICByZXR1cm4gXCJpbnRlZ2VyIG51bWJlcnNcIjtcbiAgICAgIGNhc2UgXCJmbG9hdFwiOlxuICAgICAgY2FzZSBcIm51bWJlclwiOlxuICAgICAgY2FzZSBcIm51bWVyaWNcIjpcbiAgICAgICAgcmV0dXJuIFwiZGVjaW1hbCBudW1iZXJzXCI7XG4gICAgICBjYXNlIFwiYm9vbGVhblwiOlxuICAgICAgICByZXR1cm4gXCJib29sZWFucyAodHJ1ZSBvciBmYWxzZSlcIjtcbiAgICAgIGNhc2UgXCJkYXRlXCI6XG4gICAgICAgIHJldHVybiBcIklTTyA4NjAxIGRhdGUgc3RyaW5nc1wiO1xuICAgICAgY2FzZSBcImpzb25cIjpcbiAgICAgICAgcmV0dXJuIFwidmFsaWQgSlNPTiBvYmplY3Qgb3IgYXJyYXlcIjtcbiAgICAgIGNhc2UgXCJ1dWlkXCI6XG4gICAgICAgIHJldHVybiBcIlVVSUQgc3RyaW5nc1wiO1xuICAgICAgY2FzZSBcImVudW1cIjpcbiAgICAgICAgcmV0dXJuIFwib25lIG9mIHRoZSBhbGxvd2VkIGVudW0gdmFsdWVzXCI7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gXCJwbGFpbiB0ZXh0IHN0cmluZ3NcIjtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdldEV4YW1wbGVGb3JUeXBlKHByb3BUeXBlOiBzdHJpbmcsIGxvY2FsZTogTG9jYWxlKTogc3RyaW5nIHtcbiAgICAvLyDrsLDsl7Qg7YOA7J6FIOyymOumrFxuICAgIGlmIChwcm9wVHlwZS5lbmRzV2l0aChcIltdXCIpKSB7XG4gICAgICBjb25zdCBiYXNlVHlwZSA9IHByb3BUeXBlLnNsaWNlKDAsIC0yKTtcbiAgICAgIGNvbnN0IGJhc2VFeGFtcGxlID0gdGhpcy5nZXRTY2FsYXJFeGFtcGxlKGJhc2VUeXBlLCBsb2NhbGUpO1xuICAgICAgcmV0dXJuIGBbJHtiYXNlRXhhbXBsZX1dYDtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5nZXRTY2FsYXJFeGFtcGxlKHByb3BUeXBlLCBsb2NhbGUpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRTY2FsYXJFeGFtcGxlKHByb3BUeXBlOiBzdHJpbmcsIGxvY2FsZTogTG9jYWxlKTogc3RyaW5nIHtcbiAgICBjb25zdCBpc0tvcmVhbiA9IGxvY2FsZSA9PT0gXCJrb1wiO1xuXG4gICAgc3dpdGNoIChwcm9wVHlwZSkge1xuICAgICAgY2FzZSBcImludGVnZXJcIjpcbiAgICAgIGNhc2UgXCJiaWdJbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiBcIjQyXCI7XG4gICAgICBjYXNlIFwiZmxvYXRcIjpcbiAgICAgIGNhc2UgXCJudW1iZXJcIjpcbiAgICAgIGNhc2UgXCJudW1lcmljXCI6XG4gICAgICAgIHJldHVybiBcIjMuMTRcIjtcbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBcInRydWVcIjtcbiAgICAgIGNhc2UgXCJkYXRlXCI6XG4gICAgICAgIHJldHVybiBcIjIwMjQtMDEtMDFcIjtcbiAgICAgIGNhc2UgXCJqc29uXCI6XG4gICAgICAgIHJldHVybiAne1wia2V5XCI6IFwidmFsdWVcIn0nO1xuICAgICAgY2FzZSBcInV1aWRcIjpcbiAgICAgICAgcmV0dXJuIFwiNTUwZTg0MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAwXCI7XG4gICAgICBjYXNlIFwiZW51bVwiOlxuICAgICAgICByZXR1cm4gXCJFTlVNX1ZBTFVFXCI7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gaXNLb3JlYW4gPyBcIuyViOuFle2VmOyEuOyalFwiIDogXCJIZWxsb1wiO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBMTE0g7LqQ7IucIO2GteqzhOulvCDrsJjtmZjtlanri4jri6QuXG4gICAqL1xuICBnZXRMTE1DYWNoZVN0YXRzKCkge1xuICAgIHJldHVybiB7XG4gICAgICBzaXplOiB0aGlzLmxsbUNhY2hlLnNpemUsXG4gICAgICBlbmFibGVkOiB0aGlzLm9wdGlvbnMuZW5hYmxlTExNQ2FjaGUsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMTE0g7LqQ7Iuc66W8IOy0iOq4sO2ZlO2VqeuLiOuLpC5cbiAgICovXG4gIGNsZWFyTExNQ2FjaGUoKSB7XG4gICAgdGhpcy5sbG1DYWNoZS5jbGVhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIOy7qO2FjeyKpO2KuCDsg53shLFcbiAgICovXG4gIHByaXZhdGUgY3JlYXRlQ29udGV4dCgpOiBHZW5lcmF0b3JDb250ZXh0IHtcbiAgICByZXR1cm4ge1xuICAgICAgZml4dHVyZXM6IG5ldyBNYXAoKSxcbiAgICAgIHJlZmVyZW5jZUNhY2hlOiBuZXcgTWFwKCksXG4gICAgICBpbXBvcnRlZFJlY29yZHM6IG5ldyBTZXQoKSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIOuwsOy5mCDsg53shLEg67CPIOyekOuPmSDsoIDsnqVcbiAgICpcbiAgICogMS4g6rCBIHNwZWPrs4TroZwgZml4dHVyZSDsg53shLEgKOuplOuqqOumrClcbiAgICogMi4gRml4dHVyZVJlY29yZOuhnCDrs4DtmZhcbiAgICogMy4gRml4dHVyZU1hbmFnZXIuaW5zZXJ0Rml4dHVyZXMoKeuhnCB0YXJnZXREYuyXkCDsoIDsnqVcbiAgICpcbiAgICogQHJldHVybnMg7KCA7J6l65CcIGZpeHR1cmUg642w7J207YSwICjsi6TsoJwgREIgSUQg7Y+s7ZWoKVxuICAgKi9cbiAgYXN5bmMgZ2VuZXJhdGVCYXRjaChcbiAgICBzcGVjczogQXJyYXk8eyBlbnRpdHk6IHN0cmluZzsgY291bnQ6IG51bWJlcjsgb3ZlcnJpZGVzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfT4sXG4gICk6IFByb21pc2U8Rml4dHVyZUltcG9ydFJlc3VsdFtdPiB7XG4gICAgY29uc3QgY29udGV4dCA9IHRoaXMuY3JlYXRlQ29udGV4dCgpO1xuICAgIGNvbnN0IGdlbmVyYXRlZEZpeHR1cmVzOiBBcnJheTx7IGVudGl0eTogc3RyaW5nOyBkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9PiA9IFtdO1xuXG4gICAgLy8gMS4g6rCBIHNwZWPrs4TroZwgZml4dHVyZSDsg53shLFcbiAgICBmb3IgKGNvbnN0IHNwZWMgb2Ygc3BlY3MpIHtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc3BlYy5jb3VudDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IGZpeHR1cmUgPSBhd2FpdCB0aGlzLmdlbmVyYXRlKHNwZWMuZW50aXR5LCBzcGVjLm92ZXJyaWRlcyB8fCB7fSwgY29udGV4dCk7XG4gICAgICAgIGdlbmVyYXRlZEZpeHR1cmVzLnB1c2goe1xuICAgICAgICAgIGVudGl0eTogc3BlYy5lbnRpdHksXG4gICAgICAgICAgZGF0YTogZml4dHVyZSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gMi4gRml4dHVyZVJlY29yZOuhnCDrs4DtmZhcbiAgICBjb25zdCBmaXh0dXJlUmVjb3JkczogRml4dHVyZVJlY29yZFtdID0gW107XG4gICAgZm9yIChjb25zdCB7IGVudGl0eTogZW50aXR5TmFtZSwgZGF0YSB9IG9mIGdlbmVyYXRlZEZpeHR1cmVzKSB7XG4gICAgICBjb25zdCBlbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KGVudGl0eU5hbWUpO1xuXG4gICAgICAvLyDsnoTsi5wgSUQg7IOd7ISxICh0YXJnZXREYuyXkCBJTlNFUlQg7ZuEIOyLpOygnCBJROulvCDrsJvsnYwpXG4gICAgICBjb25zdCB0ZW1wSWQgPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAxMDAwMDAwKTtcbiAgICAgIGNvbnN0IHJlY29yZHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5jcmVhdGVGaXh0dXJlUmVjb3JkKFxuICAgICAgICBlbnRpdHksXG4gICAgICAgIHsgLi4uZGF0YSwgaWQ6IHRlbXBJZCB9IGFzIHsgaWQ6IG51bWJlcjsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwgfSxcbiAgICAgICAgeyBzaW5nbGVSZWNvcmQ6IHRydWUgfSxcbiAgICAgICk7XG4gICAgICBmaXh0dXJlUmVjb3Jkcy5wdXNoKC4uLnJlY29yZHMpO1xuICAgIH1cblxuICAgIC8vIDMuIHRhcmdldERi7JeQIOyCveyehSAoRml4dHVyZU1hbmFnZXLqsIAg7J2Y7KG07ISxIOygleugrCDsspjrpqwpXG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVzKHRoaXMudGFyZ2V0RGJOYW1lLCBmaXh0dXJlUmVjb3Jkcyk7XG5cbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIGNoYWxrLmdyZWVuKGBHZW5lcmF0ZWQgYW5kIHNhdmVkICR7cmVzdWx0cy5sZW5ndGh9IGZpeHR1cmVzIHRvICR7dGhpcy50YXJnZXREYk5hbWV9YCksXG4gICAgKTtcbiAgICByZXR1cm4gcmVzdWx0cztcbiAgfVxuXG4gIC8qKlxuICAgKiDsi6TsoJwgREIoc291cmNlRGIp7JeQ7IScIOuNsOydtO2EsOulvCDsobDtmoztlZjsl6wgZml4dHVyZSBEQih0YXJnZXREYinsl5AgaW1wb3J07ZWp64uI64ukLlxuICAgKlxuICAgKiAxLiBEYXRhRXhwbG9yZXLroZwgc291cmNlRGLsl5DshJwg642w7J207YSwIOyhsO2ajCAo6rSA66CoIOuNsOydtO2EsCDtj6ztlagpXG4gICAqIDIuIEZpeHR1cmVSZWNvcmTroZwg67OA7ZmYXG4gICAqIDMuIHRhcmdldERi7JeQIOyCveyehVxuICAgKlxuICAgKiBAcGFyYW0gZW50aXR5TmFtZSAtIOyhsO2ajO2VoCBlbnRpdHkg7J2066aEXG4gICAqIEBwYXJhbSBvcHRpb25zIC0g7KGw7ZqMIOyYteyFmCAoc3RyYXRlZ3ksIGxpbWl0LCBpbmNsdWRlUmVsYXRpb25zIOuTsSlcbiAgICogQHJldHVybnMg7KCA7J6l65CcIGZpeHR1cmUg642w7J207YSwICjsi6TsoJwgREIgSUQg7Y+s7ZWoKVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiAvLyDtlITroZzrjZXshZggRELsl5DshJwgVXNlciAxMOuqhSArIOq0gOugqCBFbXBsb3llZSwgRGVwYXJ0bWVudCDqsIDsoLjsmKTquLBcbiAgICogYXdhaXQgZ2VuZXJhdG9yLmltcG9ydEZyb21Tb3VyY2UoXCJVc2VyXCIsIHtcbiAgICogICBzdHJhdGVneTogXCJzYW1wbGVcIixcbiAgICogICBsaW1pdDogMTAsXG4gICAqICAgaW5jbHVkZVJlbGF0aW9uczogdHJ1ZSxcbiAgICogICBtYXhEZXB0aDogMlxuICAgKiB9KTtcbiAgICovXG4gIGFzeW5jIGltcG9ydEZyb21Tb3VyY2UoXG4gICAgZW50aXR5TmFtZTogc3RyaW5nLFxuICAgIG9wdGlvbnM6IEV4cGxvcmVXaXRoUmVsYXRpb25zT3B0aW9ucyxcbiAgKTogUHJvbWlzZTxGaXh0dXJlSW1wb3J0UmVzdWx0W10+IHtcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIGNoYWxrLmJsdWUoXG4gICAgICAgIGBJbXBvcnRpbmcgJHtlbnRpdHlOYW1lfSBmcm9tIHNvdXJjZSBEQiB3aXRoIG9wdGlvbnM6ICR7SlNPTi5zdHJpbmdpZnkoeyBzdHJhdGVneTogb3B0aW9ucy5zdHJhdGVneSwgbGltaXQ6IG9wdGlvbnMubGltaXQsIGluY2x1ZGVSZWxhdGlvbnM6IG9wdGlvbnMuaW5jbHVkZVJlbGF0aW9ucywgbWF4RGVwdGg6IG9wdGlvbnMubWF4RGVwdGggfSl9YCxcbiAgICAgICksXG4gICAgKTtcblxuICAgIC8vIDEuIERhdGFFeHBsb3JlcuuhnCBzb3VyY2VEYuyXkOyEnCDrjbDsnbTthLAg7KGw7ZqMICjqtIDroKgg642w7J207YSwIO2PrO2VqClcbiAgICBjb25zdCBleHBsb3JlUmVzdWx0ID0gYXdhaXQgdGhpcy5kYXRhRXhwbG9yZXIuZXhwbG9yZVdpdGhSZWxhdGlvbnMoZW50aXR5TmFtZSwgb3B0aW9ucyk7XG5cbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIGNoYWxrLmN5YW4oXG4gICAgICAgIGBGb3VuZCAke2V4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzLmxlbmd0aH0gJHtlbnRpdHlOYW1lfSByZWNvcmRzIGFuZCAke2V4cGxvcmVSZXN1bHQucmVsYXRlZC5zaXplfSByZWxhdGVkIGVudGl0aWVzYCxcbiAgICAgICksXG4gICAgKTtcblxuICAgIC8vIDIuIEZpeHR1cmVSZWNvcmTroZwg67OA7ZmYXG4gICAgY29uc3QgZml4dHVyZVJlY29yZHM6IEZpeHR1cmVSZWNvcmRbXSA9IFtdO1xuXG4gICAgLy8g66mU7J24IGVudGl0eeydmCByZWNvcmRz66W8IEZpeHR1cmVSZWNvcmTroZwg67OA7ZmYXG4gICAgY29uc3QgbWFpbkVudGl0eSA9IHRoaXMuZW50aXR5TWFuYWdlci5nZXQoZW50aXR5TmFtZSk7XG4gICAgZm9yIChjb25zdCByZWNvcmQgb2YgZXhwbG9yZVJlc3VsdC5tYWluLnJlY29yZHMpIHtcbiAgICAgIGNvbnN0IHJlY29yZHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5jcmVhdGVGaXh0dXJlUmVjb3JkKFxuICAgICAgICBtYWluRW50aXR5LFxuICAgICAgICByZWNvcmQgYXMgeyBpZDogbnVtYmVyIHwgc3RyaW5nOyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuIHwgbnVsbCB9LFxuICAgICAgICB7IF9kYjogdGhpcy5zb3VyY2VEYiwgc2luZ2xlUmVjb3JkOiB0cnVlIH0sXG4gICAgICApO1xuICAgICAgZml4dHVyZVJlY29yZHMucHVzaCguLi5yZWNvcmRzKTtcbiAgICB9XG5cbiAgICAvLyDqtIDroKggZW50aXR57J2YIHJlY29yZHPrpbwgRml4dHVyZVJlY29yZOuhnCDrs4DtmZhcbiAgICBmb3IgKGNvbnN0IFtyZWxhdGVkRW50aXR5TmFtZSwgcmVsYXRlZFJlY29yZHNdIG9mIGV4cGxvcmVSZXN1bHQucmVsYXRlZC5lbnRyaWVzKCkpIHtcbiAgICAgIGNvbnN0IHJlbGF0ZWRFbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KHJlbGF0ZWRFbnRpdHlOYW1lKTtcbiAgICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIHJlbGF0ZWRSZWNvcmRzKSB7XG4gICAgICAgIGNvbnN0IHJlY29yZHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5jcmVhdGVGaXh0dXJlUmVjb3JkKFxuICAgICAgICAgIHJlbGF0ZWRFbnRpdHksXG4gICAgICAgICAgcmVjb3JkIGFzIHsgaWQ6IG51bWJlciB8IHN0cmluZzsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwgfSxcbiAgICAgICAgICB7IF9kYjogdGhpcy5zb3VyY2VEYiwgc2luZ2xlUmVjb3JkOiB0cnVlIH0sXG4gICAgICAgICk7XG4gICAgICAgIGZpeHR1cmVSZWNvcmRzLnB1c2goLi4ucmVjb3Jkcyk7XG4gICAgICB9XG5cbiAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmdyYXkoYCAgLSAke3JlbGF0ZWRFbnRpdHlOYW1lfTogJHtyZWxhdGVkUmVjb3Jkcy5sZW5ndGh9IHJlY29yZHNgKSk7XG4gICAgfVxuXG4gICAgLy8gMy4gdGFyZ2V0RGLsl5Ag7IK97J6FIChGaXh0dXJlTWFuYWdlcuqwgCDsnZjsobTshLEg7KCV66CsIOyymOumrClcbiAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgRml4dHVyZU1hbmFnZXIuaW5zZXJ0Rml4dHVyZXModGhpcy50YXJnZXREYk5hbWUsIGZpeHR1cmVSZWNvcmRzKTtcblxuICAgIGNvbnNvbGUubG9nKFxuICAgICAgY2hhbGsuZ3JlZW4oXG4gICAgICAgIGBTdWNjZXNzZnVsbHkgaW1wb3J0ZWQgJHtyZXN1bHRzLmxlbmd0aH0gcmVjb3JkcyB0byAke3RoaXMudGFyZ2V0RGJOYW1lfSAoJHtleHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcy5sZW5ndGh9ICR7ZW50aXR5TmFtZX0gKyAke3Jlc3VsdHMubGVuZ3RoIC0gZXhwbG9yZVJlc3VsdC5tYWluLnJlY29yZHMubGVuZ3RofSByZWxhdGVkKWAsXG4gICAgICApLFxuICAgICk7XG5cbiAgICByZXR1cm4gcmVzdWx0cztcbiAgfVxufVxuIl0sIm5hbWVzIjpbImNoYWxrIiwiaXNCZWxvbmdzVG9PbmVSZWxhdGlvblByb3AiLCJpc09uZVRvT25lUmVsYXRpb25Qcm9wIiwiaXNSZWxhdGlvblByb3AiLCJEYXRhRXhwbG9yZXIiLCJmYWtlck1hcHBpbmdzIiwiRml4dHVyZU1hbmFnZXIiLCJGaXh0dXJlR2VuZXJhdG9yIiwiZGF0YUV4cGxvcmVyIiwibG9jYWxlIiwibWFwcGluZ3MiLCJsbG1DYWNoZSIsIk1hcCIsIm9wdGlvbnMiLCJzb3VyY2VEYiIsIl90YXJnZXREYiIsInRhcmdldERiTmFtZSIsImVudGl0eU1hbmFnZXIiLCJ1c2VMTE0iLCJlbmFibGVMTE1DYWNoZSIsImxsbU1vZGVsIiwiZ2VuZXJhdGUiLCJlbnRpdHlOYW1lIiwib3ZlcnJpZGVzIiwiY29udGV4dCIsImNyZWF0ZUNvbnRleHQiLCJlbnRpdHkiLCJnZXQiLCJ0ZW1wSWQiLCJEYXRlIiwibm93IiwiZml4dHVyZSIsInByb3AiLCJwcm9wcyIsInZpcnR1YWwiLCJuYW1lIiwiY29uZSIsInJlbGF0aW9uVmFsdWUiLCJnZW5lcmF0ZVJlbGF0aW9uVmFsdWUiLCJoYXNKb2luQ29sdW1uIiwiZml4dHVyZUdlbmVyYXRvciIsImV4ZWN1dGVHZW5lcmF0b3IiLCJmaXh0dXJlSGludCIsImdlbmVyYXRlV2l0aExMTSIsImVycm9yIiwiY29uc29sZSIsIndhcm4iLCJpZCIsIkVycm9yIiwibWVzc2FnZSIsImZpeHR1cmVEZWZhdWx0IiwidW5kZWZpbmVkIiwiZ2VuZXJhdGVEZWZhdWx0VmFsdWUiLCJwYXNzd29yZCIsImJjcnlwdCIsImhhc2giLCJmaXh0dXJlcyIsInNldCIsImRhdGFTb3VyY2UiLCJjYWNoZUtleSIsIndpdGgiLCJKU09OIiwic3RyaW5naWZ5IiwicmVmZXJlbmNlQ2FjaGUiLCJoYXMiLCJleHBsb3JlUmVzdWx0IiwiZXhwbG9yZVdpdGhSZWxhdGlvbnMiLCJzdHJhdGVneSIsImxpbWl0IiwiY29uZmlnIiwiaW5jbHVkZVJlbGF0aW9ucyIsIm1heERlcHRoIiwibWFpbiIsInJlY29yZHMiLCJpbXBvcnRFeHBsb3JlUmVzdWx0IiwiY2FuZGlkYXRlcyIsImxlbmd0aCIsInNlbGVjdGVkIiwiTWF0aCIsImZsb29yIiwicmFuZG9tIiwiYXV0b0tleSIsImF1dG9FeHBsb3JlUmVzdWx0IiwiYXV0b0NhbmRpZGF0ZXMiLCJudWxsYWJsZSIsImFsbEZpeHR1cmVSZWNvcmRzIiwiZW50aXR5SWQiLCJyZWxhdGVkIiwiZW50cmllcyIsInJlY29yZHNUb0ltcG9ydCIsImxvZyIsImN5YW4iLCJyZWNvcmQiLCJyZWNvcmRLZXkiLCJpbXBvcnRlZFJlY29yZHMiLCJwdXNoIiwiYWRkIiwiZ3JheSIsInNsaWNlIiwiZml4dHVyZVJlY29yZHMiLCJjcmVhdGVGaXh0dXJlUmVjb3JkIiwiX2RiIiwic2luZ2xlUmVjb3JkIiwibWFpbkVudGl0eSIsIm1haW5SZWNvcmRzVG9JbXBvcnQiLCJpbnNlcnRGaXh0dXJlcyIsImdyZWVuIiwic2l6ZSIsImdlbmVyYXRvciIsInN0YXJ0c1dpdGgiLCJpc05hbWVGaWVsZCIsImZha2VyTW9kdWxlIiwiZmFrZXIiLCJmYWtlcktPIiwiZXhwciIsIm1hdGNoIiwicGF0aCIsImFyZ3NTdHIiLCJwYXJ0cyIsInNwbGl0IiwiZm4iLCJwYXJ0IiwiYXJncyIsInRyaW0iLCJwYXJzZWQiLCJwYXJzZSIsIkFycmF5IiwiaXNBcnJheSIsInRyaW1tZWQiLCJOdW1iZXIiLCJpc05hTiIsImVuZHNXaXRoIiwieWVsbG93IiwiZmFrZXJKQSIsImxvY2FsZUZha2VyIiwibG9jYWxlTWFwcGluZ3MiLCJlbiIsIm5vcm1hbGl6ZWROYW1lIiwidG9Mb3dlckNhc2UiLCJyZXBsYWNlIiwicGF0dGVybiIsIk9iamVjdCIsImZpZWxkX3BhdHRlcm5zIiwiaW5jbHVkZXMiLCJleGVjdXRlRmFrZXJFeHByZXNzaW9uIiwiZGVwYXJ0bWVudHMiLCJwcmVmaXhlcyIsInN1ZmZpeGVzIiwiZGVwdCIsImhlbHBlcnMiLCJhcnJheUVsZW1lbnQiLCJwcmVmaXgiLCJzdWZmaXgiLCJ0eXBlIiwiZ2VuZXJhdGVBcnJheVZhbHVlIiwiZW51bVZhbHVlcyIsImVudW0iLCJlbnVtTGFiZWxzIiwia2V5cyIsInR5cGVEZWZhdWx0IiwidHlwZV9kZWZhdWx0cyIsImxvcmVtIiwid29yZHMiLCJudW1iZXIiLCJpbnQiLCJtaW4iLCJtYXgiLCJiaWdJbnQiLCJmbG9hdCIsImRhdGF0eXBlIiwiYm9vbGVhbiIsImRhdGUiLCJwYXN0Iiwic3RyaW5nIiwidXVpZCIsIl9lbnRpdHkiLCJfbG9jYWxlRmFrZXIiLCJjb3VudCIsImZyb20iLCJ1cmwiLCJpbWFnZSIsInN5c3RlbSIsImZpbGVOYW1lIiwibWltZV90eXBlIiwiaW50ZXJuZXQiLCJ3b3JkIiwiZXhwcmVzc2lvbiIsImZha2VyTmFtZSIsInNlbGVjdGVkRmFrZXIiLCJmdW5jTWF0Y2giLCJhcGlLZXkiLCJnZXRBcGlLZXkiLCJjcmVhdGVBbnRocm9waWMiLCJnZW5lcmF0ZVRleHQiLCJ0ZXh0IiwibW9kZWwiLCJwcm9tcHQiLCJidWlsZExMTVByb21wdCIsInZhbHVlIiwicGFyc2VMTE1SZXNwb25zZSIsImhpbnQiLCJsYW5ndWFnZSIsImdldEV4cGVjdGVkRm9ybWF0Iiwiam9pbiIsImdldEV4YW1wbGVGb3JUeXBlIiwicHJvcFR5cGUiLCJjbGVhbmVkIiwiYmFzZVR5cGUiLCJtYXAiLCJpdGVtIiwiZ2V0RGVmYXVsdFZhbHVlRm9yVHlwZSIsInBhcnNlU2NhbGFyVmFsdWUiLCJTdHJpbmciLCJudW0iLCJwYXJzZUludCIsIkJpZ0ludCIsInBhcnNlRmxvYXQiLCJnZXRUaW1lIiwiU29uYW11IiwicmVxdWlyZSIsInNlY3JldCIsImFudGhyb3BpY19hcGlfa2V5IiwicHJvY2VzcyIsImVudiIsIkFOVEhST1BJQ19BUElfS0VZIiwiYmFzZUZvcm1hdCIsImdldFNjYWxhckZvcm1hdCIsImJhc2VFeGFtcGxlIiwiZ2V0U2NhbGFyRXhhbXBsZSIsImlzS29yZWFuIiwiZ2V0TExNQ2FjaGVTdGF0cyIsImVuYWJsZWQiLCJjbGVhckxMTUNhY2hlIiwiY2xlYXIiLCJTZXQiLCJnZW5lcmF0ZUJhdGNoIiwic3BlY3MiLCJnZW5lcmF0ZWRGaXh0dXJlcyIsInNwZWMiLCJpIiwiZGF0YSIsInJlc3VsdHMiLCJpbXBvcnRGcm9tU291cmNlIiwiYmx1ZSIsInJlbGF0ZWRFbnRpdHlOYW1lIiwicmVsYXRlZFJlY29yZHMiLCJyZWxhdGVkRW50aXR5Il0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPQSxXQUFXLFFBQVE7QUFLMUIsU0FBU0MsMEJBQTBCLEVBQUVDLHNCQUFzQixFQUFFQyxjQUFjLFFBQVEsb0JBQWlCO0FBQ3BHLFNBQ0VDLFlBQVksUUFHUCxxQkFBa0I7QUFDekIsU0FBNkJDLGFBQWEsUUFBUSxzQkFBbUI7QUFDckUsU0FBU0MsY0FBYyxRQUFRLHVCQUFvQjtBQXNCbkQsT0FBTyxNQUFNQzs7OztJQUNIQyxhQUEyQjtJQUMzQkMsT0FBZTtJQUNmQyxTQUF3QjtJQUN4QkMsV0FBaUMsSUFBSUMsTUFBTTtJQUMzQ0MsUUFBaUM7SUFFekMsWUFDRSxBQUFRQyxRQUFjLEVBQ3RCLGlFQUFpRTtJQUNqRSxnQ0FBZ0M7SUFDaENDLFNBQWUsRUFDZixBQUFRQyxZQUFzRCxFQUM5RCxBQUFRQyxhQUFtQyxFQUMzQ0osT0FBaUMsQ0FDakM7YUFQUUMsV0FBQUE7YUFJQUUsZUFBQUE7YUFDQUMsZ0JBQUFBO1FBR1IsSUFBSSxDQUFDVCxZQUFZLEdBQUcsSUFBSUosYUFBYVUsVUFBVUc7UUFDL0MsSUFBSSxDQUFDUixNQUFNLEdBQUdJLFNBQVNKLFVBQVU7UUFDakMsSUFBSSxDQUFDQyxRQUFRLEdBQUdMO1FBQ2hCLElBQUksQ0FBQ1EsT0FBTyxHQUFHO1lBQ2JKLFFBQVFJLFNBQVNKLFVBQVU7WUFDM0JTLFFBQVFMLFNBQVNLLFVBQVU7WUFDM0JDLGdCQUFnQk4sU0FBU00sbUJBQW1CO1lBQzVDQyxVQUFVUCxTQUFTTyxZQUFZO1FBQ2pDO0lBQ0Y7SUFFQTs7O0dBR0MsR0FDRCxNQUFNQyxTQUNKQyxVQUFrQixFQUNsQkMsWUFBcUMsQ0FBQyxDQUFDLEVBQ3ZDQyxVQUE0QixJ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