sonamu 0.5.7 → 0.6.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 (358) hide show
  1. package/dist/api/base-frame.js +12 -2
  2. package/dist/api/caster.js +66 -2
  3. package/dist/api/code-converters.js +489 -2
  4. package/dist/api/config.d.ts +76 -0
  5. package/dist/api/config.d.ts.map +1 -0
  6. package/dist/api/config.js +32 -0
  7. package/dist/api/context.d.ts +1 -0
  8. package/dist/api/context.d.ts.map +1 -1
  9. package/dist/api/context.js +3 -2
  10. package/dist/api/decorators.d.ts.map +1 -1
  11. package/dist/api/decorators.js +142 -2
  12. package/dist/api/index.js +9 -2
  13. package/dist/api/sonamu.d.ts +8 -22
  14. package/dist/api/sonamu.d.ts.map +1 -1
  15. package/dist/api/sonamu.js +482 -2
  16. package/dist/bin/build-config.d.ts +2 -1
  17. package/dist/bin/build-config.d.ts.map +1 -1
  18. package/dist/bin/build-config.js +12 -2
  19. package/dist/bin/cli-wrapper.js +71 -2
  20. package/dist/bin/cli.js +418 -2
  21. package/dist/bin/hot-hook-register.d.ts +11 -0
  22. package/dist/bin/hot-hook-register.d.ts.map +1 -0
  23. package/dist/bin/hot-hook-register.js +21 -0
  24. package/dist/database/_batch_update.js +78 -2
  25. package/dist/database/base-model.js +247 -2
  26. package/dist/database/code-generator.js +53 -2
  27. package/dist/database/db.d.ts +2 -16
  28. package/dist/database/db.d.ts.map +1 -1
  29. package/dist/database/db.js +132 -2
  30. package/dist/database/knex-plugins/knex-on-duplicate-update.js +39 -2
  31. package/dist/database/puri-wrapper.js +109 -2
  32. package/dist/database/puri.d.ts +23 -16
  33. package/dist/database/puri.d.ts.map +1 -1
  34. package/dist/database/puri.js +539 -2
  35. package/dist/database/puri.types.d.ts +8 -3
  36. package/dist/database/puri.types.d.ts.map +1 -1
  37. package/dist/database/puri.types.js +3 -2
  38. package/dist/database/transaction-context.js +14 -2
  39. package/dist/database/upsert-builder.js +215 -2
  40. package/dist/entity/entity-manager.d.ts +3 -1
  41. package/dist/entity/entity-manager.d.ts.map +1 -1
  42. package/dist/entity/entity-manager.js +114 -2
  43. package/dist/entity/entity-utils.js +210 -2
  44. package/dist/entity/entity.d.ts.map +1 -1
  45. package/dist/entity/entity.js +651 -2
  46. package/dist/exceptions/error-handler.js +29 -2
  47. package/dist/exceptions/so-exceptions.js +85 -2
  48. package/dist/file-storage/driver.js +79 -2
  49. package/dist/file-storage/file-storage.js +75 -2
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +28 -2
  53. package/dist/migration/code-generation.js +558 -2
  54. package/dist/migration/migration-set.js +364 -2
  55. package/dist/migration/migrator.d.ts +0 -9
  56. package/dist/migration/migrator.d.ts.map +1 -1
  57. package/dist/migration/migrator.js +510 -2
  58. package/dist/migration/types.js +3 -2
  59. package/dist/naite/naite.d.ts +12 -0
  60. package/dist/naite/naite.d.ts.map +1 -0
  61. package/dist/naite/naite.js +72 -0
  62. package/dist/stream/index.js +3 -2
  63. package/dist/stream/sse.js +38 -2
  64. package/dist/syncer/api-parser.d.ts +20 -0
  65. package/dist/syncer/api-parser.d.ts.map +1 -0
  66. package/dist/syncer/api-parser.js +229 -0
  67. package/dist/syncer/checksum.d.ts +21 -0
  68. package/dist/syncer/checksum.d.ts.map +1 -0
  69. package/dist/syncer/checksum.js +98 -0
  70. package/dist/syncer/code-generator.d.ts +20 -0
  71. package/dist/syncer/code-generator.d.ts.map +1 -0
  72. package/dist/syncer/code-generator.js +141 -0
  73. package/dist/syncer/entity-operations.d.ts +17 -0
  74. package/dist/syncer/entity-operations.d.ts.map +1 -0
  75. package/dist/syncer/entity-operations.js +58 -0
  76. package/dist/syncer/file-patterns.d.ts +29 -0
  77. package/dist/syncer/file-patterns.d.ts.map +1 -0
  78. package/dist/syncer/file-patterns.js +38 -0
  79. package/dist/syncer/index.d.ts +6 -0
  80. package/dist/syncer/index.d.ts.map +1 -1
  81. package/dist/syncer/index.js +9 -2
  82. package/dist/syncer/module-loader.d.ts +35 -0
  83. package/dist/syncer/module-loader.d.ts.map +1 -0
  84. package/dist/syncer/module-loader.js +82 -0
  85. package/dist/syncer/syncer.d.ts +93 -108
  86. package/dist/syncer/syncer.d.ts.map +1 -1
  87. package/dist/syncer/syncer.js +375 -2
  88. package/dist/template/entity-converter.d.ts +14 -0
  89. package/dist/template/entity-converter.d.ts.map +1 -0
  90. package/dist/template/entity-converter.js +101 -0
  91. package/dist/template/helpers.d.ts +23 -0
  92. package/dist/template/helpers.d.ts.map +1 -0
  93. package/dist/template/helpers.js +64 -0
  94. package/dist/{templates → template/implementations}/entity.template.d.ts +3 -3
  95. package/dist/template/implementations/entity.template.d.ts.map +1 -0
  96. package/dist/template/implementations/entity.template.js +87 -0
  97. package/dist/{templates → template/implementations}/generated.template.d.ts +3 -3
  98. package/dist/template/implementations/generated.template.d.ts.map +1 -0
  99. package/dist/template/implementations/generated.template.js +232 -0
  100. package/dist/{templates → template/implementations}/generated_http.template.d.ts +3 -3
  101. package/dist/template/implementations/generated_http.template.d.ts.map +1 -0
  102. package/dist/template/implementations/generated_http.template.js +131 -0
  103. package/dist/{templates → template/implementations}/generated_sso.template.d.ts +3 -3
  104. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -0
  105. package/dist/template/implementations/generated_sso.template.js +105 -0
  106. package/dist/{templates → template/implementations}/init_types.template.d.ts +3 -3
  107. package/dist/template/implementations/init_types.template.d.ts.map +1 -0
  108. package/dist/template/implementations/init_types.template.js +38 -0
  109. package/dist/template/implementations/model.template.d.ts +17 -0
  110. package/dist/template/implementations/model.template.d.ts.map +1 -0
  111. package/dist/template/implementations/model.template.js +171 -0
  112. package/dist/{templates → template/implementations}/model_test.template.d.ts +3 -3
  113. package/dist/template/implementations/model_test.template.d.ts.map +1 -0
  114. package/dist/template/implementations/model_test.template.js +35 -0
  115. package/dist/{templates → template/implementations}/service.template.d.ts +6 -6
  116. package/dist/template/implementations/service.template.d.ts.map +1 -0
  117. package/dist/template/implementations/service.template.js +193 -0
  118. package/dist/{templates → template/implementations}/view_enums_buttonset.template.d.ts +3 -3
  119. package/dist/template/implementations/view_enums_buttonset.template.d.ts.map +1 -0
  120. package/dist/template/implementations/view_enums_buttonset.template.js +31 -0
  121. package/dist/{templates → template/implementations}/view_enums_dropdown.template.d.ts +3 -4
  122. package/dist/template/implementations/view_enums_dropdown.template.d.ts.map +1 -0
  123. package/dist/template/implementations/view_enums_dropdown.template.js +50 -0
  124. package/dist/{templates → template/implementations}/view_enums_select.template.d.ts +3 -3
  125. package/dist/template/implementations/view_enums_select.template.d.ts.map +1 -0
  126. package/dist/template/implementations/view_enums_select.template.js +55 -0
  127. package/dist/{templates → template/implementations}/view_form.template.d.ts +5 -5
  128. package/dist/template/implementations/view_form.template.d.ts.map +1 -0
  129. package/dist/template/implementations/view_form.template.js +337 -0
  130. package/dist/{templates → template/implementations}/view_id_all_select.template.d.ts +3 -3
  131. package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -0
  132. package/dist/template/implementations/view_id_all_select.template.js +31 -0
  133. package/dist/{templates → template/implementations}/view_id_async_select.template.d.ts +3 -3
  134. package/dist/template/implementations/view_id_async_select.template.d.ts.map +1 -0
  135. package/dist/template/implementations/view_id_async_select.template.js +105 -0
  136. package/dist/{templates → template/implementations}/view_list.template.d.ts +5 -13
  137. package/dist/template/implementations/view_list.template.d.ts.map +1 -0
  138. package/dist/template/implementations/view_list.template.js +465 -0
  139. package/dist/{templates → template/implementations}/view_list_columns.template.d.ts +3 -3
  140. package/dist/template/implementations/view_list_columns.template.d.ts.map +1 -0
  141. package/dist/template/implementations/view_list_columns.template.js +49 -0
  142. package/dist/{templates → template/implementations}/view_search_input.template.d.ts +3 -3
  143. package/dist/template/implementations/view_search_input.template.d.ts.map +1 -0
  144. package/dist/template/implementations/view_search_input.template.js +64 -0
  145. package/dist/template/index.d.ts +5 -0
  146. package/dist/template/index.d.ts.map +1 -0
  147. package/dist/template/index.js +6 -0
  148. package/dist/template/template.d.ts +39 -0
  149. package/dist/template/template.d.ts.map +1 -0
  150. package/dist/template/template.js +47 -0
  151. package/dist/template/zod-converter.d.ts +18 -0
  152. package/dist/template/zod-converter.d.ts.map +1 -0
  153. package/dist/template/zod-converter.js +166 -0
  154. package/dist/testing/_relation-graph.js +80 -2
  155. package/dist/testing/fixture-manager.d.ts.map +1 -1
  156. package/dist/testing/fixture-manager.js +521 -2
  157. package/dist/types/types.d.ts +39 -40
  158. package/dist/types/types.d.ts.map +1 -1
  159. package/dist/types/types.js +289 -2
  160. package/dist/typings/knex.d.js +3 -2
  161. package/dist/utils/async-utils.d.ts +7 -0
  162. package/dist/utils/async-utils.d.ts.map +1 -1
  163. package/dist/utils/async-utils.js +57 -2
  164. package/dist/utils/console-util.d.ts +2 -0
  165. package/dist/utils/console-util.d.ts.map +1 -0
  166. package/dist/utils/console-util.js +6 -0
  167. package/dist/utils/controller.js +26 -2
  168. package/dist/utils/esm-utils.d.ts +45 -0
  169. package/dist/utils/esm-utils.d.ts.map +1 -0
  170. package/dist/utils/esm-utils.js +56 -0
  171. package/dist/utils/fs-utils.js +17 -2
  172. package/dist/utils/lodash-able.js +6 -2
  173. package/dist/utils/model.js +22 -2
  174. package/dist/utils/path-utils.d.ts +89 -0
  175. package/dist/utils/path-utils.d.ts.map +1 -0
  176. package/dist/utils/path-utils.js +60 -0
  177. package/dist/utils/process-utils.d.ts +13 -0
  178. package/dist/utils/process-utils.d.ts.map +1 -0
  179. package/dist/utils/process-utils.js +36 -0
  180. package/dist/utils/sql-parser.js +35 -2
  181. package/dist/utils/utils.d.ts +4 -7
  182. package/dist/utils/utils.d.ts.map +1 -1
  183. package/dist/utils/utils.js +33 -2
  184. package/dist/utils/zod-error.d.ts.map +1 -1
  185. package/dist/utils/zod-error.js +19 -2
  186. package/package.json +21 -8
  187. package/src/api/code-converters.ts +2 -2
  188. package/src/api/config.ts +142 -0
  189. package/src/api/context.ts +1 -0
  190. package/src/api/decorators.ts +1 -0
  191. package/src/api/sonamu.ts +81 -67
  192. package/src/bin/build-config.ts +2 -1
  193. package/src/bin/cli-wrapper.ts +10 -3
  194. package/src/bin/cli.ts +108 -56
  195. package/src/bin/hot-hook-register.ts +22 -0
  196. package/src/database/base-model.ts +1 -1
  197. package/src/database/code-generator.ts +1 -1
  198. package/src/database/db.ts +10 -52
  199. package/src/database/puri.ts +78 -53
  200. package/src/database/puri.types.ts +18 -5
  201. package/src/database/upsert-builder.ts +1 -1
  202. package/src/entity/entity-manager.ts +19 -15
  203. package/src/entity/entity.ts +4 -3
  204. package/src/index.ts +2 -0
  205. package/src/migration/code-generation.ts +1 -1
  206. package/src/migration/migration-set.ts +1 -1
  207. package/src/migration/migrator.ts +23 -152
  208. package/src/naite/naite.ts +70 -0
  209. package/src/syncer/api-parser.ts +299 -0
  210. package/src/syncer/checksum.ts +152 -0
  211. package/src/syncer/code-generator.ts +202 -0
  212. package/src/syncer/entity-operations.ts +68 -0
  213. package/src/syncer/file-patterns.ts +56 -0
  214. package/src/syncer/index.ts +6 -0
  215. package/src/syncer/module-loader.ts +125 -0
  216. package/src/syncer/syncer.ts +363 -1420
  217. package/src/template/entity-converter.ts +123 -0
  218. package/src/template/helpers.ts +84 -0
  219. package/src/{templates → template/implementations}/entity.template.ts +4 -4
  220. package/src/{templates → template/implementations}/generated.template.ts +9 -9
  221. package/src/{templates → template/implementations}/generated_http.template.ts +9 -6
  222. package/src/{templates → template/implementations}/generated_sso.template.ts +7 -7
  223. package/src/{templates → template/implementations}/init_types.template.ts +4 -4
  224. package/src/{templates → template/implementations}/model.template.ts +9 -9
  225. package/src/{templates → template/implementations}/model_test.template.ts +5 -5
  226. package/src/{templates → template/implementations}/service.template.ts +19 -11
  227. package/src/{templates → template/implementations}/view_enums_buttonset.template.ts +3 -3
  228. package/src/{templates → template/implementations}/view_enums_dropdown.template.ts +5 -21
  229. package/src/{templates → template/implementations}/view_enums_select.template.ts +4 -4
  230. package/src/{templates → template/implementations}/view_form.template.ts +11 -13
  231. package/src/{templates → template/implementations}/view_id_all_select.template.ts +3 -3
  232. package/src/{templates → template/implementations}/view_id_async_select.template.ts +3 -3
  233. package/src/{templates → template/implementations}/view_list.template.ts +13 -64
  234. package/src/{templates → template/implementations}/view_list_columns.template.ts +3 -3
  235. package/src/{templates → template/implementations}/view_search_input.template.ts +3 -3
  236. package/src/template/index.ts +4 -0
  237. package/src/template/template.ts +86 -0
  238. package/src/template/zod-converter.ts +219 -0
  239. package/src/testing/fixture-manager.ts +8 -1
  240. package/src/types/types.ts +38 -61
  241. package/src/utils/async-utils.ts +17 -0
  242. package/src/utils/console-util.ts +4 -0
  243. package/src/utils/esm-utils.ts +69 -0
  244. package/src/utils/path-utils.ts +102 -0
  245. package/src/utils/process-utils.ts +46 -0
  246. package/src/utils/sql-parser.ts +1 -1
  247. package/src/utils/utils.ts +14 -40
  248. package/src/utils/zod-error.ts +0 -1
  249. package/dist/api/base-frame.js.map +0 -1
  250. package/dist/api/caster.js.map +0 -1
  251. package/dist/api/code-converters.js.map +0 -1
  252. package/dist/api/context.js.map +0 -1
  253. package/dist/api/decorators.js.map +0 -1
  254. package/dist/api/index.js.map +0 -1
  255. package/dist/api/sonamu.js.map +0 -1
  256. package/dist/bin/build-config.js.map +0 -1
  257. package/dist/bin/cli-wrapper.js.map +0 -1
  258. package/dist/bin/cli.js.map +0 -1
  259. package/dist/database/_batch_update.js.map +0 -1
  260. package/dist/database/base-model.js.map +0 -1
  261. package/dist/database/code-generator.js.map +0 -1
  262. package/dist/database/db.js.map +0 -1
  263. package/dist/database/knex-plugins/knex-on-duplicate-update.js.map +0 -1
  264. package/dist/database/puri-wrapper.js.map +0 -1
  265. package/dist/database/puri.js.map +0 -1
  266. package/dist/database/puri.types.js.map +0 -1
  267. package/dist/database/transaction-context.js.map +0 -1
  268. package/dist/database/upsert-builder.js.map +0 -1
  269. package/dist/entity/entity-manager.js.map +0 -1
  270. package/dist/entity/entity-utils.js.map +0 -1
  271. package/dist/entity/entity.js.map +0 -1
  272. package/dist/exceptions/error-handler.js.map +0 -1
  273. package/dist/exceptions/so-exceptions.js.map +0 -1
  274. package/dist/file-storage/driver.js.map +0 -1
  275. package/dist/file-storage/file-storage.js.map +0 -1
  276. package/dist/index.js.map +0 -1
  277. package/dist/migration/code-generation.js.map +0 -1
  278. package/dist/migration/migration-set.js.map +0 -1
  279. package/dist/migration/migrator.js.map +0 -1
  280. package/dist/migration/types.js.map +0 -1
  281. package/dist/stream/index.js.map +0 -1
  282. package/dist/stream/sse.js.map +0 -1
  283. package/dist/syncer/index.js.map +0 -1
  284. package/dist/syncer/syncer.js.map +0 -1
  285. package/dist/templates/base-template.d.ts +0 -13
  286. package/dist/templates/base-template.d.ts.map +0 -1
  287. package/dist/templates/base-template.js +0 -2
  288. package/dist/templates/base-template.js.map +0 -1
  289. package/dist/templates/entity.template.d.ts.map +0 -1
  290. package/dist/templates/entity.template.js +0 -2
  291. package/dist/templates/entity.template.js.map +0 -1
  292. package/dist/templates/generated.template.d.ts.map +0 -1
  293. package/dist/templates/generated.template.js +0 -2
  294. package/dist/templates/generated.template.js.map +0 -1
  295. package/dist/templates/generated_http.template.d.ts.map +0 -1
  296. package/dist/templates/generated_http.template.js +0 -2
  297. package/dist/templates/generated_http.template.js.map +0 -1
  298. package/dist/templates/generated_sso.template.d.ts.map +0 -1
  299. package/dist/templates/generated_sso.template.js +0 -2
  300. package/dist/templates/generated_sso.template.js.map +0 -1
  301. package/dist/templates/index.d.ts +0 -2
  302. package/dist/templates/index.d.ts.map +0 -1
  303. package/dist/templates/index.js +0 -2
  304. package/dist/templates/index.js.map +0 -1
  305. package/dist/templates/init_types.template.d.ts.map +0 -1
  306. package/dist/templates/init_types.template.js +0 -2
  307. package/dist/templates/init_types.template.js.map +0 -1
  308. package/dist/templates/model.template.d.ts +0 -17
  309. package/dist/templates/model.template.d.ts.map +0 -1
  310. package/dist/templates/model.template.js +0 -2
  311. package/dist/templates/model.template.js.map +0 -1
  312. package/dist/templates/model_test.template.d.ts.map +0 -1
  313. package/dist/templates/model_test.template.js +0 -2
  314. package/dist/templates/model_test.template.js.map +0 -1
  315. package/dist/templates/service.template.d.ts.map +0 -1
  316. package/dist/templates/service.template.js +0 -2
  317. package/dist/templates/service.template.js.map +0 -1
  318. package/dist/templates/view_enums_buttonset.template.d.ts.map +0 -1
  319. package/dist/templates/view_enums_buttonset.template.js +0 -2
  320. package/dist/templates/view_enums_buttonset.template.js.map +0 -1
  321. package/dist/templates/view_enums_dropdown.template.d.ts.map +0 -1
  322. package/dist/templates/view_enums_dropdown.template.js +0 -2
  323. package/dist/templates/view_enums_dropdown.template.js.map +0 -1
  324. package/dist/templates/view_enums_select.template.d.ts.map +0 -1
  325. package/dist/templates/view_enums_select.template.js +0 -2
  326. package/dist/templates/view_enums_select.template.js.map +0 -1
  327. package/dist/templates/view_form.template.d.ts.map +0 -1
  328. package/dist/templates/view_form.template.js +0 -2
  329. package/dist/templates/view_form.template.js.map +0 -1
  330. package/dist/templates/view_id_all_select.template.d.ts.map +0 -1
  331. package/dist/templates/view_id_all_select.template.js +0 -2
  332. package/dist/templates/view_id_all_select.template.js.map +0 -1
  333. package/dist/templates/view_id_async_select.template.d.ts.map +0 -1
  334. package/dist/templates/view_id_async_select.template.js +0 -2
  335. package/dist/templates/view_id_async_select.template.js.map +0 -1
  336. package/dist/templates/view_list.template.d.ts.map +0 -1
  337. package/dist/templates/view_list.template.js +0 -2
  338. package/dist/templates/view_list.template.js.map +0 -1
  339. package/dist/templates/view_list_columns.template.d.ts.map +0 -1
  340. package/dist/templates/view_list_columns.template.js +0 -2
  341. package/dist/templates/view_list_columns.template.js.map +0 -1
  342. package/dist/templates/view_search_input.template.d.ts.map +0 -1
  343. package/dist/templates/view_search_input.template.js +0 -2
  344. package/dist/templates/view_search_input.template.js.map +0 -1
  345. package/dist/testing/_relation-graph.js.map +0 -1
  346. package/dist/testing/fixture-manager.js.map +0 -1
  347. package/dist/types/types.js.map +0 -1
  348. package/dist/typings/knex.d.js.map +0 -1
  349. package/dist/utils/async-utils.js.map +0 -1
  350. package/dist/utils/controller.js.map +0 -1
  351. package/dist/utils/fs-utils.js.map +0 -1
  352. package/dist/utils/lodash-able.js.map +0 -1
  353. package/dist/utils/model.js.map +0 -1
  354. package/dist/utils/sql-parser.js.map +0 -1
  355. package/dist/utils/utils.js.map +0 -1
  356. package/dist/utils/zod-error.js.map +0 -1
  357. package/src/templates/base-template.ts +0 -19
  358. package/src/templates/index.ts +0 -1
