sonamu 0.8.26 → 0.9.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 (684) hide show
  1. package/bin/cli.js +60 -13
  2. package/dist/_virtual/rolldown_runtime.js +39 -0
  3. package/dist/ai/agents/agent.d.ts +3 -3
  4. package/dist/ai/agents/agent.d.ts.map +1 -1
  5. package/dist/ai/agents/agent.js +76 -73
  6. package/dist/ai/agents/index.js +3 -3
  7. package/dist/ai/agents/types.d.ts +3 -3
  8. package/dist/ai/agents/types.d.ts.map +1 -1
  9. package/dist/ai/agents/types.js +1 -3
  10. package/dist/ai/index.js +3 -2
  11. package/dist/ai/providers/rtzr/api.js +25 -25
  12. package/dist/ai/providers/rtzr/error.js +25 -26
  13. package/dist/ai/providers/rtzr/index.js +5 -5
  14. package/dist/ai/providers/rtzr/model.d.ts +1 -1
  15. package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
  16. package/dist/ai/providers/rtzr/model.js +117 -133
  17. package/dist/ai/providers/rtzr/options.d.ts.map +1 -1
  18. package/dist/ai/providers/rtzr/options.js +35 -41
  19. package/dist/ai/providers/rtzr/provider.d.ts +1 -1
  20. package/dist/ai/providers/rtzr/provider.d.ts.map +1 -1
  21. package/dist/ai/providers/rtzr/provider.js +53 -51
  22. package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
  23. package/dist/ai/providers/rtzr/utils.js +84 -84
  24. package/dist/api/base-frame.d.ts +2 -2
  25. package/dist/api/base-frame.d.ts.map +1 -1
  26. package/dist/api/base-frame.js +29 -19
  27. package/dist/api/caster.d.ts +1 -1
  28. package/dist/api/caster.d.ts.map +1 -1
  29. package/dist/api/caster.js +51 -61
  30. package/dist/api/code-converters.d.ts +4 -3
  31. package/dist/api/code-converters.d.ts.map +1 -1
  32. package/dist/api/code-converters.js +226 -249
  33. package/dist/api/config.d.ts +17 -17
  34. package/dist/api/config.d.ts.map +1 -1
  35. package/dist/api/config.js +37 -30
  36. package/dist/api/context.d.ts +10 -10
  37. package/dist/api/context.d.ts.map +1 -1
  38. package/dist/api/context.js +8 -2
  39. package/dist/api/decorators.d.ts +8 -8
  40. package/dist/api/decorators.d.ts.map +1 -1
  41. package/dist/api/decorators.js +245 -268
  42. package/dist/api/index.js +39 -7
  43. package/dist/api/secret.js +22 -15
  44. package/dist/api/sonamu.d.ts +15 -15
  45. package/dist/api/sonamu.d.ts.map +1 -1
  46. package/dist/api/sonamu.js +1012 -1131
  47. package/dist/api/validator.js +88 -79
  48. package/dist/auth/auth-generator.d.ts.map +1 -1
  49. package/dist/auth/auth-generator.js +203 -200
  50. package/dist/auth/better-auth-entities.d.ts +2 -2
  51. package/dist/auth/better-auth-entities.d.ts.map +1 -1
  52. package/dist/auth/better-auth-entities.js +369 -429
  53. package/dist/auth/index.js +21 -6
  54. package/dist/auth/knex-adapter.d.ts +2 -2
  55. package/dist/auth/knex-adapter.d.ts.map +1 -1
  56. package/dist/auth/knex-adapter.js +153 -157
  57. package/dist/auth/plugins/entity-definitions/admin.d.ts +1 -1
  58. package/dist/auth/plugins/entity-definitions/admin.d.ts.map +1 -1
  59. package/dist/auth/plugins/entity-definitions/admin.js +58 -56
  60. package/dist/auth/plugins/entity-definitions/anonymous.d.ts +1 -1
  61. package/dist/auth/plugins/entity-definitions/anonymous.d.ts.map +1 -1
  62. package/dist/auth/plugins/entity-definitions/anonymous.js +20 -20
  63. package/dist/auth/plugins/entity-definitions/api-key.d.ts +1 -1
  64. package/dist/auth/plugins/entity-definitions/api-key.d.ts.map +1 -1
  65. package/dist/auth/plugins/entity-definitions/api-key.js +185 -196
  66. package/dist/auth/plugins/entity-definitions/index.d.ts +1 -1
  67. package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
  68. package/dist/auth/plugins/entity-definitions/index.js +26 -29
  69. package/dist/auth/plugins/entity-definitions/jwt.d.ts +1 -1
  70. package/dist/auth/plugins/entity-definitions/jwt.d.ts.map +1 -1
  71. package/dist/auth/plugins/entity-definitions/jwt.js +62 -64
  72. package/dist/auth/plugins/entity-definitions/organization.d.ts +1 -1
  73. package/dist/auth/plugins/entity-definitions/organization.d.ts.map +1 -1
  74. package/dist/auth/plugins/entity-definitions/organization.js +362 -421
  75. package/dist/auth/plugins/entity-definitions/passkey.d.ts +1 -1
  76. package/dist/auth/plugins/entity-definitions/passkey.d.ts.map +1 -1
  77. package/dist/auth/plugins/entity-definitions/passkey.js +115 -126
  78. package/dist/auth/plugins/entity-definitions/phone-number.d.ts +1 -1
  79. package/dist/auth/plugins/entity-definitions/phone-number.d.ts.map +1 -1
  80. package/dist/auth/plugins/entity-definitions/phone-number.js +31 -40
  81. package/dist/auth/plugins/entity-definitions/sso.d.ts +1 -1
  82. package/dist/auth/plugins/entity-definitions/sso.d.ts.map +1 -1
  83. package/dist/auth/plugins/entity-definitions/sso.js +94 -107
  84. package/dist/auth/plugins/entity-definitions/two-factor.d.ts +1 -1
  85. package/dist/auth/plugins/entity-definitions/two-factor.d.ts.map +1 -1
  86. package/dist/auth/plugins/entity-definitions/two-factor.js +78 -92
  87. package/dist/auth/plugins/entity-definitions/types.d.ts +1 -1
  88. package/dist/auth/plugins/entity-definitions/types.d.ts.map +1 -1
  89. package/dist/auth/plugins/entity-definitions/types.js +1 -10
  90. package/dist/auth/plugins/entity-definitions/username.d.ts +1 -1
  91. package/dist/auth/plugins/entity-definitions/username.d.ts.map +1 -1
  92. package/dist/auth/plugins/entity-definitions/username.js +31 -40
  93. package/dist/auth/plugins/index.js +12 -3
  94. package/dist/auth/plugins/wrappers/admin.d.ts +2 -2
  95. package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
  96. package/dist/auth/plugins/wrappers/admin.js +28 -29
  97. package/dist/auth/plugins/wrappers/anonymous.d.ts +2 -1
  98. package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -1
  99. package/dist/auth/plugins/wrappers/anonymous.js +23 -22
  100. package/dist/auth/plugins/wrappers/api-key.d.ts +2 -1
  101. package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -1
  102. package/dist/auth/plugins/wrappers/api-key.js +39 -34
  103. package/dist/auth/plugins/wrappers/index.js +11 -11
  104. package/dist/auth/plugins/wrappers/jwt.d.ts +2 -1
  105. package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -1
  106. package/dist/auth/plugins/wrappers/jwt.js +31 -26
  107. package/dist/auth/plugins/wrappers/organization.d.ts +2 -1
  108. package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -1
  109. package/dist/auth/plugins/wrappers/organization.js +65 -62
  110. package/dist/auth/plugins/wrappers/passkey.d.ts +2 -1
  111. package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -1
  112. package/dist/auth/plugins/wrappers/passkey.js +33 -28
  113. package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
  114. package/dist/auth/plugins/wrappers/phone-number.js +26 -23
  115. package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -1
  116. package/dist/auth/plugins/wrappers/sso.js +37 -31
  117. package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
  118. package/dist/auth/plugins/wrappers/two-factor.js +31 -28
  119. package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
  120. package/dist/auth/plugins/wrappers/username.js +23 -23
  121. package/dist/bin/build-config.js +31 -31
  122. package/dist/bin/cli.js +1063 -1204
  123. package/dist/bin/fixture.d.ts.map +1 -1
  124. package/dist/bin/fixture.js +266 -259
  125. package/dist/bin/hmr-hook-register.d.ts.map +1 -1
  126. package/dist/bin/hmr-hook-register.js +19 -18
  127. package/dist/bin/test-command.d.ts.map +1 -1
  128. package/dist/bin/test-command.js +180 -177
  129. package/dist/bin/ts-loader-register.js +13 -6
  130. package/dist/bin/ts-loader-registration.d.ts.map +1 -1
  131. package/dist/bin/ts-loader-registration.js +28 -38
  132. package/dist/cache/cache-manager.d.ts +1 -1
  133. package/dist/cache/cache-manager.d.ts.map +1 -1
  134. package/dist/cache/cache-manager.js +20 -15
  135. package/dist/cache/decorator.d.ts +1 -1
  136. package/dist/cache/decorator.d.ts.map +1 -1
  137. package/dist/cache/decorator.js +84 -76
  138. package/dist/cache/drivers.js +21 -34
  139. package/dist/cache/index.js +10 -7
  140. package/dist/cache/types.d.ts +2 -2
  141. package/dist/cache/types.d.ts.map +1 -1
  142. package/dist/cache/types.js +1 -6
  143. package/dist/cache-control/cache-control.d.ts +2 -2
  144. package/dist/cache-control/cache-control.d.ts.map +1 -1
  145. package/dist/cache-control/cache-control.js +106 -122
  146. package/dist/cache-control/types.d.ts +2 -2
  147. package/dist/cache-control/types.d.ts.map +1 -1
  148. package/dist/cache-control/types.js +1 -19
  149. package/dist/compress/compress.d.ts +1 -1
  150. package/dist/compress/compress.d.ts.map +1 -1
  151. package/dist/compress/compress.js +58 -56
  152. package/dist/compress/index.js +7 -2
  153. package/dist/compress/types.js +1 -11
  154. package/dist/cone/cone-generator.d.ts +1 -1
  155. package/dist/cone/cone-generator.d.ts.map +1 -1
  156. package/dist/cone/cone-generator.js +216 -219
  157. package/dist/database/_batch_update.d.ts +1 -1
  158. package/dist/database/_batch_update.d.ts.map +1 -1
  159. package/dist/database/_batch_update.js +107 -102
  160. package/dist/database/base-model.d.ts +8 -9
  161. package/dist/database/base-model.d.ts.map +1 -1
  162. package/dist/database/base-model.js +371 -392
  163. package/dist/database/base-model.types.d.ts +5 -5
  164. package/dist/database/base-model.types.d.ts.map +1 -1
  165. package/dist/database/base-model.types.js +1 -20
  166. package/dist/database/db.d.ts +5 -2
  167. package/dist/database/db.d.ts.map +1 -1
  168. package/dist/database/db.js +185 -171
  169. package/dist/database/knex.d.ts +1 -1
  170. package/dist/database/knex.d.ts.map +1 -1
  171. package/dist/database/knex.js +48 -42
  172. package/dist/database/puri-subset.types.d.ts +6 -7
  173. package/dist/database/puri-subset.types.d.ts.map +1 -1
  174. package/dist/database/puri-subset.types.js +1 -16
  175. package/dist/database/puri-wrapper.d.ts +6 -6
  176. package/dist/database/puri-wrapper.d.ts.map +1 -1
  177. package/dist/database/puri-wrapper.js +99 -101
  178. package/dist/database/puri.d.ts +4 -5
  179. package/dist/database/puri.d.ts.map +1 -1
  180. package/dist/database/puri.js +1021 -1227
  181. package/dist/database/puri.types.d.ts +6 -6
  182. package/dist/database/puri.types.d.ts.map +1 -1
  183. package/dist/database/puri.types.js +15 -6
  184. package/dist/database/transaction-context.d.ts +2 -2
  185. package/dist/database/transaction-context.d.ts.map +1 -1
  186. package/dist/database/transaction-context.js +22 -13
  187. package/dist/database/upsert-builder.d.ts +3 -3
  188. package/dist/database/upsert-builder.d.ts.map +1 -1
  189. package/dist/database/upsert-builder.js +405 -465
  190. package/dist/dict/en.js +72 -74
  191. package/dist/dict/index.js +13 -13
  192. package/dist/dict/ko.js +72 -74
  193. package/dist/dict/rc-keys.js +150 -168
  194. package/dist/dict/sd.d.ts +3 -1
  195. package/dist/dict/sd.d.ts.map +1 -1
  196. package/dist/dict/sd.js +54 -40
  197. package/dist/dict/sonamu-dictionary.d.ts +1 -1
  198. package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
  199. package/dist/dict/sonamu-dictionary.js +887 -955
  200. package/dist/dict/types.js +1 -7
  201. package/dist/dict/utils.js +26 -24
  202. package/dist/entity/entity-manager.d.ts +9 -9
  203. package/dist/entity/entity-manager.d.ts.map +1 -1
  204. package/dist/entity/entity-manager.js +226 -223
  205. package/dist/entity/entity-template-cone.d.ts +1 -1
  206. package/dist/entity/entity-template-cone.d.ts.map +1 -1
  207. package/dist/entity/entity-template-cone.js +152 -151
  208. package/dist/entity/entity.d.ts.map +1 -1
  209. package/dist/entity/entity.js +952 -1089
  210. package/dist/exceptions/error-handler.d.ts +1 -1
  211. package/dist/exceptions/error-handler.d.ts.map +1 -1
  212. package/dist/exceptions/error-handler.js +32 -27
  213. package/dist/exceptions/so-exceptions.d.ts +1 -1
  214. package/dist/exceptions/so-exceptions.d.ts.map +1 -1
  215. package/dist/exceptions/so-exceptions.js +61 -68
  216. package/dist/filter/index.js +9 -3
  217. package/dist/filter/types.js +92 -88
  218. package/dist/filter/utils.d.ts +1 -1
  219. package/dist/filter/utils.d.ts.map +1 -1
  220. package/dist/filter/utils.js +147 -161
  221. package/dist/index.js +87 -40
  222. package/dist/logger/category.d.ts.map +1 -1
  223. package/dist/logger/category.js +30 -29
  224. package/dist/logger/configure.d.ts.map +1 -1
  225. package/dist/logger/configure.js +83 -107
  226. package/dist/migration/code-generation.d.ts +2 -2
  227. package/dist/migration/code-generation.d.ts.map +1 -1
  228. package/dist/migration/code-generation.js +1385 -1578
  229. package/dist/migration/migration-set.d.ts +1 -1
  230. package/dist/migration/migration-set.d.ts.map +1 -1
  231. package/dist/migration/migration-set.js +177 -227
  232. package/dist/migration/migrator.d.ts +4 -3
  233. package/dist/migration/migrator.d.ts.map +1 -1
  234. package/dist/migration/migrator.js +340 -345
  235. package/dist/migration/postgresql-schema-reader.d.ts +2 -2
  236. package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
  237. package/dist/migration/postgresql-schema-reader.js +506 -564
  238. package/dist/migration/slack-confirm.d.ts +2 -2
  239. package/dist/migration/slack-confirm.d.ts.map +1 -1
  240. package/dist/migration/slack-confirm.js +205 -193
  241. package/dist/migration/types.d.ts +2 -2
  242. package/dist/migration/types.d.ts.map +1 -1
  243. package/dist/migration/types.js +1 -3
  244. package/dist/naite/messaging-types.d.ts +1 -0
  245. package/dist/naite/messaging-types.d.ts.map +1 -1
  246. package/dist/naite/messaging-types.js +1 -7
  247. package/dist/naite/naite-reporter.d.ts +2 -2
  248. package/dist/naite/naite-reporter.d.ts.map +1 -1
  249. package/dist/naite/naite-reporter.js +127 -120
  250. package/dist/naite/naite.d.ts +3 -2
  251. package/dist/naite/naite.d.ts.map +1 -1
  252. package/dist/naite/naite.js +266 -300
  253. package/dist/ssr/index.d.ts +2 -2
  254. package/dist/ssr/index.d.ts.map +1 -1
  255. package/dist/ssr/index.js +13 -3
  256. package/dist/ssr/registry.d.ts +1 -1
  257. package/dist/ssr/registry.d.ts.map +1 -1
  258. package/dist/ssr/registry.js +45 -37
  259. package/dist/ssr/renderer.d.ts +4 -4
  260. package/dist/ssr/renderer.d.ts.map +1 -1
  261. package/dist/ssr/renderer.js +84 -91
  262. package/dist/ssr/types.d.ts +2 -2
  263. package/dist/ssr/types.d.ts.map +1 -1
  264. package/dist/ssr/types.js +1 -3
  265. package/dist/storage/base-file.js +54 -41
  266. package/dist/storage/buffered-file.d.ts +2 -2
  267. package/dist/storage/buffered-file.d.ts.map +1 -1
  268. package/dist/storage/buffered-file.js +51 -44
  269. package/dist/storage/drivers.d.ts +2 -2
  270. package/dist/storage/drivers.d.ts.map +1 -1
  271. package/dist/storage/drivers.js +12 -7
  272. package/dist/storage/index.js +14 -7
  273. package/dist/storage/s3-driver.d.ts +2 -2
  274. package/dist/storage/s3-driver.d.ts.map +1 -1
  275. package/dist/storage/s3-driver.js +52 -48
  276. package/dist/storage/storage-manager.d.ts +2 -2
  277. package/dist/storage/storage-manager.d.ts.map +1 -1
  278. package/dist/storage/storage-manager.js +33 -25
  279. package/dist/storage/types.d.ts +2 -2
  280. package/dist/storage/types.d.ts.map +1 -1
  281. package/dist/storage/types.js +1 -5
  282. package/dist/storage/uploaded-file.d.ts +1 -1
  283. package/dist/storage/uploaded-file.d.ts.map +1 -1
  284. package/dist/storage/uploaded-file.js +45 -35
  285. package/dist/stream/index.js +7 -2
  286. package/dist/stream/sse.d.ts +2 -2
  287. package/dist/stream/sse.d.ts.map +1 -1
  288. package/dist/stream/sse.js +72 -67
  289. package/dist/syncer/api-parser.d.ts +1 -1
  290. package/dist/syncer/api-parser.d.ts.map +1 -1
  291. package/dist/syncer/api-parser.js +224 -245
  292. package/dist/syncer/checksum.d.ts +1 -1
  293. package/dist/syncer/checksum.d.ts.map +1 -1
  294. package/dist/syncer/checksum.js +86 -72
  295. package/dist/syncer/code-generator.d.ts +2 -2
  296. package/dist/syncer/code-generator.d.ts.map +1 -1
  297. package/dist/syncer/code-generator.js +154 -160
  298. package/dist/syncer/entity-operations.d.ts +1 -1
  299. package/dist/syncer/entity-operations.d.ts.map +1 -1
  300. package/dist/syncer/entity-operations.js +63 -54
  301. package/dist/syncer/file-patterns.d.ts +1 -1
  302. package/dist/syncer/file-patterns.d.ts.map +1 -1
  303. package/dist/syncer/file-patterns.js +38 -38
  304. package/dist/syncer/index.js +19 -8
  305. package/dist/syncer/module-loader.d.ts +5 -5
  306. package/dist/syncer/module-loader.d.ts.map +1 -1
  307. package/dist/syncer/module-loader.js +83 -78
  308. package/dist/syncer/syncer-actions.d.ts +2 -2
  309. package/dist/syncer/syncer-actions.d.ts.map +1 -1
  310. package/dist/syncer/syncer-actions.js +76 -91
  311. package/dist/syncer/syncer.d.ts +7 -6
  312. package/dist/syncer/syncer.d.ts.map +1 -1
  313. package/dist/syncer/syncer.js +426 -492
  314. package/dist/tasks/decorator.d.ts +3 -3
  315. package/dist/tasks/decorator.d.ts.map +1 -1
  316. package/dist/tasks/decorator.js +32 -28
  317. package/dist/tasks/step-wrapper.d.ts +1 -1
  318. package/dist/tasks/step-wrapper.d.ts.map +1 -1
  319. package/dist/tasks/step-wrapper.js +42 -41
  320. package/dist/tasks/workflow-manager.d.ts +2 -2
  321. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  322. package/dist/tasks/workflow-manager.js +192 -221
  323. package/dist/template/entity-converter.d.ts +1 -1
  324. package/dist/template/entity-converter.d.ts.map +1 -1
  325. package/dist/template/entity-converter.js +103 -103
  326. package/dist/template/helpers.d.ts.map +1 -1
  327. package/dist/template/helpers.js +163 -163
  328. package/dist/template/implementations/entity.template.d.ts +1 -1
  329. package/dist/template/implementations/entity.template.d.ts.map +1 -1
  330. package/dist/template/implementations/entity.template.js +76 -85
  331. package/dist/template/implementations/entry-server.template.d.ts +1 -1
  332. package/dist/template/implementations/entry-server.template.d.ts.map +1 -1
  333. package/dist/template/implementations/entry-server.template.js +32 -27
  334. package/dist/template/implementations/generated.template.d.ts +1 -1
  335. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  336. package/dist/template/implementations/generated.template.js +254 -275
  337. package/dist/template/implementations/generated_http.template.d.ts +2 -2
  338. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  339. package/dist/template/implementations/generated_http.template.js +114 -133
  340. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  341. package/dist/template/implementations/generated_sso.template.js +249 -275
  342. package/dist/template/implementations/init_types.template.d.ts +1 -1
  343. package/dist/template/implementations/init_types.template.d.ts.map +1 -1
  344. package/dist/template/implementations/init_types.template.js +40 -34
  345. package/dist/template/implementations/model.template.d.ts +1 -1
  346. package/dist/template/implementations/model.template.d.ts.map +1 -1
  347. package/dist/template/implementations/model.template.js +56 -53
  348. package/dist/template/implementations/model_test.template.d.ts +1 -1
  349. package/dist/template/implementations/model_test.template.d.ts.map +1 -1
  350. package/dist/template/implementations/model_test.template.js +32 -24
  351. package/dist/template/implementations/queries.template.d.ts +1 -1
  352. package/dist/template/implementations/queries.template.d.ts.map +1 -1
  353. package/dist/template/implementations/queries.template.js +84 -89
  354. package/dist/template/implementations/sd.template.d.ts +1 -1
  355. package/dist/template/implementations/sd.template.d.ts.map +1 -1
  356. package/dist/template/implementations/sd.template.js +137 -144
  357. package/dist/template/implementations/services.template.d.ts +1 -1
  358. package/dist/template/implementations/services.template.d.ts.map +1 -1
  359. package/dist/template/implementations/services.template.js +164 -189
  360. package/dist/template/implementations/view_form.template.d.ts +1 -1
  361. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  362. package/dist/template/implementations/view_form.template.js +258 -285
  363. package/dist/template/implementations/view_id_all_select.template.d.ts +1 -1
  364. package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -1
  365. package/dist/template/implementations/view_id_all_select.template.js +31 -25
  366. package/dist/template/implementations/view_list.template.d.ts +1 -1
  367. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  368. package/dist/template/implementations/view_list.template.js +304 -355
  369. package/dist/template/implementations/view_search_input.template.d.ts +1 -1
  370. package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
  371. package/dist/template/implementations/view_search_input.template.js +31 -27
  372. package/dist/template/index.js +21 -7
  373. package/dist/template/template-manager.d.ts +1 -1
  374. package/dist/template/template-manager.d.ts.map +1 -1
  375. package/dist/template/template-manager.js +132 -123
  376. package/dist/template/template-types.js +8 -6
  377. package/dist/template/template.d.ts +2 -2
  378. package/dist/template/template.d.ts.map +1 -1
  379. package/dist/template/template.js +73 -68
  380. package/dist/template/zod-converter.d.ts.map +1 -1
  381. package/dist/template/zod-converter.js +603 -657
  382. package/dist/testing/_relation-graph.d.ts +1 -1
  383. package/dist/testing/_relation-graph.d.ts.map +1 -1
  384. package/dist/testing/_relation-graph.js +93 -88
  385. package/dist/testing/bootstrap.d.ts +22 -13
  386. package/dist/testing/bootstrap.d.ts.map +1 -1
  387. package/dist/testing/bootstrap.js +114 -114
  388. package/dist/testing/data-explorer.d.ts +3 -3
  389. package/dist/testing/data-explorer.d.ts.map +1 -1
  390. package/dist/testing/data-explorer.js +237 -265
  391. package/dist/testing/dev-test-routes.d.ts +2 -2
  392. package/dist/testing/dev-test-routes.d.ts.map +1 -1
  393. package/dist/testing/dev-test-routes.js +258 -249
  394. package/dist/testing/dev-vitest-manager.d.ts +1 -1
  395. package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
  396. package/dist/testing/dev-vitest-manager.js +514 -539
  397. package/dist/testing/faker-mappings.js +422 -420
  398. package/dist/testing/fixture-generator.d.ts +3 -3
  399. package/dist/testing/fixture-generator.d.ts.map +1 -1
  400. package/dist/testing/fixture-generator.js +1216 -1346
  401. package/dist/testing/fixture-loader.js +26 -25
  402. package/dist/testing/fixture-manager.d.ts +3 -3
  403. package/dist/testing/fixture-manager.d.ts.map +1 -1
  404. package/dist/testing/fixture-manager.js +706 -776
  405. package/dist/testing/global-setup.js +53 -49
  406. package/dist/testing/index.js +19 -11
  407. package/dist/testing/naite-vitest-reporter.js +18 -13
  408. package/dist/testing/parallel-db-manager.d.ts +1 -1
  409. package/dist/testing/parallel-db-manager.d.ts.map +1 -1
  410. package/dist/testing/parallel-db-manager.js +63 -78
  411. package/dist/testing/vitest-helpers.d.ts +1 -1
  412. package/dist/testing/vitest-helpers.d.ts.map +1 -1
  413. package/dist/testing/vitest-helpers.js +37 -33
  414. package/dist/types/types.d.ts +28 -28
  415. package/dist/types/types.d.ts.map +1 -1
  416. package/dist/types/types.js +764 -890
  417. package/dist/ui/ai-api.d.ts +1 -1
  418. package/dist/ui/ai-api.d.ts.map +1 -1
  419. package/dist/ui/ai-api.js +52 -42
  420. package/dist/ui/ai-client.d.ts +1 -2
  421. package/dist/ui/ai-client.d.ts.map +1 -1
  422. package/dist/ui/ai-client.js +353 -388
  423. package/dist/ui/api.d.ts +1 -1
  424. package/dist/ui/api.d.ts.map +1 -1
  425. package/dist/ui/api.js +903 -1145
  426. package/dist/ui/cdd-service.d.ts +1 -1
  427. package/dist/ui/cdd-service.d.ts.map +1 -1
  428. package/dist/ui/cdd-service.js +406 -407
  429. package/dist/ui/cdd-types.js +1 -3
  430. package/dist/ui-web/assets/index-C-Zz-wYg.css +1 -0
  431. package/dist/ui-web/assets/index-DejDON8K.js +238 -0
  432. package/dist/ui-web/index.html +3 -3
  433. package/dist/utils/async-utils.js +57 -45
  434. package/dist/utils/console-util.d.ts.map +1 -1
  435. package/dist/utils/console-util.js +104 -87
  436. package/dist/utils/controller.js +26 -19
  437. package/dist/utils/esm-utils.js +49 -38
  438. package/dist/utils/formatter.d.ts +1 -2
  439. package/dist/utils/formatter.d.ts.map +1 -1
  440. package/dist/utils/formatter.js +89 -115
  441. package/dist/utils/fs-utils.d.ts.map +1 -1
  442. package/dist/utils/fs-utils.js +68 -65
  443. package/dist/utils/lodash-able.js +11 -4
  444. package/dist/utils/model.d.ts +1 -1
  445. package/dist/utils/model.d.ts.map +1 -1
  446. package/dist/utils/model.js +21 -19
  447. package/dist/utils/object-utils.js +148 -186
  448. package/dist/utils/path-utils.js +67 -57
  449. package/dist/utils/process-utils.d.ts.map +1 -1
  450. package/dist/utils/process-utils.js +37 -31
  451. package/dist/utils/sql-parser.d.ts +1 -1
  452. package/dist/utils/sql-parser.d.ts.map +1 -1
  453. package/dist/utils/sql-parser.js +40 -40
  454. package/dist/utils/type-utils.js +44 -43
  455. package/dist/utils/utils.d.ts +2 -3
  456. package/dist/utils/utils.d.ts.map +1 -1
  457. package/dist/utils/utils.js +81 -93
  458. package/dist/utils/zod-error.d.ts +1 -1
  459. package/dist/utils/zod-error.d.ts.map +1 -1
  460. package/dist/utils/zod-error.js +24 -17
  461. package/dist/vector/chunking.d.ts +1 -1
  462. package/dist/vector/chunking.d.ts.map +1 -1
  463. package/dist/vector/chunking.js +100 -94
  464. package/dist/vector/config.d.ts +1 -1
  465. package/dist/vector/config.d.ts.map +1 -1
  466. package/dist/vector/config.js +76 -78
  467. package/dist/vector/embedding.d.ts +1 -1
  468. package/dist/vector/embedding.d.ts.map +1 -1
  469. package/dist/vector/embedding.js +128 -125
  470. package/dist/vector/index.js +5 -5
  471. package/dist/vector/types.js +1 -5
  472. package/package.json +31 -36
  473. package/src/ai/agents/agent.ts +12 -5
  474. package/src/ai/agents/types.ts +5 -5
  475. package/src/ai/providers/rtzr/model.ts +8 -10
  476. package/src/ai/providers/rtzr/options.ts +2 -1
  477. package/src/ai/providers/rtzr/provider.ts +5 -3
  478. package/src/ai/providers/rtzr/utils.ts +2 -7
  479. package/src/api/__tests__/config.test.ts +15 -8
  480. package/src/api/base-frame.ts +5 -3
  481. package/src/api/caster.ts +7 -6
  482. package/src/api/code-converters.ts +23 -26
  483. package/src/api/config.ts +23 -17
  484. package/src/api/context.ts +18 -11
  485. package/src/api/decorators.ts +17 -18
  486. package/src/api/sonamu.ts +44 -49
  487. package/src/auth/auth-generator.ts +4 -6
  488. package/src/auth/better-auth-entities.ts +3 -2
  489. package/src/auth/knex-adapter.ts +6 -5
  490. package/src/auth/plugins/entity-definitions/admin.ts +1 -1
  491. package/src/auth/plugins/entity-definitions/anonymous.ts +1 -1
  492. package/src/auth/plugins/entity-definitions/api-key.ts +1 -1
  493. package/src/auth/plugins/entity-definitions/index.ts +1 -1
  494. package/src/auth/plugins/entity-definitions/jwt.ts +1 -1
  495. package/src/auth/plugins/entity-definitions/organization.ts +1 -1
  496. package/src/auth/plugins/entity-definitions/passkey.ts +1 -1
  497. package/src/auth/plugins/entity-definitions/phone-number.ts +1 -1
  498. package/src/auth/plugins/entity-definitions/sso.ts +1 -1
  499. package/src/auth/plugins/entity-definitions/two-factor.ts +1 -1
  500. package/src/auth/plugins/entity-definitions/types.ts +1 -1
  501. package/src/auth/plugins/entity-definitions/username.ts +1 -1
  502. package/src/auth/plugins/wrappers/admin.ts +3 -1
  503. package/src/auth/plugins/wrappers/anonymous.ts +3 -1
  504. package/src/auth/plugins/wrappers/api-key.ts +3 -1
  505. package/src/auth/plugins/wrappers/jwt.ts +3 -1
  506. package/src/auth/plugins/wrappers/organization.ts +3 -1
  507. package/src/auth/plugins/wrappers/passkey.ts +3 -1
  508. package/src/auth/plugins/wrappers/phone-number.ts +3 -1
  509. package/src/auth/plugins/wrappers/sso.ts +2 -1
  510. package/src/auth/plugins/wrappers/two-factor.ts +3 -1
  511. package/src/auth/plugins/wrappers/username.ts +3 -1
  512. package/src/bin/__tests__/ts-loader-register.test.ts +7 -12
  513. package/src/bin/build-config.ts +3 -3
  514. package/src/bin/cli.ts +27 -25
  515. package/src/bin/fixture.ts +4 -2
  516. package/src/bin/hmr-hook-register.ts +1 -0
  517. package/src/bin/test-command.ts +4 -2
  518. package/src/bin/ts-loader-registration.ts +6 -22
  519. package/src/cache/cache-manager.ts +2 -1
  520. package/src/cache/decorator.ts +2 -2
  521. package/src/cache/types.ts +3 -3
  522. package/src/cache-control/cache-control.ts +3 -2
  523. package/src/cache-control/types.ts +2 -2
  524. package/src/compress/compress.ts +1 -1
  525. package/src/cone/cone-generator.ts +5 -3
  526. package/src/database/_batch_update.ts +1 -1
  527. package/src/database/base-model.ts +20 -14
  528. package/src/database/base-model.types.ts +12 -11
  529. package/src/database/db.ts +56 -21
  530. package/src/database/knex.ts +2 -2
  531. package/src/database/puri-subset.test-d.ts +33 -32
  532. package/src/database/puri-subset.types.ts +6 -7
  533. package/src/database/puri-wrapper.ts +29 -26
  534. package/src/database/puri.ts +36 -34
  535. package/src/database/puri.types.test-d.ts +6 -5
  536. package/src/database/puri.types.ts +9 -12
  537. package/src/database/transaction-context.ts +2 -2
  538. package/src/database/upsert-builder.ts +17 -10
  539. package/src/dict/sd.ts +17 -4
  540. package/src/dict/sonamu-dictionary.ts +23 -17
  541. package/src/entity/entity-manager.ts +9 -7
  542. package/src/entity/entity-template-cone.ts +10 -3
  543. package/src/entity/entity.ts +20 -16
  544. package/src/exceptions/error-handler.ts +2 -1
  545. package/src/exceptions/so-exceptions.ts +1 -1
  546. package/src/filter/utils.ts +3 -2
  547. package/src/logger/category.ts +1 -0
  548. package/src/logger/configure.ts +5 -5
  549. package/src/migration/__tests__/code-generation.search-text.test.ts +2 -3
  550. package/src/migration/code-generation.ts +26 -25
  551. package/src/migration/migration-set.ts +16 -18
  552. package/src/migration/migrator.ts +38 -33
  553. package/src/migration/postgresql-schema-reader.ts +12 -12
  554. package/src/migration/slack-confirm.ts +5 -4
  555. package/src/migration/types.ts +2 -2
  556. package/src/naite/messaging-types.ts +1 -1
  557. package/src/naite/naite-reporter.ts +5 -3
  558. package/src/naite/naite.ts +12 -7
  559. package/src/shared/app.shared.ts.txt +2 -2
  560. package/src/shared/web.shared.ts.txt +2 -2
  561. package/src/skills/AGENTS.md +19 -18
  562. package/src/skills/commands/sonamu-skills.md +9 -9
  563. package/src/skills/sonamu/SKILL.md +111 -104
  564. package/src/skills/sonamu/ai-agents.md +27 -26
  565. package/src/skills/sonamu/api.md +81 -69
  566. package/src/skills/sonamu/auth-migration.md +13 -27
  567. package/src/skills/sonamu/auth-plugins.md +41 -31
  568. package/src/skills/sonamu/auth.md +30 -24
  569. package/src/skills/sonamu/cdd.md +26 -17
  570. package/src/skills/sonamu/cone.md +50 -50
  571. package/src/skills/sonamu/config.md +74 -51
  572. package/src/skills/sonamu/create-sonamu.md +31 -19
  573. package/src/skills/sonamu/database.md +43 -26
  574. package/src/skills/sonamu/entity-basic.md +61 -61
  575. package/src/skills/sonamu/entity-relations.md +84 -80
  576. package/src/skills/sonamu/entity-validation-checklist.md +19 -15
  577. package/src/skills/sonamu/fixture-cli.md +52 -30
  578. package/src/skills/sonamu/framework-change.md +9 -7
  579. package/src/skills/sonamu/frontend.md +64 -82
  580. package/src/skills/sonamu/i18n.md +45 -37
  581. package/src/skills/sonamu/migration.md +54 -31
  582. package/src/skills/sonamu/model.md +98 -66
  583. package/src/skills/sonamu/naite.md +34 -32
  584. package/src/skills/sonamu/project-init.md +28 -8
  585. package/src/skills/sonamu/puri.md +82 -91
  586. package/src/skills/sonamu/scaffolding.md +44 -32
  587. package/src/skills/sonamu/skill-contribution.md +50 -45
  588. package/src/skills/sonamu/subset.md +13 -13
  589. package/src/skills/sonamu/tasks.md +73 -58
  590. package/src/skills/sonamu/testing-devrunner.md +56 -36
  591. package/src/skills/sonamu/testing.md +23 -58
  592. package/src/skills/sonamu/upsert.md +32 -31
  593. package/src/skills/sonamu/vector.md +37 -36
  594. package/src/ssr/index.ts +2 -12
  595. package/src/ssr/registry.ts +1 -1
  596. package/src/ssr/renderer.ts +7 -5
  597. package/src/ssr/types.ts +2 -2
  598. package/src/storage/buffered-file.ts +4 -2
  599. package/src/storage/drivers.ts +3 -2
  600. package/src/storage/s3-driver.ts +7 -4
  601. package/src/storage/storage-manager.ts +3 -2
  602. package/src/storage/types.ts +3 -2
  603. package/src/storage/uploaded-file.ts +1 -1
  604. package/src/stream/sse.ts +2 -2
  605. package/src/syncer/api-parser.ts +8 -5
  606. package/src/syncer/checksum.ts +9 -5
  607. package/src/syncer/code-generator.ts +16 -8
  608. package/src/syncer/entity-operations.ts +5 -3
  609. package/src/syncer/file-patterns.ts +2 -1
  610. package/src/syncer/module-loader.ts +9 -6
  611. package/src/syncer/syncer-actions.ts +5 -3
  612. package/src/syncer/syncer.ts +18 -24
  613. package/src/tasks/decorator.ts +10 -8
  614. package/src/tasks/step-wrapper.ts +1 -1
  615. package/src/tasks/workflow-manager.ts +18 -15
  616. package/src/template/__tests__/generated.template.search-text.test.ts +1 -0
  617. package/src/template/entity-converter.ts +4 -2
  618. package/src/template/generated.template.test-d.ts +2 -1
  619. package/src/template/helpers.ts +5 -2
  620. package/src/template/implementations/entity.template.ts +9 -8
  621. package/src/template/implementations/entry-server.template.ts +1 -1
  622. package/src/template/implementations/generated.template.ts +21 -29
  623. package/src/template/implementations/generated_http.template.ts +9 -6
  624. package/src/template/implementations/generated_sso.template.ts +6 -4
  625. package/src/template/implementations/init_types.template.ts +3 -2
  626. package/src/template/implementations/model.template.ts +4 -2
  627. package/src/template/implementations/model_test.template.ts +3 -2
  628. package/src/template/implementations/queries.template.ts +6 -14
  629. package/src/template/implementations/sd.template.ts +4 -2
  630. package/src/template/implementations/services.template.ts +7 -11
  631. package/src/template/implementations/view_form.template.ts +5 -3
  632. package/src/template/implementations/view_id_all_select.template.ts +3 -2
  633. package/src/template/implementations/view_list.template.ts +7 -5
  634. package/src/template/implementations/view_search_input.template.ts +3 -2
  635. package/src/template/template-manager.ts +4 -3
  636. package/src/template/template.ts +4 -3
  637. package/src/template/zod-converter.ts +10 -7
  638. package/src/testing/__tests__/dev-test-routes.test.ts +3 -2
  639. package/src/testing/__tests__/dev-vitest-manager.test.ts +1 -0
  640. package/src/testing/_relation-graph.ts +2 -2
  641. package/src/testing/bootstrap.ts +55 -27
  642. package/src/testing/data-explorer.ts +5 -4
  643. package/src/testing/dev-test-routes.ts +8 -5
  644. package/src/testing/dev-vitest-manager.ts +13 -12
  645. package/src/testing/fixture-generator.ts +11 -17
  646. package/src/testing/fixture-manager.ts +21 -17
  647. package/src/testing/parallel-db-manager.ts +2 -1
  648. package/src/testing/vitest-helpers.ts +2 -1
  649. package/src/types/__tests__/entity-json-schema-search-text.test.ts +1 -0
  650. package/src/types/types.ts +8 -8
  651. package/src/typings/knex.d.ts +4 -4
  652. package/src/ui/ai-api.ts +5 -3
  653. package/src/ui/ai-client.ts +6 -5
  654. package/src/ui/api.ts +25 -23
  655. package/src/ui/cdd-service.ts +12 -11
  656. package/src/utils/console-util.ts +3 -1
  657. package/src/utils/formatter.ts +94 -102
  658. package/src/utils/fs-utils.ts +2 -1
  659. package/src/utils/model.ts +2 -2
  660. package/src/utils/object-utils.ts +3 -3
  661. package/src/utils/process-utils.ts +2 -1
  662. package/src/utils/sql-parser.ts +10 -1
  663. package/src/utils/type-utils.ts +3 -3
  664. package/src/utils/utils.ts +9 -7
  665. package/src/utils/zod-error.ts +1 -1
  666. package/src/vector/chunking.ts +1 -1
  667. package/src/vector/config.ts +1 -1
  668. package/src/vector/embedding.ts +11 -9
  669. package/tsdown.api.config.ts +50 -0
  670. package/.swcrc.project-default +0 -18
  671. package/dist/api/__tests__/config.test.js +0 -189
  672. package/dist/bin/__tests__/test-command.test.js +0 -112
  673. package/dist/bin/__tests__/ts-loader-register.test.js +0 -45
  674. package/dist/database/puri-subset.test-d.js +0 -89
  675. package/dist/database/puri.types.test-d.js +0 -129
  676. package/dist/migration/__tests__/code-generation.search-text.test.js +0 -435
  677. package/dist/template/__tests__/generated.template.search-text.test.js +0 -99
  678. package/dist/template/generated.template.test-d.js +0 -24
  679. package/dist/testing/__tests__/dev-test-routes.test.js +0 -144
  680. package/dist/testing/__tests__/dev-vitest-manager.test.js +0 -152
  681. package/dist/types/__tests__/entity-json-schema-search-text.test.js +0 -256
  682. package/dist/typings/knex.d.js +0 -3
  683. package/dist/ui-web/assets/index-CKo0Z2Iu.css +0 -1
  684. package/dist/ui-web/assets/index-DK-2aacv.js +0 -257
@@ -1,714 +1,660 @@
1
+ import { __esmMin, __toCommonJS } from "../_virtual/rolldown_runtime.js";
2
+ import { init_controller, isTest } from "../utils/controller.js";
3
+ import { init_types, isBelongsToOneRelationProp, isOneToOneRelationProp, isRelationProp } from "../types/types.js";
4
+ import { api_exports, init_api } from "../api/index.js";
5
+ import { FixtureManager, init_fixture_manager } from "./fixture-manager.js";
6
+ import { DataExplorer, init_data_explorer } from "./data-explorer.js";
7
+ import { fakerMappings, init_faker_mappings } from "./faker-mappings.js";
1
8
  import chalk from "chalk";
2
- import { isBelongsToOneRelationProp, isOneToOneRelationProp, isRelationProp } from "../types/types.js";
3
- import { isTest } from "../utils/controller.js";
4
- import { DataExplorer } from "./data-explorer.js";
5
- import { fakerMappings } from "./faker-mappings.js";
6
- import { FixtureManager } from "./fixture-manager.js";
7
- export class FixtureGenerator {
8
- sourceDb;
9
- targetDbName;
10
- entityManager;
11
- dataExplorer;
12
- locale;
13
- mappings;
14
- llmCache = new Map();
15
- entityCache = new Map();
16
- options;
17
- constructor(sourceDb, // FixtureManager.insertFixtures가 dbName 문자열을 받기 때문에 직접 사용하지 않습니다
18
- // 미래 확장성을 위해 API 시그니처에는 포함시켰습니다
19
- _targetDb, targetDbName, entityManager, options){
20
- this.sourceDb = sourceDb;
21
- this.targetDbName = targetDbName;
22
- this.entityManager = entityManager;
23
- this.dataExplorer = new DataExplorer(sourceDb, entityManager);
24
- this.locale = options?.locale || "ko";
25
- this.mappings = fakerMappings;
26
- this.options = {
27
- locale: options?.locale || "ko",
28
- useLLM: options?.useLLM || false,
29
- enableLLMCache: options?.enableLLMCache !== false,
30
- llmModel: options?.llmModel || "claude-sonnet-4-6"
31
- };
32
- }
33
- /**
34
- * Fixture 생성 (단일)
35
- * @returns 생성된 fixture 데이터 (메모리 상)
36
- */ async generate(entityName, overrides = {}, context = this.createContext()) {
37
- // Entity 캐싱: 테스트에서 entity cone 수정이 반영되도록 보장
38
- let entity = this.entityCache.get(entityName);
39
- if (!entity) {
40
- entity = this.entityManager.get(entityName);
41
- this.entityCache.set(entityName, entity);
42
- }
43
- const tempId = `${entityName}#temp#${Date.now()}`; // 임시 ID
44
- // LLM row 단위 생성을 위한 고유 키 (같은 row의 필드들이 동일한 rowKey를 공유)
45
- const rowKey = this.options.useLLM ? `${entityName}#row#${Date.now()}` : undefined;
46
- // prop 생성
47
- const fixture = {};
48
- for (const prop of entity.props){
49
- // Virtual prop 스킵
50
- if ("virtual" in prop && prop.virtual) {
51
- continue;
52
- }
53
- // id prop 처리
54
- if (prop.name === "id") {
55
- if ("cone" in prop && prop.cone?.fixtureStrategy === "sequence") {
56
- continue;
57
- }
58
- if (prop.type === "string") {
59
- // DB DEFAULT 없는 string PK: alphanumeric 32자 생성 (better-auth 스타일)
60
- const { faker: _faker } = await import("@faker-js/faker");
61
- fixture[prop.name] = _faker.string.alphanumeric(32);
62
- continue;
63
- }
64
- if (prop.type === "uuid") {
65
- const { faker: _faker } = await import("@faker-js/faker");
66
- fixture[prop.name] = _faker.string.uuid();
67
- continue;
68
- }
69
- continue;
70
- }
71
- // override가 있으면 사용
72
- if (prop.name in overrides) {
73
- fixture[prop.name] = overrides[prop.name];
74
- continue;
75
- }
76
- // cone에서 생성 전략 확인
77
- const cone = prop.cone;
78
- // 1. Relation prop 처리
79
- if (isRelationProp(prop)) {
80
- // BelongsToOne / OneToOne(hasJoinColumn)은 FK 컬럼명({prop.name}_id)으로도 override를 받는다
81
- const fkColName = `${prop.name}_id`;
82
- if (fkColName in overrides && (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop) && prop.hasJoinColumn)) {
83
- fixture[fkColName] = overrides[fkColName];
84
- continue;
85
- }
86
- const relationValue = await this.generateRelationValue(entity, prop, context);
87
- // BelongsToOne, OneToOne(hasJoinColumn)의 경우 foreign key 컬럼명으로 저장
88
- if (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop) && prop.hasJoinColumn) {
89
- fixture[`${prop.name}_id`] = relationValue;
90
- } else {
91
- fixture[prop.name] = relationValue;
92
- }
93
- continue;
94
- }
95
- // 2. cone.note + LLM 사용 (useLLM이면 fixtureGenerator보다 우선)
96
- if (cone?.note && this.options.useLLM) {
97
- try {
98
- const llmValue = await this.generateWithLLM(cone.note, prop, entity, rowKey);
99
- // string 타입이고 length 제약이 있으면 초과 시 truncation
100
- if (typeof llmValue === "string" && "length" in prop && typeof prop.length === "number" && llmValue.length > prop.length) {
101
- fixture[prop.name] = llmValue.slice(0, prop.length);
102
- } else {
103
- fixture[prop.name] = llmValue;
104
- }
105
- continue;
106
- } catch (error) {
107
- console.warn(`[FixtureGenerator] LLM generation failed for ${entity.id}.${prop.name}, falling back to fixtureGenerator or default`, error instanceof Error ? error.message : error);
108
- // fallback: fixtureGenerator fixtureDefault 기본값으로 계속
109
- }
110
- }
111
- // 3. fixtureGenerator 사용
112
- if (cone?.fixtureGenerator) {
113
- fixture[prop.name] = await this.executeGenerator(cone.fixtureGenerator, prop, entity);
114
- continue;
115
- }
116
- // 4. fixtureDefault 사용
117
- if (cone?.fixtureDefault !== undefined) {
118
- fixture[prop.name] = cone.fixtureDefault;
119
- continue;
120
- }
121
- // 5. 타입별 기본 생성
122
- fixture[prop.name] = await this.generateDefaultValue(prop, entity);
123
- }
124
- // 6. email 필드가 있고 name 필드가 있으면, email의 로컬 파트를 name 기반으로 보정
125
- if ("email" in fixture && typeof fixture.email === "string" && !("email" in overrides)) {
126
- const nameValue = fixture.name || fixture.username || fixture.full_name || fixture.name_en;
127
- if (nameValue && typeof nameValue === "string") {
128
- const domain = fixture.email.split("@")[1] || "example.com";
129
- const romanized = await this.romanizeName(nameValue);
130
- fixture.email = `${romanized}@${domain}`;
131
- }
132
- }
133
- // 7. password 필드 암호화
134
- if ("password" in fixture && fixture.password && typeof fixture.password === "string") {
135
- const bcrypt = await import("bcrypt");
136
- fixture.password = await bcrypt.hash(fixture.password, 10);
137
- }
138
- context.fixtures.set(tempId, fixture);
139
- return fixture;
140
- }
141
- /**
142
- * Relation 값 생성 + 자동 Import
143
- */ async generateRelationValue(entity, prop, context) {
144
- if (!isRelationProp(prop)) {
145
- throw new Error(`FixtureGenerator: ${entity.id}.${prop.name} is not a relation prop`);
146
- }
147
- // BelongsToOne, OneToOne(hasJoinColumn)만 처리
148
- if (!isBelongsToOneRelationProp(prop) && !(isOneToOneRelationProp(prop) && prop.hasJoinColumn)) {
149
- return null;
150
- }
151
- const cone = prop.cone;
152
- const dataSource = cone?.dataSource;
153
- // DataExplorer로 참조 데이터 조회 (sourceDb)
154
- // 관계 체인을 따라가기 위해 exploreWithRelations 사용
155
- if (dataSource) {
156
- const cacheKey = `${prop.with}:${JSON.stringify(dataSource)}`;
157
- if (!context.referenceCache.has(cacheKey)) {
158
- const exploreResult = await this.dataExplorer.exploreWithRelations(prop.with, {
159
- strategy: dataSource.strategy,
160
- limit: dataSource.config?.limit || 10,
161
- includeRelations: true,
162
- maxDepth: 3,
163
- ...dataSource.config
164
- });
165
- context.referenceCache.set(cacheKey, exploreResult.main.records);
166
- // 조회한 데이터와 관계된 모든 엔티티를 targetDb에 import
167
- await this.importExploreResult(exploreResult, context);
168
- }
169
- const candidates = context.referenceCache.get(cacheKey);
170
- if (candidates && candidates.length > 0) {
171
- // 랜덤하게 하나 선택
172
- const selected = candidates[Math.floor(Math.random() * candidates.length)];
173
- return selected.id;
174
- }
175
- }
176
- // dataSource가 없을 때 자동으로 fixture DB에서 조회 시도
177
- // 관계 체인을 따라가기 위해 exploreWithRelations 사용
178
- const autoKey = `${prop.with}:auto`;
179
- if (!context.referenceCache.has(autoKey)) {
180
- // fixture DB(sourceDb)에서 자동 조회 (관계 포함)
181
- const autoExploreResult = await this.dataExplorer.exploreWithRelations(prop.with, {
182
- strategy: "random",
183
- limit: 10,
184
- includeRelations: true,
185
- maxDepth: 3
186
- });
187
- context.referenceCache.set(autoKey, autoExploreResult.main.records);
188
- // 조회한 데이터와 관계된 모든 엔티티를 targetDb에 import
189
- if (autoExploreResult.main.records.length > 0) {
190
- await this.importExploreResult(autoExploreResult, context);
191
- }
192
- }
193
- const autoCandidates = context.referenceCache.get(autoKey);
194
- if (autoCandidates && autoCandidates.length > 0) {
195
- // 랜덤하게 하나 선택
196
- const selected = autoCandidates[Math.floor(Math.random() * autoCandidates.length)];
197
- return selected.id;
198
- }
199
- // 참조 데이터가 없으면 null 반환 (nullable인 경우)
200
- if (prop.nullable) {
201
- return null;
202
- }
203
- // nullable이 아니고 데이터도 없으면 에러
204
- throw new Error(`FixtureGenerator: ${entity.id}.${prop.name}에 필요한 ${prop.with} 데이터가 없습니다. ` + `먼저 ${prop.with}를 생성하거나 cone.dataSource를 설정하세요.`);
205
- }
206
- /**
207
- * ExploreWithRelations 결과를 targetDb에 import
208
- *
209
- * 관계 체인을 따라간 결과(main + related) 모두 import합니다.
210
- * 의존성 순서는 FixtureManager.insertFixtures가 자동으로 처리합니다.
211
- */ async importExploreResult(exploreResult, context) {
212
- const allFixtureRecords = [];
213
- // 1. Related entities import (Company, Department 등)
214
- for (const [entityId, records] of exploreResult.related.entries()){
215
- const entity = this.entityManager.get(entityId);
216
- const recordsToImport = [];
217
- !isTest() && console.log(chalk.cyan(`Importing related entity: ${entityId} (${records.length} records)`));
218
- for (const record of records){
219
- const recordKey = `${entityId}#${record.id}`;
220
- if (!context.importedRecords.has(recordKey)) {
221
- recordsToImport.push(record);
222
- context.importedRecords.add(recordKey);
223
- }
224
- }
225
- if (recordsToImport.length > 0) {
226
- for (const record of recordsToImport){
227
- !isTest() && console.log(chalk.gray(` - Processing ${entityId} record:`, JSON.stringify(record).slice(0, 100)));
228
- const fixtureRecords = await FixtureManager.createFixtureRecord(entity, record, {
229
- _db: this.sourceDb,
230
- singleRecord: true
231
- });
232
- allFixtureRecords.push(...fixtureRecords);
233
- }
234
- }
235
- }
236
- // 2. Main entity import (Employee 등)
237
- const mainEntity = this.entityManager.get(exploreResult.main.entityId);
238
- const mainRecordsToImport = [];
239
- !isTest() && console.log(chalk.cyan(`Importing main entity: ${exploreResult.main.entityId} (${exploreResult.main.records.length} records)`));
240
- for (const record of exploreResult.main.records){
241
- const recordKey = `${exploreResult.main.entityId}#${record.id}`;
242
- if (!context.importedRecords.has(recordKey)) {
243
- mainRecordsToImport.push(record);
244
- context.importedRecords.add(recordKey);
245
- }
246
- }
247
- if (mainRecordsToImport.length > 0) {
248
- for (const record of mainRecordsToImport){
249
- !isTest() && console.log(chalk.gray(` - Processing ${exploreResult.main.entityId} record:`, JSON.stringify(record).slice(0, 100)));
250
- const fixtureRecords = await FixtureManager.createFixtureRecord(mainEntity, record, {
251
- _db: this.sourceDb,
252
- singleRecord: true
253
- });
254
- allFixtureRecords.push(...fixtureRecords);
255
- }
256
- }
257
- // 3. 모든 fixture를 한 번에 삽입 (의존성 순서 자동 처리)
258
- if (allFixtureRecords.length > 0) {
259
- await FixtureManager.insertFixtures(this.targetDbName, allFixtureRecords);
260
- !isTest() && console.log(chalk.green(`Auto-imported ${exploreResult.main.entityId} with relations: ` + `${exploreResult.main.records.length} main + ${exploreResult.related.size} related entities`));
261
- }
262
- }
263
- /**
264
- * fixtureGenerator 실행 (Faker.js만 지원)
265
- *
266
- * faker.* 형식의 표현식을 안전하게 파싱하여 실행합니다.
267
- * 예: "faker.internet.email()" → faker.internet.email()
268
- * 예: "faker.lorem.words(3)" faker.lorem.words(3)
269
- */ async executeGenerator(generator, prop, entity) {
270
- // Faker.js 표현식만 지원
271
- if (generator.startsWith("faker.")) {
272
- // username이나 name 필드는 한국어 faker 사용
273
- const isNameField = prop.name === "username" || prop.name === "name";
274
- const fakerModule = await import("@faker-js/faker");
275
- const faker = isNameField ? fakerModule.fakerKO : fakerModule.faker;
276
- const expr = generator.slice(6); // "faker." 제거
277
- try {
278
- // 함수 경로와 인자 파싱
279
- const match = expr.match(/^([\w.]+)(?:\((.*?)\))?$/);
280
- if (!match) {
281
- throw new Error(`FixtureGenerator: Invalid faker expression for ${prop.name}: ${generator}`);
282
- }
283
- const [, path, argsStr] = match;
284
- const parts = path.split(".");
285
- // faker 객체에서 함수 찾기
286
- let fn = faker;
287
- for (const part of parts){
288
- if (typeof fn === "object" && fn !== null && part in fn) {
289
- fn = fn[part];
290
- } else {
291
- throw new Error(`FixtureGenerator: Invalid faker path for ${prop.name}: faker.${path}`);
292
- }
293
- }
294
- // 함수가 아니면 에러
295
- if (typeof fn !== "function") {
296
- throw new Error(`FixtureGenerator: faker.${path} is not a function (for ${prop.name})`);
297
- }
298
- let args = [];
299
- if (argsStr?.trim()) {
300
- args = this.parseGeneratorArgs(argsStr, prop.name);
301
- }
302
- return fn(...args);
303
- } catch (error) {
304
- !isTest() && console.log(chalk.yellow(`Failed to execute generator "${generator}" for ${prop.name}, falling back to default:`), error);
305
- return this.generateDefaultValue(prop, entity);
306
- }
307
- }
308
- // faker 이외의 표현식은 지원하지 않음
309
- !isTest() && console.log(chalk.yellow(`Unsupported generator expression for ${prop.name}: ${generator}. Only faker.* expressions are supported. Using default value.`));
310
- return this.generateDefaultValue(prop, entity);
311
- }
312
- /**
313
- * 필드의 타입과 이름을 분석하여 적절한 기본값을 생성합니다.
314
- *
315
- * 우선순위:
316
- * 1. 필드명 패턴 매칭 (salary, budget 등 의미있는 데이터)
317
- * 2. 특수 케이스 (Department name 등 도메인 지식)
318
- * 3. 배열 타입 (JSON 배열)
319
- * 4. Enum 타입
320
- * 5. 타입별 기본값
321
- */ async generateDefaultValue(prop, entity) {
322
- const fakerModule = await import("@faker-js/faker");
323
- const faker = fakerModule.faker;
324
- const fakerKO = fakerModule.fakerKO;
325
- const fakerJA = fakerModule.fakerJA;
326
- const localeFaker = this.locale === "ko" ? fakerKO : this.locale === "ja" ? fakerJA : faker;
327
- /**
328
- * 1. Entity-specific 특수 케이스를 먼저 처리합니다.
329
- * field_patterns보다 우선하여, 특정 엔티티의 필드에 도메인에 맞는 값을 생성합니다.
330
- * 예: Department.name → 한국어 부서명 (사람 이름이 아님)
331
- */ /**
332
- * Department name은 한국어 부서명 목록에서 선택합니다.
333
- * 고유성을 위해 70% 확률로 prefix/suffix를 추가합니다.
334
- */ if (entity?.id === "Department" && prop.name === "name") {
335
- const departments = [
336
- "개발팀",
337
- "기획팀",
338
- "마케팅팀",
339
- "영업팀",
340
- "인사팀",
341
- "총무팀",
342
- "재무팀",
343
- "회계팀",
344
- "법무팀",
345
- "디자인팀",
346
- "IT팀",
347
- "고객지원팀",
348
- "품질관리팀",
349
- "연구개발팀",
350
- "생산팀",
351
- "구매팀",
352
- "물류팀"
353
- ];
354
- const prefixes = [
355
- "신규",
356
- "통합",
357
- "전략",
358
- "글로벌",
359
- "디지털",
360
- "핵심"
361
- ];
362
- const suffixes = [
363
- "1팀",
364
- "2팀",
365
- "3팀",
366
- "A팀",
367
- "B팀",
368
- "본부",
369
- "센터",
370
- "그룹"
371
- ];
372
- const dept = faker.helpers.arrayElement(departments);
373
- const random = Math.random();
374
- if (random > 0.7) {
375
- const prefix = faker.helpers.arrayElement(prefixes);
376
- return `${prefix} ${dept}`;
377
- }
378
- if (random > 0.4) {
379
- const suffix = faker.helpers.arrayElement(suffixes);
380
- return `${dept} ${suffix}`;
381
- }
382
- return dept;
383
- }
384
- /**
385
- * 2. 필드명에서 의미를 추론하여 현실적인 데이터를 생성합니다.
386
- * 예: salary → 30M~150M (한국 연봉 범위)
387
- * budget 10M~500M (프로젝트 예산 범위)
388
- */ const localeMappings = this.mappings[this.locale] || this.mappings.en;
389
- const normalizedName = prop.name.toLowerCase().replace(/_/g, "");
390
- for (const [pattern, config] of Object.entries(localeMappings.field_patterns)){
391
- if (normalizedName.includes(pattern.toLowerCase())) {
392
- try {
393
- return await this.executeFakerExpression(config.faker, prop);
394
- } catch (error) {
395
- !isTest() && console.log(chalk.yellow(`Failed to execute field pattern "${pattern}" for ${prop.name}, falling back:`), error);
396
- break;
397
- }
398
- }
399
- }
400
- /**
401
- * 3. JSON 타입이면서 배열인 경우 (SonamuFile[], string[] 등)
402
- * 필드명 패턴을 보고 적절한 배열 데이터를 생성합니다.
403
- */ if (prop.type === "json" && "id" in prop && prop.id) {
404
- if (prop.id.endsWith("[]")) {
405
- return this.generateArrayValue(prop, entity, faker, localeFaker);
406
- }
407
- }
408
- /** 4. Enum 타입은 정의된 하나를 랜덤 선택합니다 */ if (prop.type === "enum") {
409
- let enumValues = [];
410
- if ("enum" in prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
411
- enumValues = prop.enum;
412
- } else if ("id" in prop && prop.id && entity?.enumLabels?.[prop.id]) {
413
- enumValues = Object.keys(entity.enumLabels[prop.id]);
414
- }
415
- if (enumValues.length > 0) {
416
- return faker.helpers.arrayElement(enumValues);
417
- }
418
- return prop.nullable ? null : "UNKNOWN";
419
- }
420
- if (prop.type === "enum[]") {
421
- let enumValues = [];
422
- if ("enum" in prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
423
- enumValues = prop.enum;
424
- } else if ("id" in prop && prop.id && entity?.enumLabels?.[prop.id]) {
425
- enumValues = Object.keys(entity.enumLabels[prop.id]);
426
- }
427
- if (enumValues.length > 0) {
428
- return [
429
- faker.helpers.arrayElement(enumValues)
430
- ];
431
- }
432
- return [];
433
- }
434
- /**
435
- * 5. Vector 타입은 현재 지원하지 않으므로 null을 반환합니다.
436
- * 향후 AI embedding 생성 기능 추가 시 구현 예정입니다.
437
- */ if (prop.type === "vector" || prop.type === "vector[]" || prop.type === "tsvector") {
438
- return null;
439
- }
440
- /** 6. 타입별 기본 Faker 표현식을 실행합니다 */ const typeDefault = localeMappings.type_defaults[prop.type];
441
- if (typeDefault) {
442
- try {
443
- return await this.executeFakerExpression(typeDefault.faker, prop);
444
- } catch (error) {
445
- !isTest() && console.log(chalk.yellow(`Failed to execute type default for ${prop.type}, using fallback:`, error));
446
- }
447
- }
448
- /** 7. 매핑되지 않은 타입은 기본 Faker 함수로 처리합니다 */ switch(prop.type){
449
- case "string":
450
- case "string[]":
451
- return faker.lorem.words(3);
452
- case "integer":
453
- return faker.number.int({
454
- min: 1,
455
- max: 1000
456
- });
457
- case "integer[]":
458
- return [
459
- faker.number.int({
460
- min: 1,
461
- max: 1000
462
- })
463
- ];
464
- case "bigInteger":
465
- return faker.number.bigInt({
466
- min: 1n,
467
- max: 1000n
468
- });
469
- case "bigInteger[]":
470
- return [
471
- faker.number.bigInt({
472
- min: 1n,
473
- max: 1000n
474
- })
475
- ];
476
- case "number":
477
- case "numeric":
478
- return faker.number.float({
479
- min: 0,
480
- max: 1000
481
- });
482
- case "number[]":
483
- case "numeric[]":
484
- return [
485
- faker.number.float({
486
- min: 0,
487
- max: 1000
488
- })
489
- ];
490
- case "boolean":
491
- return faker.datatype.boolean();
492
- case "boolean[]":
493
- return [
494
- faker.datatype.boolean()
495
- ];
496
- case "date":
497
- case "date[]":
498
- return faker.date.past();
499
- case "json":
500
- return {};
501
- case "uuid":
502
- case "uuid[]":
503
- return faker.string.uuid();
504
- default:
505
- return null;
506
- }
507
- }
508
- /**
509
- * 배열 타입의 값을 생성합니다.
510
- *
511
- * 타입 ID와 필드명 패턴을 분석하여 적절한 배열 데이터를 생성합니다.
512
- * 예: image_urls → [{url, name, mime_type}, ...]
513
- * tag_ids [1, 23, 45]
514
- */ generateArrayValue(prop, _entity, faker, _localeFaker) {
515
- const count = faker.number.int({
516
- min: 1,
517
- max: 3
518
- });
519
- /** SonamuFile[]은 Sonamu 내장 타입으로 구조가 정해져 있습니다 */ if ("id" in prop && prop.id === "SonamuFile[]") {
520
- return Array.from({
521
- length: count
522
- }, ()=>({
523
- url: faker.image.url(),
524
- name: faker.system.fileName(),
525
- mime_type: faker.helpers.arrayElement([
526
- "image/jpeg",
527
- "image/png",
528
- "image/gif",
529
- "application/pdf"
530
- ])
531
- }));
532
- }
533
- /** 필드명에서 배열의 용도를 추론합니다 */ const normalizedName = prop.name.toLowerCase().replace(/_/g, "");
534
- if (normalizedName.includes("url") || normalizedName.includes("image")) {
535
- return Array.from({
536
- length: count
537
- }, ()=>faker.internet.url());
538
- }
539
- if (normalizedName.includes("id") && normalizedName.endsWith("s")) {
540
- return Array.from({
541
- length: count
542
- }, ()=>faker.number.int({
543
- min: 1,
544
- max: 100
545
- }));
546
- }
547
- if (normalizedName.includes("tag") || normalizedName.includes("name")) {
548
- return Array.from({
549
- length: count
550
- }, ()=>faker.lorem.word());
551
- }
552
- /** 패턴 매칭되지 않으면 빈 배열을 반환합니다 */ return [];
553
- }
554
- /**
555
- * JSON 매핑의 Faker 표현식을 파싱하여 실행합니다.
556
- *
557
- * 표현식 예시:
558
- * - "faker.internet.email()" 인자 없음
559
- * - "faker.number.int({ min: 1, max: 100 })" → JSON 인자
560
- * - "{}" 리터럴 (JSON.parse)
561
- *
562
- * fakerKO, fakerJA도 지원하여 다국어 데이터를 생성합니다.
563
- */ async executeFakerExpression(expression, prop) {
564
- const fakerModule = await import("@faker-js/faker");
565
- const faker = fakerModule.faker;
566
- const fakerKO = fakerModule.fakerKO;
567
- const fakerJA = fakerModule.fakerJA;
568
- /** Faker 표현식이 아닌 리터럴 값은 JSON으로 파싱합니다 */ if (!expression.startsWith("faker")) {
569
- try {
570
- return JSON.parse(expression);
571
- } catch {
572
- return expression;
573
- }
574
- }
575
- /** 표현식에서 Faker 객체와 경로를 추출합니다 */ const match = expression.match(/^(faker|fakerKO|fakerJA)\.(.*?)$/);
576
- if (!match) {
577
- throw new Error(`Invalid faker expression: ${expression}`);
578
- }
579
- const [, fakerName, expr] = match;
580
- const selectedFaker = fakerName === "fakerKO" ? fakerKO : fakerName === "fakerJA" ? fakerJA : faker;
581
- const funcMatch = expr.match(/^([\w.]+)(?:\((.*?)\))?$/);
582
- if (!funcMatch) {
583
- throw new Error(`Invalid faker expression for ${prop.name}: ${expression}`);
584
- }
585
- const [, path, argsStr] = funcMatch;
586
- const parts = path.split(".");
587
- /** 표기법(dot notation)으로 Faker 함수를 찾아갑니다 */ let fn = selectedFaker;
588
- for (const part of parts){
589
- if (typeof fn === "object" && fn !== null && part in fn) {
590
- fn = fn[part];
591
- } else {
592
- throw new Error(`Invalid faker path for ${prop.name}: ${fakerName}.${path}`);
593
- }
594
- }
595
- if (typeof fn !== "function") {
596
- throw new Error(`${fakerName}.${path} is not a function (for ${prop.name})`);
597
- }
598
- let args = [];
599
- if (argsStr?.trim()) {
600
- args = this.parseGeneratorArgs(argsStr, prop.name);
601
- }
602
- return fn(...args);
603
- }
604
- /**
605
- * fixtureHint를 LLM에게 전달하여 현실적인 테스트 데이터를 생성합니다.
606
- *
607
- * faker.js로는 생성하기 어려운 복잡한 텍스트(자기소개, 설명문 등)를
608
- * LLM을 활용하여 생성합니다. 동일한 hint에 대한 중복 호출을 방지하기 위해
609
- * 캐싱을 기본으로 지원합니다 (LLM API 비용 절감).
610
- *
611
- * ai 패키지는 dynamic import로 불러오므로, useLLM이 false인 경우
612
- * 의존성이 설치되지 않아도 fixture 생성이 정상 동작합니다.
613
- */ async generateWithLLM(fixtureHint, prop, entity, rowKey) {
614
- // rowKey가 있으면 row 단위 생성 전략 사용
615
- if (rowKey) {
616
- const rowCacheKey = `${rowKey}:${prop.name}`;
617
- // 이미 이 row에 대한 LLM 호출이 완료된 경우 캐시에서 바로 반환
618
- if (this.llmCache.has(rowCacheKey)) {
619
- return this.llmCache.get(rowCacheKey);
620
- }
621
- // 새 row: LLM 대상 prop 전체를 한 번에 생성
622
- const llmProps = entity.props.filter((p)=>{
623
- if (isRelationProp(p)) return false;
624
- if (p.cone?.fixtureGenerator) return false;
625
- if (p.name === "id" && p.cone?.fixtureStrategy === "sequence") return false;
626
- return !!p.cone?.note;
627
- });
628
- // llmProps가 비어있으면 단일 필드 방식으로 fallback
629
- if (llmProps.length === 0) {
630
- !isTest() && console.log(`[FixtureGenerator] llmProps is empty for ${entity.id}.${prop.name}, using single field fallback`);
631
- return this.generateSingleWithLLM(fixtureHint, prop, entity);
632
- }
633
- const apiKey = this.getApiKey();
634
- const { createAnthropic } = await import("@ai-sdk/anthropic");
635
- const { generateText } = await import("ai");
636
- const rowResponse = await generateText({
637
- model: createAnthropic({
638
- apiKey
639
- })(this.options.llmModel || "claude-sonnet-4-6"),
640
- prompt: this.buildRowLLMPrompt(llmProps, entity)
641
- });
642
- if (!rowResponse || typeof rowResponse.text !== "string") {
643
- throw new Error("Invalid LLM response");
644
- }
645
- // 응답을 파싱하여 각 필드에 대한 결과를 캐시에 저장
646
- const rowResult = this.parseRowLLMResponse(rowResponse.text, llmProps);
647
- for (const [fieldName, value] of Object.entries(rowResult)){
648
- this.llmCache.set(`${rowKey}:${fieldName}`, value);
649
- }
650
- // 요청한 필드의 반환 (없으면 단일 필드 fallback)
651
- if (this.llmCache.has(rowCacheKey)) {
652
- return this.llmCache.get(rowCacheKey);
653
- }
654
- // 만약 row 응답에 이 필드가 누락된 경우 단일 필드 fallback
655
- return this.generateSingleWithLLM(fixtureHint, prop, entity);
656
- }
657
- // rowKey가 없으면 기존 단일 필드 방식
658
- return this.generateSingleWithLLM(fixtureHint, prop, entity);
659
- }
660
- /**
661
- * 단일 필드를 LLM으로 생성합니다 (rowKey 없을 때 fallback용)
662
- */ async generateSingleWithLLM(fixtureHint, prop, entity) {
663
- const cacheKey = `${entity.id}:${prop.name}:${fixtureHint}`;
664
- if (this.options.enableLLMCache && this.llmCache.has(cacheKey)) {
665
- return this.llmCache.get(cacheKey);
666
- }
667
- const apiKey = this.getApiKey();
668
- const { createAnthropic } = await import("@ai-sdk/anthropic");
669
- const { generateText } = await import("ai");
670
- const singleResponse = await generateText({
671
- model: createAnthropic({
672
- apiKey
673
- })(this.options.llmModel || "claude-sonnet-4-6"),
674
- prompt: this.buildLLMPrompt(fixtureHint, prop, entity)
675
- });
676
- if (!singleResponse || typeof singleResponse.text !== "string") {
677
- throw new Error("Invalid LLM response");
678
- }
679
- const value = this.parseLLMResponse(singleResponse.text, prop.type);
680
- if (this.options.enableLLMCache) {
681
- this.llmCache.set(cacheKey, value);
682
- }
683
- return value;
684
- }
685
- /**
686
- * row 전체를 한 번에 생성하는 LLM 프롬프트를 만듭니다.
687
- */ buildRowLLMPrompt(props, entity) {
688
- const locale = this.options.locale || "ko";
689
- const language = locale === "ko" ? "Korean" : locale === "ja" ? "Japanese" : "English";
690
- const fieldDescriptions = props.map((p)=>{
691
- let desc = `- ${p.name} (${p.type}): ${p.cone?.note ?? ""}`;
692
- if ((p.type === "enum" || p.type === "enum[]") && "id" in p && p.id && entity.enumLabels?.[p.id]) {
693
- const values = Object.keys(entity.enumLabels[p.id]).join(", ");
694
- desc += ` [allowed values: ${values}]`;
695
- }
696
- return desc;
697
- }).join("\n");
698
- // LLM 대상이 아닌 prop들도 맥락으로 제공 (relation 제외)
699
- const otherProps = entity.props.filter((p)=>!props.includes(p) && !isRelationProp(p) && p.name !== "id" && !("virtual" in p && p.virtual)).map((p)=>{
700
- let desc = `- ${p.name} (${p.type})`;
701
- if (p.cone?.note) desc += `: ${p.cone.note}`;
702
- if ((p.type === "enum" || p.type === "enum[]") && "id" in p && p.id && entity.enumLabels?.[p.id]) {
703
- const values = Object.keys(entity.enumLabels[p.id]).join(", ");
704
- desc += ` [allowed values: ${values}]`;
705
- }
706
- return desc;
707
- }).join("\n");
708
- const otherPropsContext = otherProps ? `\n\nOther fields in this entity (for context, do NOT generate these):\n${otherProps}` : "";
709
- const outputShape = props.map((p)=>` "${p.name}": <${p.type}>`).join(",\n");
710
- const entityContext = entity.cone?.note ? `\nEntity description: ${entity.cone.note}` : "";
711
- return `Generate test fixture data for the ${entity.id} entity. All fields must be coherent and consistent with each other.
9
+
10
+ //#region src/testing/fixture-generator.ts
11
+ var FixtureGenerator;
12
+ var init_fixture_generator = __esmMin((() => {
13
+ init_types();
14
+ init_controller();
15
+ init_data_explorer();
16
+ init_faker_mappings();
17
+ init_fixture_manager();
18
+ FixtureGenerator = class {
19
+ dataExplorer;
20
+ locale;
21
+ mappings;
22
+ llmCache = new Map();
23
+ entityCache = new Map();
24
+ options;
25
+ constructor(sourceDb, _targetDb, targetDbName, entityManager, options) {
26
+ this.sourceDb = sourceDb;
27
+ this.targetDbName = targetDbName;
28
+ this.entityManager = entityManager;
29
+ this.dataExplorer = new DataExplorer(sourceDb, entityManager);
30
+ this.locale = options?.locale || "ko";
31
+ this.mappings = fakerMappings;
32
+ this.options = {
33
+ locale: options?.locale || "ko",
34
+ useLLM: options?.useLLM || false,
35
+ enableLLMCache: options?.enableLLMCache !== false,
36
+ llmModel: options?.llmModel || "claude-sonnet-4-6"
37
+ };
38
+ }
39
+ /**
40
+ * Fixture 생성 (단일)
41
+ * @returns 생성된 fixture 데이터 (메모리 상)
42
+ */
43
+ async generate(entityName, overrides = {}, context = this.createContext()) {
44
+ let entity = this.entityCache.get(entityName);
45
+ if (!entity) {
46
+ entity = this.entityManager.get(entityName);
47
+ this.entityCache.set(entityName, entity);
48
+ }
49
+ const tempId = `${entityName}#temp#${Date.now()}`;
50
+ const rowKey = this.options.useLLM ? `${entityName}#row#${Date.now()}` : undefined;
51
+ const fixture = {};
52
+ for (const prop of entity.props) {
53
+ if ("virtual" in prop && prop.virtual) {
54
+ continue;
55
+ }
56
+ if (prop.name === "id") {
57
+ if ("cone" in prop && prop.cone?.fixtureStrategy === "sequence") {
58
+ continue;
59
+ }
60
+ if (prop.type === "string") {
61
+ const { faker: _faker } = await import("@faker-js/faker");
62
+ fixture[prop.name] = _faker.string.alphanumeric(32);
63
+ continue;
64
+ }
65
+ if (prop.type === "uuid") {
66
+ const { faker: _faker } = await import("@faker-js/faker");
67
+ fixture[prop.name] = _faker.string.uuid();
68
+ continue;
69
+ }
70
+ continue;
71
+ }
72
+ if (prop.name in overrides) {
73
+ fixture[prop.name] = overrides[prop.name];
74
+ continue;
75
+ }
76
+ const cone = prop.cone;
77
+ if (isRelationProp(prop)) {
78
+ const fkColName = `${prop.name}_id`;
79
+ if (fkColName in overrides && (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop) && prop.hasJoinColumn)) {
80
+ fixture[fkColName] = overrides[fkColName];
81
+ continue;
82
+ }
83
+ const relationValue = await this.generateRelationValue(entity, prop, context);
84
+ if (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop) && prop.hasJoinColumn) {
85
+ fixture[`${prop.name}_id`] = relationValue;
86
+ } else {
87
+ fixture[prop.name] = relationValue;
88
+ }
89
+ continue;
90
+ }
91
+ if (cone?.note && this.options.useLLM) {
92
+ try {
93
+ const llmValue = await this.generateWithLLM(cone.note, prop, entity, rowKey);
94
+ if (typeof llmValue === "string" && "length" in prop && typeof prop.length === "number" && llmValue.length > prop.length) {
95
+ fixture[prop.name] = llmValue.slice(0, prop.length);
96
+ } else {
97
+ fixture[prop.name] = llmValue;
98
+ }
99
+ continue;
100
+ } catch (error) {
101
+ console.warn(`[FixtureGenerator] LLM generation failed for ${entity.id}.${prop.name}, falling back to fixtureGenerator or default`, error instanceof Error ? error.message : error);
102
+ }
103
+ }
104
+ if (cone?.fixtureGenerator) {
105
+ fixture[prop.name] = await this.executeGenerator(cone.fixtureGenerator, prop, entity);
106
+ continue;
107
+ }
108
+ if (cone?.fixtureDefault !== undefined) {
109
+ fixture[prop.name] = cone.fixtureDefault;
110
+ continue;
111
+ }
112
+ fixture[prop.name] = await this.generateDefaultValue(prop, entity);
113
+ }
114
+ if ("email" in fixture && typeof fixture.email === "string" && !("email" in overrides)) {
115
+ const nameValue = fixture.name || fixture.username || fixture.full_name || fixture.name_en;
116
+ if (nameValue && typeof nameValue === "string") {
117
+ const domain = fixture.email.split("@")[1] || "example.com";
118
+ const romanized = await this.romanizeName(nameValue);
119
+ fixture.email = `${romanized}@${domain}`;
120
+ }
121
+ }
122
+ if ("password" in fixture && fixture.password && typeof fixture.password === "string") {
123
+ const bcrypt = await import("bcrypt");
124
+ fixture.password = await bcrypt.hash(fixture.password, 10);
125
+ }
126
+ context.fixtures.set(tempId, fixture);
127
+ return fixture;
128
+ }
129
+ /**
130
+ * Relation 값 생성 + 자동 Import
131
+ */
132
+ async generateRelationValue(entity, prop, context) {
133
+ if (!isRelationProp(prop)) {
134
+ throw new Error(`FixtureGenerator: ${entity.id}.${prop.name} is not a relation prop`);
135
+ }
136
+ if (!isBelongsToOneRelationProp(prop) && !(isOneToOneRelationProp(prop) && prop.hasJoinColumn)) {
137
+ return null;
138
+ }
139
+ const cone = prop.cone;
140
+ const dataSource = cone?.dataSource;
141
+ if (dataSource) {
142
+ const cacheKey = `${prop.with}:${JSON.stringify(dataSource)}`;
143
+ if (!context.referenceCache.has(cacheKey)) {
144
+ const exploreResult = await this.dataExplorer.exploreWithRelations(prop.with, {
145
+ strategy: dataSource.strategy,
146
+ limit: dataSource.config?.limit || 10,
147
+ includeRelations: true,
148
+ maxDepth: 3,
149
+ ...dataSource.config
150
+ });
151
+ context.referenceCache.set(cacheKey, exploreResult.main.records);
152
+ await this.importExploreResult(exploreResult, context);
153
+ }
154
+ const candidates = context.referenceCache.get(cacheKey);
155
+ if (candidates && candidates.length > 0) {
156
+ const selected = candidates[Math.floor(Math.random() * candidates.length)];
157
+ return selected.id;
158
+ }
159
+ }
160
+ const autoKey = `${prop.with}:auto`;
161
+ if (!context.referenceCache.has(autoKey)) {
162
+ const autoExploreResult = await this.dataExplorer.exploreWithRelations(prop.with, {
163
+ strategy: "random",
164
+ limit: 10,
165
+ includeRelations: true,
166
+ maxDepth: 3
167
+ });
168
+ context.referenceCache.set(autoKey, autoExploreResult.main.records);
169
+ if (autoExploreResult.main.records.length > 0) {
170
+ await this.importExploreResult(autoExploreResult, context);
171
+ }
172
+ }
173
+ const autoCandidates = context.referenceCache.get(autoKey);
174
+ if (autoCandidates && autoCandidates.length > 0) {
175
+ const selected = autoCandidates[Math.floor(Math.random() * autoCandidates.length)];
176
+ return selected.id;
177
+ }
178
+ if (prop.nullable) {
179
+ return null;
180
+ }
181
+ throw new Error(`FixtureGenerator: ${entity.id}.${prop.name}에 필요한 ${prop.with} 데이터가 없습니다. ` + `먼저 ${prop.with}를 생성하거나 cone.dataSource를 설정하세요.`);
182
+ }
183
+ /**
184
+ * ExploreWithRelations 결과를 targetDb에 import
185
+ *
186
+ * 관계 체인을 따라간 결과(main + related) 모두 import합니다.
187
+ * 의존성 순서는 FixtureManager.insertFixtures가 자동으로 처리합니다.
188
+ */
189
+ async importExploreResult(exploreResult, context) {
190
+ const allFixtureRecords = [];
191
+ for (const [entityId, records] of exploreResult.related.entries()) {
192
+ const entity = this.entityManager.get(entityId);
193
+ const recordsToImport = [];
194
+ !isTest() && console.log(chalk.cyan(`Importing related entity: ${entityId} (${records.length} records)`));
195
+ for (const record of records) {
196
+ const recordKey = `${entityId}#${record.id}`;
197
+ if (!context.importedRecords.has(recordKey)) {
198
+ recordsToImport.push(record);
199
+ context.importedRecords.add(recordKey);
200
+ }
201
+ }
202
+ if (recordsToImport.length > 0) {
203
+ for (const record of recordsToImport) {
204
+ !isTest() && console.log(chalk.gray(` - Processing ${entityId} record:`, JSON.stringify(record).slice(0, 100)));
205
+ const fixtureRecords = await FixtureManager.createFixtureRecord(entity, record, {
206
+ _db: this.sourceDb,
207
+ singleRecord: true
208
+ });
209
+ allFixtureRecords.push(...fixtureRecords);
210
+ }
211
+ }
212
+ }
213
+ const mainEntity = this.entityManager.get(exploreResult.main.entityId);
214
+ const mainRecordsToImport = [];
215
+ !isTest() && console.log(chalk.cyan(`Importing main entity: ${exploreResult.main.entityId} (${exploreResult.main.records.length} records)`));
216
+ for (const record of exploreResult.main.records) {
217
+ const recordKey = `${exploreResult.main.entityId}#${record.id}`;
218
+ if (!context.importedRecords.has(recordKey)) {
219
+ mainRecordsToImport.push(record);
220
+ context.importedRecords.add(recordKey);
221
+ }
222
+ }
223
+ if (mainRecordsToImport.length > 0) {
224
+ for (const record of mainRecordsToImport) {
225
+ !isTest() && console.log(chalk.gray(` - Processing ${exploreResult.main.entityId} record:`, JSON.stringify(record).slice(0, 100)));
226
+ const fixtureRecords = await FixtureManager.createFixtureRecord(mainEntity, record, {
227
+ _db: this.sourceDb,
228
+ singleRecord: true
229
+ });
230
+ allFixtureRecords.push(...fixtureRecords);
231
+ }
232
+ }
233
+ if (allFixtureRecords.length > 0) {
234
+ await FixtureManager.insertFixtures(this.targetDbName, allFixtureRecords);
235
+ !isTest() && console.log(chalk.green(`Auto-imported ${exploreResult.main.entityId} with relations: ` + `${exploreResult.main.records.length} main + ${exploreResult.related.size} related entities`));
236
+ }
237
+ }
238
+ /**
239
+ * fixtureGenerator 실행 (Faker.js만 지원)
240
+ *
241
+ * faker.* 형식의 표현식을 안전하게 파싱하여 실행합니다.
242
+ * 예: "faker.internet.email()" → faker.internet.email()
243
+ * 예: "faker.lorem.words(3)" faker.lorem.words(3)
244
+ */
245
+ async executeGenerator(generator, prop, entity) {
246
+ if (generator.startsWith("faker.")) {
247
+ const isNameField = prop.name === "username" || prop.name === "name";
248
+ const fakerModule = await import("@faker-js/faker");
249
+ const faker = isNameField ? fakerModule.fakerKO : fakerModule.faker;
250
+ const expr = generator.slice(6);
251
+ try {
252
+ const match = expr.match(/^([\w.]+)(?:\((.*?)\))?$/);
253
+ if (!match) {
254
+ throw new Error(`FixtureGenerator: Invalid faker expression for ${prop.name}: ${generator}`);
255
+ }
256
+ const [, path, argsStr] = match;
257
+ const parts = path.split(".");
258
+ let fn = faker;
259
+ for (const part of parts) {
260
+ if (typeof fn === "object" && fn !== null && part in fn) {
261
+ fn = fn[part];
262
+ } else {
263
+ throw new Error(`FixtureGenerator: Invalid faker path for ${prop.name}: faker.${path}`);
264
+ }
265
+ }
266
+ if (typeof fn !== "function") {
267
+ throw new Error(`FixtureGenerator: faker.${path} is not a function (for ${prop.name})`);
268
+ }
269
+ let args = [];
270
+ if (argsStr?.trim()) {
271
+ args = this.parseGeneratorArgs(argsStr, prop.name);
272
+ }
273
+ return fn(...args);
274
+ } catch (error) {
275
+ !isTest() && console.log(chalk.yellow(`Failed to execute generator "${generator}" for ${prop.name}, falling back to default:`), error);
276
+ return this.generateDefaultValue(prop, entity);
277
+ }
278
+ }
279
+ !isTest() && console.log(chalk.yellow(`Unsupported generator expression for ${prop.name}: ${generator}. Only faker.* expressions are supported. Using default value.`));
280
+ return this.generateDefaultValue(prop, entity);
281
+ }
282
+ /**
283
+ * 필드의 타입과 이름을 분석하여 적절한 기본값을 생성합니다.
284
+ *
285
+ * 우선순위:
286
+ * 1. 필드명 패턴 매칭 (salary, budget 등 의미있는 데이터)
287
+ * 2. 특수 케이스 (Department name 등 도메인 지식)
288
+ * 3. 배열 타입 (JSON 배열)
289
+ * 4. Enum 타입
290
+ * 5. 타입별 기본값
291
+ */
292
+ async generateDefaultValue(prop, entity) {
293
+ const fakerModule = await import("@faker-js/faker");
294
+ const faker = fakerModule.faker;
295
+ const fakerKO = fakerModule.fakerKO;
296
+ const fakerJA = fakerModule.fakerJA;
297
+ const localeFaker = this.locale === "ko" ? fakerKO : this.locale === "ja" ? fakerJA : faker;
298
+ /**
299
+ * 1. Entity-specific 특수 케이스를 먼저 처리합니다.
300
+ * field_patterns보다 우선하여, 특정 엔티티의 필드에 도메인에 맞는 값을 생성합니다.
301
+ * 예: Department.name → 한국어 부서명 (사람 이름이 아님)
302
+ */
303
+ /**
304
+ * Department name은 한국어 부서명 목록에서 선택합니다.
305
+ * 고유성을 위해 70% 확률로 prefix/suffix를 추가합니다.
306
+ */
307
+ if (entity?.id === "Department" && prop.name === "name") {
308
+ const departments = [
309
+ "개발팀",
310
+ "기획팀",
311
+ "마케팅팀",
312
+ "영업팀",
313
+ "인사팀",
314
+ "총무팀",
315
+ "재무팀",
316
+ "회계팀",
317
+ "법무팀",
318
+ "디자인팀",
319
+ "IT팀",
320
+ "고객지원팀",
321
+ "품질관리팀",
322
+ "연구개발팀",
323
+ "생산팀",
324
+ "구매팀",
325
+ "물류팀"
326
+ ];
327
+ const prefixes = [
328
+ "신규",
329
+ "통합",
330
+ "전략",
331
+ "글로벌",
332
+ "디지털",
333
+ "핵심"
334
+ ];
335
+ const suffixes = [
336
+ "1팀",
337
+ "2팀",
338
+ "3팀",
339
+ "A팀",
340
+ "B팀",
341
+ "본부",
342
+ "센터",
343
+ "그룹"
344
+ ];
345
+ const dept = faker.helpers.arrayElement(departments);
346
+ const random = Math.random();
347
+ if (random > .7) {
348
+ const prefix = faker.helpers.arrayElement(prefixes);
349
+ return `${prefix} ${dept}`;
350
+ }
351
+ if (random > .4) {
352
+ const suffix = faker.helpers.arrayElement(suffixes);
353
+ return `${dept} ${suffix}`;
354
+ }
355
+ return dept;
356
+ }
357
+ /**
358
+ * 2. 필드명에서 의미를 추론하여 현실적인 데이터를 생성합니다.
359
+ * 예: salary → 30M~150M (한국 연봉 범위)
360
+ * budget → 10M~500M (프로젝트 예산 범위)
361
+ */
362
+ const localeMappings = this.mappings[this.locale] || this.mappings.en;
363
+ const normalizedName = prop.name.toLowerCase().replace(/_/g, "");
364
+ for (const [pattern, config] of Object.entries(localeMappings.field_patterns)) {
365
+ if (normalizedName.includes(pattern.toLowerCase())) {
366
+ try {
367
+ return await this.executeFakerExpression(config.faker, prop);
368
+ } catch (error) {
369
+ !isTest() && console.log(chalk.yellow(`Failed to execute field pattern "${pattern}" for ${prop.name}, falling back:`), error);
370
+ break;
371
+ }
372
+ }
373
+ }
374
+ /**
375
+ * 3. JSON 타입이면서 배열인 경우 (SonamuFile[], string[] 등)
376
+ * 필드명 패턴을 보고 적절한 배열 데이터를 생성합니다.
377
+ */
378
+ if (prop.type === "json" && "id" in prop && prop.id) {
379
+ if (prop.id.endsWith("[]")) {
380
+ return this.generateArrayValue(prop, entity, faker, localeFaker);
381
+ }
382
+ }
383
+ /** 4. Enum 타입은 정의된 값 중 하나를 랜덤 선택합니다 */
384
+ if (prop.type === "enum") {
385
+ let enumValues = [];
386
+ if ("enum" in prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
387
+ enumValues = prop.enum;
388
+ } else if ("id" in prop && prop.id && entity?.enumLabels?.[prop.id]) {
389
+ enumValues = Object.keys(entity.enumLabels[prop.id]);
390
+ }
391
+ if (enumValues.length > 0) {
392
+ return faker.helpers.arrayElement(enumValues);
393
+ }
394
+ return prop.nullable ? null : "UNKNOWN";
395
+ }
396
+ if (prop.type === "enum[]") {
397
+ let enumValues = [];
398
+ if ("enum" in prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
399
+ enumValues = prop.enum;
400
+ } else if ("id" in prop && prop.id && entity?.enumLabels?.[prop.id]) {
401
+ enumValues = Object.keys(entity.enumLabels[prop.id]);
402
+ }
403
+ if (enumValues.length > 0) {
404
+ return [faker.helpers.arrayElement(enumValues)];
405
+ }
406
+ return [];
407
+ }
408
+ /**
409
+ * 5. Vector 타입은 현재 지원하지 않으므로 null을 반환합니다.
410
+ * 향후 AI embedding 생성 기능 추가 구현 예정입니다.
411
+ */
412
+ if (prop.type === "vector" || prop.type === "vector[]" || prop.type === "tsvector") {
413
+ return null;
414
+ }
415
+ /** 6. 타입별 기본 Faker 표현식을 실행합니다 */
416
+ const typeDefault = localeMappings.type_defaults[prop.type];
417
+ if (typeDefault) {
418
+ try {
419
+ return await this.executeFakerExpression(typeDefault.faker, prop);
420
+ } catch (error) {
421
+ !isTest() && console.log(chalk.yellow(`Failed to execute type default for ${prop.type}, using fallback:`, error));
422
+ }
423
+ }
424
+ /** 7. 매핑되지 않은 타입은 기본 Faker 함수로 처리합니다 */
425
+ switch (prop.type) {
426
+ case "string":
427
+ case "string[]": return faker.lorem.words(3);
428
+ case "integer": return faker.number.int({
429
+ min: 1,
430
+ max: 1e3
431
+ });
432
+ case "integer[]": return [faker.number.int({
433
+ min: 1,
434
+ max: 1e3
435
+ })];
436
+ case "bigInteger": return faker.number.bigInt({
437
+ min: 1n,
438
+ max: 1000n
439
+ });
440
+ case "bigInteger[]": return [faker.number.bigInt({
441
+ min: 1n,
442
+ max: 1000n
443
+ })];
444
+ case "number":
445
+ case "numeric": return faker.number.float({
446
+ min: 0,
447
+ max: 1e3
448
+ });
449
+ case "number[]":
450
+ case "numeric[]": return [faker.number.float({
451
+ min: 0,
452
+ max: 1e3
453
+ })];
454
+ case "boolean": return faker.datatype.boolean();
455
+ case "boolean[]": return [faker.datatype.boolean()];
456
+ case "date":
457
+ case "date[]": return faker.date.past();
458
+ case "json": return {};
459
+ case "uuid":
460
+ case "uuid[]": return faker.string.uuid();
461
+ default: return null;
462
+ }
463
+ }
464
+ /**
465
+ * 배열 타입의 값을 생성합니다.
466
+ *
467
+ * 타입 ID와 필드명 패턴을 분석하여 적절한 배열 데이터를 생성합니다.
468
+ * 예: image_urls → [{url, name, mime_type}, ...]
469
+ * tag_ids → [1, 23, 45]
470
+ */
471
+ generateArrayValue(prop, _entity, faker, _localeFaker) {
472
+ const count = faker.number.int({
473
+ min: 1,
474
+ max: 3
475
+ });
476
+ /** SonamuFile[]은 Sonamu 내장 타입으로 구조가 정해져 있습니다 */
477
+ if ("id" in prop && prop.id === "SonamuFile[]") {
478
+ return Array.from({ length: count }, () => ({
479
+ url: faker.image.url(),
480
+ name: faker.system.fileName(),
481
+ mime_type: faker.helpers.arrayElement([
482
+ "image/jpeg",
483
+ "image/png",
484
+ "image/gif",
485
+ "application/pdf"
486
+ ])
487
+ }));
488
+ }
489
+ /** 필드명에서 배열의 용도를 추론합니다 */
490
+ const normalizedName = prop.name.toLowerCase().replace(/_/g, "");
491
+ if (normalizedName.includes("url") || normalizedName.includes("image")) {
492
+ return Array.from({ length: count }, () => faker.internet.url());
493
+ }
494
+ if (normalizedName.includes("id") && normalizedName.endsWith("s")) {
495
+ return Array.from({ length: count }, () => faker.number.int({
496
+ min: 1,
497
+ max: 100
498
+ }));
499
+ }
500
+ if (normalizedName.includes("tag") || normalizedName.includes("name")) {
501
+ return Array.from({ length: count }, () => faker.lorem.word());
502
+ }
503
+ /** 패턴 매칭되지 않으면 빈 배열을 반환합니다 */
504
+ return [];
505
+ }
506
+ /**
507
+ * JSON 매핑의 Faker 표현식을 파싱하여 실행합니다.
508
+ *
509
+ * 표현식 예시:
510
+ * - "faker.internet.email()" → 인자 없음
511
+ * - "faker.number.int({ min: 1, max: 100 })" → JSON 인자
512
+ * - "{}" → 리터럴 값 (JSON.parse)
513
+ *
514
+ * fakerKO, fakerJA도 지원하여 다국어 데이터를 생성합니다.
515
+ */
516
+ async executeFakerExpression(expression, prop) {
517
+ const fakerModule = await import("@faker-js/faker");
518
+ const faker = fakerModule.faker;
519
+ const fakerKO = fakerModule.fakerKO;
520
+ const fakerJA = fakerModule.fakerJA;
521
+ /** Faker 표현식이 아닌 리터럴 값은 JSON으로 파싱합니다 */
522
+ if (!expression.startsWith("faker")) {
523
+ try {
524
+ return JSON.parse(expression);
525
+ } catch {
526
+ return expression;
527
+ }
528
+ }
529
+ /** 표현식에서 Faker 객체와 경로를 추출합니다 */
530
+ const match = expression.match(/^(faker|fakerKO|fakerJA)\.(.*?)$/);
531
+ if (!match) {
532
+ throw new Error(`Invalid faker expression: ${expression}`);
533
+ }
534
+ const [, fakerName, expr] = match;
535
+ const selectedFaker = fakerName === "fakerKO" ? fakerKO : fakerName === "fakerJA" ? fakerJA : faker;
536
+ const funcMatch = expr.match(/^([\w.]+)(?:\((.*?)\))?$/);
537
+ if (!funcMatch) {
538
+ throw new Error(`Invalid faker expression for ${prop.name}: ${expression}`);
539
+ }
540
+ const [, path, argsStr] = funcMatch;
541
+ const parts = path.split(".");
542
+ /** 점 표기법(dot notation)으로 Faker 함수를 찾아갑니다 */
543
+ let fn = selectedFaker;
544
+ for (const part of parts) {
545
+ if (typeof fn === "object" && fn !== null && part in fn) {
546
+ fn = fn[part];
547
+ } else {
548
+ throw new Error(`Invalid faker path for ${prop.name}: ${fakerName}.${path}`);
549
+ }
550
+ }
551
+ if (typeof fn !== "function") {
552
+ throw new Error(`${fakerName}.${path} is not a function (for ${prop.name})`);
553
+ }
554
+ let args = [];
555
+ if (argsStr?.trim()) {
556
+ args = this.parseGeneratorArgs(argsStr, prop.name);
557
+ }
558
+ return fn(...args);
559
+ }
560
+ /**
561
+ * fixtureHint를 LLM에게 전달하여 현실적인 테스트 데이터를 생성합니다.
562
+ *
563
+ * faker.js로는 생성하기 어려운 복잡한 텍스트(자기소개, 설명문 등)를
564
+ * LLM을 활용하여 생성합니다. 동일한 hint에 대한 중복 호출을 방지하기 위해
565
+ * 캐싱을 기본으로 지원합니다 (LLM API 비용 절감).
566
+ *
567
+ * ai 패키지는 dynamic import로 불러오므로, useLLM이 false인 경우
568
+ * 의존성이 설치되지 않아도 fixture 생성이 정상 동작합니다.
569
+ */
570
+ async generateWithLLM(fixtureHint, prop, entity, rowKey) {
571
+ if (rowKey) {
572
+ const rowCacheKey = `${rowKey}:${prop.name}`;
573
+ if (this.llmCache.has(rowCacheKey)) {
574
+ return this.llmCache.get(rowCacheKey);
575
+ }
576
+ const llmProps = entity.props.filter((p) => {
577
+ if (isRelationProp(p)) return false;
578
+ if (p.cone?.fixtureGenerator) return false;
579
+ if (p.name === "id" && p.cone?.fixtureStrategy === "sequence") return false;
580
+ return !!p.cone?.note;
581
+ });
582
+ if (llmProps.length === 0) {
583
+ !isTest() && console.log(`[FixtureGenerator] llmProps is empty for ${entity.id}.${prop.name}, using single field fallback`);
584
+ return this.generateSingleWithLLM(fixtureHint, prop, entity);
585
+ }
586
+ const apiKey = this.getApiKey();
587
+ const { createAnthropic } = await import("@ai-sdk/anthropic");
588
+ const { generateText } = await import("ai");
589
+ const rowResponse = await generateText({
590
+ model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-6"),
591
+ prompt: this.buildRowLLMPrompt(llmProps, entity)
592
+ });
593
+ if (!rowResponse || typeof rowResponse.text !== "string") {
594
+ throw new Error("Invalid LLM response");
595
+ }
596
+ const rowResult = this.parseRowLLMResponse(rowResponse.text, llmProps);
597
+ for (const [fieldName, value] of Object.entries(rowResult)) {
598
+ this.llmCache.set(`${rowKey}:${fieldName}`, value);
599
+ }
600
+ if (this.llmCache.has(rowCacheKey)) {
601
+ return this.llmCache.get(rowCacheKey);
602
+ }
603
+ return this.generateSingleWithLLM(fixtureHint, prop, entity);
604
+ }
605
+ return this.generateSingleWithLLM(fixtureHint, prop, entity);
606
+ }
607
+ /**
608
+ * 단일 필드를 LLM으로 생성합니다 (rowKey 없을 때 fallback용)
609
+ */
610
+ async generateSingleWithLLM(fixtureHint, prop, entity) {
611
+ const cacheKey = `${entity.id}:${prop.name}:${fixtureHint}`;
612
+ if (this.options.enableLLMCache && this.llmCache.has(cacheKey)) {
613
+ return this.llmCache.get(cacheKey);
614
+ }
615
+ const apiKey = this.getApiKey();
616
+ const { createAnthropic } = await import("@ai-sdk/anthropic");
617
+ const { generateText } = await import("ai");
618
+ const singleResponse = await generateText({
619
+ model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-6"),
620
+ prompt: this.buildLLMPrompt(fixtureHint, prop, entity)
621
+ });
622
+ if (!singleResponse || typeof singleResponse.text !== "string") {
623
+ throw new Error("Invalid LLM response");
624
+ }
625
+ const value = this.parseLLMResponse(singleResponse.text, prop.type);
626
+ if (this.options.enableLLMCache) {
627
+ this.llmCache.set(cacheKey, value);
628
+ }
629
+ return value;
630
+ }
631
+ /**
632
+ * row 전체를 번에 생성하는 LLM 프롬프트를 만듭니다.
633
+ */
634
+ buildRowLLMPrompt(props, entity) {
635
+ const locale = this.options.locale || "ko";
636
+ const language = locale === "ko" ? "Korean" : locale === "ja" ? "Japanese" : "English";
637
+ const fieldDescriptions = props.map((p) => {
638
+ let desc = `- ${p.name} (${p.type}): ${p.cone?.note ?? ""}`;
639
+ if ((p.type === "enum" || p.type === "enum[]") && "id" in p && p.id && entity.enumLabels?.[p.id]) {
640
+ const values = Object.keys(entity.enumLabels[p.id]).join(", ");
641
+ desc += ` [allowed values: ${values}]`;
642
+ }
643
+ return desc;
644
+ }).join("\n");
645
+ const otherProps = entity.props.filter((p) => !props.includes(p) && !isRelationProp(p) && p.name !== "id" && !("virtual" in p && p.virtual)).map((p) => {
646
+ let desc = `- ${p.name} (${p.type})`;
647
+ if (p.cone?.note) desc += `: ${p.cone.note}`;
648
+ if ((p.type === "enum" || p.type === "enum[]") && "id" in p && p.id && entity.enumLabels?.[p.id]) {
649
+ const values = Object.keys(entity.enumLabels[p.id]).join(", ");
650
+ desc += ` [allowed values: ${values}]`;
651
+ }
652
+ return desc;
653
+ }).join("\n");
654
+ const otherPropsContext = otherProps ? `\n\nOther fields in this entity (for context, do NOT generate these):\n${otherProps}` : "";
655
+ const outputShape = props.map((p) => ` "${p.name}": <${p.type}>`).join(",\n");
656
+ const entityContext = entity.cone?.note ? `\nEntity description: ${entity.cone.note}` : "";
657
+ return `Generate test fixture data for the ${entity.id} entity. All fields must be coherent and consistent with each other.
712
658
 
713
659
  Entity: ${entity.id}${entityContext}
714
660
  Locale: ${locale} (use ${language} for text fields)
@@ -726,38 +672,39 @@ Return exactly this JSON shape:
726
672
  {
727
673
  ${outputShape}
728
674
  }`;
729
- }
730
- /**
731
- * row LLM 응답을 파싱하여 필드별 값으로 변환합니다.
732
- */ parseRowLLMResponse(text, props) {
733
- const jsonText = text.trim().replace(/^```json\s*/i, "").replace(/```\s*$/, "").trim();
734
- let parsed;
735
- try {
736
- const raw = JSON.parse(jsonText);
737
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
738
- !isTest() && console.warn("[FixtureGenerator] Row LLM response is not a plain object:", text);
739
- return {};
740
- }
741
- parsed = raw;
742
- } catch {
743
- !isTest() && console.warn("[FixtureGenerator] Failed to parse row LLM response:", text);
744
- return {};
745
- }
746
- const result = {};
747
- for (const prop of props){
748
- if (prop.name in parsed) {
749
- result[prop.name] = this.parseLLMResponse(String(parsed[prop.name] ?? ""), prop.type);
750
- }
751
- }
752
- return result;
753
- }
754
- buildLLMPrompt(hint, prop, entity) {
755
- const locale = this.options.locale || "ko";
756
- const language = locale === "ko" ? "Korean" : locale === "ja" ? "Japanese" : "English";
757
- const entityContext = entity.cone?.note ? `\nEntity context: ${entity.cone.note}` : "";
758
- const otherFields = entity.props.filter((p)=>p.name !== prop.name && !isRelationProp(p) && p.cone?.note).map((p)=>`- ${p.name} (${p.type}): ${p.cone?.note}`).join("\n");
759
- const otherFieldsContext = otherFields ? `\n\nOther fields in this entity (for context):\n${otherFields}` : "";
760
- let prompt = `Generate test data for ${entity.id}.${prop.name} (type: ${prop.type})${entityContext}${otherFieldsContext}
675
+ }
676
+ /**
677
+ * row LLM 응답을 파싱하여 필드별 값으로 변환합니다.
678
+ */
679
+ parseRowLLMResponse(text, props) {
680
+ const jsonText = text.trim().replace(/^```json\s*/i, "").replace(/```\s*$/, "").trim();
681
+ let parsed;
682
+ try {
683
+ const raw = JSON.parse(jsonText);
684
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
685
+ !isTest() && console.warn("[FixtureGenerator] Row LLM response is not a plain object:", text);
686
+ return {};
687
+ }
688
+ parsed = raw;
689
+ } catch {
690
+ !isTest() && console.warn("[FixtureGenerator] Failed to parse row LLM response:", text);
691
+ return {};
692
+ }
693
+ const result = {};
694
+ for (const prop of props) {
695
+ if (prop.name in parsed) {
696
+ result[prop.name] = this.parseLLMResponse(String(parsed[prop.name] ?? ""), prop.type);
697
+ }
698
+ }
699
+ return result;
700
+ }
701
+ buildLLMPrompt(hint, prop, entity) {
702
+ const locale = this.options.locale || "ko";
703
+ const language = locale === "ko" ? "Korean" : locale === "ja" ? "Japanese" : "English";
704
+ const entityContext = entity.cone?.note ? `\nEntity context: ${entity.cone.note}` : "";
705
+ const otherFields = entity.props.filter((p) => p.name !== prop.name && !isRelationProp(p) && p.cone?.note).map((p) => `- ${p.name} (${p.type}): ${p.cone?.note}`).join("\n");
706
+ const otherFieldsContext = otherFields ? `\n\nOther fields in this entity (for context):\n${otherFields}` : "";
707
+ let prompt = `Generate test data for ${entity.id}.${prop.name} (type: ${prop.type})${entityContext}${otherFieldsContext}
761
708
 
762
709
  Requirement: ${hint}
763
710
 
@@ -765,608 +712,531 @@ Rules:
765
712
  - Return ONLY the value, no explanation or markdown
766
713
  - Use ${language} language if applicable
767
714
  - Format: ${this.getExpectedFormat(prop.type)}`;
768
- // enum 타입인 경우 가능한 목록 추가
769
- if (prop.type === "enum" || prop.type === "enum[]") {
770
- let enumValues = [];
771
- if ("enum" in prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
772
- enumValues = prop.enum;
773
- } else if ("id" in prop && prop.id && entity?.enumLabels?.[prop.id]) {
774
- enumValues = Object.keys(entity.enumLabels[prop.id]);
775
- }
776
- if (enumValues.length > 0) {
777
- prompt += `\n- IMPORTANT: Choose ONLY from these allowed values: ${enumValues.join(", ")}`;
778
- }
779
- }
780
- prompt += `\n\nExample: ${this.getExampleForType(prop.type, locale)}`;
781
- return prompt;
782
- }
783
- parseLLMResponse(text, propType) {
784
- const cleaned = text.trim();
785
- // 배열 타입 처리
786
- if (propType.endsWith("[]")) {
787
- try {
788
- const parsed = JSON.parse(cleaned);
789
- const baseType = propType.slice(0, -2); // "integer[]" -> "integer"
790
- if (Array.isArray(parsed)) {
791
- return parsed.map((item)=>{
792
- // null/undefined는 타입별 기본값으로
793
- if (item === null || item === undefined) {
794
- return this.getDefaultValueForType(baseType);
795
- }
796
- // 객체는 JSON.stringify 후 파싱 (json 타입인 경우)
797
- if (typeof item === "object") {
798
- return baseType === "json" ? item : this.parseScalarValue(JSON.stringify(item), baseType);
799
- }
800
- // primitive 값은 문자열로 변환 후 파싱
801
- return this.parseScalarValue(String(item), baseType);
802
- });
803
- }
804
- // 단일 값이 온 경우 배열로 감싸기
805
- if (parsed === null || parsed === undefined) {
806
- return [
807
- this.getDefaultValueForType(baseType)
808
- ];
809
- }
810
- return [
811
- this.parseScalarValue(String(parsed), baseType)
812
- ];
813
- } catch {
814
- return [];
815
- }
816
- }
817
- return this.parseScalarValue(cleaned, propType);
818
- }
819
- getDefaultValueForType(propType) {
820
- switch(propType){
821
- case "integer":
822
- return 0;
823
- case "bigInteger":
824
- return 0n;
825
- case "float":
826
- case "number":
827
- case "numeric":
828
- return 0;
829
- case "boolean":
830
- return false;
831
- case "date":
832
- return new Date();
833
- case "json":
834
- return {};
835
- case "uuid":
836
- return "00000000-0000-0000-0000-000000000000";
837
- default:
838
- return "";
839
- }
840
- }
841
- parseScalarValue(text, propType) {
842
- const cleaned = text.trim();
843
- switch(propType){
844
- case "integer":
845
- {
846
- const num = parseInt(cleaned, 10);
847
- return Number.isNaN(num) ? 0 : num;
848
- }
849
- case "bigInteger":
850
- {
851
- try {
852
- return BigInt(cleaned);
853
- } catch {
854
- return 0n;
855
- }
856
- }
857
- case "float":
858
- case "number":
859
- case "numeric":
860
- {
861
- const num = parseFloat(cleaned);
862
- return Number.isNaN(num) ? 0 : num;
863
- }
864
- case "boolean":
865
- return cleaned.toLowerCase() === "true";
866
- case "date":
867
- {
868
- const date = new Date(cleaned);
869
- return Number.isNaN(date.getTime()) ? new Date() : date;
870
- }
871
- case "json":
872
- try {
873
- return JSON.parse(cleaned);
874
- } catch {
875
- return cleaned;
876
- }
877
- case "uuid":
878
- case "enum":
879
- return cleaned;
880
- default:
881
- return cleaned;
882
- }
883
- }
884
- /**
885
- * faker 함수 인자 문자열을 파싱하여 인자 배열로 반환합니다.
886
- *
887
- * 3단계 전략:
888
- * 1. JSON 직접 파싱 (표준 JSON 표현식)
889
- * 2. JS 객체 리터럴 JSON 변환 후 재시도 (single quote, unquoted key 처리)
890
- * 3. 단순 단일 인자 폴백 (숫자, 문자열)
891
- */ parseGeneratorArgs(argsStr, propName) {
892
- // 1. JSON 직접 파싱
893
- try {
894
- const parsed = JSON.parse(`[${argsStr}]`);
895
- return Array.isArray(parsed) ? parsed : [
896
- parsed
897
- ];
898
- } catch {
899
- // 계속
900
- }
901
- // 2. JS 객체 리터럴 → JSON 변환 후 재시도
902
- try {
903
- const jsonStr = this.convertJsLiteralToJson(argsStr);
904
- const parsed = JSON.parse(`[${jsonStr}]`);
905
- return Array.isArray(parsed) ? parsed : [
906
- parsed
907
- ];
908
- } catch {
909
- // 계속
910
- }
911
- // 3. 단순 단일 인자 폴백
912
- const trimmed = argsStr.trim();
913
- if (!Number.isNaN(Number(trimmed))) {
914
- return [
915
- Number(trimmed)
916
- ];
917
- }
918
- if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
919
- return [
920
- trimmed.slice(1, -1)
921
- ];
922
- }
923
- throw new Error(`FixtureGenerator: Cannot parse arguments for ${propName}: ${argsStr}`);
924
- }
925
- /**
926
- * JS 객체 리터럴을 JSON으로 변환합니다.
927
- *
928
- * 두 가지 변환:
929
- * 1. Single-quoted 문자열 → double-quoted (이스케이프 처리 포함)
930
- * 2. Unquoted 객체 키 → double-quoted
931
- */ convertJsLiteralToJson(input) {
932
- // 1. 'value' → "value" (내부 " 이스케이프, \' → ')
933
- const withDoubleQuotes = input.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, (_, content)=>`"${content.replace(/"/g, '\\"').replace(/\\'/g, "'")}"`);
934
- // 2. { key: { "key":
935
- return withDoubleQuotes.replace(/([{,]\s*)([a-zA-Z_$][\w$]*)(\s*:)/g, '$1"$2"$3');
936
- }
937
- /**
938
- * Sonamu.secret을 우선으로 하고, 없으면 환경변수에서 API 키를 읽습니다.
939
- *
940
- * Sonamu.secret은 프로젝트별 설정(sonamu.config.ts)이므로 더 높은 우선순위를 가지며,
941
- * 환경변수는 개발 환경이나 CI/CD에서 fallback으로 사용됩니다.
942
- */ getApiKey() {
943
- let apiKey;
944
- try {
945
- const { Sonamu } = require("../api");
946
- apiKey = Sonamu.secrets?.anthropic_api_key;
947
- } catch {
948
- // Sonamu가 초기화되지 않은 경우 (테스트 환경 등)
949
- }
950
- if (!apiKey) {
951
- apiKey = process.env.ANTHROPIC_API_KEY;
952
- }
953
- if (!apiKey) {
954
- throw new Error("ANTHROPIC_API_KEY not found. Set it in environment variables or Sonamu.secret.anthropic_api_key");
955
- }
956
- return apiKey;
957
- }
958
- getExpectedFormat(propType) {
959
- // 배열 타입 처리
960
- if (propType.endsWith("[]")) {
961
- const baseType = propType.slice(0, -2);
962
- const baseFormat = this.getScalarFormat(baseType);
963
- return `JSON array of ${baseFormat} (e.g., [${this.getExampleForType(baseType, "en")}, ...])`;
964
- }
965
- return this.getScalarFormat(propType);
966
- }
967
- getScalarFormat(propType) {
968
- switch(propType){
969
- case "integer":
970
- case "bigInteger":
971
- return "integer numbers";
972
- case "float":
973
- case "number":
974
- case "numeric":
975
- return "decimal numbers";
976
- case "boolean":
977
- return "booleans (true or false)";
978
- case "date":
979
- return "ISO 8601 date strings";
980
- case "json":
981
- return "valid JSON object or array";
982
- case "uuid":
983
- return "UUID strings";
984
- case "enum":
985
- return "one of the allowed enum values";
986
- default:
987
- return "plain text strings";
988
- }
989
- }
990
- getExampleForType(propType, locale) {
991
- // 배열 타입 처리
992
- if (propType.endsWith("[]")) {
993
- const baseType = propType.slice(0, -2);
994
- const baseExample = this.getScalarExample(baseType, locale);
995
- return `[${baseExample}]`;
996
- }
997
- return this.getScalarExample(propType, locale);
998
- }
999
- getScalarExample(propType, locale) {
1000
- const isKorean = locale === "ko";
1001
- switch(propType){
1002
- case "integer":
1003
- case "bigInteger":
1004
- return "42";
1005
- case "float":
1006
- case "number":
1007
- case "numeric":
1008
- return "3.14";
1009
- case "boolean":
1010
- return "true";
1011
- case "date":
1012
- return "2024-01-01";
1013
- case "json":
1014
- return '{"key": "value"}';
1015
- case "uuid":
1016
- return "550e8400-e29b-41d4-a716-446655440000";
1017
- case "enum":
1018
- return "ENUM_VALUE";
1019
- default:
1020
- return isKorean ? "안녕하세요" : "Hello";
1021
- }
1022
- }
1023
- /**
1024
- * 이름을 이메일 로컬 파트용 로마나이즈드 문자열로 변환합니다.
1025
- *
1026
- * 한글 이름은 초성-중성-종성 분해 후 로마나이즈 처리합니다.
1027
- * 영문 이름은 소문자로 변환하고 공백을 점(.)\uc73c로 치환합니다.
1028
- * 예: "김철수" → "cheolsu.kim", "John Doe" → "john.doe"
1029
- */ async romanizeName(name) {
1030
- // 한글 포함 여부 확인
1031
- if (/[\uAC00-\uD7AF]/.test(name)) {
1032
- return this.romanizeKoreanName(name);
1033
- }
1034
- // 영문: 소문자 + 점 구분
1035
- return name.toLowerCase().replace(/\s+/g, ".").replace(/[^a-z0-9.]/g, "");
1036
- }
1037
- /**
1038
- * 한글 이름을 로마나이즈 처리합니다.
1039
- *
1040
- * 초성/중성/종성 매핑 테이블을 사용하여 한글을 로마자로 변환합니다.
1041
- * 첫 글자를 성으로 간주하여 "김철수" → "cheolsu.kim" 형태로 출력합니다.
1042
- */ romanizeKoreanName(name) {
1043
- const CHOSEONG = [
1044
- "g",
1045
- "kk",
1046
- "n",
1047
- "d",
1048
- "tt",
1049
- "r",
1050
- "m",
1051
- "b",
1052
- "pp",
1053
- "s",
1054
- "ss",
1055
- "",
1056
- "j",
1057
- "jj",
1058
- "ch",
1059
- "k",
1060
- "t",
1061
- "p",
1062
- "h"
1063
- ];
1064
- const JUNGSEONG = [
1065
- "a",
1066
- "ae",
1067
- "ya",
1068
- "yae",
1069
- "eo",
1070
- "e",
1071
- "yeo",
1072
- "ye",
1073
- "o",
1074
- "wa",
1075
- "wae",
1076
- "oe",
1077
- "yo",
1078
- "u",
1079
- "wo",
1080
- "we",
1081
- "wi",
1082
- "yu",
1083
- "eu",
1084
- "ui",
1085
- "i"
1086
- ];
1087
- const JONGSEONG = [
1088
- "",
1089
- "k",
1090
- "k",
1091
- "k",
1092
- "n",
1093
- "n",
1094
- "n",
1095
- "t",
1096
- "l",
1097
- "l",
1098
- "l",
1099
- "l",
1100
- "l",
1101
- "l",
1102
- "l",
1103
- "l",
1104
- "m",
1105
- "p",
1106
- "p",
1107
- "t",
1108
- "t",
1109
- "ng",
1110
- "t",
1111
- "t",
1112
- "k",
1113
- "t",
1114
- "p",
1115
- "t"
1116
- ];
1117
- const romanize = (char)=>{
1118
- const code = char.charCodeAt(0);
1119
- if (code < 0xac00 || code > 0xd7af) return char;
1120
- const offset = code - 0xac00;
1121
- const cho = Math.floor(offset / 588);
1122
- const jung = Math.floor(offset % 588 / 28);
1123
- const jong = offset % 28;
1124
- return CHOSEONG[cho] + JUNGSEONG[jung] + JONGSEONG[jong];
1125
- };
1126
- const chars = [
1127
- ...name
1128
- ];
1129
- // 글자를 성(姓)으로 간주
1130
- const familyName = romanize(chars[0]);
1131
- const givenName = chars.slice(1).map(romanize).join("");
1132
- if (givenName) {
1133
- return `${givenName}.${familyName}`;
1134
- }
1135
- return familyName;
1136
- }
1137
- /**
1138
- * LLM 캐시 통계를 반환합니다.
1139
- */ getLLMCacheStats() {
1140
- return {
1141
- size: this.llmCache.size,
1142
- enabled: this.options.enableLLMCache
1143
- };
1144
- }
1145
- /**
1146
- * LLM 캐시를 초기화합니다.
1147
- */ clearLLMCache() {
1148
- this.llmCache.clear();
1149
- }
1150
- /**
1151
- * 컨텍스트 생성
1152
- */ createContext() {
1153
- return {
1154
- fixtures: new Map(),
1155
- referenceCache: new Map(),
1156
- importedRecords: new Set()
1157
- };
1158
- }
1159
- /**
1160
- * 배치 생성 자동 저장
1161
- *
1162
- * 1. spec별로 fixture 생성 (메모리)
1163
- * 2. FixtureRecord로 변환
1164
- * 3. FixtureManager.insertFixtures()로 targetDb에 저장
1165
- *
1166
- * @returns 저장된 fixture 데이터 (실제 DB ID 포함)
1167
- */ async generateBatch(specs) {
1168
- const context = this.createContext();
1169
- const generatedFixtures = [];
1170
- // 1. 각 spec별로 fixture 생성
1171
- for (const spec of specs){
1172
- const specEntity = this.entityManager.get(spec.entity);
1173
- if (specEntity.parentId) {
1174
- // parentId 엔티티: DB에서 서브타입 행이 없는 부모 id를 조회하여 사용
1175
- // ( 부모 생성 대신 기존 데이터 재활용)
1176
- const idProp = specEntity.props.find((p)=>p.name === "id");
1177
- const parentOverrides = idProp?.cone?.fixtureParentOverrides ?? {};
1178
- const parentEntity = this.entityManager.get(specEntity.parentId);
1179
- // 부모 테이블에서 서브타입 테이블에 없는 id를 조회
1180
- let query = this.sourceDb(parentEntity.table).select(`${parentEntity.table}.id`);
1181
- for (const [col, val] of Object.entries(parentOverrides)){
1182
- query = query.where(`${parentEntity.table}.${col}`, val);
1183
- }
1184
- query = query.leftJoin(specEntity.table, `${specEntity.table}.id`, `${parentEntity.table}.id`).whereNull(`${specEntity.table}.id`).limit(spec.count);
1185
- const rows = await query;
1186
- const availableIds = rows.map((r)=>r.id);
1187
- if (availableIds.length === 0) {
1188
- !isTest() && console.warn(chalk.yellow(`[parentId] ${spec.entity}: 서브타입이 없는 부모 레코드가 부족합니다. 건너뜁니다.`));
1189
- } else {
1190
- for (const parentId of availableIds){
1191
- const fixture = await this.generate(spec.entity, spec.overrides || {}, context);
1192
- fixture.id = parentId;
1193
- generatedFixtures.push({
1194
- entity: spec.entity,
1195
- data: fixture,
1196
- explicitId: true
1197
- });
1198
- }
1199
- }
1200
- } else {
1201
- for(let i = 0; i < spec.count; i++){
1202
- const fixture = await this.generate(spec.entity, spec.overrides || {}, context);
1203
- generatedFixtures.push({
1204
- entity: spec.entity,
1205
- data: fixture
1206
- });
1207
- }
1208
- }
1209
- }
1210
- // 2. FixtureRecord로 변환
1211
- const fixtureRecords = [];
1212
- for (const { entity: entityName, data, explicitId } of generatedFixtures){
1213
- const entity = this.entityManager.get(entityName);
1214
- // integer/bigInteger PK는 임시 ID 생성 (DB 시퀀스가 실제 ID 할당)
1215
- // string PK는 generate()에서 이미 생성된 id 값을 그대로 사용
1216
- // parentId 엔티티는 부모의 실제 id를 그대로 사용 (시퀀스 미사용)
1217
- const idProp = entity.props.find((p)=>p.name === "id");
1218
- const usesSequence = !explicitId && (idProp?.type === "integer" || idProp?.type === "bigInteger" || idProp?.cone?.fixtureStrategy === "sequence");
1219
- const dataForRecord = usesSequence ? {
1220
- ...data,
1221
- id: Math.floor(Math.random() * 1000000)
1222
- } : data;
1223
- const records = await FixtureManager.createFixtureRecord(entity, dataForRecord, {
1224
- singleRecord: true
1225
- });
1226
- fixtureRecords.push(...records);
1227
- }
1228
- // 3. targetDb에 삽입 (FixtureManager가 의존성 정렬 처리)
1229
- const results = await FixtureManager.insertFixtures(this.targetDbName, fixtureRecords);
1230
- // 4. companion fixtures 생성 (fixtureCompanions가 선언된 경우)
1231
- const companionResults = await this.generateCompanions(specs, results);
1232
- const total = results.length + companionResults.length;
1233
- !isTest() && console.log(chalk.green(`Generated and saved ${total} fixtures to ${this.targetDbName}`));
1234
- return [
1235
- ...results,
1236
- ...companionResults
1237
- ];
1238
- }
1239
- /**
1240
- * 부모 fixture 결과를 기반으로 fixtureCompanions에 선언된 companion Entity를 생성합니다.
1241
- *
1242
- * generateBatch()에서만 호출되며, companion 생성 시 재귀를 방지하기 위해
1243
- * generateBatch()를 다시 호출하지 않고 직접 삽입합니다.
1244
- */ async generateCompanions(specs, parentResults) {
1245
- const allResults = [];
1246
- const processedEntities = new Set();
1247
- for (const spec of specs){
1248
- if (processedEntities.has(spec.entity)) continue;
1249
- processedEntities.add(spec.entity);
1250
- const entity = this.entityManager.get(spec.entity);
1251
- const idProp = entity.props.find((p)=>p.name === "id");
1252
- const companions = idProp?.cone?.fixtureCompanions;
1253
- if (!companions || companions.length === 0) continue;
1254
- const entityResults = parentResults.filter((r)=>r.entityId === spec.entity);
1255
- if (entityResults.length === 0) continue;
1256
- for (const companion of companions){
1257
- // companion entity에서 부모 entity로의 BelongsToOne FK 컬럼명 파악
1258
- const companionEntity = this.entityManager.get(companion.entity);
1259
- const fkProp = companionEntity.props.find((p)=>isRelationProp(p) && isBelongsToOneRelationProp(p) && p.with === spec.entity);
1260
- if (!fkProp) {
1261
- !isTest() && console.warn(chalk.yellow(`[Companion] No BelongsToOne relation from ${companion.entity} to ${spec.entity}. Skipping.`));
1262
- continue;
1263
- }
1264
- const fkColName = `${fkProp.name}_id`;
1265
- // companion의 idProp, usesSequence, count는 companion 단위로 고정
1266
- const companionIdProp = companionEntity.props.find((p)=>p.name === "id");
1267
- const usesSequence = companionIdProp?.type === "integer" || companionIdProp?.type === "bigInteger" || companionIdProp?.cone?.fixtureStrategy === "sequence";
1268
- const companionCount = companion.count ?? 1;
1269
- // 각 parent result에 대해 companion fixture 생성
1270
- const context = this.createContext();
1271
- const companionFixtureRecords = [];
1272
- for (const parentResult of entityResults){
1273
- const resolvedOverrides = this.resolveTemplateOverrides(companion.overrides ?? {}, parentResult.data);
1274
- resolvedOverrides[fkColName] = parentResult.data.id;
1275
- for(let i = 0; i < companionCount; i++){
1276
- const fixture = await this.generate(companion.entity, resolvedOverrides, context);
1277
- const dataForRecord = usesSequence ? {
1278
- ...fixture,
1279
- id: Math.floor(Math.random() * 1000000)
1280
- } : fixture;
1281
- const records = await FixtureManager.createFixtureRecord(companionEntity, dataForRecord, {
1282
- singleRecord: true
1283
- });
1284
- companionFixtureRecords.push(...records);
1285
- }
1286
- }
1287
- const companionResults = await FixtureManager.insertFixtures(this.targetDbName, companionFixtureRecords);
1288
- allResults.push(...companionResults);
1289
- !isTest() && console.log(chalk.green(`[Companion] Generated ${companionResults.length} ${companion.entity} fixtures`));
1290
- }
1291
- }
1292
- return allResults;
1293
- }
1294
- /**
1295
- * overrides 값의 "{{fieldName}}" 템플릿을 부모 fixture 데이터로 치환합니다.
1296
- *
1297
- * 예: { "account_id": "{{email}}" } → { "account_id": "user@example.com" }
1298
- */ resolveTemplateOverrides(overrides, parentData) {
1299
- const resolved = {};
1300
- for (const [key, value] of Object.entries(overrides)){
1301
- if (typeof value === "string" && value.startsWith("{{") && value.endsWith("}}")) {
1302
- const fieldName = value.slice(2, -2).trim();
1303
- if (!(fieldName in parentData)) {
1304
- throw new Error(`템플릿 필드 "${fieldName}"이(가) 부모 fixture 데이터에 존재하지 않습니다 (override key: "${key}")`);
1305
- }
1306
- resolved[key] = parentData[fieldName];
1307
- } else {
1308
- resolved[key] = value;
1309
- }
1310
- }
1311
- return resolved;
1312
- }
1313
- /**
1314
- * 실제 DB(sourceDb)에서 데이터를 조회하여 fixture DB(targetDb)에 import합니다.
1315
- *
1316
- * 1. DataExplorer로 sourceDb에서 데이터 조회 (관련 데이터 포함)
1317
- * 2. FixtureRecord로 변환
1318
- * 3. targetDb에 삽입
1319
- *
1320
- * @param entityName - 조회할 entity 이름
1321
- * @param options - 조회 옵션 (strategy, limit, includeRelations 등)
1322
- * @returns 저장된 fixture 데이터 (실제 DB ID 포함)
1323
- *
1324
- * @example
1325
- * // 프로덕션 DB에서 User 10명 + 관련 Employee, Department 가져오기
1326
- * await generator.importFromSource("User", {
1327
- * strategy: "sample",
1328
- * limit: 10,
1329
- * includeRelations: true,
1330
- * maxDepth: 2
1331
- * });
1332
- */ async importFromSource(entityName, options) {
1333
- !isTest() && console.log(chalk.blue(`Importing ${entityName} from source DB with options: ${JSON.stringify({
1334
- strategy: options.strategy,
1335
- limit: options.limit,
1336
- includeRelations: options.includeRelations,
1337
- maxDepth: options.maxDepth
1338
- })}`));
1339
- // 1. DataExplorer로 sourceDb에서 데이터 조회 (관련 데이터 포함)
1340
- const exploreResult = await this.dataExplorer.exploreWithRelations(entityName, options);
1341
- !isTest() && console.log(chalk.cyan(`Found ${exploreResult.main.records.length} ${entityName} records and ${exploreResult.related.size} related entities`));
1342
- // 2. FixtureRecord로 변환
1343
- const fixtureRecords = [];
1344
- // 메인 entity의 records를 FixtureRecord로 변환
1345
- const mainEntity = this.entityManager.get(entityName);
1346
- for (const record of exploreResult.main.records){
1347
- const records = await FixtureManager.createFixtureRecord(mainEntity, record, {
1348
- _db: this.sourceDb,
1349
- singleRecord: true
1350
- });
1351
- fixtureRecords.push(...records);
1352
- }
1353
- // 관련 entity의 records를 FixtureRecord로 변환
1354
- for (const [relatedEntityName, relatedRecords] of exploreResult.related.entries()){
1355
- const relatedEntity = this.entityManager.get(relatedEntityName);
1356
- for (const record of relatedRecords){
1357
- const records = await FixtureManager.createFixtureRecord(relatedEntity, record, {
1358
- _db: this.sourceDb,
1359
- singleRecord: true
1360
- });
1361
- fixtureRecords.push(...records);
1362
- }
1363
- !isTest() && console.log(chalk.gray(` - ${relatedEntityName}: ${relatedRecords.length} records`));
1364
- }
1365
- // 3. targetDb에 삽입 (FixtureManager가 의존성 정렬 처리)
1366
- const results = await FixtureManager.insertFixtures(this.targetDbName, fixtureRecords);
1367
- !isTest() && console.log(chalk.green(`Successfully imported ${results.length} records to ${this.targetDbName} (${exploreResult.main.records.length} ${entityName} + ${results.length - exploreResult.main.records.length} related)`));
1368
- return results;
1369
- }
1370
- }
715
+ if (prop.type === "enum" || prop.type === "enum[]") {
716
+ let enumValues = [];
717
+ if ("enum" in prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
718
+ enumValues = prop.enum;
719
+ } else if ("id" in prop && prop.id && entity?.enumLabels?.[prop.id]) {
720
+ enumValues = Object.keys(entity.enumLabels[prop.id]);
721
+ }
722
+ if (enumValues.length > 0) {
723
+ prompt += `\n- IMPORTANT: Choose ONLY from these allowed values: ${enumValues.join(", ")}`;
724
+ }
725
+ }
726
+ prompt += `\n\nExample: ${this.getExampleForType(prop.type, locale)}`;
727
+ return prompt;
728
+ }
729
+ parseLLMResponse(text, propType) {
730
+ const cleaned = text.trim();
731
+ if (propType.endsWith("[]")) {
732
+ try {
733
+ const parsed = JSON.parse(cleaned);
734
+ const baseType = propType.slice(0, -2);
735
+ if (Array.isArray(parsed)) {
736
+ return parsed.map((item) => {
737
+ if (item === null || item === undefined) {
738
+ return this.getDefaultValueForType(baseType);
739
+ }
740
+ if (typeof item === "object") {
741
+ return baseType === "json" ? item : this.parseScalarValue(JSON.stringify(item), baseType);
742
+ }
743
+ return this.parseScalarValue(String(item), baseType);
744
+ });
745
+ }
746
+ if (parsed === null || parsed === undefined) {
747
+ return [this.getDefaultValueForType(baseType)];
748
+ }
749
+ return [this.parseScalarValue(String(parsed), baseType)];
750
+ } catch {
751
+ return [];
752
+ }
753
+ }
754
+ return this.parseScalarValue(cleaned, propType);
755
+ }
756
+ getDefaultValueForType(propType) {
757
+ switch (propType) {
758
+ case "integer": return 0;
759
+ case "bigInteger": return 0n;
760
+ case "float":
761
+ case "number":
762
+ case "numeric": return 0;
763
+ case "boolean": return false;
764
+ case "date": return new Date();
765
+ case "json": return {};
766
+ case "uuid": return "00000000-0000-0000-0000-000000000000";
767
+ default: return "";
768
+ }
769
+ }
770
+ parseScalarValue(text, propType) {
771
+ const cleaned = text.trim();
772
+ switch (propType) {
773
+ case "integer": {
774
+ const num = parseInt(cleaned, 10);
775
+ return Number.isNaN(num) ? 0 : num;
776
+ }
777
+ case "bigInteger": {
778
+ try {
779
+ return BigInt(cleaned);
780
+ } catch {
781
+ return 0n;
782
+ }
783
+ }
784
+ case "float":
785
+ case "number":
786
+ case "numeric": {
787
+ const num = parseFloat(cleaned);
788
+ return Number.isNaN(num) ? 0 : num;
789
+ }
790
+ case "boolean": return cleaned.toLowerCase() === "true";
791
+ case "date": {
792
+ const date = new Date(cleaned);
793
+ return Number.isNaN(date.getTime()) ? new Date() : date;
794
+ }
795
+ case "json": try {
796
+ return JSON.parse(cleaned);
797
+ } catch {
798
+ return cleaned;
799
+ }
800
+ case "uuid":
801
+ case "enum": return cleaned;
802
+ default: return cleaned;
803
+ }
804
+ }
805
+ /**
806
+ * faker 함수 인자 문자열을 파싱하여 인자 배열로 반환합니다.
807
+ *
808
+ * 3단계 전략:
809
+ * 1. JSON 직접 파싱 (표준 JSON 표현식)
810
+ * 2. JS 객체 리터럴 → JSON 변환 후 재시도 (single quote, unquoted key 처리)
811
+ * 3. 단순 단일 인자 폴백 (숫자, 문자열)
812
+ */
813
+ parseGeneratorArgs(argsStr, propName) {
814
+ try {
815
+ const parsed = JSON.parse(`[${argsStr}]`);
816
+ return Array.isArray(parsed) ? parsed : [parsed];
817
+ } catch {}
818
+ try {
819
+ const jsonStr = this.convertJsLiteralToJson(argsStr);
820
+ const parsed = JSON.parse(`[${jsonStr}]`);
821
+ return Array.isArray(parsed) ? parsed : [parsed];
822
+ } catch {}
823
+ const trimmed = argsStr.trim();
824
+ if (!Number.isNaN(Number(trimmed))) {
825
+ return [Number(trimmed)];
826
+ }
827
+ if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) {
828
+ return [trimmed.slice(1, -1)];
829
+ }
830
+ throw new Error(`FixtureGenerator: Cannot parse arguments for ${propName}: ${argsStr}`);
831
+ }
832
+ /**
833
+ * JS 객체 리터럴을 JSON으로 변환합니다.
834
+ *
835
+ * 가지 변환:
836
+ * 1. Single-quoted 문자열double-quoted (이스케이프 처리 포함)
837
+ * 2. Unquoted 객체 double-quoted
838
+ */
839
+ convertJsLiteralToJson(input) {
840
+ const withDoubleQuotes = input.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, (_, content) => `"${content.replace(/"/g, "\\\"").replace(/\\'/g, "'")}"`);
841
+ return withDoubleQuotes.replace(/([{,]\s*)([a-zA-Z_$][\w$]*)(\s*:)/g, "$1\"$2\"$3");
842
+ }
843
+ /**
844
+ * Sonamu.secret을 우선으로 하고, 없으면 환경변수에서 API 키를 읽습니다.
845
+ *
846
+ * Sonamu.secret은 프로젝트별 설정(sonamu.config.ts)이므로 더 높은 우선순위를 가지며,
847
+ * 환경변수는 개발 환경이나 CI/CD에서 fallback으로 사용됩니다.
848
+ */
849
+ getApiKey() {
850
+ let apiKey;
851
+ try {
852
+ const { Sonamu } = (init_api(), __toCommonJS(api_exports));
853
+ apiKey = Sonamu.secrets?.anthropic_api_key;
854
+ } catch {}
855
+ if (!apiKey) {
856
+ apiKey = process.env.ANTHROPIC_API_KEY;
857
+ }
858
+ if (!apiKey) {
859
+ throw new Error("ANTHROPIC_API_KEY not found. Set it in environment variables or Sonamu.secret.anthropic_api_key");
860
+ }
861
+ return apiKey;
862
+ }
863
+ getExpectedFormat(propType) {
864
+ if (propType.endsWith("[]")) {
865
+ const baseType = propType.slice(0, -2);
866
+ const baseFormat = this.getScalarFormat(baseType);
867
+ return `JSON array of ${baseFormat} (e.g., [${this.getExampleForType(baseType, "en")}, ...])`;
868
+ }
869
+ return this.getScalarFormat(propType);
870
+ }
871
+ getScalarFormat(propType) {
872
+ switch (propType) {
873
+ case "integer":
874
+ case "bigInteger": return "integer numbers";
875
+ case "float":
876
+ case "number":
877
+ case "numeric": return "decimal numbers";
878
+ case "boolean": return "booleans (true or false)";
879
+ case "date": return "ISO 8601 date strings";
880
+ case "json": return "valid JSON object or array";
881
+ case "uuid": return "UUID strings";
882
+ case "enum": return "one of the allowed enum values";
883
+ default: return "plain text strings";
884
+ }
885
+ }
886
+ getExampleForType(propType, locale) {
887
+ if (propType.endsWith("[]")) {
888
+ const baseType = propType.slice(0, -2);
889
+ const baseExample = this.getScalarExample(baseType, locale);
890
+ return `[${baseExample}]`;
891
+ }
892
+ return this.getScalarExample(propType, locale);
893
+ }
894
+ getScalarExample(propType, locale) {
895
+ const isKorean = locale === "ko";
896
+ switch (propType) {
897
+ case "integer":
898
+ case "bigInteger": return "42";
899
+ case "float":
900
+ case "number":
901
+ case "numeric": return "3.14";
902
+ case "boolean": return "true";
903
+ case "date": return "2024-01-01";
904
+ case "json": return "{\"key\": \"value\"}";
905
+ case "uuid": return "550e8400-e29b-41d4-a716-446655440000";
906
+ case "enum": return "ENUM_VALUE";
907
+ default: return isKorean ? "안녕하세요" : "Hello";
908
+ }
909
+ }
910
+ /**
911
+ * 이름을 이메일 로컬 파트용 로마나이즈드 문자열로 변환합니다.
912
+ *
913
+ * 한글 이름은 초성-중성-종성 분해 후 로마나이즈 처리합니다.
914
+ * 영문 이름은 소문자로 변환하고 공백을 점(.)\uc73c로 치환합니다.
915
+ * 예: "김철수" → "cheolsu.kim", "John Doe" → "john.doe"
916
+ */
917
+ async romanizeName(name) {
918
+ if (/[\uAC00-\uD7AF]/.test(name)) {
919
+ return this.romanizeKoreanName(name);
920
+ }
921
+ return name.toLowerCase().replace(/\s+/g, ".").replace(/[^a-z0-9.]/g, "");
922
+ }
923
+ /**
924
+ * 한글 이름을 로마나이즈 처리합니다.
925
+ *
926
+ * 초성/중성/종성 매핑 테이블을 사용하여 한글을 로마자로 변환합니다.
927
+ * 첫 글자를 성으로 간주하여 "김철수" → "cheolsu.kim" 형태로 출력합니다.
928
+ */
929
+ romanizeKoreanName(name) {
930
+ const CHOSEONG = [
931
+ "g",
932
+ "kk",
933
+ "n",
934
+ "d",
935
+ "tt",
936
+ "r",
937
+ "m",
938
+ "b",
939
+ "pp",
940
+ "s",
941
+ "ss",
942
+ "",
943
+ "j",
944
+ "jj",
945
+ "ch",
946
+ "k",
947
+ "t",
948
+ "p",
949
+ "h"
950
+ ];
951
+ const JUNGSEONG = [
952
+ "a",
953
+ "ae",
954
+ "ya",
955
+ "yae",
956
+ "eo",
957
+ "e",
958
+ "yeo",
959
+ "ye",
960
+ "o",
961
+ "wa",
962
+ "wae",
963
+ "oe",
964
+ "yo",
965
+ "u",
966
+ "wo",
967
+ "we",
968
+ "wi",
969
+ "yu",
970
+ "eu",
971
+ "ui",
972
+ "i"
973
+ ];
974
+ const JONGSEONG = [
975
+ "",
976
+ "k",
977
+ "k",
978
+ "k",
979
+ "n",
980
+ "n",
981
+ "n",
982
+ "t",
983
+ "l",
984
+ "l",
985
+ "l",
986
+ "l",
987
+ "l",
988
+ "l",
989
+ "l",
990
+ "l",
991
+ "m",
992
+ "p",
993
+ "p",
994
+ "t",
995
+ "t",
996
+ "ng",
997
+ "t",
998
+ "t",
999
+ "k",
1000
+ "t",
1001
+ "p",
1002
+ "t"
1003
+ ];
1004
+ const romanize = (char) => {
1005
+ const code = char.charCodeAt(0);
1006
+ if (code < 44032 || code > 55215) return char;
1007
+ const offset = code - 44032;
1008
+ const cho = Math.floor(offset / 588);
1009
+ const jung = Math.floor(offset % 588 / 28);
1010
+ const jong = offset % 28;
1011
+ return CHOSEONG[cho] + JUNGSEONG[jung] + JONGSEONG[jong];
1012
+ };
1013
+ const chars = [...name];
1014
+ const familyName = romanize(chars[0]);
1015
+ const givenName = chars.slice(1).map(romanize).join("");
1016
+ if (givenName) {
1017
+ return `${givenName}.${familyName}`;
1018
+ }
1019
+ return familyName;
1020
+ }
1021
+ /**
1022
+ * LLM 캐시 통계를 반환합니다.
1023
+ */
1024
+ getLLMCacheStats() {
1025
+ return {
1026
+ size: this.llmCache.size,
1027
+ enabled: this.options.enableLLMCache
1028
+ };
1029
+ }
1030
+ /**
1031
+ * LLM 캐시를 초기화합니다.
1032
+ */
1033
+ clearLLMCache() {
1034
+ this.llmCache.clear();
1035
+ }
1036
+ /**
1037
+ * 컨텍스트 생성
1038
+ */
1039
+ createContext() {
1040
+ return {
1041
+ fixtures: new Map(),
1042
+ referenceCache: new Map(),
1043
+ importedRecords: new Set()
1044
+ };
1045
+ }
1046
+ /**
1047
+ * 배치 생성 및 자동 저장
1048
+ *
1049
+ * 1. 각 spec별로 fixture 생성 (메모리)
1050
+ * 2. FixtureRecord로 변환
1051
+ * 3. FixtureManager.insertFixtures()로 targetDb에 저장
1052
+ *
1053
+ * @returns 저장된 fixture 데이터 (실제 DB ID 포함)
1054
+ */
1055
+ async generateBatch(specs) {
1056
+ const context = this.createContext();
1057
+ const generatedFixtures = [];
1058
+ for (const spec of specs) {
1059
+ const specEntity = this.entityManager.get(spec.entity);
1060
+ if (specEntity.parentId) {
1061
+ const idProp = specEntity.props.find((p) => p.name === "id");
1062
+ const parentOverrides = idProp?.cone?.fixtureParentOverrides ?? {};
1063
+ const parentEntity = this.entityManager.get(specEntity.parentId);
1064
+ let query = this.sourceDb(parentEntity.table).select(`${parentEntity.table}.id`);
1065
+ for (const [col, val] of Object.entries(parentOverrides)) {
1066
+ query = query.where(`${parentEntity.table}.${col}`, val);
1067
+ }
1068
+ query = query.leftJoin(specEntity.table, `${specEntity.table}.id`, `${parentEntity.table}.id`).whereNull(`${specEntity.table}.id`).limit(spec.count);
1069
+ const rows = await query;
1070
+ const availableIds = rows.map((r) => r.id);
1071
+ if (availableIds.length === 0) {
1072
+ !isTest() && console.warn(chalk.yellow(`[parentId] ${spec.entity}: 서브타입이 없는 부모 레코드가 부족합니다. 건너뜁니다.`));
1073
+ } else {
1074
+ for (const parentId of availableIds) {
1075
+ const fixture = await this.generate(spec.entity, spec.overrides || {}, context);
1076
+ fixture.id = parentId;
1077
+ generatedFixtures.push({
1078
+ entity: spec.entity,
1079
+ data: fixture,
1080
+ explicitId: true
1081
+ });
1082
+ }
1083
+ }
1084
+ } else {
1085
+ for (let i = 0; i < spec.count; i++) {
1086
+ const fixture = await this.generate(spec.entity, spec.overrides || {}, context);
1087
+ generatedFixtures.push({
1088
+ entity: spec.entity,
1089
+ data: fixture
1090
+ });
1091
+ }
1092
+ }
1093
+ }
1094
+ const fixtureRecords = [];
1095
+ for (const { entity: entityName, data, explicitId } of generatedFixtures) {
1096
+ const entity = this.entityManager.get(entityName);
1097
+ const idProp = entity.props.find((p) => p.name === "id");
1098
+ const usesSequence = !explicitId && (idProp?.type === "integer" || idProp?.type === "bigInteger" || idProp?.cone?.fixtureStrategy === "sequence");
1099
+ const dataForRecord = usesSequence ? {
1100
+ ...data,
1101
+ id: Math.floor(Math.random() * 1e6)
1102
+ } : data;
1103
+ const records = await FixtureManager.createFixtureRecord(entity, dataForRecord, { singleRecord: true });
1104
+ fixtureRecords.push(...records);
1105
+ }
1106
+ const results = await FixtureManager.insertFixtures(this.targetDbName, fixtureRecords);
1107
+ const companionResults = await this.generateCompanions(specs, results);
1108
+ const total = results.length + companionResults.length;
1109
+ !isTest() && console.log(chalk.green(`Generated and saved ${total} fixtures to ${this.targetDbName}`));
1110
+ return [...results, ...companionResults];
1111
+ }
1112
+ /**
1113
+ * 부모 fixture 결과를 기반으로 fixtureCompanions에 선언된 companion Entity를 생성합니다.
1114
+ *
1115
+ * generateBatch()에서만 호출되며, companion 생성 시 재귀를 방지하기 위해
1116
+ * generateBatch()를 다시 호출하지 않고 직접 삽입합니다.
1117
+ */
1118
+ async generateCompanions(specs, parentResults) {
1119
+ const allResults = [];
1120
+ const processedEntities = new Set();
1121
+ for (const spec of specs) {
1122
+ if (processedEntities.has(spec.entity)) continue;
1123
+ processedEntities.add(spec.entity);
1124
+ const entity = this.entityManager.get(spec.entity);
1125
+ const idProp = entity.props.find((p) => p.name === "id");
1126
+ const companions = idProp?.cone?.fixtureCompanions;
1127
+ if (!companions || companions.length === 0) continue;
1128
+ const entityResults = parentResults.filter((r) => r.entityId === spec.entity);
1129
+ if (entityResults.length === 0) continue;
1130
+ for (const companion of companions) {
1131
+ const companionEntity = this.entityManager.get(companion.entity);
1132
+ const fkProp = companionEntity.props.find((p) => isRelationProp(p) && isBelongsToOneRelationProp(p) && p.with === spec.entity);
1133
+ if (!fkProp) {
1134
+ !isTest() && console.warn(chalk.yellow(`[Companion] No BelongsToOne relation from ${companion.entity} to ${spec.entity}. Skipping.`));
1135
+ continue;
1136
+ }
1137
+ const fkColName = `${fkProp.name}_id`;
1138
+ const companionIdProp = companionEntity.props.find((p) => p.name === "id");
1139
+ const usesSequence = companionIdProp?.type === "integer" || companionIdProp?.type === "bigInteger" || companionIdProp?.cone?.fixtureStrategy === "sequence";
1140
+ const companionCount = companion.count ?? 1;
1141
+ const context = this.createContext();
1142
+ const companionFixtureRecords = [];
1143
+ for (const parentResult of entityResults) {
1144
+ const resolvedOverrides = this.resolveTemplateOverrides(companion.overrides ?? {}, parentResult.data);
1145
+ resolvedOverrides[fkColName] = parentResult.data.id;
1146
+ for (let i = 0; i < companionCount; i++) {
1147
+ const fixture = await this.generate(companion.entity, resolvedOverrides, context);
1148
+ const dataForRecord = usesSequence ? {
1149
+ ...fixture,
1150
+ id: Math.floor(Math.random() * 1e6)
1151
+ } : fixture;
1152
+ const records = await FixtureManager.createFixtureRecord(companionEntity, dataForRecord, { singleRecord: true });
1153
+ companionFixtureRecords.push(...records);
1154
+ }
1155
+ }
1156
+ const companionResults = await FixtureManager.insertFixtures(this.targetDbName, companionFixtureRecords);
1157
+ allResults.push(...companionResults);
1158
+ !isTest() && console.log(chalk.green(`[Companion] Generated ${companionResults.length} ${companion.entity} fixtures`));
1159
+ }
1160
+ }
1161
+ return allResults;
1162
+ }
1163
+ /**
1164
+ * overrides 값의 "{{fieldName}}" 템플릿을 부모 fixture 데이터로 치환합니다.
1165
+ *
1166
+ * 예: { "account_id": "{{email}}" } → { "account_id": "user@example.com" }
1167
+ */
1168
+ resolveTemplateOverrides(overrides, parentData) {
1169
+ const resolved = {};
1170
+ for (const [key, value] of Object.entries(overrides)) {
1171
+ if (typeof value === "string" && value.startsWith("{{") && value.endsWith("}}")) {
1172
+ const fieldName = value.slice(2, -2).trim();
1173
+ if (!(fieldName in parentData)) {
1174
+ throw new Error(`템플릿 필드 "${fieldName}"이(가) 부모 fixture 데이터에 존재하지 않습니다 (override key: "${key}")`);
1175
+ }
1176
+ resolved[key] = parentData[fieldName];
1177
+ } else {
1178
+ resolved[key] = value;
1179
+ }
1180
+ }
1181
+ return resolved;
1182
+ }
1183
+ /**
1184
+ * 실제 DB(sourceDb)에서 데이터를 조회하여 fixture DB(targetDb)에 import합니다.
1185
+ *
1186
+ * 1. DataExplorer로 sourceDb에서 데이터 조회 (관련 데이터 포함)
1187
+ * 2. FixtureRecord로 변환
1188
+ * 3. targetDb에 삽입
1189
+ *
1190
+ * @param entityName - 조회할 entity 이름
1191
+ * @param options - 조회 옵션 (strategy, limit, includeRelations 등)
1192
+ * @returns 저장된 fixture 데이터 (실제 DB ID 포함)
1193
+ *
1194
+ * @example
1195
+ * // 프로덕션 DB에서 User 10명 + 관련 Employee, Department 가져오기
1196
+ * await generator.importFromSource("User", {
1197
+ * strategy: "sample",
1198
+ * limit: 10,
1199
+ * includeRelations: true,
1200
+ * maxDepth: 2
1201
+ * });
1202
+ */
1203
+ async importFromSource(entityName, options) {
1204
+ !isTest() && console.log(chalk.blue(`Importing ${entityName} from source DB with options: ${JSON.stringify({
1205
+ strategy: options.strategy,
1206
+ limit: options.limit,
1207
+ includeRelations: options.includeRelations,
1208
+ maxDepth: options.maxDepth
1209
+ })}`));
1210
+ const exploreResult = await this.dataExplorer.exploreWithRelations(entityName, options);
1211
+ !isTest() && console.log(chalk.cyan(`Found ${exploreResult.main.records.length} ${entityName} records and ${exploreResult.related.size} related entities`));
1212
+ const fixtureRecords = [];
1213
+ const mainEntity = this.entityManager.get(entityName);
1214
+ for (const record of exploreResult.main.records) {
1215
+ const records = await FixtureManager.createFixtureRecord(mainEntity, record, {
1216
+ _db: this.sourceDb,
1217
+ singleRecord: true
1218
+ });
1219
+ fixtureRecords.push(...records);
1220
+ }
1221
+ for (const [relatedEntityName, relatedRecords] of exploreResult.related.entries()) {
1222
+ const relatedEntity = this.entityManager.get(relatedEntityName);
1223
+ for (const record of relatedRecords) {
1224
+ const records = await FixtureManager.createFixtureRecord(relatedEntity, record, {
1225
+ _db: this.sourceDb,
1226
+ singleRecord: true
1227
+ });
1228
+ fixtureRecords.push(...records);
1229
+ }
1230
+ !isTest() && console.log(chalk.gray(` - ${relatedEntityName}: ${relatedRecords.length} records`));
1231
+ }
1232
+ const results = await FixtureManager.insertFixtures(this.targetDbName, fixtureRecords);
1233
+ !isTest() && console.log(chalk.green(`Successfully imported ${results.length} records to ${this.targetDbName} (${exploreResult.main.records.length} ${entityName} + ${results.length - exploreResult.main.records.length} related)`));
1234
+ return results;
1235
+ }
1236
+ };
1237
+ }));
1371
1238
 
1372
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0aW5nL2ZpeHR1cmUtZ2VuZXJhdG9yLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjaGFsayBmcm9tIFwiY2hhbGtcIjtcbmltcG9ydCB0eXBlIHsgS25leCB9IGZyb20gXCJrbmV4XCI7XG5pbXBvcnQgdHlwZSB7IEVudGl0eSB9IGZyb20gXCIuLi9lbnRpdHkvZW50aXR5XCI7XG5pbXBvcnQgdHlwZSB7IEVudGl0eU1hbmFnZXIgfSBmcm9tIFwiLi4vZW50aXR5L2VudGl0eS1tYW5hZ2VyXCI7XG5pbXBvcnQgdHlwZSB7IEVudGl0eVByb3AsIEZpeHR1cmVJbXBvcnRSZXN1bHQsIEZpeHR1cmVSZWNvcmQgfSBmcm9tIFwiLi4vdHlwZXMvdHlwZXNcIjtcbmltcG9ydCB7IGlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wLCBpc09uZVRvT25lUmVsYXRpb25Qcm9wLCBpc1JlbGF0aW9uUHJvcCB9IGZyb20gXCIuLi90eXBlcy90eXBlc1wiO1xuaW1wb3J0IHsgaXNUZXN0IH0gZnJvbSBcIi4uL3V0aWxzL2NvbnRyb2xsZXJcIjtcbmltcG9ydCB7XG4gIERhdGFFeHBsb3JlcixcbiAgdHlwZSBFeHBsb3JlV2l0aFJlbGF0aW9uc09wdGlvbnMsXG4gIHR5cGUgRXhwbG9yZVdpdGhSZWxhdGlvbnNSZXN1bHQsXG59IGZyb20gXCIuL2RhdGEtZXhwbG9yZXJcIjtcbmltcG9ydCB7IHR5cGUgRmFrZXJNYXBwaW5ncywgZmFrZXJNYXBwaW5ncyB9IGZyb20gXCIuL2Zha2VyLW1hcHBpbmdzXCI7XG5pbXBvcnQgeyBGaXh0dXJlTWFuYWdlciB9IGZyb20gXCIuL2ZpeHR1cmUtbWFuYWdlclwiO1xuXG5leHBvcnQgdHlwZSBMb2NhbGUgPSBcImtvXCIgfCBcImVuXCIgfCBcImphXCI7XG5cbmV4cG9ydCB0eXBlIEZpeHR1cmVHZW5lcmF0b3JPcHRpb25zID0ge1xuICBsb2NhbGU/OiBMb2NhbGU7XG4gIHVzZUxMTT86IGJvb2xlYW47XG4gIGVuYWJsZUxMTUNhY2hlPzogYm9vbGVhbjtcbiAgbGxtTW9kZWw/OiBzdHJpbmc7XG59O1xuXG5leHBvcnQgdHlwZSBHZW5lcmF0b3JDb250ZXh0ID0ge1xuICAvKiog7IOd7ISxIOykkeyduCBmaXh0dXJl65OkICjrqZTrqqjrpqwg7IOBKSAqL1xuICBmaXh0dXJlczogTWFwPHN0cmluZywgUmVjb3JkPHN0cmluZywgdW5rbm93bj4+O1xuXG4gIC8qKiDssLjsobAg642w7J207YSwIOy6kOyLnCAoRGF0YUV4cGxvcmVyIOqysOqzvCkgKi9cbiAgcmVmZXJlbmNlQ2FjaGU6IE1hcDxzdHJpbmcsIFJlY29yZDxzdHJpbmcsIHVua25vd24+W10+O1xuXG4gIC8qKiDsnbTrr7ggaW1wb3J065CcIOugiOy9lOuTnOulvCDstpTsoIHtlZjsl6wg7KSR67O1IGltcG9ydOulvCDrsKnsp4Dtlanri4jri6QgKi9cbiAgaW1wb3J0ZWRSZWNvcmRzOiBTZXQ8c3RyaW5nPjsgLy8gXCJVc2VyIzEyM1wiXG59O1xuXG5leHBvcnQgY2xhc3MgRml4dHVyZUdlbmVyYXRvciB7XG4gIHByaXZhdGUgZGF0YUV4cGxvcmVyOiBEYXRhRXhwbG9yZXI7XG4gIHByaXZhdGUgbG9jYWxlOiBMb2NhbGU7XG4gIHByaXZhdGUgbWFwcGluZ3M6IEZha2VyTWFwcGluZ3M7XG4gIHByaXZhdGUgbGxtQ2FjaGU6IE1hcDxzdHJpbmcsIHVua25vd24+ID0gbmV3IE1hcCgpO1xuICBwcml2YXRlIGVudGl0eUNhY2hlOiBNYXA8c3RyaW5nLCBFbnRpdHk+ID0gbmV3IE1hcCgpO1xuICBwcml2YXRlIG9wdGlvbnM6IEZpeHR1cmVHZW5lcmF0b3JPcHRpb25zO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgc291cmNlRGI6IEtuZXgsXG4gICAgLy8gRml4dHVyZU1hbmFnZXIuaW5zZXJ0Rml4dHVyZXPqsIAgZGJOYW1lIOusuOyekOyXtOydhCDrsJvquLAg65WM66y47JeQIOyngeygkSDsgqzsmqntlZjsp4Ag7JWK7Iq164uI64ukXG4gICAgLy8g66+4656YIO2ZleyepeyEseydhCDsnITtlbQgQVBJIOyLnOq3uOuLiOyymOyXkOuKlCDtj6ztlajsi5zsvLDsirXri4jri6RcbiAgICBfdGFyZ2V0RGI6IEtuZXgsXG4gICAgcHJpdmF0ZSB0YXJnZXREYk5hbWU6IFwiZml4dHVyZVwiIHwgXCJ0ZXN0XCIgfCBcInByb2R1Y3Rpb25fbWFzdGVyXCIsXG4gICAgcHJpdmF0ZSBlbnRpdHlNYW5hZ2VyOiB0eXBlb2YgRW50aXR5TWFuYWdlcixcbiAgICBvcHRpb25zPzogRml4dHVyZUdlbmVyYXRvck9wdGlvbnMsXG4gICkge1xuICAgIHRoaXMuZGF0YUV4cGxvcmVyID0gbmV3IERhdGFFeHBsb3Jlcihzb3VyY2VEYiwgZW50aXR5TWFuYWdlcik7XG4gICAgdGhpcy5sb2NhbGUgPSBvcHRpb25zPy5sb2NhbGUgfHwgXCJrb1wiO1xuICAgIHRoaXMubWFwcGluZ3MgPSBmYWtlck1hcHBpbmdzO1xuICAgIHRoaXMub3B0aW9ucyA9IHtcbiAgICAgIGxvY2FsZTogb3B0aW9ucz8ubG9jYWxlIHx8IFwia29cIixcbiAgICAgIHVzZUxMTTogb3B0aW9ucz8udXNlTExNIHx8IGZhbHNlLFxuICAgICAgZW5hYmxlTExNQ2FjaGU6IG9wdGlvbnM/LmVuYWJsZUxMTUNhY2hlICE9PSBmYWxzZSxcbiAgICAgIGxsbU1vZGVsOiBvcHRpb25zPy5sbG1Nb2RlbCB8fCBcImNsYXVkZS1zb25uZXQtNC02XCIsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaXh0dXJlIOyDneyEsSAo64uo7J28KVxuICAgKiBAcmV0dXJucyDsg53shLHrkJwgZml4dHVyZSDrjbDsnbTthLAgKOuplOuqqOumrCDsg4EpXG4gICAqL1xuICBhc3luYyBnZW5lcmF0ZShcbiAgICBlbnRpdHlOYW1lOiBzdHJpbmcsXG4gICAgb3ZlcnJpZGVzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9LFxuICAgIGNvbnRleHQ6IEdlbmVyYXRvckNvbnRleHQgPSB0aGlzLmNyZWF0ZUNvbnRleHQoKSxcbiAgKTogUHJvbWlzZTxSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4ge1xuICAgIC8vIEVudGl0eSDsupDsi7E6IO2FjOyKpO2KuOyXkOyEnCBlbnRpdHkgY29uZSDsiJjsoJXsnbQg67CY7JiB65CY64+E66GdIOuztOyepVxuICAgIGxldCBlbnRpdHkgPSB0aGlzLmVudGl0eUNhY2hlLmdldChlbnRpdHlOYW1lKTtcbiAgICBpZiAoIWVudGl0eSkge1xuICAgICAgZW50aXR5ID0gdGhpcy5lbnRpdHlNYW5hZ2VyLmdldChlbnRpdHlOYW1lKTtcbiAgICAgIHRoaXMuZW50aXR5Q2FjaGUuc2V0KGVudGl0eU5hbWUsIGVudGl0eSk7XG4gICAgfVxuXG4gICAgY29uc3QgdGVtcElkID0gYCR7ZW50aXR5TmFtZX0jdGVtcCMke0RhdGUubm93KCl9YDsgLy8g7J6E7IucIElEXG5cbiAgICAvLyBMTE0gcm93IOuLqOychCDsg53shLHsnYQg7JyE7ZWcIOqzoOycoCDtgqQgKOqwmeydgCByb3fsnZgg7ZWE65Oc65Ok7J20IOuPmeydvO2VnCByb3dLZXnrpbwg6rO17JygKVxuICAgIGNvbnN0IHJvd0tleSA9IHRoaXMub3B0aW9ucy51c2VMTE0gPyBgJHtlbnRpdHlOYW1lfSNyb3cjJHtEYXRlLm5vdygpfWAgOiB1bmRlZmluZWQ7XG5cbiAgICAvLyDqsIEgcHJvcOuzhCDqsJIg7IOd7ISxXG4gICAgY29uc3QgZml4dHVyZTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcblxuICAgIGZvciAoY29uc3QgcHJvcCBvZiBlbnRpdHkucHJvcHMpIHtcbiAgICAgIC8vIFZpcnR1YWwgcHJvcOydgCDsiqTtgrVcbiAgICAgIGlmIChcInZpcnR1YWxcIiBpbiBwcm9wICYmIHByb3AudmlydHVhbCkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gaWQgcHJvcCDsspjrpqxcbiAgICAgIGlmIChwcm9wLm5hbWUgPT09IFwiaWRcIikge1xuICAgICAgICBpZiAoXCJjb25lXCIgaW4gcHJvcCAmJiBwcm9wLmNvbmU/LmZpeHR1cmVTdHJhdGVneSA9PT0gXCJzZXF1ZW5jZVwiKSB7XG4gICAgICAgICAgLy8gREIgc2VxdWVuY2XqsIAg7J6Q64+ZIO2VoOuLue2VmOuvgOuhnCDsiqTtgrUgKFVzZXIg65OxKVxuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGlmIChwcm9wLnR5cGUgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICAvLyBEQiBERUZBVUxUIOyXhuuKlCBzdHJpbmcgUEs6IGFscGhhbnVtZXJpYyAzMuyekCDsg53shLEgKGJldHRlci1hdXRoIOyKpO2DgOydvClcbiAgICAgICAgICBjb25zdCB7IGZha2VyOiBfZmFrZXIgfSA9IGF3YWl0IGltcG9ydChcIkBmYWtlci1qcy9mYWtlclwiKTtcbiAgICAgICAgICBmaXh0dXJlW3Byb3AubmFtZV0gPSBfZmFrZXIuc3RyaW5nLmFscGhhbnVtZXJpYygzMik7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHByb3AudHlwZSA9PT0gXCJ1dWlkXCIpIHtcbiAgICAgICAgICBjb25zdCB7IGZha2VyOiBfZmFrZXIgfSA9IGF3YWl0IGltcG9ydChcIkBmYWtlci1qcy9mYWtlclwiKTtcbiAgICAgICAgICBmaXh0dXJlW3Byb3AubmFtZV0gPSBfZmFrZXIuc3RyaW5nLnV1aWQoKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBpbnRlZ2VyL2JpZ0ludGVnZXIgUEs6IGdlbmVyYXRlQmF0Y2jsl5DshJwgdGVtcElk66W8IOuEo+ycvOuvgOuhnCDsl6zquLDshKAg7Iqk7YK1XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICAvLyBvdmVycmlkZeqwgCDsnojsnLzrqbQg7IKs7JqpXG4gICAgICBpZiAocHJvcC5uYW1lIGluIG92ZXJyaWRlcykge1xuICAgICAgICBmaXh0dXJlW3Byb3AubmFtZV0gPSBvdmVycmlkZXNbcHJvcC5uYW1lXTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIGNvbmXsl5DshJwg7IOd7ISxIOyghOuetSDtmZXsnbhcbiAgICAgIGNvbnN0IGNvbmUgPSBwcm9wLmNvbmU7XG5cbiAgICAgIC8vIDEuIFJlbGF0aW9uIHByb3Ag7LKY66asXG4gICAgICBpZiAoaXNSZWxhdGlvblByb3AocHJvcCkpIHtcbiAgICAgICAgLy8gQmVsb25nc1RvT25lIC8gT25lVG9PbmUoaGFzSm9pbkNvbHVtbinsnYAgRksg7Lus65+866qFKHtwcm9wLm5hbWV9X2lkKeycvOuhnOuPhCBvdmVycmlkZeulvCDrsJvripTri6RcbiAgICAgICAgY29uc3QgZmtDb2xOYW1lID0gYCR7cHJvcC5uYW1lfV9pZGA7XG4gICAgICAgIGlmIChcbiAgICAgICAgICBma0NvbE5hbWUgaW4gb3ZlcnJpZGVzICYmXG4gICAgICAgICAgKGlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wKHByb3ApIHx8IChpc09uZVRvT25lUmVsYXRpb25Qcm9wKHByb3ApICYmIHByb3AuaGFzSm9pbkNvbHVtbikpXG4gICAgICAgICkge1xuICAgICAgICAgIGZpeHR1cmVbZmtDb2xOYW1lXSA9IG92ZXJyaWRlc1tma0NvbE5hbWVdO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgcmVsYXRpb25WYWx1ZSA9IGF3YWl0IHRoaXMuZ2VuZXJhdGVSZWxhdGlvblZhbHVlKGVudGl0eSwgcHJvcCwgY29udGV4dCk7XG4gICAgICAgIC8vIEJlbG9uZ3NUb09uZSwgT25lVG9PbmUoaGFzSm9pbkNvbHVtbinsnZgg6rK97JqwIGZvcmVpZ24ga2V5IOy7rOufvOuqheycvOuhnCDsoIDsnqVcbiAgICAgICAgaWYgKFxuICAgICAgICAgIGlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wKHByb3ApIHx8XG4gICAgICAgICAgKGlzT25lVG9PbmVSZWxhdGlvblByb3AocHJvcCkgJiYgcHJvcC5oYXNKb2luQ29sdW1uKVxuICAgICAgICApIHtcbiAgICAgICAgICBmaXh0dXJlW2Ake3Byb3AubmFtZX1faWRgXSA9IHJlbGF0aW9uVmFsdWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gcmVsYXRpb25WYWx1ZTtcbiAgICAgICAgfVxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gMi4gY29uZS5ub3RlICsgTExNIOyCrOyaqSAodXNlTExN7J2066m0IGZpeHR1cmVHZW5lcmF0b3Lrs7Tri6Qg7Jqw7ISgKVxuICAgICAgaWYgKGNvbmU/Lm5vdGUgJiYgdGhpcy5vcHRpb25zLnVzZUxMTSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IGxsbVZhbHVlID0gYXdhaXQgdGhpcy5nZW5lcmF0ZVdpdGhMTE0oY29uZS5ub3RlLCBwcm9wLCBlbnRpdHksIHJvd0tleSk7XG4gICAgICAgICAgLy8gc3RyaW5nIO2DgOyeheydtOqzoCBsZW5ndGgg7KCc7JW97J20IOyeiOycvOuptCDstIjqs7wg7IucIHRydW5jYXRpb25cbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICB0eXBlb2YgbGxtVmFsdWUgPT09IFwic3RyaW5nXCIgJiZcbiAgICAgICAgICAgIFwibGVuZ3RoXCIgaW4gcHJvcCAmJlxuICAgICAgICAgICAgdHlwZW9mIHByb3AubGVuZ3RoID09PSBcIm51bWJlclwiICYmXG4gICAgICAgICAgICBsbG1WYWx1ZS5sZW5ndGggPiBwcm9wLmxlbmd0aFxuICAgICAgICAgICkge1xuICAgICAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gbGxtVmFsdWUuc2xpY2UoMCwgcHJvcC5sZW5ndGgpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBmaXh0dXJlW3Byb3AubmFtZV0gPSBsbG1WYWx1ZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgICAgYFtGaXh0dXJlR2VuZXJhdG9yXSBMTE0gZ2VuZXJhdGlvbiBmYWlsZWQgZm9yICR7ZW50aXR5LmlkfS4ke3Byb3AubmFtZX0sIGZhbGxpbmcgYmFjayB0byBmaXh0dXJlR2VuZXJhdG9yIG9yIGRlZmF1bHRgLFxuICAgICAgICAgICAgZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBlcnJvcixcbiAgICAgICAgICApO1xuICAgICAgICAgIC8vIGZhbGxiYWNrOiBmaXh0dXJlR2VuZXJhdG9yIOKGkiBmaXh0dXJlRGVmYXVsdCDihpIg6riw67O46rCS7Jy866GcIOqzhOyGjVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIDMuIGZpeHR1cmVHZW5lcmF0b3Ig7IKs7JqpXG4gICAgICBpZiAoY29uZT8uZml4dHVyZUdlbmVyYXRvcikge1xuICAgICAgICBmaXh0dXJlW3Byb3AubmFtZV0gPSBhd2FpdCB0aGlzLmV4ZWN1dGVHZW5lcmF0b3IoXG4gICAgICAgICAgY29uZS5maXh0dXJlR2VuZXJhdG9yIGFzIHN0cmluZyxcbiAgICAgICAgICBwcm9wLFxuICAgICAgICAgIGVudGl0eSxcbiAgICAgICAgKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIDQuIGZpeHR1cmVEZWZhdWx0IOyCrOyaqVxuICAgICAgaWYgKGNvbmU/LmZpeHR1cmVEZWZhdWx0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gY29uZS5maXh0dXJlRGVmYXVsdDtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIDUuIO2DgOyeheuzhCDquLDrs7gg7IOd7ISxXG4gICAgICBmaXh0dXJlW3Byb3AubmFtZV0gPSBhd2FpdCB0aGlzLmdlbmVyYXRlRGVmYXVsdFZhbHVlKHByb3AsIGVudGl0eSk7XG4gICAgfVxuXG4gICAgLy8gNi4gZW1haWwg7ZWE65Oc6rCAIOyeiOqzoCBuYW1lIO2VhOuTnOqwgCDsnojsnLzrqbQsIGVtYWls7J2YIOuhnOy7rCDtjIztirjrpbwgbmFtZSDquLDrsJjsnLzroZwg67O07KCVXG4gICAgaWYgKFwiZW1haWxcIiBpbiBmaXh0dXJlICYmIHR5cGVvZiBmaXh0dXJlLmVtYWlsID09PSBcInN0cmluZ1wiICYmICEoXCJlbWFpbFwiIGluIG92ZXJyaWRlcykpIHtcbiAgICAgIGNvbnN0IG5hbWVWYWx1ZSA9IGZpeHR1cmUubmFtZSB8fCBmaXh0dXJlLnVzZXJuYW1lIHx8IGZpeHR1cmUuZnVsbF9uYW1lIHx8IGZpeHR1cmUubmFtZV9lbjtcbiAgICAgIGlmIChuYW1lVmFsdWUgJiYgdHlwZW9mIG5hbWVWYWx1ZSA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgICBjb25zdCBkb21haW4gPSBmaXh0dXJlLmVtYWlsLnNwbGl0KFwiQFwiKVsxXSB8fCBcImV4YW1wbGUuY29tXCI7XG4gICAgICAgIGNvbnN0IHJvbWFuaXplZCA9IGF3YWl0IHRoaXMucm9tYW5pemVOYW1lKG5hbWVWYWx1ZSk7XG4gICAgICAgIGZpeHR1cmUuZW1haWwgPSBgJHtyb21hbml6ZWR9QCR7ZG9tYWlufWA7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gNy4gcGFzc3dvcmQg7ZWE65OcIOyVlO2YuO2ZlFxuICAgIGlmIChcInBhc3N3b3JkXCIgaW4gZml4dHVyZSAmJiBmaXh0dXJlLnBhc3N3b3JkICYmIHR5cGVvZiBmaXh0dXJlLnBhc3N3b3JkID09PSBcInN0cmluZ1wiKSB7XG4gICAgICBjb25zdCBiY3J5cHQgPSBhd2FpdCBpbXBvcnQoXCJiY3J5cHRcIik7XG4gICAgICBmaXh0dXJlLnBhc3N3b3JkID0gYXdhaXQgYmNyeXB0Lmhhc2goZml4dHVyZS5wYXNzd29yZCwgMTApO1xuICAgIH1cblxuICAgIGNvbnRleHQuZml4dHVyZXMuc2V0KHRlbXBJZCwgZml4dHVyZSk7XG4gICAgcmV0dXJuIGZpeHR1cmU7XG4gIH1cblxuICAvKipcbiAgICogUmVsYXRpb24g6rCSIOyDneyEsSArIOyekOuPmSBJbXBvcnRcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVSZWxhdGlvblZhbHVlKFxuICAgIGVudGl0eTogRW50aXR5LFxuICAgIHByb3A6IEVudGl0eVByb3AsXG4gICAgY29udGV4dDogR2VuZXJhdG9yQ29udGV4dCxcbiAgKTogUHJvbWlzZTxudW1iZXIgfCBudWxsPiB7XG4gICAgaWYgKCFpc1JlbGF0aW9uUHJvcChwcm9wKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGaXh0dXJlR2VuZXJhdG9yOiAke2VudGl0eS5pZH0uJHtwcm9wLm5hbWV9IGlzIG5vdCBhIHJlbGF0aW9uIHByb3BgKTtcbiAgICB9XG5cbiAgICAvLyBCZWxvbmdzVG9PbmUsIE9uZVRvT25lKGhhc0pvaW5Db2x1bW4p66eMIOyymOumrFxuICAgIGlmIChcbiAgICAgICFpc0JlbG9uZ3NUb09uZVJlbGF0aW9uUHJvcChwcm9wKSAmJlxuICAgICAgIShpc09uZVRvT25lUmVsYXRpb25Qcm9wKHByb3ApICYmIHByb3AuaGFzSm9pbkNvbHVtbilcbiAgICApIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IGNvbmUgPSBwcm9wLmNvbmU7XG4gICAgY29uc3QgZGF0YVNvdXJjZSA9IGNvbmU/LmRhdGFTb3VyY2U7XG5cbiAgICAvLyBEYXRhRXhwbG9yZXLroZwg7LC47KGwIOuNsOydtO2EsCDsobDtmowgKHNvdXJjZURiKVxuICAgIC8vIOq0gOqzhCDssrTsnbjsnYQg65Sw65286rCA6riwIOychO2VtCBleHBsb3JlV2l0aFJlbGF0aW9ucyDsgqzsmqlcbiAgICBpZiAoZGF0YVNvdXJjZSkge1xuICAgICAgY29uc3QgY2FjaGVLZXkgPSBgJHtwcm9wLndpdGh9OiR7SlNPTi5zdHJpbmdpZnkoZGF0YVNvdXJjZSl9YDtcblxuICAgICAgaWYgKCFjb250ZXh0LnJlZmVyZW5jZUNhY2hlLmhhcyhjYWNoZUtleSkpIHtcbiAgICAgICAgY29uc3QgZXhwbG9yZVJlc3VsdCA9IGF3YWl0IHRoaXMuZGF0YUV4cGxvcmVyLmV4cGxvcmVXaXRoUmVsYXRpb25zKHByb3Aud2l0aCwge1xuICAgICAgICAgIHN0cmF0ZWd5OiBkYXRhU291cmNlLnN0cmF0ZWd5LFxuICAgICAgICAgIGxpbWl0OlxuICAgICAgICAgICAgKChkYXRhU291cmNlLmNvbmZpZyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCk/LmxpbWl0IGFzXG4gICAgICAgICAgICAgIHwgbnVtYmVyXG4gICAgICAgICAgICAgIHwgdW5kZWZpbmVkKSB8fCAxMCxcbiAgICAgICAgICBpbmNsdWRlUmVsYXRpb25zOiB0cnVlLFxuICAgICAgICAgIG1heERlcHRoOiAzLFxuICAgICAgICAgIC4uLihkYXRhU291cmNlLmNvbmZpZyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCksXG4gICAgICAgIH0pO1xuICAgICAgICBjb250ZXh0LnJlZmVyZW5jZUNhY2hlLnNldChjYWNoZUtleSwgZXhwbG9yZVJlc3VsdC5tYWluLnJlY29yZHMpO1xuXG4gICAgICAgIC8vIOyhsO2ajO2VnCDrjbDsnbTthLDsmYAg6rSA6rOE65CcIOuqqOuToCDsl5Tti7Dti7DrpbwgdGFyZ2V0RGLsl5AgaW1wb3J0XG4gICAgICAgIGF3YWl0IHRoaXMuaW1wb3J0RXhwbG9yZVJlc3VsdChleHBsb3JlUmVzdWx0LCBjb250ZXh0KTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgY2FuZGlkYXRlcyA9IGNvbnRleHQucmVmZXJlbmNlQ2FjaGUuZ2V0KGNhY2hlS2V5KTtcbiAgICAgIGlmIChjYW5kaWRhdGVzICYmIGNhbmRpZGF0ZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAvLyDrnpzrjaTtlZjqsowg7ZWY64KYIOyEoO2DnVxuICAgICAgICBjb25zdCBzZWxlY3RlZCA9IGNhbmRpZGF0ZXNbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogY2FuZGlkYXRlcy5sZW5ndGgpXTtcbiAgICAgICAgcmV0dXJuIHNlbGVjdGVkLmlkIGFzIG51bWJlcjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBkYXRhU291cmNl6rCAIOyXhuydhCDrlYwg7J6Q64+Z7Jy866GcIGZpeHR1cmUgRELsl5DshJwg7KGw7ZqMIOyLnOuPhFxuICAgIC8vIOq0gOqzhCDssrTsnbjsnYQg65Sw65286rCA6riwIOychO2VtCBleHBsb3JlV2l0aFJlbGF0aW9ucyDsgqzsmqlcbiAgICBjb25zdCBhdXRvS2V5ID0gYCR7cHJvcC53aXRofTphdXRvYDtcbiAgICBpZiAoIWNvbnRleHQucmVmZXJlbmNlQ2FjaGUuaGFzKGF1dG9LZXkpKSB7XG4gICAgICAvLyBmaXh0dXJlIERCKHNvdXJjZURiKeyXkOyEnCDsnpDrj5kg7KGw7ZqMICjqtIDqs4Qg7Y+s7ZWoKVxuICAgICAgY29uc3QgYXV0b0V4cGxvcmVSZXN1bHQgPSBhd2FpdCB0aGlzLmRhdGFFeHBsb3Jlci5leHBsb3JlV2l0aFJlbGF0aW9ucyhwcm9wLndpdGgsIHtcbiAgICAgICAgc3RyYXRlZ3k6IFwicmFuZG9tXCIsXG4gICAgICAgIGxpbWl0OiAxMCxcbiAgICAgICAgaW5jbHVkZVJlbGF0aW9uczogdHJ1ZSxcbiAgICAgICAgbWF4RGVwdGg6IDMsXG4gICAgICB9KTtcbiAgICAgIGNvbnRleHQucmVmZXJlbmNlQ2FjaGUuc2V0KGF1dG9LZXksIGF1dG9FeHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcyk7XG5cbiAgICAgIC8vIOyhsO2ajO2VnCDrjbDsnbTthLDsmYAg6rSA6rOE65CcIOuqqOuToCDsl5Tti7Dti7DrpbwgdGFyZ2V0RGLsl5AgaW1wb3J0XG4gICAgICBpZiAoYXV0b0V4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgYXdhaXQgdGhpcy5pbXBvcnRFeHBsb3JlUmVzdWx0KGF1dG9FeHBsb3JlUmVzdWx0LCBjb250ZXh0KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBhdXRvQ2FuZGlkYXRlcyA9IGNvbnRleHQucmVmZXJlbmNlQ2FjaGUuZ2V0KGF1dG9LZXkpO1xuICAgIGlmIChhdXRvQ2FuZGlkYXRlcyAmJiBhdXRvQ2FuZGlkYXRlcy5sZW5ndGggPiAwKSB7XG4gICAgICAvLyDrnpzrjaTtlZjqsowg7ZWY64KYIOyEoO2DnVxuICAgICAgY29uc3Qgc2VsZWN0ZWQgPSBhdXRvQ2FuZGlkYXRlc1tNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBhdXRvQ2FuZGlkYXRlcy5sZW5ndGgpXTtcbiAgICAgIHJldHVybiBzZWxlY3RlZC5pZCBhcyBudW1iZXI7XG4gICAgfVxuXG4gICAgLy8g7LC47KGwIOuNsOydtO2EsOqwgCDsl4bsnLzrqbQgbnVsbCDrsJjtmZggKG51bGxhYmxl7J24IOqyveyasClcbiAgICBpZiAocHJvcC5udWxsYWJsZSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgLy8gbnVsbGFibGXsnbQg7JWE64uI6rOgIOuNsOydtO2EsOuPhCDsl4bsnLzrqbQg7JeQ65+sXG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEZpeHR1cmVHZW5lcmF0b3I6ICR7ZW50aXR5LmlkfS4ke3Byb3AubmFtZX3sl5Ag7ZWE7JqU7ZWcICR7cHJvcC53aXRofSDrjbDsnbTthLDqsIAg7JeG7Iq164uI64ukLiBgICtcbiAgICAgICAgYOuovOyggCAke3Byb3Aud2l0aH3rpbwg7IOd7ISx7ZWY6rGw64KYIGNvbmUuZGF0YVNvdXJjZeulvCDshKTsoJXtlZjshLjsmpQuYCxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4cGxvcmVXaXRoUmVsYXRpb25zIOqysOqzvOulvCB0YXJnZXREYuyXkCBpbXBvcnRcbiAgICpcbiAgICog6rSA6rOEIOyytOyduOydhCDrlLDrnbzqsIQg6rKw6rO8KG1haW4gKyByZWxhdGVkKeulvCDrqqjrkZAgaW1wb3J07ZWp64uI64ukLlxuICAgKiDsnZjsobTshLEg7Iic7ISc64qUIEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVz6rCAIOyekOuPmeycvOuhnCDsspjrpqztlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGltcG9ydEV4cGxvcmVSZXN1bHQoXG4gICAgZXhwbG9yZVJlc3VsdDogRXhwbG9yZVdpdGhSZWxhdGlvbnNSZXN1bHQsXG4gICAgY29udGV4dDogR2VuZXJhdG9yQ29udGV4dCxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgYWxsRml4dHVyZVJlY29yZHM6IEZpeHR1cmVSZWNvcmRbXSA9IFtdO1xuXG4gICAgLy8gMS4gUmVsYXRlZCBlbnRpdGllcyBpbXBvcnQgKENvbXBhbnksIERlcGFydG1lbnQg65OxKVxuICAgIGZvciAoY29uc3QgW2VudGl0eUlkLCByZWNvcmRzXSBvZiBleHBsb3JlUmVzdWx0LnJlbGF0ZWQuZW50cmllcygpKSB7XG4gICAgICBjb25zdCBlbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KGVudGl0eUlkKTtcbiAgICAgIGNvbnN0IHJlY29yZHNUb0ltcG9ydDogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSA9IFtdO1xuXG4gICAgICAhaXNUZXN0KCkgJiZcbiAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgY2hhbGsuY3lhbihgSW1wb3J0aW5nIHJlbGF0ZWQgZW50aXR5OiAke2VudGl0eUlkfSAoJHtyZWNvcmRzLmxlbmd0aH0gcmVjb3JkcylgKSxcbiAgICAgICAgKTtcblxuICAgICAgZm9yIChjb25zdCByZWNvcmQgb2YgcmVjb3Jkcykge1xuICAgICAgICBjb25zdCByZWNvcmRLZXkgPSBgJHtlbnRpdHlJZH0jJHtyZWNvcmQuaWR9YDtcbiAgICAgICAgaWYgKCFjb250ZXh0LmltcG9ydGVkUmVjb3Jkcy5oYXMocmVjb3JkS2V5KSkge1xuICAgICAgICAgIHJlY29yZHNUb0ltcG9ydC5wdXNoKHJlY29yZCk7XG4gICAgICAgICAgY29udGV4dC5pbXBvcnRlZFJlY29yZHMuYWRkKHJlY29yZEtleSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKHJlY29yZHNUb0ltcG9ydC5sZW5ndGggPiAwKSB7XG4gICAgICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIHJlY29yZHNUb0ltcG9ydCkge1xuICAgICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgIGNoYWxrLmdyYXkoXG4gICAgICAgICAgICAgICAgYCAgLSBQcm9jZXNzaW5nICR7ZW50aXR5SWR9IHJlY29yZDpgLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KHJlY29yZCkuc2xpY2UoMCwgMTAwKSxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgY29uc3QgZml4dHVyZVJlY29yZHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5jcmVhdGVGaXh0dXJlUmVjb3JkKFxuICAgICAgICAgICAgZW50aXR5LFxuICAgICAgICAgICAgcmVjb3JkIGFzIHsgaWQ6IG51bWJlciB8IHN0cmluZzsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwgfSxcbiAgICAgICAgICAgIHsgX2RiOiB0aGlzLnNvdXJjZURiLCBzaW5nbGVSZWNvcmQ6IHRydWUgfSxcbiAgICAgICAgICApO1xuICAgICAgICAgIGFsbEZpeHR1cmVSZWNvcmRzLnB1c2goLi4uZml4dHVyZVJlY29yZHMpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gMi4gTWFpbiBlbnRpdHkgaW1wb3J0IChFbXBsb3llZSDrk7EpXG4gICAgY29uc3QgbWFpbkVudGl0eSA9IHRoaXMuZW50aXR5TWFuYWdlci5nZXQoZXhwbG9yZVJlc3VsdC5tYWluLmVudGl0eUlkKTtcbiAgICBjb25zdCBtYWluUmVjb3Jkc1RvSW1wb3J0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPltdID0gW107XG5cbiAgICAhaXNUZXN0KCkgJiZcbiAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICBjaGFsay5jeWFuKFxuICAgICAgICAgIGBJbXBvcnRpbmcgbWFpbiBlbnRpdHk6ICR7ZXhwbG9yZVJlc3VsdC5tYWluLmVudGl0eUlkfSAoJHtleHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcy5sZW5ndGh9IHJlY29yZHMpYCxcbiAgICAgICAgKSxcbiAgICAgICk7XG5cbiAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiBleHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcykge1xuICAgICAgY29uc3QgcmVjb3JkS2V5ID0gYCR7ZXhwbG9yZVJlc3VsdC5tYWluLmVudGl0eUlkfSMke3JlY29yZC5pZH1gO1xuICAgICAgaWYgKCFjb250ZXh0LmltcG9ydGVkUmVjb3Jkcy5oYXMocmVjb3JkS2V5KSkge1xuICAgICAgICBtYWluUmVjb3Jkc1RvSW1wb3J0LnB1c2gocmVjb3JkKTtcbiAgICAgICAgY29udGV4dC5pbXBvcnRlZFJlY29yZHMuYWRkKHJlY29yZEtleSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG1haW5SZWNvcmRzVG9JbXBvcnQubGVuZ3RoID4gMCkge1xuICAgICAgZm9yIChjb25zdCByZWNvcmQgb2YgbWFpblJlY29yZHNUb0ltcG9ydCkge1xuICAgICAgICAhaXNUZXN0KCkgJiZcbiAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgIGNoYWxrLmdyYXkoXG4gICAgICAgICAgICAgIGAgIC0gUHJvY2Vzc2luZyAke2V4cGxvcmVSZXN1bHQubWFpbi5lbnRpdHlJZH0gcmVjb3JkOmAsXG4gICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KHJlY29yZCkuc2xpY2UoMCwgMTAwKSxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKTtcbiAgICAgICAgY29uc3QgZml4dHVyZVJlY29yZHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5jcmVhdGVGaXh0dXJlUmVjb3JkKFxuICAgICAgICAgIG1haW5FbnRpdHksXG4gICAgICAgICAgcmVjb3JkIGFzIHsgaWQ6IG51bWJlciB8IHN0cmluZzsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwgfSxcbiAgICAgICAgICB7IF9kYjogdGhpcy5zb3VyY2VEYiwgc2luZ2xlUmVjb3JkOiB0cnVlIH0sXG4gICAgICAgICk7XG4gICAgICAgIGFsbEZpeHR1cmVSZWNvcmRzLnB1c2goLi4uZml4dHVyZVJlY29yZHMpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIDMuIOuqqOuToCBmaXh0dXJl66W8IO2VnCDrsojsl5Ag7IK97J6FICjsnZjsobTshLEg7Iic7IScIOyekOuPmSDsspjrpqwpXG4gICAgaWYgKGFsbEZpeHR1cmVSZWNvcmRzLmxlbmd0aCA+IDApIHtcbiAgICAgIGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVzKHRoaXMudGFyZ2V0RGJOYW1lLCBhbGxGaXh0dXJlUmVjb3Jkcyk7XG5cbiAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICBjaGFsay5ncmVlbihcbiAgICAgICAgICAgIGBBdXRvLWltcG9ydGVkICR7ZXhwbG9yZVJlc3VsdC5tYWluLmVudGl0eUlkfSB3aXRoIHJlbGF0aW9uczogYCArXG4gICAgICAgICAgICAgIGAke2V4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzLmxlbmd0aH0gbWFpbiArICR7ZXhwbG9yZVJlc3VsdC5yZWxhdGVkLnNpemV9IHJlbGF0ZWQgZW50aXRpZXNgLFxuICAgICAgICAgICksXG4gICAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIGZpeHR1cmVHZW5lcmF0b3Ig7Iuk7ZaJIChGYWtlci5qc+unjCDsp4Dsm5ApXG4gICAqXG4gICAqIGZha2VyLiog7ZiV7Iud7J2YIO2RnO2YhOyLneydhCDslYjsoITtlZjqsowg7YyM7Iux7ZWY7JesIOyLpO2Wie2VqeuLiOuLpC5cbiAgICog7JiIOiBcImZha2VyLmludGVybmV0LmVtYWlsKClcIiDihpIgZmFrZXIuaW50ZXJuZXQuZW1haWwoKVxuICAgKiDsmIg6IFwiZmFrZXIubG9yZW0ud29yZHMoMylcIiDihpIgZmFrZXIubG9yZW0ud29yZHMoMylcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZXhlY3V0ZUdlbmVyYXRvcihcbiAgICBnZW5lcmF0b3I6IHN0cmluZyxcbiAgICBwcm9wOiBFbnRpdHlQcm9wLFxuICAgIGVudGl0eTogRW50aXR5LFxuICApOiBQcm9taXNlPHVua25vd24+IHtcbiAgICAvLyBGYWtlci5qcyDtkZztmITsi53rp4wg7KeA7JuQXG4gICAgaWYgKGdlbmVyYXRvci5zdGFydHNXaXRoKFwiZmFrZXIuXCIpKSB7XG4gICAgICAvLyB1c2VybmFtZeydtOuCmCBuYW1lIO2VhOuTnOuKlCDtlZzqta3slrQgZmFrZXIg7IKs7JqpXG4gICAgICBjb25zdCBpc05hbWVGaWVsZCA9IHByb3AubmFtZSA9PT0gXCJ1c2VybmFtZVwiIHx8IHByb3AubmFtZSA9PT0gXCJuYW1lXCI7XG4gICAgICBjb25zdCBmYWtlck1vZHVsZSA9IGF3YWl0IGltcG9ydChcIkBmYWtlci1qcy9mYWtlclwiKTtcbiAgICAgIGNvbnN0IGZha2VyID0gaXNOYW1lRmllbGQgPyBmYWtlck1vZHVsZS5mYWtlcktPIDogZmFrZXJNb2R1bGUuZmFrZXI7XG4gICAgICBjb25zdCBleHByID0gZ2VuZXJhdG9yLnNsaWNlKDYpOyAvLyBcImZha2VyLlwiIOygnOqxsFxuXG4gICAgICB0cnkge1xuICAgICAgICAvLyDtlajsiJgg6rK966Gc7JmAIOyduOyekCDtjIzsi7FcbiAgICAgICAgY29uc3QgbWF0Y2ggPSBleHByLm1hdGNoKC9eKFtcXHcuXSspKD86XFwoKC4qPylcXCkpPyQvKTtcbiAgICAgICAgaWYgKCFtYXRjaCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBGaXh0dXJlR2VuZXJhdG9yOiBJbnZhbGlkIGZha2VyIGV4cHJlc3Npb24gZm9yICR7cHJvcC5uYW1lfTogJHtnZW5lcmF0b3J9YCxcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgWywgcGF0aCwgYXJnc1N0cl0gPSBtYXRjaDtcbiAgICAgICAgY29uc3QgcGFydHMgPSBwYXRoLnNwbGl0KFwiLlwiKTtcblxuICAgICAgICAvLyBmYWtlciDqsJ3ssrTsl5DshJwg7ZWo7IiYIOywvuq4sFxuICAgICAgICBsZXQgZm46IHVua25vd24gPSBmYWtlcjtcbiAgICAgICAgZm9yIChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgICAgICAgaWYgKHR5cGVvZiBmbiA9PT0gXCJvYmplY3RcIiAmJiBmbiAhPT0gbnVsbCAmJiBwYXJ0IGluIGZuKSB7XG4gICAgICAgICAgICBmbiA9IChmbiBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPilbcGFydF07XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRml4dHVyZUdlbmVyYXRvcjogSW52YWxpZCBmYWtlciBwYXRoIGZvciAke3Byb3AubmFtZX06IGZha2VyLiR7cGF0aH1gKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyDtlajsiJjqsIAg7JWE64uI66m0IOyXkOufrFxuICAgICAgICBpZiAodHlwZW9mIGZuICE9PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZpeHR1cmVHZW5lcmF0b3I6IGZha2VyLiR7cGF0aH0gaXMgbm90IGEgZnVuY3Rpb24gKGZvciAke3Byb3AubmFtZX0pYCk7XG4gICAgICAgIH1cblxuICAgICAgICBsZXQgYXJnczogdW5rbm93bltdID0gW107XG4gICAgICAgIGlmIChhcmdzU3RyPy50cmltKCkpIHtcbiAgICAgICAgICBhcmdzID0gdGhpcy5wYXJzZUdlbmVyYXRvckFyZ3MoYXJnc1N0ciwgcHJvcC5uYW1lKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBmbiguLi5hcmdzKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgY2hhbGsueWVsbG93KFxuICAgICAgICAgICAgICBgRmFpbGVkIHRvIGV4ZWN1dGUgZ2VuZXJhdG9yIFwiJHtnZW5lcmF0b3J9XCIgZm9yICR7cHJvcC5uYW1lfSwgZmFsbGluZyBiYWNrIHRvIGRlZmF1bHQ6YCxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgICBlcnJvcixcbiAgICAgICAgICApO1xuICAgICAgICByZXR1cm4gdGhpcy5nZW5lcmF0ZURlZmF1bHRWYWx1ZShwcm9wLCBlbnRpdHkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIGZha2VyIOydtOyZuOydmCDtkZztmITsi53snYAg7KeA7JuQ7ZWY7KeAIOyViuydjFxuICAgICFpc1Rlc3QoKSAmJlxuICAgICAgY29uc29sZS5sb2coXG4gICAgICAgIGNoYWxrLnllbGxvdyhcbiAgICAgICAgICBgVW5zdXBwb3J0ZWQgZ2VuZXJhdG9yIGV4cHJlc3Npb24gZm9yICR7cHJvcC5uYW1lfTogJHtnZW5lcmF0b3J9LiBPbmx5IGZha2VyLiogZXhwcmVzc2lvbnMgYXJlIHN1cHBvcnRlZC4gVXNpbmcgZGVmYXVsdCB2YWx1ZS5gLFxuICAgICAgICApLFxuICAgICAgKTtcbiAgICByZXR1cm4gdGhpcy5nZW5lcmF0ZURlZmF1bHRWYWx1ZShwcm9wLCBlbnRpdHkpO1xuICB9XG5cbiAgLyoqXG4gICAqIO2VhOuTnOydmCDtg4DsnoXqs7wg7J2066aE7J2EIOu2hOyEne2VmOyXrCDsoIHsoIjtlZwg6riw67O46rCS7J2EIOyDneyEse2VqeuLiOuLpC5cbiAgICpcbiAgICog7Jqw7ISg7Iic7JyEOlxuICAgKiAxLiDtlYTrk5zrqoUg7Yyo7YS0IOunpOy5rSAoc2FsYXJ5LCBidWRnZXQg65OxIOydmOuvuOyeiOuKlCDrjbDsnbTthLApXG4gICAqIDIuIO2KueyImCDsvIDsnbTsiqQgKERlcGFydG1lbnQgbmFtZSDrk7Eg64+E66mU7J24IOyngOyLnSlcbiAgICogMy4g67Cw7Je0IO2DgOyehSAoSlNPTiDrsLDsl7QpXG4gICAqIDQuIEVudW0g7YOA7J6FXG4gICAqIDUuIO2DgOyeheuzhCDquLDrs7jqsJJcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVEZWZhdWx0VmFsdWUocHJvcDogRW50aXR5UHJvcCwgZW50aXR5PzogRW50aXR5KTogUHJvbWlzZTx1bmtub3duPiB7XG4gICAgY29uc3QgZmFrZXJNb2R1bGUgPSBhd2FpdCBpbXBvcnQoXCJAZmFrZXItanMvZmFrZXJcIik7XG4gICAgY29uc3QgZmFrZXIgPSBmYWtlck1vZHVsZS5mYWtlcjtcbiAgICBjb25zdCBmYWtlcktPID0gZmFrZXJNb2R1bGUuZmFrZXJLTztcbiAgICBjb25zdCBmYWtlckpBID0gZmFrZXJNb2R1bGUuZmFrZXJKQTtcblxuICAgIGNvbnN0IGxvY2FsZUZha2VyID0gdGhpcy5sb2NhbGUgPT09IFwia29cIiA/IGZha2VyS08gOiB0aGlzLmxvY2FsZSA9PT0gXCJqYVwiID8gZmFrZXJKQSA6IGZha2VyO1xuXG4gICAgLyoqXG4gICAgICogMS4gRW50aXR5LXNwZWNpZmljIO2KueyImCDsvIDsnbTsiqTrpbwg66i87KCAIOyymOumrO2VqeuLiOuLpC5cbiAgICAgKiBmaWVsZF9wYXR0ZXJuc+uztOuLpCDsmrDshKDtlZjsl6wsIO2KueyglSDsl5Tti7Dti7DsnZgg7ZWE65Oc7JeQIOuPhOuplOyduOyXkCDrp57ripQg6rCS7J2EIOyDneyEse2VqeuLiOuLpC5cbiAgICAgKiDsmIg6IERlcGFydG1lbnQubmFtZSDihpIg7ZWc6rWt7Ja0IOu2gOyEnOuqhSAo7IKs656MIOydtOumhOydtCDslYTri5gpXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBEZXBhcnRtZW50IG5hbWXsnYAg7ZWc6rWt7Ja0IOu2gOyEnOuqhSDrqqnroZ3sl5DshJwg7ISg7YOd7ZWp64uI64ukLlxuICAgICAqIOqzoOycoOyEseydhCDsnITtlbQgNzAlIO2ZleuloOuhnCBwcmVmaXgvc3VmZml466W8IOy2lOqwgO2VqeuLiOuLpC5cbiAgICAgKi9cbiAgICBpZiAoZW50aXR5Py5pZCA9PT0gXCJEZXBhcnRtZW50XCIgJiYgcHJvcC5uYW1lID09PSBcIm5hbWVcIikge1xuICAgICAgY29uc3QgZGVwYXJ0bWVudHMgPSBbXG4gICAgICAgIFwi6rCc67Cc7YyAXCIsXG4gICAgICAgIFwi6riw7ZqN7YyAXCIsXG4gICAgICAgIFwi66eI7LyA7YyF7YyAXCIsXG4gICAgICAgIFwi7JiB7JeF7YyAXCIsXG4gICAgICAgIFwi7J247IKs7YyAXCIsXG4gICAgICAgIFwi7LSd66y07YyAXCIsXG4gICAgICAgIFwi7J6s66y07YyAXCIsXG4gICAgICAgIFwi7ZqM6rOE7YyAXCIsXG4gICAgICAgIFwi67KV66y07YyAXCIsXG4gICAgICAgIFwi65SU7J6Q7J247YyAXCIsXG4gICAgICAgIFwiSVTtjIBcIixcbiAgICAgICAgXCLqs6DqsJ3sp4Dsm5DtjIBcIixcbiAgICAgICAgXCLtkojsp4jqtIDrpqztjIBcIixcbiAgICAgICAgXCLsl7DqtazqsJzrsJztjIBcIixcbiAgICAgICAgXCLsg53sgrDtjIBcIixcbiAgICAgICAgXCLqtazrp6TtjIBcIixcbiAgICAgICAgXCLrrLzrpZjtjIBcIixcbiAgICAgIF07XG4gICAgICBjb25zdCBwcmVmaXhlcyA9IFtcIuyLoOq3nFwiLCBcIu2Gte2VqVwiLCBcIuyghOuetVwiLCBcIuq4gOuhnOuyjFwiLCBcIuuUlOyngO2EuFwiLCBcIu2VteyLrFwiXTtcbiAgICAgIGNvbnN0IHN1ZmZpeGVzID0gW1wiMe2MgFwiLCBcIjLtjIBcIiwgXCIz7YyAXCIsIFwiQe2MgFwiLCBcIkLtjIBcIiwgXCLrs7jrtoBcIiwgXCLshLzthLBcIiwgXCLqt7jro7lcIl07XG5cbiAgICAgIGNvbnN0IGRlcHQgPSBmYWtlci5oZWxwZXJzLmFycmF5RWxlbWVudChkZXBhcnRtZW50cyk7XG5cbiAgICAgIGNvbnN0IHJhbmRvbSA9IE1hdGgucmFuZG9tKCk7XG4gICAgICBpZiAocmFuZG9tID4gMC43KSB7XG4gICAgICAgIGNvbnN0IHByZWZpeCA9IGZha2VyLmhlbHBlcnMuYXJyYXlFbGVtZW50KHByZWZpeGVzKTtcbiAgICAgICAgcmV0dXJuIGAke3ByZWZpeH0gJHtkZXB0fWA7XG4gICAgICB9XG4gICAgICBpZiAocmFuZG9tID4gMC40KSB7XG4gICAgICAgIGNvbnN0IHN1ZmZpeCA9IGZha2VyLmhlbHBlcnMuYXJyYXlFbGVtZW50KHN1ZmZpeGVzKTtcbiAgICAgICAgcmV0dXJuIGAke2RlcHR9ICR7c3VmZml4fWA7XG4gICAgICB9XG4gICAgICByZXR1cm4gZGVwdDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiAyLiDtlYTrk5zrqoXsl5DshJwg7J2Y66+466W8IOy2lOuhoO2VmOyXrCDtmITsi6TsoIHsnbgg642w7J207YSw66W8IOyDneyEse2VqeuLiOuLpC5cbiAgICAgKiDsmIg6IHNhbGFyeSDihpIgMzBNfjE1ME0gKO2VnOq1rSDsl7DrtIkg67KU7JyEKVxuICAgICAqICAgICBidWRnZXQg4oaSIDEwTX41MDBNICjtlITroZzsoJ3tirgg7JiI7IKwIOuylOychClcbiAgICAgKi9cbiAgICBjb25zdCBsb2NhbGVNYXBwaW5ncyA9IHRoaXMubWFwcGluZ3NbdGhpcy5sb2NhbGVdIHx8IHRoaXMubWFwcGluZ3MuZW47XG4gICAgY29uc3Qgbm9ybWFsaXplZE5hbWUgPSBwcm9wLm5hbWUudG9Mb3dlckNhc2UoKS5yZXBsYWNlKC9fL2csIFwiXCIpO1xuXG4gICAgZm9yIChjb25zdCBbcGF0dGVybiwgY29uZmlnXSBvZiBPYmplY3QuZW50cmllcyhsb2NhbGVNYXBwaW5ncy5maWVsZF9wYXR0ZXJucykpIHtcbiAgICAgIGlmIChub3JtYWxpemVkTmFtZS5pbmNsdWRlcyhwYXR0ZXJuLnRvTG93ZXJDYXNlKCkpKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZXhlY3V0ZUZha2VyRXhwcmVzc2lvbihjb25maWcuZmFrZXIsIHByb3ApO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgIGNoYWxrLnllbGxvdyhcbiAgICAgICAgICAgICAgICBgRmFpbGVkIHRvIGV4ZWN1dGUgZmllbGQgcGF0dGVybiBcIiR7cGF0dGVybn1cIiBmb3IgJHtwcm9wLm5hbWV9LCBmYWxsaW5nIGJhY2s6YCxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgZXJyb3IsXG4gICAgICAgICAgICApO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogMy4gSlNPTiDtg4DsnoXsnbTrqbTshJwg67Cw7Je07J24IOqyveyasCAoU29uYW11RmlsZVtdLCBzdHJpbmdbXSDrk7EpXG4gICAgICog7ZWE65Oc66qFIO2MqO2EtOydhCDrs7Tqs6Ag7KCB7KCI7ZWcIOuwsOyXtCDrjbDsnbTthLDrpbwg7IOd7ISx7ZWp64uI64ukLlxuICAgICAqL1xuICAgIGlmIChwcm9wLnR5cGUgPT09IFwianNvblwiICYmIFwiaWRcIiBpbiBwcm9wICYmIHByb3AuaWQpIHtcbiAgICAgIGlmIChwcm9wLmlkLmVuZHNXaXRoKFwiW11cIikpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2VuZXJhdGVBcnJheVZhbHVlKHByb3AsIGVudGl0eSwgZmFrZXIsIGxvY2FsZUZha2VyKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogNC4gRW51bSDtg4DsnoXsnYAg7KCV7J2Y65CcIOqwkiDspJEg7ZWY64KY66W8IOuenOuNpCDshKDtg53tlanri4jri6QgKi9cbiAgICBpZiAocHJvcC50eXBlID09PSBcImVudW1cIikge1xuICAgICAgbGV0IGVudW1WYWx1ZXM6IHN0cmluZ1tdID0gW107XG5cbiAgICAgIGlmIChcImVudW1cIiBpbiBwcm9wICYmIEFycmF5LmlzQXJyYXkocHJvcC5lbnVtKSAmJiBwcm9wLmVudW0ubGVuZ3RoID4gMCkge1xuICAgICAgICBlbnVtVmFsdWVzID0gcHJvcC5lbnVtO1xuICAgICAgfSBlbHNlIGlmIChcImlkXCIgaW4gcHJvcCAmJiBwcm9wLmlkICYmIGVudGl0eT8uZW51bUxhYmVscz8uW3Byb3AuaWRdKSB7XG4gICAgICAgIGVudW1WYWx1ZXMgPSBPYmplY3Qua2V5cyhlbnRpdHkuZW51bUxhYmVsc1twcm9wLmlkXSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbnVtVmFsdWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIGZha2VyLmhlbHBlcnMuYXJyYXlFbGVtZW50KGVudW1WYWx1ZXMpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHByb3AubnVsbGFibGUgPyBudWxsIDogXCJVTktOT1dOXCI7XG4gICAgfVxuXG4gICAgaWYgKHByb3AudHlwZSA9PT0gXCJlbnVtW11cIikge1xuICAgICAgbGV0IGVudW1WYWx1ZXM6IHN0cmluZ1tdID0gW107XG5cbiAgICAgIGlmIChcImVudW1cIiBpbiBwcm9wICYmIEFycmF5LmlzQXJyYXkocHJvcC5lbnVtKSAmJiBwcm9wLmVudW0ubGVuZ3RoID4gMCkge1xuICAgICAgICBlbnVtVmFsdWVzID0gcHJvcC5lbnVtO1xuICAgICAgfSBlbHNlIGlmIChcImlkXCIgaW4gcHJvcCAmJiBwcm9wLmlkICYmIGVudGl0eT8uZW51bUxhYmVscz8uW3Byb3AuaWRdKSB7XG4gICAgICAgIGVudW1WYWx1ZXMgPSBPYmplY3Qua2V5cyhlbnRpdHkuZW51bUxhYmVsc1twcm9wLmlkXSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbnVtVmFsdWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIFtmYWtlci5oZWxwZXJzLmFycmF5RWxlbWVudChlbnVtVmFsdWVzKV07XG4gICAgICB9XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogNS4gVmVjdG9yIO2DgOyeheydgCDtmITsnqwg7KeA7JuQ7ZWY7KeAIOyViuycvOuvgOuhnCBudWxs7J2EIOuwmO2ZmO2VqeuLiOuLpC5cbiAgICAgKiDtlqXtm4QgQUkgZW1iZWRkaW5nIOyDneyEsSDquLDriqUg7LaU6rCAIOyLnCDqtaztmIQg7JiI7KCV7J6F64uI64ukLlxuICAgICAqL1xuICAgIGlmIChwcm9wLnR5cGUgPT09IFwidmVjdG9yXCIgfHwgcHJvcC50eXBlID09PSBcInZlY3RvcltdXCIgfHwgcHJvcC50eXBlID09PSBcInRzdmVjdG9yXCIpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIC8qKiA2LiDtg4DsnoXrs4Qg6riw67O4IEZha2VyIO2RnO2YhOyLneydhCDsi6Ttlontlanri4jri6QgKi9cbiAgICBjb25zdCB0eXBlRGVmYXVsdCA9IGxvY2FsZU1hcHBpbmdzLnR5cGVfZGVmYXVsdHNbcHJvcC50eXBlXTtcbiAgICBpZiAodHlwZURlZmF1bHQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmV4ZWN1dGVGYWtlckV4cHJlc3Npb24odHlwZURlZmF1bHQuZmFrZXIsIHByb3ApO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgIWlzVGVzdCgpICYmXG4gICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBjaGFsay55ZWxsb3coYEZhaWxlZCB0byBleGVjdXRlIHR5cGUgZGVmYXVsdCBmb3IgJHtwcm9wLnR5cGV9LCB1c2luZyBmYWxsYmFjazpgLCBlcnJvciksXG4gICAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogNy4g66ek7ZWR65CY7KeAIOyViuydgCDtg4DsnoXsnYAg6riw67O4IEZha2VyIO2VqOyImOuhnCDsspjrpqztlanri4jri6QgKi9cbiAgICBzd2l0Y2ggKHByb3AudHlwZSkge1xuICAgICAgY2FzZSBcInN0cmluZ1wiOlxuICAgICAgY2FzZSBcInN0cmluZ1tdXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5sb3JlbS53b3JkcygzKTtcbiAgICAgIGNhc2UgXCJpbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5udW1iZXIuaW50KHsgbWluOiAxLCBtYXg6IDEwMDAgfSk7XG4gICAgICBjYXNlIFwiaW50ZWdlcltdXCI6XG4gICAgICAgIHJldHVybiBbZmFrZXIubnVtYmVyLmludCh7IG1pbjogMSwgbWF4OiAxMDAwIH0pXTtcbiAgICAgIGNhc2UgXCJiaWdJbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5udW1iZXIuYmlnSW50KHsgbWluOiAxbiwgbWF4OiAxMDAwbiB9KTtcbiAgICAgIGNhc2UgXCJiaWdJbnRlZ2VyW11cIjpcbiAgICAgICAgcmV0dXJuIFtmYWtlci5udW1iZXIuYmlnSW50KHsgbWluOiAxbiwgbWF4OiAxMDAwbiB9KV07XG4gICAgICBjYXNlIFwibnVtYmVyXCI6XG4gICAgICBjYXNlIFwibnVtZXJpY1wiOlxuICAgICAgICByZXR1cm4gZmFrZXIubnVtYmVyLmZsb2F0KHsgbWluOiAwLCBtYXg6IDEwMDAgfSk7XG4gICAgICBjYXNlIFwibnVtYmVyW11cIjpcbiAgICAgIGNhc2UgXCJudW1lcmljW11cIjpcbiAgICAgICAgcmV0dXJuIFtmYWtlci5udW1iZXIuZmxvYXQoeyBtaW46IDAsIG1heDogMTAwMCB9KV07XG4gICAgICBjYXNlIFwiYm9vbGVhblwiOlxuICAgICAgICByZXR1cm4gZmFrZXIuZGF0YXR5cGUuYm9vbGVhbigpO1xuICAgICAgY2FzZSBcImJvb2xlYW5bXVwiOlxuICAgICAgICByZXR1cm4gW2Zha2VyLmRhdGF0eXBlLmJvb2xlYW4oKV07XG4gICAgICBjYXNlIFwiZGF0ZVwiOlxuICAgICAgY2FzZSBcImRhdGVbXVwiOlxuICAgICAgICByZXR1cm4gZmFrZXIuZGF0ZS5wYXN0KCk7XG4gICAgICBjYXNlIFwianNvblwiOlxuICAgICAgICByZXR1cm4ge307XG4gICAgICBjYXNlIFwidXVpZFwiOlxuICAgICAgY2FzZSBcInV1aWRbXVwiOlxuICAgICAgICByZXR1cm4gZmFrZXIuc3RyaW5nLnV1aWQoKTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiDrsLDsl7Qg7YOA7J6F7J2YIOqwkuydhCDsg53shLHtlanri4jri6QuXG4gICAqXG4gICAqIO2DgOyehSBJROyZgCDtlYTrk5zrqoUg7Yyo7YS07J2EIOu2hOyEne2VmOyXrCDsoIHsoIjtlZwg67Cw7Je0IOuNsOydtO2EsOulvCDsg53shLHtlanri4jri6QuXG4gICAqIOyYiDogaW1hZ2VfdXJscyDihpIgW3t1cmwsIG5hbWUsIG1pbWVfdHlwZX0sIC4uLl1cbiAgICogICAgIHRhZ19pZHMg4oaSIFsxLCAyMywgNDVdXG4gICAqL1xuICBwcml2YXRlIGdlbmVyYXRlQXJyYXlWYWx1ZShcbiAgICBwcm9wOiBFbnRpdHlQcm9wLFxuICAgIF9lbnRpdHk6IEVudGl0eSB8IHVuZGVmaW5lZCxcbiAgICBmYWtlcjogdHlwZW9mIGltcG9ydChcIkBmYWtlci1qcy9mYWtlclwiKS5mYWtlcixcbiAgICBfbG9jYWxlRmFrZXI6IHR5cGVvZiBpbXBvcnQoXCJAZmFrZXItanMvZmFrZXJcIikuZmFrZXIsXG4gICk6IHVua25vd25bXSB7XG4gICAgY29uc3QgY291bnQgPSBmYWtlci5udW1iZXIuaW50KHsgbWluOiAxLCBtYXg6IDMgfSk7XG5cbiAgICAvKiogU29uYW11RmlsZVtd7J2AIFNvbmFtdSDrgrTsnqUg7YOA7J6F7Jy866GcIOq1rOyhsOqwgCDsoJXtlbTsoLgg7J6I7Iq164uI64ukICovXG4gICAgaWYgKFwiaWRcIiBpbiBwcm9wICYmIHByb3AuaWQgPT09IFwiU29uYW11RmlsZVtdXCIpIHtcbiAgICAgIHJldHVybiBBcnJheS5mcm9tKHsgbGVuZ3RoOiBjb3VudCB9LCAoKSA9PiAoe1xuICAgICAgICB1cmw6IGZha2VyLmltYWdlLnVybCgpLFxuICAgICAgICBuYW1lOiBmYWtlci5zeXN0ZW0uZmlsZU5hbWUoKSxcbiAgICAgICAgbWltZV90eXBlOiBmYWtlci5oZWxwZXJzLmFycmF5RWxlbWVudChbXG4gICAgICAgICAgXCJpbWFnZS9qcGVnXCIsXG4gICAgICAgICAgXCJpbWFnZS9wbmdcIixcbiAgICAgICAgICBcImltYWdlL2dpZlwiLFxuICAgICAgICAgIFwiYXBwbGljYXRpb24vcGRmXCIsXG4gICAgICAgIF0pLFxuICAgICAgfSkpO1xuICAgIH1cblxuICAgIC8qKiDtlYTrk5zrqoXsl5DshJwg67Cw7Je07J2YIOyaqeuPhOulvCDstpTroaDtlanri4jri6QgKi9cbiAgICBjb25zdCBub3JtYWxpemVkTmFtZSA9IHByb3AubmFtZS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL18vZywgXCJcIik7XG5cbiAgICBpZiAobm9ybWFsaXplZE5hbWUuaW5jbHVkZXMoXCJ1cmxcIikgfHwgbm9ybWFsaXplZE5hbWUuaW5jbHVkZXMoXCJpbWFnZVwiKSkge1xuICAgICAgcmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGNvdW50IH0sICgpID0+IGZha2VyLmludGVybmV0LnVybCgpKTtcbiAgICB9XG5cbiAgICBpZiAobm9ybWFsaXplZE5hbWUuaW5jbHVkZXMoXCJpZFwiKSAmJiBub3JtYWxpemVkTmFtZS5lbmRzV2l0aChcInNcIikpIHtcbiAgICAgIHJldHVybiBBcnJheS5mcm9tKHsgbGVuZ3RoOiBjb3VudCB9LCAoKSA9PiBmYWtlci5udW1iZXIuaW50KHsgbWluOiAxLCBtYXg6IDEwMCB9KSk7XG4gICAgfVxuXG4gICAgaWYgKG5vcm1hbGl6ZWROYW1lLmluY2x1ZGVzKFwidGFnXCIpIHx8IG5vcm1hbGl6ZWROYW1lLmluY2x1ZGVzKFwibmFtZVwiKSkge1xuICAgICAgcmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGNvdW50IH0sICgpID0+IGZha2VyLmxvcmVtLndvcmQoKSk7XG4gICAgfVxuXG4gICAgLyoqIO2MqO2EtCDrp6Tsua3rkJjsp4Ag7JWK7Jy866m0IOu5iCDrsLDsl7TsnYQg67CY7ZmY7ZWp64uI64ukICovXG4gICAgcmV0dXJuIFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIEpTT04g66ek7ZWR7J2YIEZha2VyIO2RnO2YhOyLneydhCDtjIzsi7HtlZjsl6wg7Iuk7ZaJ7ZWp64uI64ukLlxuICAgKlxuICAgKiDtkZztmITsi50g7JiI7IucOlxuICAgKiAtIFwiZmFrZXIuaW50ZXJuZXQuZW1haWwoKVwiIOKGkiDsnbjsnpAg7JeG7J2MXG4gICAqIC0gXCJmYWtlci5udW1iZXIuaW50KHsgbWluOiAxLCBtYXg6IDEwMCB9KVwiIOKGkiBKU09OIOyduOyekFxuICAgKiAtIFwie31cIiDihpIg66as7YSw65+0IOqwkiAoSlNPTi5wYXJzZSlcbiAgICpcbiAgICogZmFrZXJLTywgZmFrZXJKQeuPhCDsp4Dsm5DtlZjsl6wg64uk6rWt7Ja0IOuNsOydtO2EsOulvCDsg53shLHtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGV4ZWN1dGVGYWtlckV4cHJlc3Npb24oZXhwcmVzc2lvbjogc3RyaW5nLCBwcm9wOiBFbnRpdHlQcm9wKTogUHJvbWlzZTx1bmtub3duPiB7XG4gICAgY29uc3QgZmFrZXJNb2R1bGUgPSBhd2FpdCBpbXBvcnQoXCJAZmFrZXItanMvZmFrZXJcIik7XG4gICAgY29uc3QgZmFrZXIgPSBmYWtlck1vZHVsZS5mYWtlcjtcbiAgICBjb25zdCBmYWtlcktPID0gZmFrZXJNb2R1bGUuZmFrZXJLTztcbiAgICBjb25zdCBmYWtlckpBID0gZmFrZXJNb2R1bGUuZmFrZXJKQTtcblxuICAgIC8qKiBGYWtlciDtkZztmITsi53snbQg7JWE64uMIOumrO2EsOuftCDqsJLsnYAgSlNPTuycvOuhnCDtjIzsi7Htlanri4jri6QgKi9cbiAgICBpZiAoIWV4cHJlc3Npb24uc3RhcnRzV2l0aChcImZha2VyXCIpKSB7XG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShleHByZXNzaW9uKTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4gZXhwcmVzc2lvbjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKiog7ZGc7ZiE7Iud7JeQ7IScIEZha2VyIOqwneyytOyZgCDqsr3roZzrpbwg7LaU7Lac7ZWp64uI64ukICovXG4gICAgY29uc3QgbWF0Y2ggPSBleHByZXNzaW9uLm1hdGNoKC9eKGZha2VyfGZha2VyS098ZmFrZXJKQSlcXC4oLio/KSQvKTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZmFrZXIgZXhwcmVzc2lvbjogJHtleHByZXNzaW9ufWApO1xuICAgIH1cblxuICAgIGNvbnN0IFssIGZha2VyTmFtZSwgZXhwcl0gPSBtYXRjaDtcbiAgICBjb25zdCBzZWxlY3RlZEZha2VyID1cbiAgICAgIGZha2VyTmFtZSA9PT0gXCJmYWtlcktPXCIgPyBmYWtlcktPIDogZmFrZXJOYW1lID09PSBcImZha2VySkFcIiA/IGZha2VySkEgOiBmYWtlcjtcblxuICAgIGNvbnN0IGZ1bmNNYXRjaCA9IGV4cHIubWF0Y2goL14oW1xcdy5dKykoPzpcXCgoLio/KVxcKSk/JC8pO1xuICAgIGlmICghZnVuY01hdGNoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZmFrZXIgZXhwcmVzc2lvbiBmb3IgJHtwcm9wLm5hbWV9OiAke2V4cHJlc3Npb259YCk7XG4gICAgfVxuXG4gICAgY29uc3QgWywgcGF0aCwgYXJnc1N0cl0gPSBmdW5jTWF0Y2g7XG4gICAgY29uc3QgcGFydHMgPSBwYXRoLnNwbGl0KFwiLlwiKTtcblxuICAgIC8qKiDsoJAg7ZGc6riw67KVKGRvdCBub3RhdGlvbinsnLzroZwgRmFrZXIg7ZWo7IiY66W8IOywvuyVhOqwkeuLiOuLpCAqL1xuICAgIGxldCBmbjogdW5rbm93biA9IHNlbGVjdGVkRmFrZXI7XG4gICAgZm9yIChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgICBpZiAodHlwZW9mIGZuID09PSBcIm9iamVjdFwiICYmIGZuICE9PSBudWxsICYmIHBhcnQgaW4gZm4pIHtcbiAgICAgICAgZm4gPSAoZm4gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pW3BhcnRdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGZha2VyIHBhdGggZm9yICR7cHJvcC5uYW1lfTogJHtmYWtlck5hbWV9LiR7cGF0aH1gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGZuICE9PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgJHtmYWtlck5hbWV9LiR7cGF0aH0gaXMgbm90IGEgZnVuY3Rpb24gKGZvciAke3Byb3AubmFtZX0pYCk7XG4gICAgfVxuXG4gICAgbGV0IGFyZ3M6IHVua25vd25bXSA9IFtdO1xuICAgIGlmIChhcmdzU3RyPy50cmltKCkpIHtcbiAgICAgIGFyZ3MgPSB0aGlzLnBhcnNlR2VuZXJhdG9yQXJncyhhcmdzU3RyLCBwcm9wLm5hbWUpO1xuICAgIH1cblxuICAgIHJldHVybiBmbiguLi5hcmdzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBmaXh0dXJlSGludOulvCBMTE3sl5Dqsowg7KCE64us7ZWY7JesIO2YhOyLpOyggeyduCDthYzsiqTtirgg642w7J207YSw66W8IOyDneyEse2VqeuLiOuLpC5cbiAgICpcbiAgICogZmFrZXIuanProZzripQg7IOd7ISx7ZWY6riwIOyWtOugpOyatCDrs7XsnqHtlZwg7YWN7Iqk7Yq4KOyekOq4sOyGjOqwnCwg7ISk66qF66y4IOuTsSnrpbxcbiAgICogTExN7J2EIO2ZnOyaqe2VmOyXrCDsg53shLHtlanri4jri6QuIOuPmeydvO2VnCBoaW507JeQIOuMgO2VnCDspJHrs7Ug7Zi47Lac7J2EIOuwqeyngO2VmOq4sCDsnITtlbRcbiAgICog7LqQ7Iux7J2EIOq4sOuzuOycvOuhnCDsp4Dsm5Dtlanri4jri6QgKExMTSBBUEkg67mE7JqpIOygiOqwkCkuXG4gICAqXG4gICAqIGFpIO2MqO2CpOyngOuKlCBkeW5hbWljIGltcG9ydOuhnCDrtojrn6zsmKTrr4DroZwsIHVzZUxMTeydtCBmYWxzZeyduCDqsr3smrBcbiAgICog7J2Y7KG07ISx7J20IOyEpOy5mOuQmOyngCDslYrslYTrj4QgZml4dHVyZSDsg53shLHsnbQg7KCV7IOBIOuPmeyeke2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVXaXRoTExNKFxuICAgIGZpeHR1cmVIaW50OiBzdHJpbmcsXG4gICAgcHJvcDogRW50aXR5UHJvcCxcbiAgICBlbnRpdHk6IEVudGl0eSxcbiAgICByb3dLZXk/OiBzdHJpbmcsXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIC8vIHJvd0tleeqwgCDsnojsnLzrqbQgcm93IOuLqOychCDsg53shLEg7KCE6561IOyCrOyaqVxuICAgIGlmIChyb3dLZXkpIHtcbiAgICAgIGNvbnN0IHJvd0NhY2hlS2V5ID0gYCR7cm93S2V5fToke3Byb3AubmFtZX1gO1xuXG4gICAgICAvLyDsnbTrr7gg7J20IHJvd+yXkCDrjIDtlZwgTExNIO2YuOy2nOydtCDsmYTro4zrkJwg6rK97JqwIOy6kOyLnOyXkOyEnCDrsJTroZwg67CY7ZmYXG4gICAgICBpZiAodGhpcy5sbG1DYWNoZS5oYXMocm93Q2FjaGVLZXkpKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmxsbUNhY2hlLmdldChyb3dDYWNoZUtleSk7XG4gICAgICB9XG5cbiAgICAgIC8vIOyDiCByb3c6IExMTSDrjIDsg4EgcHJvcCDsoITssrTrpbwg7ZWcIOuyiOyXkCDsg53shLFcbiAgICAgIGNvbnN0IGxsbVByb3BzID0gZW50aXR5LnByb3BzLmZpbHRlcigocCkgPT4ge1xuICAgICAgICBpZiAoaXNSZWxhdGlvblByb3AocCkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgaWYgKHAuY29uZT8uZml4dHVyZUdlbmVyYXRvcikgcmV0dXJuIGZhbHNlO1xuICAgICAgICBpZiAocC5uYW1lID09PSBcImlkXCIgJiYgcC5jb25lPy5maXh0dXJlU3RyYXRlZ3kgPT09IFwic2VxdWVuY2VcIikgcmV0dXJuIGZhbHNlO1xuICAgICAgICByZXR1cm4gISFwLmNvbmU/Lm5vdGU7XG4gICAgICB9KTtcblxuICAgICAgLy8gbGxtUHJvcHPqsIAg67mE7Ja07J6I7Jy866m0IOuLqOydvCDtlYTrk5wg67Cp7Iud7Jy866GcIGZhbGxiYWNrXG4gICAgICBpZiAobGxtUHJvcHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgYFtGaXh0dXJlR2VuZXJhdG9yXSBsbG1Qcm9wcyBpcyBlbXB0eSBmb3IgJHtlbnRpdHkuaWR9LiR7cHJvcC5uYW1lfSwgdXNpbmcgc2luZ2xlIGZpZWxkIGZhbGxiYWNrYCxcbiAgICAgICAgICApO1xuICAgICAgICByZXR1cm4gdGhpcy5nZW5lcmF0ZVNpbmdsZVdpdGhMTE0oZml4dHVyZUhpbnQsIHByb3AsIGVudGl0eSk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGFwaUtleSA9IHRoaXMuZ2V0QXBpS2V5KCk7XG4gICAgICBjb25zdCB7IGNyZWF0ZUFudGhyb3BpYyB9ID0gYXdhaXQgaW1wb3J0KFwiQGFpLXNkay9hbnRocm9waWNcIik7XG4gICAgICBjb25zdCB7IGdlbmVyYXRlVGV4dCB9ID0gYXdhaXQgaW1wb3J0KFwiYWlcIik7XG5cbiAgICAgIGNvbnN0IHJvd1Jlc3BvbnNlID0gYXdhaXQgZ2VuZXJhdGVUZXh0KHtcbiAgICAgICAgbW9kZWw6IGNyZWF0ZUFudGhyb3BpYyh7IGFwaUtleSB9KSh0aGlzLm9wdGlvbnMubGxtTW9kZWwgfHwgXCJjbGF1ZGUtc29ubmV0LTQtNlwiKSxcbiAgICAgICAgcHJvbXB0OiB0aGlzLmJ1aWxkUm93TExNUHJvbXB0KGxsbVByb3BzLCBlbnRpdHkpLFxuICAgICAgfSk7XG4gICAgICBpZiAoIXJvd1Jlc3BvbnNlIHx8IHR5cGVvZiByb3dSZXNwb25zZS50ZXh0ICE9PSBcInN0cmluZ1wiKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkludmFsaWQgTExNIHJlc3BvbnNlXCIpO1xuICAgICAgfVxuXG4gICAgICAvLyDsnZHri7XsnYQg7YyM7Iux7ZWY7JesIOqwgSDtlYTrk5zsl5Ag64yA7ZWcIOqysOqzvOulvCDsupDsi5zsl5Ag7KCA7J6lXG4gICAgICBjb25zdCByb3dSZXN1bHQgPSB0aGlzLnBhcnNlUm93TExNUmVzcG9uc2Uocm93UmVzcG9uc2UudGV4dCwgbGxtUHJvcHMpO1xuICAgICAgZm9yIChjb25zdCBbZmllbGROYW1lLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocm93UmVzdWx0KSkge1xuICAgICAgICB0aGlzLmxsbUNhY2hlLnNldChgJHtyb3dLZXl9OiR7ZmllbGROYW1lfWAsIHZhbHVlKTtcbiAgICAgIH1cblxuICAgICAgLy8g7JqU7LKt7ZWcIO2VhOuTnOydmCDqsJIg67CY7ZmYICjsl4bsnLzrqbQg64uo7J28IO2VhOuTnCBmYWxsYmFjaylcbiAgICAgIGlmICh0aGlzLmxsbUNhY2hlLmhhcyhyb3dDYWNoZUtleSkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubGxtQ2FjaGUuZ2V0KHJvd0NhY2hlS2V5KTtcbiAgICAgIH1cblxuICAgICAgLy8g66eM7JW9IHJvdyDsnZHri7Xsl5Ag7J20IO2VhOuTnOqwgCDriITrnb3rkJwg6rK97JqwIOuLqOydvCDtlYTrk5wgZmFsbGJhY2tcbiAgICAgIHJldHVybiB0aGlzLmdlbmVyYXRlU2luZ2xlV2l0aExMTShmaXh0dXJlSGludCwgcHJvcCwgZW50aXR5KTtcbiAgICB9XG5cbiAgICAvLyByb3dLZXnqsIAg7JeG7Jy866m0IOq4sOyhtCDri6jsnbwg7ZWE65OcIOuwqeyLnVxuICAgIHJldHVybiB0aGlzLmdlbmVyYXRlU2luZ2xlV2l0aExMTShmaXh0dXJlSGludCwgcHJvcCwgZW50aXR5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiDri6jsnbwg7ZWE65Oc66W8IExMTeycvOuhnCDsg53shLHtlanri4jri6QgKHJvd0tleSDsl4bsnYQg65WMIGZhbGxiYWNr7JqpKVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBnZW5lcmF0ZVNpbmdsZVdpdGhMTE0oXG4gICAgZml4dHVyZUhpbnQ6IHN0cmluZyxcbiAgICBwcm9wOiBFbnRpdHlQcm9wLFxuICAgIGVudGl0eTogRW50aXR5LFxuICApOiBQcm9taXNlPHVua25vd24+IHtcbiAgICBjb25zdCBjYWNoZUtleSA9IGAke2VudGl0eS5pZH06JHtwcm9wLm5hbWV9OiR7Zml4dHVyZUhpbnR9YDtcbiAgICBpZiAodGhpcy5vcHRpb25zLmVuYWJsZUxMTUNhY2hlICYmIHRoaXMubGxtQ2FjaGUuaGFzKGNhY2hlS2V5KSkge1xuICAgICAgcmV0dXJuIHRoaXMubGxtQ2FjaGUuZ2V0KGNhY2hlS2V5KTtcbiAgICB9XG5cbiAgICBjb25zdCBhcGlLZXkgPSB0aGlzLmdldEFwaUtleSgpO1xuICAgIGNvbnN0IHsgY3JlYXRlQW50aHJvcGljIH0gPSBhd2FpdCBpbXBvcnQoXCJAYWktc2RrL2FudGhyb3BpY1wiKTtcbiAgICBjb25zdCB7IGdlbmVyYXRlVGV4dCB9ID0gYXdhaXQgaW1wb3J0KFwiYWlcIik7XG5cbiAgICBjb25zdCBzaW5nbGVSZXNwb25zZSA9IGF3YWl0IGdlbmVyYXRlVGV4dCh7XG4gICAgICBtb2RlbDogY3JlYXRlQW50aHJvcGljKHsgYXBpS2V5IH0pKHRoaXMub3B0aW9ucy5sbG1Nb2RlbCB8fCBcImNsYXVkZS1zb25uZXQtNC02XCIpLFxuICAgICAgcHJvbXB0OiB0aGlzLmJ1aWxkTExNUHJvbXB0KGZpeHR1cmVIaW50LCBwcm9wLCBlbnRpdHkpLFxuICAgIH0pO1xuICAgIGlmICghc2luZ2xlUmVzcG9uc2UgfHwgdHlwZW9mIHNpbmdsZVJlc3BvbnNlLnRleHQgIT09IFwic3RyaW5nXCIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkludmFsaWQgTExNIHJlc3BvbnNlXCIpO1xuICAgIH1cblxuICAgIGNvbnN0IHZhbHVlID0gdGhpcy5wYXJzZUxMTVJlc3BvbnNlKHNpbmdsZVJlc3BvbnNlLnRleHQsIHByb3AudHlwZSk7XG4gICAgaWYgKHRoaXMub3B0aW9ucy5lbmFibGVMTE1DYWNoZSkge1xuICAgICAgdGhpcy5sbG1DYWNoZS5zZXQoY2FjaGVLZXksIHZhbHVlKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cblxuICAvKipcbiAgICogcm93IOyghOyytOulvCDtlZwg67KI7JeQIOyDneyEse2VmOuKlCBMTE0g7ZSE66Gs7ZSE7Yq466W8IOunjOuTreuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYnVpbGRSb3dMTE1Qcm9tcHQocHJvcHM6IEVudGl0eVByb3BbXSwgZW50aXR5OiBFbnRpdHkpOiBzdHJpbmcge1xuICAgIGNvbnN0IGxvY2FsZSA9IHRoaXMub3B0aW9ucy5sb2NhbGUgfHwgXCJrb1wiO1xuICAgIGNvbnN0IGxhbmd1YWdlID0gbG9jYWxlID09PSBcImtvXCIgPyBcIktvcmVhblwiIDogbG9jYWxlID09PSBcImphXCIgPyBcIkphcGFuZXNlXCIgOiBcIkVuZ2xpc2hcIjtcblxuICAgIGNvbnN0IGZpZWxkRGVzY3JpcHRpb25zID0gcHJvcHNcbiAgICAgIC5tYXAoKHApID0+IHtcbiAgICAgICAgbGV0IGRlc2MgPSBgLSAke3AubmFtZX0gKCR7cC50eXBlfSk6ICR7cC5jb25lPy5ub3RlID8/IFwiXCJ9YDtcbiAgICAgICAgaWYgKFxuICAgICAgICAgIChwLnR5cGUgPT09IFwiZW51bVwiIHx8IHAudHlwZSA9PT0gXCJlbnVtW11cIikgJiZcbiAgICAgICAgICBcImlkXCIgaW4gcCAmJlxuICAgICAgICAgIHAuaWQgJiZcbiAgICAgICAgICBlbnRpdHkuZW51bUxhYmVscz8uW3AuaWRdXG4gICAgICAgICkge1xuICAgICAgICAgIGNvbnN0IHZhbHVlcyA9IE9iamVjdC5rZXlzKGVudGl0eS5lbnVtTGFiZWxzW3AuaWRdKS5qb2luKFwiLCBcIik7XG4gICAgICAgICAgZGVzYyArPSBgIFthbGxvd2VkIHZhbHVlczogJHt2YWx1ZXN9XWA7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRlc2M7XG4gICAgICB9KVxuICAgICAgLmpvaW4oXCJcXG5cIik7XG5cbiAgICAvLyBMTE0g64yA7IOB7J20IOyVhOuLjCBwcm9w65Ok64+EIOunpeudveycvOuhnCDsoJzqs7UgKHJlbGF0aW9uIOygnOyZuClcbiAgICBjb25zdCBvdGhlclByb3BzID0gZW50aXR5LnByb3BzXG4gICAgICAuZmlsdGVyKFxuICAgICAgICAocCkgPT5cbiAgICAgICAgICAhcHJvcHMuaW5jbHVkZXMocCkgJiZcbiAgICAgICAgICAhaXNSZWxhdGlvblByb3AocCkgJiZcbiAgICAgICAgICBwLm5hbWUgIT09IFwiaWRcIiAmJlxuICAgICAgICAgICEoXCJ2aXJ0dWFsXCIgaW4gcCAmJiBwLnZpcnR1YWwpLFxuICAgICAgKVxuICAgICAgLm1hcCgocCkgPT4ge1xuICAgICAgICBsZXQgZGVzYyA9IGAtICR7cC5uYW1lfSAoJHtwLnR5cGV9KWA7XG4gICAgICAgIGlmIChwLmNvbmU/Lm5vdGUpIGRlc2MgKz0gYDogJHtwLmNvbmUubm90ZX1gO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgKHAudHlwZSA9PT0gXCJlbnVtXCIgfHwgcC50eXBlID09PSBcImVudW1bXVwiKSAmJlxuICAgICAgICAgIFwiaWRcIiBpbiBwICYmXG4gICAgICAgICAgcC5pZCAmJlxuICAgICAgICAgIGVudGl0eS5lbnVtTGFiZWxzPy5bcC5pZF1cbiAgICAgICAgKSB7XG4gICAgICAgICAgY29uc3QgdmFsdWVzID0gT2JqZWN0LmtleXMoZW50aXR5LmVudW1MYWJlbHNbcC5pZF0pLmpvaW4oXCIsIFwiKTtcbiAgICAgICAgICBkZXNjICs9IGAgW2FsbG93ZWQgdmFsdWVzOiAke3ZhbHVlc31dYDtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZGVzYztcbiAgICAgIH0pXG4gICAgICAuam9pbihcIlxcblwiKTtcbiAgICBjb25zdCBvdGhlclByb3BzQ29udGV4dCA9IG90aGVyUHJvcHNcbiAgICAgID8gYFxcblxcbk90aGVyIGZpZWxkcyBpbiB0aGlzIGVudGl0eSAoZm9yIGNvbnRleHQsIGRvIE5PVCBnZW5lcmF0ZSB0aGVzZSk6XFxuJHtvdGhlclByb3BzfWBcbiAgICAgIDogXCJcIjtcblxuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gcHJvcHMubWFwKChwKSA9PiBgICBcIiR7cC5uYW1lfVwiOiA8JHtwLnR5cGV9PmApLmpvaW4oXCIsXFxuXCIpO1xuXG4gICAgY29uc3QgZW50aXR5Q29udGV4dCA9IGVudGl0eS5jb25lPy5ub3RlID8gYFxcbkVudGl0eSBkZXNjcmlwdGlvbjogJHtlbnRpdHkuY29uZS5ub3RlfWAgOiBcIlwiO1xuXG4gICAgcmV0dXJuIGBHZW5lcmF0ZSB0ZXN0IGZpeHR1cmUgZGF0YSBmb3IgdGhlICR7ZW50aXR5LmlkfSBlbnRpdHkuIEFsbCBmaWVsZHMgbXVzdCBiZSBjb2hlcmVudCBhbmQgY29uc2lzdGVudCB3aXRoIGVhY2ggb3RoZXIuXG5cbkVudGl0eTogJHtlbnRpdHkuaWR9JHtlbnRpdHlDb250ZXh0fVxuTG9jYWxlOiAke2xvY2FsZX0gKHVzZSAke2xhbmd1YWdlfSBmb3IgdGV4dCBmaWVsZHMpXG5cbkZpZWxkcyB0byBnZW5lcmF0ZTpcbiR7ZmllbGREZXNjcmlwdGlvbnN9JHtvdGhlclByb3BzQ29udGV4dH1cblxuUnVsZXM6XG4tIEFsbCBmaWVsZHMgaW4gYSBzaW5nbGUgcm93IG11c3QgYmUgbG9naWNhbGx5IGNvbnNpc3RlbnQgKGUuZy4gbmFtZS9uYW1lX2VuL25hbWVfY24gc2hvdWxkIHJlcHJlc2VudCB0aGUgc2FtZSBwZXJzb24pXG4tIFJldHVybiBPTkxZIHZhbGlkIEpTT04sIG5vIG1hcmtkb3duIG9yIGV4cGxhbmF0aW9uXG4tIERhdGVzIGluIElTTyA4NjAxIGZvcm1hdFxuLSBVc2UgJHtsYW5ndWFnZX0gZm9yIHRleHQgdW5sZXNzIGZpZWxkIGRlc2NyaXB0aW9uIHNheXMgb3RoZXJ3aXNlXG5cblJldHVybiBleGFjdGx5IHRoaXMgSlNPTiBzaGFwZTpcbntcbiR7b3V0cHV0U2hhcGV9XG59YDtcbiAgfVxuXG4gIC8qKlxuICAgKiByb3cgTExNIOydkeuLteydhCDtjIzsi7HtlZjsl6wg7ZWE65Oc67OEIOqwkuycvOuhnCDrs4DtmZjtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIHBhcnNlUm93TExNUmVzcG9uc2UodGV4dDogc3RyaW5nLCBwcm9wczogRW50aXR5UHJvcFtdKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICAgIGNvbnN0IGpzb25UZXh0ID0gdGV4dFxuICAgICAgLnRyaW0oKVxuICAgICAgLnJlcGxhY2UoL15gYGBqc29uXFxzKi9pLCBcIlwiKVxuICAgICAgLnJlcGxhY2UoL2BgYFxccyokLywgXCJcIilcbiAgICAgIC50cmltKCk7XG5cbiAgICBsZXQgcGFyc2VkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmF3ID0gSlNPTi5wYXJzZShqc29uVGV4dCk7XG4gICAgICBpZiAodHlwZW9mIHJhdyAhPT0gXCJvYmplY3RcIiB8fCByYXcgPT09IG51bGwgfHwgQXJyYXkuaXNBcnJheShyYXcpKSB7XG4gICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgIGNvbnNvbGUud2FybihcIltGaXh0dXJlR2VuZXJhdG9yXSBSb3cgTExNIHJlc3BvbnNlIGlzIG5vdCBhIHBsYWluIG9iamVjdDpcIiwgdGV4dCk7XG4gICAgICAgIHJldHVybiB7fTtcbiAgICAgIH1cbiAgICAgIHBhcnNlZCA9IHJhdyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICB9IGNhdGNoIHtcbiAgICAgICFpc1Rlc3QoKSAmJiBjb25zb2xlLndhcm4oXCJbRml4dHVyZUdlbmVyYXRvcl0gRmFpbGVkIHRvIHBhcnNlIHJvdyBMTE0gcmVzcG9uc2U6XCIsIHRleHQpO1xuICAgICAgcmV0dXJuIHt9O1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IHByb3Agb2YgcHJvcHMpIHtcbiAgICAgIGlmIChwcm9wLm5hbWUgaW4gcGFyc2VkKSB7XG4gICAgICAgIHJlc3VsdFtwcm9wLm5hbWVdID0gdGhpcy5wYXJzZUxMTVJlc3BvbnNlKFN0cmluZyhwYXJzZWRbcHJvcC5uYW1lXSA/PyBcIlwiKSwgcHJvcC50eXBlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByaXZhdGUgYnVpbGRMTE1Qcm9tcHQoaGludDogc3RyaW5nLCBwcm9wOiBFbnRpdHlQcm9wLCBlbnRpdHk6IEVudGl0eSk6IHN0cmluZyB7XG4gICAgY29uc3QgbG9jYWxlID0gdGhpcy5vcHRpb25zLmxvY2FsZSB8fCBcImtvXCI7XG4gICAgY29uc3QgbGFuZ3VhZ2UgPSBsb2NhbGUgPT09IFwia29cIiA/IFwiS29yZWFuXCIgOiBsb2NhbGUgPT09IFwiamFcIiA/IFwiSmFwYW5lc2VcIiA6IFwiRW5nbGlzaFwiO1xuXG4gICAgY29uc3QgZW50aXR5Q29udGV4dCA9IGVudGl0eS5jb25lPy5ub3RlID8gYFxcbkVudGl0eSBjb250ZXh0OiAke2VudGl0eS5jb25lLm5vdGV9YCA6IFwiXCI7XG5cbiAgICBjb25zdCBvdGhlckZpZWxkcyA9IGVudGl0eS5wcm9wc1xuICAgICAgLmZpbHRlcigocCkgPT4gcC5uYW1lICE9PSBwcm9wLm5hbWUgJiYgIWlzUmVsYXRpb25Qcm9wKHApICYmIHAuY29uZT8ubm90ZSlcbiAgICAgIC5tYXAoKHApID0+IGAtICR7cC5uYW1lfSAoJHtwLnR5cGV9KTogJHtwLmNvbmU/Lm5vdGV9YClcbiAgICAgIC5qb2luKFwiXFxuXCIpO1xuICAgIGNvbnN0IG90aGVyRmllbGRzQ29udGV4dCA9IG90aGVyRmllbGRzXG4gICAgICA/IGBcXG5cXG5PdGhlciBmaWVsZHMgaW4gdGhpcyBlbnRpdHkgKGZvciBjb250ZXh0KTpcXG4ke290aGVyRmllbGRzfWBcbiAgICAgIDogXCJcIjtcblxuICAgIGxldCBwcm9tcHQgPSBgR2VuZXJhdGUgdGVzdCBkYXRhIGZvciAke2VudGl0eS5pZH0uJHtwcm9wLm5hbWV9ICh0eXBlOiAke3Byb3AudHlwZX0pJHtlbnRpdHlDb250ZXh0fSR7b3RoZXJGaWVsZHNDb250ZXh0fVxuXG5SZXF1aXJlbWVudDogJHtoaW50fVxuXG5SdWxlczpcbi0gUmV0dXJuIE9OTFkgdGhlIHZhbHVlLCBubyBleHBsYW5hdGlvbiBvciBtYXJrZG93blxuLSBVc2UgJHtsYW5ndWFnZX0gbGFuZ3VhZ2UgaWYgYXBwbGljYWJsZVxuLSBGb3JtYXQ6ICR7dGhpcy5nZXRFeHBlY3RlZEZvcm1hdChwcm9wLnR5cGUpfWA7XG5cbiAgICAvLyBlbnVtIO2DgOyeheyduCDqsr3smrAg6rCA64ql7ZWcIOqwkiDrqqnroZ0g7LaU6rCAXG4gICAgaWYgKHByb3AudHlwZSA9PT0gXCJlbnVtXCIgfHwgcHJvcC50eXBlID09PSBcImVudW1bXVwiKSB7XG4gICAgICBsZXQgZW51bVZhbHVlczogc3RyaW5nW10gPSBbXTtcblxuICAgICAgaWYgKFwiZW51bVwiIGluIHByb3AgJiYgQXJyYXkuaXNBcnJheShwcm9wLmVudW0pICYmIHByb3AuZW51bS5sZW5ndGggPiAwKSB7XG4gICAgICAgIGVudW1WYWx1ZXMgPSBwcm9wLmVudW07XG4gICAgICB9IGVsc2UgaWYgKFwiaWRcIiBpbiBwcm9wICYmIHByb3AuaWQgJiYgZW50aXR5Py5lbnVtTGFiZWxzPy5bcHJvcC5pZF0pIHtcbiAgICAgICAgZW51bVZhbHVlcyA9IE9iamVjdC5rZXlzKGVudGl0eS5lbnVtTGFiZWxzW3Byb3AuaWRdKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGVudW1WYWx1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgICBwcm9tcHQgKz0gYFxcbi0gSU1QT1JUQU5UOiBDaG9vc2UgT05MWSBmcm9tIHRoZXNlIGFsbG93ZWQgdmFsdWVzOiAke2VudW1WYWx1ZXMuam9pbihcIiwgXCIpfWA7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcHJvbXB0ICs9IGBcXG5cXG5FeGFtcGxlOiAke3RoaXMuZ2V0RXhhbXBsZUZvclR5cGUocHJvcC50eXBlLCBsb2NhbGUpfWA7XG5cbiAgICByZXR1cm4gcHJvbXB0O1xuICB9XG5cbiAgcHJpdmF0ZSBwYXJzZUxMTVJlc3BvbnNlKHRleHQ6IHN0cmluZywgcHJvcFR5cGU6IHN0cmluZyk6IHVua25vd24ge1xuICAgIGNvbnN0IGNsZWFuZWQgPSB0ZXh0LnRyaW0oKTtcblxuICAgIC8vIOuwsOyXtCDtg4DsnoUg7LKY66asXG4gICAgaWYgKHByb3BUeXBlLmVuZHNXaXRoKFwiW11cIikpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHBhcnNlZCA9IEpTT04ucGFyc2UoY2xlYW5lZCk7XG4gICAgICAgIGNvbnN0IGJhc2VUeXBlID0gcHJvcFR5cGUuc2xpY2UoMCwgLTIpOyAvLyBcImludGVnZXJbXVwiIC0+IFwiaW50ZWdlclwiXG5cbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkocGFyc2VkKSkge1xuICAgICAgICAgIHJldHVybiBwYXJzZWQubWFwKChpdGVtKSA9PiB7XG4gICAgICAgICAgICAvLyBudWxsL3VuZGVmaW5lZOuKlCDtg4DsnoXrs4Qg6riw67O46rCS7Jy866GcXG4gICAgICAgICAgICBpZiAoaXRlbSA9PT0gbnVsbCB8fCBpdGVtID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0RGVmYXVsdFZhbHVlRm9yVHlwZShiYXNlVHlwZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyDqsJ3ssrTripQgSlNPTi5zdHJpbmdpZnkg7ZuEIO2MjOyLsSAoanNvbiDtg4DsnoXsnbgg6rK97JqwKVxuICAgICAgICAgICAgaWYgKHR5cGVvZiBpdGVtID09PSBcIm9iamVjdFwiKSB7XG4gICAgICAgICAgICAgIHJldHVybiBiYXNlVHlwZSA9PT0gXCJqc29uXCJcbiAgICAgICAgICAgICAgICA/IGl0ZW1cbiAgICAgICAgICAgICAgICA6IHRoaXMucGFyc2VTY2FsYXJWYWx1ZShKU09OLnN0cmluZ2lmeShpdGVtKSwgYmFzZVR5cGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gcHJpbWl0aXZlIOqwkuydgCDrrLjsnpDsl7TroZwg67OA7ZmYIO2bhCDtjIzsi7FcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnBhcnNlU2NhbGFyVmFsdWUoU3RyaW5nKGl0ZW0pLCBiYXNlVHlwZSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyDri6jsnbwg6rCS7J20IOyYqCDqsr3smrAg67Cw7Je066GcIOqwkOyLuOq4sFxuICAgICAgICBpZiAocGFyc2VkID09PSBudWxsIHx8IHBhcnNlZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcmV0dXJuIFt0aGlzLmdldERlZmF1bHRWYWx1ZUZvclR5cGUoYmFzZVR5cGUpXTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gW3RoaXMucGFyc2VTY2FsYXJWYWx1ZShTdHJpbmcocGFyc2VkKSwgYmFzZVR5cGUpXTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMucGFyc2VTY2FsYXJWYWx1ZShjbGVhbmVkLCBwcm9wVHlwZSk7XG4gIH1cblxuICBwcml2YXRlIGdldERlZmF1bHRWYWx1ZUZvclR5cGUocHJvcFR5cGU6IHN0cmluZyk6IHVua25vd24ge1xuICAgIHN3aXRjaCAocHJvcFR5cGUpIHtcbiAgICAgIGNhc2UgXCJpbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiAwO1xuICAgICAgY2FzZSBcImJpZ0ludGVnZXJcIjpcbiAgICAgICAgcmV0dXJuIDBuO1xuICAgICAgY2FzZSBcImZsb2F0XCI6XG4gICAgICBjYXNlIFwibnVtYmVyXCI6XG4gICAgICBjYXNlIFwibnVtZXJpY1wiOlxuICAgICAgICByZXR1cm4gMDtcbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIGNhc2UgXCJkYXRlXCI6XG4gICAgICAgIHJldHVybiBuZXcgRGF0ZSgpO1xuICAgICAgY2FzZSBcImpzb25cIjpcbiAgICAgICAgcmV0dXJuIHt9O1xuICAgICAgY2FzZSBcInV1aWRcIjpcbiAgICAgICAgcmV0dXJuIFwiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwXCI7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gXCJcIjtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHBhcnNlU2NhbGFyVmFsdWUodGV4dDogc3RyaW5nLCBwcm9wVHlwZTogc3RyaW5nKTogdW5rbm93biB7XG4gICAgY29uc3QgY2xlYW5lZCA9IHRleHQudHJpbSgpO1xuXG4gICAgc3dpdGNoIChwcm9wVHlwZSkge1xuICAgICAgY2FzZSBcImludGVnZXJcIjoge1xuICAgICAgICBjb25zdCBudW0gPSBwYXJzZUludChjbGVhbmVkLCAxMCk7XG4gICAgICAgIHJldHVybiBOdW1iZXIuaXNOYU4obnVtKSA/IDAgOiBudW07XG4gICAgICB9XG4gICAgICBjYXNlIFwiYmlnSW50ZWdlclwiOiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmV0dXJuIEJpZ0ludChjbGVhbmVkKTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgcmV0dXJuIDBuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjYXNlIFwiZmxvYXRcIjpcbiAgICAgIGNhc2UgXCJudW1iZXJcIjpcbiAgICAgIGNhc2UgXCJudW1lcmljXCI6IHtcbiAgICAgICAgY29uc3QgbnVtID0gcGFyc2VGbG9hdChjbGVhbmVkKTtcbiAgICAgICAgcmV0dXJuIE51bWJlci5pc05hTihudW0pID8gMCA6IG51bTtcbiAgICAgIH1cbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBjbGVhbmVkLnRvTG93ZXJDYXNlKCkgPT09IFwidHJ1ZVwiO1xuICAgICAgY2FzZSBcImRhdGVcIjoge1xuICAgICAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoY2xlYW5lZCk7XG4gICAgICAgIHJldHVybiBOdW1iZXIuaXNOYU4oZGF0ZS5nZXRUaW1lKCkpID8gbmV3IERhdGUoKSA6IGRhdGU7XG4gICAgICB9XG4gICAgICBjYXNlIFwianNvblwiOlxuICAgICAgICB0cnkge1xuICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGNsZWFuZWQpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICByZXR1cm4gY2xlYW5lZDtcbiAgICAgICAgfVxuICAgICAgY2FzZSBcInV1aWRcIjpcbiAgICAgIGNhc2UgXCJlbnVtXCI6XG4gICAgICAgIHJldHVybiBjbGVhbmVkO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIGNsZWFuZWQ7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIGZha2VyIO2VqOyImCDsnbjsnpAg66y47J6Q7Je07J2EIO2MjOyLse2VmOyXrCDsnbjsnpAg67Cw7Je066GcIOuwmO2ZmO2VqeuLiOuLpC5cbiAgICpcbiAgICogM+uLqOqzhCDsoITrnrU6XG4gICAqIDEuIEpTT04g7KeB7KCRIO2MjOyLsSAo7ZGc7KSAIEpTT04g7ZGc7ZiE7IudKVxuICAgKiAyLiBKUyDqsJ3ssrQg66as7YSw65+0IOKGkiBKU09OIOuzgO2ZmCDtm4Qg7J6s7Iuc64+EIChzaW5nbGUgcXVvdGUsIHVucXVvdGVkIGtleSDsspjrpqwpXG4gICAqIDMuIOuLqOyInCDri6jsnbwg7J247J6QIO2PtOuwsSAo7Iir7J6QLCDrrLjsnpDsl7QpXG4gICAqL1xuICBwcml2YXRlIHBhcnNlR2VuZXJhdG9yQXJncyhhcmdzU3RyOiBzdHJpbmcsIHByb3BOYW1lOiBzdHJpbmcpOiB1bmtub3duW10ge1xuICAgIC8vIDEuIEpTT04g7KeB7KCRIO2MjOyLsVxuICAgIHRyeSB7XG4gICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKGBbJHthcmdzU3RyfV1gKSBhcyB1bmtub3duO1xuICAgICAgcmV0dXJuIEFycmF5LmlzQXJyYXkocGFyc2VkKSA/IHBhcnNlZCA6IFtwYXJzZWRdO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8g6rOE7IaNXG4gICAgfVxuXG4gICAgLy8gMi4gSlMg6rCd7LK0IOumrO2EsOuftCDihpIgSlNPTiDrs4DtmZgg7ZuEIOyerOyLnOuPhFxuICAgIHRyeSB7XG4gICAgICBjb25zdCBqc29uU3RyID0gdGhpcy5jb252ZXJ0SnNMaXRlcmFsVG9Kc29uKGFyZ3NTdHIpO1xuICAgICAgY29uc3QgcGFyc2VkID0gSlNPTi5wYXJzZShgWyR7anNvblN0cn1dYCkgYXMgdW5rbm93bjtcbiAgICAgIHJldHVybiBBcnJheS5pc0FycmF5KHBhcnNlZCkgPyBwYXJzZWQgOiBbcGFyc2VkXTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIOqzhOyGjVxuICAgIH1cblxuICAgIC8vIDMuIOuLqOyInCDri6jsnbwg7J247J6QIO2PtOuwsVxuICAgIGNvbnN0IHRyaW1tZWQgPSBhcmdzU3RyLnRyaW0oKTtcbiAgICBpZiAoIU51bWJlci5pc05hTihOdW1iZXIodHJpbW1lZCkpKSB7XG4gICAgICByZXR1cm4gW051bWJlcih0cmltbWVkKV07XG4gICAgfVxuICAgIGlmIChcbiAgICAgICh0cmltbWVkLnN0YXJ0c1dpdGgoJ1wiJykgJiYgdHJpbW1lZC5lbmRzV2l0aCgnXCInKSkgfHxcbiAgICAgICh0cmltbWVkLnN0YXJ0c1dpdGgoXCInXCIpICYmIHRyaW1tZWQuZW5kc1dpdGgoXCInXCIpKVxuICAgICkge1xuICAgICAgcmV0dXJuIFt0cmltbWVkLnNsaWNlKDEsIC0xKV07XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBGaXh0dXJlR2VuZXJhdG9yOiBDYW5ub3QgcGFyc2UgYXJndW1lbnRzIGZvciAke3Byb3BOYW1lfTogJHthcmdzU3RyfWApO1xuICB9XG5cbiAgLyoqXG4gICAqIEpTIOqwneyytCDrpqzthLDrn7TsnYQgSlNPTuycvOuhnCDrs4DtmZjtlanri4jri6QuXG4gICAqXG4gICAqIOuRkCDqsIDsp4Ag67OA7ZmYOlxuICAgKiAxLiBTaW5nbGUtcXVvdGVkIOusuOyekOyXtCDihpIgZG91YmxlLXF1b3RlZCAo7J207Iqk7LyA7J207ZSEIOyymOumrCDtj6ztlagpXG4gICAqIDIuIFVucXVvdGVkIOqwneyytCDtgqQg4oaSIGRvdWJsZS1xdW90ZWRcbiAgICovXG4gIHByaXZhdGUgY29udmVydEpzTGl0ZXJhbFRvSnNvbihpbnB1dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICAvLyAxLiAndmFsdWUnIOKGkiBcInZhbHVlXCIgKOuCtOu2gCBcIiDsnbTsiqTsvIDsnbTtlIQsIFxcJyDihpIgJylcbiAgICBjb25zdCB3aXRoRG91YmxlUXVvdGVzID0gaW5wdXQucmVwbGFjZShcbiAgICAgIC8nKFteJ1xcXFxdKig/OlxcXFwuW14nXFxcXF0qKSopJy9nLFxuICAgICAgKF8sIGNvbnRlbnQ6IHN0cmluZykgPT4gYFwiJHtjb250ZW50LnJlcGxhY2UoL1wiL2csICdcXFxcXCInKS5yZXBsYWNlKC9cXFxcJy9nLCBcIidcIil9XCJgLFxuICAgICk7XG5cbiAgICAvLyAyLiB7IGtleTog4oaSIHsgXCJrZXlcIjpcbiAgICByZXR1cm4gd2l0aERvdWJsZVF1b3Rlcy5yZXBsYWNlKC8oW3ssXVxccyopKFthLXpBLVpfJF1bXFx3JF0qKShcXHMqOikvZywgJyQxXCIkMlwiJDMnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTb25hbXUuc2VjcmV07J2EIOyasOyEoOycvOuhnCDtlZjqs6AsIOyXhuycvOuptCDtmZjqsr3rs4DsiJjsl5DshJwgQVBJIO2CpOulvCDsnb3sirXri4jri6QuXG4gICAqXG4gICAqIFNvbmFtdS5zZWNyZXTsnYAg7ZSE66Gc7KCd7Yq467OEIOyEpOyglShzb25hbXUuY29uZmlnLnRzKeydtOuvgOuhnCDrjZQg64aS7J2AIOyasOyEoOyInOychOulvCDqsIDsp4DrqbAsXG4gICAqIO2ZmOqyveuzgOyImOuKlCDqsJzrsJwg7ZmY6rK97J2064KYIENJL0NE7JeQ7IScIGZhbGxiYWNr7Jy866GcIOyCrOyaqeuQqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgZ2V0QXBpS2V5KCk6IHN0cmluZyB7XG4gICAgbGV0IGFwaUtleTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHsgU29uYW11IH0gPSByZXF1aXJlKFwiLi4vYXBpXCIpO1xuICAgICAgYXBpS2V5ID0gU29uYW11LnNlY3JldHM/LmFudGhyb3BpY19hcGlfa2V5O1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gU29uYW116rCAIOy0iOq4sO2ZlOuQmOyngCDslYrsnYAg6rK97JqwICjthYzsiqTtirgg7ZmY6rK9IOuTsSlcbiAgICB9XG5cbiAgICBpZiAoIWFwaUtleSkge1xuICAgICAgYXBpS2V5ID0gcHJvY2Vzcy5lbnYuQU5USFJPUElDX0FQSV9LRVk7XG4gICAgfVxuXG4gICAgaWYgKCFhcGlLZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgXCJBTlRIUk9QSUNfQVBJX0tFWSBub3QgZm91bmQuIFNldCBpdCBpbiBlbnZpcm9ubWVudCB2YXJpYWJsZXMgb3IgU29uYW11LnNlY3JldC5hbnRocm9waWNfYXBpX2tleVwiLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICByZXR1cm4gYXBpS2V5O1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRFeHBlY3RlZEZvcm1hdChwcm9wVHlwZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICAvLyDrsLDsl7Qg7YOA7J6FIOyymOumrFxuICAgIGlmIChwcm9wVHlwZS5lbmRzV2l0aChcIltdXCIpKSB7XG4gICAgICBjb25zdCBiYXNlVHlwZSA9IHByb3BUeXBlLnNsaWNlKDAsIC0yKTtcbiAgICAgIGNvbnN0IGJhc2VGb3JtYXQgPSB0aGlzLmdldFNjYWxhckZvcm1hdChiYXNlVHlwZSk7XG4gICAgICByZXR1cm4gYEpTT04gYXJyYXkgb2YgJHtiYXNlRm9ybWF0fSAoZS5nLiwgWyR7dGhpcy5nZXRFeGFtcGxlRm9yVHlwZShiYXNlVHlwZSwgXCJlblwiKX0sIC4uLl0pYDtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5nZXRTY2FsYXJGb3JtYXQocHJvcFR5cGUpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRTY2FsYXJGb3JtYXQocHJvcFR5cGU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgc3dpdGNoIChwcm9wVHlwZSkge1xuICAgICAgY2FzZSBcImludGVnZXJcIjpcbiAgICAgIGNhc2UgXCJiaWdJbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiBcImludGVnZXIgbnVtYmVyc1wiO1xuICAgICAgY2FzZSBcImZsb2F0XCI6XG4gICAgICBjYXNlIFwibnVtYmVyXCI6XG4gICAgICBjYXNlIFwibnVtZXJpY1wiOlxuICAgICAgICByZXR1cm4gXCJkZWNpbWFsIG51bWJlcnNcIjtcbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBcImJvb2xlYW5zICh0cnVlIG9yIGZhbHNlKVwiO1xuICAgICAgY2FzZSBcImRhdGVcIjpcbiAgICAgICAgcmV0dXJuIFwiSVNPIDg2MDEgZGF0ZSBzdHJpbmdzXCI7XG4gICAgICBjYXNlIFwianNvblwiOlxuICAgICAgICByZXR1cm4gXCJ2YWxpZCBKU09OIG9iamVjdCBvciBhcnJheVwiO1xuICAgICAgY2FzZSBcInV1aWRcIjpcbiAgICAgICAgcmV0dXJuIFwiVVVJRCBzdHJpbmdzXCI7XG4gICAgICBjYXNlIFwiZW51bVwiOlxuICAgICAgICByZXR1cm4gXCJvbmUgb2YgdGhlIGFsbG93ZWQgZW51bSB2YWx1ZXNcIjtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBcInBsYWluIHRleHQgc3RyaW5nc1wiO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZ2V0RXhhbXBsZUZvclR5cGUocHJvcFR5cGU6IHN0cmluZywgbG9jYWxlOiBMb2NhbGUpOiBzdHJpbmcge1xuICAgIC8vIOuwsOyXtCDtg4DsnoUg7LKY66asXG4gICAgaWYgKHByb3BUeXBlLmVuZHNXaXRoKFwiW11cIikpIHtcbiAgICAgIGNvbnN0IGJhc2VUeXBlID0gcHJvcFR5cGUuc2xpY2UoMCwgLTIpO1xuICAgICAgY29uc3QgYmFzZUV4YW1wbGUgPSB0aGlzLmdldFNjYWxhckV4YW1wbGUoYmFzZVR5cGUsIGxvY2FsZSk7XG4gICAgICByZXR1cm4gYFske2Jhc2VFeGFtcGxlfV1gO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLmdldFNjYWxhckV4YW1wbGUocHJvcFR5cGUsIGxvY2FsZSk7XG4gIH1cblxuICBwcml2YXRlIGdldFNjYWxhckV4YW1wbGUocHJvcFR5cGU6IHN0cmluZywgbG9jYWxlOiBMb2NhbGUpOiBzdHJpbmcge1xuICAgIGNvbnN0IGlzS29yZWFuID0gbG9jYWxlID09PSBcImtvXCI7XG5cbiAgICBzd2l0Y2ggKHByb3BUeXBlKSB7XG4gICAgICBjYXNlIFwiaW50ZWdlclwiOlxuICAgICAgY2FzZSBcImJpZ0ludGVnZXJcIjpcbiAgICAgICAgcmV0dXJuIFwiNDJcIjtcbiAgICAgIGNhc2UgXCJmbG9hdFwiOlxuICAgICAgY2FzZSBcIm51bWJlclwiOlxuICAgICAgY2FzZSBcIm51bWVyaWNcIjpcbiAgICAgICAgcmV0dXJuIFwiMy4xNFwiO1xuICAgICAgY2FzZSBcImJvb2xlYW5cIjpcbiAgICAgICAgcmV0dXJuIFwidHJ1ZVwiO1xuICAgICAgY2FzZSBcImRhdGVcIjpcbiAgICAgICAgcmV0dXJuIFwiMjAyNC0wMS0wMVwiO1xuICAgICAgY2FzZSBcImpzb25cIjpcbiAgICAgICAgcmV0dXJuICd7XCJrZXlcIjogXCJ2YWx1ZVwifSc7XG4gICAgICBjYXNlIFwidXVpZFwiOlxuICAgICAgICByZXR1cm4gXCI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDBcIjtcbiAgICAgIGNhc2UgXCJlbnVtXCI6XG4gICAgICAgIHJldHVybiBcIkVOVU1fVkFMVUVcIjtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBpc0tvcmVhbiA/IFwi7JWI64WV7ZWY7IS47JqUXCIgOiBcIkhlbGxvXCI7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIOydtOumhOydhCDsnbTrqZTsnbwg66Gc7LusIO2MjO2KuOyaqSDroZzrp4jrgpjsnbTspojrk5wg66y47J6Q7Je066GcIOuzgO2ZmO2VqeuLiOuLpC5cbiAgICpcbiAgICog7ZWc6riAIOydtOumhOydgCDstIjshLEt7KSR7ISxLeyiheyEsSDrtoTtlbQg7ZuEIOuhnOuniOuCmOydtOymiCDsspjrpqztlanri4jri6QuXG4gICAqIOyYgeusuCDsnbTrpoTsnYAg7IaM66y47J6Q66GcIOuzgO2ZmO2VmOqzoCDqs7XrsLHsnYQg7KCQKC4pXFx1YzczY+uhnCDsuZjtmZjtlanri4jri6QuXG4gICAqIOyYiDogXCLquYDssqDsiJhcIiDihpIgXCJjaGVvbHN1LmtpbVwiLCBcIkpvaG4gRG9lXCIg4oaSIFwiam9obi5kb2VcIlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByb21hbml6ZU5hbWUobmFtZTogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAvLyDtlZzquIAg7Y+s7ZWoIOyXrOu2gCDtmZXsnbhcbiAgICBpZiAoL1tcXHVBQzAwLVxcdUQ3QUZdLy50ZXN0KG5hbWUpKSB7XG4gICAgICByZXR1cm4gdGhpcy5yb21hbml6ZUtvcmVhbk5hbWUobmFtZSk7XG4gICAgfVxuICAgIC8vIOyYgeusuDog7IaM66y47J6QICsg7KCQIOq1rOu2hFxuICAgIHJldHVybiBuYW1lXG4gICAgICAudG9Mb3dlckNhc2UoKVxuICAgICAgLnJlcGxhY2UoL1xccysvZywgXCIuXCIpXG4gICAgICAucmVwbGFjZSgvW15hLXowLTkuXS9nLCBcIlwiKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlZzquIAg7J2066aE7J2EIOuhnOuniOuCmOydtOymiCDsspjrpqztlanri4jri6QuXG4gICAqXG4gICAqIOy0iOyEsS/spJHshLEv7KKF7ISxIOunpO2VkSDthYzsnbTruJTsnYQg7IKs7Jqp7ZWY7JesIO2VnOq4gOydhCDroZzrp4jsnpDroZwg67OA7ZmY7ZWp64uI64ukLlxuICAgKiDssqsg6riA7J6Q66W8IOyEseycvOuhnCDqsITso7ztlZjsl6wgXCLquYDssqDsiJhcIiDihpIgXCJjaGVvbHN1LmtpbVwiIO2Yle2DnOuhnCDstpzroKXtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIHJvbWFuaXplS29yZWFuTmFtZShuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IENIT1NFT05HID0gW1xuICAgICAgXCJnXCIsXG4gICAgICBcImtrXCIsXG4gICAgICBcIm5cIixcbiAgICAgIFwiZFwiLFxuICAgICAgXCJ0dFwiLFxuICAgICAgXCJyXCIsXG4gICAgICBcIm1cIixcbiAgICAgIFwiYlwiLFxuICAgICAgXCJwcFwiLFxuICAgICAgXCJzXCIsXG4gICAgICBcInNzXCIsXG4gICAgICBcIlwiLFxuICAgICAgXCJqXCIsXG4gICAgICBcImpqXCIsXG4gICAgICBcImNoXCIsXG4gICAgICBcImtcIixcbiAgICAgIFwidFwiLFxuICAgICAgXCJwXCIsXG4gICAgICBcImhcIixcbiAgICBdO1xuICAgIGNvbnN0IEpVTkdTRU9ORyA9IFtcbiAgICAgIFwiYVwiLFxuICAgICAgXCJhZVwiLFxuICAgICAgXCJ5YVwiLFxuICAgICAgXCJ5YWVcIixcbiAgICAgIFwiZW9cIixcbiAgICAgIFwiZVwiLFxuICAgICAgXCJ5ZW9cIixcbiAgICAgIFwieWVcIixcbiAgICAgIFwib1wiLFxuICAgICAgXCJ3YVwiLFxuICAgICAgXCJ3YWVcIixcbiAgICAgIFwib2VcIixcbiAgICAgIFwieW9cIixcbiAgICAgIFwidVwiLFxuICAgICAgXCJ3b1wiLFxuICAgICAgXCJ3ZVwiLFxuICAgICAgXCJ3aVwiLFxuICAgICAgXCJ5dVwiLFxuICAgICAgXCJldVwiLFxuICAgICAgXCJ1aVwiLFxuICAgICAgXCJpXCIsXG4gICAgXTtcbiAgICBjb25zdCBKT05HU0VPTkcgPSBbXG4gICAgICBcIlwiLFxuICAgICAgXCJrXCIsXG4gICAgICBcImtcIixcbiAgICAgIFwia1wiLFxuICAgICAgXCJuXCIsXG4gICAgICBcIm5cIixcbiAgICAgIFwiblwiLFxuICAgICAgXCJ0XCIsXG4gICAgICBcImxcIixcbiAgICAgIFwibFwiLFxuICAgICAgXCJsXCIsXG4gICAgICBcImxcIixcbiAgICAgIFwibFwiLFxuICAgICAgXCJsXCIsXG4gICAgICBcImxcIixcbiAgICAgIFwibFwiLFxuICAgICAgXCJtXCIsXG4gICAgICBcInBcIixcbiAgICAgIFwicFwiLFxuICAgICAgXCJ0XCIsXG4gICAgICBcInRcIixcbiAgICAgIFwibmdcIixcbiAgICAgIFwidFwiLFxuICAgICAgXCJ0XCIsXG4gICAgICBcImtcIixcbiAgICAgIFwidFwiLFxuICAgICAgXCJwXCIsXG4gICAgICBcInRcIixcbiAgICBdO1xuXG4gICAgY29uc3Qgcm9tYW5pemUgPSAoY2hhcjogc3RyaW5nKTogc3RyaW5nID0+IHtcbiAgICAgIGNvbnN0IGNvZGUgPSBjaGFyLmNoYXJDb2RlQXQoMCk7XG4gICAgICBpZiAoY29kZSA8IDB4YWMwMCB8fCBjb2RlID4gMHhkN2FmKSByZXR1cm4gY2hhcjtcbiAgICAgIGNvbnN0IG9mZnNldCA9IGNvZGUgLSAweGFjMDA7XG4gICAgICBjb25zdCBjaG8gPSBNYXRoLmZsb29yKG9mZnNldCAvIDU4OCk7XG4gICAgICBjb25zdCBqdW5nID0gTWF0aC5mbG9vcigob2Zmc2V0ICUgNTg4KSAvIDI4KTtcbiAgICAgIGNvbnN0IGpvbmcgPSBvZmZzZXQgJSAyODtcbiAgICAgIHJldHVybiBDSE9TRU9OR1tjaG9dICsgSlVOR1NFT05HW2p1bmddICsgSk9OR1NFT05HW2pvbmddO1xuICAgIH07XG5cbiAgICBjb25zdCBjaGFycyA9IFsuLi5uYW1lXTtcbiAgICAvLyDssqsg6riA7J6Q66W8IOyEsSjlp5Mp7Jy866GcIOqwhOyjvFxuICAgIGNvbnN0IGZhbWlseU5hbWUgPSByb21hbml6ZShjaGFyc1swXSk7XG4gICAgY29uc3QgZ2l2ZW5OYW1lID0gY2hhcnMuc2xpY2UoMSkubWFwKHJvbWFuaXplKS5qb2luKFwiXCIpO1xuXG4gICAgaWYgKGdpdmVuTmFtZSkge1xuICAgICAgcmV0dXJuIGAke2dpdmVuTmFtZX0uJHtmYW1pbHlOYW1lfWA7XG4gICAgfVxuICAgIHJldHVybiBmYW1pbHlOYW1lO1xuICB9XG5cbiAgLyoqXG4gICAqIExMTSDsupDsi5wg7Ya16rOE66W8IOuwmO2ZmO2VqeuLiOuLpC5cbiAgICovXG4gIGdldExMTUNhY2hlU3RhdHMoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNpemU6IHRoaXMubGxtQ2FjaGUuc2l6ZSxcbiAgICAgIGVuYWJsZWQ6IHRoaXMub3B0aW9ucy5lbmFibGVMTE1DYWNoZSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIExMTSDsupDsi5zrpbwg7LSI6riw7ZmU7ZWp64uI64ukLlxuICAgKi9cbiAgY2xlYXJMTE1DYWNoZSgpIHtcbiAgICB0aGlzLmxsbUNhY2hlLmNsZWFyKCk7XG4gIH1cblxuICAvKipcbiAgICog7Luo7YWN7Iqk7Yq4IOyDneyEsVxuICAgKi9cbiAgcHJpdmF0ZSBjcmVhdGVDb250ZXh0KCk6IEdlbmVyYXRvckNvbnRleHQge1xuICAgIHJldHVybiB7XG4gICAgICBmaXh0dXJlczogbmV3IE1hcCgpLFxuICAgICAgcmVmZXJlbmNlQ2FjaGU6IG5ldyBNYXAoKSxcbiAgICAgIGltcG9ydGVkUmVjb3JkczogbmV3IFNldCgpLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICog67Cw7LmYIOyDneyEsSDrsI8g7J6Q64+ZIOyggOyepVxuICAgKlxuICAgKiAxLiDqsIEgc3BlY+uzhOuhnCBmaXh0dXJlIOyDneyEsSAo66mU66qo66asKVxuICAgKiAyLiBGaXh0dXJlUmVjb3Jk66GcIOuzgO2ZmFxuICAgKiAzLiBGaXh0dXJlTWFuYWdlci5pbnNlcnRGaXh0dXJlcygp66GcIHRhcmdldERi7JeQIOyggOyepVxuICAgKlxuICAgKiBAcmV0dXJucyDsoIDsnqXrkJwgZml4dHVyZSDrjbDsnbTthLAgKOyLpOygnCBEQiBJRCDtj6ztlagpXG4gICAqL1xuICBhc3luYyBnZW5lcmF0ZUJhdGNoKFxuICAgIHNwZWNzOiBBcnJheTx7IGVudGl0eTogc3RyaW5nOyBjb3VudDogbnVtYmVyOyBvdmVycmlkZXM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9PixcbiAgKTogUHJvbWlzZTxGaXh0dXJlSW1wb3J0UmVzdWx0W10+IHtcbiAgICBjb25zdCBjb250ZXh0ID0gdGhpcy5jcmVhdGVDb250ZXh0KCk7XG4gICAgY29uc3QgZ2VuZXJhdGVkRml4dHVyZXM6IEFycmF5PHtcbiAgICAgIGVudGl0eTogc3RyaW5nO1xuICAgICAgZGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgICBleHBsaWNpdElkPzogYm9vbGVhbjtcbiAgICB9PiA9IFtdO1xuXG4gICAgLy8gMS4g6rCBIHNwZWPrs4TroZwgZml4dHVyZSDsg53shLFcbiAgICBmb3IgKGNvbnN0IHNwZWMgb2Ygc3BlY3MpIHtcbiAgICAgIGNvbnN0IHNwZWNFbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KHNwZWMuZW50aXR5KTtcblxuICAgICAgaWYgKHNwZWNFbnRpdHkucGFyZW50SWQpIHtcbiAgICAgICAgLy8gcGFyZW50SWQg7JeU7Yuw7YuwOiBEQuyXkOyEnCDshJzruIztg4DsnoUg7ZaJ7J20IOyXhuuKlCDrtoDrqqggaWTrpbwg7KGw7ZqM7ZWY7JesIOyCrOyaqVxuICAgICAgICAvLyAo7IOIIOu2gOuqqCDsg53shLEg64yA7IugIOq4sOyhtCDrjbDsnbTthLAg7J6s7Zmc7JqpKVxuICAgICAgICBjb25zdCBpZFByb3AgPSBzcGVjRW50aXR5LnByb3BzLmZpbmQoKHApID0+IHAubmFtZSA9PT0gXCJpZFwiKTtcbiAgICAgICAgY29uc3QgcGFyZW50T3ZlcnJpZGVzID1cbiAgICAgICAgICAoaWRQcm9wPy5jb25lPy5maXh0dXJlUGFyZW50T3ZlcnJpZGVzIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkKSA/PyB7fTtcbiAgICAgICAgY29uc3QgcGFyZW50RW50aXR5ID0gdGhpcy5lbnRpdHlNYW5hZ2VyLmdldChzcGVjRW50aXR5LnBhcmVudElkKTtcblxuICAgICAgICAvLyDrtoDrqqgg7YWM7J2067iU7JeQ7IScIOyEnOu4jO2DgOyehSDthYzsnbTruJTsl5Ag7JeG64qUIGlk66W8IOyhsO2ajFxuICAgICAgICBsZXQgcXVlcnkgPSB0aGlzLnNvdXJjZURiKHBhcmVudEVudGl0eS50YWJsZSkuc2VsZWN0KGAke3BhcmVudEVudGl0eS50YWJsZX0uaWRgKTtcbiAgICAgICAgZm9yIChjb25zdCBbY29sLCB2YWxdIG9mIE9iamVjdC5lbnRyaWVzKHBhcmVudE92ZXJyaWRlcykpIHtcbiAgICAgICAgICBxdWVyeSA9IHF1ZXJ5LndoZXJlKFxuICAgICAgICAgICAgYCR7cGFyZW50RW50aXR5LnRhYmxlfS4ke2NvbH1gLFxuICAgICAgICAgICAgdmFsIGFzIHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4gfCBudWxsLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgcXVlcnkgPSBxdWVyeVxuICAgICAgICAgIC5sZWZ0Sm9pbihzcGVjRW50aXR5LnRhYmxlLCBgJHtzcGVjRW50aXR5LnRhYmxlfS5pZGAsIGAke3BhcmVudEVudGl0eS50YWJsZX0uaWRgKVxuICAgICAgICAgIC53aGVyZU51bGwoYCR7c3BlY0VudGl0eS50YWJsZX0uaWRgKVxuICAgICAgICAgIC5saW1pdChzcGVjLmNvdW50KTtcblxuICAgICAgICBjb25zdCByb3dzID0gYXdhaXQgcXVlcnk7XG4gICAgICAgIGNvbnN0IGF2YWlsYWJsZUlkczogbnVtYmVyW10gPSByb3dzLm1hcCgocjogeyBpZDogbnVtYmVyIH0pID0+IHIuaWQpO1xuXG4gICAgICAgIGlmIChhdmFpbGFibGVJZHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgIWlzVGVzdCgpICYmXG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgICAgIGNoYWxrLnllbGxvdyhcbiAgICAgICAgICAgICAgICBgW3BhcmVudElkXSAke3NwZWMuZW50aXR5fTog7ISc67iM7YOA7J6F7J20IOyXhuuKlCDrtoDrqqgg66CI7L2U65Oc6rCAIOu2gOyhse2VqeuLiOuLpC4g6rG064SI65yB64uI64ukLmAsXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGZvciAoY29uc3QgcGFyZW50SWQgb2YgYXZhaWxhYmxlSWRzKSB7XG4gICAgICAgICAgICBjb25zdCBmaXh0dXJlID0gYXdhaXQgdGhpcy5nZW5lcmF0ZShzcGVjLmVudGl0eSwgc3BlYy5vdmVycmlkZXMgfHwge30sIGNvbnRleHQpO1xuICAgICAgICAgICAgZml4dHVyZS5pZCA9IHBhcmVudElkO1xuICAgICAgICAgICAgZ2VuZXJhdGVkRml4dHVyZXMucHVzaCh7IGVudGl0eTogc3BlYy5lbnRpdHksIGRhdGE6IGZpeHR1cmUsIGV4cGxpY2l0SWQ6IHRydWUgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNwZWMuY291bnQ7IGkrKykge1xuICAgICAgICAgIGNvbnN0IGZpeHR1cmUgPSBhd2FpdCB0aGlzLmdlbmVyYXRlKHNwZWMuZW50aXR5LCBzcGVjLm92ZXJyaWRlcyB8fCB7fSwgY29udGV4dCk7XG4gICAgICAgICAgZ2VuZXJhdGVkRml4dHVyZXMucHVzaCh7XG4gICAgICAgICAgICBlbnRpdHk6IHNwZWMuZW50aXR5LFxuICAgICAgICAgICAgZGF0YTogZml4dHVyZSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIDIuIEZpeHR1cmVSZWNvcmTroZwg67OA7ZmYXG4gICAgY29uc3QgZml4dHVyZVJlY29yZHM6IEZpeHR1cmVSZWNvcmRbXSA9IFtdO1xuICAgIGZvciAoY29uc3QgeyBlbnRpdHk6IGVudGl0eU5hbWUsIGRhdGEsIGV4cGxpY2l0SWQgfSBvZiBnZW5lcmF0ZWRGaXh0dXJlcykge1xuICAgICAgY29uc3QgZW50aXR5ID0gdGhpcy5lbnRpdHlNYW5hZ2VyLmdldChlbnRpdHlOYW1lKTtcblxuICAgICAgLy8gaW50ZWdlci9iaWdJbnRlZ2VyIFBL64qUIOyehOyLnCBJRCDsg53shLEgKERCIOyLnO2AgOyKpOqwgCDsi6TsoJwgSUQg7ZWg64u5KVxuICAgICAgLy8gc3RyaW5nIFBL64qUIGdlbmVyYXRlKCnsl5DshJwg7J2066+4IOyDneyEseuQnCBpZCDqsJLsnYQg6re464yA66GcIOyCrOyaqVxuICAgICAgLy8gcGFyZW50SWQg7JeU7Yuw7Yuw64qUIOu2gOuqqOydmCDsi6TsoJwgaWTrpbwg6re464yA66GcIOyCrOyaqSAo7Iuc7YCA7IqkIOuvuOyCrOyaqSlcbiAgICAgIGNvbnN0IGlkUHJvcCA9IGVudGl0eS5wcm9wcy5maW5kKChwKSA9PiBwLm5hbWUgPT09IFwiaWRcIik7XG4gICAgICBjb25zdCB1c2VzU2VxdWVuY2UgPVxuICAgICAgICAhZXhwbGljaXRJZCAmJlxuICAgICAgICAoaWRQcm9wPy50eXBlID09PSBcImludGVnZXJcIiB8fFxuICAgICAgICAgIGlkUHJvcD8udHlwZSA9PT0gXCJiaWdJbnRlZ2VyXCIgfHxcbiAgICAgICAgICBpZFByb3A/LmNvbmU/LmZpeHR1cmVTdHJhdGVneSA9PT0gXCJzZXF1ZW5jZVwiKTtcblxuICAgICAgY29uc3QgZGF0YUZvclJlY29yZCA9IHVzZXNTZXF1ZW5jZVxuICAgICAgICA/IHsgLi4uZGF0YSwgaWQ6IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIDEwMDAwMDApIH1cbiAgICAgICAgOiBkYXRhO1xuXG4gICAgICBjb25zdCByZWNvcmRzID0gYXdhaXQgRml4dHVyZU1hbmFnZXIuY3JlYXRlRml4dHVyZVJlY29yZChcbiAgICAgICAgZW50aXR5LFxuICAgICAgICBkYXRhRm9yUmVjb3JkIGFzIHsgaWQ6IG51bWJlciB8IHN0cmluZzsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwgfSxcbiAgICAgICAgeyBzaW5nbGVSZWNvcmQ6IHRydWUgfSxcbiAgICAgICk7XG4gICAgICBmaXh0dXJlUmVjb3Jkcy5wdXNoKC4uLnJlY29yZHMpO1xuICAgIH1cblxuICAgIC8vIDMuIHRhcmdldERi7JeQIOyCveyehSAoRml4dHVyZU1hbmFnZXLqsIAg7J2Y7KG07ISxIOygleugrCDsspjrpqwpXG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVzKHRoaXMudGFyZ2V0RGJOYW1lLCBmaXh0dXJlUmVjb3Jkcyk7XG5cbiAgICAvLyA0LiBjb21wYW5pb24gZml4dHVyZXMg7IOd7ISxIChmaXh0dXJlQ29tcGFuaW9uc+qwgCDshKDslrjrkJwg6rK97JqwKVxuICAgIGNvbnN0IGNvbXBhbmlvblJlc3VsdHMgPSBhd2FpdCB0aGlzLmdlbmVyYXRlQ29tcGFuaW9ucyhzcGVjcywgcmVzdWx0cyk7XG5cbiAgICBjb25zdCB0b3RhbCA9IHJlc3VsdHMubGVuZ3RoICsgY29tcGFuaW9uUmVzdWx0cy5sZW5ndGg7XG4gICAgIWlzVGVzdCgpICYmXG4gICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihgR2VuZXJhdGVkIGFuZCBzYXZlZCAke3RvdGFsfSBmaXh0dXJlcyB0byAke3RoaXMudGFyZ2V0RGJOYW1lfWApKTtcbiAgICByZXR1cm4gWy4uLnJlc3VsdHMsIC4uLmNvbXBhbmlvblJlc3VsdHNdO1xuICB9XG5cbiAgLyoqXG4gICAqIOu2gOuqqCBmaXh0dXJlIOqysOqzvOulvCDquLDrsJjsnLzroZwgZml4dHVyZUNvbXBhbmlvbnPsl5Ag7ISg7Ja465CcIGNvbXBhbmlvbiBFbnRpdHnrpbwg7IOd7ISx7ZWp64uI64ukLlxuICAgKlxuICAgKiBnZW5lcmF0ZUJhdGNoKCnsl5DshJzrp4wg7Zi47Lac65CY66mwLCBjb21wYW5pb24g7IOd7ISxIOyLnCDsnqzqt4Drpbwg67Cp7KeA7ZWY6riwIOychO2VtFxuICAgKiBnZW5lcmF0ZUJhdGNoKCnrpbwg64uk7IucIO2YuOy2nO2VmOyngCDslYrqs6Ag7KeB7KCRIOyCveyehe2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVDb21wYW5pb25zKFxuICAgIHNwZWNzOiBBcnJheTx7IGVudGl0eTogc3RyaW5nOyBjb3VudDogbnVtYmVyOyBvdmVycmlkZXM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9PixcbiAgICBwYXJlbnRSZXN1bHRzOiBGaXh0dXJlSW1wb3J0UmVzdWx0W10sXG4gICk6IFByb21pc2U8Rml4dHVyZUltcG9ydFJlc3VsdFtdPiB7XG4gICAgY29uc3QgYWxsUmVzdWx0czogRml4dHVyZUltcG9ydFJlc3VsdFtdID0gW107XG4gICAgY29uc3QgcHJvY2Vzc2VkRW50aXRpZXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcblxuICAgIGZvciAoY29uc3Qgc3BlYyBvZiBzcGVjcykge1xuICAgICAgaWYgKHByb2Nlc3NlZEVudGl0aWVzLmhhcyhzcGVjLmVudGl0eSkpIGNvbnRpbnVlO1xuICAgICAgcHJvY2Vzc2VkRW50aXRpZXMuYWRkKHNwZWMuZW50aXR5KTtcblxuICAgICAgY29uc3QgZW50aXR5ID0gdGhpcy5lbnRpdHlNYW5hZ2VyLmdldChzcGVjLmVudGl0eSk7XG4gICAgICBjb25zdCBpZFByb3AgPSBlbnRpdHkucHJvcHMuZmluZCgocCkgPT4gcC5uYW1lID09PSBcImlkXCIpO1xuICAgICAgY29uc3QgY29tcGFuaW9ucyA9IGlkUHJvcD8uY29uZT8uZml4dHVyZUNvbXBhbmlvbnM7XG4gICAgICBpZiAoIWNvbXBhbmlvbnMgfHwgY29tcGFuaW9ucy5sZW5ndGggPT09IDApIGNvbnRpbnVlO1xuXG4gICAgICBjb25zdCBlbnRpdHlSZXN1bHRzID0gcGFyZW50UmVzdWx0cy5maWx0ZXIoKHIpID0+IHIuZW50aXR5SWQgPT09IHNwZWMuZW50aXR5KTtcbiAgICAgIGlmIChlbnRpdHlSZXN1bHRzLmxlbmd0aCA9PT0gMCkgY29udGludWU7XG5cbiAgICAgIGZvciAoY29uc3QgY29tcGFuaW9uIG9mIGNvbXBhbmlvbnMpIHtcbiAgICAgICAgLy8gY29tcGFuaW9uIGVudGl0eeyXkOyEnCDrtoDrqqggZW50aXR566Gc7J2YIEJlbG9uZ3NUb09uZSBGSyDsu6zrn7zrqoUg7YyM7JWFXG4gICAgICAgIGNvbnN0IGNvbXBhbmlvbkVudGl0eSA9IHRoaXMuZW50aXR5TWFuYWdlci5nZXQoY29tcGFuaW9uLmVudGl0eSk7XG4gICAgICAgIGNvbnN0IGZrUHJvcCA9IGNvbXBhbmlvbkVudGl0eS5wcm9wcy5maW5kKFxuICAgICAgICAgIChwKSA9PiBpc1JlbGF0aW9uUHJvcChwKSAmJiBpc0JlbG9uZ3NUb09uZVJlbGF0aW9uUHJvcChwKSAmJiBwLndpdGggPT09IHNwZWMuZW50aXR5LFxuICAgICAgICApO1xuICAgICAgICBpZiAoIWZrUHJvcCkge1xuICAgICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgICAgICBjaGFsay55ZWxsb3coXG4gICAgICAgICAgICAgICAgYFtDb21wYW5pb25dIE5vIEJlbG9uZ3NUb09uZSByZWxhdGlvbiBmcm9tICR7Y29tcGFuaW9uLmVudGl0eX0gdG8gJHtzcGVjLmVudGl0eX0uIFNraXBwaW5nLmAsXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICApO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGZrQ29sTmFtZSA9IGAke2ZrUHJvcC5uYW1lfV9pZGA7XG5cbiAgICAgICAgLy8gY29tcGFuaW9u7J2YIGlkUHJvcCwgdXNlc1NlcXVlbmNlLCBjb3VudOuKlCBjb21wYW5pb24g64uo7JyE66GcIOqzoOyglVxuICAgICAgICBjb25zdCBjb21wYW5pb25JZFByb3AgPSBjb21wYW5pb25FbnRpdHkucHJvcHMuZmluZCgocCkgPT4gcC5uYW1lID09PSBcImlkXCIpO1xuICAgICAgICBjb25zdCB1c2VzU2VxdWVuY2UgPVxuICAgICAgICAgIGNvbXBhbmlvbklkUHJvcD8udHlwZSA9PT0gXCJpbnRlZ2VyXCIgfHxcbiAgICAgICAgICBjb21wYW5pb25JZFByb3A/LnR5cGUgPT09IFwiYmlnSW50ZWdlclwiIHx8XG4gICAgICAgICAgY29tcGFuaW9uSWRQcm9wPy5jb25lPy5maXh0dXJlU3RyYXRlZ3kgPT09IFwic2VxdWVuY2VcIjtcbiAgICAgICAgY29uc3QgY29tcGFuaW9uQ291bnQgPSBjb21wYW5pb24uY291bnQgPz8gMTtcblxuICAgICAgICAvLyDqsIEgcGFyZW50IHJlc3VsdOyXkCDrjIDtlbQgY29tcGFuaW9uIGZpeHR1cmUg7IOd7ISxXG4gICAgICAgIGNvbnN0IGNvbnRleHQgPSB0aGlzLmNyZWF0ZUNvbnRleHQoKTtcbiAgICAgICAgY29uc3QgY29tcGFuaW9uRml4dHVyZVJlY29yZHM6IEZpeHR1cmVSZWNvcmRbXSA9IFtdO1xuXG4gICAgICAgIGZvciAoY29uc3QgcGFyZW50UmVzdWx0IG9mIGVudGl0eVJlc3VsdHMpIHtcbiAgICAgICAgICBjb25zdCByZXNvbHZlZE92ZXJyaWRlcyA9IHRoaXMucmVzb2x2ZVRlbXBsYXRlT3ZlcnJpZGVzKFxuICAgICAgICAgICAgY29tcGFuaW9uLm92ZXJyaWRlcyA/PyB7fSxcbiAgICAgICAgICAgIHBhcmVudFJlc3VsdC5kYXRhLFxuICAgICAgICAgICk7XG4gICAgICAgICAgcmVzb2x2ZWRPdmVycmlkZXNbZmtDb2xOYW1lXSA9IHBhcmVudFJlc3VsdC5kYXRhLmlkO1xuXG4gICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjb21wYW5pb25Db3VudDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCBmaXh0dXJlID0gYXdhaXQgdGhpcy5nZW5lcmF0ZShjb21wYW5pb24uZW50aXR5LCByZXNvbHZlZE92ZXJyaWRlcywgY29udGV4dCk7XG5cbiAgICAgICAgICAgIGNvbnN0IGRhdGFGb3JSZWNvcmQgPSB1c2VzU2VxdWVuY2VcbiAgICAgICAgICAgICAgPyB7IC4uLmZpeHR1cmUsIGlkOiBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAxMDAwMDAwKSB9XG4gICAgICAgICAgICAgIDogZml4dHVyZTtcblxuICAgICAgICAgICAgY29uc3QgcmVjb3JkcyA9IGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmNyZWF0ZUZpeHR1cmVSZWNvcmQoXG4gICAgICAgICAgICAgIGNvbXBhbmlvbkVudGl0eSxcbiAgICAgICAgICAgICAgZGF0YUZvclJlY29yZCBhcyB7XG4gICAgICAgICAgICAgICAgaWQ6IG51bWJlciB8IHN0cmluZztcbiAgICAgICAgICAgICAgICBba2V5OiBzdHJpbmddOiBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuIHwgbnVsbDtcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgeyBzaW5nbGVSZWNvcmQ6IHRydWUgfSxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBjb21wYW5pb25GaXh0dXJlUmVjb3Jkcy5wdXNoKC4uLnJlY29yZHMpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGNvbXBhbmlvblJlc3VsdHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5pbnNlcnRGaXh0dXJlcyhcbiAgICAgICAgICB0aGlzLnRhcmdldERiTmFtZSxcbiAgICAgICAgICBjb21wYW5pb25GaXh0dXJlUmVjb3JkcyxcbiAgICAgICAgKTtcbiAgICAgICAgYWxsUmVzdWx0cy5wdXNoKC4uLmNvbXBhbmlvblJlc3VsdHMpO1xuXG4gICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgY2hhbGsuZ3JlZW4oXG4gICAgICAgICAgICAgIGBbQ29tcGFuaW9uXSBHZW5lcmF0ZWQgJHtjb21wYW5pb25SZXN1bHRzLmxlbmd0aH0gJHtjb21wYW5pb24uZW50aXR5fSBmaXh0dXJlc2AsXG4gICAgICAgICAgICApLFxuICAgICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGFsbFJlc3VsdHM7XG4gIH1cblxuICAvKipcbiAgICogb3ZlcnJpZGVzIOqwkuydmCBcInt7ZmllbGROYW1lfX1cIiDthZztlIzrpr/snYQg67aA66qoIGZpeHR1cmUg642w7J207YSw66GcIOy5mO2ZmO2VqeuLiOuLpC5cbiAgICpcbiAgICog7JiIOiB7IFwiYWNjb3VudF9pZFwiOiBcInt7ZW1haWx9fVwiIH0g4oaSIHsgXCJhY2NvdW50X2lkXCI6IFwidXNlckBleGFtcGxlLmNvbVwiIH1cbiAgICovXG4gIHByaXZhdGUgcmVzb2x2ZVRlbXBsYXRlT3ZlcnJpZGVzKFxuICAgIG92ZXJyaWRlczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgcGFyZW50RGF0YTogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuIHwgRGF0ZSB8IG51bGwgfSxcbiAgKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICAgIGNvbnN0IHJlc29sdmVkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG92ZXJyaWRlcykpIHtcbiAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgJiYgdmFsdWUuc3RhcnRzV2l0aChcInt7XCIpICYmIHZhbHVlLmVuZHNXaXRoKFwifX1cIikpIHtcbiAgICAgICAgY29uc3QgZmllbGROYW1lID0gdmFsdWUuc2xpY2UoMiwgLTIpLnRyaW0oKTtcbiAgICAgICAgaWYgKCEoZmllbGROYW1lIGluIHBhcmVudERhdGEpKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYO2FnO2UjOumvyDtlYTrk5wgXCIke2ZpZWxkTmFtZX1cIuydtCjqsIApIOu2gOuqqCBmaXh0dXJlIOuNsOydtO2EsOyXkCDsobTsnqztlZjsp4Ag7JWK7Iq164uI64ukIChvdmVycmlkZSBrZXk6IFwiJHtrZXl9XCIpYCxcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJlc29sdmVkW2tleV0gPSBwYXJlbnREYXRhW2ZpZWxkTmFtZV07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXNvbHZlZFtrZXldID0gdmFsdWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXNvbHZlZDtcbiAgfVxuXG4gIC8qKlxuICAgKiDsi6TsoJwgREIoc291cmNlRGIp7JeQ7IScIOuNsOydtO2EsOulvCDsobDtmoztlZjsl6wgZml4dHVyZSBEQih0YXJnZXREYinsl5AgaW1wb3J07ZWp64uI64ukLlxuICAgKlxuICAgKiAxLiBEYXRhRXhwbG9yZXLroZwgc291cmNlRGLsl5DshJwg642w7J207YSwIOyhsO2ajCAo6rSA66CoIOuNsOydtO2EsCDtj6ztlagpXG4gICAqIDIuIEZpeHR1cmVSZWNvcmTroZwg67OA7ZmYXG4gICAqIDMuIHRhcmdldERi7JeQIOyCveyehVxuICAgKlxuICAgKiBAcGFyYW0gZW50aXR5TmFtZSAtIOyhsO2ajO2VoCBlbnRpdHkg7J2066aEXG4gICAqIEBwYXJhbSBvcHRpb25zIC0g7KGw7ZqMIOyYteyFmCAoc3RyYXRlZ3ksIGxpbWl0LCBpbmNsdWRlUmVsYXRpb25zIOuTsSlcbiAgICogQHJldHVybnMg7KCA7J6l65CcIGZpeHR1cmUg642w7J207YSwICjsi6TsoJwgREIgSUQg7Y+s7ZWoKVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiAvLyDtlITroZzrjZXshZggRELsl5DshJwgVXNlciAxMOuqhSArIOq0gOugqCBFbXBsb3llZSwgRGVwYXJ0bWVudCDqsIDsoLjsmKTquLBcbiAgICogYXdhaXQgZ2VuZXJhdG9yLmltcG9ydEZyb21Tb3VyY2UoXCJVc2VyXCIsIHtcbiAgICogICBzdHJhdGVneTogXCJzYW1wbGVcIixcbiAgICogICBsaW1pdDogMTAsXG4gICAqICAgaW5jbHVkZVJlbGF0aW9uczogdHJ1ZSxcbiAgICogICBtYXhEZXB0aDogMlxuICAgKiB9KTtcbiAgICovXG4gIGFzeW5jIGltcG9ydEZyb21Tb3VyY2UoXG4gICAgZW50aXR5TmFtZTogc3RyaW5nLFxuICAgIG9wdGlvbnM6IEV4cGxvcmVXaXRoUmVsYXRpb25zT3B0aW9ucyxcbiAgKTogUHJvbWlzZTxGaXh0dXJlSW1wb3J0UmVzdWx0W10+IHtcbiAgICAhaXNUZXN0KCkgJiZcbiAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICBjaGFsay5ibHVlKFxuICAgICAgICAgIGBJbXBvcnRpbmcgJHtlbnRpdHlOYW1lfSBmcm9tIHNvdXJjZSBEQiB3aXRoIG9wdGlvbnM6ICR7SlNPTi5zdHJpbmdpZnkoeyBzdHJhdGVneTogb3B0aW9ucy5zdHJhdGVneSwgbGltaXQ6IG9wdGlvbnMubGltaXQsIGluY2x1ZGVSZWxhdGlvbnM6IG9wdGlvbnMuaW5jbHVkZVJlbGF0aW9ucywgbWF4RGVwdGg6IG9wdGlvbnMubWF4RGVwdGggfSl9YCxcbiAgICAgICAgKSxcbiAgICAgICk7XG5cbiAgICAvLyAxLiBEYXRhRXhwbG9yZXLroZwgc291cmNlRGLsl5DshJwg642w7J207YSwIOyhsO2ajCAo6rSA66CoIOuNsOydtO2EsCDtj6ztlagpXG4gICAgY29uc3QgZXhwbG9yZVJlc3VsdCA9IGF3YWl0IHRoaXMuZGF0YUV4cGxvcmVyLmV4cGxvcmVXaXRoUmVsYXRpb25zKGVudGl0eU5hbWUsIG9wdGlvbnMpO1xuXG4gICAgIWlzVGVzdCgpICYmXG4gICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgY2hhbGsuY3lhbihcbiAgICAgICAgICBgRm91bmQgJHtleHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcy5sZW5ndGh9ICR7ZW50aXR5TmFtZX0gcmVjb3JkcyBhbmQgJHtleHBsb3JlUmVzdWx0LnJlbGF0ZWQuc2l6ZX0gcmVsYXRlZCBlbnRpdGllc2AsXG4gICAgICAgICksXG4gICAgICApO1xuXG4gICAgLy8gMi4gRml4dHVyZVJlY29yZOuhnCDrs4DtmZhcbiAgICBjb25zdCBmaXh0dXJlUmVjb3JkczogRml4dHVyZVJlY29yZFtdID0gW107XG5cbiAgICAvLyDrqZTsnbggZW50aXR57J2YIHJlY29yZHPrpbwgRml4dHVyZVJlY29yZOuhnCDrs4DtmZhcbiAgICBjb25zdCBtYWluRW50aXR5ID0gdGhpcy5lbnRpdHlNYW5hZ2VyLmdldChlbnRpdHlOYW1lKTtcbiAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiBleHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcykge1xuICAgICAgY29uc3QgcmVjb3JkcyA9IGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmNyZWF0ZUZpeHR1cmVSZWNvcmQoXG4gICAgICAgIG1haW5FbnRpdHksXG4gICAgICAgIHJlY29yZCBhcyB7IGlkOiBudW1iZXIgfCBzdHJpbmc7IFtrZXk6IHN0cmluZ106IHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4gfCBudWxsIH0sXG4gICAgICAgIHsgX2RiOiB0aGlzLnNvdXJjZURiLCBzaW5nbGVSZWNvcmQ6IHRydWUgfSxcbiAgICAgICk7XG4gICAgICBmaXh0dXJlUmVjb3Jkcy5wdXNoKC4uLnJlY29yZHMpO1xuICAgIH1cblxuICAgIC8vIOq0gOugqCBlbnRpdHnsnZggcmVjb3Jkc+ulvCBGaXh0dXJlUmVjb3Jk66GcIOuzgO2ZmFxuICAgIGZvciAoY29uc3QgW3JlbGF0ZWRFbnRpdHlOYW1lLCByZWxhdGVkUmVjb3Jkc10gb2YgZXhwbG9yZVJlc3VsdC5yZWxhdGVkLmVudHJpZXMoKSkge1xuICAgICAgY29uc3QgcmVsYXRlZEVudGl0eSA9IHRoaXMuZW50aXR5TWFuYWdlci5nZXQocmVsYXRlZEVudGl0eU5hbWUpO1xuICAgICAgZm9yIChjb25zdCByZWNvcmQgb2YgcmVsYXRlZFJlY29yZHMpIHtcbiAgICAgICAgY29uc3QgcmVjb3JkcyA9IGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmNyZWF0ZUZpeHR1cmVSZWNvcmQoXG4gICAgICAgICAgcmVsYXRlZEVudGl0eSxcbiAgICAgICAgICByZWNvcmQgYXMgeyBpZDogbnVtYmVyIHwgc3RyaW5nOyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuIHwgbnVsbCB9LFxuICAgICAgICAgIHsgX2RiOiB0aGlzLnNvdXJjZURiLCBzaW5nbGVSZWNvcmQ6IHRydWUgfSxcbiAgICAgICAgKTtcbiAgICAgICAgZml4dHVyZVJlY29yZHMucHVzaCguLi5yZWNvcmRzKTtcbiAgICAgIH1cblxuICAgICAgIWlzVGVzdCgpICYmXG4gICAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmdyYXkoYCAgLSAke3JlbGF0ZWRFbnRpdHlOYW1lfTogJHtyZWxhdGVkUmVjb3Jkcy5sZW5ndGh9IHJlY29yZHNgKSk7XG4gICAgfVxuXG4gICAgLy8gMy4gdGFyZ2V0RGLsl5Ag7IK97J6FIChGaXh0dXJlTWFuYWdlcuqwgCDsnZjsobTshLEg7KCV66CsIOyymOumrClcbiAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgRml4dHVyZU1hbmFnZXIuaW5zZXJ0Rml4dHVyZXModGhpcy50YXJnZXREYk5hbWUsIGZpeHR1cmVSZWNvcmRzKTtcblxuICAgICFpc1Rlc3QoKSAmJlxuICAgICAgY29uc29sZS5sb2coXG4gICAgICAgIGNoYWxrLmdyZWVuKFxuICAgICAgICAgIGBTdWNjZXNzZnVsbHkgaW1wb3J0ZWQgJHtyZXN1bHRzLmxlbmd0aH0gcmVjb3JkcyB0byAke3RoaXMudGFyZ2V0RGJOYW1lfSAoJHtleHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcy5sZW5ndGh9ICR7ZW50aXR5TmFtZX0gKyAke3Jlc3VsdHMubGVuZ3RoIC0gZXhwbG9yZVJlc3VsdC5tYWluLnJlY29yZHMubGVuZ3RofSByZWxhdGVkKWAsXG4gICAgICAgICksXG4gICAgICApO1xuXG4gICAgcmV0dXJuIHJlc3VsdHM7XG4gIH1cbn1cbiJdLCJuYW1lcyI6WyJjaGFsayIsImlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wIiwiaXNPbmVUb09uZVJlbGF0aW9uUHJvcCIsImlzUmVsYXRpb25Qcm9wIiwiaXNUZXN0IiwiRGF0YUV4cGxvcmVyIiwiZmFrZXJNYXBwaW5ncyIsIkZpeHR1cmVNYW5hZ2VyIiwiRml4dHVyZUdlbmVyYXRvciIsImRhdGFFeHBsb3JlciIsImxvY2FsZSIsIm1hcHBpbmdzIiwibGxtQ2FjaGUiLCJNYXAiLCJlbnRpdHlDYWNoZSIsIm9wdGlvbnMiLCJzb3VyY2VEYiIsIl90YXJnZXREYiIsInRhcmdldERiTmFtZSIsImVudGl0eU1hbmFnZXIiLCJ1c2VMTE0iLCJlbmFibGVMTE1DYWNoZSIsImxsbU1vZGVsIiwiZ2VuZXJhdGUiLCJlbnRpdHlOYW1lIiwib3ZlcnJpZGVzIiwiY29udGV4dCIsImNyZWF0ZUNvbnRleHQiLCJlbnRpdHkiLCJnZXQiLCJzZXQiLCJ0ZW1wSWQiLCJEYXRlIiwibm93Iiwicm93S2V5IiwidW5kZWZpbmVkIiwiZml4dHVyZSIsInByb3AiLCJwcm9wcyIsInZpcnR1YWwiLCJuYW1lIiwiY29uZSIsImZpeHR1cmVTdHJhdGVneSIsInR5cGUiLCJmYWtlciIsIl9mYWtlciIsInN0cmluZyIsImFscGhhbnVtZXJpYyIsInV1aWQiLCJma0NvbE5hbWUiLCJoYXNKb2luQ29sdW1uIiwicmVsYXRpb25WYWx1ZSIsImdlbmVyYXRlUmVsYXRpb25WYWx1ZSIsIm5vdGUiLCJsbG1WYWx1ZSIsImdlbmVyYXRlV2l0aExMTSIsImxlbmd0aCIsInNsaWNlIiwiZXJyb3IiLCJjb25zb2xlIiwid2FybiIsImlkIiwiRXJyb3IiLCJtZXNzYWdlIiwiZml4dHVyZUdlbmVyYXRvciIsImV4ZWN1dGVHZW5lcmF0b3IiLCJmaXh0dXJlRGVmYXVsdCIsImdlbmVyYXRlRGVmYXVsdFZhbHVlIiwiZW1haWwiLCJuYW1lVmFsdWUiLCJ1c2VybmFtZSIsImZ1bGxfbmFtZSIsIm5hbWVfZW4iLCJkb21haW4iLCJzcGxpdCIsInJvbWFuaXplZCIsInJvbWFuaXplTmFtZSIsInBhc3N3b3JkIiwiYmNyeXB0IiwiaGFzaCIsImZpeHR1cmVzIiwiZGF0YVNvdXJjZSIsImNhY2hlS2V5Iiwid2l0aCIsIkpTT04iLCJzdHJpbmdpZnkiLCJyZWZlcmVuY2VDYWNoZSIsImhhcyIsImV4cGxvcmVSZXN1bHQiLCJleHBsb3JlV2l0aFJlbGF0aW9ucyIsInN0cmF0ZWd5IiwibGltaXQiLCJjb25maWciLCJpbmNsdWRlUmVsYXRpb25zIiwibWF4RGVwdGgiLCJtYWluIiwicmVjb3JkcyIsImltcG9ydEV4cGxvcmVSZXN1bHQiLCJjYW5kaWRhdGVzIiwic2VsZWN0ZWQiLCJNYXRoIiwiZmxvb3IiLCJyYW5kb20iLCJhdXRvS2V5IiwiYXV0b0V4cGxvcmVSZXN1bHQiLCJhdXRvQ2FuZGlkYXRlcyIsIm51bGxhYmxlIiwiYWxsRml4dHVyZVJlY29yZHMiLCJlbnRpdHlJZCIsInJlbGF0ZWQiLCJlbnRyaWVzIiwicmVjb3Jkc1RvSW1wb3J0IiwibG9nIiwiY3lhbiIsInJlY29yZCIsInJlY29yZEtleSIsImltcG9ydGVkUmVjb3JkcyIsInB1c2giLCJhZGQiLCJncmF5IiwiZml4dHVyZVJlY29yZHMiLCJjcmVhdGVGaXh0dXJlUmVjb3JkIiwiX2RiIiwic2luZ2xlUmVjb3JkIiwibWFpbkVudGl0eSIsIm1haW5SZWNvcmRzVG9JbXBvcnQiLCJpbnNlcnRGaXh0dXJlcyIsImdyZWVuIiwic2l6ZSIsImdlbmVyYXRvciIsInN0YXJ0c1dpdGgiLCJpc05hbWVGaWVsZCIsImZha2VyTW9kdWxlIiwiZmFrZXJLTyIsImV4cHIiLCJtYXRjaCIsInBhdGgiLCJhcmdzU3RyIiwicGFydHMiLCJmbiIsInBhcnQiLCJhcmdzIiwidHJpbSIsInBhcnNlR2VuZXJhdG9yQXJncyIsInllbGxvdyIsImZha2VySkEiLCJsb2NhbGVGYWtlciIsImRlcGFydG1lbnRzIiwicHJlZml4ZXMiLCJzdWZmaXhlcyIsImRlcHQiLCJoZWxwZXJzIiwiYXJyYXlFbGVtZW50IiwicHJlZml4Iiwic3VmZml4IiwibG9jYWxlTWFwcGluZ3MiLCJlbiIsIm5vcm1hbGl6ZWROYW1lIiwidG9Mb3dlckNhc2UiLCJyZXBsYWNlIiwicGF0dGVybiIsIk9iamVjdCIsImZpZWxkX3BhdHRlcm5zIiwiaW5jbHVkZXMiLCJleGVjdXRlRmFrZXJFeHByZXNzaW9uIiwiZW5kc1dpdGgiLCJnZW5lcmF0ZUFycmF5VmFsdWUiLCJlbnVtVmFsdWVzIiwiQXJyYXkiLCJpc0FycmF5IiwiZW51bSIsImVudW1MYWJlbHMiLCJrZXlzIiwidHlwZURlZmF1bHQiLCJ0eXBlX2RlZmF1bHRzIiwibG9yZW0iLCJ3b3JkcyIsIm51bWJlciIsImludCIsIm1pbiIsIm1heCIsImJpZ0ludCIsImZsb2F0IiwiZGF0YXR5cGUiLCJib29sZWFuIiwiZGF0ZSIsInBhc3QiLCJfZW50aXR5IiwiX2xvY2FsZUZha2VyIiwiY291bnQiLCJmcm9tIiwidXJsIiwiaW1hZ2UiLCJzeXN0ZW0iLCJmaWxlTmFtZSIsIm1pbWVfdHlwZSIsImludGVybmV0Iiwid29yZCIsImV4cHJlc3Npb24iLCJwYXJzZSIsImZha2VyTmFtZSIsInNlbGVjdGVkRmFrZXIiLCJmdW5jTWF0Y2giLCJmaXh0dXJlSGludCIsInJvd0NhY2hlS2V5IiwibGxtUHJvcHMiLCJmaWx0ZXIiLCJwIiwiZ2VuZXJhdGVTaW5nbGVXaXRoTExNIiwiYXBpS2V5IiwiZ2V0QXBpS2V5IiwiY3JlYXRlQW50aHJvcGljIiwiZ2VuZXJhdGVUZXh0Iiwicm93UmVzcG9uc2UiLCJtb2RlbCIsInByb21wdCIsImJ1aWxkUm93TExNUHJvbXB0IiwidGV4dCIsInJvd1Jlc3VsdCIsInBhcnNlUm93TExNUmVzcG9uc2UiLCJmaWVsZE5hbWUiLCJ2YWx1ZSIsInNpbmdsZVJlc3BvbnNlIiwiYnVpbGRMTE1Qcm9tcHQiLCJwYXJzZUxMTVJlc3BvbnNlIiwibGFuZ3VhZ2UiLCJmaWVsZERlc2NyaXB0aW9ucyIsIm1hcCIsImRlc2MiLCJ2YWx1ZXMiLCJqb2luIiwib3RoZXJQcm9wcyIsIm90aGVyUHJvcHNDb250ZXh0Iiwib3V0cHV0U2hhcGUiLCJlbnRpdHlDb250ZXh0IiwianNvblRleHQiLCJwYXJzZWQiLCJyYXciLCJyZXN1bHQiLCJTdHJpbmciLCJoaW50Iiwib3RoZXJGaWVsZHMiLCJvdGhlckZpZWxkc0NvbnRleHQiLCJnZXRFeHBlY3RlZEZvcm1hdCIsImdldEV4YW1wbGVGb3JUeXBlIiwicHJvcFR5cGUiLCJjbGVhbmVkIiwiYmFzZVR5cGUiLCJpdGVtIiwiZ2V0RGVmYXVsdFZhbHVlRm9yVHlwZSIsInBhcnNlU2NhbGFyVmFsdWUiLCJudW0iLCJwYXJzZUludCIsIk51bWJlciIsImlzTmFOIiwiQmlnSW50IiwicGFyc2VGbG9hdCIsImdldFRpbWUiLCJwcm9wTmFtZSIsImpzb25TdHIiLCJjb252ZXJ0SnNMaXRlcmFsVG9Kc29uIiwidHJpbW1lZCIsImlucHV0Iiwid2l0aERvdWJsZVF1b3RlcyIsIl8iLCJjb250ZW50IiwiU29uYW11IiwicmVxdWlyZSIsInNlY3JldHMiLCJhbnRocm9waWNfYXBpX2tleSIsInByb2Nlc3MiLCJlbnYiLCJBTlRIUk9QSUNfQVBJX0tFWSIsImJhc2VGb3JtYXQiLCJnZXRTY2FsYXJGb3JtYXQiLCJiYXNlRXhhbXBsZSIsImdldFNjYWxhckV4YW1wbGUiLCJpc0tvcmVhbiIsInRlc3QiLCJyb21hbml6ZUtvcmVhbk5hbWUiLCJDSE9TRU9ORyIsIkpVTkdTRU9ORyIsIkpPTkdTRU9ORyIsInJvbWFuaXplIiwiY2hhciIsImNvZGUiLCJjaGFyQ29kZUF0Iiwib2Zmc2V0IiwiY2hvIiwianVuZyIsImpvbmciLCJjaGFycyIsImZhbWlseU5hbWUiLCJnaXZlbk5hbWUiLCJnZXRMTE1DYWNoZVN0YXRzIiwiZW5hYmxlZCIsImNsZWFyTExNQ2FjaGUiLCJjbGVhciIsIlNldCIsImdlbmVyYXRlQmF0Y2giLCJzcGVjcyIsImdlbmVyYXRlZEZpeHR1cmVzIiwic3BlYyIsInNwZWNFbnRpdHkiLCJwYXJlbnRJZCIsImlkUHJvcCIsImZpbmQiLCJwYXJlbnRPdmVycmlkZXMiLCJmaXh0dXJlUGFyZW50T3ZlcnJpZGVzIiwicGFyZW50RW50aXR5IiwicXVlcnkiLCJ0YWJsZSIsInNlbGVjdCIsImNvbCIsInZhbCIsIndoZXJlIiwibGVmdEpvaW4iLCJ3aGVyZU51bGwiLCJyb3dzIiwiYXZhaWxhYmxlSWRzIiwiciIsImRhdGEiLCJleHBsaWNpdElkIiwiaSIsInVzZXNTZXF1ZW5jZSIsImRhdGFGb3JSZWNvcmQiLCJyZXN1bHRzIiwiY29tcGFuaW9uUmVzdWx0cyIsImdlbmVyYXRlQ29tcGFuaW9ucyIsInRvdGFsIiwicGFyZW50UmVzdWx0cyIsImFsbFJlc3VsdHMiLCJwcm9jZXNzZWRFbnRpdGllcyIsImNvbXBhbmlvbnMiLCJmaXh0dXJlQ29tcGFuaW9ucyIsImVudGl0eVJlc3VsdHMiLCJjb21wYW5pb24iLCJjb21wYW5pb25FbnRpdHkiLCJma1Byb3AiLCJjb21wYW5pb25JZFByb3AiLCJjb21wYW5pb25Db3VudCIsImNvbXBhbmlvbkZpeHR1cmVSZWNvcmRzIiwicGFyZW50UmVzdWx0IiwicmVzb2x2ZWRPdmVycmlkZXMiLCJyZXNvbHZlVGVtcGxhdGVPdmVycmlkZXMiLCJwYXJlbnREYXRhIiwicmVzb2x2ZWQiLCJrZXkiLCJpbXBvcnRGcm9tU291cmNlIiwiYmx1ZSIsInJlbGF0ZWRFbnRpdHlOYW1lIiwicmVsYXRlZFJlY29yZHMiLCJyZWxhdGVkRW50aXR5Il0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPQSxXQUFXLFFBQVE7QUFLMUIsU0FBU0MsMEJBQTBCLEVBQUVDLHNCQUFzQixFQUFFQyxjQUFjLFFBQVEsb0JBQWlCO0FBQ3BHLFNBQVNDLE1BQU0sUUFBUSx5QkFBc0I7QUFDN0MsU0FDRUMsWUFBWSxRQUdQLHFCQUFrQjtBQUN6QixTQUE2QkMsYUFBYSxRQUFRLHNCQUFtQjtBQUNyRSxTQUFTQyxjQUFjLFFBQVEsdUJBQW9CO0FBc0JuRCxPQUFPLE1BQU1DOzs7O0lBQ0hDLGFBQTJCO0lBQzNCQyxPQUFlO0lBQ2ZDLFNBQXdCO0lBQ3hCQyxXQUFpQyxJQUFJQyxNQUFNO0lBQzNDQyxjQUFtQyxJQUFJRCxNQUFNO0lBQzdDRSxRQUFpQztJQUV6QyxZQUNFLEFBQVFDLFFBQWMsRUFDdEIsaUVBQWlFO0lBQ2pFLGdDQUFnQztJQUNoQ0MsU0FBZSxFQUNmLEFBQVFDLFlBQXNELEVBQzlELEFBQVFDLGFBQW1DLEVBQzNDSixPQUFpQyxD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
1239
+ //#endregion
1240
+ init_fixture_generator();
1241
+ export { FixtureGenerator, init_fixture_generator };
1242
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZml4dHVyZS1nZW5lcmF0b3IuanMiLCJuYW1lcyI6WyJzb3VyY2VEYjogS25leCIsInRhcmdldERiTmFtZTogXCJmaXh0dXJlXCIgfCBcInRlc3RcIiB8IFwicHJvZHVjdGlvbl9tYXN0ZXJcIiIsImVudGl0eU1hbmFnZXI6IHR5cGVvZiBFbnRpdHlNYW5hZ2VyIiwiZml4dHVyZTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4iLCJhbGxGaXh0dXJlUmVjb3JkczogRml4dHVyZVJlY29yZFtdIiwicmVjb3Jkc1RvSW1wb3J0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPltdIiwibWFpblJlY29yZHNUb0ltcG9ydDogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSIsImZuOiB1bmtub3duIiwiYXJnczogdW5rbm93bltdIiwiZW51bVZhbHVlczogc3RyaW5nW10iLCJwYXJzZWQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IiwicmVzdWx0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiIsImFwaUtleTogc3RyaW5nIHwgdW5kZWZpbmVkIiwiZ2VuZXJhdGVkRml4dHVyZXM6IEFycmF5PHtcbiAgICAgIGVudGl0eTogc3RyaW5nO1xuICAgICAgZGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgICBleHBsaWNpdElkPzogYm9vbGVhbjtcbiAgICB9PiIsImF2YWlsYWJsZUlkczogbnVtYmVyW10iLCJmaXh0dXJlUmVjb3JkczogRml4dHVyZVJlY29yZFtdIiwiYWxsUmVzdWx0czogRml4dHVyZUltcG9ydFJlc3VsdFtdIiwiY29tcGFuaW9uRml4dHVyZVJlY29yZHM6IEZpeHR1cmVSZWNvcmRbXSIsInJlc29sdmVkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0aW5nL2ZpeHR1cmUtZ2VuZXJhdG9yLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjaGFsayBmcm9tIFwiY2hhbGtcIjtcbmltcG9ydCB7IHR5cGUgS25leCB9IGZyb20gXCJrbmV4XCI7XG5cbmltcG9ydCB7IHR5cGUgRW50aXR5IH0gZnJvbSBcIi4uL2VudGl0eS9lbnRpdHlcIjtcbmltcG9ydCB7IHR5cGUgRW50aXR5TWFuYWdlciB9IGZyb20gXCIuLi9lbnRpdHkvZW50aXR5LW1hbmFnZXJcIjtcbmltcG9ydCB7IHR5cGUgRW50aXR5UHJvcCwgdHlwZSBGaXh0dXJlSW1wb3J0UmVzdWx0LCB0eXBlIEZpeHR1cmVSZWNvcmQgfSBmcm9tIFwiLi4vdHlwZXMvdHlwZXNcIjtcbmltcG9ydCB7IGlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wLCBpc09uZVRvT25lUmVsYXRpb25Qcm9wLCBpc1JlbGF0aW9uUHJvcCB9IGZyb20gXCIuLi90eXBlcy90eXBlc1wiO1xuaW1wb3J0IHsgaXNUZXN0IH0gZnJvbSBcIi4uL3V0aWxzL2NvbnRyb2xsZXJcIjtcbmltcG9ydCB7IERhdGFFeHBsb3JlciB9IGZyb20gXCIuL2RhdGEtZXhwbG9yZXJcIjtcbmltcG9ydCB7IHR5cGUgRXhwbG9yZVdpdGhSZWxhdGlvbnNPcHRpb25zLCB0eXBlIEV4cGxvcmVXaXRoUmVsYXRpb25zUmVzdWx0IH0gZnJvbSBcIi4vZGF0YS1leHBsb3JlclwiO1xuaW1wb3J0IHsgZmFrZXJNYXBwaW5ncyB9IGZyb20gXCIuL2Zha2VyLW1hcHBpbmdzXCI7XG5pbXBvcnQgeyB0eXBlIEZha2VyTWFwcGluZ3MgfSBmcm9tIFwiLi9mYWtlci1tYXBwaW5nc1wiO1xuaW1wb3J0IHsgRml4dHVyZU1hbmFnZXIgfSBmcm9tIFwiLi9maXh0dXJlLW1hbmFnZXJcIjtcblxuZXhwb3J0IHR5cGUgTG9jYWxlID0gXCJrb1wiIHwgXCJlblwiIHwgXCJqYVwiO1xuXG5leHBvcnQgdHlwZSBGaXh0dXJlR2VuZXJhdG9yT3B0aW9ucyA9IHtcbiAgbG9jYWxlPzogTG9jYWxlO1xuICB1c2VMTE0/OiBib29sZWFuO1xuICBlbmFibGVMTE1DYWNoZT86IGJvb2xlYW47XG4gIGxsbU1vZGVsPzogc3RyaW5nO1xufTtcblxuZXhwb3J0IHR5cGUgR2VuZXJhdG9yQ29udGV4dCA9IHtcbiAgLyoqIOyDneyEsSDspJHsnbggZml4dHVyZeuTpCAo66mU66qo66asIOyDgSkgKi9cbiAgZml4dHVyZXM6IE1hcDxzdHJpbmcsIFJlY29yZDxzdHJpbmcsIHVua25vd24+PjtcblxuICAvKiog7LC47KGwIOuNsOydtO2EsCDsupDsi5wgKERhdGFFeHBsb3JlciDqsrDqs7wpICovXG4gIHJlZmVyZW5jZUNhY2hlOiBNYXA8c3RyaW5nLCBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPltdPjtcblxuICAvKiog7J2066+4IGltcG9ydOuQnCDroIjsvZTrk5zrpbwg7LaU7KCB7ZWY7JesIOykkeuztSBpbXBvcnTrpbwg67Cp7KeA7ZWp64uI64ukICovXG4gIGltcG9ydGVkUmVjb3JkczogU2V0PHN0cmluZz47IC8vIFwiVXNlciMxMjNcIlxufTtcblxuZXhwb3J0IGNsYXNzIEZpeHR1cmVHZW5lcmF0b3Ige1xuICBwcml2YXRlIGRhdGFFeHBsb3JlcjogRGF0YUV4cGxvcmVyO1xuICBwcml2YXRlIGxvY2FsZTogTG9jYWxlO1xuICBwcml2YXRlIG1hcHBpbmdzOiBGYWtlck1hcHBpbmdzO1xuICBwcml2YXRlIGxsbUNhY2hlOiBNYXA8c3RyaW5nLCB1bmtub3duPiA9IG5ldyBNYXAoKTtcbiAgcHJpdmF0ZSBlbnRpdHlDYWNoZTogTWFwPHN0cmluZywgRW50aXR5PiA9IG5ldyBNYXAoKTtcbiAgcHJpdmF0ZSBvcHRpb25zOiBGaXh0dXJlR2VuZXJhdG9yT3B0aW9ucztcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHNvdXJjZURiOiBLbmV4LFxuICAgIC8vIEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVz6rCAIGRiTmFtZSDrrLjsnpDsl7TsnYQg67Cb6riwIOuVjOusuOyXkCDsp4HsoJEg7IKs7Jqp7ZWY7KeAIOyViuyKteuLiOuLpFxuICAgIC8vIOuvuOuemCDtmZXsnqXshLHsnYQg7JyE7ZW0IEFQSSDsi5zqt7jri4jsspjsl5DripQg7Y+s7ZWo7Iuc7Lyw7Iq164uI64ukXG4gICAgX3RhcmdldERiOiBLbmV4LFxuICAgIHByaXZhdGUgdGFyZ2V0RGJOYW1lOiBcImZpeHR1cmVcIiB8IFwidGVzdFwiIHwgXCJwcm9kdWN0aW9uX21hc3RlclwiLFxuICAgIHByaXZhdGUgZW50aXR5TWFuYWdlcjogdHlwZW9mIEVudGl0eU1hbmFnZXIsXG4gICAgb3B0aW9ucz86IEZpeHR1cmVHZW5lcmF0b3JPcHRpb25zLFxuICApIHtcbiAgICB0aGlzLmRhdGFFeHBsb3JlciA9IG5ldyBEYXRhRXhwbG9yZXIoc291cmNlRGIsIGVudGl0eU1hbmFnZXIpO1xuICAgIHRoaXMubG9jYWxlID0gb3B0aW9ucz8ubG9jYWxlIHx8IFwia29cIjtcbiAgICB0aGlzLm1hcHBpbmdzID0gZmFrZXJNYXBwaW5ncztcbiAgICB0aGlzLm9wdGlvbnMgPSB7XG4gICAgICBsb2NhbGU6IG9wdGlvbnM/LmxvY2FsZSB8fCBcImtvXCIsXG4gICAgICB1c2VMTE06IG9wdGlvbnM/LnVzZUxMTSB8fCBmYWxzZSxcbiAgICAgIGVuYWJsZUxMTUNhY2hlOiBvcHRpb25zPy5lbmFibGVMTE1DYWNoZSAhPT0gZmFsc2UsXG4gICAgICBsbG1Nb2RlbDogb3B0aW9ucz8ubGxtTW9kZWwgfHwgXCJjbGF1ZGUtc29ubmV0LTQtNlwiLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogRml4dHVyZSDsg53shLEgKOuLqOydvClcbiAgICogQHJldHVybnMg7IOd7ISx65CcIGZpeHR1cmUg642w7J207YSwICjrqZTrqqjrpqwg7IOBKVxuICAgKi9cbiAgYXN5bmMgZ2VuZXJhdGUoXG4gICAgZW50aXR5TmFtZTogc3RyaW5nLFxuICAgIG92ZXJyaWRlczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fSxcbiAgICBjb250ZXh0OiBHZW5lcmF0b3JDb250ZXh0ID0gdGhpcy5jcmVhdGVDb250ZXh0KCksXG4gICk6IFByb21pc2U8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+IHtcbiAgICAvLyBFbnRpdHkg7LqQ7IuxOiDthYzsiqTtirjsl5DshJwgZW50aXR5IGNvbmUg7IiY7KCV7J20IOuwmOyYgeuQmOuPhOuhnSDrs7TsnqVcbiAgICBsZXQgZW50aXR5ID0gdGhpcy5lbnRpdHlDYWNoZS5nZXQoZW50aXR5TmFtZSk7XG4gICAgaWYgKCFlbnRpdHkpIHtcbiAgICAgIGVudGl0eSA9IHRoaXMuZW50aXR5TWFuYWdlci5nZXQoZW50aXR5TmFtZSk7XG4gICAgICB0aGlzLmVudGl0eUNhY2hlLnNldChlbnRpdHlOYW1lLCBlbnRpdHkpO1xuICAgIH1cblxuICAgIGNvbnN0IHRlbXBJZCA9IGAke2VudGl0eU5hbWV9I3RlbXAjJHtEYXRlLm5vdygpfWA7IC8vIOyehOyLnCBJRFxuXG4gICAgLy8gTExNIHJvdyDri6jsnIQg7IOd7ISx7J2EIOychO2VnCDqs6DsnKAg7YKkICjqsJnsnYAgcm937J2YIO2VhOuTnOuTpOydtCDrj5nsnbztlZwgcm93S2V566W8IOqzteycoClcbiAgICBjb25zdCByb3dLZXkgPSB0aGlzLm9wdGlvbnMudXNlTExNID8gYCR7ZW50aXR5TmFtZX0jcm93IyR7RGF0ZS5ub3coKX1gIDogdW5kZWZpbmVkO1xuXG4gICAgLy8g6rCBIHByb3Drs4Qg6rCSIOyDneyEsVxuICAgIGNvbnN0IGZpeHR1cmU6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG5cbiAgICBmb3IgKGNvbnN0IHByb3Agb2YgZW50aXR5LnByb3BzKSB7XG4gICAgICAvLyBWaXJ0dWFsIHByb3DsnYAg7Iqk7YK1XG4gICAgICBpZiAoXCJ2aXJ0dWFsXCIgaW4gcHJvcCAmJiBwcm9wLnZpcnR1YWwpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIGlkIHByb3Ag7LKY66asXG4gICAgICBpZiAocHJvcC5uYW1lID09PSBcImlkXCIpIHtcbiAgICAgICAgaWYgKFwiY29uZVwiIGluIHByb3AgJiYgcHJvcC5jb25lPy5maXh0dXJlU3RyYXRlZ3kgPT09IFwic2VxdWVuY2VcIikge1xuICAgICAgICAgIC8vIERCIHNlcXVlbmNl6rCAIOyekOuPmSDtlaDri7ntlZjrr4DroZwg7Iqk7YK1IChVc2VyIOuTsSlcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAocHJvcC50eXBlID09PSBcInN0cmluZ1wiKSB7XG4gICAgICAgICAgLy8gREIgREVGQVVMVCDsl4bripQgc3RyaW5nIFBLOiBhbHBoYW51bWVyaWMgMzLsnpAg7IOd7ISxIChiZXR0ZXItYXV0aCDsiqTtg4DsnbwpXG4gICAgICAgICAgY29uc3QgeyBmYWtlcjogX2Zha2VyIH0gPSBhd2FpdCBpbXBvcnQoXCJAZmFrZXItanMvZmFrZXJcIik7XG4gICAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gX2Zha2VyLnN0cmluZy5hbHBoYW51bWVyaWMoMzIpO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGlmIChwcm9wLnR5cGUgPT09IFwidXVpZFwiKSB7XG4gICAgICAgICAgY29uc3QgeyBmYWtlcjogX2Zha2VyIH0gPSBhd2FpdCBpbXBvcnQoXCJAZmFrZXItanMvZmFrZXJcIik7XG4gICAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gX2Zha2VyLnN0cmluZy51dWlkKCk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gaW50ZWdlci9iaWdJbnRlZ2VyIFBLOiBnZW5lcmF0ZUJhdGNo7JeQ7IScIHRlbXBJZOulvCDrhKPsnLzrr4DroZwg7Jes6riw7ISgIOyKpO2CtVxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gb3ZlcnJpZGXqsIAg7J6I7Jy866m0IOyCrOyaqVxuICAgICAgaWYgKHByb3AubmFtZSBpbiBvdmVycmlkZXMpIHtcbiAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gb3ZlcnJpZGVzW3Byb3AubmFtZV07XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICAvLyBjb25l7JeQ7IScIOyDneyEsSDsoITrnrUg7ZmV7J24XG4gICAgICBjb25zdCBjb25lID0gcHJvcC5jb25lO1xuXG4gICAgICAvLyAxLiBSZWxhdGlvbiBwcm9wIOyymOumrFxuICAgICAgaWYgKGlzUmVsYXRpb25Qcm9wKHByb3ApKSB7XG4gICAgICAgIC8vIEJlbG9uZ3NUb09uZSAvIE9uZVRvT25lKGhhc0pvaW5Db2x1bW4p7J2AIEZLIOy7rOufvOuqhSh7cHJvcC5uYW1lfV9pZCnsnLzroZzrj4Qgb3ZlcnJpZGXrpbwg67Cb64qU64ukXG4gICAgICAgIGNvbnN0IGZrQ29sTmFtZSA9IGAke3Byb3AubmFtZX1faWRgO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgZmtDb2xOYW1lIGluIG92ZXJyaWRlcyAmJlxuICAgICAgICAgIChpc0JlbG9uZ3NUb09uZVJlbGF0aW9uUHJvcChwcm9wKSB8fCAoaXNPbmVUb09uZVJlbGF0aW9uUHJvcChwcm9wKSAmJiBwcm9wLmhhc0pvaW5Db2x1bW4pKVxuICAgICAgICApIHtcbiAgICAgICAgICBmaXh0dXJlW2ZrQ29sTmFtZV0gPSBvdmVycmlkZXNbZmtDb2xOYW1lXTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHJlbGF0aW9uVmFsdWUgPSBhd2FpdCB0aGlzLmdlbmVyYXRlUmVsYXRpb25WYWx1ZShlbnRpdHksIHByb3AsIGNvbnRleHQpO1xuICAgICAgICAvLyBCZWxvbmdzVG9PbmUsIE9uZVRvT25lKGhhc0pvaW5Db2x1bW4p7J2YIOqyveyasCBmb3JlaWduIGtleSDsu6zrn7zrqoXsnLzroZwg7KCA7J6lXG4gICAgICAgIGlmIChcbiAgICAgICAgICBpc0JlbG9uZ3NUb09uZVJlbGF0aW9uUHJvcChwcm9wKSB8fFxuICAgICAgICAgIChpc09uZVRvT25lUmVsYXRpb25Qcm9wKHByb3ApICYmIHByb3AuaGFzSm9pbkNvbHVtbilcbiAgICAgICAgKSB7XG4gICAgICAgICAgZml4dHVyZVtgJHtwcm9wLm5hbWV9X2lkYF0gPSByZWxhdGlvblZhbHVlO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGZpeHR1cmVbcHJvcC5uYW1lXSA9IHJlbGF0aW9uVmFsdWU7XG4gICAgICAgIH1cbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIDIuIGNvbmUubm90ZSArIExMTSDsgqzsmqkgKHVzZUxMTeydtOuptCBmaXh0dXJlR2VuZXJhdG9y67O064ukIOyasOyEoClcbiAgICAgIGlmIChjb25lPy5ub3RlICYmIHRoaXMub3B0aW9ucy51c2VMTE0pIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBsbG1WYWx1ZSA9IGF3YWl0IHRoaXMuZ2VuZXJhdGVXaXRoTExNKGNvbmUubm90ZSwgcHJvcCwgZW50aXR5LCByb3dLZXkpO1xuICAgICAgICAgIC8vIHN0cmluZyDtg4DsnoXsnbTqs6AgbGVuZ3RoIOygnOyVveydtCDsnojsnLzrqbQg7LSI6rO8IOyLnCB0cnVuY2F0aW9uXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgdHlwZW9mIGxsbVZhbHVlID09PSBcInN0cmluZ1wiICYmXG4gICAgICAgICAgICBcImxlbmd0aFwiIGluIHByb3AgJiZcbiAgICAgICAgICAgIHR5cGVvZiBwcm9wLmxlbmd0aCA9PT0gXCJudW1iZXJcIiAmJlxuICAgICAgICAgICAgbGxtVmFsdWUubGVuZ3RoID4gcHJvcC5sZW5ndGhcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIGZpeHR1cmVbcHJvcC5uYW1lXSA9IGxsbVZhbHVlLnNsaWNlKDAsIHByb3AubGVuZ3RoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gbGxtVmFsdWU7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICAgIGBbRml4dHVyZUdlbmVyYXRvcl0gTExNIGdlbmVyYXRpb24gZmFpbGVkIGZvciAke2VudGl0eS5pZH0uJHtwcm9wLm5hbWV9LCBmYWxsaW5nIGJhY2sgdG8gZml4dHVyZUdlbmVyYXRvciBvciBkZWZhdWx0YCxcbiAgICAgICAgICAgIGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogZXJyb3IsXG4gICAgICAgICAgKTtcbiAgICAgICAgICAvLyBmYWxsYmFjazogZml4dHVyZUdlbmVyYXRvciDihpIgZml4dHVyZURlZmF1bHQg4oaSIOq4sOuzuOqwkuycvOuhnCDqs4Tsho1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyAzLiBmaXh0dXJlR2VuZXJhdG9yIOyCrOyaqVxuICAgICAgaWYgKGNvbmU/LmZpeHR1cmVHZW5lcmF0b3IpIHtcbiAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gYXdhaXQgdGhpcy5leGVjdXRlR2VuZXJhdG9yKGNvbmUuZml4dHVyZUdlbmVyYXRvciwgcHJvcCwgZW50aXR5KTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIDQuIGZpeHR1cmVEZWZhdWx0IOyCrOyaqVxuICAgICAgaWYgKGNvbmU/LmZpeHR1cmVEZWZhdWx0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgZml4dHVyZVtwcm9wLm5hbWVdID0gY29uZS5maXh0dXJlRGVmYXVsdDtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIDUuIO2DgOyeheuzhCDquLDrs7gg7IOd7ISxXG4gICAgICBmaXh0dXJlW3Byb3AubmFtZV0gPSBhd2FpdCB0aGlzLmdlbmVyYXRlRGVmYXVsdFZhbHVlKHByb3AsIGVudGl0eSk7XG4gICAgfVxuXG4gICAgLy8gNi4gZW1haWwg7ZWE65Oc6rCAIOyeiOqzoCBuYW1lIO2VhOuTnOqwgCDsnojsnLzrqbQsIGVtYWls7J2YIOuhnOy7rCDtjIztirjrpbwgbmFtZSDquLDrsJjsnLzroZwg67O07KCVXG4gICAgaWYgKFwiZW1haWxcIiBpbiBmaXh0dXJlICYmIHR5cGVvZiBmaXh0dXJlLmVtYWlsID09PSBcInN0cmluZ1wiICYmICEoXCJlbWFpbFwiIGluIG92ZXJyaWRlcykpIHtcbiAgICAgIGNvbnN0IG5hbWVWYWx1ZSA9IGZpeHR1cmUubmFtZSB8fCBmaXh0dXJlLnVzZXJuYW1lIHx8IGZpeHR1cmUuZnVsbF9uYW1lIHx8IGZpeHR1cmUubmFtZV9lbjtcbiAgICAgIGlmIChuYW1lVmFsdWUgJiYgdHlwZW9mIG5hbWVWYWx1ZSA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgICBjb25zdCBkb21haW4gPSBmaXh0dXJlLmVtYWlsLnNwbGl0KFwiQFwiKVsxXSB8fCBcImV4YW1wbGUuY29tXCI7XG4gICAgICAgIGNvbnN0IHJvbWFuaXplZCA9IGF3YWl0IHRoaXMucm9tYW5pemVOYW1lKG5hbWVWYWx1ZSk7XG4gICAgICAgIGZpeHR1cmUuZW1haWwgPSBgJHtyb21hbml6ZWR9QCR7ZG9tYWlufWA7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gNy4gcGFzc3dvcmQg7ZWE65OcIOyVlO2YuO2ZlFxuICAgIGlmIChcInBhc3N3b3JkXCIgaW4gZml4dHVyZSAmJiBmaXh0dXJlLnBhc3N3b3JkICYmIHR5cGVvZiBmaXh0dXJlLnBhc3N3b3JkID09PSBcInN0cmluZ1wiKSB7XG4gICAgICBjb25zdCBiY3J5cHQgPSBhd2FpdCBpbXBvcnQoXCJiY3J5cHRcIik7XG4gICAgICBmaXh0dXJlLnBhc3N3b3JkID0gYXdhaXQgYmNyeXB0Lmhhc2goZml4dHVyZS5wYXNzd29yZCwgMTApO1xuICAgIH1cblxuICAgIGNvbnRleHQuZml4dHVyZXMuc2V0KHRlbXBJZCwgZml4dHVyZSk7XG4gICAgcmV0dXJuIGZpeHR1cmU7XG4gIH1cblxuICAvKipcbiAgICogUmVsYXRpb24g6rCSIOyDneyEsSArIOyekOuPmSBJbXBvcnRcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVSZWxhdGlvblZhbHVlKFxuICAgIGVudGl0eTogRW50aXR5LFxuICAgIHByb3A6IEVudGl0eVByb3AsXG4gICAgY29udGV4dDogR2VuZXJhdG9yQ29udGV4dCxcbiAgKTogUHJvbWlzZTxudW1iZXIgfCBudWxsPiB7XG4gICAgaWYgKCFpc1JlbGF0aW9uUHJvcChwcm9wKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGaXh0dXJlR2VuZXJhdG9yOiAke2VudGl0eS5pZH0uJHtwcm9wLm5hbWV9IGlzIG5vdCBhIHJlbGF0aW9uIHByb3BgKTtcbiAgICB9XG5cbiAgICAvLyBCZWxvbmdzVG9PbmUsIE9uZVRvT25lKGhhc0pvaW5Db2x1bW4p66eMIOyymOumrFxuICAgIGlmIChcbiAgICAgICFpc0JlbG9uZ3NUb09uZVJlbGF0aW9uUHJvcChwcm9wKSAmJlxuICAgICAgIShpc09uZVRvT25lUmVsYXRpb25Qcm9wKHByb3ApICYmIHByb3AuaGFzSm9pbkNvbHVtbilcbiAgICApIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IGNvbmUgPSBwcm9wLmNvbmU7XG4gICAgY29uc3QgZGF0YVNvdXJjZSA9IGNvbmU/LmRhdGFTb3VyY2U7XG5cbiAgICAvLyBEYXRhRXhwbG9yZXLroZwg7LC47KGwIOuNsOydtO2EsCDsobDtmowgKHNvdXJjZURiKVxuICAgIC8vIOq0gOqzhCDssrTsnbjsnYQg65Sw65286rCA6riwIOychO2VtCBleHBsb3JlV2l0aFJlbGF0aW9ucyDsgqzsmqlcbiAgICBpZiAoZGF0YVNvdXJjZSkge1xuICAgICAgY29uc3QgY2FjaGVLZXkgPSBgJHtwcm9wLndpdGh9OiR7SlNPTi5zdHJpbmdpZnkoZGF0YVNvdXJjZSl9YDtcblxuICAgICAgaWYgKCFjb250ZXh0LnJlZmVyZW5jZUNhY2hlLmhhcyhjYWNoZUtleSkpIHtcbiAgICAgICAgY29uc3QgZXhwbG9yZVJlc3VsdCA9IGF3YWl0IHRoaXMuZGF0YUV4cGxvcmVyLmV4cGxvcmVXaXRoUmVsYXRpb25zKHByb3Aud2l0aCwge1xuICAgICAgICAgIHN0cmF0ZWd5OiBkYXRhU291cmNlLnN0cmF0ZWd5LFxuICAgICAgICAgIGxpbWl0OlxuICAgICAgICAgICAgKChkYXRhU291cmNlLmNvbmZpZyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCk/LmxpbWl0IGFzXG4gICAgICAgICAgICAgIHwgbnVtYmVyXG4gICAgICAgICAgICAgIHwgdW5kZWZpbmVkKSB8fCAxMCxcbiAgICAgICAgICBpbmNsdWRlUmVsYXRpb25zOiB0cnVlLFxuICAgICAgICAgIG1heERlcHRoOiAzLFxuICAgICAgICAgIC4uLihkYXRhU291cmNlLmNvbmZpZyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCksXG4gICAgICAgIH0pO1xuICAgICAgICBjb250ZXh0LnJlZmVyZW5jZUNhY2hlLnNldChjYWNoZUtleSwgZXhwbG9yZVJlc3VsdC5tYWluLnJlY29yZHMpO1xuXG4gICAgICAgIC8vIOyhsO2ajO2VnCDrjbDsnbTthLDsmYAg6rSA6rOE65CcIOuqqOuToCDsl5Tti7Dti7DrpbwgdGFyZ2V0RGLsl5AgaW1wb3J0XG4gICAgICAgIGF3YWl0IHRoaXMuaW1wb3J0RXhwbG9yZVJlc3VsdChleHBsb3JlUmVzdWx0LCBjb250ZXh0KTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgY2FuZGlkYXRlcyA9IGNvbnRleHQucmVmZXJlbmNlQ2FjaGUuZ2V0KGNhY2hlS2V5KTtcbiAgICAgIGlmIChjYW5kaWRhdGVzICYmIGNhbmRpZGF0ZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAvLyDrnpzrjaTtlZjqsowg7ZWY64KYIOyEoO2DnVxuICAgICAgICBjb25zdCBzZWxlY3RlZCA9IGNhbmRpZGF0ZXNbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogY2FuZGlkYXRlcy5sZW5ndGgpXTtcbiAgICAgICAgcmV0dXJuIHNlbGVjdGVkLmlkIGFzIG51bWJlcjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBkYXRhU291cmNl6rCAIOyXhuydhCDrlYwg7J6Q64+Z7Jy866GcIGZpeHR1cmUgRELsl5DshJwg7KGw7ZqMIOyLnOuPhFxuICAgIC8vIOq0gOqzhCDssrTsnbjsnYQg65Sw65286rCA6riwIOychO2VtCBleHBsb3JlV2l0aFJlbGF0aW9ucyDsgqzsmqlcbiAgICBjb25zdCBhdXRvS2V5ID0gYCR7cHJvcC53aXRofTphdXRvYDtcbiAgICBpZiAoIWNvbnRleHQucmVmZXJlbmNlQ2FjaGUuaGFzKGF1dG9LZXkpKSB7XG4gICAgICAvLyBmaXh0dXJlIERCKHNvdXJjZURiKeyXkOyEnCDsnpDrj5kg7KGw7ZqMICjqtIDqs4Qg7Y+s7ZWoKVxuICAgICAgY29uc3QgYXV0b0V4cGxvcmVSZXN1bHQgPSBhd2FpdCB0aGlzLmRhdGFFeHBsb3Jlci5leHBsb3JlV2l0aFJlbGF0aW9ucyhwcm9wLndpdGgsIHtcbiAgICAgICAgc3RyYXRlZ3k6IFwicmFuZG9tXCIsXG4gICAgICAgIGxpbWl0OiAxMCxcbiAgICAgICAgaW5jbHVkZVJlbGF0aW9uczogdHJ1ZSxcbiAgICAgICAgbWF4RGVwdGg6IDMsXG4gICAgICB9KTtcbiAgICAgIGNvbnRleHQucmVmZXJlbmNlQ2FjaGUuc2V0KGF1dG9LZXksIGF1dG9FeHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcyk7XG5cbiAgICAgIC8vIOyhsO2ajO2VnCDrjbDsnbTthLDsmYAg6rSA6rOE65CcIOuqqOuToCDsl5Tti7Dti7DrpbwgdGFyZ2V0RGLsl5AgaW1wb3J0XG4gICAgICBpZiAoYXV0b0V4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgYXdhaXQgdGhpcy5pbXBvcnRFeHBsb3JlUmVzdWx0KGF1dG9FeHBsb3JlUmVzdWx0LCBjb250ZXh0KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBhdXRvQ2FuZGlkYXRlcyA9IGNvbnRleHQucmVmZXJlbmNlQ2FjaGUuZ2V0KGF1dG9LZXkpO1xuICAgIGlmIChhdXRvQ2FuZGlkYXRlcyAmJiBhdXRvQ2FuZGlkYXRlcy5sZW5ndGggPiAwKSB7XG4gICAgICAvLyDrnpzrjaTtlZjqsowg7ZWY64KYIOyEoO2DnVxuICAgICAgY29uc3Qgc2VsZWN0ZWQgPSBhdXRvQ2FuZGlkYXRlc1tNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBhdXRvQ2FuZGlkYXRlcy5sZW5ndGgpXTtcbiAgICAgIHJldHVybiBzZWxlY3RlZC5pZCBhcyBudW1iZXI7XG4gICAgfVxuXG4gICAgLy8g7LC47KGwIOuNsOydtO2EsOqwgCDsl4bsnLzrqbQgbnVsbCDrsJjtmZggKG51bGxhYmxl7J24IOqyveyasClcbiAgICBpZiAocHJvcC5udWxsYWJsZSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgLy8gbnVsbGFibGXsnbQg7JWE64uI6rOgIOuNsOydtO2EsOuPhCDsl4bsnLzrqbQg7JeQ65+sXG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEZpeHR1cmVHZW5lcmF0b3I6ICR7ZW50aXR5LmlkfS4ke3Byb3AubmFtZX3sl5Ag7ZWE7JqU7ZWcICR7cHJvcC53aXRofSDrjbDsnbTthLDqsIAg7JeG7Iq164uI64ukLiBgICtcbiAgICAgICAgYOuovOyggCAke3Byb3Aud2l0aH3rpbwg7IOd7ISx7ZWY6rGw64KYIGNvbmUuZGF0YVNvdXJjZeulvCDshKTsoJXtlZjshLjsmpQuYCxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4cGxvcmVXaXRoUmVsYXRpb25zIOqysOqzvOulvCB0YXJnZXREYuyXkCBpbXBvcnRcbiAgICpcbiAgICog6rSA6rOEIOyytOyduOydhCDrlLDrnbzqsIQg6rKw6rO8KG1haW4gKyByZWxhdGVkKeulvCDrqqjrkZAgaW1wb3J07ZWp64uI64ukLlxuICAgKiDsnZjsobTshLEg7Iic7ISc64qUIEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVz6rCAIOyekOuPmeycvOuhnCDsspjrpqztlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGltcG9ydEV4cGxvcmVSZXN1bHQoXG4gICAgZXhwbG9yZVJlc3VsdDogRXhwbG9yZVdpdGhSZWxhdGlvbnNSZXN1bHQsXG4gICAgY29udGV4dDogR2VuZXJhdG9yQ29udGV4dCxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgYWxsRml4dHVyZVJlY29yZHM6IEZpeHR1cmVSZWNvcmRbXSA9IFtdO1xuXG4gICAgLy8gMS4gUmVsYXRlZCBlbnRpdGllcyBpbXBvcnQgKENvbXBhbnksIERlcGFydG1lbnQg65OxKVxuICAgIGZvciAoY29uc3QgW2VudGl0eUlkLCByZWNvcmRzXSBvZiBleHBsb3JlUmVzdWx0LnJlbGF0ZWQuZW50cmllcygpKSB7XG4gICAgICBjb25zdCBlbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KGVudGl0eUlkKTtcbiAgICAgIGNvbnN0IHJlY29yZHNUb0ltcG9ydDogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSA9IFtdO1xuXG4gICAgICAhaXNUZXN0KCkgJiZcbiAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgY2hhbGsuY3lhbihgSW1wb3J0aW5nIHJlbGF0ZWQgZW50aXR5OiAke2VudGl0eUlkfSAoJHtyZWNvcmRzLmxlbmd0aH0gcmVjb3JkcylgKSxcbiAgICAgICAgKTtcblxuICAgICAgZm9yIChjb25zdCByZWNvcmQgb2YgcmVjb3Jkcykge1xuICAgICAgICBjb25zdCByZWNvcmRLZXkgPSBgJHtlbnRpdHlJZH0jJHtyZWNvcmQuaWR9YDtcbiAgICAgICAgaWYgKCFjb250ZXh0LmltcG9ydGVkUmVjb3Jkcy5oYXMocmVjb3JkS2V5KSkge1xuICAgICAgICAgIHJlY29yZHNUb0ltcG9ydC5wdXNoKHJlY29yZCk7XG4gICAgICAgICAgY29udGV4dC5pbXBvcnRlZFJlY29yZHMuYWRkKHJlY29yZEtleSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKHJlY29yZHNUb0ltcG9ydC5sZW5ndGggPiAwKSB7XG4gICAgICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIHJlY29yZHNUb0ltcG9ydCkge1xuICAgICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgIGNoYWxrLmdyYXkoXG4gICAgICAgICAgICAgICAgYCAgLSBQcm9jZXNzaW5nICR7ZW50aXR5SWR9IHJlY29yZDpgLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KHJlY29yZCkuc2xpY2UoMCwgMTAwKSxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgY29uc3QgZml4dHVyZVJlY29yZHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5jcmVhdGVGaXh0dXJlUmVjb3JkKFxuICAgICAgICAgICAgZW50aXR5LFxuICAgICAgICAgICAgcmVjb3JkIGFzIHsgaWQ6IG51bWJlciB8IHN0cmluZzsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwgfSxcbiAgICAgICAgICAgIHsgX2RiOiB0aGlzLnNvdXJjZURiLCBzaW5nbGVSZWNvcmQ6IHRydWUgfSxcbiAgICAgICAgICApO1xuICAgICAgICAgIGFsbEZpeHR1cmVSZWNvcmRzLnB1c2goLi4uZml4dHVyZVJlY29yZHMpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gMi4gTWFpbiBlbnRpdHkgaW1wb3J0IChFbXBsb3llZSDrk7EpXG4gICAgY29uc3QgbWFpbkVudGl0eSA9IHRoaXMuZW50aXR5TWFuYWdlci5nZXQoZXhwbG9yZVJlc3VsdC5tYWluLmVudGl0eUlkKTtcbiAgICBjb25zdCBtYWluUmVjb3Jkc1RvSW1wb3J0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPltdID0gW107XG5cbiAgICAhaXNUZXN0KCkgJiZcbiAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICBjaGFsay5jeWFuKFxuICAgICAgICAgIGBJbXBvcnRpbmcgbWFpbiBlbnRpdHk6ICR7ZXhwbG9yZVJlc3VsdC5tYWluLmVudGl0eUlkfSAoJHtleHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcy5sZW5ndGh9IHJlY29yZHMpYCxcbiAgICAgICAgKSxcbiAgICAgICk7XG5cbiAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiBleHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcykge1xuICAgICAgY29uc3QgcmVjb3JkS2V5ID0gYCR7ZXhwbG9yZVJlc3VsdC5tYWluLmVudGl0eUlkfSMke3JlY29yZC5pZH1gO1xuICAgICAgaWYgKCFjb250ZXh0LmltcG9ydGVkUmVjb3Jkcy5oYXMocmVjb3JkS2V5KSkge1xuICAgICAgICBtYWluUmVjb3Jkc1RvSW1wb3J0LnB1c2gocmVjb3JkKTtcbiAgICAgICAgY29udGV4dC5pbXBvcnRlZFJlY29yZHMuYWRkKHJlY29yZEtleSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG1haW5SZWNvcmRzVG9JbXBvcnQubGVuZ3RoID4gMCkge1xuICAgICAgZm9yIChjb25zdCByZWNvcmQgb2YgbWFpblJlY29yZHNUb0ltcG9ydCkge1xuICAgICAgICAhaXNUZXN0KCkgJiZcbiAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgIGNoYWxrLmdyYXkoXG4gICAgICAgICAgICAgIGAgIC0gUHJvY2Vzc2luZyAke2V4cGxvcmVSZXN1bHQubWFpbi5lbnRpdHlJZH0gcmVjb3JkOmAsXG4gICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KHJlY29yZCkuc2xpY2UoMCwgMTAwKSxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKTtcbiAgICAgICAgY29uc3QgZml4dHVyZVJlY29yZHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5jcmVhdGVGaXh0dXJlUmVjb3JkKFxuICAgICAgICAgIG1haW5FbnRpdHksXG4gICAgICAgICAgcmVjb3JkIGFzIHsgaWQ6IG51bWJlciB8IHN0cmluZzsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwgfSxcbiAgICAgICAgICB7IF9kYjogdGhpcy5zb3VyY2VEYiwgc2luZ2xlUmVjb3JkOiB0cnVlIH0sXG4gICAgICAgICk7XG4gICAgICAgIGFsbEZpeHR1cmVSZWNvcmRzLnB1c2goLi4uZml4dHVyZVJlY29yZHMpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIDMuIOuqqOuToCBmaXh0dXJl66W8IO2VnCDrsojsl5Ag7IK97J6FICjsnZjsobTshLEg7Iic7IScIOyekOuPmSDsspjrpqwpXG4gICAgaWYgKGFsbEZpeHR1cmVSZWNvcmRzLmxlbmd0aCA+IDApIHtcbiAgICAgIGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVzKHRoaXMudGFyZ2V0RGJOYW1lLCBhbGxGaXh0dXJlUmVjb3Jkcyk7XG5cbiAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICBjaGFsay5ncmVlbihcbiAgICAgICAgICAgIGBBdXRvLWltcG9ydGVkICR7ZXhwbG9yZVJlc3VsdC5tYWluLmVudGl0eUlkfSB3aXRoIHJlbGF0aW9uczogYCArXG4gICAgICAgICAgICAgIGAke2V4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzLmxlbmd0aH0gbWFpbiArICR7ZXhwbG9yZVJlc3VsdC5yZWxhdGVkLnNpemV9IHJlbGF0ZWQgZW50aXRpZXNgLFxuICAgICAgICAgICksXG4gICAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIGZpeHR1cmVHZW5lcmF0b3Ig7Iuk7ZaJIChGYWtlci5qc+unjCDsp4Dsm5ApXG4gICAqXG4gICAqIGZha2VyLiog7ZiV7Iud7J2YIO2RnO2YhOyLneydhCDslYjsoITtlZjqsowg7YyM7Iux7ZWY7JesIOyLpO2Wie2VqeuLiOuLpC5cbiAgICog7JiIOiBcImZha2VyLmludGVybmV0LmVtYWlsKClcIiDihpIgZmFrZXIuaW50ZXJuZXQuZW1haWwoKVxuICAgKiDsmIg6IFwiZmFrZXIubG9yZW0ud29yZHMoMylcIiDihpIgZmFrZXIubG9yZW0ud29yZHMoMylcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZXhlY3V0ZUdlbmVyYXRvcihcbiAgICBnZW5lcmF0b3I6IHN0cmluZyxcbiAgICBwcm9wOiBFbnRpdHlQcm9wLFxuICAgIGVudGl0eTogRW50aXR5LFxuICApOiBQcm9taXNlPHVua25vd24+IHtcbiAgICAvLyBGYWtlci5qcyDtkZztmITsi53rp4wg7KeA7JuQXG4gICAgaWYgKGdlbmVyYXRvci5zdGFydHNXaXRoKFwiZmFrZXIuXCIpKSB7XG4gICAgICAvLyB1c2VybmFtZeydtOuCmCBuYW1lIO2VhOuTnOuKlCDtlZzqta3slrQgZmFrZXIg7IKs7JqpXG4gICAgICBjb25zdCBpc05hbWVGaWVsZCA9IHByb3AubmFtZSA9PT0gXCJ1c2VybmFtZVwiIHx8IHByb3AubmFtZSA9PT0gXCJuYW1lXCI7XG4gICAgICBjb25zdCBmYWtlck1vZHVsZSA9IGF3YWl0IGltcG9ydChcIkBmYWtlci1qcy9mYWtlclwiKTtcbiAgICAgIGNvbnN0IGZha2VyID0gaXNOYW1lRmllbGQgPyBmYWtlck1vZHVsZS5mYWtlcktPIDogZmFrZXJNb2R1bGUuZmFrZXI7XG4gICAgICBjb25zdCBleHByID0gZ2VuZXJhdG9yLnNsaWNlKDYpOyAvLyBcImZha2VyLlwiIOygnOqxsFxuXG4gICAgICB0cnkge1xuICAgICAgICAvLyDtlajsiJgg6rK966Gc7JmAIOyduOyekCDtjIzsi7FcbiAgICAgICAgY29uc3QgbWF0Y2ggPSBleHByLm1hdGNoKC9eKFtcXHcuXSspKD86XFwoKC4qPylcXCkpPyQvKTtcbiAgICAgICAgaWYgKCFtYXRjaCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBGaXh0dXJlR2VuZXJhdG9yOiBJbnZhbGlkIGZha2VyIGV4cHJlc3Npb24gZm9yICR7cHJvcC5uYW1lfTogJHtnZW5lcmF0b3J9YCxcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgWywgcGF0aCwgYXJnc1N0cl0gPSBtYXRjaDtcbiAgICAgICAgY29uc3QgcGFydHMgPSBwYXRoLnNwbGl0KFwiLlwiKTtcblxuICAgICAgICAvLyBmYWtlciDqsJ3ssrTsl5DshJwg7ZWo7IiYIOywvuq4sFxuICAgICAgICBsZXQgZm46IHVua25vd24gPSBmYWtlcjtcbiAgICAgICAgZm9yIChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgICAgICAgaWYgKHR5cGVvZiBmbiA9PT0gXCJvYmplY3RcIiAmJiBmbiAhPT0gbnVsbCAmJiBwYXJ0IGluIGZuKSB7XG4gICAgICAgICAgICBmbiA9IChmbiBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPilbcGFydF07XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRml4dHVyZUdlbmVyYXRvcjogSW52YWxpZCBmYWtlciBwYXRoIGZvciAke3Byb3AubmFtZX06IGZha2VyLiR7cGF0aH1gKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyDtlajsiJjqsIAg7JWE64uI66m0IOyXkOufrFxuICAgICAgICBpZiAodHlwZW9mIGZuICE9PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZpeHR1cmVHZW5lcmF0b3I6IGZha2VyLiR7cGF0aH0gaXMgbm90IGEgZnVuY3Rpb24gKGZvciAke3Byb3AubmFtZX0pYCk7XG4gICAgICAgIH1cblxuICAgICAgICBsZXQgYXJnczogdW5rbm93bltdID0gW107XG4gICAgICAgIGlmIChhcmdzU3RyPy50cmltKCkpIHtcbiAgICAgICAgICBhcmdzID0gdGhpcy5wYXJzZUdlbmVyYXRvckFyZ3MoYXJnc1N0ciwgcHJvcC5uYW1lKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBmbiguLi5hcmdzKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgY2hhbGsueWVsbG93KFxuICAgICAgICAgICAgICBgRmFpbGVkIHRvIGV4ZWN1dGUgZ2VuZXJhdG9yIFwiJHtnZW5lcmF0b3J9XCIgZm9yICR7cHJvcC5uYW1lfSwgZmFsbGluZyBiYWNrIHRvIGRlZmF1bHQ6YCxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgICBlcnJvcixcbiAgICAgICAgICApO1xuICAgICAgICByZXR1cm4gdGhpcy5nZW5lcmF0ZURlZmF1bHRWYWx1ZShwcm9wLCBlbnRpdHkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIGZha2VyIOydtOyZuOydmCDtkZztmITsi53snYAg7KeA7JuQ7ZWY7KeAIOyViuydjFxuICAgICFpc1Rlc3QoKSAmJlxuICAgICAgY29uc29sZS5sb2coXG4gICAgICAgIGNoYWxrLnllbGxvdyhcbiAgICAgICAgICBgVW5zdXBwb3J0ZWQgZ2VuZXJhdG9yIGV4cHJlc3Npb24gZm9yICR7cHJvcC5uYW1lfTogJHtnZW5lcmF0b3J9LiBPbmx5IGZha2VyLiogZXhwcmVzc2lvbnMgYXJlIHN1cHBvcnRlZC4gVXNpbmcgZGVmYXVsdCB2YWx1ZS5gLFxuICAgICAgICApLFxuICAgICAgKTtcbiAgICByZXR1cm4gdGhpcy5nZW5lcmF0ZURlZmF1bHRWYWx1ZShwcm9wLCBlbnRpdHkpO1xuICB9XG5cbiAgLyoqXG4gICAqIO2VhOuTnOydmCDtg4DsnoXqs7wg7J2066aE7J2EIOu2hOyEne2VmOyXrCDsoIHsoIjtlZwg6riw67O46rCS7J2EIOyDneyEse2VqeuLiOuLpC5cbiAgICpcbiAgICog7Jqw7ISg7Iic7JyEOlxuICAgKiAxLiDtlYTrk5zrqoUg7Yyo7YS0IOunpOy5rSAoc2FsYXJ5LCBidWRnZXQg65OxIOydmOuvuOyeiOuKlCDrjbDsnbTthLApXG4gICAqIDIuIO2KueyImCDsvIDsnbTsiqQgKERlcGFydG1lbnQgbmFtZSDrk7Eg64+E66mU7J24IOyngOyLnSlcbiAgICogMy4g67Cw7Je0IO2DgOyehSAoSlNPTiDrsLDsl7QpXG4gICAqIDQuIEVudW0g7YOA7J6FXG4gICAqIDUuIO2DgOyeheuzhCDquLDrs7jqsJJcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVEZWZhdWx0VmFsdWUocHJvcDogRW50aXR5UHJvcCwgZW50aXR5PzogRW50aXR5KTogUHJvbWlzZTx1bmtub3duPiB7XG4gICAgY29uc3QgZmFrZXJNb2R1bGUgPSBhd2FpdCBpbXBvcnQoXCJAZmFrZXItanMvZmFrZXJcIik7XG4gICAgY29uc3QgZmFrZXIgPSBmYWtlck1vZHVsZS5mYWtlcjtcbiAgICBjb25zdCBmYWtlcktPID0gZmFrZXJNb2R1bGUuZmFrZXJLTztcbiAgICBjb25zdCBmYWtlckpBID0gZmFrZXJNb2R1bGUuZmFrZXJKQTtcblxuICAgIGNvbnN0IGxvY2FsZUZha2VyID0gdGhpcy5sb2NhbGUgPT09IFwia29cIiA/IGZha2VyS08gOiB0aGlzLmxvY2FsZSA9PT0gXCJqYVwiID8gZmFrZXJKQSA6IGZha2VyO1xuXG4gICAgLyoqXG4gICAgICogMS4gRW50aXR5LXNwZWNpZmljIO2KueyImCDsvIDsnbTsiqTrpbwg66i87KCAIOyymOumrO2VqeuLiOuLpC5cbiAgICAgKiBmaWVsZF9wYXR0ZXJuc+uztOuLpCDsmrDshKDtlZjsl6wsIO2KueyglSDsl5Tti7Dti7DsnZgg7ZWE65Oc7JeQIOuPhOuplOyduOyXkCDrp57ripQg6rCS7J2EIOyDneyEse2VqeuLiOuLpC5cbiAgICAgKiDsmIg6IERlcGFydG1lbnQubmFtZSDihpIg7ZWc6rWt7Ja0IOu2gOyEnOuqhSAo7IKs656MIOydtOumhOydtCDslYTri5gpXG4gICAgICovXG5cbiAgICAvKipcbiAgICAgKiBEZXBhcnRtZW50IG5hbWXsnYAg7ZWc6rWt7Ja0IOu2gOyEnOuqhSDrqqnroZ3sl5DshJwg7ISg7YOd7ZWp64uI64ukLlxuICAgICAqIOqzoOycoOyEseydhCDsnITtlbQgNzAlIO2ZleuloOuhnCBwcmVmaXgvc3VmZml466W8IOy2lOqwgO2VqeuLiOuLpC5cbiAgICAgKi9cbiAgICBpZiAoZW50aXR5Py5pZCA9PT0gXCJEZXBhcnRtZW50XCIgJiYgcHJvcC5uYW1lID09PSBcIm5hbWVcIikge1xuICAgICAgY29uc3QgZGVwYXJ0bWVudHMgPSBbXG4gICAgICAgIFwi6rCc67Cc7YyAXCIsXG4gICAgICAgIFwi6riw7ZqN7YyAXCIsXG4gICAgICAgIFwi66eI7LyA7YyF7YyAXCIsXG4gICAgICAgIFwi7JiB7JeF7YyAXCIsXG4gICAgICAgIFwi7J247IKs7YyAXCIsXG4gICAgICAgIFwi7LSd66y07YyAXCIsXG4gICAgICAgIFwi7J6s66y07YyAXCIsXG4gICAgICAgIFwi7ZqM6rOE7YyAXCIsXG4gICAgICAgIFwi67KV66y07YyAXCIsXG4gICAgICAgIFwi65SU7J6Q7J247YyAXCIsXG4gICAgICAgIFwiSVTtjIBcIixcbiAgICAgICAgXCLqs6DqsJ3sp4Dsm5DtjIBcIixcbiAgICAgICAgXCLtkojsp4jqtIDrpqztjIBcIixcbiAgICAgICAgXCLsl7DqtazqsJzrsJztjIBcIixcbiAgICAgICAgXCLsg53sgrDtjIBcIixcbiAgICAgICAgXCLqtazrp6TtjIBcIixcbiAgICAgICAgXCLrrLzrpZjtjIBcIixcbiAgICAgIF07XG4gICAgICBjb25zdCBwcmVmaXhlcyA9IFtcIuyLoOq3nFwiLCBcIu2Gte2VqVwiLCBcIuyghOuetVwiLCBcIuq4gOuhnOuyjFwiLCBcIuuUlOyngO2EuFwiLCBcIu2VteyLrFwiXTtcbiAgICAgIGNvbnN0IHN1ZmZpeGVzID0gW1wiMe2MgFwiLCBcIjLtjIBcIiwgXCIz7YyAXCIsIFwiQe2MgFwiLCBcIkLtjIBcIiwgXCLrs7jrtoBcIiwgXCLshLzthLBcIiwgXCLqt7jro7lcIl07XG5cbiAgICAgIGNvbnN0IGRlcHQgPSBmYWtlci5oZWxwZXJzLmFycmF5RWxlbWVudChkZXBhcnRtZW50cyk7XG5cbiAgICAgIGNvbnN0IHJhbmRvbSA9IE1hdGgucmFuZG9tKCk7XG4gICAgICBpZiAocmFuZG9tID4gMC43KSB7XG4gICAgICAgIGNvbnN0IHByZWZpeCA9IGZha2VyLmhlbHBlcnMuYXJyYXlFbGVtZW50KHByZWZpeGVzKTtcbiAgICAgICAgcmV0dXJuIGAke3ByZWZpeH0gJHtkZXB0fWA7XG4gICAgICB9XG4gICAgICBpZiAocmFuZG9tID4gMC40KSB7XG4gICAgICAgIGNvbnN0IHN1ZmZpeCA9IGZha2VyLmhlbHBlcnMuYXJyYXlFbGVtZW50KHN1ZmZpeGVzKTtcbiAgICAgICAgcmV0dXJuIGAke2RlcHR9ICR7c3VmZml4fWA7XG4gICAgICB9XG4gICAgICByZXR1cm4gZGVwdDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiAyLiDtlYTrk5zrqoXsl5DshJwg7J2Y66+466W8IOy2lOuhoO2VmOyXrCDtmITsi6TsoIHsnbgg642w7J207YSw66W8IOyDneyEse2VqeuLiOuLpC5cbiAgICAgKiDsmIg6IHNhbGFyeSDihpIgMzBNfjE1ME0gKO2VnOq1rSDsl7DrtIkg67KU7JyEKVxuICAgICAqICAgICBidWRnZXQg4oaSIDEwTX41MDBNICjtlITroZzsoJ3tirgg7JiI7IKwIOuylOychClcbiAgICAgKi9cbiAgICBjb25zdCBsb2NhbGVNYXBwaW5ncyA9IHRoaXMubWFwcGluZ3NbdGhpcy5sb2NhbGVdIHx8IHRoaXMubWFwcGluZ3MuZW47XG4gICAgY29uc3Qgbm9ybWFsaXplZE5hbWUgPSBwcm9wLm5hbWUudG9Mb3dlckNhc2UoKS5yZXBsYWNlKC9fL2csIFwiXCIpO1xuXG4gICAgZm9yIChjb25zdCBbcGF0dGVybiwgY29uZmlnXSBvZiBPYmplY3QuZW50cmllcyhsb2NhbGVNYXBwaW5ncy5maWVsZF9wYXR0ZXJucykpIHtcbiAgICAgIGlmIChub3JtYWxpemVkTmFtZS5pbmNsdWRlcyhwYXR0ZXJuLnRvTG93ZXJDYXNlKCkpKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZXhlY3V0ZUZha2VyRXhwcmVzc2lvbihjb25maWcuZmFrZXIsIHByb3ApO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgIGNoYWxrLnllbGxvdyhcbiAgICAgICAgICAgICAgICBgRmFpbGVkIHRvIGV4ZWN1dGUgZmllbGQgcGF0dGVybiBcIiR7cGF0dGVybn1cIiBmb3IgJHtwcm9wLm5hbWV9LCBmYWxsaW5nIGJhY2s6YCxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgZXJyb3IsXG4gICAgICAgICAgICApO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogMy4gSlNPTiDtg4DsnoXsnbTrqbTshJwg67Cw7Je07J24IOqyveyasCAoU29uYW11RmlsZVtdLCBzdHJpbmdbXSDrk7EpXG4gICAgICog7ZWE65Oc66qFIO2MqO2EtOydhCDrs7Tqs6Ag7KCB7KCI7ZWcIOuwsOyXtCDrjbDsnbTthLDrpbwg7IOd7ISx7ZWp64uI64ukLlxuICAgICAqL1xuICAgIGlmIChwcm9wLnR5cGUgPT09IFwianNvblwiICYmIFwiaWRcIiBpbiBwcm9wICYmIHByb3AuaWQpIHtcbiAgICAgIGlmIChwcm9wLmlkLmVuZHNXaXRoKFwiW11cIikpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2VuZXJhdGVBcnJheVZhbHVlKHByb3AsIGVudGl0eSwgZmFrZXIsIGxvY2FsZUZha2VyKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogNC4gRW51bSDtg4DsnoXsnYAg7KCV7J2Y65CcIOqwkiDspJEg7ZWY64KY66W8IOuenOuNpCDshKDtg53tlanri4jri6QgKi9cbiAgICBpZiAocHJvcC50eXBlID09PSBcImVudW1cIikge1xuICAgICAgbGV0IGVudW1WYWx1ZXM6IHN0cmluZ1tdID0gW107XG5cbiAgICAgIGlmIChcImVudW1cIiBpbiBwcm9wICYmIEFycmF5LmlzQXJyYXkocHJvcC5lbnVtKSAmJiBwcm9wLmVudW0ubGVuZ3RoID4gMCkge1xuICAgICAgICBlbnVtVmFsdWVzID0gcHJvcC5lbnVtO1xuICAgICAgfSBlbHNlIGlmIChcImlkXCIgaW4gcHJvcCAmJiBwcm9wLmlkICYmIGVudGl0eT8uZW51bUxhYmVscz8uW3Byb3AuaWRdKSB7XG4gICAgICAgIGVudW1WYWx1ZXMgPSBPYmplY3Qua2V5cyhlbnRpdHkuZW51bUxhYmVsc1twcm9wLmlkXSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbnVtVmFsdWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIGZha2VyLmhlbHBlcnMuYXJyYXlFbGVtZW50KGVudW1WYWx1ZXMpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHByb3AubnVsbGFibGUgPyBudWxsIDogXCJVTktOT1dOXCI7XG4gICAgfVxuXG4gICAgaWYgKHByb3AudHlwZSA9PT0gXCJlbnVtW11cIikge1xuICAgICAgbGV0IGVudW1WYWx1ZXM6IHN0cmluZ1tdID0gW107XG5cbiAgICAgIGlmIChcImVudW1cIiBpbiBwcm9wICYmIEFycmF5LmlzQXJyYXkocHJvcC5lbnVtKSAmJiBwcm9wLmVudW0ubGVuZ3RoID4gMCkge1xuICAgICAgICBlbnVtVmFsdWVzID0gcHJvcC5lbnVtO1xuICAgICAgfSBlbHNlIGlmIChcImlkXCIgaW4gcHJvcCAmJiBwcm9wLmlkICYmIGVudGl0eT8uZW51bUxhYmVscz8uW3Byb3AuaWRdKSB7XG4gICAgICAgIGVudW1WYWx1ZXMgPSBPYmplY3Qua2V5cyhlbnRpdHkuZW51bUxhYmVsc1twcm9wLmlkXSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbnVtVmFsdWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIFtmYWtlci5oZWxwZXJzLmFycmF5RWxlbWVudChlbnVtVmFsdWVzKV07XG4gICAgICB9XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogNS4gVmVjdG9yIO2DgOyeheydgCDtmITsnqwg7KeA7JuQ7ZWY7KeAIOyViuycvOuvgOuhnCBudWxs7J2EIOuwmO2ZmO2VqeuLiOuLpC5cbiAgICAgKiDtlqXtm4QgQUkgZW1iZWRkaW5nIOyDneyEsSDquLDriqUg7LaU6rCAIOyLnCDqtaztmIQg7JiI7KCV7J6F64uI64ukLlxuICAgICAqL1xuICAgIGlmIChwcm9wLnR5cGUgPT09IFwidmVjdG9yXCIgfHwgcHJvcC50eXBlID09PSBcInZlY3RvcltdXCIgfHwgcHJvcC50eXBlID09PSBcInRzdmVjdG9yXCIpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIC8qKiA2LiDtg4DsnoXrs4Qg6riw67O4IEZha2VyIO2RnO2YhOyLneydhCDsi6Ttlontlanri4jri6QgKi9cbiAgICBjb25zdCB0eXBlRGVmYXVsdCA9IGxvY2FsZU1hcHBpbmdzLnR5cGVfZGVmYXVsdHNbcHJvcC50eXBlXTtcbiAgICBpZiAodHlwZURlZmF1bHQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmV4ZWN1dGVGYWtlckV4cHJlc3Npb24odHlwZURlZmF1bHQuZmFrZXIsIHByb3ApO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgIWlzVGVzdCgpICYmXG4gICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBjaGFsay55ZWxsb3coYEZhaWxlZCB0byBleGVjdXRlIHR5cGUgZGVmYXVsdCBmb3IgJHtwcm9wLnR5cGV9LCB1c2luZyBmYWxsYmFjazpgLCBlcnJvciksXG4gICAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogNy4g66ek7ZWR65CY7KeAIOyViuydgCDtg4DsnoXsnYAg6riw67O4IEZha2VyIO2VqOyImOuhnCDsspjrpqztlanri4jri6QgKi9cbiAgICBzd2l0Y2ggKHByb3AudHlwZSkge1xuICAgICAgY2FzZSBcInN0cmluZ1wiOlxuICAgICAgY2FzZSBcInN0cmluZ1tdXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5sb3JlbS53b3JkcygzKTtcbiAgICAgIGNhc2UgXCJpbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5udW1iZXIuaW50KHsgbWluOiAxLCBtYXg6IDEwMDAgfSk7XG4gICAgICBjYXNlIFwiaW50ZWdlcltdXCI6XG4gICAgICAgIHJldHVybiBbZmFrZXIubnVtYmVyLmludCh7IG1pbjogMSwgbWF4OiAxMDAwIH0pXTtcbiAgICAgIGNhc2UgXCJiaWdJbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiBmYWtlci5udW1iZXIuYmlnSW50KHsgbWluOiAxbiwgbWF4OiAxMDAwbiB9KTtcbiAgICAgIGNhc2UgXCJiaWdJbnRlZ2VyW11cIjpcbiAgICAgICAgcmV0dXJuIFtmYWtlci5udW1iZXIuYmlnSW50KHsgbWluOiAxbiwgbWF4OiAxMDAwbiB9KV07XG4gICAgICBjYXNlIFwibnVtYmVyXCI6XG4gICAgICBjYXNlIFwibnVtZXJpY1wiOlxuICAgICAgICByZXR1cm4gZmFrZXIubnVtYmVyLmZsb2F0KHsgbWluOiAwLCBtYXg6IDEwMDAgfSk7XG4gICAgICBjYXNlIFwibnVtYmVyW11cIjpcbiAgICAgIGNhc2UgXCJudW1lcmljW11cIjpcbiAgICAgICAgcmV0dXJuIFtmYWtlci5udW1iZXIuZmxvYXQoeyBtaW46IDAsIG1heDogMTAwMCB9KV07XG4gICAgICBjYXNlIFwiYm9vbGVhblwiOlxuICAgICAgICByZXR1cm4gZmFrZXIuZGF0YXR5cGUuYm9vbGVhbigpO1xuICAgICAgY2FzZSBcImJvb2xlYW5bXVwiOlxuICAgICAgICByZXR1cm4gW2Zha2VyLmRhdGF0eXBlLmJvb2xlYW4oKV07XG4gICAgICBjYXNlIFwiZGF0ZVwiOlxuICAgICAgY2FzZSBcImRhdGVbXVwiOlxuICAgICAgICByZXR1cm4gZmFrZXIuZGF0ZS5wYXN0KCk7XG4gICAgICBjYXNlIFwianNvblwiOlxuICAgICAgICByZXR1cm4ge307XG4gICAgICBjYXNlIFwidXVpZFwiOlxuICAgICAgY2FzZSBcInV1aWRbXVwiOlxuICAgICAgICByZXR1cm4gZmFrZXIuc3RyaW5nLnV1aWQoKTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiDrsLDsl7Qg7YOA7J6F7J2YIOqwkuydhCDsg53shLHtlanri4jri6QuXG4gICAqXG4gICAqIO2DgOyehSBJROyZgCDtlYTrk5zrqoUg7Yyo7YS07J2EIOu2hOyEne2VmOyXrCDsoIHsoIjtlZwg67Cw7Je0IOuNsOydtO2EsOulvCDsg53shLHtlanri4jri6QuXG4gICAqIOyYiDogaW1hZ2VfdXJscyDihpIgW3t1cmwsIG5hbWUsIG1pbWVfdHlwZX0sIC4uLl1cbiAgICogICAgIHRhZ19pZHMg4oaSIFsxLCAyMywgNDVdXG4gICAqL1xuICBwcml2YXRlIGdlbmVyYXRlQXJyYXlWYWx1ZShcbiAgICBwcm9wOiBFbnRpdHlQcm9wLFxuICAgIF9lbnRpdHk6IEVudGl0eSB8IHVuZGVmaW5lZCxcbiAgICBmYWtlcjogdHlwZW9mIGltcG9ydChcIkBmYWtlci1qcy9mYWtlclwiKS5mYWtlcixcbiAgICBfbG9jYWxlRmFrZXI6IHR5cGVvZiBpbXBvcnQoXCJAZmFrZXItanMvZmFrZXJcIikuZmFrZXIsXG4gICk6IHVua25vd25bXSB7XG4gICAgY29uc3QgY291bnQgPSBmYWtlci5udW1iZXIuaW50KHsgbWluOiAxLCBtYXg6IDMgfSk7XG5cbiAgICAvKiogU29uYW11RmlsZVtd7J2AIFNvbmFtdSDrgrTsnqUg7YOA7J6F7Jy866GcIOq1rOyhsOqwgCDsoJXtlbTsoLgg7J6I7Iq164uI64ukICovXG4gICAgaWYgKFwiaWRcIiBpbiBwcm9wICYmIHByb3AuaWQgPT09IFwiU29uYW11RmlsZVtdXCIpIHtcbiAgICAgIHJldHVybiBBcnJheS5mcm9tKHsgbGVuZ3RoOiBjb3VudCB9LCAoKSA9PiAoe1xuICAgICAgICB1cmw6IGZha2VyLmltYWdlLnVybCgpLFxuICAgICAgICBuYW1lOiBmYWtlci5zeXN0ZW0uZmlsZU5hbWUoKSxcbiAgICAgICAgbWltZV90eXBlOiBmYWtlci5oZWxwZXJzLmFycmF5RWxlbWVudChbXG4gICAgICAgICAgXCJpbWFnZS9qcGVnXCIsXG4gICAgICAgICAgXCJpbWFnZS9wbmdcIixcbiAgICAgICAgICBcImltYWdlL2dpZlwiLFxuICAgICAgICAgIFwiYXBwbGljYXRpb24vcGRmXCIsXG4gICAgICAgIF0pLFxuICAgICAgfSkpO1xuICAgIH1cblxuICAgIC8qKiDtlYTrk5zrqoXsl5DshJwg67Cw7Je07J2YIOyaqeuPhOulvCDstpTroaDtlanri4jri6QgKi9cbiAgICBjb25zdCBub3JtYWxpemVkTmFtZSA9IHByb3AubmFtZS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL18vZywgXCJcIik7XG5cbiAgICBpZiAobm9ybWFsaXplZE5hbWUuaW5jbHVkZXMoXCJ1cmxcIikgfHwgbm9ybWFsaXplZE5hbWUuaW5jbHVkZXMoXCJpbWFnZVwiKSkge1xuICAgICAgcmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGNvdW50IH0sICgpID0+IGZha2VyLmludGVybmV0LnVybCgpKTtcbiAgICB9XG5cbiAgICBpZiAobm9ybWFsaXplZE5hbWUuaW5jbHVkZXMoXCJpZFwiKSAmJiBub3JtYWxpemVkTmFtZS5lbmRzV2l0aChcInNcIikpIHtcbiAgICAgIHJldHVybiBBcnJheS5mcm9tKHsgbGVuZ3RoOiBjb3VudCB9LCAoKSA9PiBmYWtlci5udW1iZXIuaW50KHsgbWluOiAxLCBtYXg6IDEwMCB9KSk7XG4gICAgfVxuXG4gICAgaWYgKG5vcm1hbGl6ZWROYW1lLmluY2x1ZGVzKFwidGFnXCIpIHx8IG5vcm1hbGl6ZWROYW1lLmluY2x1ZGVzKFwibmFtZVwiKSkge1xuICAgICAgcmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGNvdW50IH0sICgpID0+IGZha2VyLmxvcmVtLndvcmQoKSk7XG4gICAgfVxuXG4gICAgLyoqIO2MqO2EtCDrp6Tsua3rkJjsp4Ag7JWK7Jy866m0IOu5iCDrsLDsl7TsnYQg67CY7ZmY7ZWp64uI64ukICovXG4gICAgcmV0dXJuIFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIEpTT04g66ek7ZWR7J2YIEZha2VyIO2RnO2YhOyLneydhCDtjIzsi7HtlZjsl6wg7Iuk7ZaJ7ZWp64uI64ukLlxuICAgKlxuICAgKiDtkZztmITsi50g7JiI7IucOlxuICAgKiAtIFwiZmFrZXIuaW50ZXJuZXQuZW1haWwoKVwiIOKGkiDsnbjsnpAg7JeG7J2MXG4gICAqIC0gXCJmYWtlci5udW1iZXIuaW50KHsgbWluOiAxLCBtYXg6IDEwMCB9KVwiIOKGkiBKU09OIOyduOyekFxuICAgKiAtIFwie31cIiDihpIg66as7YSw65+0IOqwkiAoSlNPTi5wYXJzZSlcbiAgICpcbiAgICogZmFrZXJLTywgZmFrZXJKQeuPhCDsp4Dsm5DtlZjsl6wg64uk6rWt7Ja0IOuNsOydtO2EsOulvCDsg53shLHtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGV4ZWN1dGVGYWtlckV4cHJlc3Npb24oZXhwcmVzc2lvbjogc3RyaW5nLCBwcm9wOiBFbnRpdHlQcm9wKTogUHJvbWlzZTx1bmtub3duPiB7XG4gICAgY29uc3QgZmFrZXJNb2R1bGUgPSBhd2FpdCBpbXBvcnQoXCJAZmFrZXItanMvZmFrZXJcIik7XG4gICAgY29uc3QgZmFrZXIgPSBmYWtlck1vZHVsZS5mYWtlcjtcbiAgICBjb25zdCBmYWtlcktPID0gZmFrZXJNb2R1bGUuZmFrZXJLTztcbiAgICBjb25zdCBmYWtlckpBID0gZmFrZXJNb2R1bGUuZmFrZXJKQTtcblxuICAgIC8qKiBGYWtlciDtkZztmITsi53snbQg7JWE64uMIOumrO2EsOuftCDqsJLsnYAgSlNPTuycvOuhnCDtjIzsi7Htlanri4jri6QgKi9cbiAgICBpZiAoIWV4cHJlc3Npb24uc3RhcnRzV2l0aChcImZha2VyXCIpKSB7XG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShleHByZXNzaW9uKTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4gZXhwcmVzc2lvbjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKiog7ZGc7ZiE7Iud7JeQ7IScIEZha2VyIOqwneyytOyZgCDqsr3roZzrpbwg7LaU7Lac7ZWp64uI64ukICovXG4gICAgY29uc3QgbWF0Y2ggPSBleHByZXNzaW9uLm1hdGNoKC9eKGZha2VyfGZha2VyS098ZmFrZXJKQSlcXC4oLio/KSQvKTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZmFrZXIgZXhwcmVzc2lvbjogJHtleHByZXNzaW9ufWApO1xuICAgIH1cblxuICAgIGNvbnN0IFssIGZha2VyTmFtZSwgZXhwcl0gPSBtYXRjaDtcbiAgICBjb25zdCBzZWxlY3RlZEZha2VyID1cbiAgICAgIGZha2VyTmFtZSA9PT0gXCJmYWtlcktPXCIgPyBmYWtlcktPIDogZmFrZXJOYW1lID09PSBcImZha2VySkFcIiA/IGZha2VySkEgOiBmYWtlcjtcblxuICAgIGNvbnN0IGZ1bmNNYXRjaCA9IGV4cHIubWF0Y2goL14oW1xcdy5dKykoPzpcXCgoLio/KVxcKSk/JC8pO1xuICAgIGlmICghZnVuY01hdGNoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZmFrZXIgZXhwcmVzc2lvbiBmb3IgJHtwcm9wLm5hbWV9OiAke2V4cHJlc3Npb259YCk7XG4gICAgfVxuXG4gICAgY29uc3QgWywgcGF0aCwgYXJnc1N0cl0gPSBmdW5jTWF0Y2g7XG4gICAgY29uc3QgcGFydHMgPSBwYXRoLnNwbGl0KFwiLlwiKTtcblxuICAgIC8qKiDsoJAg7ZGc6riw67KVKGRvdCBub3RhdGlvbinsnLzroZwgRmFrZXIg7ZWo7IiY66W8IOywvuyVhOqwkeuLiOuLpCAqL1xuICAgIGxldCBmbjogdW5rbm93biA9IHNlbGVjdGVkRmFrZXI7XG4gICAgZm9yIChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgICBpZiAodHlwZW9mIGZuID09PSBcIm9iamVjdFwiICYmIGZuICE9PSBudWxsICYmIHBhcnQgaW4gZm4pIHtcbiAgICAgICAgZm4gPSAoZm4gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pW3BhcnRdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGZha2VyIHBhdGggZm9yICR7cHJvcC5uYW1lfTogJHtmYWtlck5hbWV9LiR7cGF0aH1gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGZuICE9PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgJHtmYWtlck5hbWV9LiR7cGF0aH0gaXMgbm90IGEgZnVuY3Rpb24gKGZvciAke3Byb3AubmFtZX0pYCk7XG4gICAgfVxuXG4gICAgbGV0IGFyZ3M6IHVua25vd25bXSA9IFtdO1xuICAgIGlmIChhcmdzU3RyPy50cmltKCkpIHtcbiAgICAgIGFyZ3MgPSB0aGlzLnBhcnNlR2VuZXJhdG9yQXJncyhhcmdzU3RyLCBwcm9wLm5hbWUpO1xuICAgIH1cblxuICAgIHJldHVybiBmbiguLi5hcmdzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBmaXh0dXJlSGludOulvCBMTE3sl5Dqsowg7KCE64us7ZWY7JesIO2YhOyLpOyggeyduCDthYzsiqTtirgg642w7J207YSw66W8IOyDneyEse2VqeuLiOuLpC5cbiAgICpcbiAgICogZmFrZXIuanProZzripQg7IOd7ISx7ZWY6riwIOyWtOugpOyatCDrs7XsnqHtlZwg7YWN7Iqk7Yq4KOyekOq4sOyGjOqwnCwg7ISk66qF66y4IOuTsSnrpbxcbiAgICogTExN7J2EIO2ZnOyaqe2VmOyXrCDsg53shLHtlanri4jri6QuIOuPmeydvO2VnCBoaW507JeQIOuMgO2VnCDspJHrs7Ug7Zi47Lac7J2EIOuwqeyngO2VmOq4sCDsnITtlbRcbiAgICog7LqQ7Iux7J2EIOq4sOuzuOycvOuhnCDsp4Dsm5Dtlanri4jri6QgKExMTSBBUEkg67mE7JqpIOygiOqwkCkuXG4gICAqXG4gICAqIGFpIO2MqO2CpOyngOuKlCBkeW5hbWljIGltcG9ydOuhnCDrtojrn6zsmKTrr4DroZwsIHVzZUxMTeydtCBmYWxzZeyduCDqsr3smrBcbiAgICog7J2Y7KG07ISx7J20IOyEpOy5mOuQmOyngCDslYrslYTrj4QgZml4dHVyZSDsg53shLHsnbQg7KCV7IOBIOuPmeyeke2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVXaXRoTExNKFxuICAgIGZpeHR1cmVIaW50OiBzdHJpbmcsXG4gICAgcHJvcDogRW50aXR5UHJvcCxcbiAgICBlbnRpdHk6IEVudGl0eSxcbiAgICByb3dLZXk/OiBzdHJpbmcsXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIC8vIHJvd0tleeqwgCDsnojsnLzrqbQgcm93IOuLqOychCDsg53shLEg7KCE6561IOyCrOyaqVxuICAgIGlmIChyb3dLZXkpIHtcbiAgICAgIGNvbnN0IHJvd0NhY2hlS2V5ID0gYCR7cm93S2V5fToke3Byb3AubmFtZX1gO1xuXG4gICAgICAvLyDsnbTrr7gg7J20IHJvd+yXkCDrjIDtlZwgTExNIO2YuOy2nOydtCDsmYTro4zrkJwg6rK97JqwIOy6kOyLnOyXkOyEnCDrsJTroZwg67CY7ZmYXG4gICAgICBpZiAodGhpcy5sbG1DYWNoZS5oYXMocm93Q2FjaGVLZXkpKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmxsbUNhY2hlLmdldChyb3dDYWNoZUtleSk7XG4gICAgICB9XG5cbiAgICAgIC8vIOyDiCByb3c6IExMTSDrjIDsg4EgcHJvcCDsoITssrTrpbwg7ZWcIOuyiOyXkCDsg53shLFcbiAgICAgIGNvbnN0IGxsbVByb3BzID0gZW50aXR5LnByb3BzLmZpbHRlcigocCkgPT4ge1xuICAgICAgICBpZiAoaXNSZWxhdGlvblByb3AocCkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgaWYgKHAuY29uZT8uZml4dHVyZUdlbmVyYXRvcikgcmV0dXJuIGZhbHNlO1xuICAgICAgICBpZiAocC5uYW1lID09PSBcImlkXCIgJiYgcC5jb25lPy5maXh0dXJlU3RyYXRlZ3kgPT09IFwic2VxdWVuY2VcIikgcmV0dXJuIGZhbHNlO1xuICAgICAgICByZXR1cm4gISFwLmNvbmU/Lm5vdGU7XG4gICAgICB9KTtcblxuICAgICAgLy8gbGxtUHJvcHPqsIAg67mE7Ja07J6I7Jy866m0IOuLqOydvCDtlYTrk5wg67Cp7Iud7Jy866GcIGZhbGxiYWNrXG4gICAgICBpZiAobGxtUHJvcHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgYFtGaXh0dXJlR2VuZXJhdG9yXSBsbG1Qcm9wcyBpcyBlbXB0eSBmb3IgJHtlbnRpdHkuaWR9LiR7cHJvcC5uYW1lfSwgdXNpbmcgc2luZ2xlIGZpZWxkIGZhbGxiYWNrYCxcbiAgICAgICAgICApO1xuICAgICAgICByZXR1cm4gdGhpcy5nZW5lcmF0ZVNpbmdsZVdpdGhMTE0oZml4dHVyZUhpbnQsIHByb3AsIGVudGl0eSk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGFwaUtleSA9IHRoaXMuZ2V0QXBpS2V5KCk7XG4gICAgICBjb25zdCB7IGNyZWF0ZUFudGhyb3BpYyB9ID0gYXdhaXQgaW1wb3J0KFwiQGFpLXNkay9hbnRocm9waWNcIik7XG4gICAgICBjb25zdCB7IGdlbmVyYXRlVGV4dCB9ID0gYXdhaXQgaW1wb3J0KFwiYWlcIik7XG5cbiAgICAgIGNvbnN0IHJvd1Jlc3BvbnNlID0gYXdhaXQgZ2VuZXJhdGVUZXh0KHtcbiAgICAgICAgbW9kZWw6IGNyZWF0ZUFudGhyb3BpYyh7IGFwaUtleSB9KSh0aGlzLm9wdGlvbnMubGxtTW9kZWwgfHwgXCJjbGF1ZGUtc29ubmV0LTQtNlwiKSxcbiAgICAgICAgcHJvbXB0OiB0aGlzLmJ1aWxkUm93TExNUHJvbXB0KGxsbVByb3BzLCBlbnRpdHkpLFxuICAgICAgfSk7XG4gICAgICBpZiAoIXJvd1Jlc3BvbnNlIHx8IHR5cGVvZiByb3dSZXNwb25zZS50ZXh0ICE9PSBcInN0cmluZ1wiKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkludmFsaWQgTExNIHJlc3BvbnNlXCIpO1xuICAgICAgfVxuXG4gICAgICAvLyDsnZHri7XsnYQg7YyM7Iux7ZWY7JesIOqwgSDtlYTrk5zsl5Ag64yA7ZWcIOqysOqzvOulvCDsupDsi5zsl5Ag7KCA7J6lXG4gICAgICBjb25zdCByb3dSZXN1bHQgPSB0aGlzLnBhcnNlUm93TExNUmVzcG9uc2Uocm93UmVzcG9uc2UudGV4dCwgbGxtUHJvcHMpO1xuICAgICAgZm9yIChjb25zdCBbZmllbGROYW1lLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocm93UmVzdWx0KSkge1xuICAgICAgICB0aGlzLmxsbUNhY2hlLnNldChgJHtyb3dLZXl9OiR7ZmllbGROYW1lfWAsIHZhbHVlKTtcbiAgICAgIH1cblxuICAgICAgLy8g7JqU7LKt7ZWcIO2VhOuTnOydmCDqsJIg67CY7ZmYICjsl4bsnLzrqbQg64uo7J28IO2VhOuTnCBmYWxsYmFjaylcbiAgICAgIGlmICh0aGlzLmxsbUNhY2hlLmhhcyhyb3dDYWNoZUtleSkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubGxtQ2FjaGUuZ2V0KHJvd0NhY2hlS2V5KTtcbiAgICAgIH1cblxuICAgICAgLy8g66eM7JW9IHJvdyDsnZHri7Xsl5Ag7J20IO2VhOuTnOqwgCDriITrnb3rkJwg6rK97JqwIOuLqOydvCDtlYTrk5wgZmFsbGJhY2tcbiAgICAgIHJldHVybiB0aGlzLmdlbmVyYXRlU2luZ2xlV2l0aExMTShmaXh0dXJlSGludCwgcHJvcCwgZW50aXR5KTtcbiAgICB9XG5cbiAgICAvLyByb3dLZXnqsIAg7JeG7Jy866m0IOq4sOyhtCDri6jsnbwg7ZWE65OcIOuwqeyLnVxuICAgIHJldHVybiB0aGlzLmdlbmVyYXRlU2luZ2xlV2l0aExMTShmaXh0dXJlSGludCwgcHJvcCwgZW50aXR5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiDri6jsnbwg7ZWE65Oc66W8IExMTeycvOuhnCDsg53shLHtlanri4jri6QgKHJvd0tleSDsl4bsnYQg65WMIGZhbGxiYWNr7JqpKVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBnZW5lcmF0ZVNpbmdsZVdpdGhMTE0oXG4gICAgZml4dHVyZUhpbnQ6IHN0cmluZyxcbiAgICBwcm9wOiBFbnRpdHlQcm9wLFxuICAgIGVudGl0eTogRW50aXR5LFxuICApOiBQcm9taXNlPHVua25vd24+IHtcbiAgICBjb25zdCBjYWNoZUtleSA9IGAke2VudGl0eS5pZH06JHtwcm9wLm5hbWV9OiR7Zml4dHVyZUhpbnR9YDtcbiAgICBpZiAodGhpcy5vcHRpb25zLmVuYWJsZUxMTUNhY2hlICYmIHRoaXMubGxtQ2FjaGUuaGFzKGNhY2hlS2V5KSkge1xuICAgICAgcmV0dXJuIHRoaXMubGxtQ2FjaGUuZ2V0KGNhY2hlS2V5KTtcbiAgICB9XG5cbiAgICBjb25zdCBhcGlLZXkgPSB0aGlzLmdldEFwaUtleSgpO1xuICAgIGNvbnN0IHsgY3JlYXRlQW50aHJvcGljIH0gPSBhd2FpdCBpbXBvcnQoXCJAYWktc2RrL2FudGhyb3BpY1wiKTtcbiAgICBjb25zdCB7IGdlbmVyYXRlVGV4dCB9ID0gYXdhaXQgaW1wb3J0KFwiYWlcIik7XG5cbiAgICBjb25zdCBzaW5nbGVSZXNwb25zZSA9IGF3YWl0IGdlbmVyYXRlVGV4dCh7XG4gICAgICBtb2RlbDogY3JlYXRlQW50aHJvcGljKHsgYXBpS2V5IH0pKHRoaXMub3B0aW9ucy5sbG1Nb2RlbCB8fCBcImNsYXVkZS1zb25uZXQtNC02XCIpLFxuICAgICAgcHJvbXB0OiB0aGlzLmJ1aWxkTExNUHJvbXB0KGZpeHR1cmVIaW50LCBwcm9wLCBlbnRpdHkpLFxuICAgIH0pO1xuICAgIGlmICghc2luZ2xlUmVzcG9uc2UgfHwgdHlwZW9mIHNpbmdsZVJlc3BvbnNlLnRleHQgIT09IFwic3RyaW5nXCIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkludmFsaWQgTExNIHJlc3BvbnNlXCIpO1xuICAgIH1cblxuICAgIGNvbnN0IHZhbHVlID0gdGhpcy5wYXJzZUxMTVJlc3BvbnNlKHNpbmdsZVJlc3BvbnNlLnRleHQsIHByb3AudHlwZSk7XG4gICAgaWYgKHRoaXMub3B0aW9ucy5lbmFibGVMTE1DYWNoZSkge1xuICAgICAgdGhpcy5sbG1DYWNoZS5zZXQoY2FjaGVLZXksIHZhbHVlKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cblxuICAvKipcbiAgICogcm93IOyghOyytOulvCDtlZwg67KI7JeQIOyDneyEse2VmOuKlCBMTE0g7ZSE66Gs7ZSE7Yq466W8IOunjOuTreuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYnVpbGRSb3dMTE1Qcm9tcHQocHJvcHM6IEVudGl0eVByb3BbXSwgZW50aXR5OiBFbnRpdHkpOiBzdHJpbmcge1xuICAgIGNvbnN0IGxvY2FsZSA9IHRoaXMub3B0aW9ucy5sb2NhbGUgfHwgXCJrb1wiO1xuICAgIGNvbnN0IGxhbmd1YWdlID0gbG9jYWxlID09PSBcImtvXCIgPyBcIktvcmVhblwiIDogbG9jYWxlID09PSBcImphXCIgPyBcIkphcGFuZXNlXCIgOiBcIkVuZ2xpc2hcIjtcblxuICAgIGNvbnN0IGZpZWxkRGVzY3JpcHRpb25zID0gcHJvcHNcbiAgICAgIC5tYXAoKHApID0+IHtcbiAgICAgICAgbGV0IGRlc2MgPSBgLSAke3AubmFtZX0gKCR7cC50eXBlfSk6ICR7cC5jb25lPy5ub3RlID8/IFwiXCJ9YDtcbiAgICAgICAgaWYgKFxuICAgICAgICAgIChwLnR5cGUgPT09IFwiZW51bVwiIHx8IHAudHlwZSA9PT0gXCJlbnVtW11cIikgJiZcbiAgICAgICAgICBcImlkXCIgaW4gcCAmJlxuICAgICAgICAgIHAuaWQgJiZcbiAgICAgICAgICBlbnRpdHkuZW51bUxhYmVscz8uW3AuaWRdXG4gICAgICAgICkge1xuICAgICAgICAgIGNvbnN0IHZhbHVlcyA9IE9iamVjdC5rZXlzKGVudGl0eS5lbnVtTGFiZWxzW3AuaWRdKS5qb2luKFwiLCBcIik7XG4gICAgICAgICAgZGVzYyArPSBgIFthbGxvd2VkIHZhbHVlczogJHt2YWx1ZXN9XWA7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRlc2M7XG4gICAgICB9KVxuICAgICAgLmpvaW4oXCJcXG5cIik7XG5cbiAgICAvLyBMTE0g64yA7IOB7J20IOyVhOuLjCBwcm9w65Ok64+EIOunpeudveycvOuhnCDsoJzqs7UgKHJlbGF0aW9uIOygnOyZuClcbiAgICBjb25zdCBvdGhlclByb3BzID0gZW50aXR5LnByb3BzXG4gICAgICAuZmlsdGVyKFxuICAgICAgICAocCkgPT5cbiAgICAgICAgICAhcHJvcHMuaW5jbHVkZXMocCkgJiZcbiAgICAgICAgICAhaXNSZWxhdGlvblByb3AocCkgJiZcbiAgICAgICAgICBwLm5hbWUgIT09IFwiaWRcIiAmJlxuICAgICAgICAgICEoXCJ2aXJ0dWFsXCIgaW4gcCAmJiBwLnZpcnR1YWwpLFxuICAgICAgKVxuICAgICAgLm1hcCgocCkgPT4ge1xuICAgICAgICBsZXQgZGVzYyA9IGAtICR7cC5uYW1lfSAoJHtwLnR5cGV9KWA7XG4gICAgICAgIGlmIChwLmNvbmU/Lm5vdGUpIGRlc2MgKz0gYDogJHtwLmNvbmUubm90ZX1gO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgKHAudHlwZSA9PT0gXCJlbnVtXCIgfHwgcC50eXBlID09PSBcImVudW1bXVwiKSAmJlxuICAgICAgICAgIFwiaWRcIiBpbiBwICYmXG4gICAgICAgICAgcC5pZCAmJlxuICAgICAgICAgIGVudGl0eS5lbnVtTGFiZWxzPy5bcC5pZF1cbiAgICAgICAgKSB7XG4gICAgICAgICAgY29uc3QgdmFsdWVzID0gT2JqZWN0LmtleXMoZW50aXR5LmVudW1MYWJlbHNbcC5pZF0pLmpvaW4oXCIsIFwiKTtcbiAgICAgICAgICBkZXNjICs9IGAgW2FsbG93ZWQgdmFsdWVzOiAke3ZhbHVlc31dYDtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZGVzYztcbiAgICAgIH0pXG4gICAgICAuam9pbihcIlxcblwiKTtcbiAgICBjb25zdCBvdGhlclByb3BzQ29udGV4dCA9IG90aGVyUHJvcHNcbiAgICAgID8gYFxcblxcbk90aGVyIGZpZWxkcyBpbiB0aGlzIGVudGl0eSAoZm9yIGNvbnRleHQsIGRvIE5PVCBnZW5lcmF0ZSB0aGVzZSk6XFxuJHtvdGhlclByb3BzfWBcbiAgICAgIDogXCJcIjtcblxuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gcHJvcHMubWFwKChwKSA9PiBgICBcIiR7cC5uYW1lfVwiOiA8JHtwLnR5cGV9PmApLmpvaW4oXCIsXFxuXCIpO1xuXG4gICAgY29uc3QgZW50aXR5Q29udGV4dCA9IGVudGl0eS5jb25lPy5ub3RlID8gYFxcbkVudGl0eSBkZXNjcmlwdGlvbjogJHtlbnRpdHkuY29uZS5ub3RlfWAgOiBcIlwiO1xuXG4gICAgcmV0dXJuIGBHZW5lcmF0ZSB0ZXN0IGZpeHR1cmUgZGF0YSBmb3IgdGhlICR7ZW50aXR5LmlkfSBlbnRpdHkuIEFsbCBmaWVsZHMgbXVzdCBiZSBjb2hlcmVudCBhbmQgY29uc2lzdGVudCB3aXRoIGVhY2ggb3RoZXIuXG5cbkVudGl0eTogJHtlbnRpdHkuaWR9JHtlbnRpdHlDb250ZXh0fVxuTG9jYWxlOiAke2xvY2FsZX0gKHVzZSAke2xhbmd1YWdlfSBmb3IgdGV4dCBmaWVsZHMpXG5cbkZpZWxkcyB0byBnZW5lcmF0ZTpcbiR7ZmllbGREZXNjcmlwdGlvbnN9JHtvdGhlclByb3BzQ29udGV4dH1cblxuUnVsZXM6XG4tIEFsbCBmaWVsZHMgaW4gYSBzaW5nbGUgcm93IG11c3QgYmUgbG9naWNhbGx5IGNvbnNpc3RlbnQgKGUuZy4gbmFtZS9uYW1lX2VuL25hbWVfY24gc2hvdWxkIHJlcHJlc2VudCB0aGUgc2FtZSBwZXJzb24pXG4tIFJldHVybiBPTkxZIHZhbGlkIEpTT04sIG5vIG1hcmtkb3duIG9yIGV4cGxhbmF0aW9uXG4tIERhdGVzIGluIElTTyA4NjAxIGZvcm1hdFxuLSBVc2UgJHtsYW5ndWFnZX0gZm9yIHRleHQgdW5sZXNzIGZpZWxkIGRlc2NyaXB0aW9uIHNheXMgb3RoZXJ3aXNlXG5cblJldHVybiBleGFjdGx5IHRoaXMgSlNPTiBzaGFwZTpcbntcbiR7b3V0cHV0U2hhcGV9XG59YDtcbiAgfVxuXG4gIC8qKlxuICAgKiByb3cgTExNIOydkeuLteydhCDtjIzsi7HtlZjsl6wg7ZWE65Oc67OEIOqwkuycvOuhnCDrs4DtmZjtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIHBhcnNlUm93TExNUmVzcG9uc2UodGV4dDogc3RyaW5nLCBwcm9wczogRW50aXR5UHJvcFtdKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICAgIGNvbnN0IGpzb25UZXh0ID0gdGV4dFxuICAgICAgLnRyaW0oKVxuICAgICAgLnJlcGxhY2UoL15gYGBqc29uXFxzKi9pLCBcIlwiKVxuICAgICAgLnJlcGxhY2UoL2BgYFxccyokLywgXCJcIilcbiAgICAgIC50cmltKCk7XG5cbiAgICBsZXQgcGFyc2VkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmF3ID0gSlNPTi5wYXJzZShqc29uVGV4dCk7XG4gICAgICBpZiAodHlwZW9mIHJhdyAhPT0gXCJvYmplY3RcIiB8fCByYXcgPT09IG51bGwgfHwgQXJyYXkuaXNBcnJheShyYXcpKSB7XG4gICAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICAgIGNvbnNvbGUud2FybihcIltGaXh0dXJlR2VuZXJhdG9yXSBSb3cgTExNIHJlc3BvbnNlIGlzIG5vdCBhIHBsYWluIG9iamVjdDpcIiwgdGV4dCk7XG4gICAgICAgIHJldHVybiB7fTtcbiAgICAgIH1cbiAgICAgIHBhcnNlZCA9IHJhdyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICB9IGNhdGNoIHtcbiAgICAgICFpc1Rlc3QoKSAmJiBjb25zb2xlLndhcm4oXCJbRml4dHVyZUdlbmVyYXRvcl0gRmFpbGVkIHRvIHBhcnNlIHJvdyBMTE0gcmVzcG9uc2U6XCIsIHRleHQpO1xuICAgICAgcmV0dXJuIHt9O1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IHByb3Agb2YgcHJvcHMpIHtcbiAgICAgIGlmIChwcm9wLm5hbWUgaW4gcGFyc2VkKSB7XG4gICAgICAgIHJlc3VsdFtwcm9wLm5hbWVdID0gdGhpcy5wYXJzZUxMTVJlc3BvbnNlKFN0cmluZyhwYXJzZWRbcHJvcC5uYW1lXSA/PyBcIlwiKSwgcHJvcC50eXBlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByaXZhdGUgYnVpbGRMTE1Qcm9tcHQoaGludDogc3RyaW5nLCBwcm9wOiBFbnRpdHlQcm9wLCBlbnRpdHk6IEVudGl0eSk6IHN0cmluZyB7XG4gICAgY29uc3QgbG9jYWxlID0gdGhpcy5vcHRpb25zLmxvY2FsZSB8fCBcImtvXCI7XG4gICAgY29uc3QgbGFuZ3VhZ2UgPSBsb2NhbGUgPT09IFwia29cIiA/IFwiS29yZWFuXCIgOiBsb2NhbGUgPT09IFwiamFcIiA/IFwiSmFwYW5lc2VcIiA6IFwiRW5nbGlzaFwiO1xuXG4gICAgY29uc3QgZW50aXR5Q29udGV4dCA9IGVudGl0eS5jb25lPy5ub3RlID8gYFxcbkVudGl0eSBjb250ZXh0OiAke2VudGl0eS5jb25lLm5vdGV9YCA6IFwiXCI7XG5cbiAgICBjb25zdCBvdGhlckZpZWxkcyA9IGVudGl0eS5wcm9wc1xuICAgICAgLmZpbHRlcigocCkgPT4gcC5uYW1lICE9PSBwcm9wLm5hbWUgJiYgIWlzUmVsYXRpb25Qcm9wKHApICYmIHAuY29uZT8ubm90ZSlcbiAgICAgIC5tYXAoKHApID0+IGAtICR7cC5uYW1lfSAoJHtwLnR5cGV9KTogJHtwLmNvbmU/Lm5vdGV9YClcbiAgICAgIC5qb2luKFwiXFxuXCIpO1xuICAgIGNvbnN0IG90aGVyRmllbGRzQ29udGV4dCA9IG90aGVyRmllbGRzXG4gICAgICA/IGBcXG5cXG5PdGhlciBmaWVsZHMgaW4gdGhpcyBlbnRpdHkgKGZvciBjb250ZXh0KTpcXG4ke290aGVyRmllbGRzfWBcbiAgICAgIDogXCJcIjtcblxuICAgIGxldCBwcm9tcHQgPSBgR2VuZXJhdGUgdGVzdCBkYXRhIGZvciAke2VudGl0eS5pZH0uJHtwcm9wLm5hbWV9ICh0eXBlOiAke3Byb3AudHlwZX0pJHtlbnRpdHlDb250ZXh0fSR7b3RoZXJGaWVsZHNDb250ZXh0fVxuXG5SZXF1aXJlbWVudDogJHtoaW50fVxuXG5SdWxlczpcbi0gUmV0dXJuIE9OTFkgdGhlIHZhbHVlLCBubyBleHBsYW5hdGlvbiBvciBtYXJrZG93blxuLSBVc2UgJHtsYW5ndWFnZX0gbGFuZ3VhZ2UgaWYgYXBwbGljYWJsZVxuLSBGb3JtYXQ6ICR7dGhpcy5nZXRFeHBlY3RlZEZvcm1hdChwcm9wLnR5cGUpfWA7XG5cbiAgICAvLyBlbnVtIO2DgOyeheyduCDqsr3smrAg6rCA64ql7ZWcIOqwkiDrqqnroZ0g7LaU6rCAXG4gICAgaWYgKHByb3AudHlwZSA9PT0gXCJlbnVtXCIgfHwgcHJvcC50eXBlID09PSBcImVudW1bXVwiKSB7XG4gICAgICBsZXQgZW51bVZhbHVlczogc3RyaW5nW10gPSBbXTtcblxuICAgICAgaWYgKFwiZW51bVwiIGluIHByb3AgJiYgQXJyYXkuaXNBcnJheShwcm9wLmVudW0pICYmIHByb3AuZW51bS5sZW5ndGggPiAwKSB7XG4gICAgICAgIGVudW1WYWx1ZXMgPSBwcm9wLmVudW07XG4gICAgICB9IGVsc2UgaWYgKFwiaWRcIiBpbiBwcm9wICYmIHByb3AuaWQgJiYgZW50aXR5Py5lbnVtTGFiZWxzPy5bcHJvcC5pZF0pIHtcbiAgICAgICAgZW51bVZhbHVlcyA9IE9iamVjdC5rZXlzKGVudGl0eS5lbnVtTGFiZWxzW3Byb3AuaWRdKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGVudW1WYWx1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgICBwcm9tcHQgKz0gYFxcbi0gSU1QT1JUQU5UOiBDaG9vc2UgT05MWSBmcm9tIHRoZXNlIGFsbG93ZWQgdmFsdWVzOiAke2VudW1WYWx1ZXMuam9pbihcIiwgXCIpfWA7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcHJvbXB0ICs9IGBcXG5cXG5FeGFtcGxlOiAke3RoaXMuZ2V0RXhhbXBsZUZvclR5cGUocHJvcC50eXBlLCBsb2NhbGUpfWA7XG5cbiAgICByZXR1cm4gcHJvbXB0O1xuICB9XG5cbiAgcHJpdmF0ZSBwYXJzZUxMTVJlc3BvbnNlKHRleHQ6IHN0cmluZywgcHJvcFR5cGU6IHN0cmluZyk6IHVua25vd24ge1xuICAgIGNvbnN0IGNsZWFuZWQgPSB0ZXh0LnRyaW0oKTtcblxuICAgIC8vIOuwsOyXtCDtg4DsnoUg7LKY66asXG4gICAgaWYgKHByb3BUeXBlLmVuZHNXaXRoKFwiW11cIikpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHBhcnNlZCA9IEpTT04ucGFyc2UoY2xlYW5lZCk7XG4gICAgICAgIGNvbnN0IGJhc2VUeXBlID0gcHJvcFR5cGUuc2xpY2UoMCwgLTIpOyAvLyBcImludGVnZXJbXVwiIC0+IFwiaW50ZWdlclwiXG5cbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkocGFyc2VkKSkge1xuICAgICAgICAgIHJldHVybiBwYXJzZWQubWFwKChpdGVtKSA9PiB7XG4gICAgICAgICAgICAvLyBudWxsL3VuZGVmaW5lZOuKlCDtg4DsnoXrs4Qg6riw67O46rCS7Jy866GcXG4gICAgICAgICAgICBpZiAoaXRlbSA9PT0gbnVsbCB8fCBpdGVtID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0RGVmYXVsdFZhbHVlRm9yVHlwZShiYXNlVHlwZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyDqsJ3ssrTripQgSlNPTi5zdHJpbmdpZnkg7ZuEIO2MjOyLsSAoanNvbiDtg4DsnoXsnbgg6rK97JqwKVxuICAgICAgICAgICAgaWYgKHR5cGVvZiBpdGVtID09PSBcIm9iamVjdFwiKSB7XG4gICAgICAgICAgICAgIHJldHVybiBiYXNlVHlwZSA9PT0gXCJqc29uXCJcbiAgICAgICAgICAgICAgICA/IGl0ZW1cbiAgICAgICAgICAgICAgICA6IHRoaXMucGFyc2VTY2FsYXJWYWx1ZShKU09OLnN0cmluZ2lmeShpdGVtKSwgYmFzZVR5cGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gcHJpbWl0aXZlIOqwkuydgCDrrLjsnpDsl7TroZwg67OA7ZmYIO2bhCDtjIzsi7FcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnBhcnNlU2NhbGFyVmFsdWUoU3RyaW5nKGl0ZW0pLCBiYXNlVHlwZSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyDri6jsnbwg6rCS7J20IOyYqCDqsr3smrAg67Cw7Je066GcIOqwkOyLuOq4sFxuICAgICAgICBpZiAocGFyc2VkID09PSBudWxsIHx8IHBhcnNlZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcmV0dXJuIFt0aGlzLmdldERlZmF1bHRWYWx1ZUZvclR5cGUoYmFzZVR5cGUpXTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gW3RoaXMucGFyc2VTY2FsYXJWYWx1ZShTdHJpbmcocGFyc2VkKSwgYmFzZVR5cGUpXTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMucGFyc2VTY2FsYXJWYWx1ZShjbGVhbmVkLCBwcm9wVHlwZSk7XG4gIH1cblxuICBwcml2YXRlIGdldERlZmF1bHRWYWx1ZUZvclR5cGUocHJvcFR5cGU6IHN0cmluZyk6IHVua25vd24ge1xuICAgIHN3aXRjaCAocHJvcFR5cGUpIHtcbiAgICAgIGNhc2UgXCJpbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiAwO1xuICAgICAgY2FzZSBcImJpZ0ludGVnZXJcIjpcbiAgICAgICAgcmV0dXJuIDBuO1xuICAgICAgY2FzZSBcImZsb2F0XCI6XG4gICAgICBjYXNlIFwibnVtYmVyXCI6XG4gICAgICBjYXNlIFwibnVtZXJpY1wiOlxuICAgICAgICByZXR1cm4gMDtcbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIGNhc2UgXCJkYXRlXCI6XG4gICAgICAgIHJldHVybiBuZXcgRGF0ZSgpO1xuICAgICAgY2FzZSBcImpzb25cIjpcbiAgICAgICAgcmV0dXJuIHt9O1xuICAgICAgY2FzZSBcInV1aWRcIjpcbiAgICAgICAgcmV0dXJuIFwiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwXCI7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gXCJcIjtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHBhcnNlU2NhbGFyVmFsdWUodGV4dDogc3RyaW5nLCBwcm9wVHlwZTogc3RyaW5nKTogdW5rbm93biB7XG4gICAgY29uc3QgY2xlYW5lZCA9IHRleHQudHJpbSgpO1xuXG4gICAgc3dpdGNoIChwcm9wVHlwZSkge1xuICAgICAgY2FzZSBcImludGVnZXJcIjoge1xuICAgICAgICBjb25zdCBudW0gPSBwYXJzZUludChjbGVhbmVkLCAxMCk7XG4gICAgICAgIHJldHVybiBOdW1iZXIuaXNOYU4obnVtKSA/IDAgOiBudW07XG4gICAgICB9XG4gICAgICBjYXNlIFwiYmlnSW50ZWdlclwiOiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmV0dXJuIEJpZ0ludChjbGVhbmVkKTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgcmV0dXJuIDBuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjYXNlIFwiZmxvYXRcIjpcbiAgICAgIGNhc2UgXCJudW1iZXJcIjpcbiAgICAgIGNhc2UgXCJudW1lcmljXCI6IHtcbiAgICAgICAgY29uc3QgbnVtID0gcGFyc2VGbG9hdChjbGVhbmVkKTtcbiAgICAgICAgcmV0dXJuIE51bWJlci5pc05hTihudW0pID8gMCA6IG51bTtcbiAgICAgIH1cbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBjbGVhbmVkLnRvTG93ZXJDYXNlKCkgPT09IFwidHJ1ZVwiO1xuICAgICAgY2FzZSBcImRhdGVcIjoge1xuICAgICAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoY2xlYW5lZCk7XG4gICAgICAgIHJldHVybiBOdW1iZXIuaXNOYU4oZGF0ZS5nZXRUaW1lKCkpID8gbmV3IERhdGUoKSA6IGRhdGU7XG4gICAgICB9XG4gICAgICBjYXNlIFwianNvblwiOlxuICAgICAgICB0cnkge1xuICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGNsZWFuZWQpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICByZXR1cm4gY2xlYW5lZDtcbiAgICAgICAgfVxuICAgICAgY2FzZSBcInV1aWRcIjpcbiAgICAgIGNhc2UgXCJlbnVtXCI6XG4gICAgICAgIHJldHVybiBjbGVhbmVkO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIGNsZWFuZWQ7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIGZha2VyIO2VqOyImCDsnbjsnpAg66y47J6Q7Je07J2EIO2MjOyLse2VmOyXrCDsnbjsnpAg67Cw7Je066GcIOuwmO2ZmO2VqeuLiOuLpC5cbiAgICpcbiAgICogM+uLqOqzhCDsoITrnrU6XG4gICAqIDEuIEpTT04g7KeB7KCRIO2MjOyLsSAo7ZGc7KSAIEpTT04g7ZGc7ZiE7IudKVxuICAgKiAyLiBKUyDqsJ3ssrQg66as7YSw65+0IOKGkiBKU09OIOuzgO2ZmCDtm4Qg7J6s7Iuc64+EIChzaW5nbGUgcXVvdGUsIHVucXVvdGVkIGtleSDsspjrpqwpXG4gICAqIDMuIOuLqOyInCDri6jsnbwg7J247J6QIO2PtOuwsSAo7Iir7J6QLCDrrLjsnpDsl7QpXG4gICAqL1xuICBwcml2YXRlIHBhcnNlR2VuZXJhdG9yQXJncyhhcmdzU3RyOiBzdHJpbmcsIHByb3BOYW1lOiBzdHJpbmcpOiB1bmtub3duW10ge1xuICAgIC8vIDEuIEpTT04g7KeB7KCRIO2MjOyLsVxuICAgIHRyeSB7XG4gICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKGBbJHthcmdzU3RyfV1gKSBhcyB1bmtub3duO1xuICAgICAgcmV0dXJuIEFycmF5LmlzQXJyYXkocGFyc2VkKSA/IHBhcnNlZCA6IFtwYXJzZWRdO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8g6rOE7IaNXG4gICAgfVxuXG4gICAgLy8gMi4gSlMg6rCd7LK0IOumrO2EsOuftCDihpIgSlNPTiDrs4DtmZgg7ZuEIOyerOyLnOuPhFxuICAgIHRyeSB7XG4gICAgICBjb25zdCBqc29uU3RyID0gdGhpcy5jb252ZXJ0SnNMaXRlcmFsVG9Kc29uKGFyZ3NTdHIpO1xuICAgICAgY29uc3QgcGFyc2VkID0gSlNPTi5wYXJzZShgWyR7anNvblN0cn1dYCkgYXMgdW5rbm93bjtcbiAgICAgIHJldHVybiBBcnJheS5pc0FycmF5KHBhcnNlZCkgPyBwYXJzZWQgOiBbcGFyc2VkXTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIOqzhOyGjVxuICAgIH1cblxuICAgIC8vIDMuIOuLqOyInCDri6jsnbwg7J247J6QIO2PtOuwsVxuICAgIGNvbnN0IHRyaW1tZWQgPSBhcmdzU3RyLnRyaW0oKTtcbiAgICBpZiAoIU51bWJlci5pc05hTihOdW1iZXIodHJpbW1lZCkpKSB7XG4gICAgICByZXR1cm4gW051bWJlcih0cmltbWVkKV07XG4gICAgfVxuICAgIGlmIChcbiAgICAgICh0cmltbWVkLnN0YXJ0c1dpdGgoJ1wiJykgJiYgdHJpbW1lZC5lbmRzV2l0aCgnXCInKSkgfHxcbiAgICAgICh0cmltbWVkLnN0YXJ0c1dpdGgoXCInXCIpICYmIHRyaW1tZWQuZW5kc1dpdGgoXCInXCIpKVxuICAgICkge1xuICAgICAgcmV0dXJuIFt0cmltbWVkLnNsaWNlKDEsIC0xKV07XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBGaXh0dXJlR2VuZXJhdG9yOiBDYW5ub3QgcGFyc2UgYXJndW1lbnRzIGZvciAke3Byb3BOYW1lfTogJHthcmdzU3RyfWApO1xuICB9XG5cbiAgLyoqXG4gICAqIEpTIOqwneyytCDrpqzthLDrn7TsnYQgSlNPTuycvOuhnCDrs4DtmZjtlanri4jri6QuXG4gICAqXG4gICAqIOuRkCDqsIDsp4Ag67OA7ZmYOlxuICAgKiAxLiBTaW5nbGUtcXVvdGVkIOusuOyekOyXtCDihpIgZG91YmxlLXF1b3RlZCAo7J207Iqk7LyA7J207ZSEIOyymOumrCDtj6ztlagpXG4gICAqIDIuIFVucXVvdGVkIOqwneyytCDtgqQg4oaSIGRvdWJsZS1xdW90ZWRcbiAgICovXG4gIHByaXZhdGUgY29udmVydEpzTGl0ZXJhbFRvSnNvbihpbnB1dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICAvLyAxLiAndmFsdWUnIOKGkiBcInZhbHVlXCIgKOuCtOu2gCBcIiDsnbTsiqTsvIDsnbTtlIQsIFxcJyDihpIgJylcbiAgICBjb25zdCB3aXRoRG91YmxlUXVvdGVzID0gaW5wdXQucmVwbGFjZShcbiAgICAgIC8nKFteJ1xcXFxdKig/OlxcXFwuW14nXFxcXF0qKSopJy9nLFxuICAgICAgKF8sIGNvbnRlbnQ6IHN0cmluZykgPT4gYFwiJHtjb250ZW50LnJlcGxhY2UoL1wiL2csICdcXFxcXCInKS5yZXBsYWNlKC9cXFxcJy9nLCBcIidcIil9XCJgLFxuICAgICk7XG5cbiAgICAvLyAyLiB7IGtleTog4oaSIHsgXCJrZXlcIjpcbiAgICByZXR1cm4gd2l0aERvdWJsZVF1b3Rlcy5yZXBsYWNlKC8oW3ssXVxccyopKFthLXpBLVpfJF1bXFx3JF0qKShcXHMqOikvZywgJyQxXCIkMlwiJDMnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTb25hbXUuc2VjcmV07J2EIOyasOyEoOycvOuhnCDtlZjqs6AsIOyXhuycvOuptCDtmZjqsr3rs4DsiJjsl5DshJwgQVBJIO2CpOulvCDsnb3sirXri4jri6QuXG4gICAqXG4gICAqIFNvbmFtdS5zZWNyZXTsnYAg7ZSE66Gc7KCd7Yq467OEIOyEpOyglShzb25hbXUuY29uZmlnLnRzKeydtOuvgOuhnCDrjZQg64aS7J2AIOyasOyEoOyInOychOulvCDqsIDsp4DrqbAsXG4gICAqIO2ZmOqyveuzgOyImOuKlCDqsJzrsJwg7ZmY6rK97J2064KYIENJL0NE7JeQ7IScIGZhbGxiYWNr7Jy866GcIOyCrOyaqeuQqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgZ2V0QXBpS2V5KCk6IHN0cmluZyB7XG4gICAgbGV0IGFwaUtleTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHsgU29uYW11IH0gPSByZXF1aXJlKFwiLi4vYXBpXCIpO1xuICAgICAgYXBpS2V5ID0gU29uYW11LnNlY3JldHM/LmFudGhyb3BpY19hcGlfa2V5O1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gU29uYW116rCAIOy0iOq4sO2ZlOuQmOyngCDslYrsnYAg6rK97JqwICjthYzsiqTtirgg7ZmY6rK9IOuTsSlcbiAgICB9XG5cbiAgICBpZiAoIWFwaUtleSkge1xuICAgICAgYXBpS2V5ID0gcHJvY2Vzcy5lbnYuQU5USFJPUElDX0FQSV9LRVk7XG4gICAgfVxuXG4gICAgaWYgKCFhcGlLZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgXCJBTlRIUk9QSUNfQVBJX0tFWSBub3QgZm91bmQuIFNldCBpdCBpbiBlbnZpcm9ubWVudCB2YXJpYWJsZXMgb3IgU29uYW11LnNlY3JldC5hbnRocm9waWNfYXBpX2tleVwiLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICByZXR1cm4gYXBpS2V5O1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRFeHBlY3RlZEZvcm1hdChwcm9wVHlwZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICAvLyDrsLDsl7Qg7YOA7J6FIOyymOumrFxuICAgIGlmIChwcm9wVHlwZS5lbmRzV2l0aChcIltdXCIpKSB7XG4gICAgICBjb25zdCBiYXNlVHlwZSA9IHByb3BUeXBlLnNsaWNlKDAsIC0yKTtcbiAgICAgIGNvbnN0IGJhc2VGb3JtYXQgPSB0aGlzLmdldFNjYWxhckZvcm1hdChiYXNlVHlwZSk7XG4gICAgICByZXR1cm4gYEpTT04gYXJyYXkgb2YgJHtiYXNlRm9ybWF0fSAoZS5nLiwgWyR7dGhpcy5nZXRFeGFtcGxlRm9yVHlwZShiYXNlVHlwZSwgXCJlblwiKX0sIC4uLl0pYDtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5nZXRTY2FsYXJGb3JtYXQocHJvcFR5cGUpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRTY2FsYXJGb3JtYXQocHJvcFR5cGU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgc3dpdGNoIChwcm9wVHlwZSkge1xuICAgICAgY2FzZSBcImludGVnZXJcIjpcbiAgICAgIGNhc2UgXCJiaWdJbnRlZ2VyXCI6XG4gICAgICAgIHJldHVybiBcImludGVnZXIgbnVtYmVyc1wiO1xuICAgICAgY2FzZSBcImZsb2F0XCI6XG4gICAgICBjYXNlIFwibnVtYmVyXCI6XG4gICAgICBjYXNlIFwibnVtZXJpY1wiOlxuICAgICAgICByZXR1cm4gXCJkZWNpbWFsIG51bWJlcnNcIjtcbiAgICAgIGNhc2UgXCJib29sZWFuXCI6XG4gICAgICAgIHJldHVybiBcImJvb2xlYW5zICh0cnVlIG9yIGZhbHNlKVwiO1xuICAgICAgY2FzZSBcImRhdGVcIjpcbiAgICAgICAgcmV0dXJuIFwiSVNPIDg2MDEgZGF0ZSBzdHJpbmdzXCI7XG4gICAgICBjYXNlIFwianNvblwiOlxuICAgICAgICByZXR1cm4gXCJ2YWxpZCBKU09OIG9iamVjdCBvciBhcnJheVwiO1xuICAgICAgY2FzZSBcInV1aWRcIjpcbiAgICAgICAgcmV0dXJuIFwiVVVJRCBzdHJpbmdzXCI7XG4gICAgICBjYXNlIFwiZW51bVwiOlxuICAgICAgICByZXR1cm4gXCJvbmUgb2YgdGhlIGFsbG93ZWQgZW51bSB2YWx1ZXNcIjtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBcInBsYWluIHRleHQgc3RyaW5nc1wiO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZ2V0RXhhbXBsZUZvclR5cGUocHJvcFR5cGU6IHN0cmluZywgbG9jYWxlOiBMb2NhbGUpOiBzdHJpbmcge1xuICAgIC8vIOuwsOyXtCDtg4DsnoUg7LKY66asXG4gICAgaWYgKHByb3BUeXBlLmVuZHNXaXRoKFwiW11cIikpIHtcbiAgICAgIGNvbnN0IGJhc2VUeXBlID0gcHJvcFR5cGUuc2xpY2UoMCwgLTIpO1xuICAgICAgY29uc3QgYmFzZUV4YW1wbGUgPSB0aGlzLmdldFNjYWxhckV4YW1wbGUoYmFzZVR5cGUsIGxvY2FsZSk7XG4gICAgICByZXR1cm4gYFske2Jhc2VFeGFtcGxlfV1gO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLmdldFNjYWxhckV4YW1wbGUocHJvcFR5cGUsIGxvY2FsZSk7XG4gIH1cblxuICBwcml2YXRlIGdldFNjYWxhckV4YW1wbGUocHJvcFR5cGU6IHN0cmluZywgbG9jYWxlOiBMb2NhbGUpOiBzdHJpbmcge1xuICAgIGNvbnN0IGlzS29yZWFuID0gbG9jYWxlID09PSBcImtvXCI7XG5cbiAgICBzd2l0Y2ggKHByb3BUeXBlKSB7XG4gICAgICBjYXNlIFwiaW50ZWdlclwiOlxuICAgICAgY2FzZSBcImJpZ0ludGVnZXJcIjpcbiAgICAgICAgcmV0dXJuIFwiNDJcIjtcbiAgICAgIGNhc2UgXCJmbG9hdFwiOlxuICAgICAgY2FzZSBcIm51bWJlclwiOlxuICAgICAgY2FzZSBcIm51bWVyaWNcIjpcbiAgICAgICAgcmV0dXJuIFwiMy4xNFwiO1xuICAgICAgY2FzZSBcImJvb2xlYW5cIjpcbiAgICAgICAgcmV0dXJuIFwidHJ1ZVwiO1xuICAgICAgY2FzZSBcImRhdGVcIjpcbiAgICAgICAgcmV0dXJuIFwiMjAyNC0wMS0wMVwiO1xuICAgICAgY2FzZSBcImpzb25cIjpcbiAgICAgICAgcmV0dXJuICd7XCJrZXlcIjogXCJ2YWx1ZVwifSc7XG4gICAgICBjYXNlIFwidXVpZFwiOlxuICAgICAgICByZXR1cm4gXCI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDBcIjtcbiAgICAgIGNhc2UgXCJlbnVtXCI6XG4gICAgICAgIHJldHVybiBcIkVOVU1fVkFMVUVcIjtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBpc0tvcmVhbiA/IFwi7JWI64WV7ZWY7IS47JqUXCIgOiBcIkhlbGxvXCI7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIOydtOumhOydhCDsnbTrqZTsnbwg66Gc7LusIO2MjO2KuOyaqSDroZzrp4jrgpjsnbTspojrk5wg66y47J6Q7Je066GcIOuzgO2ZmO2VqeuLiOuLpC5cbiAgICpcbiAgICog7ZWc6riAIOydtOumhOydgCDstIjshLEt7KSR7ISxLeyiheyEsSDrtoTtlbQg7ZuEIOuhnOuniOuCmOydtOymiCDsspjrpqztlanri4jri6QuXG4gICAqIOyYgeusuCDsnbTrpoTsnYAg7IaM66y47J6Q66GcIOuzgO2ZmO2VmOqzoCDqs7XrsLHsnYQg7KCQKC4pXFx1YzczY+uhnCDsuZjtmZjtlanri4jri6QuXG4gICAqIOyYiDogXCLquYDssqDsiJhcIiDihpIgXCJjaGVvbHN1LmtpbVwiLCBcIkpvaG4gRG9lXCIg4oaSIFwiam9obi5kb2VcIlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByb21hbml6ZU5hbWUobmFtZTogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAvLyDtlZzquIAg7Y+s7ZWoIOyXrOu2gCDtmZXsnbhcbiAgICBpZiAoL1tcXHVBQzAwLVxcdUQ3QUZdLy50ZXN0KG5hbWUpKSB7XG4gICAgICByZXR1cm4gdGhpcy5yb21hbml6ZUtvcmVhbk5hbWUobmFtZSk7XG4gICAgfVxuICAgIC8vIOyYgeusuDog7IaM66y47J6QICsg7KCQIOq1rOu2hFxuICAgIHJldHVybiBuYW1lXG4gICAgICAudG9Mb3dlckNhc2UoKVxuICAgICAgLnJlcGxhY2UoL1xccysvZywgXCIuXCIpXG4gICAgICAucmVwbGFjZSgvW15hLXowLTkuXS9nLCBcIlwiKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlZzquIAg7J2066aE7J2EIOuhnOuniOuCmOydtOymiCDsspjrpqztlanri4jri6QuXG4gICAqXG4gICAqIOy0iOyEsS/spJHshLEv7KKF7ISxIOunpO2VkSDthYzsnbTruJTsnYQg7IKs7Jqp7ZWY7JesIO2VnOq4gOydhCDroZzrp4jsnpDroZwg67OA7ZmY7ZWp64uI64ukLlxuICAgKiDssqsg6riA7J6Q66W8IOyEseycvOuhnCDqsITso7ztlZjsl6wgXCLquYDssqDsiJhcIiDihpIgXCJjaGVvbHN1LmtpbVwiIO2Yle2DnOuhnCDstpzroKXtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIHJvbWFuaXplS29yZWFuTmFtZShuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IENIT1NFT05HID0gW1xuICAgICAgXCJnXCIsXG4gICAgICBcImtrXCIsXG4gICAgICBcIm5cIixcbiAgICAgIFwiZFwiLFxuICAgICAgXCJ0dFwiLFxuICAgICAgXCJyXCIsXG4gICAgICBcIm1cIixcbiAgICAgIFwiYlwiLFxuICAgICAgXCJwcFwiLFxuICAgICAgXCJzXCIsXG4gICAgICBcInNzXCIsXG4gICAgICBcIlwiLFxuICAgICAgXCJqXCIsXG4gICAgICBcImpqXCIsXG4gICAgICBcImNoXCIsXG4gICAgICBcImtcIixcbiAgICAgIFwidFwiLFxuICAgICAgXCJwXCIsXG4gICAgICBcImhcIixcbiAgICBdO1xuICAgIGNvbnN0IEpVTkdTRU9ORyA9IFtcbiAgICAgIFwiYVwiLFxuICAgICAgXCJhZVwiLFxuICAgICAgXCJ5YVwiLFxuICAgICAgXCJ5YWVcIixcbiAgICAgIFwiZW9cIixcbiAgICAgIFwiZVwiLFxuICAgICAgXCJ5ZW9cIixcbiAgICAgIFwieWVcIixcbiAgICAgIFwib1wiLFxuICAgICAgXCJ3YVwiLFxuICAgICAgXCJ3YWVcIixcbiAgICAgIFwib2VcIixcbiAgICAgIFwieW9cIixcbiAgICAgIFwidVwiLFxuICAgICAgXCJ3b1wiLFxuICAgICAgXCJ3ZVwiLFxuICAgICAgXCJ3aVwiLFxuICAgICAgXCJ5dVwiLFxuICAgICAgXCJldVwiLFxuICAgICAgXCJ1aVwiLFxuICAgICAgXCJpXCIsXG4gICAgXTtcbiAgICBjb25zdCBKT05HU0VPTkcgPSBbXG4gICAgICBcIlwiLFxuICAgICAgXCJrXCIsXG4gICAgICBcImtcIixcbiAgICAgIFwia1wiLFxuICAgICAgXCJuXCIsXG4gICAgICBcIm5cIixcbiAgICAgIFwiblwiLFxuICAgICAgXCJ0XCIsXG4gICAgICBcImxcIixcbiAgICAgIFwibFwiLFxuICAgICAgXCJsXCIsXG4gICAgICBcImxcIixcbiAgICAgIFwibFwiLFxuICAgICAgXCJsXCIsXG4gICAgICBcImxcIixcbiAgICAgIFwibFwiLFxuICAgICAgXCJtXCIsXG4gICAgICBcInBcIixcbiAgICAgIFwicFwiLFxuICAgICAgXCJ0XCIsXG4gICAgICBcInRcIixcbiAgICAgIFwibmdcIixcbiAgICAgIFwidFwiLFxuICAgICAgXCJ0XCIsXG4gICAgICBcImtcIixcbiAgICAgIFwidFwiLFxuICAgICAgXCJwXCIsXG4gICAgICBcInRcIixcbiAgICBdO1xuXG4gICAgY29uc3Qgcm9tYW5pemUgPSAoY2hhcjogc3RyaW5nKTogc3RyaW5nID0+IHtcbiAgICAgIGNvbnN0IGNvZGUgPSBjaGFyLmNoYXJDb2RlQXQoMCk7XG4gICAgICBpZiAoY29kZSA8IDB4YWMwMCB8fCBjb2RlID4gMHhkN2FmKSByZXR1cm4gY2hhcjtcbiAgICAgIGNvbnN0IG9mZnNldCA9IGNvZGUgLSAweGFjMDA7XG4gICAgICBjb25zdCBjaG8gPSBNYXRoLmZsb29yKG9mZnNldCAvIDU4OCk7XG4gICAgICBjb25zdCBqdW5nID0gTWF0aC5mbG9vcigob2Zmc2V0ICUgNTg4KSAvIDI4KTtcbiAgICAgIGNvbnN0IGpvbmcgPSBvZmZzZXQgJSAyODtcbiAgICAgIHJldHVybiBDSE9TRU9OR1tjaG9dICsgSlVOR1NFT05HW2p1bmddICsgSk9OR1NFT05HW2pvbmddO1xuICAgIH07XG5cbiAgICBjb25zdCBjaGFycyA9IFsuLi5uYW1lXTtcbiAgICAvLyDssqsg6riA7J6Q66W8IOyEsSjlp5Mp7Jy866GcIOqwhOyjvFxuICAgIGNvbnN0IGZhbWlseU5hbWUgPSByb21hbml6ZShjaGFyc1swXSk7XG4gICAgY29uc3QgZ2l2ZW5OYW1lID0gY2hhcnMuc2xpY2UoMSkubWFwKHJvbWFuaXplKS5qb2luKFwiXCIpO1xuXG4gICAgaWYgKGdpdmVuTmFtZSkge1xuICAgICAgcmV0dXJuIGAke2dpdmVuTmFtZX0uJHtmYW1pbHlOYW1lfWA7XG4gICAgfVxuICAgIHJldHVybiBmYW1pbHlOYW1lO1xuICB9XG5cbiAgLyoqXG4gICAqIExMTSDsupDsi5wg7Ya16rOE66W8IOuwmO2ZmO2VqeuLiOuLpC5cbiAgICovXG4gIGdldExMTUNhY2hlU3RhdHMoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNpemU6IHRoaXMubGxtQ2FjaGUuc2l6ZSxcbiAgICAgIGVuYWJsZWQ6IHRoaXMub3B0aW9ucy5lbmFibGVMTE1DYWNoZSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIExMTSDsupDsi5zrpbwg7LSI6riw7ZmU7ZWp64uI64ukLlxuICAgKi9cbiAgY2xlYXJMTE1DYWNoZSgpIHtcbiAgICB0aGlzLmxsbUNhY2hlLmNsZWFyKCk7XG4gIH1cblxuICAvKipcbiAgICog7Luo7YWN7Iqk7Yq4IOyDneyEsVxuICAgKi9cbiAgcHJpdmF0ZSBjcmVhdGVDb250ZXh0KCk6IEdlbmVyYXRvckNvbnRleHQge1xuICAgIHJldHVybiB7XG4gICAgICBmaXh0dXJlczogbmV3IE1hcCgpLFxuICAgICAgcmVmZXJlbmNlQ2FjaGU6IG5ldyBNYXAoKSxcbiAgICAgIGltcG9ydGVkUmVjb3JkczogbmV3IFNldCgpLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICog67Cw7LmYIOyDneyEsSDrsI8g7J6Q64+ZIOyggOyepVxuICAgKlxuICAgKiAxLiDqsIEgc3BlY+uzhOuhnCBmaXh0dXJlIOyDneyEsSAo66mU66qo66asKVxuICAgKiAyLiBGaXh0dXJlUmVjb3Jk66GcIOuzgO2ZmFxuICAgKiAzLiBGaXh0dXJlTWFuYWdlci5pbnNlcnRGaXh0dXJlcygp66GcIHRhcmdldERi7JeQIOyggOyepVxuICAgKlxuICAgKiBAcmV0dXJucyDsoIDsnqXrkJwgZml4dHVyZSDrjbDsnbTthLAgKOyLpOygnCBEQiBJRCDtj6ztlagpXG4gICAqL1xuICBhc3luYyBnZW5lcmF0ZUJhdGNoKFxuICAgIHNwZWNzOiBBcnJheTx7IGVudGl0eTogc3RyaW5nOyBjb3VudDogbnVtYmVyOyBvdmVycmlkZXM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9PixcbiAgKTogUHJvbWlzZTxGaXh0dXJlSW1wb3J0UmVzdWx0W10+IHtcbiAgICBjb25zdCBjb250ZXh0ID0gdGhpcy5jcmVhdGVDb250ZXh0KCk7XG4gICAgY29uc3QgZ2VuZXJhdGVkRml4dHVyZXM6IEFycmF5PHtcbiAgICAgIGVudGl0eTogc3RyaW5nO1xuICAgICAgZGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgICBleHBsaWNpdElkPzogYm9vbGVhbjtcbiAgICB9PiA9IFtdO1xuXG4gICAgLy8gMS4g6rCBIHNwZWPrs4TroZwgZml4dHVyZSDsg53shLFcbiAgICBmb3IgKGNvbnN0IHNwZWMgb2Ygc3BlY3MpIHtcbiAgICAgIGNvbnN0IHNwZWNFbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KHNwZWMuZW50aXR5KTtcblxuICAgICAgaWYgKHNwZWNFbnRpdHkucGFyZW50SWQpIHtcbiAgICAgICAgLy8gcGFyZW50SWQg7JeU7Yuw7YuwOiBEQuyXkOyEnCDshJzruIztg4DsnoUg7ZaJ7J20IOyXhuuKlCDrtoDrqqggaWTrpbwg7KGw7ZqM7ZWY7JesIOyCrOyaqVxuICAgICAgICAvLyAo7IOIIOu2gOuqqCDsg53shLEg64yA7IugIOq4sOyhtCDrjbDsnbTthLAg7J6s7Zmc7JqpKVxuICAgICAgICBjb25zdCBpZFByb3AgPSBzcGVjRW50aXR5LnByb3BzLmZpbmQoKHApID0+IHAubmFtZSA9PT0gXCJpZFwiKTtcbiAgICAgICAgY29uc3QgcGFyZW50T3ZlcnJpZGVzID0gaWRQcm9wPy5jb25lPy5maXh0dXJlUGFyZW50T3ZlcnJpZGVzID8/IHt9O1xuICAgICAgICBjb25zdCBwYXJlbnRFbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KHNwZWNFbnRpdHkucGFyZW50SWQpO1xuXG4gICAgICAgIC8vIOu2gOuqqCDthYzsnbTruJTsl5DshJwg7ISc67iM7YOA7J6FIO2FjOydtOu4lOyXkCDsl4bripQgaWTrpbwg7KGw7ZqMXG4gICAgICAgIGxldCBxdWVyeSA9IHRoaXMuc291cmNlRGIocGFyZW50RW50aXR5LnRhYmxlKS5zZWxlY3QoYCR7cGFyZW50RW50aXR5LnRhYmxlfS5pZGApO1xuICAgICAgICBmb3IgKGNvbnN0IFtjb2wsIHZhbF0gb2YgT2JqZWN0LmVudHJpZXMocGFyZW50T3ZlcnJpZGVzKSkge1xuICAgICAgICAgIHF1ZXJ5ID0gcXVlcnkud2hlcmUoXG4gICAgICAgICAgICBgJHtwYXJlbnRFbnRpdHkudGFibGV9LiR7Y29sfWAsXG4gICAgICAgICAgICB2YWwgYXMgc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwsXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBxdWVyeSA9IHF1ZXJ5XG4gICAgICAgICAgLmxlZnRKb2luKHNwZWNFbnRpdHkudGFibGUsIGAke3NwZWNFbnRpdHkudGFibGV9LmlkYCwgYCR7cGFyZW50RW50aXR5LnRhYmxlfS5pZGApXG4gICAgICAgICAgLndoZXJlTnVsbChgJHtzcGVjRW50aXR5LnRhYmxlfS5pZGApXG4gICAgICAgICAgLmxpbWl0KHNwZWMuY291bnQpO1xuXG4gICAgICAgIGNvbnN0IHJvd3MgPSBhd2FpdCBxdWVyeTtcbiAgICAgICAgY29uc3QgYXZhaWxhYmxlSWRzOiBudW1iZXJbXSA9IHJvd3MubWFwKChyOiB7IGlkOiBudW1iZXIgfSkgPT4gci5pZCk7XG5cbiAgICAgICAgaWYgKGF2YWlsYWJsZUlkcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAhaXNUZXN0KCkgJiZcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICAgICAgY2hhbGsueWVsbG93KFxuICAgICAgICAgICAgICAgIGBbcGFyZW50SWRdICR7c3BlYy5lbnRpdHl9OiDshJzruIztg4DsnoXsnbQg7JeG64qUIOu2gOuqqCDroIjsvZTrk5zqsIAg67aA7KGx7ZWp64uI64ukLiDqsbTrhIjrnIHri4jri6QuYCxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZm9yIChjb25zdCBwYXJlbnRJZCBvZiBhdmFpbGFibGVJZHMpIHtcbiAgICAgICAgICAgIGNvbnN0IGZpeHR1cmUgPSBhd2FpdCB0aGlzLmdlbmVyYXRlKHNwZWMuZW50aXR5LCBzcGVjLm92ZXJyaWRlcyB8fCB7fSwgY29udGV4dCk7XG4gICAgICAgICAgICBmaXh0dXJlLmlkID0gcGFyZW50SWQ7XG4gICAgICAgICAgICBnZW5lcmF0ZWRGaXh0dXJlcy5wdXNoKHsgZW50aXR5OiBzcGVjLmVudGl0eSwgZGF0YTogZml4dHVyZSwgZXhwbGljaXRJZDogdHJ1ZSB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc3BlYy5jb3VudDsgaSsrKSB7XG4gICAgICAgICAgY29uc3QgZml4dHVyZSA9IGF3YWl0IHRoaXMuZ2VuZXJhdGUoc3BlYy5lbnRpdHksIHNwZWMub3ZlcnJpZGVzIHx8IHt9LCBjb250ZXh0KTtcbiAgICAgICAgICBnZW5lcmF0ZWRGaXh0dXJlcy5wdXNoKHtcbiAgICAgICAgICAgIGVudGl0eTogc3BlYy5lbnRpdHksXG4gICAgICAgICAgICBkYXRhOiBmaXh0dXJlLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gMi4gRml4dHVyZVJlY29yZOuhnCDrs4DtmZhcbiAgICBjb25zdCBmaXh0dXJlUmVjb3JkczogRml4dHVyZVJlY29yZFtdID0gW107XG4gICAgZm9yIChjb25zdCB7IGVudGl0eTogZW50aXR5TmFtZSwgZGF0YSwgZXhwbGljaXRJZCB9IG9mIGdlbmVyYXRlZEZpeHR1cmVzKSB7XG4gICAgICBjb25zdCBlbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KGVudGl0eU5hbWUpO1xuXG4gICAgICAvLyBpbnRlZ2VyL2JpZ0ludGVnZXIgUEvripQg7J6E7IucIElEIOyDneyEsSAoREIg7Iuc7YCA7Iqk6rCAIOyLpOygnCBJRCDtlaDri7kpXG4gICAgICAvLyBzdHJpbmcgUEvripQgZ2VuZXJhdGUoKeyXkOyEnCDsnbTrr7gg7IOd7ISx65CcIGlkIOqwkuydhCDqt7jrjIDroZwg7IKs7JqpXG4gICAgICAvLyBwYXJlbnRJZCDsl5Tti7Dti7DripQg67aA66qo7J2YIOyLpOygnCBpZOulvCDqt7jrjIDroZwg7IKs7JqpICjsi5ztgIDsiqQg66+47IKs7JqpKVxuICAgICAgY29uc3QgaWRQcm9wID0gZW50aXR5LnByb3BzLmZpbmQoKHApID0+IHAubmFtZSA9PT0gXCJpZFwiKTtcbiAgICAgIGNvbnN0IHVzZXNTZXF1ZW5jZSA9XG4gICAgICAgICFleHBsaWNpdElkICYmXG4gICAgICAgIChpZFByb3A/LnR5cGUgPT09IFwiaW50ZWdlclwiIHx8XG4gICAgICAgICAgaWRQcm9wPy50eXBlID09PSBcImJpZ0ludGVnZXJcIiB8fFxuICAgICAgICAgIGlkUHJvcD8uY29uZT8uZml4dHVyZVN0cmF0ZWd5ID09PSBcInNlcXVlbmNlXCIpO1xuXG4gICAgICBjb25zdCBkYXRhRm9yUmVjb3JkID0gdXNlc1NlcXVlbmNlXG4gICAgICAgID8geyAuLi5kYXRhLCBpZDogTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogMTAwMDAwMCkgfVxuICAgICAgICA6IGRhdGE7XG5cbiAgICAgIGNvbnN0IHJlY29yZHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5jcmVhdGVGaXh0dXJlUmVjb3JkKFxuICAgICAgICBlbnRpdHksXG4gICAgICAgIGRhdGFGb3JSZWNvcmQgYXMgeyBpZDogbnVtYmVyIHwgc3RyaW5nOyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuIHwgbnVsbCB9LFxuICAgICAgICB7IHNpbmdsZVJlY29yZDogdHJ1ZSB9LFxuICAgICAgKTtcbiAgICAgIGZpeHR1cmVSZWNvcmRzLnB1c2goLi4ucmVjb3Jkcyk7XG4gICAgfVxuXG4gICAgLy8gMy4gdGFyZ2V0RGLsl5Ag7IK97J6FIChGaXh0dXJlTWFuYWdlcuqwgCDsnZjsobTshLEg7KCV66CsIOyymOumrClcbiAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgRml4dHVyZU1hbmFnZXIuaW5zZXJ0Rml4dHVyZXModGhpcy50YXJnZXREYk5hbWUsIGZpeHR1cmVSZWNvcmRzKTtcblxuICAgIC8vIDQuIGNvbXBhbmlvbiBmaXh0dXJlcyDsg53shLEgKGZpeHR1cmVDb21wYW5pb25z6rCAIOyEoOyWuOuQnCDqsr3smrApXG4gICAgY29uc3QgY29tcGFuaW9uUmVzdWx0cyA9IGF3YWl0IHRoaXMuZ2VuZXJhdGVDb21wYW5pb25zKHNwZWNzLCByZXN1bHRzKTtcblxuICAgIGNvbnN0IHRvdGFsID0gcmVzdWx0cy5sZW5ndGggKyBjb21wYW5pb25SZXN1bHRzLmxlbmd0aDtcbiAgICAhaXNUZXN0KCkgJiZcbiAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmdyZWVuKGBHZW5lcmF0ZWQgYW5kIHNhdmVkICR7dG90YWx9IGZpeHR1cmVzIHRvICR7dGhpcy50YXJnZXREYk5hbWV9YCkpO1xuICAgIHJldHVybiBbLi4ucmVzdWx0cywgLi4uY29tcGFuaW9uUmVzdWx0c107XG4gIH1cblxuICAvKipcbiAgICog67aA66qoIGZpeHR1cmUg6rKw6rO866W8IOq4sOuwmOycvOuhnCBmaXh0dXJlQ29tcGFuaW9uc+yXkCDshKDslrjrkJwgY29tcGFuaW9uIEVudGl0eeulvCDsg53shLHtlanri4jri6QuXG4gICAqXG4gICAqIGdlbmVyYXRlQmF0Y2goKeyXkOyEnOunjCDtmLjstpzrkJjrqbAsIGNvbXBhbmlvbiDsg53shLEg7IucIOyerOq3gOulvCDrsKnsp4DtlZjquLAg7JyE7ZW0XG4gICAqIGdlbmVyYXRlQmF0Y2goKeulvCDri6Tsi5wg7Zi47Lac7ZWY7KeAIOyViuqzoCDsp4HsoJEg7IK97J6F7ZWp64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBnZW5lcmF0ZUNvbXBhbmlvbnMoXG4gICAgc3BlY3M6IEFycmF5PHsgZW50aXR5OiBzdHJpbmc7IGNvdW50OiBudW1iZXI7IG92ZXJyaWRlcz86IFJlY29yZDxzdHJpbmcsIHVua25vd24+IH0+LFxuICAgIHBhcmVudFJlc3VsdHM6IEZpeHR1cmVJbXBvcnRSZXN1bHRbXSxcbiAgKTogUHJvbWlzZTxGaXh0dXJlSW1wb3J0UmVzdWx0W10+IHtcbiAgICBjb25zdCBhbGxSZXN1bHRzOiBGaXh0dXJlSW1wb3J0UmVzdWx0W10gPSBbXTtcbiAgICBjb25zdCBwcm9jZXNzZWRFbnRpdGllcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuXG4gICAgZm9yIChjb25zdCBzcGVjIG9mIHNwZWNzKSB7XG4gICAgICBpZiAocHJvY2Vzc2VkRW50aXRpZXMuaGFzKHNwZWMuZW50aXR5KSkgY29udGludWU7XG4gICAgICBwcm9jZXNzZWRFbnRpdGllcy5hZGQoc3BlYy5lbnRpdHkpO1xuXG4gICAgICBjb25zdCBlbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KHNwZWMuZW50aXR5KTtcbiAgICAgIGNvbnN0IGlkUHJvcCA9IGVudGl0eS5wcm9wcy5maW5kKChwKSA9PiBwLm5hbWUgPT09IFwiaWRcIik7XG4gICAgICBjb25zdCBjb21wYW5pb25zID0gaWRQcm9wPy5jb25lPy5maXh0dXJlQ29tcGFuaW9ucztcbiAgICAgIGlmICghY29tcGFuaW9ucyB8fCBjb21wYW5pb25zLmxlbmd0aCA9PT0gMCkgY29udGludWU7XG5cbiAgICAgIGNvbnN0IGVudGl0eVJlc3VsdHMgPSBwYXJlbnRSZXN1bHRzLmZpbHRlcigocikgPT4gci5lbnRpdHlJZCA9PT0gc3BlYy5lbnRpdHkpO1xuICAgICAgaWYgKGVudGl0eVJlc3VsdHMubGVuZ3RoID09PSAwKSBjb250aW51ZTtcblxuICAgICAgZm9yIChjb25zdCBjb21wYW5pb24gb2YgY29tcGFuaW9ucykge1xuICAgICAgICAvLyBjb21wYW5pb24gZW50aXR57JeQ7IScIOu2gOuqqCBlbnRpdHnroZzsnZggQmVsb25nc1RvT25lIEZLIOy7rOufvOuqhSDtjIzslYVcbiAgICAgICAgY29uc3QgY29tcGFuaW9uRW50aXR5ID0gdGhpcy5lbnRpdHlNYW5hZ2VyLmdldChjb21wYW5pb24uZW50aXR5KTtcbiAgICAgICAgY29uc3QgZmtQcm9wID0gY29tcGFuaW9uRW50aXR5LnByb3BzLmZpbmQoXG4gICAgICAgICAgKHApID0+IGlzUmVsYXRpb25Qcm9wKHApICYmIGlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wKHApICYmIHAud2l0aCA9PT0gc3BlYy5lbnRpdHksXG4gICAgICAgICk7XG4gICAgICAgIGlmICghZmtQcm9wKSB7XG4gICAgICAgICAgIWlzVGVzdCgpICYmXG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgICAgIGNoYWxrLnllbGxvdyhcbiAgICAgICAgICAgICAgICBgW0NvbXBhbmlvbl0gTm8gQmVsb25nc1RvT25lIHJlbGF0aW9uIGZyb20gJHtjb21wYW5pb24uZW50aXR5fSB0byAke3NwZWMuZW50aXR5fS4gU2tpcHBpbmcuYCxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgZmtDb2xOYW1lID0gYCR7ZmtQcm9wLm5hbWV9X2lkYDtcblxuICAgICAgICAvLyBjb21wYW5pb27snZggaWRQcm9wLCB1c2VzU2VxdWVuY2UsIGNvdW5064qUIGNvbXBhbmlvbiDri6jsnITroZwg6rOg7KCVXG4gICAgICAgIGNvbnN0IGNvbXBhbmlvbklkUHJvcCA9IGNvbXBhbmlvbkVudGl0eS5wcm9wcy5maW5kKChwKSA9PiBwLm5hbWUgPT09IFwiaWRcIik7XG4gICAgICAgIGNvbnN0IHVzZXNTZXF1ZW5jZSA9XG4gICAgICAgICAgY29tcGFuaW9uSWRQcm9wPy50eXBlID09PSBcImludGVnZXJcIiB8fFxuICAgICAgICAgIGNvbXBhbmlvbklkUHJvcD8udHlwZSA9PT0gXCJiaWdJbnRlZ2VyXCIgfHxcbiAgICAgICAgICBjb21wYW5pb25JZFByb3A/LmNvbmU/LmZpeHR1cmVTdHJhdGVneSA9PT0gXCJzZXF1ZW5jZVwiO1xuICAgICAgICBjb25zdCBjb21wYW5pb25Db3VudCA9IGNvbXBhbmlvbi5jb3VudCA/PyAxO1xuXG4gICAgICAgIC8vIOqwgSBwYXJlbnQgcmVzdWx07JeQIOuMgO2VtCBjb21wYW5pb24gZml4dHVyZSDsg53shLFcbiAgICAgICAgY29uc3QgY29udGV4dCA9IHRoaXMuY3JlYXRlQ29udGV4dCgpO1xuICAgICAgICBjb25zdCBjb21wYW5pb25GaXh0dXJlUmVjb3JkczogRml4dHVyZVJlY29yZFtdID0gW107XG5cbiAgICAgICAgZm9yIChjb25zdCBwYXJlbnRSZXN1bHQgb2YgZW50aXR5UmVzdWx0cykge1xuICAgICAgICAgIGNvbnN0IHJlc29sdmVkT3ZlcnJpZGVzID0gdGhpcy5yZXNvbHZlVGVtcGxhdGVPdmVycmlkZXMoXG4gICAgICAgICAgICBjb21wYW5pb24ub3ZlcnJpZGVzID8/IHt9LFxuICAgICAgICAgICAgcGFyZW50UmVzdWx0LmRhdGEsXG4gICAgICAgICAgKTtcbiAgICAgICAgICByZXNvbHZlZE92ZXJyaWRlc1tma0NvbE5hbWVdID0gcGFyZW50UmVzdWx0LmRhdGEuaWQ7XG5cbiAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNvbXBhbmlvbkNvdW50OyBpKyspIHtcbiAgICAgICAgICAgIGNvbnN0IGZpeHR1cmUgPSBhd2FpdCB0aGlzLmdlbmVyYXRlKGNvbXBhbmlvbi5lbnRpdHksIHJlc29sdmVkT3ZlcnJpZGVzLCBjb250ZXh0KTtcblxuICAgICAgICAgICAgY29uc3QgZGF0YUZvclJlY29yZCA9IHVzZXNTZXF1ZW5jZVxuICAgICAgICAgICAgICA/IHsgLi4uZml4dHVyZSwgaWQ6IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIDEwMDAwMDApIH1cbiAgICAgICAgICAgICAgOiBmaXh0dXJlO1xuXG4gICAgICAgICAgICBjb25zdCByZWNvcmRzID0gYXdhaXQgRml4dHVyZU1hbmFnZXIuY3JlYXRlRml4dHVyZVJlY29yZChcbiAgICAgICAgICAgICAgY29tcGFuaW9uRW50aXR5LFxuICAgICAgICAgICAgICBkYXRhRm9yUmVjb3JkIGFzIHtcbiAgICAgICAgICAgICAgICBpZDogbnVtYmVyIHwgc3RyaW5nO1xuICAgICAgICAgICAgICAgIFtrZXk6IHN0cmluZ106IHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4gfCBudWxsO1xuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICB7IHNpbmdsZVJlY29yZDogdHJ1ZSB9LFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGNvbXBhbmlvbkZpeHR1cmVSZWNvcmRzLnB1c2goLi4ucmVjb3Jkcyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgY29tcGFuaW9uUmVzdWx0cyA9IGF3YWl0IEZpeHR1cmVNYW5hZ2VyLmluc2VydEZpeHR1cmVzKFxuICAgICAgICAgIHRoaXMudGFyZ2V0RGJOYW1lLFxuICAgICAgICAgIGNvbXBhbmlvbkZpeHR1cmVSZWNvcmRzLFxuICAgICAgICApO1xuICAgICAgICBhbGxSZXN1bHRzLnB1c2goLi4uY29tcGFuaW9uUmVzdWx0cyk7XG5cbiAgICAgICAgIWlzVGVzdCgpICYmXG4gICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBjaGFsay5ncmVlbihcbiAgICAgICAgICAgICAgYFtDb21wYW5pb25dIEdlbmVyYXRlZCAke2NvbXBhbmlvblJlc3VsdHMubGVuZ3RofSAke2NvbXBhbmlvbi5lbnRpdHl9IGZpeHR1cmVzYCxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gYWxsUmVzdWx0cztcbiAgfVxuXG4gIC8qKlxuICAgKiBvdmVycmlkZXMg6rCS7J2YIFwie3tmaWVsZE5hbWV9fVwiIO2FnO2UjOumv+ydhCDrtoDrqqggZml4dHVyZSDrjbDsnbTthLDroZwg7LmY7ZmY7ZWp64uI64ukLlxuICAgKlxuICAgKiDsmIg6IHsgXCJhY2NvdW50X2lkXCI6IFwie3tlbWFpbH19XCIgfSDihpIgeyBcImFjY291bnRfaWRcIjogXCJ1c2VyQGV4YW1wbGUuY29tXCIgfVxuICAgKi9cbiAgcHJpdmF0ZSByZXNvbHZlVGVtcGxhdGVPdmVycmlkZXMoXG4gICAgb3ZlcnJpZGVzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICBwYXJlbnREYXRhOiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4gfCBEYXRlIHwgbnVsbCB9LFxuICApOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gICAgY29uc3QgcmVzb2x2ZWQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob3ZlcnJpZGVzKSkge1xuICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiAmJiB2YWx1ZS5zdGFydHNXaXRoKFwie3tcIikgJiYgdmFsdWUuZW5kc1dpdGgoXCJ9fVwiKSkge1xuICAgICAgICBjb25zdCBmaWVsZE5hbWUgPSB2YWx1ZS5zbGljZSgyLCAtMikudHJpbSgpO1xuICAgICAgICBpZiAoIShmaWVsZE5hbWUgaW4gcGFyZW50RGF0YSkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBg7YWc7ZSM66a/IO2VhOuTnCBcIiR7ZmllbGROYW1lfVwi7J20KOqwgCkg67aA66qoIGZpeHR1cmUg642w7J207YSw7JeQIOyhtOyerO2VmOyngCDslYrsirXri4jri6QgKG92ZXJyaWRlIGtleTogXCIke2tleX1cIilgLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgcmVzb2x2ZWRba2V5XSA9IHBhcmVudERhdGFbZmllbGROYW1lXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc29sdmVkW2tleV0gPSB2YWx1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJlc29sdmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIOyLpOygnCBEQihzb3VyY2VEYinsl5DshJwg642w7J207YSw66W8IOyhsO2ajO2VmOyXrCBmaXh0dXJlIERCKHRhcmdldERiKeyXkCBpbXBvcnTtlanri4jri6QuXG4gICAqXG4gICAqIDEuIERhdGFFeHBsb3JlcuuhnCBzb3VyY2VEYuyXkOyEnCDrjbDsnbTthLAg7KGw7ZqMICjqtIDroKgg642w7J207YSwIO2PrO2VqClcbiAgICogMi4gRml4dHVyZVJlY29yZOuhnCDrs4DtmZhcbiAgICogMy4gdGFyZ2V0RGLsl5Ag7IK97J6FXG4gICAqXG4gICAqIEBwYXJhbSBlbnRpdHlOYW1lIC0g7KGw7ZqM7ZWgIGVudGl0eSDsnbTrpoRcbiAgICogQHBhcmFtIG9wdGlvbnMgLSDsobDtmowg7Ji17IWYIChzdHJhdGVneSwgbGltaXQsIGluY2x1ZGVSZWxhdGlvbnMg65OxKVxuICAgKiBAcmV0dXJucyDsoIDsnqXrkJwgZml4dHVyZSDrjbDsnbTthLAgKOyLpOygnCBEQiBJRCDtj6ztlagpXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIC8vIO2UhOuhnOuNleyFmCBEQuyXkOyEnCBVc2VyIDEw66qFICsg6rSA66CoIEVtcGxveWVlLCBEZXBhcnRtZW50IOqwgOyguOyYpOq4sFxuICAgKiBhd2FpdCBnZW5lcmF0b3IuaW1wb3J0RnJvbVNvdXJjZShcIlVzZXJcIiwge1xuICAgKiAgIHN0cmF0ZWd5OiBcInNhbXBsZVwiLFxuICAgKiAgIGxpbWl0OiAxMCxcbiAgICogICBpbmNsdWRlUmVsYXRpb25zOiB0cnVlLFxuICAgKiAgIG1heERlcHRoOiAyXG4gICAqIH0pO1xuICAgKi9cbiAgYXN5bmMgaW1wb3J0RnJvbVNvdXJjZShcbiAgICBlbnRpdHlOYW1lOiBzdHJpbmcsXG4gICAgb3B0aW9uczogRXhwbG9yZVdpdGhSZWxhdGlvbnNPcHRpb25zLFxuICApOiBQcm9taXNlPEZpeHR1cmVJbXBvcnRSZXN1bHRbXT4ge1xuICAgICFpc1Rlc3QoKSAmJlxuICAgICAgY29uc29sZS5sb2coXG4gICAgICAgIGNoYWxrLmJsdWUoXG4gICAgICAgICAgYEltcG9ydGluZyAke2VudGl0eU5hbWV9IGZyb20gc291cmNlIERCIHdpdGggb3B0aW9uczogJHtKU09OLnN0cmluZ2lmeSh7IHN0cmF0ZWd5OiBvcHRpb25zLnN0cmF0ZWd5LCBsaW1pdDogb3B0aW9ucy5saW1pdCwgaW5jbHVkZVJlbGF0aW9uczogb3B0aW9ucy5pbmNsdWRlUmVsYXRpb25zLCBtYXhEZXB0aDogb3B0aW9ucy5tYXhEZXB0aCB9KX1gLFxuICAgICAgICApLFxuICAgICAgKTtcblxuICAgIC8vIDEuIERhdGFFeHBsb3JlcuuhnCBzb3VyY2VEYuyXkOyEnCDrjbDsnbTthLAg7KGw7ZqMICjqtIDroKgg642w7J207YSwIO2PrO2VqClcbiAgICBjb25zdCBleHBsb3JlUmVzdWx0ID0gYXdhaXQgdGhpcy5kYXRhRXhwbG9yZXIuZXhwbG9yZVdpdGhSZWxhdGlvbnMoZW50aXR5TmFtZSwgb3B0aW9ucyk7XG5cbiAgICAhaXNUZXN0KCkgJiZcbiAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICBjaGFsay5jeWFuKFxuICAgICAgICAgIGBGb3VuZCAke2V4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzLmxlbmd0aH0gJHtlbnRpdHlOYW1lfSByZWNvcmRzIGFuZCAke2V4cGxvcmVSZXN1bHQucmVsYXRlZC5zaXplfSByZWxhdGVkIGVudGl0aWVzYCxcbiAgICAgICAgKSxcbiAgICAgICk7XG5cbiAgICAvLyAyLiBGaXh0dXJlUmVjb3Jk66GcIOuzgO2ZmFxuICAgIGNvbnN0IGZpeHR1cmVSZWNvcmRzOiBGaXh0dXJlUmVjb3JkW10gPSBbXTtcblxuICAgIC8vIOuplOyduCBlbnRpdHnsnZggcmVjb3Jkc+ulvCBGaXh0dXJlUmVjb3Jk66GcIOuzgO2ZmFxuICAgIGNvbnN0IG1haW5FbnRpdHkgPSB0aGlzLmVudGl0eU1hbmFnZXIuZ2V0KGVudGl0eU5hbWUpO1xuICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIGV4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzKSB7XG4gICAgICBjb25zdCByZWNvcmRzID0gYXdhaXQgRml4dHVyZU1hbmFnZXIuY3JlYXRlRml4dHVyZVJlY29yZChcbiAgICAgICAgbWFpbkVudGl0eSxcbiAgICAgICAgcmVjb3JkIGFzIHsgaWQ6IG51bWJlciB8IHN0cmluZzsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IG51bGwgfSxcbiAgICAgICAgeyBfZGI6IHRoaXMuc291cmNlRGIsIHNpbmdsZVJlY29yZDogdHJ1ZSB9LFxuICAgICAgKTtcbiAgICAgIGZpeHR1cmVSZWNvcmRzLnB1c2goLi4ucmVjb3Jkcyk7XG4gICAgfVxuXG4gICAgLy8g6rSA66CoIGVudGl0eeydmCByZWNvcmRz66W8IEZpeHR1cmVSZWNvcmTroZwg67OA7ZmYXG4gICAgZm9yIChjb25zdCBbcmVsYXRlZEVudGl0eU5hbWUsIHJlbGF0ZWRSZWNvcmRzXSBvZiBleHBsb3JlUmVzdWx0LnJlbGF0ZWQuZW50cmllcygpKSB7XG4gICAgICBjb25zdCByZWxhdGVkRW50aXR5ID0gdGhpcy5lbnRpdHlNYW5hZ2VyLmdldChyZWxhdGVkRW50aXR5TmFtZSk7XG4gICAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiByZWxhdGVkUmVjb3Jkcykge1xuICAgICAgICBjb25zdCByZWNvcmRzID0gYXdhaXQgRml4dHVyZU1hbmFnZXIuY3JlYXRlRml4dHVyZVJlY29yZChcbiAgICAgICAgICByZWxhdGVkRW50aXR5LFxuICAgICAgICAgIHJlY29yZCBhcyB7IGlkOiBudW1iZXIgfCBzdHJpbmc7IFtrZXk6IHN0cmluZ106IHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4gfCBudWxsIH0sXG4gICAgICAgICAgeyBfZGI6IHRoaXMuc291cmNlRGIsIHNpbmdsZVJlY29yZDogdHJ1ZSB9LFxuICAgICAgICApO1xuICAgICAgICBmaXh0dXJlUmVjb3Jkcy5wdXNoKC4uLnJlY29yZHMpO1xuICAgICAgfVxuXG4gICAgICAhaXNUZXN0KCkgJiZcbiAgICAgICAgY29uc29sZS5sb2coY2hhbGsuZ3JheShgICAtICR7cmVsYXRlZEVudGl0eU5hbWV9OiAke3JlbGF0ZWRSZWNvcmRzLmxlbmd0aH0gcmVjb3Jkc2ApKTtcbiAgICB9XG5cbiAgICAvLyAzLiB0YXJnZXREYuyXkCDsgr3snoUgKEZpeHR1cmVNYW5hZ2Vy6rCAIOydmOyhtOyEsSDsoJXroKwg7LKY66asKVxuICAgIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBGaXh0dXJlTWFuYWdlci5pbnNlcnRGaXh0dXJlcyh0aGlzLnRhcmdldERiTmFtZSwgZml4dHVyZVJlY29yZHMpO1xuXG4gICAgIWlzVGVzdCgpICYmXG4gICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgY2hhbGsuZ3JlZW4oXG4gICAgICAgICAgYFN1Y2Nlc3NmdWxseSBpbXBvcnRlZCAke3Jlc3VsdHMubGVuZ3RofSByZWNvcmRzIHRvICR7dGhpcy50YXJnZXREYk5hbWV9ICgke2V4cGxvcmVSZXN1bHQubWFpbi5yZWNvcmRzLmxlbmd0aH0gJHtlbnRpdHlOYW1lfSArICR7cmVzdWx0cy5sZW5ndGggLSBleHBsb3JlUmVzdWx0Lm1haW4ucmVjb3Jkcy5sZW5ndGh9IHJlbGF0ZWQpYCxcbiAgICAgICAgKSxcbiAgICAgICk7XG5cbiAgICByZXR1cm4gcmVzdWx0cztcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7YUFNb0c7a0JBQ3ZEO3FCQUNFO3NCQUVFO3VCQUVFO0NBc0J0QyxtQkFBYixNQUE4QjtFQUM1QixBQUFRO0VBQ1IsQUFBUTtFQUNSLEFBQVE7RUFDUixBQUFRLFdBQWlDLElBQUksS0FBSztFQUNsRCxBQUFRLGNBQW1DLElBQUksS0FBSztFQUNwRCxBQUFRO0VBRVIsWUFDRSxBQUFRQSxVQUdSLFdBQ0EsQUFBUUMsY0FDUixBQUFRQyxlQUNSLFNBQ0E7R0FQUTtHQUlBO0dBQ0E7QUFHUixRQUFLLGVBQWUsSUFBSSxhQUFhLFVBQVUsY0FBYztBQUM3RCxRQUFLLFNBQVMsU0FBUyxVQUFVO0FBQ2pDLFFBQUssV0FB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