@@ -1,2 +1,521 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get FixtureManager(){return FixtureManager},get FixtureManagerClass(){return FixtureManagerClass}});var _chalk=/*#__PURE__*/_interop_require_default(require("chalk"));var _lodash=/*#__PURE__*/_interop_require_default(require("lodash"));var _api=require("../api");var _entitymanager=require("../entity/entity-manager");var _types=require("../types/types");var _inflection=/*#__PURE__*/_interop_require_default(require("inflection"));var _fs=require("fs");var _relationgraph=require("./_relation-graph");var _knex=/*#__PURE__*/_interop_require_default(require("knex"));var _basemodel=require("../database/base-model");function _array_like_to_array(arr,len){if(len==null||len>arr.length)len=arr.length;for(var i=0,arr2=new Array(len);i<len;i++)arr2[i]=arr[i];return arr2}function _array_with_holes(arr){if(Array.isArray(arr))return arr}function _array_without_holes(arr){if(Array.isArray(arr))return _array_like_to_array(arr)}function _async_iterator(iterable){var method,async,sync,retry=2;for("undefined"!=typeof Symbol&&(async=Symbol.asyncIterator,sync=Symbol.iterator);retry--;){if(async&&null!=(method=iterable[async]))return method.call(iterable);if(sync&&null!=(method=iterable[sync]))return new AsyncFromSyncIterator(method.call(iterable));async="@@asyncIterator",sync="@@iterator"}throw new TypeError("Object is not async iterable")}function AsyncFromSyncIterator(s){function AsyncFromSyncIteratorContinuation(r){if(Object(r)!==r)return Promise.reject(new TypeError(r+" is not an object."));var done=r.done;return Promise.resolve(r.value).then(function(value){return{value:value,done:done}})}return AsyncFromSyncIterator=function(s){this.s=s,this.n=s.next},AsyncFromSyncIterator.prototype={s:null,n:null,next:function(){return AsyncFromSyncIteratorContinuation(this.n.apply(this.s,arguments))},return:function(value){var ret=this.s.return;return void 0===ret?Promise.resolve({value:value,done:!0}):AsyncFromSyncIteratorContinuation(ret.apply(this.s,arguments))},throw:function(value){var thr=this.s.return;return void 0===thr?Promise.reject(value):AsyncFromSyncIteratorContinuation(thr.apply(this.s,arguments))}},new AsyncFromSyncIterator(s)}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{Promise.resolve(value).then(_next,_throw)}}function _async_to_generator(fn){return function(){var self=this,args=arguments;return new Promise(function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(undefined)})}}function _class_call_check(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function")}}function _defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if("value"in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor)}}function _create_class(Constructor,protoProps,staticProps){if(protoProps)_defineProperties(Constructor.prototype,protoProps);if(staticProps)_defineProperties(Constructor,staticProps);return Constructor}function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}function _instanceof(left,right){if(right!=null&&typeof Symbol!=="undefined"&&right[Symbol.hasInstance]){return!!right[Symbol.hasInstance](left)}else{return left instanceof right}}function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}function _iterable_to_array(iter){if(typeof Symbol!=="undefined"&&iter[Symbol.iterator]!=null||iter["@@iterator"]!=null)return Array.from(iter)}function _iterable_to_array_limit(arr,i){var _i=arr==null?null:typeof Symbol!=="undefined"&&arr[Symbol.iterator]||arr["@@iterator"];if(_i==null)return;var _arr=[];var _n=true;var _d=false;var _s,_e;try{for(_i=_i.call(arr);!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i["return"]!=null)_i["return"]()}finally{if(_d)throw _e}}return _arr}function _non_iterable_rest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _non_iterable_spread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _sliced_to_array(arr,i){return _array_with_holes(arr)||_iterable_to_array_limit(arr,i)||_unsupported_iterable_to_array(arr,i)||_non_iterable_rest()}function _to_consumable_array(arr){return _array_without_holes(arr)||_iterable_to_array(arr)||_unsupported_iterable_to_array(arr)||_non_iterable_spread()}function _type_of(obj){"@swc/helpers - typeof";return obj&&typeof Symbol!=="undefined"&&obj.constructor===Symbol?"symbol":typeof obj}function _unsupported_iterable_to_array(o,minLen){if(!o)return;if(typeof o==="string")return _array_like_to_array(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);if(n==="Object"&&o.constructor)n=o.constructor.name;if(n==="Map"||n==="Set")return Array.from(n);if(n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return _array_like_to_array(o,minLen)}function _ts_generator(thisArg,body){var f,y,t,_={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},g=Object.create((typeof Iterator==="function"?Iterator:Object).prototype);return g.next=verb(0),g["throw"]=verb(1),g["return"]=verb(2),typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(g&&(g=0,op[0]&&(_=0)),_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]<t[3])){_.label=op[1];break}if(op[0]===6&&_.label<t[1]){_.label=t[1];t=op;break}if(t&&_.label<t[2]){_.label=t[2];_.ops.push(op);break}if(t[2])_.ops.pop();_.trys.pop();continue}op=body.call(thisArg,_)}catch(e){op=[6,e];y=0}finally{f=t=0}if(op[0]&5)throw op[1];return{value:op[0]?op[1]:void 0,done:true}}}function _ts_values(o){var s=typeof Symbol==="function"&&Symbol.iterator,m=s&&o[s],i=0;if(m)return m.call(o);if(o&&typeof o.length==="number")return{next:function(){if(o&&i>=o.length)o=void 0;return{value:o&&o[i++],done:!o}}};throw new TypeError(s?"Object is not iterable.":"Symbol.iterator is not defined.")}var FixtureManagerClass=/*#__PURE__*/function(){"use strict";function FixtureManagerClass(){_class_call_check(this,FixtureManagerClass);_define_property(this,"_tdb",null);_define_property(this,"_fdb",null);_define_property(this,"cachedTableNames",null);_define_property(this,"relationGraph",new _relationgraph.RelationGraph);_define_property(this,"visitedRecords",new Set)}_create_class(FixtureManagerClass,[{key:"tdb",get:function get(){if(this._tdb===null){throw new Error("FixtureManager has not been initialized")}return this._tdb},set:function set(tdb){this._tdb=tdb}},{key:"fdb",get:function get(){if(this._fdb===null){throw new Error("FixtureManager has not been initialized")}return this._fdb},set:function set(fdb){this._fdb=fdb}},{key:"init",value:function init(){if(this._tdb!==null){return}if(_api.Sonamu.dbConfig.test&&_api.Sonamu.dbConfig.production_master){var tConn=_api.Sonamu.dbConfig.test.connection;var pConn=_api.Sonamu.dbConfig.production_master.connection;var _tConn_host,_tConn_port,_pConn_host,_pConn_port;if("".concat((_tConn_host=tConn.host)!==null&&_tConn_host!==void 0?_tConn_host:"localhost",":").concat((_tConn_port=tConn.port)!==null&&_tConn_port!==void 0?_tConn_port:3306,"/").concat(tConn.database)==="".concat((_pConn_host=pConn.host)!==null&&_pConn_host!==void 0?_pConn_host:"localhost",":").concat((_pConn_port=pConn.port)!==null&&_pConn_port!==void 0?_pConn_port:3306,"/").concat(pConn.database)){throw new Error("테스트DB와 프로덕션DB에 동일한 데이터베이스가 사용되었습니다.")}}this.tdb=(0,_knex.default)(_api.Sonamu.dbConfig.test);this.fdb=(0,_knex.default)(_api.Sonamu.dbConfig.fixture_local)}},{key:"cleanAndSeed",value:function cleanAndSeed(usingTables){return _async_to_generator(function(){var _this,tableNames,tableListStr,_ref,fdbChecksumRows,_ref1,tdbChecksumRows,fdbChecksums,tdbChecksums,changedTables;return _ts_generator(this,function(_state){switch(_state.label){case 0:_this=this;return[4,function(){return _async_to_generator(function(){var _ref,tables,tableNames;return _ts_generator(this,function(_state){switch(_state.label){case 0:if(usingTables){return[2,usingTables]}if(this.cachedTableNames){return[2,this.cachedTableNames]}return[4,this.tdb.raw("SHOW TABLE STATUS WHERE Engine IS NOT NULL AND Name != 'migrations'")];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]),tables=_ref[0];tableNames=tables.map(function(tableInfo){return tableInfo["Name"]});this.cachedTableNames=tableNames;return[2,tableNames]}})}).call(_this)}()];case 1:tableNames=_state.sent();tableListStr=tableNames.join(", ");return[4,this.fdb.raw("CHECKSUM TABLE ".concat(tableListStr))];case 2:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]),fdbChecksumRows=_ref[0];return[4,this.tdb.raw("CHECKSUM TABLE ".concat(tableListStr))];case 3:_ref1=_sliced_to_array.apply(void 0,[_state.sent(),1]),tdbChecksumRows=_ref1[0];fdbChecksums=new Map(fdbChecksumRows.map(function(row){return[row.Table.split(".").pop(),row.Checksum]}));tdbChecksums=new Map(tdbChecksumRows.map(function(row){return[row.Table.split(".").pop(),row.Checksum]}));changedTables=tableNames.filter(function(tableName){return fdbChecksums.get(tableName)!==tdbChecksums.get(tableName)});return[4,this.tdb.transaction(function(trx){return _async_to_generator(function(){return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,trx.raw("SET FOREIGN_KEY_CHECKS = 0")];case 1:_state.sent();return[4,Promise.all(changedTables.map(function(tableName){return _async_to_generator(function(){var rawQuery;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,trx.raw("SET FOREIGN_KEY_CHECKS = 0")];case 1:_state.sent();return[4,trx(tableName).truncate()];case 2:_state.sent();rawQuery="INSERT INTO ".concat(_api.Sonamu.dbConfig.test.connection.database,".").concat(tableName,"\n SELECT * FROM ").concat(_api.Sonamu.dbConfig.fixture_local.connection.database,".").concat(tableName);return[4,trx.raw(rawQuery)];case 3:_state.sent();return[2]}})})()}))];case 2:_state.sent();return[4,trx.raw("SET FOREIGN_KEY_CHECKS = 1")];case 3:_state.sent();return[2]}})})()})];case 4:_state.sent();return[2]}})}).call(this)}},{key:"getChecksum",value:function getChecksum(db,tableName){return _async_to_generator(function(){var _ref,_ref_,checksumRow;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,db.raw("CHECKSUM TABLE ".concat(tableName))];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]),_ref_=_sliced_to_array(_ref[0],1),checksumRow=_ref_[0];return[2,checksumRow.Checksum]}})})()}},{key:"sync",value:function sync(){return _async_to_generator(function(){var _this,frdb,_ref,tables,tableNames;return _ts_generator(this,function(_state){switch(_state.label){case 0:_this=this;frdb=(0,_knex.default)(_api.Sonamu.dbConfig.fixture_remote);return[4,this.fdb.raw("SHOW TABLE STATUS WHERE Engine IS NOT NULL")];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]),tables=_ref[0];tableNames=tables.map(function(table){return table.Name});console.log(_chalk.default.magenta("SYNC..."));return[4,Promise.all(tableNames.map(function(tableName){return _async_to_generator(function(){var remoteChecksum,localChecksum;return _ts_generator(this,function(_state){switch(_state.label){case 0:if(tableName.startsWith("knex_migrations")){return[2]}return[4,this.getChecksum(frdb,tableName)];case 1:remoteChecksum=_state.sent();return[4,this.getChecksum(this.fdb,tableName)];case 2:localChecksum=_state.sent();if(!(remoteChecksum!==localChecksum))return[3,4];return[4,this.fdb.transaction(function(transaction){return _async_to_generator(function(){var rows;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,transaction.raw("SET FOREIGN_KEY_CHECKS = 0")];case 1:_state.sent();return[4,transaction(tableName).truncate()];case 2:_state.sent();return[4,frdb(tableName)];case 3:rows=_state.sent();if(rows.length===0){return[2]}console.log(_chalk.default.blue(tableName),rows.length);return[4,transaction.insert(rows.map(function(row){return Object.fromEntries(Object.entries(row).map(function(param){var _param=_sliced_to_array(param,2),key=_param[0],value=_param[1];if(value===null){return[key,null]}else if(typeof value==="boolean"){return[key,value?1:0]}else if((typeof value==="undefined"?"undefined":_type_of(value))==="object"&&!_instanceof(value,Date)){return[key,JSON.stringify(value)]}else{return[key,value]}}))})).into(tableName)];case 4:_state.sent();console.log("OK");return[4,transaction.raw("SET FOREIGN_KEY_CHECKS = 1")];case 5:_state.sent();return[2]}})})()})];case 3:_state.sent();_state.label=4;case 4:return[2]}})}).call(_this)}))];case 2:_state.sent();console.log(_chalk.default.magenta("DONE!"));return[4,frdb.destroy()];case 3:_state.sent();return[2]}})}).call(this)}},{key:"importFixture",value:function importFixture(entityId,ids){return _async_to_generator(function(){var _this,queries,_,wdb,_iteratorNormalCompletion,_didIteratorError,_iteratorError,_iterator,_step,query,_ref,rsh,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:_this=this;this.visitedRecords.clear();_=_lodash.default.uniq;return[4,Promise.all(ids.map(function(id){return _async_to_generator(function(){return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,this.getImportQueries(entityId,"id",id)];case 1:return[2,_state.sent()]}})}).call(_this)}))];case 1:queries=_.apply(_lodash.default,[_state.sent().flat()]);wdb=_basemodel.BaseModel.getDB("w");_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=2;case 2:_state.trys.push([2,7,8,9]);_iterator=queries[Symbol.iterator]();_state.label=3;case 3:if(!!(_iteratorNormalCompletion=(_step=_iterator.next()).done))return[3,6];query=_step.value;return[4,wdb.raw(query)];case 4:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]),rsh=_ref[0];console.log({query:query,info:rsh.info});_state.label=5;case 5:_iteratorNormalCompletion=true;return[3,3];case 6:return[3,9];case 7:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,9];case 8:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 9:return[2]}})}).call(this)}},{key:"getImportQueries",value:function getImportQueries(entityId,field,id){return _async_to_generator(function(){var _this,recordKey,entity,wdb,_ref,row,fixtureDatabase,realDatabase,selfQuery,args,relQueries;return _ts_generator(this,function(_state){switch(_state.label){case 0:_this=this;recordKey="".concat(entityId,"#").concat(field,"#").concat(id);if(this.visitedRecords.has(recordKey)){return[2,[]]}this.visitedRecords.add(recordKey);console.log({entityId:entityId,field:field,id:id});entity=_entitymanager.EntityManager.get(entityId);wdb=_basemodel.BaseModel.getDB("w");return[4,wdb(entity.table).where(field,id).limit(1)];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]),row=_ref[0];if(row===undefined){throw new Error("".concat(entityId,"#").concat(id," row를 찾을 수 없습니다."))}fixtureDatabase=_api.Sonamu.dbConfig.fixture_remote.connection.database;realDatabase=_api.Sonamu.dbConfig.production_master.connection.database;selfQuery="INSERT IGNORE INTO `".concat(fixtureDatabase,"`.`").concat(entity.table,"` (SELECT * FROM `").concat(realDatabase,"`.`").concat(entity.table,"` WHERE `id` = ").concat(id,")");args=Object.entries(entity.relations).filter(function(param){var _param=_sliced_to_array(param,2),relation=_param[1];return(0,_types.isBelongsToOneRelationProp)(relation)||(0,_types.isOneToOneRelationProp)(relation)&&relation.customJoinClause===undefined}).map(function(param){var _param=_sliced_to_array(param,2),relation=_param[1];var _$field;var _$id;if((0,_types.isOneToOneRelationProp)(relation)&&!relation.hasJoinColumn){var _relatedEntity_props_find;var relatedEntity=_entitymanager.EntityManager.get(relation.with);var relatedIdColumnName=(_relatedEntity_props_find=relatedEntity.props.find(function(p){return(0,_types.isRelationProp)(p)&&p.with===entity.id}))===null||_relatedEntity_props_find===void 0?void 0:_relatedEntity_props_find.name;if(!relatedIdColumnName){throw new Error("".concat(relatedEntity.id,"의 ").concat(entity.id," 관계 프롭을 찾을 수 없습니다."))}_$field="".concat(relatedIdColumnName,"_id");_$id=row["id"]}else{_$field="id";_$id=row["".concat(relation.name,"_id")]}return{entityId:relation.with,field:_$field,id:_$id}}).filter(function(arg){return arg.id!==null});return[4,Promise.all(args.map(function(args){return _async_to_generator(function(){return _ts_generator(this,function(_state){return[2,this.getImportQueries(args.entityId,args.field,args.id)]})}).call(_this)}))];case 2:relQueries=_state.sent();return[2,_to_consumable_array(_lodash.default.uniq(relQueries.reverse().flat())).concat([selfQuery])]}})}).call(this)}},{key:"destroy",value:function destroy(){return _async_to_generator(function(){return _ts_generator(this,function(_state){switch(_state.label){case 0:if(!this._tdb)return[3,2];return[4,this._tdb.destroy()];case 1:_state.sent();this._tdb=null;_state.label=2;case 2:if(!this._fdb)return[3,4];return[4,this._fdb.destroy()];case 3:_state.sent();this._fdb=null;_state.label=4;case 4:return[4,_basemodel.BaseModel.destroy()];case 5:_state.sent();return[2]}})}).call(this)}},{key:"getFixtures",value:function getFixtures(sourceDBName,targetDBName,searchOptions){return _async_to_generator(function(){var _entity_props_find,sourceDB,targetDB,entityId,field,value,searchType,entity,column,query,rows,fixtures,_iteratorNormalCompletion,_didIteratorError,_iteratorError,_this,_loop,_iterator,_step,err,_iteratorAbruptCompletion,_didIteratorError1,_iteratorError1,_iterator1,_step1,_value,fixture,entity1,row,_ref,record,uniqueRow,_ref1,record1,err1;return _ts_generator(this,function(_state){switch(_state.label){case 0:sourceDB=(0,_knex.default)(_api.Sonamu.dbConfig[sourceDBName]);targetDB=(0,_knex.default)(_api.Sonamu.dbConfig[targetDBName]);entityId=searchOptions.entityId,field=searchOptions.field,value=searchOptions.value,searchType=searchOptions.searchType;entity=_entitymanager.EntityManager.get(entityId);column=((_entity_props_find=entity.props.find(function(prop){return prop.name===field}))===null||_entity_props_find===void 0?void 0:_entity_props_find.type)==="relation"?"".concat(field,"_id"):field;query=sourceDB(entity.table);if(searchType==="equals"){query=query.where(column,value)}else if(searchType==="like"){query=query.where(column,"like","%".concat(value,"%"))}return[4,query];case 1:rows=_state.sent();if(rows.length===0){throw new Error("No records found")}fixtures=[];_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=2;case 2:_state.trys.push([2,7,8,9]);_loop=function(){var row,_fixtures,initialRecordsLength,newRecords,currentFixtureRecord;return _ts_generator(this,function(_state){switch(_state.label){case 0:row=_step.value;initialRecordsLength=fixtures.length;return[4,_this.createFixtureRecord(entity,row)];case 1:newRecords=_state.sent();(_fixtures=fixtures).push.apply(_fixtures,_to_consumable_array(newRecords));currentFixtureRecord=fixtures.find(function(r){return r.fixtureId==="".concat(entityId,"#").concat(row.id)});if(currentFixtureRecord){currentFixtureRecord.fetchedRecords=fixtures.filter(function(r){return r.fixtureId!==currentFixtureRecord.fixtureId}).slice(initialRecordsLength).map(function(r){return r.fixtureId})}return[2]}})};_iterator=rows[Symbol.iterator]();_state.label=3;case 3:if(!!(_iteratorNormalCompletion=(_step=_iterator.next()).done))return[3,6];_this=this;return[5,_ts_values(_loop())];case 4:_state.sent();_state.label=5;case 5:_iteratorNormalCompletion=true;return[3,3];case 6:return[3,9];case 7:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,9];case 8:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 9:_iteratorAbruptCompletion=false,_didIteratorError1=false;_state.label=10;case 10:_state.trys.push([10,20,21,26]);_iterator1=_async_iterator(fixtures);_state.label=11;case 11:return[4,_iterator1.next()];case 12:if(!(_iteratorAbruptCompletion=!(_step1=_state.sent()).done))return[3,19];_value=_step1.value;fixture=_value;entity1=_entitymanager.EntityManager.get(fixture.entityId);return[4,targetDB(entity1.table).where("id",fixture.id).first()];case 13:row=_state.sent();if(!row)return[3,15];return[4,this.createFixtureRecord(entity1,row,{singleRecord:true,_db:targetDB})];case 14:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]),record=_ref[0];fixture.target=record;return[3,18];case 15:return[4,this.checkUniqueViolation(targetDB,entity1,fixture)];case 16:uniqueRow=_state.sent();if(!uniqueRow)return[3,18];return[4,this.createFixtureRecord(entity1,uniqueRow,{singleRecord:true,_db:targetDB})];case 17:_ref1=_sliced_to_array.apply(void 0,[_state.sent(),1]),record1=_ref1[0];fixture.unique=record1;_state.label=18;case 18:_iteratorAbruptCompletion=false;return[3,11];case 19:return[3,26];case 20:err1=_state.sent();_didIteratorError1=true;_iteratorError1=err1;return[3,26];case 21:_state.trys.push([21,,24,25]);if(!(_iteratorAbruptCompletion&&_iterator1.return!=null))return[3,23];return[4,_iterator1.return()];case 22:_state.sent();_state.label=23;case 23:return[3,25];case 24:if(_didIteratorError1){throw _iteratorError1}return[7];case 25:return[7];case 26:return[2,_lodash.default.uniqBy(fixtures,function(f){return f.fixtureId})]}})}).call(this)}},{key:"createFixtureRecord",value:function createFixtureRecord(entity,row,options){return _async_to_generator(function(){var records,visitedEntities,create;return _ts_generator(this,function(_state){switch(_state.label){case 0:records=[];visitedEntities=new Set;create=function(entity,row){return _async_to_generator(function(){var fixtureId,record,_iteratorNormalCompletion,_didIteratorError,_iteratorError,_iterator,_step,prop,_options__db,db,relatedEntity,throughTable,fromColumn,toColumn,relatedIds,relatedEntity1,relatedIds1,relatedEntity2,relatedProp,relatedRow,relatedId,relatedEntity3,relatedRow1,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:fixtureId="".concat(entity.id,"#").concat(row.id);if(visitedEntities.has(fixtureId)){return[2]}visitedEntities.add(fixtureId);record={fixtureId:fixtureId,entityId:entity.id,id:row.id,columns:{},fetchedRecords:[],belongsRecords:[]};_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=1;case 1:_state.trys.push([1,14,15,16]);_iterator=entity.props[Symbol.iterator]();_state.label=2;case 2:if(!!(_iteratorNormalCompletion=(_step=_iterator.next()).done))return[3,13];prop=_step.value;if((0,_types.isVirtualProp)(prop)){return[3,12]}record.columns[prop.name]={prop:prop,value:row[prop.name]};db=(_options__db=options===null||options===void 0?void 0:options._db)!==null&&_options__db!==void 0?_options__db:_basemodel.BaseModel.getDB("w");if(!(0,_types.isManyToManyRelationProp)(prop))return[3,4];relatedEntity=_entitymanager.EntityManager.get(prop.with);throughTable=prop.joinTable;fromColumn="".concat(_inflection.default.singularize(entity.table),"_id");toColumn="".concat(_inflection.default.singularize(relatedEntity.table),"_id");return[4,db(throughTable).where(fromColumn,row.id).pluck(toColumn)];case 3:relatedIds=_state.sent();record.columns[prop.name].value=relatedIds;return[3,12];case 4:if(!(0,_types.isHasManyRelationProp)(prop))return[3,6];relatedEntity1=_entitymanager.EntityManager.get(prop.with);return[4,db(relatedEntity1.table).where(prop.joinColumn,row.id).pluck("id")];case 5:relatedIds1=_state.sent();record.columns[prop.name].value=relatedIds1;return[3,12];case 6:if(!((0,_types.isOneToOneRelationProp)(prop)&&!prop.hasJoinColumn))return[3,9];relatedEntity2=_entitymanager.EntityManager.get(prop.with);relatedProp=relatedEntity2.props.find(function(p){return(0,_types.isRelationProp)(p)&&p.with===entity.id});if(!relatedProp)return[3,8];return[4,db(relatedEntity2.table).where("id",row.id).first()];case 7:relatedRow=_state.sent();record.columns[prop.name].value=relatedRow===null||relatedRow===void 0?void 0:relatedRow.id;_state.label=8;case 8:return[3,12];case 9:if(!(0,_types.isRelationProp)(prop))return[3,12];relatedId=row["".concat(prop.name,"_id")];record.columns[prop.name].value=relatedId;if(relatedId){record.belongsRecords.push("".concat(prop.with,"#").concat(relatedId))}if(!(!(options===null||options===void 0?void 0:options.singleRecord)&&relatedId))return[3,12];relatedEntity3=_entitymanager.EntityManager.get(prop.with);return[4,db(relatedEntity3.table).where("id",relatedId).first()];case 10:relatedRow1=_state.sent();if(!relatedRow1)return[3,12];return[4,create(relatedEntity3,relatedRow1)];case 11:_state.sent();_state.label=12;case 12:_iteratorNormalCompletion=true;return[3,2];case 13:return[3,16];case 14:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,16];case 15:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 16:records.push(record);return[2]}})})()};return[4,create(entity,row)];case 1:_state.sent();return[2,records]}})})()}},{key:"insertFixtures",value:function insertFixtures(dbName,_fixtures){return _async_to_generator(function(){var _this,fixtures,insertionOrder,db,records,_iteratorAbruptCompletion,_didIteratorError,_iteratorError,_iterator,_step,_value,r,entity,record,err1;return _ts_generator(this,function(_state){switch(_state.label){case 0:_this=this;fixtures=_lodash.default.uniqBy(_fixtures,function(f){return f.fixtureId});this.relationGraph.buildGraph(fixtures);insertionOrder=this.relationGraph.getInsertionOrder();db=(0,_knex.default)(_api.Sonamu.dbConfig[dbName]);return[4,db.transaction(function(trx){return _async_to_generator(function(){var _iteratorNormalCompletion,_didIteratorError,_iteratorError,_this,_loop,_iterator,_step,err,_iteratorNormalCompletion1,_didIteratorError1,_iteratorError1,_this1,_loop1,_iterator1,_step1,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,trx.raw("SET FOREIGN_KEY_CHECKS = 0")];case 1:_state.sent();_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=2;case 2:_state.trys.push([2,7,8,9]);_loop=function(){var fixtureId,fixture,result;return _ts_generator(this,function(_state){switch(_state.label){case 0:fixtureId=_step.value;fixture=fixtures.find(function(f){return f.fixtureId===fixtureId});return[4,_this.insertFixture(trx,fixture)];case 1:result=_state.sent();if(result.id!==fixture.id){console.log(_chalk.default.yellow("Unique constraint violation: ".concat(fixture.entityId,"#").concat(fixture.id," -> ").concat(fixture.entityId,"#").concat(result.id)));fixtures.forEach(function(f){Object.values(f.columns).forEach(function(column){if(column.prop.type==="relation"&&column.prop.with===result.entityId&&column.value===fixture.id){column.value=result.id}})});fixture.id=result.id}return[2]}})};_iterator=insertionOrder[Symbol.iterator]();_state.label=3;case 3:if(!!(_iteratorNormalCompletion=(_step=_iterator.next()).done))return[3,6];_this=this;return[5,_ts_values(_loop())];case 4:_state.sent();_state.label=5;case 5:_iteratorNormalCompletion=true;return[3,3];case 6:return[3,9];case 7:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,9];case 8:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 9:_iteratorNormalCompletion1=true,_didIteratorError1=false,_iteratorError1=undefined;_state.label=10;case 10:_state.trys.push([10,15,16,17]);_loop1=function(){var fixtureId,fixture;return _ts_generator(this,function(_state){switch(_state.label){case 0:fixtureId=_step1.value;fixture=fixtures.find(function(f){return f.fixtureId===fixtureId});return[4,_this1.handleManyToManyRelations(trx,fixture,fixtures)];case 1:_state.sent();return[2]}})};_iterator1=insertionOrder[Symbol.iterator]();_state.label=11;case 11:if(!!(_iteratorNormalCompletion1=(_step1=_iterator1.next()).done))return[3,14];_this1=this;return[5,_ts_values(_loop1())];case 12:_state.sent();_state.label=13;case 13:_iteratorNormalCompletion1=true;return[3,11];case 14:return[3,17];case 15:err=_state.sent();_didIteratorError1=true;_iteratorError1=err;return[3,17];case 16:try{if(!_iteratorNormalCompletion1&&_iterator1.return!=null){_iterator1.return()}}finally{if(_didIteratorError1){throw _iteratorError1}}return[7];case 17:return[4,trx.raw("SET FOREIGN_KEY_CHECKS = 1")];case 18:_state.sent();return[2]}})}).call(_this)})];case 1:_state.sent();records=[];_iteratorAbruptCompletion=false,_didIteratorError=false;_state.label=2;case 2:_state.trys.push([2,8,9,14]);_iterator=_async_iterator(fixtures);_state.label=3;case 3:return[4,_iterator.next()];case 4:if(!(_iteratorAbruptCompletion=!(_step=_state.sent()).done))return[3,7];_value=_step.value;r=_value;entity=_entitymanager.EntityManager.get(r.entityId);return[4,db(entity.table).where("id",r.id).first()];case 5:record=_state.sent();records.push({entityId:r.entityId,data:record});_state.label=6;case 6:_iteratorAbruptCompletion=false;return[3,3];case 7:return[3,14];case 8:err1=_state.sent();_didIteratorError=true;_iteratorError=err1;return[3,14];case 9:_state.trys.push([9,,12,13]);if(!(_iteratorAbruptCompletion&&_iterator.return!=null))return[3,11];return[4,_iterator.return()];case 10:_state.sent();_state.label=11;case 11:return[3,13];case 12:if(_didIteratorError){throw _iteratorError}return[7];case 13:return[7];case 14:return[2,_lodash.default.uniqBy(records,function(r){return"".concat(r.entityId,"#").concat(r.data.id)})]}})}).call(this)}},{key:"prepareInsertData",value:function prepareInsertData(fixture){var insertData={};var _iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;try{for(var _iterator=Object.entries(fixture.columns)[Symbol.iterator](),_step;!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=true){var _step_value=_sliced_to_array(_step.value,2),propName=_step_value[0],column=_step_value[1];if((0,_types.isVirtualProp)(column.prop)){continue}var prop=column.prop;if(!(0,_types.isRelationProp)(prop)){if(prop.type==="json"){insertData[propName]=JSON.stringify(column.value)}else if(prop.type==="timestamp"||prop.type==="datetime"){insertData[propName]=new Date(column.value)}else{insertData[propName]=column.value}}else if((0,_types.isBelongsToOneRelationProp)(prop)||(0,_types.isOneToOneRelationProp)(prop)&&prop.hasJoinColumn){insertData["".concat(propName,"_id")]=column.value}}}catch(err){_didIteratorError=true;_iteratorError=err}finally{try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}}return insertData}},{key:"insertFixture",value:function insertFixture(db,fixture){return _async_to_generator(function(){var insertData,entity,uniqueFound,found,q,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:insertData=this.prepareInsertData(fixture);entity=_entitymanager.EntityManager.get(fixture.entityId);_state.label=1;case 1:_state.trys.push([1,5,,6]);return[4,this.checkUniqueViolation(db,entity,fixture)];case 2:uniqueFound=_state.sent();if(uniqueFound){return[2,{entityId:fixture.entityId,id:uniqueFound.id}]}return[4,db(entity.table).where("id",fixture.id).first()];case 3:found=_state.sent();if(found&&!fixture.override){return[2,{entityId:fixture.entityId,id:found.id}]}q=db.insert(insertData).into(entity.table);return[4,q.onDuplicateUpdate.apply(q,Object.keys(insertData))];case 4:_state.sent();return[2,{entityId:fixture.entityId,id:fixture.id}];case 5:err=_state.sent();console.log(err);throw err;case 6:return[2]}})}).call(this)}},{key:"handleManyToManyRelations",value:function handleManyToManyRelations(db,fixture,fixtures){return _async_to_generator(function(){var _iteratorNormalCompletion,_didIteratorError,_iteratorError,_loop,_iterator,_step,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=1;case 1:_state.trys.push([1,6,7,8]);_loop=function(){var _step_value,column,prop,joinTable,relatedIds,_iteratorNormalCompletion,_didIteratorError,_iteratorError,_loop,_iterator,_step1,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:_step_value=_sliced_to_array(_step.value,2),column=_step_value[1];prop=column.prop;if(!(0,_types.isManyToManyRelationProp)(prop))return[3,8];joinTable=prop.joinTable;relatedIds=column.value;_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=1;case 1:_state.trys.push([1,6,7,8]);_loop=function(){var relatedId,entity,relatedEntity,_obj,_ref,found,_obj1,newIds;return _ts_generator(this,function(_state){switch(_state.label){case 0:relatedId=_step1.value;if(!fixtures.find(function(f){return f.fixtureId==="".concat(prop.with,"#").concat(relatedId)})){return[2,"continue"]}entity=_entitymanager.EntityManager.get(fixture.entityId);relatedEntity=_entitymanager.EntityManager.get(prop.with);if(!entity||!relatedEntity){throw new Error("Entity not found: ".concat(fixture.entityId,", ").concat(prop.with))}return[4,db(joinTable).where((_obj={},_define_property(_obj,"".concat(_inflection.default.singularize(entity.table),"_id"),fixture.id),_define_property(_obj,"".concat(_inflection.default.singularize(relatedEntity.table),"_id"),relatedId),_obj)).limit(1)];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]),found=_ref[0];if(found){return[2,"continue"]}return[4,db(joinTable).insert((_obj1={},_define_property(_obj1,"".concat(_inflection.default.singularize(entity.table),"_id"),fixture.id),_define_property(_obj1,"".concat(_inflection.default.singularize(relatedEntity.table),"_id"),relatedId),_obj1))];case 2:newIds=_state.sent();console.log(_chalk.default.green("Inserted into ".concat(joinTable,": ").concat(entity.table,"(").concat(fixture.id,") - ").concat(relatedEntity.table,"(").concat(relatedId,") ID: ").concat(newIds)));return[2]}})};_iterator=relatedIds[Symbol.iterator]();_state.label=2;case 2:if(!!(_iteratorNormalCompletion=(_step1=_iterator.next()).done))return[3,5];return[5,_ts_values(_loop())];case 3:_state.sent();_state.label=4;case 4:_iteratorNormalCompletion=true;return[3,2];case 5:return[3,8];case 6:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,8];case 7:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 8:return[2]}})};_iterator=Object.entries(fixture.columns)[Symbol.iterator]();_state.label=2;case 2:if(!!(_iteratorNormalCompletion=(_step=_iterator.next()).done))return[3,5];return[5,_ts_values(_loop())];case 3:_state.sent();_state.label=4;case 4:_iteratorNormalCompletion=true;return[3,2];case 5:return[3,8];case 6:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,8];case 7:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 8:return[2]}})})()}},{key:"addFixtureLoader",value:function addFixtureLoader(code){return _async_to_generator(function(){var path,content,fixtureLoaderStart,fixtureLoaderEnd,newContent;return _ts_generator(this,function(_state){path=_api.Sonamu.apiRootPath+"/src/testing/fixture.ts";content=(0,_fs.readFileSync)(path).toString();fixtureLoaderStart=content.indexOf("const fixtureLoader = {");fixtureLoaderEnd=content.indexOf("};",fixtureLoaderStart);if(fixtureLoaderStart!==-1&&fixtureLoaderEnd!==-1){newContent=content.slice(0,fixtureLoaderEnd)+" "+code+"\n"+content.slice(fixtureLoaderEnd);(0,_fs.writeFileSync)(path,newContent)}else{throw new Error("Failed to find fixtureLoader in fixture.ts")}return[2]})})()}},{key:"checkUniqueViolation",value:function checkUniqueViolation(db,entity,fixture){return _async_to_generator(function(){var _uniqueIndexes,uniqueIndexes,uniqueQuery,_iteratorNormalCompletion,_didIteratorError,_iteratorError,_loop,_iterator,_step,_ref,uniqueFound;return _ts_generator(this,function(_state){switch(_state.label){case 0:_uniqueIndexes=entity.indexes.filter(function(i){return i.type==="unique"});uniqueIndexes=_uniqueIndexes.filter(function(index){return index.columns.every(function(column){return!column.startsWith("".concat(entity.table,"__"))})});if(uniqueIndexes.length===0){return[2,null]}uniqueQuery=db(entity.table);_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;try{_loop=function(){var index=_step.value;var containsNull=index.columns.some(function(column){var field=column.split("_id")[0];return fixture.columns[field].value===null});if(containsNull){return"continue"}uniqueQuery=uniqueQuery.orWhere(function(qb){var _iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;try{for(var _iterator=index.columns[Symbol.iterator](),_step;!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=true){var column=_step.value;var field=column.split("_id")[0];if(Array.isArray(fixture.columns[field].value)){qb.whereIn(column,fixture.columns[field].value)}else{qb.andWhere(column,fixture.columns[field].value)}}}catch(err){_didIteratorError=true;_iteratorError=err}finally{try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}}})};for(_iterator=uniqueIndexes[Symbol.iterator]();!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=true)_loop()}catch(err){_didIteratorError=true;_iteratorError=err}finally{try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}}return[4,uniqueQuery];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]),uniqueFound=_ref[0];return[2,uniqueFound]}})})()}}]);return FixtureManagerClass}();var FixtureManager=new FixtureManagerClass;
2
- //# sourceMappingURL=fixture-manager.js.map
1
+ import chalk from "chalk";
2
+ import * as _ from "lodash-es";
3
+ import { Sonamu } from "../api/index.js";
4
+ import { EntityManager } from "../entity/entity-manager.js";
5
+ import { isBelongsToOneRelationProp, isHasManyRelationProp, isManyToManyRelationProp, isOneToOneRelationProp, isRelationProp, isVirtualProp } from "../types/types.js";
6
+ import inflection from "inflection";
7
+ import { readFileSync, writeFileSync } from "fs";
8
+ import { RelationGraph } from "./_relation-graph.js";
9
+ import knex from "knex";
10
+ import { BaseModel } from "../database/base-model.js";
11
+ export class FixtureManagerClass {
12
+ _tdb = null;
13
+ set tdb(tdb) {
14
+ this._tdb = tdb;
15
+ }
16
+ get tdb() {
17
+ if (this._tdb === null) {
18
+ throw new Error("FixtureManager has not been initialized");
19
+ }
20
+ return this._tdb;
21
+ }
22
+ _fdb = null;
23
+ set fdb(fdb) {
24
+ this._fdb = fdb;
25
+ }
26
+ get fdb() {
27
+ if (this._fdb === null) {
28
+ throw new Error("FixtureManager has not been initialized");
29
+ }
30
+ return this._fdb;
31
+ }
32
+ cachedTableNames = null;
33
+ relationGraph = new RelationGraph();
34
+ init() {
35
+ if (this._tdb !== null) {
36
+ return;
37
+ }
38
+ if (Sonamu.dbConfig.test && Sonamu.dbConfig.production_master) {
39
+ const tConn = Sonamu.dbConfig.test.connection;
40
+ const pConn = Sonamu.dbConfig.production_master.connection;
41
+ if (`${tConn.host ?? "localhost"}:${tConn.port ?? 3306}/${tConn.database}` === `${pConn.host ?? "localhost"}:${pConn.port ?? 3306}/${pConn.database}`) {
42
+ throw new Error(`테스트DB와 프로덕션DB에 동일한 데이터베이스가 사용되었습니다.`);
43
+ }
44
+ }
45
+ this.tdb = knex(Sonamu.dbConfig.test);
46
+ this.fdb = knex(Sonamu.dbConfig.fixture_local);
47
+ }
48
+ async cleanAndSeed(usingTables) {
49
+ const tableNames = await (async ()=>{
50
+ if (usingTables) {
51
+ return usingTables;
52
+ }
53
+ if (this.cachedTableNames) {
54
+ return this.cachedTableNames;
55
+ }
56
+ const [tables] = await this.tdb.raw(`SHOW TABLE STATUS WHERE Engine IS NOT NULL AND Name != 'migrations'`);
57
+ const tableNames = tables.map((tableInfo)=>tableInfo["Name"]);
58
+ this.cachedTableNames = tableNames;
59
+ return tableNames;
60
+ })();
61
+ // migrations 제외한 테이블 목록
62
+ const tableListStr = tableNames.join(", ");
63
+ // 한 번에 모든 테이블 체크섬 확인
64
+ const [fdbChecksumRows] = await this.fdb.raw(`CHECKSUM TABLE ${tableListStr}`);
65
+ const [tdbChecksumRows] = await this.tdb.raw(`CHECKSUM TABLE ${tableListStr}`);
66
+ // 체크섬 맵 생성
67
+ const fdbChecksums = new Map(fdbChecksumRows.map((row)=>[
68
+ row.Table.split(".").pop(),
69
+ row.Checksum
70
+ ]));
71
+ const tdbChecksums = new Map(tdbChecksumRows.map((row)=>[
72
+ row.Table.split(".").pop(),
73
+ row.Checksum
74
+ ]));
75
+ // 변경된 테이블들만 처리
76
+ const changedTables = tableNames.filter((tableName)=>fdbChecksums.get(tableName) !== tdbChecksums.get(tableName));
77
+ // 병렬로 truncate + insert 실행
78
+ await this.tdb.transaction(async (trx)=>{
79
+ await trx.raw(`SET FOREIGN_KEY_CHECKS = 0`);
80
+ await Promise.all(changedTables.map(async (tableName)=>{
81
+ await trx.raw(`SET FOREIGN_KEY_CHECKS = 0`);
82
+ await trx(tableName).truncate();
83
+ const rawQuery = `INSERT INTO ${Sonamu.dbConfig.test.connection.database}.${tableName}
84
+ SELECT * FROM ${Sonamu.dbConfig.fixture_local.connection.database}.${tableName}`;
85
+ await trx.raw(rawQuery);
86
+ }));
87
+ await trx.raw(`SET FOREIGN_KEY_CHECKS = 1`);
88
+ });
89
+ // console.timeEnd("FIXTURE-CleanAndSeed");
90
+ }
91
+ async getChecksum(db, tableName) {
92
+ const [[checksumRow]] = await db.raw(`CHECKSUM TABLE ${tableName}`);
93
+ return checksumRow.Checksum;
94
+ }
95
+ async sync() {
96
+ const frdb = knex(Sonamu.dbConfig.fixture_remote);
97
+ const [tables] = await this.fdb.raw("SHOW TABLE STATUS WHERE Engine IS NOT NULL");
98
+ const tableNames = tables.map((table)=>table.Name);
99
+ console.log(chalk.magenta("SYNC..."));
100
+ await Promise.all(tableNames.map(async (tableName)=>{
101
+ if (tableName.startsWith("knex_migrations")) {
102
+ return;
103
+ }
104
+ const remoteChecksum = await this.getChecksum(frdb, tableName);
105
+ const localChecksum = await this.getChecksum(this.fdb, tableName);
106
+ if (remoteChecksum !== localChecksum) {
107
+ await this.fdb.transaction(async (transaction)=>{
108
+ await transaction.raw(`SET FOREIGN_KEY_CHECKS = 0`);
109
+ await transaction(tableName).truncate();
110
+ const rows = await frdb(tableName);
111
+ if (rows.length === 0) {
112
+ return;
113
+ }
114
+ console.log(chalk.blue(tableName), rows.length);
115
+ await transaction.insert(rows.map((row)=>{
116
+ return Object.fromEntries(Object.entries(row).map(([key, value])=>{
117
+ if (value === null) {
118
+ return [
119
+ key,
120
+ null
121
+ ];
122
+ } else if (typeof value === "boolean") {
123
+ return [
124
+ key,
125
+ value ? 1 : 0
126
+ ];
127
+ } else if (typeof value === "object" && !(value instanceof Date)) {
128
+ return [
129
+ key,
130
+ JSON.stringify(value)
131
+ ];
132
+ } else {
133
+ return [
134
+ key,
135
+ value
136
+ ];
137
+ }
138
+ }));
139
+ })).into(tableName);
140
+ console.log("OK");
141
+ await transaction.raw(`SET FOREIGN_KEY_CHECKS = 1`);
142
+ });
143
+ }
144
+ }));
145
+ console.log(chalk.magenta("DONE!"));
146
+ await frdb.destroy();
147
+ }
148
+ visitedRecords = new Set();
149
+ async importFixture(entityId, ids) {
150
+ // 방문 기록 초기화 (새로운 import 작업 시작)
151
+ this.visitedRecords.clear();
152
+ const queries = _.uniq((await Promise.all(ids.map(async (id)=>{
153
+ return await this.getImportQueries(entityId, "id", id);
154
+ }))).flat());
155
+ const wdb = BaseModel.getDB("w");
156
+ for (let query of queries){
157
+ const [rsh] = await wdb.raw(query);
158
+ console.log({
159
+ query,
160
+ info: rsh.info
161
+ });
162
+ }
163
+ }
164
+ async getImportQueries(entityId, field, id) {
165
+ const recordKey = `${entityId}#${field}#${id}`;
166
+ // 순환 참조 방지: 이미 방문한 레코드는 스킵
167
+ if (this.visitedRecords.has(recordKey)) {
168
+ return [];
169
+ }
170
+ this.visitedRecords.add(recordKey);
171
+ console.log({
172
+ entityId,
173
+ field,
174
+ id
175
+ });
176
+ const entity = EntityManager.get(entityId);
177
+ const wdb = BaseModel.getDB("w");
178
+ // 여기서 실DB의 row 가져옴
179
+ const [row] = await wdb(entity.table).where(field, id).limit(1);
180
+ if (row === undefined) {
181
+ throw new Error(`${entityId}#${id} row를 찾을 수 없습니다.`);
182
+ }
183
+ // 픽스쳐DB, 실DB
184
+ const fixtureDatabase = Sonamu.dbConfig.fixture_remote.connection.database;
185
+ const realDatabase = Sonamu.dbConfig.production_master.connection.database;
186
+ const selfQuery = `INSERT IGNORE INTO \`${fixtureDatabase}\`.\`${entity.table}\` (SELECT * FROM \`${realDatabase}\`.\`${entity.table}\` WHERE \`id\` = ${id})`;
187
+ const args = Object.entries(entity.relations).filter(([, relation])=>isBelongsToOneRelationProp(relation) || isOneToOneRelationProp(relation) && relation.customJoinClause === undefined).map(([, relation])=>{
188
+ /*
189
+ BelongsToOne인 경우
190
+ Category / 'id' / row[category_id] 호출
191
+ OneToOne에 joinColumn === true 인 경우
192
+ Profile / 'id' / row[profile_id] 호출
193
+ OneToOne에 joinColumn === false 인 경우
194
+ Profile / 'profile_id' / row['id'] 호출
195
+ */ let field;
196
+ let id;
197
+ if (isOneToOneRelationProp(relation) && !relation.hasJoinColumn) {
198
+ const relatedEntity = EntityManager.get(relation.with);
199
+ const relatedIdColumnName = relatedEntity.props.find((p)=>isRelationProp(p) && p.with === entity.id)?.name;
200
+ if (!relatedIdColumnName) {
201
+ throw new Error(`${relatedEntity.id}의 ${entity.id} 관계 프롭을 찾을 수 없습니다.`);
202
+ }
203
+ field = `${relatedIdColumnName}_id`;
204
+ id = row["id"];
205
+ } else {
206
+ field = "id";
207
+ id = row[`${relation.name}_id`];
208
+ }
209
+ return {
210
+ entityId: relation.with,
211
+ field,
212
+ id
213
+ };
214
+ }).filter((arg)=>arg.id !== null);
215
+ const relQueries = await Promise.all(args.map(async (args)=>{
216
+ return this.getImportQueries(args.entityId, args.field, args.id);
217
+ }));
218
+ return [
219
+ ..._.uniq(relQueries.reverse().flat()),
220
+ selfQuery
221
+ ];
222
+ }
223
+ async destroy() {
224
+ if (this._tdb) {
225
+ await this._tdb.destroy();
226
+ this._tdb = null;
227
+ }
228
+ if (this._fdb) {
229
+ await this._fdb.destroy();
230
+ this._fdb = null;
231
+ }
232
+ await BaseModel.destroy();
233
+ }
234
+ async getFixtures(sourceDBName, targetDBName, searchOptions) {
235
+ const sourceDB = knex(Sonamu.dbConfig[sourceDBName]);
236
+ const targetDB = knex(Sonamu.dbConfig[targetDBName]);
237
+ const { entityId, field, value, searchType } = searchOptions;
238
+ const entity = EntityManager.get(entityId);
239
+ const column = entity.props.find((prop)=>prop.name === field)?.type === "relation" ? `${field}_id` : field;
240
+ let query = sourceDB(entity.table);
241
+ if (searchType === "equals") {
242
+ query = query.where(column, value);
243
+ } else if (searchType === "like") {
244
+ query = query.where(column, "like", `%${value}%`);
245
+ }
246
+ const rows = await query;
247
+ if (rows.length === 0) {
248
+ throw new Error("No records found");
249
+ }
250
+ const fixtures = [];
251
+ for (const row of rows){
252
+ const initialRecordsLength = fixtures.length;
253
+ const newRecords = await this.createFixtureRecord(entity, row);
254
+ fixtures.push(...newRecords);
255
+ const currentFixtureRecord = fixtures.find((r)=>r.fixtureId === `${entityId}#${row.id}`);
256
+ if (currentFixtureRecord) {
257
+ // 현재 fixture로부터 생성된 fetchedRecords 설정
258
+ currentFixtureRecord.fetchedRecords = fixtures.filter((r)=>r.fixtureId !== currentFixtureRecord.fixtureId).slice(initialRecordsLength).map((r)=>r.fixtureId);
259
+ }
260
+ }
261
+ for await (const fixture of fixtures){
262
+ const entity = EntityManager.get(fixture.entityId);
263
+ // ID를 이용하여 targetDB에 레코드가 존재하는지 확인
264
+ const row = await targetDB(entity.table).where("id", fixture.id).first();
265
+ if (row) {
266
+ const [record] = await this.createFixtureRecord(entity, row, {
267
+ singleRecord: true,
268
+ _db: targetDB
269
+ });
270
+ fixture.target = record;
271
+ continue;
272
+ }
273
+ // ID를 이용하여 targetDB에서 조회되지 않는 경우, unique 제약을 위반하는지 확인
274
+ const uniqueRow = await this.checkUniqueViolation(targetDB, entity, fixture);
275
+ if (uniqueRow) {
276
+ const [record] = await this.createFixtureRecord(entity, uniqueRow, {
277
+ singleRecord: true,
278
+ _db: targetDB
279
+ });
280
+ fixture.unique = record;
281
+ }
282
+ }
283
+ await targetDB.destroy();
284
+ await sourceDB.destroy();
285
+ return _.uniqBy(fixtures, (f)=>f.fixtureId);
286
+ }
287
+ async createFixtureRecord(entity, row, options) {
288
+ const records = [];
289
+ const visitedEntities = new Set();
290
+ const create = async (entity, row)=>{
291
+ const fixtureId = `${entity.id}#${row.id}`;
292
+ if (visitedEntities.has(fixtureId)) {
293
+ return;
294
+ }
295
+ visitedEntities.add(fixtureId);
296
+ const record = {
297
+ fixtureId,
298
+ entityId: entity.id,
299
+ id: row.id,
300
+ columns: {},
301
+ fetchedRecords: [],
302
+ belongsRecords: []
303
+ };
304
+ for (const prop of entity.props){
305
+ if (isVirtualProp(prop)) {
306
+ continue;
307
+ }
308
+ record.columns[prop.name] = {
309
+ prop: prop,
310
+ value: row[prop.name]
311
+ };
312
+ const db = options?._db ?? BaseModel.getDB("w");
313
+ if (isManyToManyRelationProp(prop)) {
314
+ const relatedEntity = EntityManager.get(prop.with);
315
+ const throughTable = prop.joinTable;
316
+ const fromColumn = `${inflection.singularize(entity.table)}_id`;
317
+ const toColumn = `${inflection.singularize(relatedEntity.table)}_id`;
318
+ const relatedIds = await db(throughTable).where(fromColumn, row.id).pluck(toColumn);
319
+ record.columns[prop.name].value = relatedIds;
320
+ } else if (isHasManyRelationProp(prop)) {
321
+ const relatedEntity = EntityManager.get(prop.with);
322
+ const relatedIds = await db(relatedEntity.table).where(prop.joinColumn, row.id).pluck("id");
323
+ record.columns[prop.name].value = relatedIds;
324
+ } else if (isOneToOneRelationProp(prop) && !prop.hasJoinColumn) {
325
+ const relatedEntity = EntityManager.get(prop.with);
326
+ const relatedProp = relatedEntity.props.find((p)=>isRelationProp(p) && p.with === entity.id);
327
+ if (relatedProp) {
328
+ const relatedRow = await db(relatedEntity.table).where("id", row.id).first();
329
+ record.columns[prop.name].value = relatedRow?.id;
330
+ }
331
+ } else if (isRelationProp(prop)) {
332
+ const relatedId = row[`${prop.name}_id`];
333
+ record.columns[prop.name].value = relatedId;
334
+ if (relatedId) {
335
+ record.belongsRecords.push(`${prop.with}#${relatedId}`);
336
+ }
337
+ if (!options?.singleRecord && relatedId) {
338
+ const relatedEntity = EntityManager.get(prop.with);
339
+ const relatedRow = await db(relatedEntity.table).where("id", relatedId).first();
340
+ if (relatedRow) {
341
+ await create(relatedEntity, relatedRow);
342
+ }
343
+ }
344
+ }
345
+ }
346
+ records.push(record);
347
+ };
348
+ await create(entity, row);
349
+ return records;
350
+ }
351
+ async insertFixtures(dbName, _fixtures) {
352
+ const fixtures = _.uniqBy(_fixtures, (f)=>f.fixtureId);
353
+ this.relationGraph.buildGraph(fixtures);
354
+ const insertionOrder = this.relationGraph.getInsertionOrder();
355
+ const db = knex(Sonamu.dbConfig[dbName]);
356
+ await db.transaction(async (trx)=>{
357
+ await trx.raw(`SET FOREIGN_KEY_CHECKS = 0`);
358
+ for (const fixtureId of insertionOrder){
359
+ const fixture = fixtures.find((f)=>f.fixtureId === fixtureId);
360
+ const result = await this.insertFixture(trx, fixture);
361
+ if (result.id !== fixture.id) {
362
+ // ID가 변경된 경우, 다른 fixture에서 참조하는 경우가 찾아서 수정
363
+ console.log(chalk.yellow(`Unique constraint violation: ${fixture.entityId}#${fixture.id} -> ${fixture.entityId}#${result.id}`));
364
+ fixtures.forEach((f)=>{
365
+ Object.values(f.columns).forEach((column)=>{
366
+ if (column.prop.type === "relation" && column.prop.with === result.entityId && column.value === fixture.id) {
367
+ column.value = result.id;
368
+ }
369
+ });
370
+ });
371
+ fixture.id = result.id;
372
+ }
373
+ }
374
+ for (const fixtureId of insertionOrder){
375
+ const fixture = fixtures.find((f)=>f.fixtureId === fixtureId);
376
+ await this.handleManyToManyRelations(trx, fixture, fixtures);
377
+ }
378
+ await trx.raw(`SET FOREIGN_KEY_CHECKS = 1`);
379
+ });
380
+ const records = [];
381
+ for await (const r of fixtures){
382
+ const entity = EntityManager.get(r.entityId);
383
+ const record = await db(entity.table).where("id", r.id).first();
384
+ records.push({
385
+ entityId: r.entityId,
386
+ data: record
387
+ });
388
+ }
389
+ await db.destroy();
390
+ return _.uniqBy(records, (r)=>`${r.entityId}#${r.data.id}`);
391
+ }
392
+ prepareInsertData(fixture) {
393
+ const insertData = {};
394
+ for (const [propName, column] of Object.entries(fixture.columns)){
395
+ if (isVirtualProp(column.prop)) {
396
+ continue;
397
+ }
398
+ const prop = column.prop;
399
+ if (!isRelationProp(prop)) {
400
+ if (prop.type === "json") {
401
+ insertData[propName] = JSON.stringify(column.value);
402
+ } else if (prop.type === "timestamp" || prop.type === "datetime") {
403
+ insertData[propName] = new Date(column.value);
404
+ } else {
405
+ insertData[propName] = column.value;
406
+ }
407
+ } else if (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop) && prop.hasJoinColumn) {
408
+ insertData[`${propName}_id`] = column.value;
409
+ }
410
+ }
411
+ return insertData;
412
+ }
413
+ async insertFixture(db, fixture) {
414
+ const insertData = this.prepareInsertData(fixture);
415
+ const entity = EntityManager.get(fixture.entityId);
416
+ try {
417
+ const uniqueFound = await this.checkUniqueViolation(db, entity, fixture);
418
+ if (uniqueFound) {
419
+ return {
420
+ entityId: fixture.entityId,
421
+ id: uniqueFound.id
422
+ };
423
+ }
424
+ const found = await db(entity.table).where("id", fixture.id).first();
425
+ if (found && !fixture.override) {
426
+ return {
427
+ entityId: fixture.entityId,
428
+ id: found.id
429
+ };
430
+ }
431
+ const q = db.insert(insertData).into(entity.table);
432
+ await q.onDuplicateUpdate.apply(q, Object.keys(insertData));
433
+ console.log(chalk.green(`Inserted into ${entity.table}: #${fixture.id}`));
434
+ return {
435
+ entityId: fixture.entityId,
436
+ id: fixture.id
437
+ };
438
+ } catch (err) {
439
+ console.log(err);
440
+ throw err;
441
+ }
442
+ }
443
+ async handleManyToManyRelations(db, fixture, fixtures) {
444
+ for (const [, column] of Object.entries(fixture.columns)){
445
+ const prop = column.prop;
446
+ if (isManyToManyRelationProp(prop)) {
447
+ const joinTable = prop.joinTable;
448
+ const relatedIds = column.value;
449
+ for (const relatedId of relatedIds){
450
+ if (!fixtures.find((f)=>f.fixtureId === `${prop.with}#${relatedId}`)) {
451
+ continue;
452
+ }
453
+ const entity = EntityManager.get(fixture.entityId);
454
+ const relatedEntity = EntityManager.get(prop.with);
455
+ if (!entity || !relatedEntity) {
456
+ throw new Error(`Entity not found: ${fixture.entityId}, ${prop.with}`);
457
+ }
458
+ const [found] = await db(joinTable).where({
459
+ [`${inflection.singularize(entity.table)}_id`]: fixture.id,
460
+ [`${inflection.singularize(relatedEntity.table)}_id`]: relatedId
461
+ }).limit(1);
462
+ if (found) {
463
+ continue;
464
+ }
465
+ const newIds = await db(joinTable).insert({
466
+ [`${inflection.singularize(entity.table)}_id`]: fixture.id,
467
+ [`${inflection.singularize(relatedEntity.table)}_id`]: relatedId
468
+ });
469
+ console.log(chalk.green(`Inserted into ${joinTable}: ${entity.table}(${fixture.id}) - ${relatedEntity.table}(${relatedId}) ID: ${newIds}`));
470
+ }
471
+ }
472
+ }
473
+ }
474
+ async addFixtureLoader(code) {
475
+ const path = Sonamu.apiRootPath + "/src/testing/fixture.ts";
476
+ let content = readFileSync(path).toString();
477
+ const fixtureLoaderStart = content.indexOf("const fixtureLoader = {");
478
+ const fixtureLoaderEnd = content.indexOf("};", fixtureLoaderStart);
479
+ if (fixtureLoaderStart !== -1 && fixtureLoaderEnd !== -1) {
480
+ const newContent = content.slice(0, fixtureLoaderEnd) + " " + code + "\n" + content.slice(fixtureLoaderEnd);
481
+ writeFileSync(path, newContent);
482
+ } else {
483
+ throw new Error("Failed to find fixtureLoader in fixture.ts");
484
+ }
485
+ }
486
+ // 해당 픽스쳐의 값으로 유니크 제약에 위배되는 레코드가 있는지 확인
487
+ async checkUniqueViolation(db, entity, fixture) {
488
+ const _uniqueIndexes = entity.indexes.filter((i)=>i.type === "unique");
489
+ // ManyToMany 관계 테이블의 유니크 제약은 제외
490
+ const uniqueIndexes = _uniqueIndexes.filter((index)=>index.columns.every((column)=>!column.startsWith(`${entity.table}__`)));
491
+ if (uniqueIndexes.length === 0) {
492
+ return null;
493
+ }
494
+ let uniqueQuery = db(entity.table);
495
+ for (const index of uniqueIndexes){
496
+ // 컬럼 중 하나라도 null이면 유니크 제약을 위반하지 않기 때문에 해당 인덱스는 무시
497
+ const containsNull = index.columns.some((column)=>{
498
+ const field = column.split("_id")[0];
499
+ return fixture.columns[field].value === null;
500
+ });
501
+ if (containsNull) {
502
+ continue;
503
+ }
504
+ uniqueQuery = uniqueQuery.orWhere((qb)=>{
505
+ for (const column of index.columns){
506
+ const field = column.split("_id")[0];
507
+ if (Array.isArray(fixture.columns[field].value)) {
508
+ qb.whereIn(column, fixture.columns[field].value);
509
+ } else {
510
+ qb.andWhere(column, fixture.columns[field].value);
511
+ }
512
+ }
513
+ });
514
+ }
515
+ const [uniqueFound] = await uniqueQuery;
516
+ return uniqueFound;
517
+ }
518
+ }
519
+ export const FixtureManager = new FixtureManagerClass();
520
+
521
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0aW5nL2ZpeHR1cmUtbWFuYWdlci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgY2hhbGsgZnJvbSBcImNoYWxrXCI7XG5pbXBvcnQgKiBhcyBfIGZyb20gXCJsb2Rhc2gtZXNcIjtcbmltcG9ydCB7IFNvbmFtdSB9IGZyb20gXCIuLi9hcGlcIjtcbmltcG9ydCB7IEVudGl0eU1hbmFnZXIgfSBmcm9tIFwiLi4vZW50aXR5L2VudGl0eS1tYW5hZ2VyXCI7XG5pbXBvcnQge1xuICBFbnRpdHlQcm9wLFxuICBGaXh0dXJlSW1wb3J0UmVzdWx0LFxuICBGaXh0dXJlUmVjb3JkLFxuICBGaXh0dXJlU2VhcmNoT3B0aW9ucyxcbiAgTWFueVRvTWFueVJlbGF0aW9uUHJvcCxcbiAgaXNCZWxvbmdzVG9PbmVSZWxhdGlvblByb3AsXG4gIGlzSGFzTWFueVJlbGF0aW9uUHJvcCxcbiAgaXNNYW55VG9NYW55UmVsYXRpb25Qcm9wLFxuICBpc09uZVRvT25lUmVsYXRpb25Qcm9wLFxuICBpc1JlbGF0aW9uUHJvcCxcbiAgaXNWaXJ0dWFsUHJvcCxcbn0gZnJvbSBcIi4uL3R5cGVzL3R5cGVzXCI7XG5pbXBvcnQgeyBFbnRpdHkgfSBmcm9tIFwiLi4vZW50aXR5L2VudGl0eVwiO1xuaW1wb3J0IGluZmxlY3Rpb24gZnJvbSBcImluZmxlY3Rpb25cIjtcbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gXCJmc1wiO1xuaW1wb3J0IHsgUmVsYXRpb25HcmFwaCB9IGZyb20gXCIuL19yZWxhdGlvbi1ncmFwaFwiO1xuaW1wb3J0IGtuZXgsIHsgS25leCB9IGZyb20gXCJrbmV4XCI7XG5pbXBvcnQgeyBCYXNlTW9kZWwgfSBmcm9tIFwiLi4vZGF0YWJhc2UvYmFzZS1tb2RlbFwiO1xuaW1wb3J0IHsgU29uYW11REJDb25maWcgfSBmcm9tIFwiLi4vZGF0YWJhc2UvZGJcIjtcblxuZXhwb3J0IGNsYXNzIEZpeHR1cmVNYW5hZ2VyQ2xhc3Mge1xuICBwcml2YXRlIF90ZGI6IEtuZXggfCBudWxsID0gbnVsbDtcbiAgc2V0IHRkYih0ZGI6IEtuZXgpIHtcbiAgICB0aGlzLl90ZGIgPSB0ZGI7XG4gIH1cbiAgZ2V0IHRkYigpOiBLbmV4IHtcbiAgICBpZiAodGhpcy5fdGRiID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJGaXh0dXJlTWFuYWdlciBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl90ZGI7XG4gIH1cblxuICBwcml2YXRlIF9mZGI6IEtuZXggfCBudWxsID0gbnVsbDtcbiAgc2V0IGZkYihmZGI6IEtuZXgpIHtcbiAgICB0aGlzLl9mZGIgPSBmZGI7XG4gIH1cbiAgZ2V0IGZkYigpOiBLbmV4IHtcbiAgICBpZiAodGhpcy5fZmRiID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJGaXh0dXJlTWFuYWdlciBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9mZGI7XG4gIH1cbiAgY2FjaGVkVGFibGVOYW1lczogc3RyaW5nW10gfCBudWxsID0gbnVsbDtcblxuICBwcml2YXRlIHJlbGF0aW9uR3JhcGggPSBuZXcgUmVsYXRpb25HcmFwaCgpO1xuXG4gIGluaXQoKSB7XG4gICAgaWYgKHRoaXMuX3RkYiAhPT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoU29uYW11LmRiQ29uZmlnLnRlc3QgJiYgU29uYW11LmRiQ29uZmlnLnByb2R1Y3Rpb25fbWFzdGVyKSB7XG4gICAgICBjb25zdCB0Q29ubiA9IFNvbmFtdS5kYkNvbmZpZy50ZXN0LmNvbm5lY3Rpb24gYXMgS25leC5Db25uZWN0aW9uQ29uZmlnICYge1xuICAgICAgICBwb3J0PzogbnVtYmVyO1xuICAgICAgfTtcbiAgICAgIGNvbnN0IHBDb25uID0gU29uYW11LmRiQ29uZmlnLnByb2R1Y3Rpb25fbWFzdGVyXG4gICAgICAgIC5jb25uZWN0aW9uIGFzIEtuZXguQ29ubmVjdGlvbkNvbmZpZyAmIHsgcG9ydD86IG51bWJlciB9O1xuICAgICAgaWYgKFxuICAgICAgICBgJHt0Q29ubi5ob3N0ID8/IFwibG9jYWxob3N0XCJ9OiR7dENvbm4ucG9ydCA/PyAzMzA2fS8ke1xuICAgICAgICAgIHRDb25uLmRhdGFiYXNlXG4gICAgICAgIH1gID09PVxuICAgICAgICBgJHtwQ29ubi5ob3N0ID8/IFwibG9jYWxob3N0XCJ9OiR7cENvbm4ucG9ydCA/PyAzMzA2fS8ke3BDb25uLmRhdGFiYXNlfWBcbiAgICAgICkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYO2FjOyKpO2KuERC7JmAIO2UhOuhnOuNleyFmERC7JeQIOuPmeydvO2VnCDrjbDsnbTthLDrsqDsnbTsiqTqsIAg7IKs7Jqp65CY7JeI7Iq164uI64ukLmBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLnRkYiA9IGtuZXgoU29uYW11LmRiQ29uZmlnLnRlc3QpO1xuICAgIHRoaXMuZmRiID0ga25leChTb25hbXUuZGJDb25maWcuZml4dHVyZV9sb2NhbCk7XG4gIH1cblxuICBhc3luYyBjbGVhbkFuZFNlZWQodXNpbmdUYWJsZXM/OiBzdHJpbmdbXSkge1xuICAgIGNvbnN0IHRhYmxlTmFtZXM6IHN0cmluZ1tdID0gYXdhaXQgKGFzeW5jICgpID0+IHtcbiAgICAgIGlmICh1c2luZ1RhYmxlcykge1xuICAgICAgICByZXR1cm4gdXNpbmdUYWJsZXM7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5jYWNoZWRUYWJsZU5hbWVzKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNhY2hlZFRhYmxlTmFtZXM7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IFt0YWJsZXNdID0gYXdhaXQgdGhpcy50ZGIucmF3KFxuICAgICAgICBgU0hPVyBUQUJMRSBTVEFUVVMgV0hFUkUgRW5naW5lIElTIE5PVCBOVUxMIEFORCBOYW1lICE9ICdtaWdyYXRpb25zJ2BcbiAgICAgICk7XG4gICAgICBjb25zdCB0YWJsZU5hbWVzID0gdGFibGVzLm1hcChcbiAgICAgICAgKHRhYmxlSW5mbzogeyBOYW1lOiBzdHJpbmcgfSkgPT4gdGFibGVJbmZvW1wiTmFtZVwiXVxuICAgICAgKTtcbiAgICAgIHRoaXMuY2FjaGVkVGFibGVOYW1lcyA9IHRhYmxlTmFtZXM7XG4gICAgICByZXR1cm4gdGFibGVOYW1lcztcbiAgICB9KSgpO1xuXG4gICAgLy8gbWlncmF0aW9ucyDsoJzsmbjtlZwg7YWM7J2067iUIOuqqeuhnVxuICAgIGNvbnN0IHRhYmxlTGlzdFN0ciA9IHRhYmxlTmFtZXMuam9pbihcIiwgXCIpO1xuXG4gICAgLy8g7ZWcIOuyiOyXkCDrqqjrk6Ag7YWM7J2067iUIOyytO2BrOyErCDtmZXsnbhcbiAgICBjb25zdCBbZmRiQ2hlY2tzdW1Sb3dzXSA9IGF3YWl0IHRoaXMuZmRiLnJhdzxcbiAgICAgIFt7IFRhYmxlOiBzdHJpbmc7IENoZWNrc3VtOiBudW1iZXIgfVtdXVxuICAgID4oYENIRUNLU1VNIFRBQkxFICR7dGFibGVMaXN0U3RyfWApO1xuICAgIGNvbnN0IFt0ZGJDaGVja3N1bVJvd3NdID0gYXdhaXQgdGhpcy50ZGIucmF3PFxuICAgICAgW3sgVGFibGU6IHN0cmluZzsgQ2hlY2tzdW06IG51bWJlciB9W11dXG4gICAgPihgQ0hFQ0tTVU0gVEFCTEUgJHt0YWJsZUxpc3RTdHJ9YCk7XG5cbiAgICAvLyDssrTtgazshKwg66e1IOyDneyEsVxuICAgIGNvbnN0IGZkYkNoZWNrc3VtcyA9IG5ldyBNYXAoXG4gICAgICBmZGJDaGVja3N1bVJvd3MubWFwKChyb3cpID0+IFtyb3cuVGFibGUuc3BsaXQoXCIuXCIpLnBvcCgpISwgcm93LkNoZWNrc3VtXSlcbiAgICApO1xuICAgIGNvbnN0IHRkYkNoZWNrc3VtcyA9IG5ldyBNYXAoXG4gICAgICB0ZGJDaGVja3N1bVJvd3MubWFwKChyb3cpID0+IFtyb3cuVGFibGUuc3BsaXQoXCIuXCIpLnBvcCgpISwgcm93LkNoZWNrc3VtXSlcbiAgICApO1xuXG4gICAgLy8g67OA6rK965CcIO2FjOydtOu4lOuTpOunjCDsspjrpqxcbiAgICBjb25zdCBjaGFuZ2VkVGFibGVzID0gdGFibGVOYW1lcy5maWx0ZXIoXG4gICAgICAodGFibGVOYW1lKSA9PiBmZGJDaGVja3N1bXMuZ2V0KHRhYmxlTmFtZSkgIT09IHRkYkNoZWNrc3Vtcy5nZXQodGFibGVOYW1lKVxuICAgICk7XG5cbiAgICAvLyDrs5HroKzroZwgdHJ1bmNhdGUgKyBpbnNlcnQg7Iuk7ZaJXG4gICAgYXdhaXQgdGhpcy50ZGIudHJhbnNhY3Rpb24oYXN5bmMgKHRyeCkgPT4ge1xuICAgICAgYXdhaXQgdHJ4LnJhdyhgU0VUIEZPUkVJR05fS0VZX0NIRUNLUyA9IDBgKTtcblxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIGNoYW5nZWRUYWJsZXMubWFwKGFzeW5jICh0YWJsZU5hbWUpID0+IHtcbiAgICAgICAgICBhd2FpdCB0cngucmF3KGBTRVQgRk9SRUlHTl9LRVlfQ0hFQ0tTID0gMGApO1xuICAgICAgICAgIGF3YWl0IHRyeCh0YWJsZU5hbWUpLnRydW5jYXRlKCk7XG4gICAgICAgICAgY29uc3QgcmF3UXVlcnkgPSBgSU5TRVJUIElOVE8gJHtcbiAgICAgICAgICAgIChTb25hbXUuZGJDb25maWcudGVzdC5jb25uZWN0aW9uIGFzIEtuZXguQ29ubmVjdGlvbkNvbmZpZykuZGF0YWJhc2VcbiAgICAgICAgICB9LiR7dGFibGVOYW1lfVxuICAgICAgICBTRUxFQ1QgKiBGUk9NICR7XG4gICAgICAgICAgKFNvbmFtdS5kYkNvbmZpZy5maXh0dXJlX2xvY2FsLmNvbm5lY3Rpb24gYXMgS25leC5Db25uZWN0aW9uQ29uZmlnKVxuICAgICAgICAgICAgLmRhdGFiYXNlXG4gICAgICAgIH0uJHt0YWJsZU5hbWV9YDtcbiAgICAgICAgICBhd2FpdCB0cngucmF3KHJhd1F1ZXJ5KTtcbiAgICAgICAgfSlcbiAgICAgICk7XG4gICAgICBhd2FpdCB0cngucmF3KGBTRVQgRk9SRUlHTl9LRVlfQ0hFQ0tTID0gMWApO1xuICAgIH0pO1xuXG4gICAgLy8gY29uc29sZS50aW1lRW5kKFwiRklYVFVSRS1DbGVhbkFuZFNlZWRcIik7XG4gIH1cblxuICBhc3luYyBnZXRDaGVja3N1bShkYjogS25leCwgdGFibGVOYW1lOiBzdHJpbmcpIHtcbiAgICBjb25zdCBbW2NoZWNrc3VtUm93XV0gPSBhd2FpdCBkYi5yYXcoYENIRUNLU1VNIFRBQkxFICR7dGFibGVOYW1lfWApO1xuICAgIHJldHVybiBjaGVja3N1bVJvdy5DaGVja3N1bTtcbiAgfVxuXG4gIGFzeW5jIHN5bmMoKSB7XG4gICAgY29uc3QgZnJkYiA9IGtuZXgoU29uYW11LmRiQ29uZmlnLmZpeHR1cmVfcmVtb3RlKTtcblxuICAgIGNvbnN0IFt0YWJsZXNdID0gYXdhaXQgdGhpcy5mZGIucmF3KFxuICAgICAgXCJTSE9XIFRBQkxFIFNUQVRVUyBXSEVSRSBFbmdpbmUgSVMgTk9UIE5VTExcIlxuICAgICk7XG4gICAgY29uc3QgdGFibGVOYW1lczogc3RyaW5nW10gPSB0YWJsZXMubWFwKFxuICAgICAgKHRhYmxlOiBhbnkpID0+IHRhYmxlLk5hbWUgYXMgc3RyaW5nXG4gICAgKTtcblxuICAgIGNvbnNvbGUubG9nKGNoYWxrLm1hZ2VudGEoXCJTWU5DLi4uXCIpKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgIHRhYmxlTmFtZXMubWFwKGFzeW5jICh0YWJsZU5hbWUpID0+IHtcbiAgICAgICAgaWYgKHRhYmxlTmFtZS5zdGFydHNXaXRoKFwia25leF9taWdyYXRpb25zXCIpKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgcmVtb3RlQ2hlY2tzdW0gPSBhd2FpdCB0aGlzLmdldENoZWNrc3VtKGZyZGIsIHRhYmxlTmFtZSk7XG4gICAgICAgIGNvbnN0IGxvY2FsQ2hlY2tzdW0gPSBhd2FpdCB0aGlzLmdldENoZWNrc3VtKHRoaXMuZmRiLCB0YWJsZU5hbWUpO1xuXG4gICAgICAgIGlmIChyZW1vdGVDaGVja3N1bSAhPT0gbG9jYWxDaGVja3N1bSkge1xuICAgICAgICAgIGF3YWl0IHRoaXMuZmRiLnRyYW5zYWN0aW9uKGFzeW5jICh0cmFuc2FjdGlvbikgPT4ge1xuICAgICAgICAgICAgYXdhaXQgdHJhbnNhY3Rpb24ucmF3KGBTRVQgRk9SRUlHTl9LRVlfQ0hFQ0tTID0gMGApO1xuICAgICAgICAgICAgYXdhaXQgdHJhbnNhY3Rpb24odGFibGVOYW1lKS50cnVuY2F0ZSgpO1xuXG4gICAgICAgICAgICBjb25zdCByb3dzID0gYXdhaXQgZnJkYih0YWJsZU5hbWUpO1xuICAgICAgICAgICAgaWYgKHJvd3MubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc29sZS5sb2coY2hhbGsuYmx1ZSh0YWJsZU5hbWUpLCByb3dzLmxlbmd0aCk7XG4gICAgICAgICAgICBhd2FpdCB0cmFuc2FjdGlvblxuICAgICAgICAgICAgICAuaW5zZXJ0KFxuICAgICAgICAgICAgICAgIHJvd3MubWFwKChyb3c6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgICAgICAgICAgICAgICAgT2JqZWN0LmVudHJpZXMocm93KS5tYXAoKFtrZXksIHZhbHVlXSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgIGlmICh2YWx1ZSA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtrZXksIG51bGxdO1xuICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSBcImJvb2xlYW5cIikge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtrZXksIHZhbHVlID8gMSA6IDBdO1xuICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09IFwib2JqZWN0XCIgJiZcbiAgICAgICAgICAgICAgICAgICAgICAgICEodmFsdWUgaW5zdGFuY2VvZiBEYXRlKVxuICAgICAgICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtrZXksIEpTT04uc3RyaW5naWZ5KHZhbHVlKV07XG4gICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBba2V5LCB2YWx1ZV07XG4gICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIC5pbnRvKHRhYmxlTmFtZSk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcIk9LXCIpO1xuICAgICAgICAgICAgYXdhaXQgdHJhbnNhY3Rpb24ucmF3KGBTRVQgRk9SRUlHTl9LRVlfQ0hFQ0tTID0gMWApO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICk7XG4gICAgY29uc29sZS5sb2coY2hhbGsubWFnZW50YShcIkRPTkUhXCIpKTtcblxuICAgIGF3YWl0IGZyZGIuZGVzdHJveSgpO1xuICB9XG5cbiAgcHJpdmF0ZSB2aXNpdGVkUmVjb3JkcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICBhc3luYyBpbXBvcnRGaXh0dXJlKGVudGl0eUlkOiBzdHJpbmcsIGlkczogbnVtYmVyW10pIHtcbiAgICAvLyDrsKnrrLgg6riw66GdIOy0iOq4sO2ZlCAo7IOI66Gc7Jq0IGltcG9ydCDsnpHsl4Ug7Iuc7J6RKVxuICAgIHRoaXMudmlzaXRlZFJlY29yZHMuY2xlYXIoKTtcblxuICAgIGNvbnN0IHF1ZXJpZXMgPSBfLnVuaXEoXG4gICAgICAoXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICAgIGlkcy5tYXAoYXN5bmMgKGlkKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRJbXBvcnRRdWVyaWVzKGVudGl0eUlkLCBcImlkXCIsIGlkKTtcbiAgICAgICAgICB9KVxuICAgICAgICApXG4gICAgICApLmZsYXQoKVxuICAgICk7XG5cbiAgICBjb25zdCB3ZGIgPSBCYXNlTW9kZWwuZ2V0REIoXCJ3XCIpO1xuICAgIGZvciAobGV0IHF1ZXJ5IG9mIHF1ZXJpZXMpIHtcbiAgICAgIGNvbnN0IFtyc2hdID0gYXdhaXQgd2RiLnJhdyhxdWVyeSk7XG4gICAgICBjb25zb2xlLmxvZyh7XG4gICAgICAgIHF1ZXJ5LFxuICAgICAgICBpbmZvOiByc2guaW5mbyxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGdldEltcG9ydFF1ZXJpZXMoXG4gICAgZW50aXR5SWQ6IHN0cmluZyxcbiAgICBmaWVsZDogc3RyaW5nLFxuICAgIGlkOiBudW1iZXJcbiAgKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICAgIGNvbnN0IHJlY29yZEtleSA9IGAke2VudGl0eUlkfSMke2ZpZWxkfSMke2lkfWA7XG5cbiAgICAvLyDsiJztmZgg7LC47KGwIOuwqeyngDog7J2066+4IOuwqeusuO2VnCDroIjsvZTrk5zripQg7Iqk7YK1XG4gICAgaWYgKHRoaXMudmlzaXRlZFJlY29yZHMuaGFzKHJlY29yZEtleSkpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gICAgdGhpcy52aXNpdGVkUmVjb3Jkcy5hZGQocmVjb3JkS2V5KTtcblxuICAgIGNvbnNvbGUubG9nKHsgZW50aXR5SWQsIGZpZWxkLCBpZCB9KTtcbiAgICBjb25zdCBlbnRpdHkgPSBFbnRpdHlNYW5hZ2VyLmdldChlbnRpdHlJZCk7XG4gICAgY29uc3Qgd2RiID0gQmFzZU1vZGVsLmdldERCKFwid1wiKTtcblxuICAgIC8vIOyXrOq4sOyEnCDsi6REQuydmCByb3cg6rCA7KC47Ji0XG4gICAgY29uc3QgW3Jvd10gPSBhd2FpdCB3ZGIoZW50aXR5LnRhYmxlKS53aGVyZShmaWVsZCwgaWQpLmxpbWl0KDEpO1xuICAgIGlmIChyb3cgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGAke2VudGl0eUlkfSMke2lkfSByb3frpbwg7LC+7J2EIOyImCDsl4bsirXri4jri6QuYCk7XG4gICAgfVxuXG4gICAgLy8g7ZS97Iqk7LOQREIsIOyLpERCXG4gICAgY29uc3QgZml4dHVyZURhdGFiYXNlID0gKFNvbmFtdS5kYkNvbmZpZy5maXh0dXJlX3JlbW90ZS5jb25uZWN0aW9uIGFzIGFueSlcbiAgICAgIC5kYXRhYmFzZTtcbiAgICBjb25zdCByZWFsRGF0YWJhc2UgPSAoU29uYW11LmRiQ29uZmlnLnByb2R1Y3Rpb25fbWFzdGVyLmNvbm5lY3Rpb24gYXMgYW55KVxuICAgICAgLmRhdGFiYXNlO1xuXG4gICAgY29uc3Qgc2VsZlF1ZXJ5ID0gYElOU0VSVCBJR05PUkUgSU5UTyBcXGAke2ZpeHR1cmVEYXRhYmFzZX1cXGAuXFxgJHtlbnRpdHkudGFibGV9XFxgIChTRUxFQ1QgKiBGUk9NIFxcYCR7cmVhbERhdGFiYXNlfVxcYC5cXGAke2VudGl0eS50YWJsZX1cXGAgV0hFUkUgXFxgaWRcXGAgPSAke2lkfSlgO1xuXG4gICAgY29uc3QgYXJncyA9IE9iamVjdC5lbnRyaWVzKGVudGl0eS5yZWxhdGlvbnMpXG4gICAgICAuZmlsdGVyKFxuICAgICAgICAoWywgcmVsYXRpb25dKSA9PlxuICAgICAgICAgIGlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wKHJlbGF0aW9uKSB8fFxuICAgICAgICAgIChpc09uZVRvT25lUmVsYXRpb25Qcm9wKHJlbGF0aW9uKSAmJlxuICAgICAgICAgICAgcmVsYXRpb24uY3VzdG9tSm9pbkNsYXVzZSA9PT0gdW5kZWZpbmVkKVxuICAgICAgKVxuICAgICAgLm1hcCgoWywgcmVsYXRpb25dKSA9PiB7XG4gICAgICAgIC8qXG4gICAgICAgIEJlbG9uZ3NUb09uZeyduCDqsr3smrBcbiAgICAgICAgICBDYXRlZ29yeSAvICdpZCcgLyByb3dbY2F0ZWdvcnlfaWRdIO2YuOy2nFxuICAgICAgICBPbmVUb09uZeyXkCBqb2luQ29sdW1uID09PSB0cnVlIOyduCDqsr3smrBcbiAgICAgICAgICBQcm9maWxlIC8gJ2lkJyAvIHJvd1twcm9maWxlX2lkXSDtmLjstpxcbiAgICAgICAgT25lVG9PbmXsl5Agam9pbkNvbHVtbiA9PT0gZmFsc2Ug7J24IOqyveyasFxuICAgICAgICAgIFByb2ZpbGUgLyAncHJvZmlsZV9pZCcgLyByb3dbJ2lkJ10g7Zi47LacXG4gICAgICAgICovXG4gICAgICAgIGxldCBmaWVsZDogc3RyaW5nO1xuICAgICAgICBsZXQgaWQ6IG51bWJlcjtcbiAgICAgICAgaWYgKGlzT25lVG9PbmVSZWxhdGlvblByb3AocmVsYXRpb24pICYmICFyZWxhdGlvbi5oYXNKb2luQ29sdW1uKSB7XG4gICAgICAgICAgY29uc3QgcmVsYXRlZEVudGl0eSA9IEVudGl0eU1hbmFnZXIuZ2V0KHJlbGF0aW9uLndpdGgpO1xuICAgICAgICAgIGNvbnN0IHJlbGF0ZWRJZENvbHVtbk5hbWUgPSByZWxhdGVkRW50aXR5LnByb3BzLmZpbmQoXG4gICAgICAgICAgICAocCkgPT4gaXNSZWxhdGlvblByb3AocCkgJiYgcC53aXRoID09PSBlbnRpdHkuaWRcbiAgICAgICAgICApPy5uYW1lO1xuICAgICAgICAgIGlmICghcmVsYXRlZElkQ29sdW1uTmFtZSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICBgJHtyZWxhdGVkRW50aXR5LmlkfeydmCAke2VudGl0eS5pZH0g6rSA6rOEIO2UhOuhreydhCDssL7snYQg7IiYIOyXhuyKteuLiOuLpC5gXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgICBmaWVsZCA9IGAke3JlbGF0ZWRJZENvbHVtbk5hbWV9X2lkYDtcbiAgICAgICAgICBpZCA9IHJvd1tcImlkXCJdO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGZpZWxkID0gXCJpZFwiO1xuICAgICAgICAgIGlkID0gcm93W2Ake3JlbGF0aW9uLm5hbWV9X2lkYF07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBlbnRpdHlJZDogcmVsYXRpb24ud2l0aCxcbiAgICAgICAgICBmaWVsZCxcbiAgICAgICAgICBpZCxcbiAgICAgICAgfTtcbiAgICAgIH0pXG4gICAgICAuZmlsdGVyKChhcmcpID0+IGFyZy5pZCAhPT0gbnVsbCk7XG5cbiAgICBjb25zdCByZWxRdWVyaWVzID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICBhcmdzLm1hcChhc3luYyAoYXJncykgPT4ge1xuICAgICAgICByZXR1cm4gdGhpcy5nZXRJbXBvcnRRdWVyaWVzKGFyZ3MuZW50aXR5SWQsIGFyZ3MuZmllbGQsIGFyZ3MuaWQpO1xuICAgICAgfSlcbiAgICApO1xuXG4gICAgcmV0dXJuIFsuLi5fLnVuaXEocmVsUXVlcmllcy5yZXZlcnNlKCkuZmxhdCgpKSwgc2VsZlF1ZXJ5XTtcbiAgfVxuXG4gIGFzeW5jIGRlc3Ryb3koKSB7XG4gICAgaWYgKHRoaXMuX3RkYikge1xuICAgICAgYXdhaXQgdGhpcy5fdGRiLmRlc3Ryb3koKTtcbiAgICAgIHRoaXMuX3RkYiA9IG51bGw7XG4gICAgfVxuICAgIGlmICh0aGlzLl9mZGIpIHtcbiAgICAgIGF3YWl0IHRoaXMuX2ZkYi5kZXN0cm95KCk7XG4gICAgICB0aGlzLl9mZGIgPSBudWxsO1xuICAgIH1cbiAgICBhd2FpdCBCYXNlTW9kZWwuZGVzdHJveSgpO1xuICB9XG5cbiAgYXN5bmMgZ2V0Rml4dHVyZXMoXG4gICAgc291cmNlREJOYW1lOiBrZXlvZiBTb25hbXVEQkNvbmZpZyxcbiAgICB0YXJnZXREQk5hbWU6IGtleW9mIFNvbmFtdURCQ29uZmlnLFxuICAgIHNlYXJjaE9wdGlvbnM6IEZpeHR1cmVTZWFyY2hPcHRpb25zXG4gICkge1xuICAgIGNvbnN0IHNvdXJjZURCID0ga25leChTb25hbXUuZGJDb25maWdbc291cmNlREJOYW1lXSk7XG4gICAgY29uc3QgdGFyZ2V0REIgPSBrbmV4KFNvbmFtdS5kYkNvbmZpZ1t0YXJnZXREQk5hbWVdKTtcbiAgICBjb25zdCB7IGVudGl0eUlkLCBmaWVsZCwgdmFsdWUsIHNlYXJjaFR5cGUgfSA9IHNlYXJjaE9wdGlvbnM7XG5cbiAgICBjb25zdCBlbnRpdHkgPSBFbnRpdHlNYW5hZ2VyLmdldChlbnRpdHlJZCk7XG4gICAgY29uc3QgY29sdW1uID1cbiAgICAgIGVudGl0eS5wcm9wcy5maW5kKChwcm9wKSA9PiBwcm9wLm5hbWUgPT09IGZpZWxkKT8udHlwZSA9PT0gXCJyZWxhdGlvblwiXG4gICAgICAgID8gYCR7ZmllbGR9X2lkYFxuICAgICAgICA6IGZpZWxkO1xuXG4gICAgbGV0IHF1ZXJ5ID0gc291cmNlREIoZW50aXR5LnRhYmxlKTtcbiAgICBpZiAoc2VhcmNoVHlwZSA9PT0gXCJlcXVhbHNcIikge1xuICAgICAgcXVlcnkgPSBxdWVyeS53aGVyZShjb2x1bW4sIHZhbHVlKTtcbiAgICB9IGVsc2UgaWYgKHNlYXJjaFR5cGUgPT09IFwibGlrZVwiKSB7XG4gICAgICBxdWVyeSA9IHF1ZXJ5LndoZXJlKGNvbHVtbiwgXCJsaWtlXCIsIGAlJHt2YWx1ZX0lYCk7XG4gICAgfVxuXG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHF1ZXJ5O1xuICAgIGlmIChyb3dzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiTm8gcmVjb3JkcyBmb3VuZFwiKTtcbiAgICB9XG5cbiAgICBjb25zdCBmaXh0dXJlczogRml4dHVyZVJlY29yZFtdID0gW107XG4gICAgZm9yIChjb25zdCByb3cgb2Ygcm93cykge1xuICAgICAgY29uc3QgaW5pdGlhbFJlY29yZHNMZW5ndGggPSBmaXh0dXJlcy5sZW5ndGg7XG4gICAgICBjb25zdCBuZXdSZWNvcmRzID0gYXdhaXQgdGhpcy5jcmVhdGVGaXh0dXJlUmVjb3JkKGVudGl0eSwgcm93KTtcbiAgICAgIGZpeHR1cmVzLnB1c2goLi4ubmV3UmVjb3Jkcyk7XG4gICAgICBjb25zdCBjdXJyZW50Rml4dHVyZVJlY29yZCA9IGZpeHR1cmVzLmZpbmQoXG4gICAgICAgIChyKSA9PiByLmZpeHR1cmVJZCA9PT0gYCR7ZW50aXR5SWR9IyR7cm93LmlkfWBcbiAgICAgICk7XG5cbiAgICAgIGlmIChjdXJyZW50Rml4dHVyZVJlY29yZCkge1xuICAgICAgICAvLyDtmITsnqwgZml4dHVyZeuhnOu2gO2EsCDsg53shLHrkJwgZmV0Y2hlZFJlY29yZHMg7ISk7KCVXG4gICAgICAgIGN1cnJlbnRGaXh0dXJlUmVjb3JkLmZldGNoZWRSZWNvcmRzID0gZml4dHVyZXNcbiAgICAgICAgICAuZmlsdGVyKChyKSA9PiByLmZpeHR1cmVJZCAhPT0gY3VycmVudEZpeHR1cmVSZWNvcmQuZml4dHVyZUlkKVxuICAgICAgICAgIC5zbGljZShpbml0aWFsUmVjb3Jkc0xlbmd0aClcbiAgICAgICAgICAubWFwKChyKSA9PiByLmZpeHR1cmVJZCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgZm9yIGF3YWl0IChjb25zdCBmaXh0dXJlIG9mIGZpeHR1cmVzKSB7XG4gICAgICBjb25zdCBlbnRpdHkgPSBFbnRpdHlNYW5hZ2VyLmdldChmaXh0dXJlLmVudGl0eUlkKTtcblxuICAgICAgLy8gSUTrpbwg7J207Jqp7ZWY7JesIHRhcmdldERC7JeQIOugiOy9lOuTnOqwgCDsobTsnqztlZjripTsp4Ag7ZmV7J24XG4gICAgICBjb25zdCByb3cgPSBhd2FpdCB0YXJnZXREQihlbnRpdHkudGFibGUpLndoZXJlKFwiaWRcIiwgZml4dHVyZS5pZCkuZmlyc3QoKTtcbiAgICAgIGlmIChyb3cpIHtcbiAgICAgICAgY29uc3QgW3JlY29yZF0gPSBhd2FpdCB0aGlzLmNyZWF0ZUZpeHR1cmVSZWNvcmQoZW50aXR5LCByb3csIHtcbiAgICAgICAgICBzaW5nbGVSZWNvcmQ6IHRydWUsXG4gICAgICAgICAgX2RiOiB0YXJnZXREQixcbiAgICAgICAgfSk7XG4gICAgICAgIGZpeHR1cmUudGFyZ2V0ID0gcmVjb3JkO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gSUTrpbwg7J207Jqp7ZWY7JesIHRhcmdldERC7JeQ7IScIOyhsO2ajOuQmOyngCDslYrripQg6rK97JqwLCB1bmlxdWUg7KCc7JW97J2EIOychOuwmO2VmOuKlOyngCDtmZXsnbhcbiAgICAgIGNvbnN0IHVuaXF1ZVJvdyA9IGF3YWl0IHRoaXMuY2hlY2tVbmlxdWVWaW9sYXRpb24oXG4gICAgICAgIHRhcmdldERCLFxuICAgICAgICBlbnRpdHksXG4gICAgICAgIGZpeHR1cmVcbiAgICAgICk7XG4gICAgICBpZiAodW5pcXVlUm93KSB7XG4gICAgICAgIGNvbnN0IFtyZWNvcmRdID0gYXdhaXQgdGhpcy5jcmVhdGVGaXh0dXJlUmVjb3JkKGVudGl0eSwgdW5pcXVlUm93LCB7XG4gICAgICAgICAgc2luZ2xlUmVjb3JkOiB0cnVlLFxuICAgICAgICAgIF9kYjogdGFyZ2V0REIsXG4gICAgICAgIH0pO1xuICAgICAgICBmaXh0dXJlLnVuaXF1ZSA9IHJlY29yZDtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBhd2FpdCB0YXJnZXREQi5kZXN0cm95KCk7XG4gICAgYXdhaXQgc291cmNlREIuZGVzdHJveSgpO1xuXG4gICAgcmV0dXJuIF8udW5pcUJ5KGZpeHR1cmVzLCAoZikgPT4gZi5maXh0dXJlSWQpO1xuICB9XG5cbiAgYXN5bmMgY3JlYXRlRml4dHVyZVJlY29yZChcbiAgICBlbnRpdHk6IEVudGl0eSxcbiAgICByb3c6IGFueSxcbiAgICBvcHRpb25zPzoge1xuICAgICAgc2luZ2xlUmVjb3JkPzogYm9vbGVhbjtcbiAgICAgIF9kYj86IEtuZXg7XG4gICAgfVxuICApOiBQcm9taXNlPEZpeHR1cmVSZWNvcmRbXT4ge1xuICAgIGNvbnN0IHJlY29yZHM6IEZpeHR1cmVSZWNvcmRbXSA9IFtdO1xuICAgIGNvbnN0IHZpc2l0ZWRFbnRpdGllcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuXG4gICAgY29uc3QgY3JlYXRlID0gYXN5bmMgKGVudGl0eTogRW50aXR5LCByb3c6IGFueSkgPT4ge1xuICAgICAgY29uc3QgZml4dHVyZUlkID0gYCR7ZW50aXR5LmlkfSMke3Jvdy5pZH1gO1xuICAgICAgaWYgKHZpc2l0ZWRFbnRpdGllcy5oYXMoZml4dHVyZUlkKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB2aXNpdGVkRW50aXRpZXMuYWRkKGZpeHR1cmVJZCk7XG5cbiAgICAgIGNvbnN0IHJlY29yZDogRml4dHVyZVJlY29yZCA9IHtcbiAgICAgICAgZml4dHVyZUlkLFxuICAgICAgICBlbnRpdHlJZDogZW50aXR5LmlkLFxuICAgICAgICBpZDogcm93LmlkLFxuICAgICAgICBjb2x1bW5zOiB7fSxcbiAgICAgICAgZmV0Y2hlZFJlY29yZHM6IFtdLFxuICAgICAgICBiZWxvbmdzUmVjb3JkczogW10sXG4gICAgICB9O1xuXG4gICAgICBmb3IgKGNvbnN0IHByb3Agb2YgZW50aXR5LnByb3BzKSB7XG4gICAgICAgIGlmIChpc1ZpcnR1YWxQcm9wKHByb3ApKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICByZWNvcmQuY29sdW1uc1twcm9wLm5hbWVdID0ge1xuICAgICAgICAgIHByb3A6IHByb3AsXG4gICAgICAgICAgdmFsdWU6IHJvd1twcm9wLm5hbWVdLFxuICAgICAgICB9O1xuXG4gICAgICAgIGNvbnN0IGRiID0gb3B0aW9ucz8uX2RiID8/IEJhc2VNb2RlbC5nZXREQihcIndcIik7XG4gICAgICAgIGlmIChpc01hbnlUb01hbnlSZWxhdGlvblByb3AocHJvcCkpIHtcbiAgICAgICAgICBjb25zdCByZWxhdGVkRW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQocHJvcC53aXRoKTtcbiAgICAgICAgICBjb25zdCB0aHJvdWdoVGFibGUgPSBwcm9wLmpvaW5UYWJsZTtcbiAgICAgICAgICBjb25zdCBmcm9tQ29sdW1uID0gYCR7aW5mbGVjdGlvbi5zaW5ndWxhcml6ZShlbnRpdHkudGFibGUpfV9pZGA7XG4gICAgICAgICAgY29uc3QgdG9Db2x1bW4gPSBgJHtpbmZsZWN0aW9uLnNpbmd1bGFyaXplKHJlbGF0ZWRFbnRpdHkudGFibGUpfV9pZGA7XG5cbiAgICAgICAgICBjb25zdCByZWxhdGVkSWRzID0gYXdhaXQgZGIodGhyb3VnaFRhYmxlKVxuICAgICAgICAgICAgLndoZXJlKGZyb21Db2x1bW4sIHJvdy5pZClcbiAgICAgICAgICAgIC5wbHVjayh0b0NvbHVtbik7XG4gICAgICAgICAgcmVjb3JkLmNvbHVtbnNbcHJvcC5uYW1lXS52YWx1ZSA9IHJlbGF0ZWRJZHM7XG4gICAgICAgIH0gZWxzZSBpZiAoaXNIYXNNYW55UmVsYXRpb25Qcm9wKHByb3ApKSB7XG4gICAgICAgICAgY29uc3QgcmVsYXRlZEVudGl0eSA9IEVudGl0eU1hbmFnZXIuZ2V0KHByb3Aud2l0aCk7XG4gICAgICAgICAgY29uc3QgcmVsYXRlZElkcyA9IGF3YWl0IGRiKHJlbGF0ZWRFbnRpdHkudGFibGUpXG4gICAgICAgICAgICAud2hlcmUocHJvcC5qb2luQ29sdW1uLCByb3cuaWQpXG4gICAgICAgICAgICAucGx1Y2soXCJpZFwiKTtcbiAgICAgICAgICByZWNvcmQuY29sdW1uc1twcm9wLm5hbWVdLnZhbHVlID0gcmVsYXRlZElkcztcbiAgICAgICAgfSBlbHNlIGlmIChpc09uZVRvT25lUmVsYXRpb25Qcm9wKHByb3ApICYmICFwcm9wLmhhc0pvaW5Db2x1bW4pIHtcbiAgICAgICAgICBjb25zdCByZWxhdGVkRW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQocHJvcC53aXRoKTtcbiAgICAgICAgICBjb25zdCByZWxhdGVkUHJvcCA9IHJlbGF0ZWRFbnRpdHkucHJvcHMuZmluZChcbiAgICAgICAgICAgIChwKSA9PiBpc1JlbGF0aW9uUHJvcChwKSAmJiBwLndpdGggPT09IGVudGl0eS5pZFxuICAgICAgICAgICk7XG4gICAgICAgICAgaWYgKHJlbGF0ZWRQcm9wKSB7XG4gICAgICAgICAgICBjb25zdCByZWxhdGVkUm93ID0gYXdhaXQgZGIocmVsYXRlZEVudGl0eS50YWJsZSlcbiAgICAgICAgICAgICAgLndoZXJlKFwiaWRcIiwgcm93LmlkKVxuICAgICAgICAgICAgICAuZmlyc3QoKTtcbiAgICAgICAgICAgIHJlY29yZC5jb2x1bW5zW3Byb3AubmFtZV0udmFsdWUgPSByZWxhdGVkUm93Py5pZDtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoaXNSZWxhdGlvblByb3AocHJvcCkpIHtcbiAgICAgICAgICBjb25zdCByZWxhdGVkSWQgPSByb3dbYCR7cHJvcC5uYW1lfV9pZGBdO1xuICAgICAgICAgIHJlY29yZC5jb2x1bW5zW3Byb3AubmFtZV0udmFsdWUgPSByZWxhdGVkSWQ7XG4gICAgICAgICAgaWYgKHJlbGF0ZWRJZCkge1xuICAgICAgICAgICAgcmVjb3JkLmJlbG9uZ3NSZWNvcmRzLnB1c2goYCR7cHJvcC53aXRofSMke3JlbGF0ZWRJZH1gKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKCFvcHRpb25zPy5zaW5nbGVSZWNvcmQgJiYgcmVsYXRlZElkKSB7XG4gICAgICAgICAgICBjb25zdCByZWxhdGVkRW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQocHJvcC53aXRoKTtcbiAgICAgICAgICAgIGNvbnN0IHJlbGF0ZWRSb3cgPSBhd2FpdCBkYihyZWxhdGVkRW50aXR5LnRhYmxlKVxuICAgICAgICAgICAgICAud2hlcmUoXCJpZFwiLCByZWxhdGVkSWQpXG4gICAgICAgICAgICAgIC5maXJzdCgpO1xuICAgICAgICAgICAgaWYgKHJlbGF0ZWRSb3cpIHtcbiAgICAgICAgICAgICAgYXdhaXQgY3JlYXRlKHJlbGF0ZWRFbnRpdHksIHJlbGF0ZWRSb3cpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICByZWNvcmRzLnB1c2gocmVjb3JkKTtcbiAgICB9O1xuXG4gICAgYXdhaXQgY3JlYXRlKGVudGl0eSwgcm93KTtcblxuICAgIHJldHVybiByZWNvcmRzO1xuICB9XG5cbiAgYXN5bmMgaW5zZXJ0Rml4dHVyZXMoXG4gICAgZGJOYW1lOiBrZXlvZiBTb25hbXVEQkNvbmZpZyxcbiAgICBfZml4dHVyZXM6IEZpeHR1cmVSZWNvcmRbXVxuICApIHtcbiAgICBjb25zdCBmaXh0dXJlcyA9IF8udW5pcUJ5KF9maXh0dXJlcywgKGYpID0+IGYuZml4dHVyZUlkKTtcblxuICAgIHRoaXMucmVsYXRpb25HcmFwaC5idWlsZEdyYXBoKGZpeHR1cmVzKTtcbiAgICBjb25zdCBpbnNlcnRpb25PcmRlciA9IHRoaXMucmVsYXRpb25HcmFwaC5nZXRJbnNlcnRpb25PcmRlcigpO1xuICAgIGNvbnN0IGRiID0ga25leChTb25hbXUuZGJDb25maWdbZGJOYW1lXSk7XG5cbiAgICBhd2FpdCBkYi50cmFuc2FjdGlvbihhc3luYyAodHJ4KSA9PiB7XG4gICAgICBhd2FpdCB0cngucmF3KGBTRVQgRk9SRUlHTl9LRVlfQ0hFQ0tTID0gMGApO1xuXG4gICAgICBmb3IgKGNvbnN0IGZpeHR1cmVJZCBvZiBpbnNlcnRpb25PcmRlcikge1xuICAgICAgICBjb25zdCBmaXh0dXJlID0gZml4dHVyZXMuZmluZCgoZikgPT4gZi5maXh0dXJlSWQgPT09IGZpeHR1cmVJZCkhO1xuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLmluc2VydEZpeHR1cmUodHJ4IGFzIGFueSwgZml4dHVyZSk7XG4gICAgICAgIGlmIChyZXN1bHQuaWQgIT09IGZpeHR1cmUuaWQpIHtcbiAgICAgICAgICAvLyBJROqwgCDrs4Dqsr3rkJwg6rK97JqwLCDri6TrpbggZml4dHVyZeyXkOyEnCDssLjsobDtlZjripQg6rK97Jqw6rCAIOywvuyVhOyEnCDsiJjsoJVcbiAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgIGNoYWxrLnllbGxvdyhcbiAgICAgICAgICAgICAgYFVuaXF1ZSBjb25zdHJhaW50IHZpb2xhdGlvbjogJHtmaXh0dXJlLmVudGl0eUlkfSMke2ZpeHR1cmUuaWR9IC0+ICR7Zml4dHVyZS5lbnRpdHlJZH0jJHtyZXN1bHQuaWR9YFxuICAgICAgICAgICAgKVxuICAgICAgICAgICk7XG4gICAgICAgICAgZml4dHVyZXMuZm9yRWFjaCgoZikgPT4ge1xuICAgICAgICAgICAgT2JqZWN0LnZhbHVlcyhmLmNvbHVtbnMpLmZvckVhY2goKGNvbHVtbikgPT4ge1xuICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgY29sdW1uLnByb3AudHlwZSA9PT0gXCJyZWxhdGlvblwiICYmXG4gICAgICAgICAgICAgICAgY29sdW1uLnByb3Aud2l0aCA9PT0gcmVzdWx0LmVudGl0eUlkICYmXG4gICAgICAgICAgICAgICAgY29sdW1uLnZhbHVlID09PSBmaXh0dXJlLmlkXG4gICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIGNvbHVtbi52YWx1ZSA9IHJlc3VsdC5pZDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgZml4dHVyZS5pZCA9IHJlc3VsdC5pZDtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBmb3IgKGNvbnN0IGZpeHR1cmVJZCBvZiBpbnNlcnRpb25PcmRlcikge1xuICAgICAgICBjb25zdCBmaXh0dXJlID0gZml4dHVyZXMuZmluZCgoZikgPT4gZi5maXh0dXJlSWQgPT09IGZpeHR1cmVJZCkhO1xuICAgICAgICBhd2FpdCB0aGlzLmhhbmRsZU1hbnlUb01hbnlSZWxhdGlvbnModHJ4IGFzIGFueSwgZml4dHVyZSwgZml4dHVyZXMpO1xuICAgICAgfVxuICAgICAgYXdhaXQgdHJ4LnJhdyhgU0VUIEZPUkVJR05fS0VZX0NIRUNLUyA9IDFgKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IHJlY29yZHM6IEZpeHR1cmVJbXBvcnRSZXN1bHRbXSA9IFtdO1xuXG4gICAgZm9yIGF3YWl0IChjb25zdCByIG9mIGZpeHR1cmVzKSB7XG4gICAgICBjb25zdCBlbnRpdHkgPSBFbnRpdHlNYW5hZ2VyLmdldChyLmVudGl0eUlkKTtcbiAgICAgIGNvbnN0IHJlY29yZCA9IGF3YWl0IGRiKGVudGl0eS50YWJsZSkud2hlcmUoXCJpZFwiLCByLmlkKS5maXJzdCgpO1xuICAgICAgcmVjb3Jkcy5wdXNoKHtcbiAgICAgICAgZW50aXR5SWQ6IHIuZW50aXR5SWQsXG4gICAgICAgIGRhdGE6IHJlY29yZCxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGF3YWl0IGRiLmRlc3Ryb3koKTtcblxuICAgIHJldHVybiBfLnVuaXFCeShyZWNvcmRzLCAocikgPT4gYCR7ci5lbnRpdHlJZH0jJHtyLmRhdGEuaWR9YCk7XG4gIH1cblxuICBwcml2YXRlIHByZXBhcmVJbnNlcnREYXRhKGZpeHR1cmU6IEZpeHR1cmVSZWNvcmQpIHtcbiAgICBjb25zdCBpbnNlcnREYXRhOiBhbnkgPSB7fTtcbiAgICBmb3IgKGNvbnN0IFtwcm9wTmFtZSwgY29sdW1uXSBvZiBPYmplY3QuZW50cmllcyhmaXh0dXJlLmNvbHVtbnMpKSB7XG4gICAgICBpZiAoaXNWaXJ0dWFsUHJvcChjb2x1bW4ucHJvcCkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHByb3AgPSBjb2x1bW4ucHJvcCBhcyBFbnRpdHlQcm9wO1xuICAgICAgaWYgKCFpc1JlbGF0aW9uUHJvcChwcm9wKSkge1xuICAgICAgICBpZiAocHJvcC50eXBlID09PSBcImpzb25cIikge1xuICAgICAgICAgIGluc2VydERhdGFbcHJvcE5hbWVdID0gSlNPTi5zdHJpbmdpZnkoY29sdW1uLnZhbHVlKTtcbiAgICAgICAgfSBlbHNlIGlmIChwcm9wLnR5cGUgPT09IFwidGltZXN0YW1wXCIgfHwgcHJvcC50eXBlID09PSBcImRhdGV0aW1lXCIpIHtcbiAgICAgICAgICBpbnNlcnREYXRhW3Byb3BOYW1lXSA9IG5ldyBEYXRlKGNvbHVtbi52YWx1ZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaW5zZXJ0RGF0YVtwcm9wTmFtZV0gPSBjb2x1bW4udmFsdWU7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgIGlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wKHByb3ApIHx8XG4gICAgICAgIChpc09uZVRvT25lUmVsYXRpb25Qcm9wKHByb3ApICYmIHByb3AuaGFzSm9pbkNvbHVtbilcbiAgICAgICkge1xuICAgICAgICBpbnNlcnREYXRhW2Ake3Byb3BOYW1lfV9pZGBdID0gY29sdW1uLnZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gaW5zZXJ0RGF0YTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaW5zZXJ0Rml4dHVyZShkYjogS25leCwgZml4dHVyZTogRml4dHVyZVJlY29yZCkge1xuICAgIGNvbnN0IGluc2VydERhdGEgPSB0aGlzLnByZXBhcmVJbnNlcnREYXRhKGZpeHR1cmUpO1xuICAgIGNvbnN0IGVudGl0eSA9IEVudGl0eU1hbmFnZXIuZ2V0KGZpeHR1cmUuZW50aXR5SWQpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHVuaXF1ZUZvdW5kID0gYXdhaXQgdGhpcy5jaGVja1VuaXF1ZVZpb2xhdGlvbihkYiwgZW50aXR5LCBmaXh0dXJlKTtcbiAgICAgIGlmICh1bmlxdWVGb3VuZCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGVudGl0eUlkOiBmaXh0dXJlLmVudGl0eUlkLFxuICAgICAgICAgIGlkOiB1bmlxdWVGb3VuZC5pZCxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgZm91bmQgPSBhd2FpdCBkYihlbnRpdHkudGFibGUpLndoZXJlKFwiaWRcIiwgZml4dHVyZS5pZCkuZmlyc3QoKTtcbiAgICAgIGlmIChmb3VuZCAmJiAhZml4dHVyZS5vdmVycmlkZSkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGVudGl0eUlkOiBmaXh0dXJlLmVudGl0eUlkLFxuICAgICAgICAgIGlkOiBmb3VuZC5pZCxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcSA9IGRiLmluc2VydChpbnNlcnREYXRhKS5pbnRvKGVudGl0eS50YWJsZSk7XG4gICAgICBhd2FpdCBxLm9uRHVwbGljYXRlVXBkYXRlLmFwcGx5KHEsIE9iamVjdC5rZXlzKGluc2VydERhdGEpKTtcbiAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmdyZWVuKGBJbnNlcnRlZCBpbnRvICR7ZW50aXR5LnRhYmxlfTogIyR7Zml4dHVyZS5pZH1gKSk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGVudGl0eUlkOiBmaXh0dXJlLmVudGl0eUlkLFxuICAgICAgICBpZDogZml4dHVyZS5pZCxcbiAgICAgIH07XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zb2xlLmxvZyhlcnIpO1xuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlTWFueVRvTWFueVJlbGF0aW9ucyhcbiAgICBkYjogS25leCxcbiAgICBmaXh0dXJlOiBGaXh0dXJlUmVjb3JkLFxuICAgIGZpeHR1cmVzOiBGaXh0dXJlUmVjb3JkW11cbiAgKSB7XG4gICAgZm9yIChjb25zdCBbLCBjb2x1bW5dIG9mIE9iamVjdC5lbnRyaWVzKGZpeHR1cmUuY29sdW1ucykpIHtcbiAgICAgIGNvbnN0IHByb3AgPSBjb2x1bW4ucHJvcCBhcyBFbnRpdHlQcm9wO1xuICAgICAgaWYgKGlzTWFueVRvTWFueVJlbGF0aW9uUHJvcChwcm9wKSkge1xuICAgICAgICBjb25zdCBqb2luVGFibGUgPSAocHJvcCBhcyBNYW55VG9NYW55UmVsYXRpb25Qcm9wKS5qb2luVGFibGU7XG4gICAgICAgIGNvbnN0IHJlbGF0ZWRJZHMgPSBjb2x1bW4udmFsdWUgYXMgbnVtYmVyW107XG5cbiAgICAgICAgZm9yIChjb25zdCByZWxhdGVkSWQgb2YgcmVsYXRlZElkcykge1xuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICFmaXh0dXJlcy5maW5kKChmKSA9PiBmLmZpeHR1cmVJZCA9PT0gYCR7cHJvcC53aXRofSMke3JlbGF0ZWRJZH1gKVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgZW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQoZml4dHVyZS5lbnRpdHlJZCk7XG4gICAgICAgICAgY29uc3QgcmVsYXRlZEVudGl0eSA9IEVudGl0eU1hbmFnZXIuZ2V0KHByb3Aud2l0aCk7XG4gICAgICAgICAgaWYgKCFlbnRpdHkgfHwgIXJlbGF0ZWRFbnRpdHkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgYEVudGl0eSBub3QgZm91bmQ6ICR7Zml4dHVyZS5lbnRpdHlJZH0sICR7cHJvcC53aXRofWBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgW2ZvdW5kXSA9IGF3YWl0IGRiKGpvaW5UYWJsZSlcbiAgICAgICAgICAgIC53aGVyZSh7XG4gICAgICAgICAgICAgIFtgJHtpbmZsZWN0aW9uLnNpbmd1bGFyaXplKGVudGl0eS50YWJsZSl9X2lkYF06IGZpeHR1cmUuaWQsXG4gICAgICAgICAgICAgIFtgJHtpbmZsZWN0aW9uLnNpbmd1bGFyaXplKHJlbGF0ZWRFbnRpdHkudGFibGUpfV9pZGBdOiByZWxhdGVkSWQsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLmxpbWl0KDEpO1xuICAgICAgICAgIGlmIChmb3VuZCkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgbmV3SWRzID0gYXdhaXQgZGIoam9pblRhYmxlKS5pbnNlcnQoe1xuICAgICAgICAgICAgW2Ake2luZmxlY3Rpb24uc2luZ3VsYXJpemUoZW50aXR5LnRhYmxlKX1faWRgXTogZml4dHVyZS5pZCxcbiAgICAgICAgICAgIFtgJHtpbmZsZWN0aW9uLnNpbmd1bGFyaXplKHJlbGF0ZWRFbnRpdHkudGFibGUpfV9pZGBdOiByZWxhdGVkSWQsXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBjaGFsay5ncmVlbihcbiAgICAgICAgICAgICAgYEluc2VydGVkIGludG8gJHtqb2luVGFibGV9OiAke2VudGl0eS50YWJsZX0oJHtmaXh0dXJlLmlkfSkgLSAke3JlbGF0ZWRFbnRpdHkudGFibGV9KCR7cmVsYXRlZElkfSkgSUQ6ICR7bmV3SWRzfWBcbiAgICAgICAgICAgIClcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgYWRkRml4dHVyZUxvYWRlcihjb2RlOiBzdHJpbmcpIHtcbiAgICBjb25zdCBwYXRoID0gU29uYW11LmFwaVJvb3RQYXRoICsgXCIvc3JjL3Rlc3RpbmcvZml4dHVyZS50c1wiO1xuICAgIGxldCBjb250ZW50ID0gcmVhZEZpbGVTeW5jKHBhdGgpLnRvU3RyaW5nKCk7XG5cbiAgICBjb25zdCBmaXh0dXJlTG9hZGVyU3RhcnQgPSBjb250ZW50LmluZGV4T2YoXCJjb25zdCBmaXh0dXJlTG9hZGVyID0ge1wiKTtcbiAgICBjb25zdCBmaXh0dXJlTG9hZGVyRW5kID0gY29udGVudC5pbmRleE9mKFwifTtcIiwgZml4dHVyZUxvYWRlclN0YXJ0KTtcblxuICAgIGlmIChmaXh0dXJlTG9hZGVyU3RhcnQgIT09IC0xICYmIGZpeHR1cmVMb2FkZXJFbmQgIT09IC0xKSB7XG4gICAgICBjb25zdCBuZXdDb250ZW50ID1cbiAgICAgICAgY29udGVudC5zbGljZSgwLCBmaXh0dXJlTG9hZGVyRW5kKSArXG4gICAgICAgIFwiICBcIiArXG4gICAgICAgIGNvZGUgK1xuICAgICAgICBcIlxcblwiICtcbiAgICAgICAgY29udGVudC5zbGljZShmaXh0dXJlTG9hZGVyRW5kKTtcblxuICAgICAgd3JpdGVGaWxlU3luYyhwYXRoLCBuZXdDb250ZW50KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmFpbGVkIHRvIGZpbmQgZml4dHVyZUxvYWRlciBpbiBmaXh0dXJlLnRzXCIpO1xuICAgIH1cbiAgfVxuXG4gIC8vIO2VtOuLuSDtlL3siqTss5DsnZgg6rCS7Jy866GcIOycoOuLiO2BrCDsoJzslb3sl5Ag7JyE67Cw65CY64qUIOugiOy9lOuTnOqwgCDsnojripTsp4Ag7ZmV7J24XG4gIHByaXZhdGUgYXN5bmMgY2hlY2tVbmlxdWVWaW9sYXRpb24oXG4gICAgZGI6IEtuZXgsXG4gICAgZW50aXR5OiBFbnRpdHksXG4gICAgZml4dHVyZTogRml4dHVyZVJlY29yZFxuICApIHtcbiAgICBjb25zdCBfdW5pcXVlSW5kZXhlcyA9IGVudGl0eS5pbmRleGVzLmZpbHRlcigoaSkgPT4gaS50eXBlID09PSBcInVuaXF1ZVwiKTtcblxuICAgIC8vIE1hbnlUb01hbnkg6rSA6rOEIO2FjOydtOu4lOydmCDsnKDri4jtgawg7KCc7JW97J2AIOygnOyZuFxuICAgIGNvbnN0IHVuaXF1ZUluZGV4ZXMgPSBfdW5pcXVlSW5kZXhlcy5maWx0ZXIoKGluZGV4KSA9PlxuICAgICAgaW5kZXguY29sdW1ucy5ldmVyeSgoY29sdW1uKSA9PiAhY29sdW1uLnN0YXJ0c1dpdGgoYCR7ZW50aXR5LnRhYmxlfV9fYCkpXG4gICAgKTtcbiAgICBpZiAodW5pcXVlSW5kZXhlcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGxldCB1bmlxdWVRdWVyeSA9IGRiKGVudGl0eS50YWJsZSk7XG4gICAgZm9yIChjb25zdCBpbmRleCBvZiB1bmlxdWVJbmRleGVzKSB7XG4gICAgICAvLyDsu6zrn7wg7KSRIO2VmOuCmOudvOuPhCBudWxs7J2066m0IOycoOuLiO2BrCDsoJzslb3snYQg7JyE67CY7ZWY7KeAIOyViuq4sCDrlYzrrLjsl5Ag7ZW064u5IOyduOuNseyKpOuKlCDrrLTsi5xcbiAgICAgIGNvbnN0IGNvbnRhaW5zTnVsbCA9IGluZGV4LmNvbHVtbnMuc29tZSgoY29sdW1uKSA9PiB7XG4gICAgICAgIGNvbnN0IGZpZWxkID0gY29sdW1uLnNwbGl0KFwiX2lkXCIpWzBdO1xuICAgICAgICByZXR1cm4gZml4dHVyZS5jb2x1bW5zW2ZpZWxkXS52YWx1ZSA9PT0gbnVsbDtcbiAgICAgIH0pO1xuICAgICAgaWYgKGNvbnRhaW5zTnVsbCkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgdW5pcXVlUXVlcnkgPSB1bmlxdWVRdWVyeS5vcldoZXJlKChxYikgPT4ge1xuICAgICAgICBmb3IgKGNvbnN0IGNvbHVtbiBvZiBpbmRleC5jb2x1bW5zKSB7XG4gICAgICAgICAgY29uc3QgZmllbGQgPSBjb2x1bW4uc3BsaXQoXCJfaWRcIilbMF07XG5cbiAgICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShmaXh0dXJlLmNvbHVtbnNbZmllbGRdLnZhbHVlKSkge1xuICAgICAgICAgICAgcWIud2hlcmVJbihjb2x1bW4sIGZpeHR1cmUuY29sdW1uc1tmaWVsZF0udmFsdWUpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBxYi5hbmRXaGVyZShjb2x1bW4sIGZpeHR1cmUuY29sdW1uc1tmaWVsZF0udmFsdWUpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICAgIGNvbnN0IFt1bmlxdWVGb3VuZF0gPSBhd2FpdCB1bmlxdWVRdWVyeTtcbiAgICByZXR1cm4gdW5pcXVlRm91bmQ7XG4gIH1cbn1cbmV4cG9ydCBjb25zdCBGaXh0dXJlTWFuYWdlciA9IG5ldyBGaXh0dXJlTWFuYWdlckNsYXNzKCk7XG4iXSwibmFtZXMiOlsiY2hhbGsiLCJfIiwiU29uYW11IiwiRW50aXR5TWFuYWdlciIsImlzQmVsb25nc1RvT25lUmVsYXRpb25Qcm9wIiwiaXNIYXNNYW55UmVsYXRpb25Qcm9wIiwiaXNNYW55VG9NYW55UmVsYXRpb25Qcm9wIiwiaXNPbmVUb09uZVJlbGF0aW9uUHJvcCIsImlzUmVsYXRpb25Qcm9wIiwiaXNWaXJ0dWFsUHJvcCIsImluZmxlY3Rpb24iLCJyZWFkRmlsZVN5bmMiLCJ3cml0ZUZpbGVTeW5jIiwiUmVsYXRpb25HcmFwaCIsImtuZXgiLCJCYXNlTW9kZWwiLCJGaXh0dXJlTWFuYWdlckNsYXNzIiwiX3RkYiIsInRkYiIsIkVycm9yIiwiX2ZkYiIsImZkYiIsImNhY2hlZFRhYmxlTmFtZXMiLCJyZWxhdGlvbkdyYXBoIiwiaW5pdCIsImRiQ29uZmlnIiwidGVzdCIsInByb2R1Y3Rpb25fbWFzdGVyIiwidENvbm4iLCJjb25uZWN0aW9uIiwicENvbm4iLCJob3N0IiwicG9ydCIsImRhdGFiYXNlIiwiZml4dHVyZV9sb2NhbCIsImNsZWFuQW5kU2VlZCIsInVzaW5nVGFibGVzIiwidGFibGVOYW1lcyIsInRhYmxlcyIsInJhdyIsIm1hcCIsInRhYmxlSW5mbyIsInRhYmxlTGlzdFN0ciIsImpvaW4iLCJmZGJDaGVja3N1bVJvd3MiLCJ0ZGJDaGVja3N1bVJvd3MiLCJmZGJDaGVja3N1bXMiLCJNYXAiLCJyb3ciLCJUYWJsZSIsInNwbGl0IiwicG9wIiwiQ2hlY2tzdW0iLCJ0ZGJDaGVja3N1bXMiLCJjaGFuZ2VkVGFibGVzIiwiZmlsdGVyIiwidGFibGVOYW1lIiwiZ2V0IiwidHJhbnNhY3Rpb24iLCJ0cngiLCJQcm9taXNlIiwiYWxsIiwidHJ1bmNhdGUiLCJyYXdRdWVyeSIsImdldENoZWNrc3VtIiwiZGIiLCJjaGVja3N1bVJvdyIsInN5bmMiLCJmcmRiIiwiZml4dHVyZV9yZW1vdGUiLCJ0YWJsZSIsIk5hbWUiLCJjb25zb2xlIiwibG9nIiwibWFnZW50YSIsInN0YXJ0c1dpdGgiLCJyZW1vdGVDaGVja3N1bSIsImxvY2FsQ2hlY2tzdW0iLCJyb3dzIiwibGVuZ3RoIiwiYmx1ZSIsImluc2VydCIsIk9iamVjdCIsImZyb21FbnRyaWVzIiwiZW50cmllcyIsImtleSIsInZhbHVlIiwiRGF0ZSIsIkpTT04iLCJzdHJpbmdpZnkiLCJpbnRvIiwiZGVzdHJveSIsInZpc2l0ZWRSZWNvcmRzIiwiU2V0IiwiaW1wb3J0Rml4dHVyZSIsImVudGl0eUlkIiwiaWRzIiwiY2xlYXIiLCJxdWVyaWVzIiwidW5pcSIsImlkIiwiZ2V0SW1wb3J0UXVlcmllcyIsImZsYXQiLCJ3ZGIiLCJnZXREQiIsInF1ZXJ5IiwicnNoIiwiaW5mbyIsImZpZWxkIiwicmVjb3JkS2V5IiwiaGFzIiwiYWRkIiwiZW50aXR5Iiwid2hlcmUiLCJsaW1pdCIsInVuZGVmaW5lZCIsImZpeHR1cmVEYXRhYmFzZSIsInJlYWxEYXRhYmFzZSIsInNlbGZRdWVyeSIsImFyZ3MiLCJyZWxhdGlvbnMiLCJyZWxhdGlvbiIsImN1c3RvbUpvaW5DbGF1c2UiLCJoYXNKb2luQ29sdW1uIiwicmVsYXRlZEVudGl0eSIsIndpdGgiLCJyZWxhdGVkSWRDb2x1bW5OYW1lIiwicHJvcHMiLCJmaW5kIiwicCIsIm5hbWUiLCJhcmciLCJyZWxRdWVyaWVzIiwicmV2ZXJzZSIsImdldEZpeHR1cmVzIiwic291cmNlREJOYW1lIiwidGFyZ2V0REJOYW1lIiwic2VhcmNoT3B0aW9ucyIsInNvdXJjZURCIiwidGFyZ2V0REIiLCJzZWFyY2hUeXBlIiwiY29sdW1uIiwicHJvcCIsInR5cGUiLCJmaXh0dXJlcyIsImluaXRpYWxSZWNvcmRzTGVuZ3RoIiwibmV3UmVjb3JkcyIsImNyZWF0ZUZpeHR1cmVSZWNvcmQiLCJwdXNoIiwiY3VycmVudEZpeHR1cmVSZWNvcmQiLCJyIiwiZml4dHVyZUlkIiwiZmV0Y2hlZFJlY29yZHMiLCJzbGljZSIsImZpeHR1cmUiLCJmaXJzdCIsInJlY29yZCIsInNpbmdsZVJlY29yZCIsIl9kYiIsInRhcmdldCIsInVuaXF1ZVJvdyIsImNoZWNrVW5pcXVlVmlvbGF0aW9uIiwidW5pcXVlIiwidW5pcUJ5IiwiZiIsIm9wdGlvbnMiLCJyZWNvcmRzIiwidmlzaXRlZEVudGl0aWVzIiwiY3JlYXRlIiwiY29sdW1ucyIsImJlbG9uZ3NSZWNvcmRzIiwidGhyb3VnaFRhYmxlIiwiam9pblRhYmxlIiwiZnJvbUNvbHVtbiIsInNpbmd1bGFyaXplIiwidG9Db2x1bW4iLCJyZWxhdGVkSWRzIiwicGx1Y2siLCJqb2luQ29sdW1uIiwicmVsYXRlZFByb3AiLCJyZWxhdGVkUm93IiwicmVsYXRlZElkIiwiaW5zZXJ0Rml4dHVyZXMiLCJkYk5hbWUiLCJfZml4dHVyZXMiLCJidWlsZEdyYXBoIiwiaW5zZXJ0aW9uT3JkZXIiLCJnZXRJbnNlcnRpb25PcmRlciIsInJlc3VsdCIsImluc2VydEZpeHR1cmUiLCJ5ZWxsb3ciLCJmb3JFYWNoIiwidmFsdWVzIiwiaGFuZGxlTWFueVRvTWFueVJlbGF0aW9ucyIsImRhdGEiLCJwcmVwYXJlSW5zZXJ0RGF0YSIsImluc2VydERhdGEiLCJwcm9wTmFtZSIsInVuaXF1ZUZvdW5kIiwiZm91bmQiLCJvdmVycmlkZSIsInEiLCJvbkR1cGxpY2F0ZVVwZGF0ZSIsImFwcGx5Iiwia2V5cyIsImdyZWVuIiwiZXJyIiwibmV3SWRzIiwiYWRkRml4dHVyZUxvYWRlciIsImNvZGUiLCJwYXRoIiwiYXBpUm9vdFBhdGgiLCJjb250ZW50IiwidG9TdHJpbmciLCJmaXh0dXJlTG9hZGVyU3RhcnQiLCJpbmRleE9mIiwiZml4dHVyZUxvYWRlckVuZCIsIm5ld0NvbnRlbnQiLCJfdW5pcXVlSW5kZXhlcyIsImluZGV4ZXMiLCJpIiwidW5pcXVlSW5kZXhlcyIsImluZGV4IiwiZXZlcnkiLCJ1bmlxdWVRdWVyeSIsImNvbnRhaW5zTnVsbCIsInNvbWUiLCJvcldoZXJlIiwicWIiLCJBcnJheSIsImlzQXJyYXkiLCJ3aGVyZUluIiwiYW5kV2hlcmUiLCJGaXh0dXJlTWFuYWdlciJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsV0FBVyxRQUFRO0FBQzFCLFlBQVlDLE9BQU8sWUFBWTtBQUMvQixTQUFTQyxNQUFNLFFBQVEsa0JBQVM7QUFDaEMsU0FBU0MsYUFBYSxRQUFRLDhCQUEyQjtBQUN6RCxTQU1FQywwQkFBMEIsRUFDMUJDLHFCQUFxQixFQUNyQkMsd0JBQXdCLEVBQ3hCQyxzQkFBc0IsRUFDdEJDLGNBQWMsRUFDZEMsYUFBYSxRQUNSLG9CQUFpQjtBQUV4QixPQUFPQyxnQkFBZ0IsYUFBYTtBQUNwQyxTQUFTQyxZQUFZLEVBQUVDLGFBQWEsUUFBUSxLQUFLO0FBQ2pELFNBQVNDLGFBQWEsUUFBUSx1QkFBb0I7QUFDbEQsT0FBT0MsVUFBb0IsT0FBTztBQUNsQyxTQUFTQyxTQUFTLFFBQVEsNEJBQXlCO0FBR25ELE9BQU8sTUFBTUM7SUFDSEMsT0FBb0IsS0FBSztJQUNqQyxJQUFJQyxJQUFJQSxHQUFTLEVBQUU7UUFDakIsSUFBSSxDQUFDRCxJQUFJLEdBQUdDO0lBQ2Q7SUFDQSxJQUFJQSxNQUFZO1FBQ2QsSUFBSSxJQUFJLENBQUNELElBQUksS0FBSyxNQUFNO1lBQ3RCLE1BQU0sSUFBSUUsTUFBTTtRQUNsQjtRQUNBLE9BQU8sSUFBSSxDQUFDRixJQUFJO0lBQ2xCO0lBRVFHLE9BQW9CLEtBQUs7SUFDakMsSUFBSUMsSUFBSUEsR0FBUyxFQUFFO1FBQ2pCLElBQUksQ0FBQ0QsSUFBSSxHQUFHQztJQUNkO0lBQ0EsSUFBSUEsTUFBWTtRQUNkLElBQUksSUFBSSxDQUFDRCxJQUFJLEtBQUssTUFBTTtZQUN0QixNQUFNLElBQUlELE1BQU07UUFDbEI7UUFDQSxPQUFPLElBQUksQ0FBQ0MsSUFBSTtJQUNsQjtJQUNBRSxtQkFBb0MsS0FBSztJQUVqQ0MsZ0JBQWdCLElBQUlWLGdCQUFnQjtJQUU1Q1csT0FBTztRQUNMLElBQUksSUFBSSxDQUFDUCxJQUFJLEtBQUssTUFBTTtZQUN0QjtRQUNGO1FBQ0EsSUFBSWYsT0FBT3VCLFFBQVEsQ0FBQ0MsSUFBSSxJQUFJeEIsT0FBT3VCLFFBQVEsQ0FBQ0UsaUJBQWlCLEVBQUU7WUFDN0QsTUFBTUMsUUFBUTFCLE9BQU91QixRQUFRLENBQUNDLElBQUksQ0FBQ0csVUFBVTtZQUc3QyxNQUFNQyxRQUFRNUIsT0FBT3VCLFFBQVEsQ0FBQ0UsaUJBQWlCLENBQzVDRSxVQUFVO1lBQ2IsSUFDRSxHQUFHRCxNQUFNRyxJQUFJLElBQUksWUFBWSxDQUFDLEVBQUVILE1BQU1JLElBQUksSUFBSSxLQUFLLENBQUMsRUFDbERKLE1BQU1LLFFBQVEsRUFDZCxLQUNGLEdBQUdILE1BQU1DLElBQUksSUFBSSxZQUFZLENBQUMsRUFBRUQsTUFBTUUsSUFBSSxJQUFJLEtBQUssQ0FBQyxFQUFFRixNQUFNRyxRQUFRLEVBQUUsRUFDdEU7Z0JBQ0EsTUFBTSxJQUFJZCxNQUNSLENBQUMsbUNBQW1DLENBQUM7WUFFekM7UUFDRjtRQUVBLElBQUksQ0FBQ0QsR0FBRyxHQUFHSixLQUFLWixPQUFPdUIsUUFBUSxDQUFDQyxJQUFJO1FBQ3BDLElBQUksQ0FBQ0wsR0FBRyxHQUFHUCxLQUFLWixPQUFPdUIsUUFBUSxDQUFDUyxhQUFhO0lBQy9DO0lBRUEsTUFBTUMsYUFBYUMsV0FBc0IsRUFBRTtRQUN6QyxNQUFNQyxhQUF1QixNQUFNLEFBQUMsQ0FBQTtZQUNsQyxJQUFJRCxhQUFhO2dCQUNmLE9BQU9BO1lBQ1Q7WUFDQSxJQUFJLElBQUksQ0FBQ2QsZ0JBQWdCLEVBQUU7Z0JBQ3pCLE9BQU8sSUFBSSxDQUFDQSxnQkFBZ0I7WUFDOUI7WUFFQSxNQUFNLENBQUNnQixPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUNwQixHQUFHLENBQUNxQixHQUFHLENBQ2pDLENBQUMsbUVBQW1FLENBQUM7WUFFdkUsTUFBTUYsYUFBYUMsT0FBT0UsR0FBRyxDQUMzQixDQUFDQyxZQUFnQ0EsU0FBUyxDQUFDLE9BQU87WUFFcEQsSUFBSSxDQUFDbkIsZ0JBQWdCLEdBQUdlO1lBQ3hCLE9BQU9BO1FBQ1QsQ0FBQTtRQUVBLHdCQUF3QjtRQUN4QixNQUFNSyxlQUFlTCxXQUFXTSxJQUFJLENBQUM7UUFFckMscUJBQXFCO1FBQ3JCLE1BQU0sQ0FBQ0MsZ0JBQWdCLEdBQUcsTUFBTSxJQUFJLENBQUN2QixHQUFHLENBQUNrQixHQUFHLENBRTFDLENBQUMsZUFBZSxFQUFFRyxjQUFjO1FBQ2xDLE1BQU0sQ0FBQ0csZ0JBQWdCLEdBQUcsTUFBTSxJQUFJLENBQUMzQixHQUFHLENBQUNxQixHQUFHLENBRTFDLENBQUMsZUFBZSxFQUFFRyxjQUFjO1FBRWxDLFdBQVc7UUFDWCxNQUFNSSxlQUFlLElBQUlDLElBQ3ZCSCxnQkFBZ0JKLEdBQUcsQ0FBQyxDQUFDUSxNQUFRO2dCQUFDQSxJQUFJQyxLQUFLLENBQUNDLEtBQUssQ0FBQyxLQUFLQyxHQUFHO2dCQUFLSCxJQUFJSSxRQUFRO2FBQUM7UUFFMUUsTUFBTUMsZUFBZSxJQUFJTixJQUN2QkYsZ0JBQWdCTCxHQUFHLENBQUMsQ0FBQ1EsTUFBUTtnQkFBQ0EsSUFBSUMsS0FBSyxDQUFDQyxLQUFLLENBQUMsS0FBS0MsR0FBRztnQkFBS0gsSUFBSUksUUFBUTthQUFDO1FBRzFFLGVBQWU7UUFDZixNQUFNRSxnQkFBZ0JqQixXQUFXa0IsTUFBTSxDQUNyQyxDQUFDQyxZQUFjVixhQUFhVyxHQUFHLENBQUNELGVBQWVILGFBQWFJLEdBQUcsQ0FBQ0Q7UUFHbEUsMkJBQTJCO1FBQzNCLE1BQU0sSUFBSSxDQUFDdEMsR0FBRyxDQUFDd0MsV0FBVyxDQUFDLE9BQU9DO1lBQ2hDLE1BQU1BLElBQUlwQixHQUFHLENBQUMsQ0FBQywwQkFBMEIsQ0FBQztZQUUxQyxNQUFNcUIsUUFBUUMsR0FBRyxDQUNmUCxjQUFjZCxHQUFHLENBQUMsT0FBT2dCO2dCQUN2QixNQUFNRyxJQUFJcEIsR0FBRyxDQUFDLENBQUMsMEJBQTBCLENBQUM7Z0JBQzFDLE1BQU1vQixJQUFJSCxXQUFXTSxRQUFRO2dCQUM3QixNQUFNQyxXQUFXLENBQUMsWUFBWSxFQUM1QixBQUFDN0QsT0FBT3VCLFFBQVEsQ0FBQ0MsSUFBSSxDQUFDRyxVQUFVLENBQTJCSSxRQUFRLENBQ3BFLENBQUMsRUFBRXVCLFVBQVU7c0JBQ0YsRUFDWixBQUFDdEQsT0FBT3VCLFFBQVEsQ0FBQ1MsYUFBYSxDQUFDTCxVQUFVLENBQ3RDSSxRQUFRLENBQ1osQ0FBQyxFQUFFdUIsV0FBVztnQkFDYixNQUFNRyxJQUFJcEIsR0FBRyxDQUFDd0I7WUFDaEI7WUFFRixNQUFNSixJQUFJcEIsR0FBRyxDQUFDLENBQUMsMEJBQTBCLENBQUM7UUFDNUM7SUFFQSwyQ0FBMkM7SUFDN0M7SUFFQSxNQUFNeUIsWUFBWUMsRUFBUSxFQUFFVCxTQUFpQixFQUFFO1FBQzdDLE1BQU0sQ0FBQyxDQUFDVSxZQUFZLENBQUMsR0FBRyxNQUFNRCxHQUFHMUIsR0FBRyxDQUFDLENBQUMsZUFBZSxFQUFFaUIsV0FBVztRQUNsRSxPQUFPVSxZQUFZZCxRQUFRO0lBQzdCO0lBRUEsTUFBTWUsT0FBTztRQUNYLE1BQU1DLE9BQU90RCxLQUFLWixPQUFPdUIsUUFBUSxDQUFDNEMsY0FBYztRQUVoRCxNQUFNLENBQUMvQixPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUNqQixHQUFHLENBQUNrQixHQUFHLENBQ2pDO1FBRUYsTUFBTUYsYUFBdUJDLE9BQU9FLEdBQUcsQ0FDckMsQ0FBQzhCLFFBQWVBLE1BQU1DLElBQUk7UUFHNUJDLFFBQVFDLEdBQUcsQ0FBQ3pFLE1BQU0wRSxPQUFPLENBQUM7UUFDMUIsTUFBTWQsUUFBUUMsR0FBRyxDQUNmeEIsV0FBV0csR0FBRyxDQUFDLE9BQU9nQjtZQUNwQixJQUFJQSxVQUFVbUIsVUFBVSxDQUFDLG9CQUFvQjtnQkFDM0M7WUFDRjtZQUVBLE1BQU1DLGlCQUFpQixNQUFNLElBQUksQ0FBQ1osV0FBVyxDQUFDSSxNQUFNWjtZQUNwRCxNQUFNcUIsZ0JBQWdCLE1BQU0sSUFBSSxDQUFDYixXQUFXLENBQUMsSUFBSSxDQUFDM0MsR0FBRyxFQUFFbUM7WUFFdkQsSUFBSW9CLG1CQUFtQkMsZUFBZTtnQkFDcEMsTUFBTSxJQUFJLENBQUN4RCxHQUFHLENBQUNxQyxXQUFXLENBQUMsT0FBT0E7b0JBQ2hDLE1BQU1BLFlBQVluQixHQUFHLENBQUMsQ0FBQywwQkFBMEIsQ0FBQztvQkFDbEQsTUFBTW1CLFlBQVlGLFdBQVdNLFFBQVE7b0JBRXJDLE1BQU1nQixPQUFPLE1BQU1WLEtBQUtaO29CQUN4QixJQUFJc0IsS0FBS0MsTUFBTSxLQUFLLEdBQUc7d0JBQ3JCO29CQUNGO29CQUVBUCxRQUFRQyxHQUFHLENBQUN6RSxNQUFNZ0YsSUFBSSxDQUFDeEIsWUFBWXNCLEtBQUtDLE1BQU07b0JBQzlDLE1BQU1yQixZQUNIdUIsTUFBTSxDQUNMSCxLQUFLdEMsR0FBRyxDQUFDLENBQUNRO3dCQUNSLE9BQU9rQyxPQUFPQyxXQUFXLENBQ3ZCRCxPQUFPRSxPQUFPLENBQUNwQyxLQUFLUixHQUFHLENBQUMsQ0FBQyxDQUFDNkMsS0FBS0MsTUFBTTs0QkFDbkMsSUFBSUEsVUFBVSxNQUFNO2dDQUNsQixPQUFPO29DQUFDRDtvQ0FBSztpQ0FBSzs0QkFDcEIsT0FBTyxJQUFJLE9BQU9DLFVBQVUsV0FBVztnQ0FDckMsT0FBTztvQ0FBQ0Q7b0NBQUtDLFFBQVEsSUFBSTtpQ0FBRTs0QkFDN0IsT0FBTyxJQUNMLE9BQU9BLFVBQVUsWUFDakIsQ0FBRUEsQ0FBQUEsaUJBQWlCQyxJQUFHLEdBQ3RCO2dDQUNBLE9BQU87b0NBQUNGO29DQUFLRyxLQUFLQyxTQUFTLENBQUNIO2lDQUFPOzRCQUNyQyxPQUFPO2dDQUNMLE9BQU87b0NBQUNEO29DQUFLQztpQ0FBTTs0QkFDckI7d0JBQ0Y7b0JBRUosSUFFREksSUFBSSxDQUFDbEM7b0JBQ1JnQixRQUFRQyxHQUFHLENBQUM7b0JBQ1osTUFBTWYsWUFBWW5CLEdBQUcsQ0FBQyxDQUFDLDBCQUEwQixDQUFDO2dCQUNwRDtZQUNGO1FBQ0Y7UUFFRmlDLFFBQVFDLEdBQUcsQ0FBQ3pFLE1BQU0wRSxPQUFPLENBQUM7UUFFMUIsTUFBTU4sS0FBS3VCLE9BQU87SUFDcEI7SUFFUUMsaUJBQWlCLElBQUlDLE1BQWM7SUFDM0MsTUFBTUMsY0FBY0MsUUFBZ0IsRUFBRUMsR0FBYSxFQUFFO1FBQ25ELCtCQUErQjtRQUMvQixJQUFJLENBQUNKLGNBQWMsQ0FBQ0ssS0FBSztRQUV6QixNQUFNQyxVQUFVakcsRUFBRWtHLElBQUksQ0FDcEIsQUFDRSxDQUFBLE1BQU12QyxRQUFRQyxHQUFHLENBQ2ZtQyxJQUFJeEQsR0FBRyxDQUFDLE9BQU80RDtZQUNiLE9BQU8sTUFBTSxJQUFJLENBQUNDLGdCQUFnQixDQUFDTixVQUFVLE1BQU1LO1FBQ3JELEdBQ0YsRUFDQUUsSUFBSTtRQUdSLE1BQU1DLE1BQU14RixVQUFVeUYsS0FBSyxDQUFDO1FBQzVCLEtBQUssSUFBSUMsU0FBU1AsUUFBUztZQUN6QixNQUFNLENBQUNRLElBQUksR0FBRyxNQUFNSCxJQUFJaEUsR0FBRyxDQUFDa0U7WUFDNUJqQyxRQUFRQyxHQUFHLENBQUM7Z0JBQ1ZnQztnQkFDQUUsTUFBTUQsSUFBSUMsSUFBSTtZQUNoQjtRQUNGO0lBQ0Y7SUFFQSxNQUFNTixpQkFDSk4sUUFBZ0IsRUFDaEJhLEtBQWEsRUFDYlIsRUFBVSxFQUNTO1FBQ25CLE1BQU1TLFlBQVksR0FBR2QsU0FBUyxDQUFDLEVBQUVhLE1BQU0sQ0FBQyxFQUFFUixJQUFJO1FBRTlDLDJCQUEyQjtRQUMzQixJQUFJLElBQUksQ0FBQ1IsY0FBYyxDQUFDa0IsR0FBRyxDQUFDRCxZQUFZO1lBQ3RDLE9BQU8sRUFBRTtRQUNYO1FBQ0EsSUFBSSxDQUFDakIsY0FBYyxDQUFDbUIsR0FBRyxDQUFDRjtRQUV4QnJDLFFBQVFDLEdBQUcsQ0FBQztZQUFFc0I7WUFBVWE7WUFBT1I7UUFBRztRQUNsQyxNQUFNWSxTQUFTN0csY0FBY3NELEdBQUcsQ0FBQ3NDO1FBQ2pDLE1BQU1RLE1BQU14RixVQUFVeUYsS0FBSyxDQUFDO1FBRTVCLG1CQUFtQjtRQUNuQixNQUFNLENBQUN4RCxJQUFJLEdBQUcsTUFBTXVELElBQUlTLE9BQU8xQyxLQUFLLEVBQUUyQyxLQUFLLENBQUNMLE9BQU9SLElBQUljLEtBQUssQ0FBQztRQUM3RCxJQUFJbEUsUUFBUW1FLFdBQVc7WUFDckIsTUFBTSxJQUFJaEcsTUFBTSxHQUFHNEUsU0FBUyxDQUFDLEVBQUVLLEdBQUcsZ0JBQWdCLENBQUM7UUFDckQ7UUFFQSxhQUFhO1FBQ2IsTUFBTWdCLGtCQUFrQixBQUFDbEgsT0FBT3VCLFFBQVEsQ0FBQzRDLGNBQWMsQ0FBQ3hDLFVBQVUsQ0FDL0RJLFFBQVE7UUFDWCxNQUFNb0YsZUFBZSxBQUFDbkgsT0FBT3VCLFFBQVEsQ0FBQ0UsaUJBQWlCLENBQUNFLFVBQVUsQ0FDL0RJLFFBQVE7UUFFWCxNQUFNcUYsWUFBWSxDQUFDLHFCQUFxQixFQUFFRixnQkFBZ0IsS0FBSyxFQUFFSixPQUFPMUMsS0FBSyxDQUFDLG9CQUFvQixFQUFFK0MsYUFBYSxLQUFLLEVBQUVMLE9BQU8xQyxLQUFLLENBQUMsa0JBQWtCLEVBQUU4QixHQUFHLENBQUMsQ0FBQztRQUU5SixNQUFNbUIsT0FBT3JDLE9BQU9FLE9BQU8sQ0FBQzRCLE9BQU9RLFNBQVMsRUFDekNqRSxNQUFNLENBQ0wsQ0FBQyxHQUFHa0UsU0FBUyxHQUNYckgsMkJBQTJCcUgsYUFDMUJsSCx1QkFBdUJrSCxhQUN0QkEsU0FBU0MsZ0JBQWdCLEtBQUtQLFdBRW5DM0UsR0FBRyxDQUFDLENBQUMsR0FBR2lGLFNBQVM7WUFDaEI7Ozs7Ozs7UUFPQSxHQUNBLElBQUliO1lBQ0osSUFBSVI7WUFDSixJQUFJN0YsdUJBQXVCa0gsYUFBYSxDQUFDQSxTQUFTRSxhQUFhLEVBQUU7Z0JBQy9ELE1BQU1DLGdCQUFnQnpILGNBQWNzRCxHQUFHLENBQUNnRSxTQUFTSSxJQUFJO2dCQUNyRCxNQUFNQyxzQkFBc0JGLGNBQWNHLEtBQUssQ0FBQ0MsSUFBSSxDQUNsRCxDQUFDQyxJQUFNekgsZUFBZXlILE1BQU1BLEVBQUVKLElBQUksS0FBS2IsT0FBT1osRUFBRSxHQUMvQzhCO2dCQUNILElBQUksQ0FBQ0oscUJBQXFCO29CQUN4QixNQUFNLElBQUkzRyxNQUNSLEdBQUd5RyxjQUFjeEIsRUFBRSxDQUFDLEVBQUUsRUFBRVksT0FBT1osRUFBRSxDQUFDLGtCQUFrQixDQUFDO2dCQUV6RDtnQkFDQVEsUUFBUSxHQUFHa0Isb0JBQW9CLEdBQUcsQ0FBQztnQkFDbkMxQixLQUFLcEQsR0FBRyxDQUFDLEtBQUs7WUFDaEIsT0FBTztnQkFDTDRELFFBQVE7Z0JBQ1JSLEtBQUtwRCxHQUFHLENBQUMsR0FBR3lFLFNBQVNTLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNqQztZQUNBLE9BQU87Z0JBQ0xuQyxVQUFVMEIsU0FBU0ksSUFBSTtnQkFDdkJqQjtnQkFDQVI7WUFDRjtRQUNGLEdBQ0M3QyxNQUFNLENBQUMsQ0FBQzRFLE1BQVFBLElBQUkvQixFQUFFLEtBQUs7UUFFOUIsTUFBTWdDLGFBQWEsTUFBTXhFLFFBQVFDLEdBQUcsQ0FDbEMwRCxLQUFLL0UsR0FBRyxDQUFDLE9BQU8rRTtZQUNkLE9BQU8sSUFBSSxDQUFDbEIsZ0JBQWdCLENBQUNrQixLQUFLeEIsUUFBUSxFQUFFd0IsS0FBS1gsS0FBSyxFQUFFVyxLQUFLbkIsRUFBRTtRQUNqRTtRQUdGLE9BQU87ZUFBSW5HLEVBQUVrRyxJQUFJLENBQUNpQyxXQUFXQyxPQUFPLEdBQUcvQixJQUFJO1lBQUtnQjtTQUFVO0lBQzVEO0lBRUEsTUFBTTNCLFVBQVU7UUFDZCxJQUFJLElBQUksQ0FBQzFFLElBQUksRUFBRTtZQUNiLE1BQU0sSUFBSSxDQUFDQSxJQUFJLENBQUMwRSxPQUFPO1lBQ3ZCLElBQUksQ0FBQzFFLElBQUksR0FBRztRQUNkO1FBQ0EsSUFBSSxJQUFJLENBQUNHLElBQUksRUFBRTtZQUNiLE1BQU0sSUFBSSxDQUFDQSxJQUFJLENBQUN1RSxPQUFPO1lBQ3ZCLElBQUksQ0FBQ3ZFLElBQUksR0FBRztRQUNkO1FBQ0EsTUFBTUwsVUFBVTRFLE9BQU87SUFDekI7SUFFQSxNQUFNMkMsWUFDSkMsWUFBa0MsRUFDbENDLFlBQWtDLEVBQ2xDQyxhQUFtQyxFQUNuQztRQUNBLE1BQU1DLFdBQVc1SCxLQUFLWixPQUFPdUIsUUFBUSxDQUFDOEcsYUFBYTtRQUNuRCxNQUFNSSxXQUFXN0gsS0FBS1osT0FBT3VCLFFBQVEsQ0FBQytHLGFBQWE7UUFDbkQsTUFBTSxFQUFFekMsUUFBUSxFQUFFYSxLQUFLLEVBQUV0QixLQUFLLEVBQUVzRCxVQUFVLEVBQUUsR0FBR0g7UUFFL0MsTUFBTXpCLFNBQVM3RyxjQUFjc0QsR0FBRyxDQUFDc0M7UUFDakMsTUFBTThDLFNBQ0o3QixPQUFPZSxLQUFLLENBQUNDLElBQUksQ0FBQyxDQUFDYyxPQUFTQSxLQUFLWixJQUFJLEtBQUt0QixRQUFRbUMsU0FBUyxhQUN2RCxHQUFHbkMsTUFBTSxHQUFHLENBQUMsR0FDYkE7UUFFTixJQUFJSCxRQUFRaUMsU0FBUzFCLE9BQU8xQyxLQUFLO1FBQ2pDLElBQUlzRSxlQUFlLFVBQVU7WUFDM0JuQyxRQUFRQSxNQUFNUSxLQUFLLENBQUM0QixRQUFRdkQ7UUFDOUIsT0FBTyxJQUFJc0QsZUFBZSxRQUFRO1lBQ2hDbkMsUUFBUUEsTUFBTVEsS0FBSyxDQUFDNEIsUUFBUSxRQUFRLENBQUMsQ0FBQyxFQUFFdkQsTUFBTSxDQUFDLENBQUM7UUFDbEQ7UUFFQSxNQUFNUixPQUFPLE1BQU0yQjtRQUNuQixJQUFJM0IsS0FBS0MsTUFBTSxLQUFLLEdBQUc7WUFDckIsTUFBTSxJQUFJNUQsTUFBTTtRQUNsQjtRQUVBLE1BQU02SCxXQUE0QixFQUFFO1FBQ3BDLEtBQUssTUFBTWhHLE9BQU84QixLQUFNO1lBQ3RCLE1BQU1tRSx1QkFBdUJELFNBQVNqRSxNQUFNO1lBQzVDLE1BQU1tRSxhQUFhLE1BQU0sSUFBSSxDQUFDQyxtQkFBbUIsQ0FBQ25DLFFBQVFoRTtZQUMxRGdHLFNBQVNJLElBQUksSUFBSUY7WUFDakIsTUFBTUcsdUJBQXVCTCxTQUFTaEIsSUFBSSxDQUN4QyxDQUFDc0IsSUFBTUEsRUFBRUMsU0FBUyxLQUFLLEdBQUd4RCxTQUFTLENBQUMsRUFBRS9DLElBQUlvRCxFQUFFLEVBQUU7WUFHaEQsSUFBSWlELHNCQUFzQjtnQkFDeEIsc0NBQXNDO2dCQUN0Q0EscUJBQXFCRyxjQUFjLEdBQUdSLFNBQ25DekYsTUFBTSxDQUFDLENBQUMrRixJQUFNQSxFQUFFQyxTQUFTLEtBQUtGLHFCQUFxQkUsU0FBUyxFQUM1REUsS0FBSyxDQUFDUixzQkFDTnpHLEdBQUcsQ0FBQyxDQUFDOEcsSUFBTUEsRUFBRUMsU0FBUztZQUMzQjtRQUNGO1FBRUEsV0FBVyxNQUFNRyxXQUFXVixTQUFVO1lBQ3BDLE1BQU1oQyxTQUFTN0csY0FBY3NELEdBQUcsQ0FBQ2lHLFFBQVEzRCxRQUFRO1lBRWpELG1DQUFtQztZQUNuQyxNQUFNL0MsTUFBTSxNQUFNMkYsU0FBUzNCLE9BQU8xQyxLQUFLLEVBQUUyQyxLQUFLLENBQUMsTUFBTXlDLFFBQVF0RCxFQUFFLEVBQUV1RCxLQUFLO1lBQ3RFLElBQUkzRyxLQUFLO2dCQUNQLE1BQU0sQ0FBQzRHLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQ1QsbUJBQW1CLENBQUNuQyxRQUFRaEUsS0FBSztvQkFDM0Q2RyxjQUFjO29CQUNkQyxLQUFLbkI7Z0JBQ1A7Z0JBQ0FlLFFBQVFLLE1BQU0sR0FBR0g7Z0JBQ2pCO1lBQ0Y7WUFFQSxzREFBc0Q7WUFDdEQsTUFBTUksWUFBWSxNQUFNLElBQUksQ0FBQ0Msb0JBQW9CLENBQy9DdEIsVUFDQTNCLFFBQ0EwQztZQUVGLElBQUlNLFdBQVc7Z0JBQ2IsTUFBTSxDQUFDSixPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUNULG1CQUFtQixDQUFDbkMsUUFBUWdELFdBQVc7b0JBQ2pFSCxjQUFjO29CQUNkQyxLQUFLbkI7Z0JBQ1A7Z0JBQ0FlLFFBQVFRLE1BQU0sR0FBR047WUFDbkI7UUFDRjtRQUVBLE1BQU1qQixTQUFTaEQsT0FBTztRQUN0QixNQUFNK0MsU0FBUy9DLE9BQU87UUFFdEIsT0FBTzFGLEVBQUVrSyxNQUFNLENBQUNuQixVQUFVLENBQUNvQixJQUFNQSxFQUFFYixTQUFTO0lBQzlDO0lBRUEsTUFBTUosb0JBQ0puQyxNQUFjLEVBQ2RoRSxHQUFRLEVBQ1JxSCxPQUdDLEVBQ3lCO1FBQzFCLE1BQU1DLFVBQTJCLEVBQUU7UUFDbkMsTUFBTUMsa0JBQWtCLElBQUkxRTtRQUU1QixNQUFNMkUsU0FBUyxPQUFPeEQsUUFBZ0JoRTtZQUNwQyxNQUFNdUcsWUFBWSxHQUFHdkMsT0FBT1osRUFBRSxDQUFDLENBQUMsRUFBRXBELElBQUlvRCxFQUFFLEVBQUU7WUFDMUMsSUFBSW1FLGdCQUFnQnpELEdBQUcsQ0FBQ3lDLFlBQVk7Z0JBQ2xDO1lBQ0Y7WUFDQWdCLGdCQUFnQnhELEdBQUcsQ0FBQ3dDO1lBRXBCLE1BQU1LLFNBQXdCO2dCQUM1Qkw7Z0JBQ0F4RCxVQUFVaUIsT0FBT1osRUFBRTtnQkFDbkJBLElBQUlwRCxJQUFJb0QsRUFBRTtnQkFDVnFFLFNBQVMsQ0FBQztnQkFDVmpCLGdCQUFnQixFQUFFO2dCQUNsQmtCLGdCQUFnQixFQUFFO1lBQ3BCO1lBRUEsS0FBSyxNQUFNNUIsUUFBUTlCLE9BQU9lLEtBQUssQ0FBRTtnQkFDL0IsSUFBSXRILGNBQWNxSSxPQUFPO29CQUN2QjtnQkFDRjtnQkFFQWMsT0FBT2EsT0FBTyxDQUFDM0IsS0FBS1osSUFBSSxDQUFDLEdBQUc7b0JBQzFCWSxNQUFNQTtvQkFDTnhELE9BQU90QyxHQUFHLENBQUM4RixLQUFLWixJQUFJLENBQUM7Z0JBQ3ZCO2dCQUVBLE1BQU1qRSxLQUFLb0csU0FBU1AsT0FBTy9JLFVBQVV5RixLQUFLLENBQUM7Z0JBQzNDLElBQUlsRyx5QkFBeUJ3SSxPQUFPO29CQUNsQyxNQUFNbEIsZ0JBQWdCekgsY0FBY3NELEdBQUcsQ0FBQ3FGLEtBQUtqQixJQUFJO29CQUNqRCxNQUFNOEMsZUFBZTdCLEtBQUs4QixTQUFTO29CQUNuQyxNQUFNQyxhQUFhLEdBQUduSyxXQUFXb0ssV0FBVyxDQUFDOUQsT0FBTzFDLEtBQUssRUFBRSxHQUFHLENBQUM7b0JBQy9ELE1BQU15RyxXQUFXLEdBQUdySyxXQUFXb0ssV0FBVyxDQUFDbEQsY0FBY3RELEtBQUssRUFBRSxHQUFHLENBQUM7b0JBRXBFLE1BQU0wRyxhQUFhLE1BQU0vRyxHQUFHMEcsY0FDekIxRCxLQUFLLENBQUM0RCxZQUFZN0gsSUFBSW9ELEVBQUUsRUFDeEI2RSxLQUFLLENBQUNGO29CQUNUbkIsT0FBT2EsT0FBTyxDQUFDM0IsS0FBS1osSUFBSSxDQUFDLENBQUM1QyxLQUFLLEdBQUcwRjtnQkFDcEMsT0FBTyxJQUFJM0ssc0JBQXNCeUksT0FBTztvQkFDdEMsTUFBTWxCLGdCQUFnQnpILGNBQWNzRCxHQUFHLENBQUNxRixLQUFLakIsSUFBSTtvQkFDakQsTUFBTW1ELGFBQWEsTUFBTS9HLEdBQUcyRCxjQUFjdEQsS0FBSyxFQUM1QzJDLEtBQUssQ0FBQzZCLEtBQUtvQyxVQUFVLEVBQUVsSSxJQUFJb0QsRUFBRSxFQUM3QjZFLEtBQUssQ0FBQztvQkFDVHJCLE9BQU9hLE9BQU8sQ0FBQzNCLEtBQUtaLElBQUksQ0FBQyxDQUFDNUMsS0FBSyxHQUFHMEY7Z0JBQ3BDLE9BQU8sSUFBSXpLLHVCQUF1QnVJLFNBQVMsQ0FBQ0EsS0FBS25CLGFBQWEsRUFBRTtvQkFDOUQsTUFBTUMsZ0JBQWdCekgsY0FBY3NELEdBQUcsQ0FBQ3FGLEtBQUtqQixJQUFJO29CQUNqRCxNQUFNc0QsY0FBY3ZELGNBQWNHLEtBQUssQ0FBQ0MsSUFBSSxDQUMxQyxDQUFDQyxJQUFNekgsZUFBZXlILE1BQU1BLEVBQUVKLElBQUksS0FBS2IsT0FBT1osRUFBRTtvQkFFbEQsSUFBSStFLGFBQWE7d0JBQ2YsTUFBTUMsYUFBYSxNQUFNbkgsR0FBRzJELGNBQWN0RCxLQUFLLEVBQzVDMkMsS0FBSyxDQUFDLE1BQU1qRSxJQUFJb0QsRUFBRSxFQUNsQnVELEtBQUs7d0JBQ1JDLE9BQU9hLE9BQU8sQ0FBQzNCLEtBQUtaLElBQUksQ0FBQyxDQUFDNUMsS0FBSyxHQUFHOEYsWUFBWWhGO29CQUNoRDtnQkFDRixPQUFPLElBQUk1RixlQUFlc0ksT0FBTztvQkFDL0IsTUFBTXVDLFlBQVlySSxHQUFHLENBQUMsR0FBRzhGLEtBQUtaLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztvQkFDeEMwQixPQUFPYSxPQUFPLENBQUMzQixLQUFLWixJQUFJLENBQUMsQ0FBQzVDLEtBQUssR0FBRytGO29CQUNsQyxJQUFJQSxXQUFXO3dCQUNiekIsT0FBT2MsY0FBYyxDQUFDdEIsSUFBSSxDQUFDLEdBQUdOLEtBQUtqQixJQUFJLENBQUMsQ0FBQyxFQUFFd0QsV0FBVztvQkFDeEQ7b0JBQ0EsSUFBSSxDQUFDaEIsU0FBU1IsZ0JBQWdCd0IsV0FBVzt3QkFDdkMsTUFBTXpELGdCQUFnQnpILGNBQWNzRCxHQUFHLENBQUNxRixLQUFLakIsSUFBSTt3QkFDakQsTUFBTXVELGFBQWEsTUFBTW5ILEdBQUcyRCxjQUFjdEQsS0FBSyxFQUM1QzJDLEtBQUssQ0FBQyxNQUFNb0UsV0FDWjFCLEtBQUs7d0JBQ1IsSUFBSXlCLFlBQVk7NEJBQ2QsTUFBTVosT0FBTzVDLGVBQWV3RDt3QkFDOUI7b0JBQ0Y7Z0JBQ0Y7WUFDRjtZQUVBZCxRQUFRbEIsSUFBSSxDQUFDUTtRQUNmO1FBRUEsTUFBTVksT0FBT3hELFFBQVFoRTtRQUVyQixPQUFPc0g7SUFDVDtJQUVBLE1BQU1nQixlQUNKQyxNQUE0QixFQUM1QkMsU0FBMEIsRUFDMUI7UUFDQSxNQUFNeEMsV0FBVy9JLEVBQUVrSyxNQUFNLENBQUNxQixXQUFXLENBQUNwQixJQUFNQSxFQUFFYixTQUFTO1FBRXZELElBQUksQ0FBQ2hJLGFBQWEsQ0FBQ2tLLFVBQVUsQ0FBQ3pDO1FBQzlCLE1BQU0wQyxpQkFBaUIsSUFBSSxDQUFDbkssYUFBYSxDQUFDb0ssaUJBQWlCO1FBQzNELE1BQU0xSCxLQUFLbkQsS0FBS1osT0FBT3VCLFFBQVEsQ0FBQzhKLE9BQU87UUFFdkMsTUFBTXRILEdBQUdQLFdBQVcsQ0FBQyxPQUFPQztZQUMxQixNQUFNQSxJQUFJcEIsR0FBRyxDQUFDLENBQUMsMEJBQTBCLENBQUM7WUFFMUMsS0FBSyxNQUFNZ0gsYUFBYW1DLGVBQWdCO2dCQUN0QyxNQUFNaEMsVUFBVVYsU0FBU2hCLElBQUksQ0FBQyxDQUFDb0MsSUFBTUEsRUFBRWIsU0FBUyxLQUFLQTtnQkFDckQsTUFBTXFDLFNBQVMsTUFBTSxJQUFJLENBQUNDLGFBQWEsQ0FBQ2xJLEtBQVkrRjtnQkFDcEQsSUFBSWtDLE9BQU94RixFQUFFLEtBQUtzRCxRQUFRdEQsRUFBRSxFQUFFO29CQUM1QiwyQ0FBMkM7b0JBQzNDNUIsUUFBUUMsR0FBRyxDQUNUekUsTUFBTThMLE1BQU0sQ0FDVixDQUFDLDZCQUE2QixFQUFFcEMsUUFBUTNELFFBQVEsQ0FBQyxDQUFDLEVBQUUyRCxRQUFRdEQsRUFBRSxDQUFDLElBQUksRUFBRXNELFFBQVEzRCxRQUFRLENBQUMsQ0FBQyxFQUFFNkYsT0FBT3hGLEVBQUUsRUFBRTtvQkFHeEc0QyxTQUFTK0MsT0FBTyxDQUFDLENBQUMzQjt3QkFDaEJsRixPQUFPOEcsTUFBTSxDQUFDNUIsRUFBRUssT0FBTyxFQUFFc0IsT0FBTyxDQUFDLENBQUNsRDs0QkFDaEMsSUFDRUEsT0FBT0MsSUFBSSxDQUFDQyxJQUFJLEtBQUssY0FDckJGLE9BQU9DLElBQUksQ0FBQ2pCLElBQUksS0FBSytELE9BQU83RixRQUFRLElBQ3BDOEMsT0FBT3ZELEtBQUssS0FBS29FLFFBQVF0RCxFQUFFLEVBQzNCO2dDQUNBeUMsT0FBT3ZELEtBQUssR0FBR3NHLE9BQU94RixFQUFFOzRCQUMxQjt3QkFDRjtvQkFDRjtvQkFDQXNELFFBQVF0RCxFQUFFLEdBQUd3RixPQUFPeEYsRUFBRTtnQkFDeEI7WUFDRjtZQUVBLEtBQUssTUFBTW1ELGFBQWFtQyxlQUFnQjtnQkFDdEMsTUFBTWhDLFVBQVVWLFNBQVNoQixJQUFJLENBQUMsQ0FBQ29DLElBQU1BLEVBQUViLFNBQVMsS0FBS0E7Z0JBQ3JELE1BQU0sSUFBSSxDQUFDMEMseUJBQXlCLENBQUN0SSxLQUFZK0YsU0FBU1Y7WUFDNUQ7WUFDQSxNQUFNckYsSUFBSXBCLEdBQUcsQ0FBQyxDQUFDLDBCQUEwQixDQUFDO1FBQzVDO1FBRUEsTUFBTStILFVBQWlDLEVBQUU7UUFFekMsV0FBVyxNQUFNaEIsS0FBS04sU0FBVTtZQUM5QixNQUFNaEMsU0FBUzdHLGNBQWNzRCxHQUFHLENBQUM2RixFQUFFdkQsUUFBUTtZQUMzQyxNQUFNNkQsU0FBUyxNQUFNM0YsR0FBRytDLE9BQU8xQyxLQUFLLEVBQUUyQyxLQUFLLENBQUMsTUFBTXFDLEVBQUVsRCxFQUFFLEVBQUV1RCxLQUFLO1lBQzdEVyxRQUFRbEIsSUFBSSxDQUFDO2dCQUNYckQsVUFBVXVELEVBQUV2RCxRQUFRO2dCQUNwQm1HLE1BQU10QztZQUNSO1FBQ0Y7UUFFQSxNQUFNM0YsR0FBRzBCLE9BQU87UUFFaEIsT0FBTzFGLEVBQUVrSyxNQUFNLENBQUNHLFNBQVMsQ0FBQ2hCLElBQU0sR0FBR0EsRUFBRXZELFFBQVEsQ0FBQyxDQUFDLEVBQUV1RCxFQUFFNEMsSUFBSSxDQUFDOUYsRUFBRSxFQUFFO0lBQzlEO0lBRVErRixrQkFBa0J6QyxPQUFzQixFQUFFO1FBQ2hELE1BQU0wQyxhQUFrQixDQUFDO1FBQ3pCLEtBQUssTUFBTSxDQUFDQyxVQUFVeEQsT0FBTyxJQUFJM0QsT0FBT0UsT0FBTyxDQUFDc0UsUUFBUWUsT0FBTyxFQUFHO1lBQ2hFLElBQUloSyxjQUFjb0ksT0FBT0MsSUFBSSxHQUFHO2dCQUM5QjtZQUNGO1lBRUEsTUFBTUEsT0FBT0QsT0FBT0MsSUFBSTtZQUN4QixJQUFJLENBQUN0SSxlQUFlc0ksT0FBTztnQkFDekIsSUFBSUEsS0FBS0MsSUFBSSxLQUFLLFFBQVE7b0JBQ3hCcUQsVUFBVSxDQUFDQyxTQUFTLEdBQUc3RyxLQUFLQyxTQUFTLENBQUNvRCxPQUFPdkQsS0FBSztnQkFDcEQsT0FBTyxJQUFJd0QsS0FBS0MsSUFBSSxLQUFLLGVBQWVELEtBQUtDLElBQUksS0FBSyxZQUFZO29CQUNoRXFELFVBQVUsQ0FBQ0MsU0FBUyxHQUFHLElBQUk5RyxLQUFLc0QsT0FBT3ZELEtBQUs7Z0JBQzlDLE9BQU87b0JBQ0w4RyxVQUFVLENBQUNDLFNBQVMsR0FBR3hELE9BQU92RCxLQUFLO2dCQUNyQztZQUNGLE9BQU8sSUFDTGxGLDJCQUEyQjBJLFNBQzFCdkksdUJBQXVCdUksU0FBU0EsS0FBS25CLGFBQWEsRUFDbkQ7Z0JBQ0F5RSxVQUFVLENBQUMsR0FBR0MsU0FBUyxHQUFHLENBQUMsQ0FBQyxHQUFHeEQsT0FBT3ZELEtBQUs7WUFDN0M7UUFDRjtRQUNBLE9BQU84RztJQUNUO0lBRUEsTUFBY1AsY0FBYzVILEVBQVEsRUFBRXlGLE9BQXNCLEVBQUU7UUFDNUQsTUFBTTBDLGFBQWEsSUFBSSxDQUFDRCxpQkFBaUIsQ0FBQ3pDO1FBQzFDLE1BQU0xQyxTQUFTN0csY0FBY3NELEdBQUcsQ0FBQ2lHLFFBQVEzRCxRQUFRO1FBRWpELElBQUk7WUFDRixNQUFNdUcsY0FBYyxNQUFNLElBQUksQ0FBQ3JDLG9CQUFvQixDQUFDaEcsSUFBSStDLFFBQVEwQztZQUNoRSxJQUFJNEMsYUFBYTtnQkFDZixPQUFPO29CQUNMdkcsVUFBVTJELFFBQVEzRCxRQUFRO29CQUMxQkssSUFBSWtHLFlBQVlsRyxFQUFFO2dCQUNwQjtZQUNGO1lBRUEsTUFBTW1HLFFBQVEsTUFBTXRJLEdBQUcrQyxPQUFPMUMsS0FBSyxFQUFFMkMsS0FBSyxDQUFDLE1BQU15QyxRQUFRdEQsRUFBRSxFQUFFdUQsS0FBSztZQUNsRSxJQUFJNEMsU0FBUyxDQUFDN0MsUUFBUThDLFFBQVEsRUFBRTtnQkFDOUIsT0FBTztvQkFDTHpHLFVBQVUyRCxRQUFRM0QsUUFBUTtvQkFDMUJLLElBQUltRyxNQUFNbkcsRUFBRTtnQkFDZDtZQUNGO1lBRUEsTUFBTXFHLElBQUl4SSxHQUFHZ0IsTUFBTSxDQUFDbUgsWUFBWTFHLElBQUksQ0FBQ3NCLE9BQU8xQyxLQUFLO1lBQ2pELE1BQU1tSSxFQUFFQyxpQkFBaUIsQ0FBQ0MsS0FBSyxDQUFDRixHQUFHdkgsT0FBTzBILElBQUksQ0FBQ1I7WUFDL0M1SCxRQUFRQyxHQUFHLENBQUN6RSxNQUFNNk0sS0FBSyxDQUFDLENBQUMsY0FBYyxFQUFFN0YsT0FBTzFDLEtBQUssQ0FBQyxHQUFHLEVBQUVvRixRQUFRdEQsRUFBRSxFQUFFO1lBRXZFLE9BQU87Z0JBQ0xMLFVBQVUyRCxRQUFRM0QsUUFBUTtnQkFDMUJLLElBQUlzRCxRQUFRdEQsRUFBRTtZQUNoQjtRQUNGLEVBQUUsT0FBTzBHLEtBQUs7WUFDWnRJLFFBQVFDLEdBQUcsQ0FBQ3FJO1lBQ1osTUFBTUE7UUFDUjtJQUNGO0lBRUEsTUFBY2IsMEJBQ1poSSxFQUFRLEVBQ1J5RixPQUFzQixFQUN0QlYsUUFBeUIsRUFDekI7UUFDQSxLQUFLLE1BQU0sR0FBR0gsT0FBTyxJQUFJM0QsT0FBT0UsT0FBTyxDQUFDc0UsUUFBUWUsT0FBTyxFQUFHO1lBQ3hELE1BQU0zQixPQUFPRCxPQUFPQyxJQUFJO1lBQ3hCLElBQUl4SSx5QkFBeUJ3SSxPQUFPO2dCQUNsQyxNQUFNOEIsWUFBWSxBQUFDOUIsS0FBZ0M4QixTQUFTO2dCQUM1RCxNQUFNSSxhQUFhbkMsT0FBT3ZELEtBQUs7Z0JBRS9CLEtBQUssTUFBTStGLGFBQWFMLFdBQVk7b0JBQ2xDLElBQ0UsQ0FBQ2hDLFNBQVNoQixJQUFJLENBQUMsQ0FBQ29DLElBQU1BLEVBQUViLFNBQVMsS0FBSyxHQUFHVCxLQUFLakIsSUFBSSxDQUFDLENBQUMsRUFBRXdELFdBQVcsR0FDakU7d0JBQ0E7b0JBQ0Y7b0JBRUEsTUFBTXJFLFNBQVM3RyxjQUFjc0QsR0FBRyxDQUFDaUcsUUFBUTNELFFBQVE7b0JBQ2pELE1BQU02QixnQkFBZ0J6SCxjQUFjc0QsR0FBRyxDQUFDcUYsS0FBS2pCLElBQUk7b0JBQ2pELElBQUksQ0FBQ2IsVUFBVSxDQUFDWSxlQUFlO3dCQUM3QixNQUFNLElBQUl6RyxNQUNSLENBQUMsa0JBQWtCLEVBQUV1SSxRQUFRM0QsUUFBUSxDQUFDLEVBQUUsRUFBRStDLEtBQUtqQixJQUFJLEVBQUU7b0JBRXpEO29CQUVBLE1BQU0sQ0FBQzBFLE1BQU0sR0FBRyxNQUFNdEksR0FBRzJHLFdBQ3RCM0QsS0FBSyxDQUFDO3dCQUNMLENBQUMsR0FBR3ZHLFdBQVdvSyxXQUFXLENBQUM5RCxPQUFPMUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDLEVBQUVvRixRQUFRdEQsRUFBRTt3QkFDMUQsQ0FBQyxHQUFHMUYsV0FBV29LLFdBQVcsQ0FBQ2xELGNBQWN0RCxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUMsRUFBRStHO29CQUN6RCxHQUNDbkUsS0FBSyxDQUFDO29CQUNULElBQUlxRixPQUFPO3dCQUNUO29CQUNGO29CQUVBLE1BQU1RLFNBQVMsTUFBTTlJLEdBQUcyRyxXQUFXM0YsTUFBTSxDQUFDO3dCQUN4QyxDQUFDLEdBQUd2RSxXQUFXb0ssV0FBVyxDQUFDOUQsT0FBTzFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQyxFQUFFb0YsUUFBUXRELEVBQUU7d0JBQzFELENBQUMsR0FBRzFGLFdBQVdvSyxXQUFXLENBQUNsRCxjQUFjdEQsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDLEVBQUUrRztvQkFDekQ7b0JBQ0E3RyxRQUFRQyxHQUFHLENBQ1R6RSxNQUFNNk0sS0FBSyxDQUNULENBQUMsY0FBYyxFQUFFakMsVUFBVSxFQUFFLEVBQUU1RCxPQUFPMUMsS0FBSyxDQUFDLENBQUMsRUFBRW9GLFFBQVF0RCxFQUFFLENBQUMsSUFBSSxFQUFFd0IsY0FBY3RELEtBQUssQ0FBQyxDQUFDLEVBQUUrRyxVQUFVLE1BQU0sRUFBRTBCLFFBQVE7Z0JBR3ZIO1lBQ0Y7UUFDRjtJQUNGO0lBRUEsTUFBTUMsaUJBQWlCQyxJQUFZLEVBQUU7UUFDbkMsTUFBTUMsT0FBT2hOLE9BQU9pTixXQUFXLEdBQUc7UUFDbEMsSUFBSUMsVUFBVXpNLGFBQWF1TSxNQUFNRyxRQUFRO1FBRXpDLE1BQU1DLHFCQUFxQkYsUUFBUUcsT0FBTyxDQUFDO1FBQzNDLE1BQU1DLG1CQUFtQkosUUFBUUcsT0FBTyxDQUFDLE1BQU1EO1FBRS9DLElBQUlBLHVCQUF1QixDQUFDLEtBQUtFLHFCQUFxQixDQUFDLEdBQUc7WUFDeEQsTUFBTUMsYUFDSkwsUUFBUTNELEtBQUssQ0FBQyxHQUFHK0Qsb0JBQ2pCLE9BQ0FQLE9BQ0EsT0FDQUcsUUFBUTNELEtBQUssQ0FBQytEO1lBRWhCNU0sY0FBY3NNLE1BQU1PO1FBQ3RCLE9BQU87WUFDTCxNQUFNLElBQUl0TSxNQUFNO1FBQ2xCO0lBQ0Y7SUFFQSx1Q0FBdUM7SUFDdkMsTUFBYzhJLHFCQUNaaEcsRUFBUSxFQUNSK0MsTUFBYyxFQUNkMEMsT0FBc0IsRUFDdEI7UUFDQSxNQUFNZ0UsaUJBQWlCMUcsT0FBTzJHLE9BQU8sQ0FBQ3BLLE1BQU0sQ0FBQyxDQUFDcUssSUFBTUEsRUFBRTdFLElBQUksS0FBSztRQUUvRCxnQ0FBZ0M7UUFDaEMsTUFBTThFLGdCQUFnQkgsZUFBZW5LLE1BQU0sQ0FBQyxDQUFDdUssUUFDM0NBLE1BQU1yRCxPQUFPLENBQUNzRCxLQUFLLENBQUMsQ0FBQ2xGLFNBQVcsQ0FBQ0EsT0FBT2xFLFVBQVUsQ0FBQyxHQUFHcUMsT0FBTzFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFFeEUsSUFBSXVKLGNBQWM5SSxNQUFNLEtBQUssR0FBRztZQUM5QixPQUFPO1FBQ1Q7UUFFQSxJQUFJaUosY0FBYy9KLEdBQUcrQyxPQUFPMUMsS0FBSztRQUNqQyxLQUFLLE1BQU13SixTQUFTRCxjQUFlO1lBQ2pDLGtEQUFrRDtZQUNsRCxNQUFNSSxlQUFlSCxNQUFNckQsT0FBTyxDQUFDeUQsSUFBSSxDQUFDLENBQUNyRjtnQkFDdkMsTUFBTWpDLFFBQVFpQyxPQUFPM0YsS0FBSyxDQUFDLE1BQU0sQ0FBQyxFQUFFO2dCQUNwQyxPQUFPd0csUUFBUWUsT0FBTyxDQUFDN0QsTUFBTSxDQUFDdEIsS0FBSyxLQUFLO1lBQzFDO1lBQ0EsSUFBSTJJLGNBQWM7Z0JBQ2hCO1lBQ0Y7WUFFQUQsY0FBY0EsWUFBWUcsT0FBTyxDQUFDLENBQUNDO2dCQUNqQyxLQUFLLE1BQU12RixVQUFVaUYsTUFBTXJELE9BQU8sQ0FBRTtvQkFDbEMsTUFBTTdELFFBQVFpQyxPQUFPM0YsS0FBSyxDQUFDLE1BQU0sQ0FBQyxFQUFFO29CQUVwQyxJQUFJbUwsTUFBTUMsT0FBTyxDQUFDNUUsUUFBUWUsT0FBTyxDQUFDN0QsTUFBTSxDQUFDdEIsS0FBSyxHQUFHO3dCQUMvQzhJLEdBQUdHLE9BQU8sQ0FBQzFGLFFBQVFhLFFBQVFlLE9BQU8sQ0FBQzdELE1BQU0sQ0FBQ3RCLEtBQUs7b0JBQ2pELE9BQU87d0JBQ0w4SSxHQUFHSSxRQUFRLENBQUMzRixRQUFRYSxRQUFRZSxPQUFPLENBQUM3RCxNQUFNLENBQUN0QixLQUFLO29CQUNsRDtnQkFDRjtZQUNGO1FBQ0Y7UUFDQSxNQUFNLENBQUNnSCxZQUFZLEdBQUcsTUFBTTBCO1FBQzVCLE9BQU8xQjtJQUNUO0FBQ0Y7QUFDQSxPQUFPLE1BQU1tQyxpQkFBaUIsSUFBSXpOLHNCQUFzQiJ9