sonamu 0.8.26 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (684) hide show
  1. package/bin/cli.js +60 -13
  2. package/dist/_virtual/rolldown_runtime.js +39 -0
  3. package/dist/ai/agents/agent.d.ts +3 -3
  4. package/dist/ai/agents/agent.d.ts.map +1 -1
  5. package/dist/ai/agents/agent.js +76 -73
  6. package/dist/ai/agents/index.js +3 -3
  7. package/dist/ai/agents/types.d.ts +3 -3
  8. package/dist/ai/agents/types.d.ts.map +1 -1
  9. package/dist/ai/agents/types.js +1 -3
  10. package/dist/ai/index.js +3 -2
  11. package/dist/ai/providers/rtzr/api.js +25 -25
  12. package/dist/ai/providers/rtzr/error.js +25 -26
  13. package/dist/ai/providers/rtzr/index.js +5 -5
  14. package/dist/ai/providers/rtzr/model.d.ts +1 -1
  15. package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
  16. package/dist/ai/providers/rtzr/model.js +117 -133
  17. package/dist/ai/providers/rtzr/options.d.ts.map +1 -1
  18. package/dist/ai/providers/rtzr/options.js +35 -41
  19. package/dist/ai/providers/rtzr/provider.d.ts +1 -1
  20. package/dist/ai/providers/rtzr/provider.d.ts.map +1 -1
  21. package/dist/ai/providers/rtzr/provider.js +53 -51
  22. package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
  23. package/dist/ai/providers/rtzr/utils.js +84 -84
  24. package/dist/api/base-frame.d.ts +2 -2
  25. package/dist/api/base-frame.d.ts.map +1 -1
  26. package/dist/api/base-frame.js +29 -19
  27. package/dist/api/caster.d.ts +1 -1
  28. package/dist/api/caster.d.ts.map +1 -1
  29. package/dist/api/caster.js +51 -61
  30. package/dist/api/code-converters.d.ts +4 -3
  31. package/dist/api/code-converters.d.ts.map +1 -1
  32. package/dist/api/code-converters.js +226 -249
  33. package/dist/api/config.d.ts +17 -17
  34. package/dist/api/config.d.ts.map +1 -1
  35. package/dist/api/config.js +37 -30
  36. package/dist/api/context.d.ts +10 -10
  37. package/dist/api/context.d.ts.map +1 -1
  38. package/dist/api/context.js +8 -2
  39. package/dist/api/decorators.d.ts +8 -8
  40. package/dist/api/decorators.d.ts.map +1 -1
  41. package/dist/api/decorators.js +245 -268
  42. package/dist/api/index.js +39 -7
  43. package/dist/api/secret.js +22 -15
  44. package/dist/api/sonamu.d.ts +15 -15
  45. package/dist/api/sonamu.d.ts.map +1 -1
  46. package/dist/api/sonamu.js +1012 -1131
  47. package/dist/api/validator.js +88 -79
  48. package/dist/auth/auth-generator.d.ts.map +1 -1
  49. package/dist/auth/auth-generator.js +203 -200
  50. package/dist/auth/better-auth-entities.d.ts +2 -2
  51. package/dist/auth/better-auth-entities.d.ts.map +1 -1
  52. package/dist/auth/better-auth-entities.js +369 -429
  53. package/dist/auth/index.js +21 -6
  54. package/dist/auth/knex-adapter.d.ts +2 -2
  55. package/dist/auth/knex-adapter.d.ts.map +1 -1
  56. package/dist/auth/knex-adapter.js +153 -157
  57. package/dist/auth/plugins/entity-definitions/admin.d.ts +1 -1
  58. package/dist/auth/plugins/entity-definitions/admin.d.ts.map +1 -1
  59. package/dist/auth/plugins/entity-definitions/admin.js +58 -56
  60. package/dist/auth/plugins/entity-definitions/anonymous.d.ts +1 -1
  61. package/dist/auth/plugins/entity-definitions/anonymous.d.ts.map +1 -1
  62. package/dist/auth/plugins/entity-definitions/anonymous.js +20 -20
  63. package/dist/auth/plugins/entity-definitions/api-key.d.ts +1 -1
  64. package/dist/auth/plugins/entity-definitions/api-key.d.ts.map +1 -1
  65. package/dist/auth/plugins/entity-definitions/api-key.js +185 -196
  66. package/dist/auth/plugins/entity-definitions/index.d.ts +1 -1
  67. package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
  68. package/dist/auth/plugins/entity-definitions/index.js +26 -29
  69. package/dist/auth/plugins/entity-definitions/jwt.d.ts +1 -1
  70. package/dist/auth/plugins/entity-definitions/jwt.d.ts.map +1 -1
  71. package/dist/auth/plugins/entity-definitions/jwt.js +62 -64
  72. package/dist/auth/plugins/entity-definitions/organization.d.ts +1 -1
  73. package/dist/auth/plugins/entity-definitions/organization.d.ts.map +1 -1
  74. package/dist/auth/plugins/entity-definitions/organization.js +362 -421
  75. package/dist/auth/plugins/entity-definitions/passkey.d.ts +1 -1
  76. package/dist/auth/plugins/entity-definitions/passkey.d.ts.map +1 -1
  77. package/dist/auth/plugins/entity-definitions/passkey.js +115 -126
  78. package/dist/auth/plugins/entity-definitions/phone-number.d.ts +1 -1
  79. package/dist/auth/plugins/entity-definitions/phone-number.d.ts.map +1 -1
  80. package/dist/auth/plugins/entity-definitions/phone-number.js +31 -40
  81. package/dist/auth/plugins/entity-definitions/sso.d.ts +1 -1
  82. package/dist/auth/plugins/entity-definitions/sso.d.ts.map +1 -1
  83. package/dist/auth/plugins/entity-definitions/sso.js +94 -107
  84. package/dist/auth/plugins/entity-definitions/two-factor.d.ts +1 -1
  85. package/dist/auth/plugins/entity-definitions/two-factor.d.ts.map +1 -1
  86. package/dist/auth/plugins/entity-definitions/two-factor.js +78 -92
  87. package/dist/auth/plugins/entity-definitions/types.d.ts +1 -1
  88. package/dist/auth/plugins/entity-definitions/types.d.ts.map +1 -1
  89. package/dist/auth/plugins/entity-definitions/types.js +1 -10
  90. package/dist/auth/plugins/entity-definitions/username.d.ts +1 -1
  91. package/dist/auth/plugins/entity-definitions/username.d.ts.map +1 -1
  92. package/dist/auth/plugins/entity-definitions/username.js +31 -40
  93. package/dist/auth/plugins/index.js +12 -3
  94. package/dist/auth/plugins/wrappers/admin.d.ts +2 -2
  95. package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
  96. package/dist/auth/plugins/wrappers/admin.js +28 -29
  97. package/dist/auth/plugins/wrappers/anonymous.d.ts +2 -1
  98. package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -1
  99. package/dist/auth/plugins/wrappers/anonymous.js +23 -22
  100. package/dist/auth/plugins/wrappers/api-key.d.ts +2 -1
  101. package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -1
  102. package/dist/auth/plugins/wrappers/api-key.js +39 -34
  103. package/dist/auth/plugins/wrappers/index.js +11 -11
  104. package/dist/auth/plugins/wrappers/jwt.d.ts +2 -1
  105. package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -1
  106. package/dist/auth/plugins/wrappers/jwt.js +31 -26
  107. package/dist/auth/plugins/wrappers/organization.d.ts +2 -1
  108. package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -1
  109. package/dist/auth/plugins/wrappers/organization.js +65 -62
  110. package/dist/auth/plugins/wrappers/passkey.d.ts +2 -1
  111. package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -1
  112. package/dist/auth/plugins/wrappers/passkey.js +33 -28
  113. package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
  114. package/dist/auth/plugins/wrappers/phone-number.js +26 -23
  115. package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -1
  116. package/dist/auth/plugins/wrappers/sso.js +37 -31
  117. package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
  118. package/dist/auth/plugins/wrappers/two-factor.js +31 -28
  119. package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
  120. package/dist/auth/plugins/wrappers/username.js +23 -23
  121. package/dist/bin/build-config.js +31 -31
  122. package/dist/bin/cli.js +1063 -1204
  123. package/dist/bin/fixture.d.ts.map +1 -1
  124. package/dist/bin/fixture.js +266 -259
  125. package/dist/bin/hmr-hook-register.d.ts.map +1 -1
  126. package/dist/bin/hmr-hook-register.js +19 -18
  127. package/dist/bin/test-command.d.ts.map +1 -1
  128. package/dist/bin/test-command.js +180 -177
  129. package/dist/bin/ts-loader-register.js +13 -6
  130. package/dist/bin/ts-loader-registration.d.ts.map +1 -1
  131. package/dist/bin/ts-loader-registration.js +28 -38
  132. package/dist/cache/cache-manager.d.ts +1 -1
  133. package/dist/cache/cache-manager.d.ts.map +1 -1
  134. package/dist/cache/cache-manager.js +20 -15
  135. package/dist/cache/decorator.d.ts +1 -1
  136. package/dist/cache/decorator.d.ts.map +1 -1
  137. package/dist/cache/decorator.js +84 -76
  138. package/dist/cache/drivers.js +21 -34
  139. package/dist/cache/index.js +10 -7
  140. package/dist/cache/types.d.ts +2 -2
  141. package/dist/cache/types.d.ts.map +1 -1
  142. package/dist/cache/types.js +1 -6
  143. package/dist/cache-control/cache-control.d.ts +2 -2
  144. package/dist/cache-control/cache-control.d.ts.map +1 -1
  145. package/dist/cache-control/cache-control.js +106 -122
  146. package/dist/cache-control/types.d.ts +2 -2
  147. package/dist/cache-control/types.d.ts.map +1 -1
  148. package/dist/cache-control/types.js +1 -19
  149. package/dist/compress/compress.d.ts +1 -1
  150. package/dist/compress/compress.d.ts.map +1 -1
  151. package/dist/compress/compress.js +58 -56
  152. package/dist/compress/index.js +7 -2
  153. package/dist/compress/types.js +1 -11
  154. package/dist/cone/cone-generator.d.ts +1 -1
  155. package/dist/cone/cone-generator.d.ts.map +1 -1
  156. package/dist/cone/cone-generator.js +216 -219
  157. package/dist/database/_batch_update.d.ts +1 -1
  158. package/dist/database/_batch_update.d.ts.map +1 -1
  159. package/dist/database/_batch_update.js +107 -102
  160. package/dist/database/base-model.d.ts +8 -9
  161. package/dist/database/base-model.d.ts.map +1 -1
  162. package/dist/database/base-model.js +371 -392
  163. package/dist/database/base-model.types.d.ts +5 -5
  164. package/dist/database/base-model.types.d.ts.map +1 -1
  165. package/dist/database/base-model.types.js +1 -20
  166. package/dist/database/db.d.ts +5 -2
  167. package/dist/database/db.d.ts.map +1 -1
  168. package/dist/database/db.js +185 -171
  169. package/dist/database/knex.d.ts +1 -1
  170. package/dist/database/knex.d.ts.map +1 -1
  171. package/dist/database/knex.js +48 -42
  172. package/dist/database/puri-subset.types.d.ts +6 -7
  173. package/dist/database/puri-subset.types.d.ts.map +1 -1
  174. package/dist/database/puri-subset.types.js +1 -16
  175. package/dist/database/puri-wrapper.d.ts +6 -6
  176. package/dist/database/puri-wrapper.d.ts.map +1 -1
  177. package/dist/database/puri-wrapper.js +99 -101
  178. package/dist/database/puri.d.ts +4 -5
  179. package/dist/database/puri.d.ts.map +1 -1
  180. package/dist/database/puri.js +1021 -1227
  181. package/dist/database/puri.types.d.ts +6 -6
  182. package/dist/database/puri.types.d.ts.map +1 -1
  183. package/dist/database/puri.types.js +15 -6
  184. package/dist/database/transaction-context.d.ts +2 -2
  185. package/dist/database/transaction-context.d.ts.map +1 -1
  186. package/dist/database/transaction-context.js +22 -13
  187. package/dist/database/upsert-builder.d.ts +3 -3
  188. package/dist/database/upsert-builder.d.ts.map +1 -1
  189. package/dist/database/upsert-builder.js +405 -465
  190. package/dist/dict/en.js +72 -74
  191. package/dist/dict/index.js +13 -13
  192. package/dist/dict/ko.js +72 -74
  193. package/dist/dict/rc-keys.js +150 -168
  194. package/dist/dict/sd.d.ts +3 -1
  195. package/dist/dict/sd.d.ts.map +1 -1
  196. package/dist/dict/sd.js +54 -40
  197. package/dist/dict/sonamu-dictionary.d.ts +1 -1
  198. package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
  199. package/dist/dict/sonamu-dictionary.js +887 -955
  200. package/dist/dict/types.js +1 -7
  201. package/dist/dict/utils.js +26 -24
  202. package/dist/entity/entity-manager.d.ts +9 -9
  203. package/dist/entity/entity-manager.d.ts.map +1 -1
  204. package/dist/entity/entity-manager.js +226 -223
  205. package/dist/entity/entity-template-cone.d.ts +1 -1
  206. package/dist/entity/entity-template-cone.d.ts.map +1 -1
  207. package/dist/entity/entity-template-cone.js +152 -151
  208. package/dist/entity/entity.d.ts.map +1 -1
  209. package/dist/entity/entity.js +952 -1089
  210. package/dist/exceptions/error-handler.d.ts +1 -1
  211. package/dist/exceptions/error-handler.d.ts.map +1 -1
  212. package/dist/exceptions/error-handler.js +32 -27
  213. package/dist/exceptions/so-exceptions.d.ts +1 -1
  214. package/dist/exceptions/so-exceptions.d.ts.map +1 -1
  215. package/dist/exceptions/so-exceptions.js +61 -68
  216. package/dist/filter/index.js +9 -3
  217. package/dist/filter/types.js +92 -88
  218. package/dist/filter/utils.d.ts +1 -1
  219. package/dist/filter/utils.d.ts.map +1 -1
  220. package/dist/filter/utils.js +147 -161
  221. package/dist/index.js +87 -40
  222. package/dist/logger/category.d.ts.map +1 -1
  223. package/dist/logger/category.js +30 -29
  224. package/dist/logger/configure.d.ts.map +1 -1
  225. package/dist/logger/configure.js +83 -107
  226. package/dist/migration/code-generation.d.ts +2 -2
  227. package/dist/migration/code-generation.d.ts.map +1 -1
  228. package/dist/migration/code-generation.js +1385 -1578
  229. package/dist/migration/migration-set.d.ts +1 -1
  230. package/dist/migration/migration-set.d.ts.map +1 -1
  231. package/dist/migration/migration-set.js +177 -227
  232. package/dist/migration/migrator.d.ts +4 -3
  233. package/dist/migration/migrator.d.ts.map +1 -1
  234. package/dist/migration/migrator.js +340 -345
  235. package/dist/migration/postgresql-schema-reader.d.ts +2 -2
  236. package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
  237. package/dist/migration/postgresql-schema-reader.js +506 -564
  238. package/dist/migration/slack-confirm.d.ts +2 -2
  239. package/dist/migration/slack-confirm.d.ts.map +1 -1
  240. package/dist/migration/slack-confirm.js +205 -193
  241. package/dist/migration/types.d.ts +2 -2
  242. package/dist/migration/types.d.ts.map +1 -1
  243. package/dist/migration/types.js +1 -3
  244. package/dist/naite/messaging-types.d.ts +1 -0
  245. package/dist/naite/messaging-types.d.ts.map +1 -1
  246. package/dist/naite/messaging-types.js +1 -7
  247. package/dist/naite/naite-reporter.d.ts +2 -2
  248. package/dist/naite/naite-reporter.d.ts.map +1 -1
  249. package/dist/naite/naite-reporter.js +127 -120
  250. package/dist/naite/naite.d.ts +3 -2
  251. package/dist/naite/naite.d.ts.map +1 -1
  252. package/dist/naite/naite.js +266 -300
  253. package/dist/ssr/index.d.ts +2 -2
  254. package/dist/ssr/index.d.ts.map +1 -1
  255. package/dist/ssr/index.js +13 -3
  256. package/dist/ssr/registry.d.ts +1 -1
  257. package/dist/ssr/registry.d.ts.map +1 -1
  258. package/dist/ssr/registry.js +45 -37
  259. package/dist/ssr/renderer.d.ts +4 -4
  260. package/dist/ssr/renderer.d.ts.map +1 -1
  261. package/dist/ssr/renderer.js +84 -91
  262. package/dist/ssr/types.d.ts +2 -2
  263. package/dist/ssr/types.d.ts.map +1 -1
  264. package/dist/ssr/types.js +1 -3
  265. package/dist/storage/base-file.js +54 -41
  266. package/dist/storage/buffered-file.d.ts +2 -2
  267. package/dist/storage/buffered-file.d.ts.map +1 -1
  268. package/dist/storage/buffered-file.js +51 -44
  269. package/dist/storage/drivers.d.ts +2 -2
  270. package/dist/storage/drivers.d.ts.map +1 -1
  271. package/dist/storage/drivers.js +12 -7
  272. package/dist/storage/index.js +14 -7
  273. package/dist/storage/s3-driver.d.ts +2 -2
  274. package/dist/storage/s3-driver.d.ts.map +1 -1
  275. package/dist/storage/s3-driver.js +52 -48
  276. package/dist/storage/storage-manager.d.ts +2 -2
  277. package/dist/storage/storage-manager.d.ts.map +1 -1
  278. package/dist/storage/storage-manager.js +33 -25
  279. package/dist/storage/types.d.ts +2 -2
  280. package/dist/storage/types.d.ts.map +1 -1
  281. package/dist/storage/types.js +1 -5
  282. package/dist/storage/uploaded-file.d.ts +1 -1
  283. package/dist/storage/uploaded-file.d.ts.map +1 -1
  284. package/dist/storage/uploaded-file.js +45 -35
  285. package/dist/stream/index.js +7 -2
  286. package/dist/stream/sse.d.ts +2 -2
  287. package/dist/stream/sse.d.ts.map +1 -1
  288. package/dist/stream/sse.js +72 -67
  289. package/dist/syncer/api-parser.d.ts +1 -1
  290. package/dist/syncer/api-parser.d.ts.map +1 -1
  291. package/dist/syncer/api-parser.js +224 -245
  292. package/dist/syncer/checksum.d.ts +1 -1
  293. package/dist/syncer/checksum.d.ts.map +1 -1
  294. package/dist/syncer/checksum.js +86 -72
  295. package/dist/syncer/code-generator.d.ts +2 -2
  296. package/dist/syncer/code-generator.d.ts.map +1 -1
  297. package/dist/syncer/code-generator.js +154 -160
  298. package/dist/syncer/entity-operations.d.ts +1 -1
  299. package/dist/syncer/entity-operations.d.ts.map +1 -1
  300. package/dist/syncer/entity-operations.js +63 -54
  301. package/dist/syncer/file-patterns.d.ts +1 -1
  302. package/dist/syncer/file-patterns.d.ts.map +1 -1
  303. package/dist/syncer/file-patterns.js +38 -38
  304. package/dist/syncer/index.js +19 -8
  305. package/dist/syncer/module-loader.d.ts +5 -5
  306. package/dist/syncer/module-loader.d.ts.map +1 -1
  307. package/dist/syncer/module-loader.js +83 -78
  308. package/dist/syncer/syncer-actions.d.ts +2 -2
  309. package/dist/syncer/syncer-actions.d.ts.map +1 -1
  310. package/dist/syncer/syncer-actions.js +76 -91
  311. package/dist/syncer/syncer.d.ts +7 -6
  312. package/dist/syncer/syncer.d.ts.map +1 -1
  313. package/dist/syncer/syncer.js +426 -492
  314. package/dist/tasks/decorator.d.ts +3 -3
  315. package/dist/tasks/decorator.d.ts.map +1 -1
  316. package/dist/tasks/decorator.js +32 -28
  317. package/dist/tasks/step-wrapper.d.ts +1 -1
  318. package/dist/tasks/step-wrapper.d.ts.map +1 -1
  319. package/dist/tasks/step-wrapper.js +42 -41
  320. package/dist/tasks/workflow-manager.d.ts +2 -2
  321. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  322. package/dist/tasks/workflow-manager.js +192 -221
  323. package/dist/template/entity-converter.d.ts +1 -1
  324. package/dist/template/entity-converter.d.ts.map +1 -1
  325. package/dist/template/entity-converter.js +103 -103
  326. package/dist/template/helpers.d.ts.map +1 -1
  327. package/dist/template/helpers.js +163 -163
  328. package/dist/template/implementations/entity.template.d.ts +1 -1
  329. package/dist/template/implementations/entity.template.d.ts.map +1 -1
  330. package/dist/template/implementations/entity.template.js +76 -85
  331. package/dist/template/implementations/entry-server.template.d.ts +1 -1
  332. package/dist/template/implementations/entry-server.template.d.ts.map +1 -1
  333. package/dist/template/implementations/entry-server.template.js +32 -27
  334. package/dist/template/implementations/generated.template.d.ts +1 -1
  335. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  336. package/dist/template/implementations/generated.template.js +254 -275
  337. package/dist/template/implementations/generated_http.template.d.ts +2 -2
  338. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  339. package/dist/template/implementations/generated_http.template.js +114 -133
  340. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  341. package/dist/template/implementations/generated_sso.template.js +249 -275
  342. package/dist/template/implementations/init_types.template.d.ts +1 -1
  343. package/dist/template/implementations/init_types.template.d.ts.map +1 -1
  344. package/dist/template/implementations/init_types.template.js +40 -34
  345. package/dist/template/implementations/model.template.d.ts +1 -1
  346. package/dist/template/implementations/model.template.d.ts.map +1 -1
  347. package/dist/template/implementations/model.template.js +56 -53
  348. package/dist/template/implementations/model_test.template.d.ts +1 -1
  349. package/dist/template/implementations/model_test.template.d.ts.map +1 -1
  350. package/dist/template/implementations/model_test.template.js +32 -24
  351. package/dist/template/implementations/queries.template.d.ts +1 -1
  352. package/dist/template/implementations/queries.template.d.ts.map +1 -1
  353. package/dist/template/implementations/queries.template.js +84 -89
  354. package/dist/template/implementations/sd.template.d.ts +1 -1
  355. package/dist/template/implementations/sd.template.d.ts.map +1 -1
  356. package/dist/template/implementations/sd.template.js +137 -144
  357. package/dist/template/implementations/services.template.d.ts +1 -1
  358. package/dist/template/implementations/services.template.d.ts.map +1 -1
  359. package/dist/template/implementations/services.template.js +164 -189
  360. package/dist/template/implementations/view_form.template.d.ts +1 -1
  361. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  362. package/dist/template/implementations/view_form.template.js +258 -285
  363. package/dist/template/implementations/view_id_all_select.template.d.ts +1 -1
  364. package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -1
  365. package/dist/template/implementations/view_id_all_select.template.js +31 -25
  366. package/dist/template/implementations/view_list.template.d.ts +1 -1
  367. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  368. package/dist/template/implementations/view_list.template.js +304 -355
  369. package/dist/template/implementations/view_search_input.template.d.ts +1 -1
  370. package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
  371. package/dist/template/implementations/view_search_input.template.js +31 -27
  372. package/dist/template/index.js +21 -7
  373. package/dist/template/template-manager.d.ts +1 -1
  374. package/dist/template/template-manager.d.ts.map +1 -1
  375. package/dist/template/template-manager.js +132 -123
  376. package/dist/template/template-types.js +8 -6
  377. package/dist/template/template.d.ts +2 -2
  378. package/dist/template/template.d.ts.map +1 -1
  379. package/dist/template/template.js +73 -68
  380. package/dist/template/zod-converter.d.ts.map +1 -1
  381. package/dist/template/zod-converter.js +603 -657
  382. package/dist/testing/_relation-graph.d.ts +1 -1
  383. package/dist/testing/_relation-graph.d.ts.map +1 -1
  384. package/dist/testing/_relation-graph.js +93 -88
  385. package/dist/testing/bootstrap.d.ts +22 -13
  386. package/dist/testing/bootstrap.d.ts.map +1 -1
  387. package/dist/testing/bootstrap.js +114 -114
  388. package/dist/testing/data-explorer.d.ts +3 -3
  389. package/dist/testing/data-explorer.d.ts.map +1 -1
  390. package/dist/testing/data-explorer.js +237 -265
  391. package/dist/testing/dev-test-routes.d.ts +2 -2
  392. package/dist/testing/dev-test-routes.d.ts.map +1 -1
  393. package/dist/testing/dev-test-routes.js +258 -249
  394. package/dist/testing/dev-vitest-manager.d.ts +1 -1
  395. package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
  396. package/dist/testing/dev-vitest-manager.js +514 -539
  397. package/dist/testing/faker-mappings.js +422 -420
  398. package/dist/testing/fixture-generator.d.ts +3 -3
  399. package/dist/testing/fixture-generator.d.ts.map +1 -1
  400. package/dist/testing/fixture-generator.js +1216 -1346
  401. package/dist/testing/fixture-loader.js +26 -25
  402. package/dist/testing/fixture-manager.d.ts +3 -3
  403. package/dist/testing/fixture-manager.d.ts.map +1 -1
  404. package/dist/testing/fixture-manager.js +706 -776
  405. package/dist/testing/global-setup.js +53 -49
  406. package/dist/testing/index.js +19 -11
  407. package/dist/testing/naite-vitest-reporter.js +18 -13
  408. package/dist/testing/parallel-db-manager.d.ts +1 -1
  409. package/dist/testing/parallel-db-manager.d.ts.map +1 -1
  410. package/dist/testing/parallel-db-manager.js +63 -78
  411. package/dist/testing/vitest-helpers.d.ts +1 -1
  412. package/dist/testing/vitest-helpers.d.ts.map +1 -1
  413. package/dist/testing/vitest-helpers.js +37 -33
  414. package/dist/types/types.d.ts +28 -28
  415. package/dist/types/types.d.ts.map +1 -1
  416. package/dist/types/types.js +764 -890
  417. package/dist/ui/ai-api.d.ts +1 -1
  418. package/dist/ui/ai-api.d.ts.map +1 -1
  419. package/dist/ui/ai-api.js +52 -42
  420. package/dist/ui/ai-client.d.ts +1 -2
  421. package/dist/ui/ai-client.d.ts.map +1 -1
  422. package/dist/ui/ai-client.js +353 -388
  423. package/dist/ui/api.d.ts +1 -1
  424. package/dist/ui/api.d.ts.map +1 -1
  425. package/dist/ui/api.js +903 -1145
  426. package/dist/ui/cdd-service.d.ts +1 -1
  427. package/dist/ui/cdd-service.d.ts.map +1 -1
  428. package/dist/ui/cdd-service.js +406 -407
  429. package/dist/ui/cdd-types.js +1 -3
  430. package/dist/ui-web/assets/index-C-Zz-wYg.css +1 -0
  431. package/dist/ui-web/assets/index-DejDON8K.js +238 -0
  432. package/dist/ui-web/index.html +3 -3
  433. package/dist/utils/async-utils.js +57 -45
  434. package/dist/utils/console-util.d.ts.map +1 -1
  435. package/dist/utils/console-util.js +104 -87
  436. package/dist/utils/controller.js +26 -19
  437. package/dist/utils/esm-utils.js +49 -38
  438. package/dist/utils/formatter.d.ts +1 -2
  439. package/dist/utils/formatter.d.ts.map +1 -1
  440. package/dist/utils/formatter.js +89 -115
  441. package/dist/utils/fs-utils.d.ts.map +1 -1
  442. package/dist/utils/fs-utils.js +68 -65
  443. package/dist/utils/lodash-able.js +11 -4
  444. package/dist/utils/model.d.ts +1 -1
  445. package/dist/utils/model.d.ts.map +1 -1
  446. package/dist/utils/model.js +21 -19
  447. package/dist/utils/object-utils.js +148 -186
  448. package/dist/utils/path-utils.js +67 -57
  449. package/dist/utils/process-utils.d.ts.map +1 -1
  450. package/dist/utils/process-utils.js +37 -31
  451. package/dist/utils/sql-parser.d.ts +1 -1
  452. package/dist/utils/sql-parser.d.ts.map +1 -1
  453. package/dist/utils/sql-parser.js +40 -40
  454. package/dist/utils/type-utils.js +44 -43
  455. package/dist/utils/utils.d.ts +2 -3
  456. package/dist/utils/utils.d.ts.map +1 -1
  457. package/dist/utils/utils.js +81 -93
  458. package/dist/utils/zod-error.d.ts +1 -1
  459. package/dist/utils/zod-error.d.ts.map +1 -1
  460. package/dist/utils/zod-error.js +24 -17
  461. package/dist/vector/chunking.d.ts +1 -1
  462. package/dist/vector/chunking.d.ts.map +1 -1
  463. package/dist/vector/chunking.js +100 -94
  464. package/dist/vector/config.d.ts +1 -1
  465. package/dist/vector/config.d.ts.map +1 -1
  466. package/dist/vector/config.js +76 -78
  467. package/dist/vector/embedding.d.ts +1 -1
  468. package/dist/vector/embedding.d.ts.map +1 -1
  469. package/dist/vector/embedding.js +128 -125
  470. package/dist/vector/index.js +5 -5
  471. package/dist/vector/types.js +1 -5
  472. package/package.json +31 -36
  473. package/src/ai/agents/agent.ts +12 -5
  474. package/src/ai/agents/types.ts +5 -5
  475. package/src/ai/providers/rtzr/model.ts +8 -10
  476. package/src/ai/providers/rtzr/options.ts +2 -1
  477. package/src/ai/providers/rtzr/provider.ts +5 -3
  478. package/src/ai/providers/rtzr/utils.ts +2 -7
  479. package/src/api/__tests__/config.test.ts +15 -8
  480. package/src/api/base-frame.ts +5 -3
  481. package/src/api/caster.ts +7 -6
  482. package/src/api/code-converters.ts +23 -26
  483. package/src/api/config.ts +23 -17
  484. package/src/api/context.ts +18 -11
  485. package/src/api/decorators.ts +17 -18
  486. package/src/api/sonamu.ts +44 -49
  487. package/src/auth/auth-generator.ts +4 -6
  488. package/src/auth/better-auth-entities.ts +3 -2
  489. package/src/auth/knex-adapter.ts +6 -5
  490. package/src/auth/plugins/entity-definitions/admin.ts +1 -1
  491. package/src/auth/plugins/entity-definitions/anonymous.ts +1 -1
  492. package/src/auth/plugins/entity-definitions/api-key.ts +1 -1
  493. package/src/auth/plugins/entity-definitions/index.ts +1 -1
  494. package/src/auth/plugins/entity-definitions/jwt.ts +1 -1
  495. package/src/auth/plugins/entity-definitions/organization.ts +1 -1
  496. package/src/auth/plugins/entity-definitions/passkey.ts +1 -1
  497. package/src/auth/plugins/entity-definitions/phone-number.ts +1 -1
  498. package/src/auth/plugins/entity-definitions/sso.ts +1 -1
  499. package/src/auth/plugins/entity-definitions/two-factor.ts +1 -1
  500. package/src/auth/plugins/entity-definitions/types.ts +1 -1
  501. package/src/auth/plugins/entity-definitions/username.ts +1 -1
  502. package/src/auth/plugins/wrappers/admin.ts +3 -1
  503. package/src/auth/plugins/wrappers/anonymous.ts +3 -1
  504. package/src/auth/plugins/wrappers/api-key.ts +3 -1
  505. package/src/auth/plugins/wrappers/jwt.ts +3 -1
  506. package/src/auth/plugins/wrappers/organization.ts +3 -1
  507. package/src/auth/plugins/wrappers/passkey.ts +3 -1
  508. package/src/auth/plugins/wrappers/phone-number.ts +3 -1
  509. package/src/auth/plugins/wrappers/sso.ts +2 -1
  510. package/src/auth/plugins/wrappers/two-factor.ts +3 -1
  511. package/src/auth/plugins/wrappers/username.ts +3 -1
  512. package/src/bin/__tests__/ts-loader-register.test.ts +7 -12
  513. package/src/bin/build-config.ts +3 -3
  514. package/src/bin/cli.ts +27 -25
  515. package/src/bin/fixture.ts +4 -2
  516. package/src/bin/hmr-hook-register.ts +1 -0
  517. package/src/bin/test-command.ts +4 -2
  518. package/src/bin/ts-loader-registration.ts +6 -22
  519. package/src/cache/cache-manager.ts +2 -1
  520. package/src/cache/decorator.ts +2 -2
  521. package/src/cache/types.ts +3 -3
  522. package/src/cache-control/cache-control.ts +3 -2
  523. package/src/cache-control/types.ts +2 -2
  524. package/src/compress/compress.ts +1 -1
  525. package/src/cone/cone-generator.ts +5 -3
  526. package/src/database/_batch_update.ts +1 -1
  527. package/src/database/base-model.ts +20 -14
  528. package/src/database/base-model.types.ts +12 -11
  529. package/src/database/db.ts +56 -21
  530. package/src/database/knex.ts +2 -2
  531. package/src/database/puri-subset.test-d.ts +33 -32
  532. package/src/database/puri-subset.types.ts +6 -7
  533. package/src/database/puri-wrapper.ts +29 -26
  534. package/src/database/puri.ts +36 -34
  535. package/src/database/puri.types.test-d.ts +6 -5
  536. package/src/database/puri.types.ts +9 -12
  537. package/src/database/transaction-context.ts +2 -2
  538. package/src/database/upsert-builder.ts +17 -10
  539. package/src/dict/sd.ts +17 -4
  540. package/src/dict/sonamu-dictionary.ts +23 -17
  541. package/src/entity/entity-manager.ts +9 -7
  542. package/src/entity/entity-template-cone.ts +10 -3
  543. package/src/entity/entity.ts +20 -16
  544. package/src/exceptions/error-handler.ts +2 -1
  545. package/src/exceptions/so-exceptions.ts +1 -1
  546. package/src/filter/utils.ts +3 -2
  547. package/src/logger/category.ts +1 -0
  548. package/src/logger/configure.ts +5 -5
  549. package/src/migration/__tests__/code-generation.search-text.test.ts +2 -3
  550. package/src/migration/code-generation.ts +26 -25
  551. package/src/migration/migration-set.ts +16 -18
  552. package/src/migration/migrator.ts +38 -33
  553. package/src/migration/postgresql-schema-reader.ts +12 -12
  554. package/src/migration/slack-confirm.ts +5 -4
  555. package/src/migration/types.ts +2 -2
  556. package/src/naite/messaging-types.ts +1 -1
  557. package/src/naite/naite-reporter.ts +5 -3
  558. package/src/naite/naite.ts +12 -7
  559. package/src/shared/app.shared.ts.txt +2 -2
  560. package/src/shared/web.shared.ts.txt +2 -2
  561. package/src/skills/AGENTS.md +19 -18
  562. package/src/skills/commands/sonamu-skills.md +9 -9
  563. package/src/skills/sonamu/SKILL.md +111 -104
  564. package/src/skills/sonamu/ai-agents.md +27 -26
  565. package/src/skills/sonamu/api.md +81 -69
  566. package/src/skills/sonamu/auth-migration.md +13 -27
  567. package/src/skills/sonamu/auth-plugins.md +41 -31
  568. package/src/skills/sonamu/auth.md +30 -24
  569. package/src/skills/sonamu/cdd.md +26 -17
  570. package/src/skills/sonamu/cone.md +50 -50
  571. package/src/skills/sonamu/config.md +74 -51
  572. package/src/skills/sonamu/create-sonamu.md +31 -19
  573. package/src/skills/sonamu/database.md +43 -26
  574. package/src/skills/sonamu/entity-basic.md +61 -61
  575. package/src/skills/sonamu/entity-relations.md +84 -80
  576. package/src/skills/sonamu/entity-validation-checklist.md +19 -15
  577. package/src/skills/sonamu/fixture-cli.md +52 -30
  578. package/src/skills/sonamu/framework-change.md +9 -7
  579. package/src/skills/sonamu/frontend.md +64 -82
  580. package/src/skills/sonamu/i18n.md +45 -37
  581. package/src/skills/sonamu/migration.md +54 -31
  582. package/src/skills/sonamu/model.md +98 -66
  583. package/src/skills/sonamu/naite.md +34 -32
  584. package/src/skills/sonamu/project-init.md +28 -8
  585. package/src/skills/sonamu/puri.md +82 -91
  586. package/src/skills/sonamu/scaffolding.md +44 -32
  587. package/src/skills/sonamu/skill-contribution.md +50 -45
  588. package/src/skills/sonamu/subset.md +13 -13
  589. package/src/skills/sonamu/tasks.md +73 -58
  590. package/src/skills/sonamu/testing-devrunner.md +56 -36
  591. package/src/skills/sonamu/testing.md +23 -58
  592. package/src/skills/sonamu/upsert.md +32 -31
  593. package/src/skills/sonamu/vector.md +37 -36
  594. package/src/ssr/index.ts +2 -12
  595. package/src/ssr/registry.ts +1 -1
  596. package/src/ssr/renderer.ts +7 -5
  597. package/src/ssr/types.ts +2 -2
  598. package/src/storage/buffered-file.ts +4 -2
  599. package/src/storage/drivers.ts +3 -2
  600. package/src/storage/s3-driver.ts +7 -4
  601. package/src/storage/storage-manager.ts +3 -2
  602. package/src/storage/types.ts +3 -2
  603. package/src/storage/uploaded-file.ts +1 -1
  604. package/src/stream/sse.ts +2 -2
  605. package/src/syncer/api-parser.ts +8 -5
  606. package/src/syncer/checksum.ts +9 -5
  607. package/src/syncer/code-generator.ts +16 -8
  608. package/src/syncer/entity-operations.ts +5 -3
  609. package/src/syncer/file-patterns.ts +2 -1
  610. package/src/syncer/module-loader.ts +9 -6
  611. package/src/syncer/syncer-actions.ts +5 -3
  612. package/src/syncer/syncer.ts +18 -24
  613. package/src/tasks/decorator.ts +10 -8
  614. package/src/tasks/step-wrapper.ts +1 -1
  615. package/src/tasks/workflow-manager.ts +18 -15
  616. package/src/template/__tests__/generated.template.search-text.test.ts +1 -0
  617. package/src/template/entity-converter.ts +4 -2
  618. package/src/template/generated.template.test-d.ts +2 -1
  619. package/src/template/helpers.ts +5 -2
  620. package/src/template/implementations/entity.template.ts +9 -8
  621. package/src/template/implementations/entry-server.template.ts +1 -1
  622. package/src/template/implementations/generated.template.ts +21 -29
  623. package/src/template/implementations/generated_http.template.ts +9 -6
  624. package/src/template/implementations/generated_sso.template.ts +6 -4
  625. package/src/template/implementations/init_types.template.ts +3 -2
  626. package/src/template/implementations/model.template.ts +4 -2
  627. package/src/template/implementations/model_test.template.ts +3 -2
  628. package/src/template/implementations/queries.template.ts +6 -14
  629. package/src/template/implementations/sd.template.ts +4 -2
  630. package/src/template/implementations/services.template.ts +7 -11
  631. package/src/template/implementations/view_form.template.ts +5 -3
  632. package/src/template/implementations/view_id_all_select.template.ts +3 -2
  633. package/src/template/implementations/view_list.template.ts +7 -5
  634. package/src/template/implementations/view_search_input.template.ts +3 -2
  635. package/src/template/template-manager.ts +4 -3
  636. package/src/template/template.ts +4 -3
  637. package/src/template/zod-converter.ts +10 -7
  638. package/src/testing/__tests__/dev-test-routes.test.ts +3 -2
  639. package/src/testing/__tests__/dev-vitest-manager.test.ts +1 -0
  640. package/src/testing/_relation-graph.ts +2 -2
  641. package/src/testing/bootstrap.ts +55 -27
  642. package/src/testing/data-explorer.ts +5 -4
  643. package/src/testing/dev-test-routes.ts +8 -5
  644. package/src/testing/dev-vitest-manager.ts +13 -12
  645. package/src/testing/fixture-generator.ts +11 -17
  646. package/src/testing/fixture-manager.ts +21 -17
  647. package/src/testing/parallel-db-manager.ts +2 -1
  648. package/src/testing/vitest-helpers.ts +2 -1
  649. package/src/types/__tests__/entity-json-schema-search-text.test.ts +1 -0
  650. package/src/types/types.ts +8 -8
  651. package/src/typings/knex.d.ts +4 -4
  652. package/src/ui/ai-api.ts +5 -3
  653. package/src/ui/ai-client.ts +6 -5
  654. package/src/ui/api.ts +25 -23
  655. package/src/ui/cdd-service.ts +12 -11
  656. package/src/utils/console-util.ts +3 -1
  657. package/src/utils/formatter.ts +94 -102
  658. package/src/utils/fs-utils.ts +2 -1
  659. package/src/utils/model.ts +2 -2
  660. package/src/utils/object-utils.ts +3 -3
  661. package/src/utils/process-utils.ts +2 -1
  662. package/src/utils/sql-parser.ts +10 -1
  663. package/src/utils/type-utils.ts +3 -3
  664. package/src/utils/utils.ts +9 -7
  665. package/src/utils/zod-error.ts +1 -1
  666. package/src/vector/chunking.ts +1 -1
  667. package/src/vector/config.ts +1 -1
  668. package/src/vector/embedding.ts +11 -9
  669. package/tsdown.api.config.ts +50 -0
  670. package/.swcrc.project-default +0 -18
  671. package/dist/api/__tests__/config.test.js +0 -189
  672. package/dist/bin/__tests__/test-command.test.js +0 -112
  673. package/dist/bin/__tests__/ts-loader-register.test.js +0 -45
  674. package/dist/database/puri-subset.test-d.js +0 -89
  675. package/dist/database/puri.types.test-d.js +0 -129
  676. package/dist/migration/__tests__/code-generation.search-text.test.js +0 -435
  677. package/dist/template/__tests__/generated.template.search-text.test.js +0 -99
  678. package/dist/template/generated.template.test-d.js +0 -24
  679. package/dist/testing/__tests__/dev-test-routes.test.js +0 -144
  680. package/dist/testing/__tests__/dev-vitest-manager.test.js +0 -152
  681. package/dist/types/__tests__/entity-json-schema-search-text.test.js +0 -256
  682. package/dist/typings/knex.d.js +0 -3
  683. package/dist/ui-web/assets/index-CKo0Z2Iu.css +0 -1
  684. package/dist/ui-web/assets/index-DK-2aacv.js +0 -257
@@ -1,1136 +1,1017 @@
1
- import { dispose as logtapeDispose } from "@logtape/logtape";
2
- import assert from "assert";
1
+ import { __esmMin } from "../_virtual/rolldown_runtime.js";
2
+ import { DB, init_db } from "../database/db.js";
3
+ import { BASE_FIELD_MAPPINGS, init_better_auth_entities } from "../auth/better-auth-entities.js";
4
+ import { CachePresets, applyCacheHeaders, init_cache_control } from "../cache-control/cache-control.js";
5
+ import { init_compress, toFastifyCompressOption } from "../compress/compress.js";
6
+ import { SD, init_sd, setSDConfig } from "../dict/sd.js";
7
+ import { NotFoundException, init_so_exceptions } from "../exceptions/so-exceptions.js";
8
+ import { BufferedFile, init_buffered_file } from "../storage/buffered-file.js";
9
+ import { UploadedFile, init_uploaded_file } from "../storage/uploaded-file.js";
10
+ import { createMockSSEFactory, init_sse } from "../stream/sse.js";
11
+ import { init_controller, isDaemonServer } from "../utils/controller.js";
12
+ import { exists, fileExists, init_fs_utils } from "../utils/fs-utils.js";
13
+ import { convertFastifyHeadersToStandard, init_utils, merge } from "../utils/utils.js";
14
+ import { getSecrets, init_secret } from "./secret.js";
3
15
  import { AsyncLocalStorage } from "async_hooks";
4
- import fs from "node:fs/promises";
5
- import mime, { lookup as mimeLookup } from "mime-types";
6
- import os from "os";
16
+ import { dispose } from "@logtape/logtape";
17
+ import assert from "assert";
18
+ import fs from "fs/promises";
7
19
  import path from "path";
8
- import { BASE_FIELD_MAPPINGS, convertFastifyHeadersToStandard, createMockSSEFactory, DB, isDaemonServer, merge, NotFoundException } from "../index.js";
9
- import { applyCacheHeaders, CachePresets } from "../cache-control/cache-control.js";
10
- import { toFastifyCompressOption } from "../compress/compress.js";
11
- import { SD } from "../dict/sd.js";
12
- import { Naite } from "../naite/naite.js";
13
- import { BufferedFile } from "../storage/buffered-file.js";
14
- import { UploadedFile } from "../storage/uploaded-file.js";
15
- import { exists, fileExists } from "../utils/fs-utils.js";
16
- import { getSecrets } from "./secret.js";
17
- class SonamuClass {
18
- isInitialized = false;
19
- forTesting = false;
20
- asyncLocalStorage = new AsyncLocalStorage();
21
- getContext() {
22
- const store = this.asyncLocalStorage.getStore();
23
- if (store?.context) {
24
- return store.context;
25
- }
26
- if (process.env.NODE_ENV === "test") {
27
- // 테스팅 환경에서 컨텍스트가 주입되지 않은 경우 빈 컨텍스트 리턴
28
- return {
29
- request: null,
30
- reply: null,
31
- headers: {},
32
- createSSE: (schema)=>createMockSSEFactory(schema),
33
- // biome-ignore lint/suspicious/noExplicitAny: 테스팅 환경에서 컨텍스트가 주입되지 않은 경우 빈 컨텍스트 리턴
34
- naiteStore: new Map()
35
- };
36
- } else {
37
- throw new Error("Sonamu cannot find context");
38
- }
39
- }
40
- _apiRootPath = null;
41
- set apiRootPath(apiRootPath) {
42
- this._apiRootPath = apiRootPath;
43
- }
44
- get apiRootPath() {
45
- if (this._apiRootPath === null) {
46
- throw new Error("Sonamu has not been initialized");
47
- }
48
- return this._apiRootPath;
49
- }
50
- get appRootPath() {
51
- return this.apiRootPath.split(path.sep).slice(0, -1).join(path.sep);
52
- }
53
- _dbConfig = null;
54
- set dbConfig(dbConfig) {
55
- this._dbConfig = dbConfig;
56
- }
57
- get dbConfig() {
58
- if (this._dbConfig === null) {
59
- throw new Error("Sonamu has not been initialized");
60
- }
61
- return this._dbConfig;
62
- }
63
- _syncer = null;
64
- set syncer(syncer) {
65
- this._syncer = syncer;
66
- }
67
- get syncer() {
68
- if (this._syncer === null) {
69
- throw new Error("Sonamu has not been initialized");
70
- }
71
- return this._syncer;
72
- }
73
- _config = null;
74
- set config(config) {
75
- this._config = config;
76
- }
77
- get config() {
78
- if (this._config === null) {
79
- throw new Error("Sonamu has not been initialized");
80
- }
81
- return this._config;
82
- }
83
- secrets = getSecrets();
84
- _storage = null;
85
- /**
86
- * StorageManager 인스턴스
87
- */ get storage() {
88
- if (!this._storage) {
89
- throw new Error("Storage has not been initialized. Check storage config.");
90
- }
91
- return this._storage;
92
- }
93
- _cache = null;
94
- /**
95
- * CacheManager 인스턴스 (BentoCache)
96
- */ get cache() {
97
- if (!this._cache) {
98
- throw new Error("Cache has not been initialized. Check cache config in sonamu.config.ts.");
99
- }
100
- return this._cache;
101
- }
102
- _workflows = null;
103
- get workflows() {
104
- if (this._workflows === null) {
105
- throw new Error("Sonamu has not been initialized");
106
- }
107
- return this._workflows;
108
- }
109
- _auth = null;
110
- get auth() {
111
- if (!this._auth) {
112
- throw new Error("Auth has not been initialized. Check auth config in sonamu.config.ts.");
113
- }
114
- return this._auth;
115
- }
116
- _devVitestManager = null;
117
- get devVitestManager() {
118
- return this._devVitestManager;
119
- }
120
- set devVitestManager(manager) {
121
- this._devVitestManager = manager;
122
- }
123
- // HMR 처리
124
- watcher = null;
125
- pendingFiles = [];
126
- hmrStartTime = 0;
127
- server = null;
128
- async initForTesting() {
129
- await this.init(true, false, undefined, true);
130
- }
131
- async init(doSilent = false, enableSync = true, apiRootPath, forTesting = false) {
132
- this.forTesting = forTesting;
133
- if (this.isInitialized) {
134
- return;
135
- }
136
- if (!doSilent) {
137
- const chalk = (await import("chalk")).default;
138
- console.time(chalk.cyan(`Sonamu.init${forTesting ? " for testing" : ""}`));
139
- }
140
- // API 루트 패스
141
- const { findApiRootPath } = await import("../utils/utils.js");
142
- this.apiRootPath = apiRootPath ?? findApiRootPath();
143
- // 설정을 로딩하는 것부터 시작
144
- const { loadConfig } = await import("./config.js");
145
- this.config = await loadConfig(this.apiRootPath);
146
- // sonamu.config.ts 기본값 설정
147
- this.config.database.database = this.config.database.database ?? "pg";
148
- this.config.database.defaultOptions.client = this.config.database.database ?? "pg";
149
- // 로깅 설정
150
- const { configureLogTape } = await import("../logger/configure.js");
151
- if (this.config.logging !== false) {
152
- await configureLogTape({
153
- ...this.config.logging
154
- });
155
- }
156
- // DB 로드
157
- const { DB } = await import("../database/db.js");
158
- this.dbConfig = DB.generateDBConfig(this.config.database);
159
- if (!doSilent) {
160
- const chalk = (await import("chalk")).default;
161
- console.log(chalk.green("DB Config Loaded!"));
162
- }
163
- // Entity 로드
164
- // 테스트에서도 Entity 정보는 필요합니다.
165
- // upsert가 제대로 작동하려면 entity의 unique index 정보가 필요하기 때문입니다.
166
- const { EntityManager } = await import("../entity/entity-manager.js");
167
- await EntityManager.autoload(doSilent);
168
- // Cache 초기화
169
- await this.initializeCache(this.config.server.cache, forTesting);
170
- // BetterAuth 초기화
171
- const authConfig = this.config.server.auth;
172
- if (authConfig) {
173
- // 사용자 설정과 기본값을 merge
174
- const mergedFieldMappings = merge(BASE_FIELD_MAPPINGS, authConfig);
175
- // better-auth 인스턴스 생성
176
- const { betterAuth } = await import("better-auth");
177
- const { sonamuKnexAdapter } = await import("../auth/knex-adapter.js");
178
- this._auth = betterAuth({
179
- database: sonamuKnexAdapter(),
180
- ...mergedFieldMappings
181
- });
182
- }
183
- // 테스팅인 경우 싱크 없이 중단
184
- if (forTesting) {
185
- this.isInitialized = true;
186
- return;
187
- }
188
- // Task 등록
189
- await this.initializeWorkflows(this.config.tasks);
190
- // Syncer
191
- const { Syncer } = await import("../syncer/syncer.js");
192
- this.syncer = new Syncer();
193
- // Autoload: Models / Types / APIs / Workflows / Templates / SSR Routes
194
- await this.syncer.autoloadTypes();
195
- await this.syncer.autoloadModels();
196
- await this.syncer.autoloadApis();
197
- await this.syncer.autoloadWorkflows();
198
- const { TemplateManager } = await import("../template/index.js");
199
- await TemplateManager.autoload();
200
- await this.syncer.autoloadSSRRoutes();
201
- const { isLocal, isTest } = await import("../utils/controller.js");
202
- if (isLocal()) {
203
- // 로컬에서는 코드 생성을 위해 Biome 셋업이 필요함 (현재 apiRootPath 전달하여 실행)
204
- (await import("../utils/formatter.js")).setupBiome(this.apiRootPath);
205
- }
206
- const { isHotReloadServer } = await import("../utils/controller.js");
207
- if (isLocal() && !isTest() && isHotReloadServer() && enableSync) {
208
- await this.syncer.sync();
209
- await this.startWatcher();
210
- }
211
- this.isInitialized = true;
212
- if (!doSilent) {
213
- const chalk = (await import("chalk")).default;
214
- console.timeEnd(chalk.cyan("Sonamu.init"));
215
- }
216
- }
217
- async createServer(initOptions) {
218
- if (this.isInitialized === false) {
219
- await this.init(initOptions?.doSilent, initOptions?.enableSync);
220
- }
221
- const options = this.config.server;
222
- const { default: fastify } = await import("fastify");
223
- const { getLogTapeFastifyLogger } = await import("@logtape/fastify");
224
- const server = fastify({
225
- ...options.fastify,
226
- logger: this.config.logging !== false ? getLogTapeFastifyLogger({
227
- category: this.config.logging?.fastifyCategory ?? [
228
- "fastify"
229
- ]
230
- }) : undefined
231
- });
232
- this.server = server;
233
- // Storage 설정 → StorageManager 생성
234
- if (options.storage) {
235
- const { StorageManager } = await import("../storage/storage-manager.js");
236
- this._storage = new StorageManager(options.storage);
237
- }
238
- // 플러그인 등록
239
- if (options.plugins) {
240
- await this.registerPlugins(server, options.plugins);
241
- }
242
- if (options.auth) {
243
- await this.registerBetterAuth(server, options.auth);
244
- }
245
- // API 라우팅 설정
246
- await this.withFastify(server, options.apiConfig, {
247
- enableSync: initOptions?.enableSync,
248
- doSilent: initOptions?.doSilent
249
- });
250
- // 서버 시작
251
- await this.boot(server, options);
252
- return server;
253
- }
254
- async withFastify(server, config, options) {
255
- if (this.isInitialized === false) {
256
- await this.init(options?.doSilent, options?.enableSync);
257
- }
258
- this.server = server;
259
- // timezone 설정
260
- const timezone = this.config.api.timezone;
261
- if (timezone) {
262
- // 타임존에 맞게 응답 날짜 스트링을 변환해주어야 합니다.
263
- // 가령 timezone이 "Asia/Seoul" 이면
264
- // "2025-11-21T00:00:00.000Z" 를 "2025-11-21T09:00:00+09:00" 으로 변환해주어야 합니다.
265
- const { formatInTimeZone } = await import("date-fns-tz");
266
- // ISO 8601 날짜 형식 정규식 (예: 2024-01-15T09:30:00.000Z)
267
- const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/;
268
- // T를 둘러싼 작은따옴표가 없다면 "2025-11-19176354618900018:56:29+09:00"와 같은 결과가 나옵니다.
269
- // 이는 date-fns 특입니다.
270
- // 이렇게 해도 괜찮습니다. "2025-11-19T18:56:29+09:00" 모양으로 잘 나옵니다.
271
- const DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX";
272
- server.setReplySerializer((payload)=>{
273
- return JSON.stringify(payload, (_key, value)=>{
274
- if (typeof value === "string" && ISO_DATE_REGEX.test(value)) {
275
- return formatInTimeZone(new Date(value), timezone, DATE_FORMAT);
276
- }
277
- return value;
278
- });
279
- });
280
- if (!options?.doSilent) {
281
- const chalk = (await import("chalk")).default;
282
- console.log(chalk.green(`Timezone set to ${timezone}`));
283
- }
284
- }
285
- // 전체 라우팅 리스트
286
- server.get(`${this.config.api.route.prefix}/routes`, async (_request, _reply)=>{
287
- return this.syncer.apis;
288
- });
289
- // Healthcheck API
290
- server.get(`${this.config.api.route.prefix}/healthcheck`, async (_request, _reply)=>{
291
- return "ok";
292
- });
293
- // Sonamu UI API (로컬 환경에서만)
294
- const { isLocal } = await import("../utils/controller.js");
295
- if (isLocal()) {
296
- const { sonamuUIApiPlugin } = await import("../ui/api.js");
297
- server.register(sonamuUIApiPlugin);
298
- }
299
- // DevRunner 테스트 엔드포인트 (로컬 환경 + devRunner 활성화 시)
300
- if (isLocal() && this.config.test?.devRunner?.enabled) {
301
- const { registerDevTestRoutes } = await import("../testing/dev-test-routes.js");
302
- await registerDevTestRoutes(server, this.config.test.devRunner);
303
- }
304
- const webPath = path.join(this.appRootPath, "web");
305
- const hasWeb = await exists(webPath);
306
- // 전역 compress 옵션 계산 (route.compress: true일 때 사용)
307
- const pluginCompress = this.config.server.plugins?.compress;
308
- const globalCompressOptions = pluginCompress ? pluginCompress === true ? {
309
- threshold: 1024,
310
- encodings: [
311
- "br",
312
- "gzip",
313
- "deflate"
314
- ]
315
- } : {
316
- threshold: pluginCompress.threshold,
317
- encodings: pluginCompress.encodings,
318
- customTypes: pluginCompress.customTypes
319
- } : undefined;
320
- if (isLocal()) {
321
- // 로컬 개발 환경: catch-all로 API를 동적 매칭하여 HMR을 지원합니다.
322
- // SONAMU_DISABLE_INTEGRATED_WEB=yes로 설정하면 dev_api 모드에서 Vite 통합을 비활성화할 수 있습니다.
323
- const disableIntegratedWeb = process.env.SONAMU_DISABLE_INTEGRATED_WEB === "yes";
324
- if (hasWeb && !disableIntegratedWeb) {
325
- await this.setupDevServerWithVite(server, webPath, config);
326
- } else {
327
- this.setupDevServer(server, config);
328
- }
329
- } else {
330
- // 프로덕션 환경: 개별 API 라우트 + 정적 파일 서빙
331
- for (const api of this.syncer.apis){
332
- if (this.syncer.models[api.modelName] === undefined) {
333
- throw new Error(`정의되지 않은 모델에 접근 ${api.modelName}`);
334
- }
335
- server.route({
336
- method: api.options.httpMethod ?? "GET",
337
- url: this.config.api.route.prefix + api.path,
338
- handler: this.createApiHandler(api, config),
339
- compress: toFastifyCompressOption(api.options.compress, globalCompressOptions)
340
- });
341
- }
342
- // 프로덕션에서는 web 소스(appRoot/web) 유무와 무관하게,
343
- // api/web-dist 존재 여부를 setupStaticWebServer 내부에서 판단합니다.
344
- await this.setupStaticWebServer(server, config, globalCompressOptions);
345
- }
346
- }
347
- /**
348
- * dev 모드 공통: catch-all에서 syncer.apis를 동적으로 탐색하여 API 요청을 처리합니다.
349
- * server.route()로 개별 등록하면 handler가 고정되어 HMR이 동작하지 않으므로,
350
- * 매 요청마다 syncer.apis를 조회하는 이 방식을 사용합니다.
351
- *
352
- * 요청이 /api(정확히는 this.config.api.route.prefix)로 시작하지 않는 경우라면 null을 반환하며 끝냅니다.
353
- */ handleDevApiRequest(request, config) {
354
- const url = this.getPathnameFromUrl(request.url);
355
- const method = request.method;
356
- if (!url.startsWith(this.config.api.route.prefix)) {
357
- return null;
358
- }
359
- // syncer.apis의 path는 :param 형태를 포함할 수 있으므로 세그먼트 단위로 매칭합니다.
360
- // 정규식 생성 방식은 path 문자열 내 특수문자(., +, (, [ 등)로 오작동할 수 있어 사용하지 않습니다.
361
- const matchedApi = this.syncer.apis.find((api)=>{
362
- if (this.syncer.models[api.modelName] === undefined) {
363
- return false;
364
- }
365
- const apiMethod = api.options.httpMethod ?? "GET";
366
- if (apiMethod !== method) return false;
367
- const fullPath = this.config.api.route.prefix + api.path;
368
- return this.isPathPatternMatch(fullPath, url);
369
- });
370
- if (!matchedApi) {
371
- throw new NotFoundException(SD("error.api.notFound"));
372
- }
373
- return this.createApiHandler(matchedApi, config);
374
- }
375
- /**
376
- * dev api 모드: Vite 없이 API 동적 라우팅만 제공합니다.
377
- * HMR을 위해 catch-all에서 매 요청마다 syncer.apis를 조회합니다.
378
- */ setupDevServer(server, config) {
379
- server.route({
380
- method: [
381
- "GET",
382
- "HEAD",
383
- "POST",
384
- "PUT",
385
- "DELETE",
386
- "PATCH"
387
- ],
388
- url: `${this.config.api.route.prefix}/*`,
389
- handler: async (request, reply)=>{
390
- const handler = this.handleDevApiRequest(request, config);
391
- if (handler) {
392
- return handler(request, reply);
393
- }
394
- // 등록된 API와 일치하지 않는 요청에 대한 fallback입니다.
395
- throw new NotFoundException(SD("error.api.notFound"));
396
- }
397
- });
398
- }
399
- // biome-ignore lint/suspicious/noExplicitAny: ViteDevServer 타입을 동적으로 로드해야 함
400
- viteServer = null;
401
- /**
402
- * dev all 모드: Vite Dev Server를 통합하여 API + SSR + CSR을 모두 제공합니다.
403
- * API 동적 매칭은 handleDevApiRequest를 공유합니다.
404
- */ async setupDevServerWithVite(server, webPath, config) {
405
- // @fastify/middie 등록 (Connect-style middleware 지원)
406
- await server.register((await import("@fastify/middie")).default);
407
- const vite = await import("vite");
408
- this.viteServer = await vite.createServer({
409
- root: webPath,
410
- server: {
411
- middlewareMode: true,
412
- hmr: {
413
- server: server.server
414
- }
415
- },
416
- appType: "custom"
417
- });
418
- // Vite middleware 등록 (Vite 에셋 처리)
419
- server.use((req, res, next)=>{
420
- // API와 Sonamu UI는 Fastify 라우트가 처리하도록 skip
421
- if (req.url?.startsWith(this.config.api.route.prefix) || req.url?.startsWith("/sonamu-ui")) {
422
- return next();
423
- }
424
- // 나머지는 Vite middleware로 전달
425
- return this.viteServer.middlewares(req, res, next);
426
- });
427
- // catch-all 라우트에서 동적으로 API/SSR 처리
428
- // 개발 환경에서는 라우트별 compress 옵션을 포기하고 HMR 이점을 취합니다.
429
- server.route({
430
- method: [
431
- "GET",
432
- "HEAD",
433
- "POST",
434
- "PUT",
435
- "DELETE",
436
- "PATCH"
437
- ],
438
- url: "/*",
439
- handler: async (request, reply)=>{
440
- // 1. API 요청 처리
441
- const result = this.handleDevApiRequest(request, config);
442
- if (result) {
443
- return result(request, reply);
444
- }
445
- const url = request.url;
446
- // 2. SSR 라우트 처리
447
- const { matchSSRRoute, renderSSR } = await import("../ssr/index.js");
448
- const ssrMatch = matchSSRRoute(url);
449
- if (ssrMatch) {
450
- console.log(`[SSR] Matched route: ${ssrMatch.route.path}`);
451
- const html = await renderSSR(url, ssrMatch.route, ssrMatch.params, request, reply, config, this.viteServer);
452
- reply.type("text/html");
453
- return html;
454
- }
455
- // 3. CSR fallback
456
- try {
457
- const fs = await import("node:fs/promises");
458
- let template = await fs.readFile(path.join(this.viteServer.config.root, "index.html"), "utf-8");
459
- template = await this.viteServer.transformIndexHtml(url, template);
460
- reply.type("text/html");
461
- return template;
462
- } catch (e) {
463
- this.viteServer.ssrFixStacktrace(e);
464
- console.error(e);
465
- reply.status(500);
466
- return e.message;
467
- }
468
- }
469
- });
470
- // 서버 종료 시 Vite도 종료
471
- server.addHook("onClose", async ()=>{
472
- await this.viteServer.close();
473
- });
474
- console.log("✓ Vite dev server integrated");
475
- }
476
- async setupStaticWebServer(server, config, globalCompressOptions) {
477
- // 경로 명확화: api/web-dist/client (정적 파일), api/web-dist/server (SSR entry), api/dist/ssr (SSR routes - API 소유)
478
- const webDistPath = path.join(this.apiRootPath, "web-dist", "client");
479
- const ssrPath = path.join(this.apiRootPath, "web-dist", "server");
480
- const ssrEntryPath = path.join(ssrPath, "entry-server.generated.js");
481
- const ssrRoutesPath = path.join(this.apiRootPath, "dist", "ssr", "routes.js");
482
- if (!await exists(webDistPath)) {
483
- console.warn(`⚠ Web dist not found: ${webDistPath}`);
484
- return;
485
- }
486
- // SSR entry 존재 여부 확인
487
- const ssrAvailable = await exists(ssrEntryPath);
488
- if (!ssrAvailable) {
489
- console.warn(`⚠ SSR entry not found: ${ssrEntryPath}`);
490
- console.warn(" SSR will be disabled. Only CSR will work.");
491
- }
492
- // SSR 라우트 로드 (production에서만, 사용자 프로젝트의 ssr/routes.ts)
493
- if (ssrAvailable) {
494
- if (await exists(ssrRoutesPath)) {
495
- // ts-loader라면 "file://"로 시작하는 fully-resolved path만 받기에 이를 처리해주는 importMembers를 사용해야 했겠지만,
496
- // 여기는 프로덕션 환경에서 loader 없이 돌아가기 때문에 "진짜 js 파일"의 "그냥" 절대경로를 바로 import해도 됩니다.
497
- // 이 내용은 이 함수 내에서 아래에 나올 다른 import 호출에도 동일하게 적용됩니다.
498
- await import(ssrRoutesPath);
499
- console.log("✓ SSR routes loaded");
500
- } else {
501
- console.warn(`⚠ SSR routes not found: ${ssrRoutesPath}`);
502
- }
503
- }
504
- // 롤링 업데이트 대응: asset hash 불일치 시 현재 버전 직접 서빙
505
- server.get("/assets/:filename", async (request, reply)=>{
506
- const requestedFile = request.params.filename;
507
- const assetsDir = path.join(webDistPath, "assets");
508
- const safeFilePath = this.resolvePathWithinBaseDir(assetsDir, requestedFile);
509
- if (safeFilePath === null) {
510
- reply.status(403).send();
511
- return;
512
- }
513
- const normalizedRequestedFile = path.relative(assetsDir, safeFilePath).replace(/\\/g, "/");
514
- const assetPath = `/assets/${normalizedRequestedFile}`;
515
- // Cache-Control 헤더 결정
516
- const getCacheControlForAsset = ()=>{
517
- const cacheReq = {
518
- type: "assets",
519
- url: request.url,
520
- path: assetPath,
521
- method: request.method
522
- };
523
- // 사용자 정의 핸들러 우선
524
- if (config.cacheControlHandler) {
525
- const result = config.cacheControlHandler(cacheReq);
526
- if (result) return result;
527
- }
528
- // 기본값: immutable
529
- return CachePresets.immutable;
530
- };
531
- // index-*.js 또는 index-*.css 요청인 경우
532
- if (/^index-[a-f0-9]+\.(js|css)$/.test(normalizedRequestedFile)) {
533
- const ext = normalizedRequestedFile.split(".").pop();
534
- const files = await fs.readdir(assetsDir);
535
- const currentFile = files.find((f)=>f.startsWith("index-") && f.endsWith(`.${ext}`));
536
- if (currentFile) {
537
- const filePath = path.join(assetsDir, currentFile);
538
- const content = await fs.readFile(filePath);
539
- reply.type(ext === "js" ? "application/javascript" : "text/css");
540
- applyCacheHeaders(reply, getCacheControlForAsset());
541
- return reply.send(content);
542
- }
543
- }
544
- // 일반 파일 서빙
545
- const filePath = safeFilePath;
546
- if (await exists(filePath)) {
547
- const content = await fs.readFile(filePath);
548
- const ext = normalizedRequestedFile.split(".").pop();
549
- reply.type(ext === "js" ? "application/javascript" : ext === "css" ? "text/css" : "");
550
- if (normalizedRequestedFile.includes("-")) {
551
- applyCacheHeaders(reply, getCacheControlForAsset());
552
- }
553
- return reply.send(content);
554
- }
555
- reply.status(404).send();
556
- });
557
- // SSR 라우트 개별 등록 (compress 옵션이 라우트별로 적용되도록)
558
- if (ssrAvailable) {
559
- const { getSSRRoutes } = await import("../ssr/index.js");
560
- const { renderSSR } = await import("../ssr/renderer.js");
561
- const ssrRoutes = getSSRRoutes();
562
- for (const route of ssrRoutes){
563
- server.route({
564
- method: [
565
- "GET",
566
- "HEAD"
567
- ],
568
- url: route.path,
569
- compress: toFastifyCompressOption(route.compress ?? true, globalCompressOptions),
570
- handler: async (request, reply)=>{
571
- const url = request.url;
572
- console.log(`[SSR] Matched route: ${route.path}`);
573
- const params = this.extractPathParams(route.path, url);
574
- const html = await renderSSR(url, route, params, request, reply, config);
575
- reply.type("text/html");
576
- return html;
577
- }
578
- });
579
- }
580
- }
581
- // CSR or Static File Fallback (SSR 라우트에 매칭되지 않는 모든 요청)
582
- server.route({
583
- method: [
584
- "GET",
585
- "HEAD"
586
- ],
587
- url: "*",
588
- handler: async (request, reply)=>{
589
- // /api, /sonamu-ui는 404 그대로
590
- if (request.url.startsWith("/api") || request.url.startsWith("/sonamu-ui")) {
591
- reply.status(404).send();
592
- return;
593
- }
594
- // CSR용 Cache-Control 헤더 설정
595
- if (config.cacheControlHandler) {
596
- const csrCacheReq = {
597
- type: "csr",
598
- url: request.url,
599
- path: request.url.split("?")[0],
600
- method: request.method
601
- };
602
- const csrCacheConfig = config.cacheControlHandler(csrCacheReq);
603
- if (csrCacheConfig) {
604
- applyCacheHeaders(reply, csrCacheConfig);
605
- }
606
- }
607
- // 정적 파일이 존재할 경우, 정적 파일을 먼저 서빙해야함
608
- const requestPath = this.getPathnameFromUrl(request.url);
609
- const safeFilePath = this.resolvePathWithinBaseDir(webDistPath, requestPath);
610
- if (safeFilePath === null) {
611
- reply.status(403).send();
612
- return;
613
- }
614
- if (await fileExists(safeFilePath)) {
615
- const content = await fs.readFile(safeFilePath);
616
- return reply.type(mimeLookup(safeFilePath) || "application/octet-stream").send(content);
617
- }
618
- // CSR fallback: index.html 서빙
619
- const indexPath = path.join(webDistPath, "index.html");
620
- return reply.type("text/html").send(await fs.readFile(indexPath, "utf-8"));
621
- }
622
- });
623
- console.log(`✓ Static web server configured with ${ssrAvailable ? "SSR" : "CSR only"} support`);
624
- }
625
- createApiHandler(api, config) {
626
- return async (request, reply)=>{
627
- // Context 생성
628
- const context = await this.createContext(config, request, reply);
629
- return this.asyncLocalStorage.run({
630
- context
631
- }, async ()=>{
632
- // guards 처리
633
- (api.options.guards ?? []).every((guard)=>config.guardHandler(guard, request, api));
634
- // 파라미터 정보로 zod 스키마 빌드
635
- const { getZodObjectFromApi } = await import("./code-converters.js");
636
- const ReqType = getZodObjectFromApi(api, this.syncer.types);
637
- // request 파싱
638
- const which = api.options.httpMethod === "GET" ? "query" : "body";
639
- let reqBody;
640
- // 파일 업로드 있는 경우 임시 데이터
641
- const files = {
642
- bufferedFiles: [],
643
- uploadedFiles: []
644
- };
645
- try {
646
- const body = request[which] ?? {};
647
- if (api.uploadOptions) {
648
- const parts = request.parts({
649
- limits: api.uploadOptions.limits
650
- });
651
- // FormData의 field들을 임시로 저장
652
- const fields = {};
653
- if (api.uploadOptions.consume === "buffer" || !api.uploadOptions.consume) {
654
- // Buffer 모드: 메모리에 로드
655
- for await (const part of parts){
656
- if (part.type === "file") {
657
- // CRITICAL: 파일 스트림을 즉시 consume해야 다음 part로 넘어갈 수 있음
658
- // 이 호출이 없으면 종종 multipart 파싱이 pending 상태로 타임아웃 발생
659
- const buffer = await part.toBuffer();
660
- files.bufferedFiles.push(new BufferedFile(part, buffer));
661
- } else if (part.type === "field") {
662
- fields[part.fieldname] = String(part.value);
663
- }
664
- }
665
- } else if (api.uploadOptions.consume === "stream") {
666
- // Stream 모드: 즉시 저장소로 스트리밍
667
- const diskName = api.uploadOptions.destination;
668
- const disk = this.storage.use(diskName);
669
- // 우선순위: 데코레이터 > 전역 설정 > 기본값
670
- const keyGenerator = api.uploadOptions.keyGenerator ?? this.config.server.storage?.keyGenerator ?? defaultKeyGenerator;
671
- for await (const part of parts){
672
- if (part.type === "file") {
673
- const key = await keyGenerator({
674
- filename: part.filename,
675
- mimetype: part.mimetype
676
- });
677
- await disk.putStream(key, part.file, {
678
- contentType: part.mimetype
679
- });
680
- const url = await disk.getUrl(key);
681
- const signedUrl = await disk.getSignedUrl(key);
682
- files.uploadedFiles.push(new UploadedFile({
683
- filename: part.filename,
684
- mimetype: part.mimetype,
685
- size: part.file.bytesRead,
686
- url,
687
- signedUrl,
688
- key,
689
- diskName
690
- }));
691
- } else if (part.type === "field") {
692
- fields[part.fieldname] = String(part.value);
693
- }
694
- }
695
- }
696
- // qs로 중첩 구조 파싱: params[category] → { params: { category: "test" } }
697
- const qs = await import("qs");
698
- const parsed = qs.default.parse(fields);
699
- Object.assign(body, parsed);
700
- }
701
- const { fastifyCaster } = await import("./caster.js");
702
- reqBody = fastifyCaster(ReqType).parse(body);
703
- } catch (e) {
704
- const { ZodError } = await import("zod");
705
- if (e instanceof ZodError) {
706
- const { humanizeZodError } = await import("../utils/zod-error.js");
707
- const messages = humanizeZodError(e).map((issue)=>issue.message).join(" ");
708
- const { BadRequestException } = await import("../exceptions/so-exceptions.js");
709
- throw new BadRequestException(messages, {
710
- zodError: e
711
- });
712
- } else {
713
- throw e;
714
- }
715
- }
716
- // Content-Type
717
- reply.type(api.options.contentType ?? "application/json");
718
- // Cache-Control 헤더 설정
719
- const apiCacheConfig = this.getApiCacheControl(api, request, config);
720
- if (apiCacheConfig) {
721
- applyCacheHeaders(reply, apiCacheConfig);
722
- }
723
- // 업로드 옵션이 있는 경우 파일 데이터를 Context에 추가
724
- if (api.uploadOptions) {
725
- const consume = api.uploadOptions.consume ?? "buffer";
726
- if (consume === "buffer") {
727
- context.bufferedFiles = files.bufferedFiles;
728
- } else if (consume === "stream") {
729
- context.uploadedFiles = files.uploadedFiles;
730
- }
731
- }
732
- // 모델 메소드 args 생성하여 호출
733
- const { ApiParamType } = await import("../types/types.js");
734
- const args = api.parameters.map((param)=>{
735
- // Context 인젝션
736
- if (ApiParamType.isContext(param.type)) {
737
- return context;
738
- } else {
739
- return reqBody[param.name];
740
- }
741
- });
742
- return this.invokeModelMethod(api, args, reply);
743
- });
744
- };
745
- }
746
- /**
747
- * URL에서 path params를 추출합니다.
748
- * 예: pattern="/admin/companies/:companyId", url="/admin/companies/123" → { companyId: "123" }
749
- */ extractPathParams(pattern, url) {
750
- const patternParts = pattern.split("/").filter(Boolean);
751
- const urlParts = this.getPathnameFromUrl(url).split("/").filter(Boolean);
752
- const params = {};
753
- for(let i = 0; i < patternParts.length; i++){
754
- if (patternParts[i].startsWith(":")) {
755
- params[patternParts[i].slice(1)] = urlParts[i];
756
- }
757
- }
758
- return params;
759
- }
760
- isPathPatternMatch(pattern, url) {
761
- const patternParts = pattern.split("/").filter(Boolean);
762
- const urlParts = this.getPathnameFromUrl(url).split("/").filter(Boolean);
763
- if (patternParts.length !== urlParts.length) {
764
- return false;
765
- }
766
- for(let i = 0; i < patternParts.length; i++){
767
- const patternPart = patternParts[i];
768
- const urlPart = urlParts[i];
769
- if (patternPart.startsWith(":")) {
770
- continue;
771
- }
772
- if (patternPart !== urlPart) {
773
- return false;
774
- }
775
- }
776
- return true;
777
- }
778
- getPathnameFromUrl(url) {
779
- return url.split("?")[0];
780
- }
781
- resolvePathWithinBaseDir(baseDir, inputPath) {
782
- try {
783
- const decoded = decodeURIComponent(inputPath).replace(/\\/g, "/");
784
- if (decoded.includes("\0")) {
785
- return null;
786
- }
787
- const relativePath = decoded.replace(/^\/+/, "");
788
- const resolvedPath = path.resolve(baseDir, relativePath);
789
- const relativeFromBase = path.relative(baseDir, resolvedPath);
790
- if (relativeFromBase.startsWith("..") || path.isAbsolute(relativeFromBase)) {
791
- return null;
792
- }
793
- return resolvedPath;
794
- } catch {
795
- return null;
796
- }
797
- }
798
- /**
799
- * API 응답에 적용할 Cache-Control 설정을 결정합니다.
800
- * 우선순위: 개별 지정 > cacheControlHandler
801
- */ getApiCacheControl(api, request, config) {
802
- // 데코레이터 설정 우선
803
- if (api.options.cacheControl) {
804
- return api.options.cacheControl;
805
- }
806
- // 전역 핸들러
807
- if (config.cacheControlHandler) {
808
- const cacheReq = {
809
- type: "api",
810
- url: request.url,
811
- path: request.routeOptions?.url ?? request.url.split("?")[0],
812
- method: request.method,
813
- api
814
- };
815
- const result = config.cacheControlHandler(cacheReq);
816
- if (result) return result;
817
- }
818
- return null;
819
- }
820
- /**
821
- * SSR용 API 호출 (HTTP 오버헤드 없이 직접 호출)
822
- * createApiHandler의 로직을 재사용하되, request 파싱 대신 params 직접 사용
823
- */ async invokeApiForSSR(api, // biome-ignore lint/suspicious/noExplicitAny: SSR에서 다양한 타입의 params를 받아야 함
824
- params, config, request, reply) {
825
- // Context 생성 (기존 메소드 재사용)
826
- const context = await this.createContext(config, request, reply);
827
- return this.asyncLocalStorage.run({
828
- context
829
- }, async ()=>{
830
- // args 생성: Context 파라미터는 주입, 나머지는 params에서 가져오기
831
- const { ApiParamType } = await import("../types/types.js");
832
- let paramsIndex = 0;
833
- const args = api.parameters.map((param)=>{
834
- if (ApiParamType.isContext(param.type)) {
835
- return context;
836
- }
837
- return params[paramsIndex++];
838
- });
839
- // 모델 메서드 호출 (기존 메서드 재사용)
840
- return this.invokeModelMethod(api, args, reply);
841
- });
842
- }
843
- async invokeModelMethod(api, args, reply) {
844
- const model = this.syncer.models[api.modelName];
845
- // biome-ignore lint/suspicious/noExplicitAny: model은 모델 인스턴스이므로 메서드 호출 가능
846
- const result = await model[api.methodName].apply(model, args);
847
- reply.type(api.options.contentType ?? "application/json");
848
- return result;
849
- }
850
- async createContext(config, request, reply) {
851
- // createSSEFactory 함수에 미리 request의 socket과 reply를 바인딩.
852
- const { createSSEFactory } = await import("../stream/sse.js");
853
- const createSSE = ((_request, _reply, _events)=>createSSEFactory(_request.socket, _reply, _events)).bind(null, request, reply);
854
- // locale 감지
855
- const locale = this.detectLocale(request.headers["accept-language"], this.config.i18n.supportedLocales) ?? this.config.i18n.defaultLocale;
856
- // auth context 추가
857
- const headers = convertFastifyHeadersToStandard(request.headers);
858
- const session = await this._auth?.api.getSession({
859
- headers
860
- }) ?? null;
861
- const context = {
862
- ...await Promise.resolve(config.contextProvider({
863
- request,
864
- reply,
865
- headers: request.headers,
866
- createSSE,
867
- naiteStore: Naite.createStore(),
868
- locale,
869
- // auth
870
- user: session?.user ?? null,
871
- session: session?.session ?? null
872
- }, request, reply))
873
- };
874
- return context;
875
- }
876
- /**
877
- * Accept-Language 헤더에서 지원하는 locale을 찾습니다.
878
- * @example "ko-KR,ko;q=0.9,en;q=0.8" → "ko"
879
- */ detectLocale(acceptLanguage, supported) {
880
- if (!acceptLanguage) return undefined;
881
- // Accept-Language: ko-KR,ko;q=0.9,en;q=0.8
882
- const langs = acceptLanguage.split(",").map((lang)=>{
883
- const [code] = lang.split(";");
884
- return code.trim().split("-")[0]; // ko-KR → ko
885
- });
886
- return langs.find((lang)=>supported.includes(lang));
887
- }
888
- async startWatcher() {
889
- const watchPath = [
890
- path.join(this.apiRootPath, "src")
891
- ];
892
- const chokidar = (await import("chokidar")).default;
893
- this.watcher = chokidar.watch(watchPath, {
894
- ignored: (path, stats)=>!!stats?.isFile() && !path.endsWith(".ts") && !path.endsWith(".json"),
895
- persistent: true,
896
- ignoreInitial: true
897
- });
898
- this.watcher.on("all", async (event, filePath)=>{
899
- const absolutePath = filePath;
900
- assert(absolutePath.startsWith(this.apiRootPath), "File path is not within the API root path");
901
- if (event !== "change" && event !== "add") {
902
- return;
903
- }
904
- try {
905
- // sonamu.config.ts 변경 시 재시작
906
- const isConfigTs = filePath === path.join(this.apiRootPath, "src", "sonamu.config.ts");
907
- if (isConfigTs) {
908
- const relativePath = filePath.replace(this.apiRootPath, "api");
909
- const chalk = (await import("chalk")).default;
910
- console.log(chalk.bold(`Detected(${event}): ${chalk.blue(relativePath)} - Restarting...`));
911
- process.kill(process.pid, "SIGUSR2");
912
- return;
913
- }
914
- await this.handleFileChange(event, absolutePath);
915
- } catch (e) {
916
- console.error(e);
917
- }
918
- });
919
- }
920
- /*
921
- A function that automatically handles init and destroy when using Sonamu via scripts.
922
- */ async runScript(fn) {
923
- await this.init(true, false, undefined, false);
924
- try {
925
- await fn();
926
- } finally{
927
- await this.destroy();
928
- }
929
- }
930
- async registerPlugins(server, plugins) {
931
- if (!plugins) {
932
- return;
933
- }
934
- // compress 플러그인은 다른 플러그인보다 먼저 등록되어야 합니다.
935
- if (plugins.compress) {
936
- const compressPlugin = (await import("@fastify/compress")).default;
937
- const defaultOptions = {
938
- threshold: 1024,
939
- encodings: [
940
- "br",
941
- "gzip",
942
- "deflate"
943
- ]
944
- };
945
- if (plugins.compress === true) {
946
- server.register(compressPlugin, defaultOptions);
947
- } else {
948
- server.register(compressPlugin, {
949
- ...defaultOptions,
950
- ...plugins.compress
951
- });
952
- }
953
- }
954
- const pluginsModules = {
955
- cors: "@fastify/cors",
956
- formbody: "@fastify/formbody",
957
- multipart: "@fastify/multipart",
958
- qs: "fastify-qs",
959
- sse: "fastify-sse-v2",
960
- static: "@fastify/static"
961
- };
962
- const registerPlugin = async (key, pluginName)=>{
963
- const option = plugins[key];
964
- if (!option) return;
965
- if (option === true) {
966
- server.register((await import(pluginName)).default);
967
- } else {
968
- server.register((await import(pluginName)).default, option);
969
- }
970
- };
971
- for (const [key, pluginName] of Object.entries(pluginsModules)){
972
- await registerPlugin(key, pluginName);
973
- }
974
- if (plugins.custom) {
975
- plugins.custom(server);
976
- }
977
- }
978
- /**
979
- * better-auth 라우트를 등록합니다.
980
- * /api/auth/* 경로로 인증 API가 자동 등록됩니다.
981
- */ async registerBetterAuth(server, options) {
982
- if (!options) return;
983
- const basePath = options.basePath ?? "/api/auth";
984
- // better-auth 라우트 등록
985
- server.route({
986
- method: [
987
- "GET",
988
- "POST"
989
- ],
990
- url: `${basePath}/*`,
991
- handler: async (request, reply)=>{
992
- const url = new URL(request.url, `http://${request.headers.host}`);
993
- const headers = convertFastifyHeadersToStandard(request.headers);
994
- const req = new Request(url.toString(), {
995
- method: request.method,
996
- headers,
997
- ...request.body ? {
998
- body: JSON.stringify(request.body)
999
- } : {}
1000
- });
1001
- const response = await this.auth.handler(req);
1002
- reply.status(response.status);
1003
- response.headers.forEach((value, key)=>{
1004
- reply.header(key, value);
1005
- });
1006
- return reply.send(response.body ? await response.text() : null);
1007
- }
1008
- });
1009
- const chalk = (await import("chalk")).default;
1010
- console.log(chalk.green(`✓ better-auth registered at ${basePath}/*`));
1011
- }
1012
- async initializeCache(config, forTesting) {
1013
- const { setCacheManagerRef } = await import("../cache/decorator.js");
1014
- // 테스트 환경에서 메모리 드라이버 자동 사용
1015
- if (forTesting) {
1016
- const { createTestCacheManager } = await import("../cache/cache-manager.js");
1017
- this._cache = createTestCacheManager();
1018
- setCacheManagerRef(this._cache);
1019
- return;
1020
- }
1021
- // 설정이 없으면 캐시 비활성화
1022
- if (!config) {
1023
- setCacheManagerRef(null);
1024
- return;
1025
- }
1026
- // 설정에 따라 CacheManager 생성
1027
- const { createCacheManager } = await import("../cache/cache-manager.js");
1028
- this._cache = createCacheManager(config);
1029
- setCacheManagerRef(this._cache);
1030
- }
1031
- async initializeWorkflows(options) {
1032
- const { WorkflowManager } = await import("../tasks/workflow-manager.js");
1033
- // NOTE: @sonamu-kit/tasks 안에선 knex config를 수정하기 때문에 connection이 아닌 config 째로 보냅니다.
1034
- this._workflows = new WorkflowManager(DB.getDBConfig("w"));
1035
- if (!options) {
1036
- return;
1037
- }
1038
- const enableWorker = options.enableWorker ?? isDaemonServer();
1039
- const defaultWorkerOptions = {
1040
- concurrency: os.cpus().length - 1,
1041
- usePubSub: true,
1042
- listenDelay: 500
1043
- };
1044
- if (enableWorker) {
1045
- this.workflows.setupWorker({
1046
- ...defaultWorkerOptions,
1047
- ...options.workerOptions
1048
- });
1049
- }
1050
- }
1051
- async boot(server, options) {
1052
- const port = options.listen?.port ?? 3000;
1053
- const host = options.listen?.host ?? "localhost";
1054
- server.addHook("onClose", async ()=>{
1055
- await options.lifecycle?.onShutdown?.(server);
1056
- await this.workflows.destroy();
1057
- await this.destroy();
1058
- });
1059
- const shutdown = async ()=>{
1060
- try {
1061
- await server.close();
1062
- process.exit(0);
1063
- } catch (err) {
1064
- console.error("Error during shutdown:", err);
1065
- process.exit(1);
1066
- }
1067
- };
1068
- process.on("SIGINT", shutdown);
1069
- process.on("SIGTERM", shutdown);
1070
- if (options.lifecycle?.onError) {
1071
- server.setErrorHandler(options.lifecycle?.onError);
1072
- }
1073
- server.listen({
1074
- port,
1075
- host
1076
- }).then(async ()=>{
1077
- await this.workflows.startWorker();
1078
- await options.lifecycle?.onStart?.(server);
1079
- }).catch(async (err)=>{
1080
- const chalk = (await import("chalk")).default;
1081
- console.error(chalk.red("Failed to start server:", err));
1082
- await shutdown();
1083
- });
1084
- }
1085
- async handleFileChange(event, filePath) {
1086
- // 첫 번째 파일이면 HMR 시작 시간 기록
1087
- if (this.pendingFiles.length === 0) {
1088
- this.hmrStartTime = Date.now();
1089
- }
1090
- this.pendingFiles.push(filePath);
1091
- const relativePath = path.relative(this.apiRootPath, filePath);
1092
- const chalk = (await import("chalk")).default;
1093
- console.log(chalk.bold(`Detected(${event}): ${chalk.blue(relativePath)}`));
1094
- await this.syncer.syncFromWatcher(event, filePath);
1095
- // 처리 완료된 파일을 대기 목록에서 제거
1096
- this.pendingFiles = this.pendingFiles.slice(1);
1097
- // 모든 파일 처리가 완료되면 최종 메시지 출력
1098
- if (this.pendingFiles.length === 0) {
1099
- await this.finishHMR();
1100
- }
1101
- }
1102
- async finishHMR() {
1103
- await this.syncer.renewChecksums();
1104
- const endTime = Date.now();
1105
- const totalTime = endTime - this.hmrStartTime;
1106
- const [chalk, { centerText }] = await Promise.all([
1107
- (await import("chalk")).default,
1108
- import("../utils/console-util.js")
1109
- ]);
1110
- const msg = `HMR Done! ${chalk.bold.white(`${totalTime}ms`)}`;
1111
- console.log(chalk.black.bgGreen(centerText(msg)));
1112
- }
1113
- async destroy() {
1114
- const { BaseModel } = await import("../database/base-model.js");
1115
- // 먼저 처리해야함.
1116
- await BaseModel.destroy();
1117
- await Promise.allSettled([
1118
- this._workflows?.destroy() ?? Promise.resolve(),
1119
- this._cache?.disconnect() ?? Promise.resolve(),
1120
- this._devVitestManager?.shutdown() ?? Promise.resolve(),
1121
- this.watcher?.close() ?? Promise.resolve(),
1122
- logtapeDispose()
1123
- ]);
1124
- }
1125
- }
1126
- export const Sonamu = new SonamuClass();
20
+ import os from "os";
21
+ import mime, { lookup } from "mime-types";
22
+
23
+ //#region src/api/sonamu.ts
1127
24
  /**
1128
- * stream 모드에서 키 생성 함수가 지정되지 않았을 때 사용하는 기본 함수입니다.
1129
- */ function defaultKeyGenerator(file) {
1130
- const ext = mime.extension(file.mimetype) || "bin";
1131
- const timestamp = Date.now();
1132
- const random = Math.random().toString(36).slice(2, 8);
1133
- return `uploads/${timestamp}-${random}.${ext}`;
25
+ * stream 모드에서 키 생성 함수가 지정되지 않았을 때 사용하는 기본 함수입니다.
26
+ */
27
+ function defaultKeyGenerator(file) {
28
+ const ext = mime.extension(file.mimetype) || "bin";
29
+ const timestamp = Date.now();
30
+ const random = Math.random().toString(36).slice(2, 8);
31
+ return `uploads/${timestamp}-${random}.${ext}`;
1134
32
  }
33
+ var SonamuClass, Sonamu;
34
+ var init_sonamu = __esmMin((() => {
35
+ init_better_auth_entities();
36
+ init_cache_control();
37
+ init_compress();
38
+ init_db();
39
+ init_sd();
40
+ init_so_exceptions();
41
+ init_buffered_file();
42
+ init_uploaded_file();
43
+ init_sse();
44
+ init_controller();
45
+ init_fs_utils();
46
+ init_utils();
47
+ init_secret();
48
+ SonamuClass = class {
49
+ isInitialized = false;
50
+ forTesting = false;
51
+ asyncLocalStorage = new AsyncLocalStorage();
52
+ getContext() {
53
+ const store = this.asyncLocalStorage.getStore();
54
+ if (store?.context) {
55
+ return store.context;
56
+ }
57
+ if (process.env.NODE_ENV === "test") {
58
+ return {
59
+ request: null,
60
+ reply: null,
61
+ headers: {},
62
+ createSSE: (schema) => createMockSSEFactory(schema),
63
+ naiteStore: new Map()
64
+ };
65
+ } else {
66
+ throw new Error("Sonamu cannot find context");
67
+ }
68
+ }
69
+ _apiRootPath = null;
70
+ set apiRootPath(apiRootPath) {
71
+ this._apiRootPath = apiRootPath;
72
+ }
73
+ get apiRootPath() {
74
+ if (this._apiRootPath === null) {
75
+ throw new Error("Sonamu has not been initialized");
76
+ }
77
+ return this._apiRootPath;
78
+ }
79
+ get appRootPath() {
80
+ return this.apiRootPath.split(path.sep).slice(0, -1).join(path.sep);
81
+ }
82
+ _dbConfig = null;
83
+ set dbConfig(dbConfig) {
84
+ this._dbConfig = dbConfig;
85
+ }
86
+ get dbConfig() {
87
+ if (this._dbConfig === null) {
88
+ throw new Error("Sonamu has not been initialized");
89
+ }
90
+ return this._dbConfig;
91
+ }
92
+ _syncer = null;
93
+ set syncer(syncer) {
94
+ this._syncer = syncer;
95
+ }
96
+ get syncer() {
97
+ if (this._syncer === null) {
98
+ throw new Error("Sonamu has not been initialized");
99
+ }
100
+ return this._syncer;
101
+ }
102
+ _config = null;
103
+ set config(config) {
104
+ this._config = config;
105
+ }
106
+ get config() {
107
+ if (this._config === null) {
108
+ throw new Error("Sonamu has not been initialized");
109
+ }
110
+ return this._config;
111
+ }
112
+ secrets = getSecrets();
113
+ _storage = null;
114
+ /**
115
+ * StorageManager 인스턴스
116
+ */
117
+ get storage() {
118
+ if (!this._storage) {
119
+ throw new Error("Storage has not been initialized. Check storage config.");
120
+ }
121
+ return this._storage;
122
+ }
123
+ _cache = null;
124
+ /**
125
+ * CacheManager 인스턴스 (BentoCache)
126
+ */
127
+ get cache() {
128
+ if (!this._cache) {
129
+ throw new Error("Cache has not been initialized. Check cache config in sonamu.config.ts.");
130
+ }
131
+ return this._cache;
132
+ }
133
+ _workflows = null;
134
+ get workflows() {
135
+ if (this._workflows === null) {
136
+ throw new Error("Sonamu has not been initialized");
137
+ }
138
+ return this._workflows;
139
+ }
140
+ _auth = null;
141
+ get auth() {
142
+ if (!this._auth) {
143
+ throw new Error("Auth has not been initialized. Check auth config in sonamu.config.ts.");
144
+ }
145
+ return this._auth;
146
+ }
147
+ _devVitestManager = null;
148
+ get devVitestManager() {
149
+ return this._devVitestManager;
150
+ }
151
+ set devVitestManager(manager) {
152
+ this._devVitestManager = manager;
153
+ }
154
+ watcher = null;
155
+ pendingFiles = [];
156
+ hmrStartTime = 0;
157
+ server = null;
158
+ async initForTesting() {
159
+ await this.init(true, false, undefined, true);
160
+ }
161
+ async init(doSilent = false, enableSync = true, apiRootPath, forTesting = false) {
162
+ this.forTesting = forTesting;
163
+ if (this.isInitialized) {
164
+ return;
165
+ }
166
+ if (!doSilent) {
167
+ const chalk = (await import("chalk")).default;
168
+ console.time(chalk.cyan(`Sonamu.init${forTesting ? " for testing" : ""}`));
169
+ }
170
+ const { findApiRootPath } = await import("../utils/utils.js");
171
+ this.apiRootPath = apiRootPath ?? findApiRootPath();
172
+ const { loadConfig } = await import("./config.js");
173
+ this.config = await loadConfig(this.apiRootPath);
174
+ setSDConfig(this.config.i18n);
175
+ this.config.database.database = this.config.database.database ?? "pg";
176
+ this.config.database.defaultOptions.client = this.config.database.database ?? "pg";
177
+ const { configureLogTape } = await import("../logger/configure.js");
178
+ if (this.config.logging !== false) {
179
+ await configureLogTape({ ...this.config.logging });
180
+ }
181
+ const { DB: DB$1 } = await import("../database/db.js");
182
+ this.dbConfig = DB$1.generateDBConfig(this.config.database);
183
+ DB$1.setConfig(this.dbConfig);
184
+ if (!doSilent) {
185
+ const chalk = (await import("chalk")).default;
186
+ console.log(chalk.green("DB Config Loaded!"));
187
+ }
188
+ const { EntityManager } = await import("../entity/entity-manager.js");
189
+ await EntityManager.autoload(doSilent);
190
+ await this.initializeCache(this.config.server.cache, forTesting);
191
+ const authConfig = this.config.server.auth;
192
+ if (authConfig) {
193
+ const mergedFieldMappings = merge(BASE_FIELD_MAPPINGS, authConfig);
194
+ const { betterAuth } = await import("better-auth");
195
+ const { sonamuKnexAdapter } = await import("../auth/knex-adapter.js");
196
+ this._auth = betterAuth({
197
+ database: sonamuKnexAdapter(),
198
+ ...mergedFieldMappings
199
+ });
200
+ }
201
+ if (forTesting) {
202
+ this.isInitialized = true;
203
+ return;
204
+ }
205
+ await this.initializeWorkflows(this.config.tasks);
206
+ const { Syncer } = await import("../syncer/syncer.js");
207
+ this.syncer = new Syncer();
208
+ await this.syncer.autoloadTypes();
209
+ await this.syncer.autoloadModels();
210
+ await this.syncer.autoloadApis();
211
+ await this.syncer.autoloadWorkflows();
212
+ const { TemplateManager } = await import("../template/index.js");
213
+ await TemplateManager.autoload();
214
+ await this.syncer.autoloadSSRRoutes();
215
+ const { isLocal, isTest, isHotReloadServer } = await import("../utils/controller.js");
216
+ if (isLocal() && !isTest() && isHotReloadServer() && enableSync) {
217
+ await this.syncer.sync();
218
+ await this.startWatcher();
219
+ }
220
+ this.isInitialized = true;
221
+ if (!doSilent) {
222
+ const chalk = (await import("chalk")).default;
223
+ console.timeEnd(chalk.cyan("Sonamu.init"));
224
+ }
225
+ }
226
+ async createServer(initOptions) {
227
+ if (!this.isInitialized) {
228
+ await this.init(initOptions?.doSilent, initOptions?.enableSync);
229
+ }
230
+ const options = this.config.server;
231
+ const { default: fastify } = await import("fastify");
232
+ const { getLogTapeFastifyLogger } = await import("@logtape/fastify");
233
+ const server = fastify({
234
+ ...options.fastify,
235
+ logger: this.config.logging !== false ? getLogTapeFastifyLogger({ category: this.config.logging?.fastifyCategory ?? ["fastify"] }) : undefined
236
+ });
237
+ this.server = server;
238
+ if (options.storage) {
239
+ const { StorageManager } = await import("../storage/storage-manager.js");
240
+ this._storage = new StorageManager(options.storage);
241
+ }
242
+ if (options.plugins) {
243
+ await this.registerPlugins(server, options.plugins);
244
+ }
245
+ if (options.auth) {
246
+ await this.registerBetterAuth(server, options.auth);
247
+ }
248
+ await this.withFastify(server, options.apiConfig, {
249
+ enableSync: initOptions?.enableSync,
250
+ doSilent: initOptions?.doSilent
251
+ });
252
+ await this.boot(server, options);
253
+ return server;
254
+ }
255
+ async withFastify(server, config, options) {
256
+ if (!this.isInitialized) {
257
+ await this.init(options?.doSilent, options?.enableSync);
258
+ }
259
+ this.server = server;
260
+ const timezone = this.config.api.timezone;
261
+ if (timezone) {
262
+ const { formatInTimeZone } = await import("date-fns-tz");
263
+ const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/;
264
+ const DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX";
265
+ server.setReplySerializer((payload) => {
266
+ return JSON.stringify(payload, (_key, value) => {
267
+ if (typeof value === "string" && ISO_DATE_REGEX.test(value)) {
268
+ return formatInTimeZone(new Date(value), timezone, DATE_FORMAT);
269
+ }
270
+ return value;
271
+ });
272
+ });
273
+ if (!options?.doSilent) {
274
+ const chalk = (await import("chalk")).default;
275
+ console.log(chalk.green(`Timezone set to ${timezone}`));
276
+ }
277
+ }
278
+ server.get(`${this.config.api.route.prefix}/routes`, async (_request, _reply) => {
279
+ return this.syncer.apis;
280
+ });
281
+ server.get(`${this.config.api.route.prefix}/healthcheck`, async (_request, _reply) => {
282
+ return "ok";
283
+ });
284
+ const { isLocal } = await import("../utils/controller.js");
285
+ if (isLocal()) {
286
+ const { sonamuUIApiPlugin } = await import("../ui/api.js");
287
+ server.register(sonamuUIApiPlugin);
288
+ }
289
+ if (isLocal() && this.config.test?.devRunner?.enabled) {
290
+ const { registerDevTestRoutes } = await import("../testing/dev-test-routes.js");
291
+ await registerDevTestRoutes(server, this.config.test.devRunner);
292
+ }
293
+ const webPath = path.join(this.appRootPath, "web");
294
+ const hasWeb = await exists(webPath);
295
+ const pluginCompress = this.config.server.plugins?.compress;
296
+ const globalCompressOptions = pluginCompress ? pluginCompress === true ? {
297
+ threshold: 1024,
298
+ encodings: [
299
+ "br",
300
+ "gzip",
301
+ "deflate"
302
+ ]
303
+ } : {
304
+ threshold: pluginCompress.threshold,
305
+ encodings: pluginCompress.encodings,
306
+ customTypes: pluginCompress.customTypes
307
+ } : undefined;
308
+ if (isLocal()) {
309
+ const disableIntegratedWeb = process.env.SONAMU_DISABLE_INTEGRATED_WEB === "yes";
310
+ if (hasWeb && !disableIntegratedWeb) {
311
+ await this.setupDevServerWithVite(server, webPath, config);
312
+ } else {
313
+ this.setupDevServer(server, config);
314
+ }
315
+ } else {
316
+ for (const api of this.syncer.apis) {
317
+ if (this.syncer.models[api.modelName] === undefined) {
318
+ throw new Error(`정의되지 않은 모델에 접근 ${api.modelName}`);
319
+ }
320
+ server.route({
321
+ method: api.options.httpMethod ?? "GET",
322
+ url: this.config.api.route.prefix + api.path,
323
+ handler: this.createApiHandler(api, config),
324
+ compress: toFastifyCompressOption(api.options.compress, globalCompressOptions)
325
+ });
326
+ }
327
+ await this.setupStaticWebServer(server, config, globalCompressOptions);
328
+ }
329
+ }
330
+ /**
331
+ * dev 모드 공통: catch-all에서 syncer.apis를 동적으로 탐색하여 API 요청을 처리합니다.
332
+ * server.route()로 개별 등록하면 handler가 고정되어 HMR이 동작하지 않으므로,
333
+ * 매 요청마다 syncer.apis를 조회하는 이 방식을 사용합니다.
334
+ *
335
+ * 요청이 /api(정확히는 this.config.api.route.prefix)로 시작하지 않는 경우라면 null을 반환하며 끝냅니다.
336
+ */
337
+ handleDevApiRequest(request, config) {
338
+ const url = this.getPathnameFromUrl(request.url);
339
+ const method = request.method;
340
+ if (!url.startsWith(this.config.api.route.prefix)) {
341
+ return null;
342
+ }
343
+ const matchedApi = this.syncer.apis.find((api) => {
344
+ if (this.syncer.models[api.modelName] === undefined) {
345
+ return false;
346
+ }
347
+ const apiMethod = api.options.httpMethod ?? "GET";
348
+ if (apiMethod !== method) return false;
349
+ const fullPath = this.config.api.route.prefix + api.path;
350
+ return this.isPathPatternMatch(fullPath, url);
351
+ });
352
+ if (!matchedApi) {
353
+ throw new NotFoundException(SD("error.api.notFound"));
354
+ }
355
+ return this.createApiHandler(matchedApi, config);
356
+ }
357
+ /**
358
+ * dev api 모드: Vite 없이 API 동적 라우팅만 제공합니다.
359
+ * HMR을 위해 catch-all에서 매 요청마다 syncer.apis를 조회합니다.
360
+ */
361
+ setupDevServer(server, config) {
362
+ server.route({
363
+ method: [
364
+ "GET",
365
+ "HEAD",
366
+ "POST",
367
+ "PUT",
368
+ "DELETE",
369
+ "PATCH"
370
+ ],
371
+ url: `${this.config.api.route.prefix}/*`,
372
+ handler: async (request, reply) => {
373
+ const handler = this.handleDevApiRequest(request, config);
374
+ if (handler) {
375
+ return handler(request, reply);
376
+ }
377
+ throw new NotFoundException(SD("error.api.notFound"));
378
+ }
379
+ });
380
+ }
381
+ viteServer = null;
382
+ /**
383
+ * dev all 모드: Vite Dev Server를 통합하여 API + SSR + CSR을 모두 제공합니다.
384
+ * API 동적 매칭은 handleDevApiRequest를 공유합니다.
385
+ */
386
+ async setupDevServerWithVite(server, webPath, config) {
387
+ await server.register((await import("@fastify/middie")).default);
388
+ const vite = await import("vite");
389
+ this.viteServer = await vite.createServer({
390
+ root: webPath,
391
+ server: {
392
+ middlewareMode: true,
393
+ hmr: { server: server.server }
394
+ },
395
+ appType: "custom"
396
+ });
397
+ server.use((req, res, next) => {
398
+ if (req.url?.startsWith(this.config.api.route.prefix) || req.url?.startsWith("/sonamu-ui")) {
399
+ return next();
400
+ }
401
+ return this.viteServer.middlewares(req, res, next);
402
+ });
403
+ server.route({
404
+ method: [
405
+ "GET",
406
+ "HEAD",
407
+ "POST",
408
+ "PUT",
409
+ "DELETE",
410
+ "PATCH"
411
+ ],
412
+ url: "/*",
413
+ handler: async (request, reply) => {
414
+ const result = this.handleDevApiRequest(request, config);
415
+ if (result) {
416
+ return result(request, reply);
417
+ }
418
+ const url = request.url;
419
+ const { matchSSRRoute, renderSSR } = await import("../ssr/index.js");
420
+ const ssrMatch = matchSSRRoute(url);
421
+ if (ssrMatch) {
422
+ console.log(`[SSR] Matched route: ${ssrMatch.route.path}`);
423
+ const html = await renderSSR(url, ssrMatch.route, ssrMatch.params, request, reply, config, this.viteServer);
424
+ reply.type("text/html");
425
+ return html;
426
+ }
427
+ try {
428
+ const fs$1 = await import("node:fs/promises");
429
+ let template = await fs$1.readFile(path.join(this.viteServer.config.root, "index.html"), "utf-8");
430
+ template = await this.viteServer.transformIndexHtml(url, template);
431
+ reply.type("text/html");
432
+ return template;
433
+ } catch (e) {
434
+ this.viteServer.ssrFixStacktrace(e);
435
+ console.error(e);
436
+ reply.status(500);
437
+ return e.message;
438
+ }
439
+ }
440
+ });
441
+ server.addHook("onClose", async () => {
442
+ await this.viteServer.close();
443
+ });
444
+ console.log("✓ Vite dev server integrated");
445
+ }
446
+ async setupStaticWebServer(server, config, globalCompressOptions) {
447
+ const webDistPath = path.join(this.apiRootPath, "web-dist", "client");
448
+ const ssrPath = path.join(this.apiRootPath, "web-dist", "server");
449
+ const ssrEntryPath = path.join(ssrPath, "entry-server.generated.js");
450
+ const ssrRoutesPath = path.join(this.apiRootPath, "dist", "ssr", "routes.js");
451
+ if (!await exists(webDistPath)) {
452
+ console.warn(`⚠ Web dist not found: ${webDistPath}`);
453
+ return;
454
+ }
455
+ const ssrAvailable = await exists(ssrEntryPath);
456
+ if (!ssrAvailable) {
457
+ console.warn(`⚠ SSR entry not found: ${ssrEntryPath}`);
458
+ console.warn(" SSR will be disabled. Only CSR will work.");
459
+ }
460
+ if (ssrAvailable) {
461
+ if (await exists(ssrRoutesPath)) {
462
+ await import(ssrRoutesPath);
463
+ console.log("✓ SSR routes loaded");
464
+ } else {
465
+ console.warn(`⚠ SSR routes not found: ${ssrRoutesPath}`);
466
+ }
467
+ }
468
+ server.get("/assets/:filename", async (request, reply) => {
469
+ const requestedFile = request.params.filename;
470
+ const assetsDir = path.join(webDistPath, "assets");
471
+ const safeFilePath = this.resolvePathWithinBaseDir(assetsDir, requestedFile);
472
+ if (safeFilePath === null) {
473
+ reply.status(403).send();
474
+ return;
475
+ }
476
+ const normalizedRequestedFile = path.relative(assetsDir, safeFilePath).replace(/\\/g, "/");
477
+ const assetPath = `/assets/${normalizedRequestedFile}`;
478
+ const getCacheControlForAsset = () => {
479
+ const cacheReq = {
480
+ type: "assets",
481
+ url: request.url,
482
+ path: assetPath,
483
+ method: request.method
484
+ };
485
+ if (config.cacheControlHandler) {
486
+ const result = config.cacheControlHandler(cacheReq);
487
+ if (result) return result;
488
+ }
489
+ return CachePresets.immutable;
490
+ };
491
+ if (/^index-[a-f0-9]+\.(js|css)$/.test(normalizedRequestedFile)) {
492
+ const ext = normalizedRequestedFile.split(".").pop();
493
+ const files = await fs.readdir(assetsDir);
494
+ const currentFile = files.find((f) => f.startsWith("index-") && f.endsWith(`.${ext}`));
495
+ if (currentFile) {
496
+ const filePath$1 = path.join(assetsDir, currentFile);
497
+ const content = await fs.readFile(filePath$1);
498
+ reply.type(ext === "js" ? "application/javascript" : "text/css");
499
+ applyCacheHeaders(reply, getCacheControlForAsset());
500
+ return reply.send(content);
501
+ }
502
+ }
503
+ const filePath = safeFilePath;
504
+ if (await exists(filePath)) {
505
+ const content = await fs.readFile(filePath);
506
+ const ext = normalizedRequestedFile.split(".").pop();
507
+ reply.type(ext === "js" ? "application/javascript" : ext === "css" ? "text/css" : "");
508
+ if (normalizedRequestedFile.includes("-")) {
509
+ applyCacheHeaders(reply, getCacheControlForAsset());
510
+ }
511
+ return reply.send(content);
512
+ }
513
+ reply.status(404).send();
514
+ });
515
+ if (ssrAvailable) {
516
+ const { getSSRRoutes } = await import("../ssr/index.js");
517
+ const { renderSSR } = await import("../ssr/renderer.js");
518
+ const ssrRoutes = getSSRRoutes();
519
+ for (const route of ssrRoutes) {
520
+ server.route({
521
+ method: ["GET", "HEAD"],
522
+ url: route.path,
523
+ compress: toFastifyCompressOption(route.compress ?? true, globalCompressOptions),
524
+ handler: async (request, reply) => {
525
+ const url = request.url;
526
+ console.log(`[SSR] Matched route: ${route.path}`);
527
+ const params = this.extractPathParams(route.path, url);
528
+ const html = await renderSSR(url, route, params, request, reply, config);
529
+ reply.type("text/html");
530
+ return html;
531
+ }
532
+ });
533
+ }
534
+ }
535
+ server.route({
536
+ method: ["GET", "HEAD"],
537
+ url: "*",
538
+ handler: async (request, reply) => {
539
+ if (request.url.startsWith("/api") || request.url.startsWith("/sonamu-ui")) {
540
+ reply.status(404).send();
541
+ return;
542
+ }
543
+ if (config.cacheControlHandler) {
544
+ const csrCacheReq = {
545
+ type: "csr",
546
+ url: request.url,
547
+ path: request.url.split("?")[0],
548
+ method: request.method
549
+ };
550
+ const csrCacheConfig = config.cacheControlHandler(csrCacheReq);
551
+ if (csrCacheConfig) {
552
+ applyCacheHeaders(reply, csrCacheConfig);
553
+ }
554
+ }
555
+ const requestPath = this.getPathnameFromUrl(request.url);
556
+ const safeFilePath = this.resolvePathWithinBaseDir(webDistPath, requestPath);
557
+ if (safeFilePath === null) {
558
+ reply.status(403).send();
559
+ return;
560
+ }
561
+ if (await fileExists(safeFilePath)) {
562
+ const content = await fs.readFile(safeFilePath);
563
+ return reply.type(lookup(safeFilePath) || "application/octet-stream").send(content);
564
+ }
565
+ const indexPath = path.join(webDistPath, "index.html");
566
+ return reply.type("text/html").send(await fs.readFile(indexPath, "utf-8"));
567
+ }
568
+ });
569
+ console.log(`✓ Static web server configured with ${ssrAvailable ? "SSR" : "CSR only"} support`);
570
+ }
571
+ createApiHandler(api, config) {
572
+ return async (request, reply) => {
573
+ const context = await this.createContext(config, request, reply);
574
+ return this.asyncLocalStorage.run({ context }, async () => {
575
+ (api.options.guards ?? []).every((guard) => config.guardHandler(guard, request, api));
576
+ const { getZodObjectFromApi } = await import("./code-converters.js");
577
+ const ReqType = getZodObjectFromApi(api, this.syncer.types);
578
+ const which = api.options.httpMethod === "GET" ? "query" : "body";
579
+ let reqBody;
580
+ const files = {
581
+ bufferedFiles: [],
582
+ uploadedFiles: []
583
+ };
584
+ try {
585
+ const body = request[which] ?? {};
586
+ if (api.uploadOptions) {
587
+ const parts = request.parts({ limits: api.uploadOptions.limits });
588
+ const fields = {};
589
+ if (api.uploadOptions.consume === "buffer" || !api.uploadOptions.consume) {
590
+ for await (const part of parts) {
591
+ if (part.type === "file") {
592
+ const buffer = await part.toBuffer();
593
+ files.bufferedFiles.push(new BufferedFile(part, buffer));
594
+ } else if (part.type === "field") {
595
+ fields[part.fieldname] = String(part.value);
596
+ }
597
+ }
598
+ } else if (api.uploadOptions.consume === "stream") {
599
+ const diskName = api.uploadOptions.destination;
600
+ const disk = this.storage.use(diskName);
601
+ const keyGenerator = api.uploadOptions.keyGenerator ?? this.config.server.storage?.keyGenerator ?? defaultKeyGenerator;
602
+ for await (const part of parts) {
603
+ if (part.type === "file") {
604
+ const key = await keyGenerator({
605
+ filename: part.filename,
606
+ mimetype: part.mimetype
607
+ });
608
+ await disk.putStream(key, part.file, { contentType: part.mimetype });
609
+ const url = await disk.getUrl(key);
610
+ const signedUrl = await disk.getSignedUrl(key);
611
+ files.uploadedFiles.push(new UploadedFile({
612
+ filename: part.filename,
613
+ mimetype: part.mimetype,
614
+ size: part.file.bytesRead,
615
+ url,
616
+ signedUrl,
617
+ key,
618
+ diskName
619
+ }));
620
+ } else if (part.type === "field") {
621
+ fields[part.fieldname] = String(part.value);
622
+ }
623
+ }
624
+ }
625
+ const qs = await import("qs");
626
+ const parsed = qs.default.parse(fields);
627
+ Object.assign(body, parsed);
628
+ }
629
+ const { fastifyCaster } = await import("./caster.js");
630
+ reqBody = fastifyCaster(ReqType).parse(body);
631
+ } catch (e) {
632
+ const { ZodError } = await import("zod");
633
+ if (e instanceof ZodError) {
634
+ const { humanizeZodError } = await import("../utils/zod-error.js");
635
+ const messages = humanizeZodError(e).map((issue) => issue.message).join(" ");
636
+ const { BadRequestException } = await import("../exceptions/so-exceptions.js");
637
+ throw new BadRequestException(messages, { zodError: e });
638
+ } else {
639
+ throw e;
640
+ }
641
+ }
642
+ reply.type(api.options.contentType ?? "application/json");
643
+ const apiCacheConfig = this.getApiCacheControl(api, request, config);
644
+ if (apiCacheConfig) {
645
+ applyCacheHeaders(reply, apiCacheConfig);
646
+ }
647
+ if (api.uploadOptions) {
648
+ const consume = api.uploadOptions.consume ?? "buffer";
649
+ if (consume === "buffer") {
650
+ context.bufferedFiles = files.bufferedFiles;
651
+ } else if (consume === "stream") {
652
+ context.uploadedFiles = files.uploadedFiles;
653
+ }
654
+ }
655
+ const { ApiParamType } = await import("../types/types.js");
656
+ const args = api.parameters.map((param) => {
657
+ if (ApiParamType.isContext(param.type)) {
658
+ return context;
659
+ } else {
660
+ return reqBody[param.name];
661
+ }
662
+ });
663
+ return this.invokeModelMethod(api, args, reply);
664
+ });
665
+ };
666
+ }
667
+ /**
668
+ * URL에서 path params를 추출합니다.
669
+ * 예: pattern="/admin/companies/:companyId", url="/admin/companies/123" → { companyId: "123" }
670
+ */
671
+ extractPathParams(pattern, url) {
672
+ const patternParts = pattern.split("/").filter(Boolean);
673
+ const urlParts = this.getPathnameFromUrl(url).split("/").filter(Boolean);
674
+ const params = {};
675
+ for (let i = 0; i < patternParts.length; i++) {
676
+ if (patternParts[i].startsWith(":")) {
677
+ params[patternParts[i].slice(1)] = urlParts[i];
678
+ }
679
+ }
680
+ return params;
681
+ }
682
+ isPathPatternMatch(pattern, url) {
683
+ const patternParts = pattern.split("/").filter(Boolean);
684
+ const urlParts = this.getPathnameFromUrl(url).split("/").filter(Boolean);
685
+ if (patternParts.length !== urlParts.length) {
686
+ return false;
687
+ }
688
+ for (let i = 0; i < patternParts.length; i++) {
689
+ const patternPart = patternParts[i];
690
+ const urlPart = urlParts[i];
691
+ if (patternPart.startsWith(":")) {
692
+ continue;
693
+ }
694
+ if (patternPart !== urlPart) {
695
+ return false;
696
+ }
697
+ }
698
+ return true;
699
+ }
700
+ getPathnameFromUrl(url) {
701
+ return url.split("?")[0];
702
+ }
703
+ resolvePathWithinBaseDir(baseDir, inputPath) {
704
+ try {
705
+ const decoded = decodeURIComponent(inputPath).replace(/\\/g, "/");
706
+ if (decoded.includes("\0")) {
707
+ return null;
708
+ }
709
+ const relativePath = decoded.replace(/^\/+/, "");
710
+ const resolvedPath = path.resolve(baseDir, relativePath);
711
+ const relativeFromBase = path.relative(baseDir, resolvedPath);
712
+ if (relativeFromBase.startsWith("..") || path.isAbsolute(relativeFromBase)) {
713
+ return null;
714
+ }
715
+ return resolvedPath;
716
+ } catch {
717
+ return null;
718
+ }
719
+ }
720
+ /**
721
+ * API 응답에 적용할 Cache-Control 설정을 결정합니다.
722
+ * 우선순위: 개별 지정 > cacheControlHandler
723
+ */
724
+ getApiCacheControl(api, request, config) {
725
+ if (api.options.cacheControl) {
726
+ return api.options.cacheControl;
727
+ }
728
+ if (config.cacheControlHandler) {
729
+ const cacheReq = {
730
+ type: "api",
731
+ url: request.url,
732
+ path: request.routeOptions?.url ?? request.url.split("?")[0],
733
+ method: request.method,
734
+ api
735
+ };
736
+ const result = config.cacheControlHandler(cacheReq);
737
+ if (result) return result;
738
+ }
739
+ return null;
740
+ }
741
+ /**
742
+ * SSR용 API 호출 (HTTP 오버헤드 없이 직접 호출)
743
+ * createApiHandler의 로직을 재사용하되, request 파싱 대신 params 직접 사용
744
+ */
745
+ async invokeApiForSSR(api, params, config, request, reply) {
746
+ const context = await this.createContext(config, request, reply);
747
+ return this.asyncLocalStorage.run({ context }, async () => {
748
+ const { ApiParamType } = await import("../types/types.js");
749
+ let paramsIndex = 0;
750
+ const args = api.parameters.map((param) => {
751
+ if (ApiParamType.isContext(param.type)) {
752
+ return context;
753
+ }
754
+ return params[paramsIndex++];
755
+ });
756
+ return this.invokeModelMethod(api, args, reply);
757
+ });
758
+ }
759
+ async invokeModelMethod(api, args, reply) {
760
+ const model = this.syncer.models[api.modelName];
761
+ const result = await model[api.methodName].apply(model, args);
762
+ reply.type(api.options.contentType ?? "application/json");
763
+ return result;
764
+ }
765
+ async createContext(config, request, reply) {
766
+ const { createSSEFactory } = await import("../stream/sse.js");
767
+ const createSSE = ((_request, _reply, _events) => createSSEFactory(_request.socket, _reply, _events)).bind(null, request, reply);
768
+ const locale = this.detectLocale(request.headers["accept-language"], this.config.i18n.supportedLocales) ?? this.config.i18n.defaultLocale;
769
+ const headers = convertFastifyHeadersToStandard(request.headers);
770
+ const session = await this._auth?.api.getSession({ headers }) ?? null;
771
+ const context = { ...await Promise.resolve(config.contextProvider({
772
+ request,
773
+ reply,
774
+ headers: request.headers,
775
+ createSSE,
776
+ naiteStore: new Map(),
777
+ locale,
778
+ user: session?.user ?? null,
779
+ session: session?.session ?? null
780
+ }, request, reply)) };
781
+ return context;
782
+ }
783
+ /**
784
+ * Accept-Language 헤더에서 지원하는 locale을 찾습니다.
785
+ * @example "ko-KR,ko;q=0.9,en;q=0.8" → "ko"
786
+ */
787
+ detectLocale(acceptLanguage, supported) {
788
+ if (!acceptLanguage) return undefined;
789
+ const langs = acceptLanguage.split(",").map((lang) => {
790
+ const [code] = lang.split(";");
791
+ return code.trim().split("-")[0];
792
+ });
793
+ return langs.find((lang) => supported.includes(lang));
794
+ }
795
+ async startWatcher() {
796
+ const watchPath = [path.join(this.apiRootPath, "src")];
797
+ const chokidar = (await import("chokidar")).default;
798
+ this.watcher = chokidar.watch(watchPath, {
799
+ ignored: (path$1, stats) => !!stats?.isFile() && !path$1.endsWith(".ts") && !path$1.endsWith(".json"),
800
+ persistent: true,
801
+ ignoreInitial: true
802
+ });
803
+ this.watcher.on("all", async (event, filePath) => {
804
+ const absolutePath = filePath;
805
+ assert(absolutePath.startsWith(this.apiRootPath), "File path is not within the API root path");
806
+ if (event !== "change" && event !== "add") {
807
+ return;
808
+ }
809
+ try {
810
+ const isConfigTs = filePath === path.join(this.apiRootPath, "src", "sonamu.config.ts");
811
+ if (isConfigTs) {
812
+ const relativePath = filePath.replace(this.apiRootPath, "api");
813
+ const chalk = (await import("chalk")).default;
814
+ console.log(chalk.bold(`Detected(${event}): ${chalk.blue(relativePath)} - Restarting...`));
815
+ process.kill(process.pid, "SIGUSR2");
816
+ return;
817
+ }
818
+ await this.handleFileChange(event, absolutePath);
819
+ } catch (e) {
820
+ console.error(e);
821
+ }
822
+ });
823
+ }
824
+ async runScript(fn) {
825
+ await this.init(true, false, undefined, false);
826
+ try {
827
+ await fn();
828
+ } finally {
829
+ await this.destroy();
830
+ }
831
+ }
832
+ async registerPlugins(server, plugins) {
833
+ if (!plugins) {
834
+ return;
835
+ }
836
+ if (plugins.compress) {
837
+ const compressPlugin = (await import("@fastify/compress")).default;
838
+ const defaultOptions = {
839
+ threshold: 1024,
840
+ encodings: [
841
+ "br",
842
+ "gzip",
843
+ "deflate"
844
+ ]
845
+ };
846
+ if (plugins.compress === true) {
847
+ server.register(compressPlugin, defaultOptions);
848
+ } else {
849
+ server.register(compressPlugin, {
850
+ ...defaultOptions,
851
+ ...plugins.compress
852
+ });
853
+ }
854
+ }
855
+ const pluginsModules = {
856
+ cors: "@fastify/cors",
857
+ formbody: "@fastify/formbody",
858
+ multipart: "@fastify/multipart",
859
+ qs: "fastify-qs",
860
+ sse: "fastify-sse-v2",
861
+ static: "@fastify/static"
862
+ };
863
+ const registerPlugin = async (key, pluginName) => {
864
+ const option = plugins[key];
865
+ if (!option) return;
866
+ if (option === true) {
867
+ server.register((await import(pluginName)).default);
868
+ } else {
869
+ server.register((await import(pluginName)).default, option);
870
+ }
871
+ };
872
+ for (const [key, pluginName] of Object.entries(pluginsModules)) {
873
+ await registerPlugin(key, pluginName);
874
+ }
875
+ if (plugins.custom) {
876
+ plugins.custom(server);
877
+ }
878
+ }
879
+ /**
880
+ * better-auth 라우트를 등록합니다.
881
+ * /api/auth/* 경로로 인증 API가 자동 등록됩니다.
882
+ */
883
+ async registerBetterAuth(server, options) {
884
+ if (!options) return;
885
+ const basePath = options.basePath ?? "/api/auth";
886
+ server.route({
887
+ method: ["GET", "POST"],
888
+ url: `${basePath}/*`,
889
+ handler: async (request, reply) => {
890
+ const url = new URL(request.url, `http://${request.headers.host}`);
891
+ const headers = convertFastifyHeadersToStandard(request.headers);
892
+ const req = new Request(url.toString(), {
893
+ method: request.method,
894
+ headers,
895
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
896
+ });
897
+ const response = await this.auth.handler(req);
898
+ reply.status(response.status);
899
+ response.headers.forEach((value, key) => {
900
+ reply.header(key, value);
901
+ });
902
+ return reply.send(response.body ? await response.text() : null);
903
+ }
904
+ });
905
+ const chalk = (await import("chalk")).default;
906
+ console.log(chalk.green(`✓ better-auth registered at ${basePath}/*`));
907
+ }
908
+ async initializeCache(config, forTesting) {
909
+ const { setCacheManagerRef } = await import("../cache/decorator.js");
910
+ if (forTesting) {
911
+ const { createTestCacheManager } = await import("../cache/cache-manager.js");
912
+ this._cache = createTestCacheManager();
913
+ setCacheManagerRef(this._cache);
914
+ return;
915
+ }
916
+ if (!config) {
917
+ setCacheManagerRef(null);
918
+ return;
919
+ }
920
+ const { createCacheManager } = await import("../cache/cache-manager.js");
921
+ this._cache = createCacheManager(config);
922
+ setCacheManagerRef(this._cache);
923
+ }
924
+ async initializeWorkflows(options) {
925
+ const { WorkflowManager } = await import("../tasks/workflow-manager.js");
926
+ this._workflows = new WorkflowManager(DB.getDBConfig("w"));
927
+ if (!options) {
928
+ return;
929
+ }
930
+ const enableWorker = options.enableWorker ?? isDaemonServer();
931
+ const defaultWorkerOptions = {
932
+ concurrency: os.cpus().length - 1,
933
+ usePubSub: true,
934
+ listenDelay: 500
935
+ };
936
+ if (enableWorker) {
937
+ this.workflows.setupWorker({
938
+ ...defaultWorkerOptions,
939
+ ...options.workerOptions
940
+ });
941
+ }
942
+ }
943
+ async boot(server, options) {
944
+ const port = options.listen?.port ?? 3e3;
945
+ const host = options.listen?.host ?? "localhost";
946
+ server.addHook("onClose", async () => {
947
+ await options.lifecycle?.onShutdown?.(server);
948
+ await this.workflows.destroy();
949
+ await this.destroy();
950
+ });
951
+ const shutdown = async () => {
952
+ try {
953
+ await server.close();
954
+ process.exit(0);
955
+ } catch (err) {
956
+ console.error("Error during shutdown:", err);
957
+ process.exit(1);
958
+ }
959
+ };
960
+ process.on("SIGINT", shutdown);
961
+ process.on("SIGTERM", shutdown);
962
+ if (options.lifecycle?.onError) {
963
+ server.setErrorHandler(options.lifecycle?.onError);
964
+ }
965
+ server.listen({
966
+ port,
967
+ host
968
+ }).then(async () => {
969
+ await this.workflows.startWorker();
970
+ await options.lifecycle?.onStart?.(server);
971
+ }).catch(async (err) => {
972
+ const chalk = (await import("chalk")).default;
973
+ console.error(chalk.red("Failed to start server:", err));
974
+ await shutdown();
975
+ });
976
+ }
977
+ async handleFileChange(event, filePath) {
978
+ if (this.pendingFiles.length === 0) {
979
+ this.hmrStartTime = Date.now();
980
+ }
981
+ this.pendingFiles.push(filePath);
982
+ const relativePath = path.relative(this.apiRootPath, filePath);
983
+ const chalk = (await import("chalk")).default;
984
+ console.log(chalk.bold(`Detected(${event}): ${chalk.blue(relativePath)}`));
985
+ await this.syncer.syncFromWatcher(event, filePath);
986
+ this.pendingFiles = this.pendingFiles.slice(1);
987
+ if (this.pendingFiles.length === 0) {
988
+ await this.finishHMR();
989
+ }
990
+ }
991
+ async finishHMR() {
992
+ await this.syncer.renewChecksums();
993
+ const endTime = Date.now();
994
+ const totalTime = endTime - this.hmrStartTime;
995
+ const [chalk, { centerText }] = await Promise.all([(await import("chalk")).default, import("../utils/console-util.js")]);
996
+ const msg = `HMR Done! ${chalk.bold.white(`${totalTime}ms`)}`;
997
+ console.log(chalk.black.bgGreen(centerText(msg)));
998
+ }
999
+ async destroy() {
1000
+ const { BaseModel } = await import("../database/base-model.js");
1001
+ await BaseModel.destroy();
1002
+ await Promise.allSettled([
1003
+ this._workflows?.destroy() ?? Promise.resolve(),
1004
+ this._cache?.disconnect() ?? Promise.resolve(),
1005
+ this._devVitestManager?.shutdown() ?? Promise.resolve(),
1006
+ this.watcher?.close() ?? Promise.resolve(),
1007
+ dispose()
1008
+ ]);
1009
+ }
1010
+ };
1011
+ Sonamu = new SonamuClass();
1012
+ }));
1135
1013
 
1136
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvc29uYW11LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGRpc3Bvc2UgYXMgbG9ndGFwZURpc3Bvc2UgfSBmcm9tIFwiQGxvZ3RhcGUvbG9ndGFwZVwiO1xuaW1wb3J0IGFzc2VydCBmcm9tIFwiYXNzZXJ0XCI7XG5pbXBvcnQgeyBBc3luY0xvY2FsU3RvcmFnZSB9IGZyb20gXCJhc3luY19ob29rc1wiO1xuaW1wb3J0IHR5cGUgeyBBdXRoIH0gZnJvbSBcImJldHRlci1hdXRoXCI7XG5pbXBvcnQgdHlwZSB7IEZTV2F0Y2hlciB9IGZyb20gXCJjaG9raWRhclwiO1xuaW1wb3J0IHR5cGUgeyBGYXN0aWZ5SW5zdGFuY2UsIEZhc3RpZnlSZXBseSwgRmFzdGlmeVJlcXVlc3QgfSBmcm9tIFwiZmFzdGlmeVwiO1xuaW1wb3J0IGZzIGZyb20gXCJmcy9wcm9taXNlc1wiO1xuaW1wb3J0IHR5cGUgeyBJbmNvbWluZ01lc3NhZ2UsIFNlcnZlciwgU2VydmVyUmVzcG9uc2UgfSBmcm9tIFwiaHR0cFwiO1xuaW1wb3J0IG1pbWUsIHsgbG9va3VwIGFzIG1pbWVMb29rdXAgfSBmcm9tIFwibWltZS10eXBlc1wiO1xuaW1wb3J0IG9zIGZyb20gXCJvc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB0eXBlIHsgWm9kT2JqZWN0IH0gZnJvbSBcInpvZFwiO1xuaW1wb3J0IHtcbiAgQkFTRV9GSUVMRF9NQVBQSU5HUyxcbiAgY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZCxcbiAgY3JlYXRlTW9ja1NTRUZhY3RvcnksXG4gIERCLFxuICBpc0RhZW1vblNlcnZlcixcbiAgbWVyZ2UsXG4gIE5vdEZvdW5kRXhjZXB0aW9uLFxufSBmcm9tIFwiLi5cIjtcbmltcG9ydCB0eXBlIHsgQ2FjaGVDb25maWcsIENhY2hlTWFuYWdlciB9IGZyb20gXCIuLi9jYWNoZS90eXBlc1wiO1xuaW1wb3J0IHsgYXBwbHlDYWNoZUhlYWRlcnMsIENhY2hlUHJlc2V0cyB9IGZyb20gXCIuLi9jYWNoZS1jb250cm9sL2NhY2hlLWNvbnRyb2xcIjtcbmltcG9ydCB0eXBlIHsgQ2FjaGVDb250cm9sQ29uZmlnLCBDYWNoZUNvbnRyb2xSZXF1ZXN0IH0gZnJvbSBcIi4uL2NhY2hlLWNvbnRyb2wvdHlwZXNcIjtcbmltcG9ydCB7IHRvRmFzdGlmeUNvbXByZXNzT3B0aW9uIH0gZnJvbSBcIi4uL2NvbXByZXNzL2NvbXByZXNzXCI7XG5pbXBvcnQgdHlwZSB7IENvbXByZXNzT3B0aW9ucyB9IGZyb20gXCIuLi9jb21wcmVzcy90eXBlc1wiO1xuaW1wb3J0IHR5cGUgeyBTb25hbXVEQkNvbmZpZyB9IGZyb20gXCIuLi9kYXRhYmFzZS9kYlwiO1xuaW1wb3J0IHsgU0QgfSBmcm9tIFwiLi4vZGljdC9zZFwiO1xuaW1wb3J0IHR5cGUgeyBMb2NhbGl6ZWRTdHJpbmcgfSBmcm9tIFwiLi4vZGljdC90eXBlc1wiO1xuaW1wb3J0IHsgTmFpdGUgfSBmcm9tIFwiLi4vbmFpdGUvbmFpdGVcIjtcbmltcG9ydCB7IEJ1ZmZlcmVkRmlsZSB9IGZyb20gXCIuLi9zdG9yYWdlL2J1ZmZlcmVkLWZpbGVcIjtcbmltcG9ydCB0eXBlIHsgU3RvcmFnZU1hbmFnZXIgfSBmcm9tIFwiLi4vc3RvcmFnZS9zdG9yYWdlLW1hbmFnZXJcIjtcbmltcG9ydCB0eXBlIHsgS2V5R2VuZXJhdG9yIH0gZnJvbSBcIi4uL3N0b3JhZ2UvdHlwZXNcIjtcbmltcG9ydCB7IFVwbG9hZGVkRmlsZSB9IGZyb20gXCIuLi9zdG9yYWdlL3VwbG9hZGVkLWZpbGVcIjtcbmltcG9ydCB0eXBlIHsgU3luY2VyIH0gZnJvbSBcIi4uL3N5bmNlci9zeW5jZXJcIjtcbmltcG9ydCB0eXBlIHsgV29ya2Zsb3dNYW5hZ2VyIH0gZnJvbSBcIi4uL3Rhc2tzL3dvcmtmbG93LW1hbmFnZXJcIjtcbmltcG9ydCB0eXBlIHsgRGV2Vml0ZXN0TWFuYWdlciB9IGZyb20gXCIuLi90ZXN0aW5nL2Rldi12aXRlc3QtbWFuYWdlclwiO1xuaW1wb3J0IHR5cGUgeyBTb25hbXVGYXN0aWZ5Q29uZmlnIH0gZnJvbSBcIi4uL3R5cGVzL3R5cGVzXCI7XG5pbXBvcnQgeyBleGlzdHMsIGZpbGVFeGlzdHMgfSBmcm9tIFwiLi4vdXRpbHMvZnMtdXRpbHNcIjtcbmltcG9ydCB0eXBlIHsgQWJzb2x1dGVQYXRoIH0gZnJvbSBcIi4uL3V0aWxzL3BhdGgtdXRpbHNcIjtcbmltcG9ydCB0eXBlIHsgU29uYW11Q29uZmlnLCBTb25hbXVTZXJ2ZXJPcHRpb25zLCBTb25hbXVUYXNrT3B0aW9ucyB9IGZyb20gXCIuL2NvbmZpZ1wiO1xuaW1wb3J0IHR5cGUgeyBDb250ZXh0IH0gZnJvbSBcIi4vY29udGV4dFwiO1xuaW1wb3J0IHR5cGUgeyBFeHRlbmRlZEFwaSB9IGZyb20gXCIuL2RlY29yYXRvcnNcIjtcbmltcG9ydCB7IGdldFNlY3JldHMsIHR5cGUgU29uYW11U2VjcmV0cyB9IGZyb20gXCIuL3NlY3JldFwiO1xuXG5jbGFzcyBTb25hbXVDbGFzcyB7XG4gIHB1YmxpYyBpc0luaXRpYWxpemVkOiBib29sZWFuID0gZmFsc2U7XG4gIHB1YmxpYyBmb3JUZXN0aW5nOiBib29sZWFuID0gZmFsc2U7XG4gIHB1YmxpYyBhc3luY0xvY2FsU3RvcmFnZTogQXN5bmNMb2NhbFN0b3JhZ2U8e1xuICAgIGNvbnRleHQ6IENvbnRleHQ7XG4gIH0+ID0gbmV3IEFzeW5jTG9jYWxTdG9yYWdlKCk7XG5cbiAgcHVibGljIGdldENvbnRleHQoKTogQ29udGV4dCB7XG4gICAgY29uc3Qgc3RvcmUgPSB0aGlzLmFzeW5jTG9jYWxTdG9yYWdlLmdldFN0b3JlKCk7XG4gICAgaWYgKHN0b3JlPy5jb250ZXh0KSB7XG4gICAgICByZXR1cm4gc3RvcmUuY29udGV4dCBhcyBDb250ZXh0O1xuICAgIH1cblxuICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gXCJ0ZXN0XCIpIHtcbiAgICAgIC8vIO2FjOyKpO2MhSDtmZjqsr3sl5DshJwg7Luo7YWN7Iqk7Yq46rCAIOyjvOyeheuQmOyngCDslYrsnYAg6rK97JqwIOu5iCDsu6jthY3siqTtirgg66as7YS0XG4gICAgICByZXR1cm4ge1xuICAgICAgICByZXF1ZXN0OiBudWxsLFxuICAgICAgICByZXBseTogbnVsbCxcbiAgICAgICAgaGVhZGVyczoge30sXG4gICAgICAgIGNyZWF0ZVNTRTogKHNjaGVtYTogWm9kT2JqZWN0KSA9PiBjcmVhdGVNb2NrU1NFRmFjdG9yeShzY2hlbWEpLFxuICAgICAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IO2FjOyKpO2MhSDtmZjqsr3sl5DshJwg7Luo7YWN7Iqk7Yq46rCAIOyjvOyeheuQmOyngCDslYrsnYAg6rK97JqwIOu5iCDsu6jthY3siqTtirgg66as7YS0XG4gICAgICAgIG5haXRlU3RvcmU6IG5ldyBNYXA8c3RyaW5nLCBhbnk+KCksXG4gICAgICB9IGFzIHVua25vd24gYXMgQ29udGV4dDtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGNhbm5vdCBmaW5kIGNvbnRleHRcIik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBfYXBpUm9vdFBhdGg6IEFic29sdXRlUGF0aCB8IG51bGwgPSBudWxsO1xuICBzZXQgYXBpUm9vdFBhdGgoYXBpUm9vdFBhdGg6IEFic29sdXRlUGF0aCkge1xuICAgIHRoaXMuX2FwaVJvb3RQYXRoID0gYXBpUm9vdFBhdGg7XG4gIH1cbiAgZ2V0IGFwaVJvb3RQYXRoKCk6IEFic29sdXRlUGF0aCB7XG4gICAgaWYgKHRoaXMuX2FwaVJvb3RQYXRoID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJTb25hbXUgaGFzIG5vdCBiZWVuIGluaXRpYWxpemVkXCIpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fYXBpUm9vdFBhdGg7XG4gIH1cbiAgZ2V0IGFwcFJvb3RQYXRoKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuYXBpUm9vdFBhdGguc3BsaXQocGF0aC5zZXApLnNsaWNlKDAsIC0xKS5qb2luKHBhdGguc2VwKTtcbiAgfVxuXG4gIHByaXZhdGUgX2RiQ29uZmlnOiBTb25hbXVEQkNvbmZpZyB8IG51bGwgPSBudWxsO1xuICBzZXQgZGJDb25maWcoZGJDb25maWc6IFNvbmFtdURCQ29uZmlnKSB7XG4gICAgdGhpcy5fZGJDb25maWcgPSBkYkNvbmZpZztcbiAgfVxuICBnZXQgZGJDb25maWcoKTogU29uYW11REJDb25maWcge1xuICAgIGlmICh0aGlzLl9kYkNvbmZpZyA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGhhcyBub3QgYmVlbiBpbml0aWFsaXplZFwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2RiQ29uZmlnO1xuICB9XG5cbiAgcHJpdmF0ZSBfc3luY2VyOiBTeW5jZXIgfCBudWxsID0gbnVsbDtcbiAgc2V0IHN5bmNlcihzeW5jZXI6IFN5bmNlcikge1xuICAgIHRoaXMuX3N5bmNlciA9IHN5bmNlcjtcbiAgfVxuICBnZXQgc3luY2VyKCk6IFN5bmNlciB7XG4gICAgaWYgKHRoaXMuX3N5bmNlciA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGhhcyBub3QgYmVlbiBpbml0aWFsaXplZFwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX3N5bmNlcjtcbiAgfVxuXG4gIHByaXZhdGUgX2NvbmZpZzogU29uYW11Q29uZmlnIHwgbnVsbCA9IG51bGw7XG4gIHNldCBjb25maWcoY29uZmlnOiBTb25hbXVDb25maWcpIHtcbiAgICB0aGlzLl9jb25maWcgPSBjb25maWc7XG4gIH1cbiAgZ2V0IGNvbmZpZygpOiBTb25hbXVDb25maWcge1xuICAgIGlmICh0aGlzLl9jb25maWcgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlNvbmFtdSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9jb25maWc7XG4gIH1cblxuICBwdWJsaWMgcmVhZG9ubHkgc2VjcmV0czogU29uYW11U2VjcmV0cyA9IGdldFNlY3JldHMoKTtcblxuICBwcml2YXRlIF9zdG9yYWdlOiBTdG9yYWdlTWFuYWdlciB8IG51bGwgPSBudWxsO1xuICAvKipcbiAgICogU3RvcmFnZU1hbmFnZXIg7J247Iqk7YS07IqkXG4gICAqL1xuICBnZXQgc3RvcmFnZSgpOiBTdG9yYWdlTWFuYWdlciB7XG4gICAgaWYgKCF0aGlzLl9zdG9yYWdlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJTdG9yYWdlIGhhcyBub3QgYmVlbiBpbml0aWFsaXplZC4gQ2hlY2sgc3RvcmFnZSBjb25maWcuXCIpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fc3RvcmFnZTtcbiAgfVxuXG4gIHByaXZhdGUgX2NhY2hlOiBDYWNoZU1hbmFnZXIgfCBudWxsID0gbnVsbDtcbiAgLyoqXG4gICAqIENhY2hlTWFuYWdlciDsnbjsiqTthLTsiqQgKEJlbnRvQ2FjaGUpXG4gICAqL1xuICBnZXQgY2FjaGUoKTogQ2FjaGVNYW5hZ2VyIHtcbiAgICBpZiAoIXRoaXMuX2NhY2hlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDYWNoZSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWQuIENoZWNrIGNhY2hlIGNvbmZpZyBpbiBzb25hbXUuY29uZmlnLnRzLlwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2NhY2hlO1xuICB9XG5cbiAgcHJpdmF0ZSBfd29ya2Zsb3dzOiBXb3JrZmxvd01hbmFnZXIgfCBudWxsID0gbnVsbDtcbiAgZ2V0IHdvcmtmbG93cygpOiBXb3JrZmxvd01hbmFnZXIge1xuICAgIGlmICh0aGlzLl93b3JrZmxvd3MgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlNvbmFtdSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuX3dvcmtmbG93cztcbiAgfVxuXG4gIHByaXZhdGUgX2F1dGg6IEF1dGggfCBudWxsID0gbnVsbDtcbiAgZ2V0IGF1dGgoKTogQXV0aCB7XG4gICAgaWYgKCF0aGlzLl9hdXRoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJBdXRoIGhhcyBub3QgYmVlbiBpbml0aWFsaXplZC4gQ2hlY2sgYXV0aCBjb25maWcgaW4gc29uYW11LmNvbmZpZy50cy5cIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9hdXRoO1xuICB9XG5cbiAgcHJpdmF0ZSBfZGV2Vml0ZXN0TWFuYWdlcjogRGV2Vml0ZXN0TWFuYWdlciB8IG51bGwgPSBudWxsO1xuICBnZXQgZGV2Vml0ZXN0TWFuYWdlcigpOiBEZXZWaXRlc3RNYW5hZ2VyIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuX2RldlZpdGVzdE1hbmFnZXI7XG4gIH1cbiAgc2V0IGRldlZpdGVzdE1hbmFnZXIobWFuYWdlcjogRGV2Vml0ZXN0TWFuYWdlciB8IG51bGwpIHtcbiAgICB0aGlzLl9kZXZWaXRlc3RNYW5hZ2VyID0gbWFuYWdlcjtcbiAgfVxuXG4gIC8vIEhNUiDsspjrpqxcbiAgcHVibGljIHdhdGNoZXI6IEZTV2F0Y2hlciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHBlbmRpbmdGaWxlczogc3RyaW5nW10gPSBbXTtcbiAgcHJpdmF0ZSBobXJTdGFydFRpbWU6IG51bWJlciA9IDA7XG5cbiAgcHVibGljIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlIHwgbnVsbCA9IG51bGw7XG5cbiAgYXN5bmMgaW5pdEZvclRlc3RpbmcoKSB7XG4gICAgYXdhaXQgdGhpcy5pbml0KHRydWUsIGZhbHNlLCB1bmRlZmluZWQsIHRydWUpO1xuICB9XG5cbiAgYXN5bmMgaW5pdChcbiAgICBkb1NpbGVudDogYm9vbGVhbiA9IGZhbHNlLFxuICAgIGVuYWJsZVN5bmM6IGJvb2xlYW4gPSB0cnVlLFxuICAgIGFwaVJvb3RQYXRoPzogQWJzb2x1dGVQYXRoLFxuICAgIGZvclRlc3Rpbmc6IGJvb2xlYW4gPSBmYWxzZSxcbiAgKSB7XG4gICAgdGhpcy5mb3JUZXN0aW5nID0gZm9yVGVzdGluZztcblxuICAgIGlmICh0aGlzLmlzSW5pdGlhbGl6ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoIWRvU2lsZW50KSB7XG4gICAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICAgIGNvbnNvbGUudGltZShjaGFsay5jeWFuKGBTb25hbXUuaW5pdCR7Zm9yVGVzdGluZyA/IFwiIGZvciB0ZXN0aW5nXCIgOiBcIlwifWApKTtcbiAgICB9XG5cbiAgICAvLyBBUEkg66Oo7Yq4IO2MqOyKpFxuICAgIGNvbnN0IHsgZmluZEFwaVJvb3RQYXRoIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy91dGlsc1wiKTtcbiAgICB0aGlzLmFwaVJvb3RQYXRoID0gYXBpUm9vdFBhdGggPz8gZmluZEFwaVJvb3RQYXRoKCk7XG5cbiAgICAvLyDshKTsoJXsnYQg66Gc65Sp7ZWY64qUIOqyg+u2gO2EsCDsi5zsnpFcbiAgICBjb25zdCB7IGxvYWRDb25maWcgfSA9IGF3YWl0IGltcG9ydChcIi4vY29uZmlnXCIpO1xuICAgIHRoaXMuY29uZmlnID0gYXdhaXQgbG9hZENvbmZpZyh0aGlzLmFwaVJvb3RQYXRoKTtcbiAgICAvLyBzb25hbXUuY29uZmlnLnRzIOq4sOuzuOqwkiDshKTsoJVcbiAgICB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kYXRhYmFzZSA9IHRoaXMuY29uZmlnLmRhdGFiYXNlLmRhdGFiYXNlID8/IFwicGdcIjtcbiAgICB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kZWZhdWx0T3B0aW9ucy5jbGllbnQgPSB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kYXRhYmFzZSA/PyBcInBnXCI7XG5cbiAgICAvLyDroZzquYUg7ISk7KCVXG4gICAgY29uc3QgeyBjb25maWd1cmVMb2dUYXBlIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9sb2dnZXIvY29uZmlndXJlXCIpO1xuICAgIGlmICh0aGlzLmNvbmZpZy5sb2dnaW5nICE9PSBmYWxzZSkge1xuICAgICAgYXdhaXQgY29uZmlndXJlTG9nVGFwZSh7XG4gICAgICAgIC4uLnRoaXMuY29uZmlnLmxvZ2dpbmcsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBEQiDroZzrk5xcbiAgICBjb25zdCB7IERCIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9kYXRhYmFzZS9kYlwiKTtcbiAgICB0aGlzLmRiQ29uZmlnID0gREIuZ2VuZXJhdGVEQkNvbmZpZyh0aGlzLmNvbmZpZy5kYXRhYmFzZSk7XG4gICAgaWYgKCFkb1NpbGVudCkge1xuICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihcIkRCIENvbmZpZyBMb2FkZWQhXCIpKTtcbiAgICB9XG5cbiAgICAvLyBFbnRpdHkg66Gc65OcXG4gICAgLy8g7YWM7Iqk7Yq47JeQ7ISc64+EIEVudGl0eSDsoJXrs7TripQg7ZWE7JqU7ZWp64uI64ukLlxuICAgIC8vIHVwc2VydOqwgCDsoJzrjIDroZwg7J6R64+Z7ZWY66Ck66m0IGVudGl0eeydmCB1bmlxdWUgaW5kZXgg7KCV67O06rCAIO2VhOyalO2VmOq4sCDrlYzrrLjsnoXri4jri6QuXG4gICAgY29uc3QgeyBFbnRpdHlNYW5hZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9lbnRpdHkvZW50aXR5LW1hbmFnZXJcIik7XG4gICAgYXdhaXQgRW50aXR5TWFuYWdlci5hdXRvbG9hZChkb1NpbGVudCk7XG5cbiAgICAvLyBDYWNoZSDstIjquLDtmZRcbiAgICBhd2FpdCB0aGlzLmluaXRpYWxpemVDYWNoZSh0aGlzLmNvbmZpZy5zZXJ2ZXIuY2FjaGUsIGZvclRlc3RpbmcpO1xuXG4gICAgLy8gQmV0dGVyQXV0aCDstIjquLDtmZRcbiAgICBjb25zdCBhdXRoQ29uZmlnID0gdGhpcy5jb25maWcuc2VydmVyLmF1dGg7XG4gICAgaWYgKGF1dGhDb25maWcpIHtcbiAgICAgIC8vIOyCrOyaqeyekCDshKTsoJXqs7wg6riw67O46rCS7J2EIG1lcmdlXG4gICAgICBjb25zdCBtZXJnZWRGaWVsZE1hcHBpbmdzID0gbWVyZ2UoQkFTRV9GSUVMRF9NQVBQSU5HUywgYXV0aENvbmZpZyk7XG5cbiAgICAgIC8vIGJldHRlci1hdXRoIOyduOyKpO2EtOyKpCDsg53shLFcbiAgICAgIGNvbnN0IHsgYmV0dGVyQXV0aCB9ID0gYXdhaXQgaW1wb3J0KFwiYmV0dGVyLWF1dGhcIik7XG4gICAgICBjb25zdCB7IHNvbmFtdUtuZXhBZGFwdGVyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9hdXRoL2tuZXgtYWRhcHRlclwiKTtcblxuICAgICAgdGhpcy5fYXV0aCA9IGJldHRlckF1dGgoe1xuICAgICAgICBkYXRhYmFzZTogc29uYW11S25leEFkYXB0ZXIoKSxcbiAgICAgICAgLi4ubWVyZ2VkRmllbGRNYXBwaW5ncyxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIO2FjOyKpO2MheyduCDqsr3smrAg7Iux7YGsIOyXhuydtCDspJHri6hcbiAgICBpZiAoZm9yVGVzdGluZykge1xuICAgICAgdGhpcy5pc0luaXRpYWxpemVkID0gdHJ1ZTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBUYXNrIOuTseuhnVxuICAgIGF3YWl0IHRoaXMuaW5pdGlhbGl6ZVdvcmtmbG93cyh0aGlzLmNvbmZpZy50YXNrcyk7XG5cbiAgICAvLyBTeW5jZXJcbiAgICBjb25zdCB7IFN5bmNlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vc3luY2VyL3N5bmNlclwiKTtcbiAgICB0aGlzLnN5bmNlciA9IG5ldyBTeW5jZXIoKTtcblxuICAgIC8vIEF1dG9sb2FkOiBNb2RlbHMgLyBUeXBlcyAvIEFQSXMgLyBXb3JrZmxvd3MgLyBUZW1wbGF0ZXMgLyBTU1IgUm91dGVzXG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuYXV0b2xvYWRUeXBlcygpO1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkTW9kZWxzKCk7XG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuYXV0b2xvYWRBcGlzKCk7XG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuYXV0b2xvYWRXb3JrZmxvd3MoKTtcbiAgICBjb25zdCB7IFRlbXBsYXRlTWFuYWdlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdGVtcGxhdGVcIik7XG4gICAgYXdhaXQgVGVtcGxhdGVNYW5hZ2VyLmF1dG9sb2FkKCk7XG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuYXV0b2xvYWRTU1JSb3V0ZXMoKTtcblxuICAgIGNvbnN0IHsgaXNMb2NhbCwgaXNUZXN0IH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy9jb250cm9sbGVyXCIpO1xuICAgIGlmIChpc0xvY2FsKCkpIHtcbiAgICAgIC8vIOuhnOy7rOyXkOyEnOuKlCDsvZTrk5wg7IOd7ISx7J2EIOychO2VtCBCaW9tZSDshYvsl4XsnbQg7ZWE7JqU7ZWoICjtmITsnqwgYXBpUm9vdFBhdGgg7KCE64us7ZWY7JesIOyLpO2WiSlcbiAgICAgIChhd2FpdCBpbXBvcnQoXCIuLi91dGlscy9mb3JtYXR0ZXJcIikpLnNldHVwQmlvbWUodGhpcy5hcGlSb290UGF0aCk7XG4gICAgfVxuXG4gICAgY29uc3QgeyBpc0hvdFJlbG9hZFNlcnZlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdXRpbHMvY29udHJvbGxlclwiKTtcbiAgICBpZiAoaXNMb2NhbCgpICYmICFpc1Rlc3QoKSAmJiBpc0hvdFJlbG9hZFNlcnZlcigpICYmIGVuYWJsZVN5bmMpIHtcbiAgICAgIGF3YWl0IHRoaXMuc3luY2VyLnN5bmMoKTtcbiAgICAgIGF3YWl0IHRoaXMuc3RhcnRXYXRjaGVyKCk7XG4gICAgfVxuXG4gICAgdGhpcy5pc0luaXRpYWxpemVkID0gdHJ1ZTtcbiAgICBpZiAoIWRvU2lsZW50KSB7XG4gICAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICAgIGNvbnNvbGUudGltZUVuZChjaGFsay5jeWFuKFwiU29uYW11LmluaXRcIikpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGNyZWF0ZVNlcnZlcihpbml0T3B0aW9ucz86IHsgZW5hYmxlU3luYz86IGJvb2xlYW47IGRvU2lsZW50PzogYm9vbGVhbiB9KSB7XG4gICAgaWYgKHRoaXMuaXNJbml0aWFsaXplZCA9PT0gZmFsc2UpIHtcbiAgICAgIGF3YWl0IHRoaXMuaW5pdChpbml0T3B0aW9ucz8uZG9TaWxlbnQsIGluaXRPcHRpb25zPy5lbmFibGVTeW5jKTtcbiAgICB9XG5cbiAgICBjb25zdCBvcHRpb25zID0gdGhpcy5jb25maWcuc2VydmVyO1xuICAgIGNvbnN0IHsgZGVmYXVsdDogZmFzdGlmeSB9ID0gYXdhaXQgaW1wb3J0KFwiZmFzdGlmeVwiKTtcbiAgICBjb25zdCB7IGdldExvZ1RhcGVGYXN0aWZ5TG9nZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCJAbG9ndGFwZS9mYXN0aWZ5XCIpO1xuICAgIGNvbnN0IHNlcnZlciA9IGZhc3RpZnkoe1xuICAgICAgLi4ub3B0aW9ucy5mYXN0aWZ5LFxuICAgICAgbG9nZ2VyOlxuICAgICAgICB0aGlzLmNvbmZpZy5sb2dnaW5nICE9PSBmYWxzZVxuICAgICAgICAgID8gZ2V0TG9nVGFwZUZhc3RpZnlMb2dnZXIoe1xuICAgICAgICAgICAgICBjYXRlZ29yeTogdGhpcy5jb25maWcubG9nZ2luZz8uZmFzdGlmeUNhdGVnb3J5ID8/IFtcImZhc3RpZnlcIl0sXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIDogdW5kZWZpbmVkLFxuICAgIH0pO1xuICAgIHRoaXMuc2VydmVyID0gc2VydmVyO1xuXG4gICAgLy8gU3RvcmFnZSDshKTsoJUg4oaSIFN0b3JhZ2VNYW5hZ2VyIOyDneyEsVxuICAgIGlmIChvcHRpb25zLnN0b3JhZ2UpIHtcbiAgICAgIGNvbnN0IHsgU3RvcmFnZU1hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3N0b3JhZ2Uvc3RvcmFnZS1tYW5hZ2VyXCIpO1xuICAgICAgdGhpcy5fc3RvcmFnZSA9IG5ldyBTdG9yYWdlTWFuYWdlcihvcHRpb25zLnN0b3JhZ2UpO1xuICAgIH1cblxuICAgIC8vIO2UjOufrOq3uOyduCDrk7HroZ1cbiAgICBpZiAob3B0aW9ucy5wbHVnaW5zKSB7XG4gICAgICBhd2FpdCB0aGlzLnJlZ2lzdGVyUGx1Z2lucyhzZXJ2ZXIsIG9wdGlvbnMucGx1Z2lucyk7XG4gICAgfVxuXG4gICAgaWYgKG9wdGlvbnMuYXV0aCkge1xuICAgICAgYXdhaXQgdGhpcy5yZWdpc3RlckJldHRlckF1dGgoc2VydmVyLCBvcHRpb25zLmF1dGgpO1xuICAgIH1cblxuICAgIC8vIEFQSSDrnbzsmrDtjIUg7ISk7KCVXG4gICAgYXdhaXQgdGhpcy53aXRoRmFzdGlmeShzZXJ2ZXIsIG9wdGlvbnMuYXBpQ29uZmlnLCB7XG4gICAgICBlbmFibGVTeW5jOiBpbml0T3B0aW9ucz8uZW5hYmxlU3luYyxcbiAgICAgIGRvU2lsZW50OiBpbml0T3B0aW9ucz8uZG9TaWxlbnQsXG4gICAgfSk7XG5cbiAgICAvLyDshJzrsoQg7Iuc7J6RXG4gICAgYXdhaXQgdGhpcy5ib290KHNlcnZlciwgb3B0aW9ucyk7XG5cbiAgICByZXR1cm4gc2VydmVyO1xuICB9XG5cbiAgYXN5bmMgd2l0aEZhc3RpZnkoXG4gICAgc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2U8U2VydmVyLCBJbmNvbWluZ01lc3NhZ2UsIFNlcnZlclJlc3BvbnNlPixcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICAgb3B0aW9ucz86IHtcbiAgICAgIGVuYWJsZVN5bmM/OiBib29sZWFuO1xuICAgICAgZG9TaWxlbnQ/OiBib29sZWFuO1xuICAgIH0sXG4gICkge1xuICAgIGlmICh0aGlzLmlzSW5pdGlhbGl6ZWQgPT09IGZhbHNlKSB7XG4gICAgICBhd2FpdCB0aGlzLmluaXQob3B0aW9ucz8uZG9TaWxlbnQsIG9wdGlvbnM/LmVuYWJsZVN5bmMpO1xuICAgIH1cblxuICAgIHRoaXMuc2VydmVyID0gc2VydmVyO1xuXG4gICAgLy8gdGltZXpvbmUg7ISk7KCVXG4gICAgY29uc3QgdGltZXpvbmUgPSB0aGlzLmNvbmZpZy5hcGkudGltZXpvbmU7XG4gICAgaWYgKHRpbWV6b25lKSB7XG4gICAgICAvLyDtg4DsnoTsobTsl5Ag66ee6rKMIOydkeuLtSDrgqDsp5wg7Iqk7Yq466eB7J2EIOuzgO2ZmO2VtOyjvOyWtOyVvCDtlanri4jri6QuXG4gICAgICAvLyDqsIDroLkgdGltZXpvbmXsnbQgXCJBc2lhL1Nlb3VsXCIg7J2066m0XG4gICAgICAvLyBcIjIwMjUtMTEtMjFUMDA6MDA6MDAuMDAwWlwiIOulvCBcIjIwMjUtMTEtMjFUMDk6MDA6MDArMDk6MDBcIiDsnLzroZwg67OA7ZmY7ZW07KO87Ja07JW8IO2VqeuLiOuLpC5cbiAgICAgIGNvbnN0IHsgZm9ybWF0SW5UaW1lWm9uZSB9ID0gYXdhaXQgaW1wb3J0KFwiZGF0ZS1mbnMtdHpcIik7XG5cbiAgICAgIC8vIElTTyA4NjAxIOuCoOynnCDtmJXsi50g7KCV6rec7IudICjsmIg6IDIwMjQtMDEtMTVUMDk6MzA6MDAuMDAwWilcbiAgICAgIGNvbnN0IElTT19EQVRFX1JFR0VYID0gL15cXGR7NH0tXFxkezJ9LVxcZHsyfVRcXGR7Mn06XFxkezJ9OlxcZHsyfShcXC5cXGR7M30pP1okLztcblxuICAgICAgLy8gVOulvCDrkZjrn6zsi7wg7J6R7J2A65Sw7Ji07ZGc6rCAIOyXhuuLpOuptCBcIjIwMjUtMTEtMTkxNzYzNTQ2MTg5MDAwMTg6NTY6MjkrMDk6MDBcIuyZgCDqsJnsnYAg6rKw6rO86rCAIOuCmOyYteuLiOuLpC5cbiAgICAgIC8vIOydtOuKlCBkYXRlLWZucyDtirnsnoXri4jri6QuXG4gICAgICAvLyDsnbTroIfqsowg7ZW064+EIOq0nOywruyKteuLiOuLpC4gXCIyMDI1LTExLTE5VDE4OjU2OjI5KzA5OjAwXCIg66qo7JaR7Jy866GcIOyemCDrgpjsmLXri4jri6QuXG4gICAgICBjb25zdCBEQVRFX0ZPUk1BVCA9IFwieXl5eS1NTS1kZCdUJ0hIOm1tOnNzWFhYXCI7XG5cbiAgICAgIHNlcnZlci5zZXRSZXBseVNlcmlhbGl6ZXIoKHBheWxvYWQpID0+IHtcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHBheWxvYWQsIChfa2V5LCB2YWx1ZSkgPT4ge1xuICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgJiYgSVNPX0RBVEVfUkVHRVgudGVzdCh2YWx1ZSkpIHtcbiAgICAgICAgICAgIHJldHVybiBmb3JtYXRJblRpbWVab25lKFxuICAgICAgICAgICAgICBuZXcgRGF0ZSh2YWx1ZSksXG4gICAgICAgICAgICAgIHRpbWV6b25lIGFzIGAke3N0cmluZ30vJHtzdHJpbmd9YCxcbiAgICAgICAgICAgICAgREFURV9GT1JNQVQsXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgICBpZiAoIW9wdGlvbnM/LmRvU2lsZW50KSB7XG4gICAgICAgIGNvbnN0IGNoYWxrID0gKGF3YWl0IGltcG9ydChcImNoYWxrXCIpKS5kZWZhdWx0O1xuICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihgVGltZXpvbmUgc2V0IHRvICR7dGltZXpvbmV9YCkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIOyghOyytCDrnbzsmrDtjIUg66as7Iqk7Yq4XG4gICAgc2VydmVyLmdldChcbiAgICAgIGAke3RoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXh9L3JvdXRlc2AsXG4gICAgICBhc3luYyAoX3JlcXVlc3QsIF9yZXBseSk6IFByb21pc2U8dHlwZW9mIHRoaXMuc3luY2VyLmFwaXM+ID0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3luY2VyLmFwaXM7XG4gICAgICB9LFxuICAgICk7XG5cbiAgICAvLyBIZWFsdGhjaGVjayBBUElcbiAgICBzZXJ2ZXIuZ2V0KFxuICAgICAgYCR7dGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeH0vaGVhbHRoY2hlY2tgLFxuICAgICAgYXN5bmMgKF9yZXF1ZXN0LCBfcmVwbHkpOiBQcm9taXNlPHN0cmluZz4gPT4ge1xuICAgICAgICByZXR1cm4gXCJva1wiO1xuICAgICAgfSxcbiAgICApO1xuXG4gICAgLy8gU29uYW11IFVJIEFQSSAo66Gc7LusIO2ZmOqyveyXkOyEnOunjClcbiAgICBjb25zdCB7IGlzTG9jYWwgfSA9IGF3YWl0IGltcG9ydChcIi4uL3V0aWxzL2NvbnRyb2xsZXJcIik7XG4gICAgaWYgKGlzTG9jYWwoKSkge1xuICAgICAgY29uc3QgeyBzb25hbXVVSUFwaVBsdWdpbiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdWkvYXBpXCIpO1xuICAgICAgc2VydmVyLnJlZ2lzdGVyKHNvbmFtdVVJQXBpUGx1Z2luKTtcbiAgICB9XG5cbiAgICAvLyBEZXZSdW5uZXIg7YWM7Iqk7Yq4IOyXlOuTnO2PrOyduO2KuCAo66Gc7LusIO2ZmOqyvSArIGRldlJ1bm5lciDtmZzshLHtmZQg7IucKVxuICAgIGlmIChpc0xvY2FsKCkgJiYgdGhpcy5jb25maWcudGVzdD8uZGV2UnVubmVyPy5lbmFibGVkKSB7XG4gICAgICBjb25zdCB7IHJlZ2lzdGVyRGV2VGVzdFJvdXRlcyB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdGVzdGluZy9kZXYtdGVzdC1yb3V0ZXNcIik7XG4gICAgICBhd2FpdCByZWdpc3RlckRldlRlc3RSb3V0ZXMoc2VydmVyLCB0aGlzLmNvbmZpZy50ZXN0LmRldlJ1bm5lcik7XG4gICAgfVxuXG4gICAgY29uc3Qgd2ViUGF0aCA9IHBhdGguam9pbih0aGlzLmFwcFJvb3RQYXRoLCBcIndlYlwiKTtcbiAgICBjb25zdCBoYXNXZWIgPSBhd2FpdCBleGlzdHMod2ViUGF0aCk7XG5cbiAgICAvLyDsoITsl60gY29tcHJlc3Mg7Ji17IWYIOqzhOyCsCAocm91dGUuY29tcHJlc3M6IHRydWXsnbwg65WMIOyCrOyaqSlcbiAgICBjb25zdCBwbHVnaW5Db21wcmVzcyA9IHRoaXMuY29uZmlnLnNlcnZlci5wbHVnaW5zPy5jb21wcmVzcztcbiAgICBjb25zdCBnbG9iYWxDb21wcmVzc09wdGlvbnM6IENvbXByZXNzT3B0aW9ucyB8IHVuZGVmaW5lZCA9IHBsdWdpbkNvbXByZXNzXG4gICAgICA/IHBsdWdpbkNvbXByZXNzID09PSB0cnVlXG4gICAgICAgID8geyB0aHJlc2hvbGQ6IDEwMjQsIGVuY29kaW5nczogW1wiYnJcIiwgXCJnemlwXCIsIFwiZGVmbGF0ZVwiXSB9XG4gICAgICAgIDoge1xuICAgICAgICAgICAgdGhyZXNob2xkOiBwbHVnaW5Db21wcmVzcy50aHJlc2hvbGQsXG4gICAgICAgICAgICBlbmNvZGluZ3M6IHBsdWdpbkNvbXByZXNzLmVuY29kaW5ncyxcbiAgICAgICAgICAgIGN1c3RvbVR5cGVzOiBwbHVnaW5Db21wcmVzcy5jdXN0b21UeXBlcyxcbiAgICAgICAgICB9XG4gICAgICA6IHVuZGVmaW5lZDtcblxuICAgIGlmIChpc0xvY2FsKCkpIHtcbiAgICAgIC8vIOuhnOy7rCDqsJzrsJwg7ZmY6rK9OiBjYXRjaC1hbGzroZwgQVBJ66W8IOuPmeyggSDrp6Tsua3tlZjsl6wgSE1S7J2EIOyngOybkO2VqeuLiOuLpC5cbiAgICAgIC8vIFNPTkFNVV9ESVNBQkxFX0lOVEVHUkFURURfV0VCPXllc+uhnCDshKTsoJXtlZjrqbQgZGV2X2FwaSDrqqjrk5zsl5DshJwgVml0ZSDthrXtlansnYQg67mE7Zmc7ISx7ZmU7ZWgIOyImCDsnojsirXri4jri6QuXG4gICAgICBjb25zdCBkaXNhYmxlSW50ZWdyYXRlZFdlYiA9IHByb2Nlc3MuZW52LlNPTkFNVV9ESVNBQkxFX0lOVEVHUkFURURfV0VCID09PSBcInllc1wiO1xuICAgICAgaWYgKGhhc1dlYiAmJiAhZGlzYWJsZUludGVncmF0ZWRXZWIpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5zZXR1cERldlNlcnZlcldpdGhWaXRlKHNlcnZlciwgd2ViUGF0aCwgY29uZmlnKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuc2V0dXBEZXZTZXJ2ZXIoc2VydmVyLCBjb25maWcpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyDtlITroZzrjZXshZgg7ZmY6rK9OiDqsJzrs4QgQVBJIOudvOyasO2KuCArIOygleyggSDtjIzsnbwg7ISc67mZXG4gICAgICBmb3IgKGNvbnN0IGFwaSBvZiB0aGlzLnN5bmNlci5hcGlzKSB7XG4gICAgICAgIGlmICh0aGlzLnN5bmNlci5tb2RlbHNbYXBpLm1vZGVsTmFtZV0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihg7KCV7J2Y65CY7KeAIOyViuydgCDrqqjrjbjsl5Ag7KCR6re8ICR7YXBpLm1vZGVsTmFtZX1gKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHNlcnZlci5yb3V0ZSh7XG4gICAgICAgICAgbWV0aG9kOiBhcGkub3B0aW9ucy5odHRwTWV0aG9kID8/IFwiR0VUXCIsXG4gICAgICAgICAgdXJsOiB0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4ICsgYXBpLnBhdGgsXG4gICAgICAgICAgaGFuZGxlcjogdGhpcy5jcmVhdGVBcGlIYW5kbGVyKGFwaSwgY29uZmlnKSxcbiAgICAgICAgICBjb21wcmVzczogdG9GYXN0aWZ5Q29tcHJlc3NPcHRpb24oYXBpLm9wdGlvbnMuY29tcHJlc3MsIGdsb2JhbENvbXByZXNzT3B0aW9ucyksXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICAvLyDtlITroZzrjZXshZjsl5DshJzripQgd2ViIOyGjOyKpChhcHBSb290L3dlYikg7Jyg66y07JmAIOustOq0gO2VmOqyjCxcbiAgICAgIC8vIGFwaS93ZWItZGlzdCDsobTsnqwg7Jes67aA66W8IHNldHVwU3RhdGljV2ViU2VydmVyIOuCtOu2gOyXkOyEnCDtjJDri6jtlanri4jri6QuXG4gICAgICBhd2FpdCB0aGlzLnNldHVwU3RhdGljV2ViU2VydmVyKHNlcnZlciwgY29uZmlnLCBnbG9iYWxDb21wcmVzc09wdGlvbnMpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBkZXYg66qo65OcIOqzte2GtTogY2F0Y2gtYWxs7JeQ7IScIHN5bmNlci5hcGlz66W8IOuPmeyggeycvOuhnCDtg5Dsg4ntlZjsl6wgQVBJIOyalOyyreydhCDsspjrpqztlanri4jri6QuXG4gICAqIHNlcnZlci5yb3V0ZSgp66GcIOqwnOuzhCDrk7HroZ3tlZjrqbQgaGFuZGxlcuqwgCDqs6DsoJXrkJjslrQgSE1S7J20IOuPmeyeke2VmOyngCDslYrsnLzrr4DroZwsXG4gICAqIOunpCDsmpTssq3rp4jri6Qgc3luY2VyLmFwaXPrpbwg7KGw7ZqM7ZWY64qUIOydtCDrsKnsi53snYQg7IKs7Jqp7ZWp64uI64ukLlxuICAgKlxuICAgKiDsmpTssq3snbQgL2FwaSjsoJXtmZXtnojripQgdGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeCnroZwg7Iuc7J6R7ZWY7KeAIOyViuuKlCDqsr3smrDrnbzrqbQgbnVsbOydhCDrsJjtmZjtlZjrqbAg64Gd64OF64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBoYW5kbGVEZXZBcGlSZXF1ZXN0KFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgKTogKChyZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCwgcmVwbHk6IEZhc3RpZnlSZXBseSkgPT4gUHJvbWlzZTx1bmtub3duPikgfCBudWxsIHtcbiAgICBjb25zdCB1cmwgPSB0aGlzLmdldFBhdGhuYW1lRnJvbVVybChyZXF1ZXN0LnVybCk7XG4gICAgY29uc3QgbWV0aG9kID0gcmVxdWVzdC5tZXRob2Q7XG5cbiAgICBpZiAoIXVybC5zdGFydHNXaXRoKHRoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXgpKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBzeW5jZXIuYXBpc+ydmCBwYXRo64qUIDpwYXJhbSDtmJXtg5zrpbwg7Y+s7ZWo7ZWgIOyImCDsnojsnLzrr4DroZwg7IS46re466i87Yq4IOuLqOychOuhnCDrp6Tsua3tlanri4jri6QuXG4gICAgLy8g7KCV6rec7IudIOyDneyEsSDrsKnsi53snYAgcGF0aCDrrLjsnpDsl7Qg64K0IO2KueyImOusuOyekCguLCArLCAoLCBbIOuTsSnroZwg7Jik7J6R64+Z7ZWgIOyImCDsnojslrQg7IKs7Jqp7ZWY7KeAIOyViuyKteuLiOuLpC5cbiAgICBjb25zdCBtYXRjaGVkQXBpID0gdGhpcy5zeW5jZXIuYXBpcy5maW5kKChhcGkpID0+IHtcbiAgICAgIGlmICh0aGlzLnN5bmNlci5tb2RlbHNbYXBpLm1vZGVsTmFtZV0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICBjb25zdCBhcGlNZXRob2QgPSBhcGkub3B0aW9ucy5odHRwTWV0aG9kID8/IFwiR0VUXCI7XG4gICAgICBpZiAoYXBpTWV0aG9kICE9PSBtZXRob2QpIHJldHVybiBmYWxzZTtcblxuICAgICAgY29uc3QgZnVsbFBhdGggPSB0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4ICsgYXBpLnBhdGg7XG4gICAgICByZXR1cm4gdGhpcy5pc1BhdGhQYXR0ZXJuTWF0Y2goZnVsbFBhdGgsIHVybCk7XG4gICAgfSk7XG5cbiAgICBpZiAoIW1hdGNoZWRBcGkpIHtcbiAgICAgIHRocm93IG5ldyBOb3RGb3VuZEV4Y2VwdGlvbihTRChcImVycm9yLmFwaS5ub3RGb3VuZFwiKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuY3JlYXRlQXBpSGFuZGxlcihtYXRjaGVkQXBpLCBjb25maWcpO1xuICB9XG5cbiAgLyoqXG4gICAqIGRldiBhcGkg66qo65OcOiBWaXRlIOyXhuydtCBBUEkg64+Z7KCBIOudvOyasO2MheunjCDsoJzqs7Xtlanri4jri6QuXG4gICAqIEhNUuydhCDsnITtlbQgY2F0Y2gtYWxs7JeQ7IScIOunpCDsmpTssq3rp4jri6Qgc3luY2VyLmFwaXPrpbwg7KGw7ZqM7ZWp64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBzZXR1cERldlNlcnZlcihcbiAgICBzZXJ2ZXI6IEZhc3RpZnlJbnN0YW5jZTxTZXJ2ZXIsIEluY29taW5nTWVzc2FnZSwgU2VydmVyUmVzcG9uc2U+LFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgKTogdm9pZCB7XG4gICAgc2VydmVyLnJvdXRlKHtcbiAgICAgIG1ldGhvZDogW1wiR0VUXCIsIFwiSEVBRFwiLCBcIlBPU1RcIiwgXCJQVVRcIiwgXCJERUxFVEVcIiwgXCJQQVRDSFwiXSxcbiAgICAgIHVybDogYCR7dGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeH0vKmAsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgICAgY29uc3QgaGFuZGxlciA9IHRoaXMuaGFuZGxlRGV2QXBpUmVxdWVzdChyZXF1ZXN0LCBjb25maWcpO1xuICAgICAgICBpZiAoaGFuZGxlcikge1xuICAgICAgICAgIHJldHVybiBoYW5kbGVyKHJlcXVlc3QsIHJlcGx5KTtcbiAgICAgICAgfVxuICAgICAgICAvLyDrk7HroZ3rkJwgQVBJ7JmAIOydvOy5mO2VmOyngCDslYrripQg7JqU7LKt7JeQIOuMgO2VnCBmYWxsYmFja+yeheuLiOuLpC5cbiAgICAgICAgdGhyb3cgbmV3IE5vdEZvdW5kRXhjZXB0aW9uKFNEKFwiZXJyb3IuYXBpLm5vdEZvdW5kXCIpKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IFZpdGVEZXZTZXJ2ZXIg7YOA7J6F7J2EIOuPmeyggeycvOuhnCDroZzrk5ztlbTslbwg7ZWoXG4gIHByaXZhdGUgdml0ZVNlcnZlcjogYW55ID0gbnVsbDtcblxuICAvKipcbiAgICogZGV2IGFsbCDrqqjrk5w6IFZpdGUgRGV2IFNlcnZlcuulvCDthrXtlantlZjsl6wgQVBJICsgU1NSICsgQ1NS7J2EIOuqqOuRkCDsoJzqs7Xtlanri4jri6QuXG4gICAqIEFQSSDrj5nsoIEg66ek7Lmt7J2AIGhhbmRsZURldkFwaVJlcXVlc3Trpbwg6rO17Jyg7ZWp64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBzZXR1cERldlNlcnZlcldpdGhWaXRlKFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlPFNlcnZlciwgSW5jb21pbmdNZXNzYWdlLCBTZXJ2ZXJSZXNwb25zZT4sXG4gICAgd2ViUGF0aDogc3RyaW5nLFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gQGZhc3RpZnkvbWlkZGllIOuTseuhnSAoQ29ubmVjdC1zdHlsZSBtaWRkbGV3YXJlIOyngOybkClcbiAgICBhd2FpdCBzZXJ2ZXIucmVnaXN0ZXIoKGF3YWl0IGltcG9ydChcIkBmYXN0aWZ5L21pZGRpZVwiKSkuZGVmYXVsdCk7XG5cbiAgICBjb25zdCB2aXRlID0gYXdhaXQgaW1wb3J0KFwidml0ZVwiKTtcblxuICAgIHRoaXMudml0ZVNlcnZlciA9IGF3YWl0IHZpdGUuY3JlYXRlU2VydmVyKHtcbiAgICAgIHJvb3Q6IHdlYlBhdGgsXG4gICAgICBzZXJ2ZXI6IHtcbiAgICAgICAgbWlkZGxld2FyZU1vZGU6IHRydWUsXG4gICAgICAgIGhtcjoge1xuICAgICAgICAgIHNlcnZlcjogc2VydmVyLnNlcnZlcixcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICBhcHBUeXBlOiBcImN1c3RvbVwiLFxuICAgIH0pO1xuXG4gICAgLy8gVml0ZSBtaWRkbGV3YXJlIOuTseuhnSAoVml0ZSDsl5DshYsg7LKY66asKVxuICAgIHNlcnZlci51c2UoKHJlcSwgcmVzLCBuZXh0KSA9PiB7XG4gICAgICAvLyBBUEnsmYAgU29uYW11IFVJ64qUIEZhc3RpZnkg65287Jqw7Yq46rCAIOyymOumrO2VmOuPhOuhnSBza2lwXG4gICAgICBpZiAocmVxLnVybD8uc3RhcnRzV2l0aCh0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4KSB8fCByZXEudXJsPy5zdGFydHNXaXRoKFwiL3NvbmFtdS11aVwiKSkge1xuICAgICAgICByZXR1cm4gbmV4dCgpO1xuICAgICAgfVxuICAgICAgLy8g64KY66i47KeA64qUIFZpdGUgbWlkZGxld2FyZeuhnCDsoITri6xcbiAgICAgIHJldHVybiB0aGlzLnZpdGVTZXJ2ZXIubWlkZGxld2FyZXMocmVxLCByZXMsIG5leHQpO1xuICAgIH0pO1xuXG4gICAgLy8gY2F0Y2gtYWxsIOudvOyasO2KuOyXkOyEnCDrj5nsoIHsnLzroZwgQVBJL1NTUiDsspjrpqxcbiAgICAvLyDqsJzrsJwg7ZmY6rK97JeQ7ISc64qUIOudvOyasO2KuOuzhCBjb21wcmVzcyDsmLXshZjsnYQg7Y+s6riw7ZWY6rOgIEhNUiDsnbTsoJDsnYQg7Leo7ZWp64uI64ukLlxuICAgIHNlcnZlci5yb3V0ZSh7XG4gICAgICBtZXRob2Q6IFtcIkdFVFwiLCBcIkhFQURcIiwgXCJQT1NUXCIsIFwiUFVUXCIsIFwiREVMRVRFXCIsIFwiUEFUQ0hcIl0sXG4gICAgICB1cmw6IFwiLypcIixcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChyZXF1ZXN0LCByZXBseSkgPT4ge1xuICAgICAgICAvLyAxLiBBUEkg7JqU7LKtIOyymOumrFxuICAgICAgICBjb25zdCByZXN1bHQgPSB0aGlzLmhhbmRsZURldkFwaVJlcXVlc3QocmVxdWVzdCwgY29uZmlnKTtcbiAgICAgICAgaWYgKHJlc3VsdCkge1xuICAgICAgICAgIHJldHVybiByZXN1bHQocmVxdWVzdCwgcmVwbHkpO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdXJsID0gcmVxdWVzdC51cmw7XG5cbiAgICAgICAgLy8gMi4gU1NSIOudvOyasO2KuCDsspjrpqxcbiAgICAgICAgY29uc3QgeyBtYXRjaFNTUlJvdXRlLCByZW5kZXJTU1IgfSA9IGF3YWl0IGltcG9ydChcIi4uL3NzclwiKTtcbiAgICAgICAgY29uc3Qgc3NyTWF0Y2ggPSBtYXRjaFNTUlJvdXRlKHVybCk7XG4gICAgICAgIGlmIChzc3JNYXRjaCkge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbU1NSXSBNYXRjaGVkIHJvdXRlOiAke3Nzck1hdGNoLnJvdXRlLnBhdGh9YCk7XG4gICAgICAgICAgY29uc3QgaHRtbCA9IGF3YWl0IHJlbmRlclNTUihcbiAgICAgICAgICAgIHVybCxcbiAgICAgICAgICAgIHNzck1hdGNoLnJvdXRlLFxuICAgICAgICAgICAgc3NyTWF0Y2gucGFyYW1zLFxuICAgICAgICAgICAgcmVxdWVzdCxcbiAgICAgICAgICAgIHJlcGx5LFxuICAgICAgICAgICAgY29uZmlnLFxuICAgICAgICAgICAgdGhpcy52aXRlU2VydmVyLFxuICAgICAgICAgICk7XG4gICAgICAgICAgcmVwbHkudHlwZShcInRleHQvaHRtbFwiKTtcbiAgICAgICAgICByZXR1cm4gaHRtbDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIDMuIENTUiBmYWxsYmFja1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IGZzID0gYXdhaXQgaW1wb3J0KFwibm9kZTpmcy9wcm9taXNlc1wiKTtcbiAgICAgICAgICBsZXQgdGVtcGxhdGUgPSBhd2FpdCBmcy5yZWFkRmlsZShcbiAgICAgICAgICAgIHBhdGguam9pbih0aGlzLnZpdGVTZXJ2ZXIuY29uZmlnLnJvb3QsIFwiaW5kZXguaHRtbFwiKSxcbiAgICAgICAgICAgIFwidXRmLThcIixcbiAgICAgICAgICApO1xuICAgICAgICAgIHRlbXBsYXRlID0gYXdhaXQgdGhpcy52aXRlU2VydmVyLnRyYW5zZm9ybUluZGV4SHRtbCh1cmwsIHRlbXBsYXRlKTtcblxuICAgICAgICAgIHJlcGx5LnR5cGUoXCJ0ZXh0L2h0bWxcIik7XG4gICAgICAgICAgcmV0dXJuIHRlbXBsYXRlO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgdGhpcy52aXRlU2VydmVyLnNzckZpeFN0YWNrdHJhY2UoZSBhcyBFcnJvcik7XG4gICAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgICAgICByZXBseS5zdGF0dXMoNTAwKTtcbiAgICAgICAgICByZXR1cm4gKGUgYXMgRXJyb3IpLm1lc3NhZ2U7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICAvLyDshJzrsoQg7KKF66OMIOyLnCBWaXRl64+EIOyiheujjFxuICAgIHNlcnZlci5hZGRIb29rKFwib25DbG9zZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCB0aGlzLnZpdGVTZXJ2ZXIuY2xvc2UoKTtcbiAgICB9KTtcblxuICAgIGNvbnNvbGUubG9nKFwi4pyTIFZpdGUgZGV2IHNlcnZlciBpbnRlZ3JhdGVkXCIpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBzZXR1cFN0YXRpY1dlYlNlcnZlcihcbiAgICBzZXJ2ZXI6IEZhc3RpZnlJbnN0YW5jZTxTZXJ2ZXIsIEluY29taW5nTWVzc2FnZSwgU2VydmVyUmVzcG9uc2U+LFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgICBnbG9iYWxDb21wcmVzc09wdGlvbnM6IENvbXByZXNzT3B0aW9ucyB8IHVuZGVmaW5lZCxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8g6rK966GcIOuqhe2Zle2ZlDogYXBpL3dlYi1kaXN0L2NsaWVudCAo7KCV7KCBIO2MjOydvCksIGFwaS93ZWItZGlzdC9zZXJ2ZXIgKFNTUiBlbnRyeSksIGFwaS9kaXN0L3NzciAoU1NSIHJvdXRlcyAtIEFQSSDshozsnKApXG4gICAgY29uc3Qgd2ViRGlzdFBhdGggPSBwYXRoLmpvaW4odGhpcy5hcGlSb290UGF0aCwgXCJ3ZWItZGlzdFwiLCBcImNsaWVudFwiKTtcbiAgICBjb25zdCBzc3JQYXRoID0gcGF0aC5qb2luKHRoaXMuYXBpUm9vdFBhdGgsIFwid2ViLWRpc3RcIiwgXCJzZXJ2ZXJcIik7XG4gICAgY29uc3Qgc3NyRW50cnlQYXRoID0gcGF0aC5qb2luKHNzclBhdGgsIFwiZW50cnktc2VydmVyLmdlbmVyYXRlZC5qc1wiKTtcbiAgICBjb25zdCBzc3JSb3V0ZXNQYXRoID0gcGF0aC5qb2luKHRoaXMuYXBpUm9vdFBhdGgsIFwiZGlzdFwiLCBcInNzclwiLCBcInJvdXRlcy5qc1wiKTtcblxuICAgIGlmICghKGF3YWl0IGV4aXN0cyh3ZWJEaXN0UGF0aCkpKSB7XG4gICAgICBjb25zb2xlLndhcm4oYOKaoCBXZWIgZGlzdCBub3QgZm91bmQ6ICR7d2ViRGlzdFBhdGh9YCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gU1NSIGVudHJ5IOyhtOyerCDsl6zrtoAg7ZmV7J24XG4gICAgY29uc3Qgc3NyQXZhaWxhYmxlID0gYXdhaXQgZXhpc3RzKHNzckVudHJ5UGF0aCk7XG5cbiAgICBpZiAoIXNzckF2YWlsYWJsZSkge1xuICAgICAgY29uc29sZS53YXJuKGDimqAgU1NSIGVudHJ5IG5vdCBmb3VuZDogJHtzc3JFbnRyeVBhdGh9YCk7XG4gICAgICBjb25zb2xlLndhcm4oXCIgIFNTUiB3aWxsIGJlIGRpc2FibGVkLiBPbmx5IENTUiB3aWxsIHdvcmsuXCIpO1xuICAgIH1cblxuICAgIC8vIFNTUiDrnbzsmrDtirgg66Gc65OcIChwcm9kdWN0aW9u7JeQ7ISc66eMLCDsgqzsmqnsnpAg7ZSE66Gc7KCd7Yq47J2YIHNzci9yb3V0ZXMudHMpXG4gICAgaWYgKHNzckF2YWlsYWJsZSkge1xuICAgICAgaWYgKGF3YWl0IGV4aXN0cyhzc3JSb3V0ZXNQYXRoKSkge1xuICAgICAgICAvLyB0cy1sb2FkZXLrnbzrqbQgXCJmaWxlOi8vXCLroZwg7Iuc7J6R7ZWY64qUIGZ1bGx5LXJlc29sdmVkIHBhdGjrp4wg67Cb6riw7JeQIOydtOulvCDsspjrpqztlbTso7zripQgaW1wb3J0TWVtYmVyc+ulvCDsgqzsmqntlbTslbwg7ZaI6rKg7KeA66eMLFxuICAgICAgICAvLyDsl6zquLDripQg7ZSE66Gc642V7IWYIO2ZmOqyveyXkOyEnCBsb2FkZXIg7JeG7J20IOuPjOyVhOqwgOq4sCDrlYzrrLjsl5AgXCLsp4Tsp5wganMg7YyM7J28XCLsnZggXCLqt7jrg6VcIiDsoIjrjIDqsr3roZzrpbwg67CU66GcIGltcG9ydO2VtOuPhCDrkKnri4jri6QuXG4gICAgICAgIC8vIOydtCDrgrTsmqnsnYAg7J20IO2VqOyImCDrgrTsl5DshJwg7JWE656Y7JeQIOuCmOyYrCDri6TrpbggaW1wb3J0IO2YuOy2nOyXkOuPhCDrj5nsnbztlZjqsowg7KCB7Jqp65Cp64uI64ukLlxuICAgICAgICBhd2FpdCBpbXBvcnQoc3NyUm91dGVzUGF0aCk7XG4gICAgICAgIGNvbnNvbGUubG9nKFwi4pyTIFNTUiByb3V0ZXMgbG9hZGVkXCIpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS53YXJuKGDimqAgU1NSIHJvdXRlcyBub3QgZm91bmQ6ICR7c3NyUm91dGVzUGF0aH1gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyDroaTrp4Eg7JeF642w7J207Yq4IOuMgOydkTogYXNzZXQgaGFzaCDrtojsnbzsuZgg7IucIO2YhOyerCDrsoTsoIQg7KeB7KCRIOyEnOu5mVxuICAgIHNlcnZlci5nZXQoXCIvYXNzZXRzLzpmaWxlbmFtZVwiLCBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgIGNvbnN0IHJlcXVlc3RlZEZpbGUgPSAocmVxdWVzdC5wYXJhbXMgYXMgeyBmaWxlbmFtZTogc3RyaW5nIH0pLmZpbGVuYW1lO1xuICAgICAgY29uc3QgYXNzZXRzRGlyID0gcGF0aC5qb2luKHdlYkRpc3RQYXRoLCBcImFzc2V0c1wiKTtcbiAgICAgIGNvbnN0IHNhZmVGaWxlUGF0aCA9IHRoaXMucmVzb2x2ZVBhdGhXaXRoaW5CYXNlRGlyKGFzc2V0c0RpciwgcmVxdWVzdGVkRmlsZSk7XG4gICAgICBpZiAoc2FmZUZpbGVQYXRoID09PSBudWxsKSB7XG4gICAgICAgIHJlcGx5LnN0YXR1cyg0MDMpLnNlbmQoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3Qgbm9ybWFsaXplZFJlcXVlc3RlZEZpbGUgPSBwYXRoLnJlbGF0aXZlKGFzc2V0c0Rpciwgc2FmZUZpbGVQYXRoKS5yZXBsYWNlKC9cXFxcL2csIFwiL1wiKTtcblxuICAgICAgY29uc3QgYXNzZXRQYXRoID0gYC9hc3NldHMvJHtub3JtYWxpemVkUmVxdWVzdGVkRmlsZX1gO1xuXG4gICAgICAvLyBDYWNoZS1Db250cm9sIO2XpOuNlCDqsrDsoJVcbiAgICAgIGNvbnN0IGdldENhY2hlQ29udHJvbEZvckFzc2V0ID0gKCk6IENhY2hlQ29udHJvbENvbmZpZyA9PiB7XG4gICAgICAgIGNvbnN0IGNhY2hlUmVxOiBDYWNoZUNvbnRyb2xSZXF1ZXN0ID0ge1xuICAgICAgICAgIHR5cGU6IFwiYXNzZXRzXCIsXG4gICAgICAgICAgdXJsOiByZXF1ZXN0LnVybCxcbiAgICAgICAgICBwYXRoOiBhc3NldFBhdGgsXG4gICAgICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgICAgfTtcblxuICAgICAgICAvLyDsgqzsmqnsnpAg7KCV7J2YIO2VuOuTpOufrCDsmrDshKBcbiAgICAgICAgaWYgKGNvbmZpZy5jYWNoZUNvbnRyb2xIYW5kbGVyKSB7XG4gICAgICAgICAgY29uc3QgcmVzdWx0ID0gY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIoY2FjaGVSZXEpO1xuICAgICAgICAgIGlmIChyZXN1bHQpIHJldHVybiByZXN1bHQ7XG4gICAgICAgIH1cblxuICAgICAgICAvLyDquLDrs7jqsJI6IGltbXV0YWJsZVxuICAgICAgICByZXR1cm4gQ2FjaGVQcmVzZXRzLmltbXV0YWJsZTtcbiAgICAgIH07XG5cbiAgICAgIC8vIGluZGV4LSouanMg65iQ64qUIGluZGV4LSouY3NzIOyalOyyreyduCDqsr3smrBcbiAgICAgIGlmICgvXmluZGV4LVthLWYwLTldK1xcLihqc3xjc3MpJC8udGVzdChub3JtYWxpemVkUmVxdWVzdGVkRmlsZSkpIHtcbiAgICAgICAgY29uc3QgZXh0ID0gbm9ybWFsaXplZFJlcXVlc3RlZEZpbGUuc3BsaXQoXCIuXCIpLnBvcCgpO1xuICAgICAgICBjb25zdCBmaWxlcyA9IGF3YWl0IGZzLnJlYWRkaXIoYXNzZXRzRGlyKTtcbiAgICAgICAgY29uc3QgY3VycmVudEZpbGUgPSBmaWxlcy5maW5kKChmKSA9PiBmLnN0YXJ0c1dpdGgoXCJpbmRleC1cIikgJiYgZi5lbmRzV2l0aChgLiR7ZXh0fWApKTtcblxuICAgICAgICBpZiAoY3VycmVudEZpbGUpIHtcbiAgICAgICAgICBjb25zdCBmaWxlUGF0aCA9IHBhdGguam9pbihhc3NldHNEaXIsIGN1cnJlbnRGaWxlKTtcbiAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgZnMucmVhZEZpbGUoZmlsZVBhdGgpO1xuICAgICAgICAgIHJlcGx5LnR5cGUoZXh0ID09PSBcImpzXCIgPyBcImFwcGxpY2F0aW9uL2phdmFzY3JpcHRcIiA6IFwidGV4dC9jc3NcIik7XG4gICAgICAgICAgYXBwbHlDYWNoZUhlYWRlcnMocmVwbHksIGdldENhY2hlQ29udHJvbEZvckFzc2V0KCkpO1xuICAgICAgICAgIHJldHVybiByZXBseS5zZW5kKGNvbnRlbnQpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIOydvOuwmCDtjIzsnbwg7ISc67mZXG4gICAgICBjb25zdCBmaWxlUGF0aCA9IHNhZmVGaWxlUGF0aDtcbiAgICAgIGlmIChhd2FpdCBleGlzdHMoZmlsZVBhdGgpKSB7XG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShmaWxlUGF0aCk7XG4gICAgICAgIGNvbnN0IGV4dCA9IG5vcm1hbGl6ZWRSZXF1ZXN0ZWRGaWxlLnNwbGl0KFwiLlwiKS5wb3AoKTtcbiAgICAgICAgcmVwbHkudHlwZShleHQgPT09IFwianNcIiA/IFwiYXBwbGljYXRpb24vamF2YXNjcmlwdFwiIDogZXh0ID09PSBcImNzc1wiID8gXCJ0ZXh0L2Nzc1wiIDogXCJcIik7XG4gICAgICAgIGlmIChub3JtYWxpemVkUmVxdWVzdGVkRmlsZS5pbmNsdWRlcyhcIi1cIikpIHtcbiAgICAgICAgICBhcHBseUNhY2hlSGVhZGVycyhyZXBseSwgZ2V0Q2FjaGVDb250cm9sRm9yQXNzZXQoKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlcGx5LnNlbmQoY29udGVudCk7XG4gICAgICB9XG5cbiAgICAgIHJlcGx5LnN0YXR1cyg0MDQpLnNlbmQoKTtcbiAgICB9KTtcblxuICAgIC8vIFNTUiDrnbzsmrDtirgg6rCc67OEIOuTseuhnSAoY29tcHJlc3Mg7Ji17IWY7J20IOudvOyasO2KuOuzhOuhnCDsoIHsmqnrkJjrj4TroZ0pXG4gICAgaWYgKHNzckF2YWlsYWJsZSkge1xuICAgICAgY29uc3QgeyBnZXRTU1JSb3V0ZXMgfSA9IGF3YWl0IGltcG9ydChcIi4uL3NzclwiKTtcbiAgICAgIGNvbnN0IHsgcmVuZGVyU1NSIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9zc3IvcmVuZGVyZXJcIik7XG4gICAgICBjb25zdCBzc3JSb3V0ZXMgPSBnZXRTU1JSb3V0ZXMoKTtcblxuICAgICAgZm9yIChjb25zdCByb3V0ZSBvZiBzc3JSb3V0ZXMpIHtcbiAgICAgICAgc2VydmVyLnJvdXRlKHtcbiAgICAgICAgICBtZXRob2Q6IFtcIkdFVFwiLCBcIkhFQURcIl0sXG4gICAgICAgICAgdXJsOiByb3V0ZS5wYXRoLFxuICAgICAgICAgIGNvbXByZXNzOiB0b0Zhc3RpZnlDb21wcmVzc09wdGlvbihyb3V0ZS5jb21wcmVzcyA/PyB0cnVlLCBnbG9iYWxDb21wcmVzc09wdGlvbnMpLFxuICAgICAgICAgIGhhbmRsZXI6IGFzeW5jIChyZXF1ZXN0LCByZXBseSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdXJsID0gcmVxdWVzdC51cmw7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgW1NTUl0gTWF0Y2hlZCByb3V0ZTogJHtyb3V0ZS5wYXRofWApO1xuXG4gICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB0aGlzLmV4dHJhY3RQYXRoUGFyYW1zKHJvdXRlLnBhdGgsIHVybCk7XG4gICAgICAgICAgICBjb25zdCBodG1sID0gYXdhaXQgcmVuZGVyU1NSKHVybCwgcm91dGUsIHBhcmFtcywgcmVxdWVzdCwgcmVwbHksIGNvbmZpZyk7XG5cbiAgICAgICAgICAgIHJlcGx5LnR5cGUoXCJ0ZXh0L2h0bWxcIik7XG4gICAgICAgICAgICByZXR1cm4gaHRtbDtcbiAgICAgICAgICB9LFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBDU1Igb3IgU3RhdGljIEZpbGUgRmFsbGJhY2sgKFNTUiDrnbzsmrDtirjsl5Ag66ek7Lmt65CY7KeAIOyViuuKlCDrqqjrk6Ag7JqU7LKtKVxuICAgIHNlcnZlci5yb3V0ZSh7XG4gICAgICBtZXRob2Q6IFtcIkdFVFwiLCBcIkhFQURcIl0sXG4gICAgICB1cmw6IFwiKlwiLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcXVlc3QsIHJlcGx5KSA9PiB7XG4gICAgICAgIC8vIC9hcGksIC9zb25hbXUtdWnripQgNDA0IOq3uOuMgOuhnFxuICAgICAgICBpZiAocmVxdWVzdC51cmwuc3RhcnRzV2l0aChcIi9hcGlcIikgfHwgcmVxdWVzdC51cmwuc3RhcnRzV2l0aChcIi9zb25hbXUtdWlcIikpIHtcbiAgICAgICAgICByZXBseS5zdGF0dXMoNDA0KS5zZW5kKCk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQ1NS7JqpIENhY2hlLUNvbnRyb2wg7Zek642UIOyEpOyglVxuICAgICAgICBpZiAoY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIpIHtcbiAgICAgICAgICBjb25zdCBjc3JDYWNoZVJlcTogQ2FjaGVDb250cm9sUmVxdWVzdCA9IHtcbiAgICAgICAgICAgIHR5cGU6IFwiY3NyXCIsXG4gICAgICAgICAgICB1cmw6IHJlcXVlc3QudXJsLFxuICAgICAgICAgICAgcGF0aDogcmVxdWVzdC51cmwuc3BsaXQoXCI/XCIpWzBdLFxuICAgICAgICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgICAgICB9O1xuICAgICAgICAgIGNvbnN0IGNzckNhY2hlQ29uZmlnID0gY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIoY3NyQ2FjaGVSZXEpO1xuXG4gICAgICAgICAgaWYgKGNzckNhY2hlQ29uZmlnKSB7XG4gICAgICAgICAgICBhcHBseUNhY2hlSGVhZGVycyhyZXBseSwgY3NyQ2FjaGVDb25maWcpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOygleyggSDtjIzsnbzsnbQg7KG07J6s7ZWgIOqyveyasCwg7KCV7KCBIO2MjOydvOydhCDrqLzsoIAg7ISc67mZ7ZW07JW87ZWoXG4gICAgICAgIGNvbnN0IHJlcXVlc3RQYXRoID0gdGhpcy5nZXRQYXRobmFtZUZyb21VcmwocmVxdWVzdC51cmwpO1xuICAgICAgICBjb25zdCBzYWZlRmlsZVBhdGggPSB0aGlzLnJlc29sdmVQYXRoV2l0aGluQmFzZURpcih3ZWJEaXN0UGF0aCwgcmVxdWVzdFBhdGgpO1xuICAgICAgICBpZiAoc2FmZUZpbGVQYXRoID09PSBudWxsKSB7XG4gICAgICAgICAgcmVwbHkuc3RhdHVzKDQwMykuc2VuZCgpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoYXdhaXQgZmlsZUV4aXN0cyhzYWZlRmlsZVBhdGgpKSB7XG4gICAgICAgICAgY29uc3QgY29udGVudCA9IGF3YWl0IGZzLnJlYWRGaWxlKHNhZmVGaWxlUGF0aCk7XG4gICAgICAgICAgcmV0dXJuIHJlcGx5LnR5cGUobWltZUxvb2t1cChzYWZlRmlsZVBhdGgpIHx8IFwiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtXCIpLnNlbmQoY29udGVudCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDU1IgZmFsbGJhY2s6IGluZGV4Lmh0bWwg7ISc67mZXG4gICAgICAgIGNvbnN0IGluZGV4UGF0aCA9IHBhdGguam9pbih3ZWJEaXN0UGF0aCwgXCJpbmRleC5odG1sXCIpO1xuICAgICAgICByZXR1cm4gcmVwbHkudHlwZShcInRleHQvaHRtbFwiKS5zZW5kKGF3YWl0IGZzLnJlYWRGaWxlKGluZGV4UGF0aCwgXCJ1dGYtOFwiKSk7XG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgY29uc29sZS5sb2coYOKckyBTdGF0aWMgd2ViIHNlcnZlciBjb25maWd1cmVkIHdpdGggJHtzc3JBdmFpbGFibGUgPyBcIlNTUlwiIDogXCJDU1Igb25seVwifSBzdXBwb3J0YCk7XG4gIH1cblxuICBjcmVhdGVBcGlIYW5kbGVyKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICApOiAocmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsIHJlcGx5OiBGYXN0aWZ5UmVwbHkpID0+IFByb21pc2U8dW5rbm93bj4ge1xuICAgIHJldHVybiBhc3luYyAocmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsIHJlcGx5OiBGYXN0aWZ5UmVwbHkpOiBQcm9taXNlPHVua25vd24+ID0+IHtcbiAgICAgIC8vIENvbnRleHQg7IOd7ISxXG4gICAgICBjb25zdCBjb250ZXh0OiBDb250ZXh0ID0gYXdhaXQgdGhpcy5jcmVhdGVDb250ZXh0KGNvbmZpZywgcmVxdWVzdCwgcmVwbHkpO1xuXG4gICAgICByZXR1cm4gdGhpcy5hc3luY0xvY2FsU3RvcmFnZS5ydW4oeyBjb250ZXh0IH0sIGFzeW5jICgpID0+IHtcbiAgICAgICAgLy8gZ3VhcmRzIOyymOumrFxuICAgICAgICAoYXBpLm9wdGlvbnMuZ3VhcmRzID8/IFtdKS5ldmVyeSgoZ3VhcmQpID0+IGNvbmZpZy5ndWFyZEhhbmRsZXIoZ3VhcmQsIHJlcXVlc3QsIGFwaSkpO1xuXG4gICAgICAgIC8vIO2MjOudvOuvuO2EsCDsoJXrs7TroZwgem9kIOyKpO2CpOuniCDruYzrk5xcbiAgICAgICAgY29uc3QgeyBnZXRab2RPYmplY3RGcm9tQXBpIH0gPSBhd2FpdCBpbXBvcnQoXCIuL2NvZGUtY29udmVydGVyc1wiKTtcbiAgICAgICAgY29uc3QgUmVxVHlwZSA9IGdldFpvZE9iamVjdEZyb21BcGkoYXBpLCB0aGlzLnN5bmNlci50eXBlcyk7XG5cbiAgICAgICAgLy8gcmVxdWVzdCDtjIzsi7FcbiAgICAgICAgY29uc3Qgd2hpY2ggPSBhcGkub3B0aW9ucy5odHRwTWV0aG9kID09PSBcIkdFVFwiID8gXCJxdWVyeVwiIDogXCJib2R5XCI7XG4gICAgICAgIGxldCByZXFCb2R5OiB7XG4gICAgICAgICAgW2tleTogc3RyaW5nXTogdW5rbm93bjtcbiAgICAgICAgfTtcbiAgICAgICAgLy8g7YyM7J28IOyXheuhnOuTnCDsnojripQg6rK97JqwIOyehOyLnCDrjbDsnbTthLBcbiAgICAgICAgY29uc3QgZmlsZXM6IHtcbiAgICAgICAgICBidWZmZXJlZEZpbGVzOiBCdWZmZXJlZEZpbGVbXTtcbiAgICAgICAgICB1cGxvYWRlZEZpbGVzOiBVcGxvYWRlZEZpbGVbXTtcbiAgICAgICAgfSA9IHtcbiAgICAgICAgICBidWZmZXJlZEZpbGVzOiBbXSxcbiAgICAgICAgICB1cGxvYWRlZEZpbGVzOiBbXSxcbiAgICAgICAgfTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IGJvZHkgPSAocmVxdWVzdFt3aGljaF0gPz8ge30pIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgICAgICAgIGlmIChhcGkudXBsb2FkT3B0aW9ucykge1xuICAgICAgICAgICAgY29uc3QgcGFydHMgPSByZXF1ZXN0LnBhcnRzKHtcbiAgICAgICAgICAgICAgbGltaXRzOiBhcGkudXBsb2FkT3B0aW9ucy5saW1pdHMsXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgLy8gRm9ybURhdGHsnZggZmllbGTrk6TsnYQg7J6E7Iuc66GcIOyggOyepVxuICAgICAgICAgICAgY29uc3QgZmllbGRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgICAgICAgICAgIGlmIChhcGkudXBsb2FkT3B0aW9ucy5jb25zdW1lID09PSBcImJ1ZmZlclwiIHx8ICFhcGkudXBsb2FkT3B0aW9ucy5jb25zdW1lKSB7XG4gICAgICAgICAgICAgIC8vIEJ1ZmZlciDrqqjrk5w6IOuplOuqqOumrOyXkCDroZzrk5xcbiAgICAgICAgICAgICAgZm9yIGF3YWl0IChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgICAgICAgICAgICAgaWYgKHBhcnQudHlwZSA9PT0gXCJmaWxlXCIpIHtcbiAgICAgICAgICAgICAgICAgIC8vIENSSVRJQ0FMOiDtjIzsnbwg7Iqk7Yq466a87J2EIOymieyLnCBjb25zdW1l7ZW07JW8IOuLpOydjCBwYXJ066GcIOuEmOyWtOqwiCDsiJgg7J6I7J2MXG4gICAgICAgICAgICAgICAgICAvLyDsnbQg7Zi47Lac7J20IOyXhuycvOuptCDsooXsooUgbXVsdGlwYXJ0IO2MjOyLseydtCBwZW5kaW5nIOyDge2DnOuhnCDtg4DsnoTslYTsm4Mg67Cc7IOdXG4gICAgICAgICAgICAgICAgICBjb25zdCBidWZmZXIgPSBhd2FpdCBwYXJ0LnRvQnVmZmVyKCk7XG4gICAgICAgICAgICAgICAgICBmaWxlcy5idWZmZXJlZEZpbGVzLnB1c2gobmV3IEJ1ZmZlcmVkRmlsZShwYXJ0LCBidWZmZXIpKTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHBhcnQudHlwZSA9PT0gXCJmaWVsZFwiKSB7XG4gICAgICAgICAgICAgICAgICBmaWVsZHNbcGFydC5maWVsZG5hbWVdID0gU3RyaW5nKHBhcnQudmFsdWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmIChhcGkudXBsb2FkT3B0aW9ucy5jb25zdW1lID09PSBcInN0cmVhbVwiKSB7XG4gICAgICAgICAgICAgIC8vIFN0cmVhbSDrqqjrk5w6IOymieyLnCDsoIDsnqXshozroZwg7Iqk7Yq466as67CNXG4gICAgICAgICAgICAgIGNvbnN0IGRpc2tOYW1lID0gYXBpLnVwbG9hZE9wdGlvbnMuZGVzdGluYXRpb247XG4gICAgICAgICAgICAgIGNvbnN0IGRpc2sgPSB0aGlzLnN0b3JhZ2UudXNlKGRpc2tOYW1lKTtcblxuICAgICAgICAgICAgICAvLyDsmrDshKDsiJzsnIQ6IOuNsOy9lOugiOydtO2EsCA+IOyghOyXrSDshKTsoJUgPiDquLDrs7jqsJJcbiAgICAgICAgICAgICAgY29uc3Qga2V5R2VuZXJhdG9yOiBLZXlHZW5lcmF0b3IgPVxuICAgICAgICAgICAgICAgIGFwaS51cGxvYWRPcHRpb25zLmtleUdlbmVyYXRvciA/P1xuICAgICAgICAgICAgICAgIHRoaXMuY29uZmlnLnNlcnZlci5zdG9yYWdlPy5rZXlHZW5lcmF0b3IgPz9cbiAgICAgICAgICAgICAgICBkZWZhdWx0S2V5R2VuZXJhdG9yO1xuXG4gICAgICAgICAgICAgIGZvciBhd2FpdCAoY29uc3QgcGFydCBvZiBwYXJ0cykge1xuICAgICAgICAgICAgICAgIGlmIChwYXJ0LnR5cGUgPT09IFwiZmlsZVwiKSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCBrZXkgPSBhd2FpdCBrZXlHZW5lcmF0b3Ioe1xuICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZTogcGFydC5maWxlbmFtZSxcbiAgICAgICAgICAgICAgICAgICAgbWltZXR5cGU6IHBhcnQubWltZXR5cGUsXG4gICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgYXdhaXQgZGlzay5wdXRTdHJlYW0oa2V5LCBwYXJ0LmZpbGUsIHtcbiAgICAgICAgICAgICAgICAgICAgY29udGVudFR5cGU6IHBhcnQubWltZXR5cGUsXG4gICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgY29uc3QgdXJsID0gYXdhaXQgZGlzay5nZXRVcmwoa2V5KTtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHNpZ25lZFVybCA9IGF3YWl0IGRpc2suZ2V0U2lnbmVkVXJsKGtleSk7XG5cbiAgICAgICAgICAgICAgICAgIGZpbGVzLnVwbG9hZGVkRmlsZXMucHVzaChcbiAgICAgICAgICAgICAgICAgICAgbmV3IFVwbG9hZGVkRmlsZSh7XG4gICAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWU6IHBhcnQuZmlsZW5hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgbWltZXR5cGU6IHBhcnQubWltZXR5cGUsXG4gICAgICAgICAgICAgICAgICAgICAgc2l6ZTogcGFydC5maWxlLmJ5dGVzUmVhZCxcbiAgICAgICAgICAgICAgICAgICAgICB1cmwsXG4gICAgICAgICAgICAgICAgICAgICAgc2lnbmVkVXJsLFxuICAgICAgICAgICAgICAgICAgICAgIGtleSxcbiAgICAgICAgICAgICAgICAgICAgICBkaXNrTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAocGFydC50eXBlID09PSBcImZpZWxkXCIpIHtcbiAgICAgICAgICAgICAgICAgIGZpZWxkc1twYXJ0LmZpZWxkbmFtZV0gPSBTdHJpbmcocGFydC52YWx1ZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIHFz66GcIOykkeyyqSDqtazsobAg7YyM7IuxOiBwYXJhbXNbY2F0ZWdvcnldIOKGkiB7IHBhcmFtczogeyBjYXRlZ29yeTogXCJ0ZXN0XCIgfSB9XG4gICAgICAgICAgICBjb25zdCBxcyA9IGF3YWl0IGltcG9ydChcInFzXCIpO1xuICAgICAgICAgICAgY29uc3QgcGFyc2VkID0gcXMuZGVmYXVsdC5wYXJzZShmaWVsZHMpO1xuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbihib2R5LCBwYXJzZWQpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IHsgZmFzdGlmeUNhc3RlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi9jYXN0ZXJcIik7XG4gICAgICAgICAgcmVxQm9keSA9IGZhc3RpZnlDYXN0ZXIoUmVxVHlwZSkucGFyc2UoYm9keSk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICBjb25zdCB7IFpvZEVycm9yIH0gPSBhd2FpdCBpbXBvcnQoXCJ6b2RcIik7XG4gICAgICAgICAgaWYgKGUgaW5zdGFuY2VvZiBab2RFcnJvcikge1xuICAgICAgICAgICAgY29uc3QgeyBodW1hbml6ZVpvZEVycm9yIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy96b2QtZXJyb3JcIik7XG4gICAgICAgICAgICBjb25zdCBtZXNzYWdlcyA9IGh1bWFuaXplWm9kRXJyb3IoZSlcbiAgICAgICAgICAgICAgLm1hcCgoaXNzdWUpID0+IGlzc3VlLm1lc3NhZ2UpXG4gICAgICAgICAgICAgIC5qb2luKFwiIFwiKTtcbiAgICAgICAgICAgIGNvbnN0IHsgQmFkUmVxdWVzdEV4Y2VwdGlvbiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vZXhjZXB0aW9ucy9zby1leGNlcHRpb25zXCIpO1xuICAgICAgICAgICAgdGhyb3cgbmV3IEJhZFJlcXVlc3RFeGNlcHRpb24obWVzc2FnZXMgYXMgTG9jYWxpemVkU3RyaW5nLCB7XG4gICAgICAgICAgICAgIHpvZEVycm9yOiBlLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRocm93IGU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gQ29udGVudC1UeXBlXG4gICAgICAgIHJlcGx5LnR5cGUoYXBpLm9wdGlvbnMuY29udGVudFR5cGUgPz8gXCJhcHBsaWNhdGlvbi9qc29uXCIpO1xuXG4gICAgICAgIC8vIENhY2hlLUNvbnRyb2wg7Zek642UIOyEpOyglVxuICAgICAgICBjb25zdCBhcGlDYWNoZUNvbmZpZyA9IHRoaXMuZ2V0QXBpQ2FjaGVDb250cm9sKGFwaSwgcmVxdWVzdCwgY29uZmlnKTtcbiAgICAgICAgaWYgKGFwaUNhY2hlQ29uZmlnKSB7XG4gICAgICAgICAgYXBwbHlDYWNoZUhlYWRlcnMocmVwbHksIGFwaUNhY2hlQ29uZmlnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOyXheuhnOuTnCDsmLXshZjsnbQg7J6I64qUIOqyveyasCDtjIzsnbwg642w7J207YSw66W8IENvbnRleHTsl5Ag7LaU6rCAXG4gICAgICAgIGlmIChhcGkudXBsb2FkT3B0aW9ucykge1xuICAgICAgICAgIGNvbnN0IGNvbnN1bWUgPSBhcGkudXBsb2FkT3B0aW9ucy5jb25zdW1lID8/IFwiYnVmZmVyXCI7XG4gICAgICAgICAgaWYgKGNvbnN1bWUgPT09IFwiYnVmZmVyXCIpIHtcbiAgICAgICAgICAgIGNvbnRleHQuYnVmZmVyZWRGaWxlcyA9IGZpbGVzLmJ1ZmZlcmVkRmlsZXM7XG4gICAgICAgICAgfSBlbHNlIGlmIChjb25zdW1lID09PSBcInN0cmVhbVwiKSB7XG4gICAgICAgICAgICBjb250ZXh0LnVwbG9hZGVkRmlsZXMgPSBmaWxlcy51cGxvYWRlZEZpbGVzO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOuqqOuNuCDrqZTshozrk5wgYXJncyDsg53shLHtlZjsl6wg7Zi47LacXG4gICAgICAgIGNvbnN0IHsgQXBpUGFyYW1UeXBlIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi90eXBlcy90eXBlc1wiKTtcbiAgICAgICAgY29uc3QgYXJncyA9IGFwaS5wYXJhbWV0ZXJzLm1hcCgocGFyYW0pID0+IHtcbiAgICAgICAgICAvLyBDb250ZXh0IOyduOygneyFmFxuICAgICAgICAgIGlmIChBcGlQYXJhbVR5cGUuaXNDb250ZXh0KHBhcmFtLnR5cGUpKSB7XG4gICAgICAgICAgICByZXR1cm4gY29udGV4dDtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHJlcUJvZHlbcGFyYW0ubmFtZV07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gdGhpcy5pbnZva2VNb2RlbE1ldGhvZChhcGksIGFyZ3MsIHJlcGx5KTtcbiAgICAgIH0pO1xuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogVVJM7JeQ7IScIHBhdGggcGFyYW1z66W8IOy2lOy2nO2VqeuLiOuLpC5cbiAgICog7JiIOiBwYXR0ZXJuPVwiL2FkbWluL2NvbXBhbmllcy86Y29tcGFueUlkXCIsIHVybD1cIi9hZG1pbi9jb21wYW5pZXMvMTIzXCIg4oaSIHsgY29tcGFueUlkOiBcIjEyM1wiIH1cbiAgICovXG4gIHByaXZhdGUgZXh0cmFjdFBhdGhQYXJhbXMocGF0dGVybjogc3RyaW5nLCB1cmw6IHN0cmluZyk6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4ge1xuICAgIGNvbnN0IHBhdHRlcm5QYXJ0cyA9IHBhdHRlcm4uc3BsaXQoXCIvXCIpLmZpbHRlcihCb29sZWFuKTtcbiAgICBjb25zdCB1cmxQYXJ0cyA9IHRoaXMuZ2V0UGF0aG5hbWVGcm9tVXJsKHVybCkuc3BsaXQoXCIvXCIpLmZpbHRlcihCb29sZWFuKTtcbiAgICBjb25zdCBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGF0dGVyblBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAocGF0dGVyblBhcnRzW2ldLnN0YXJ0c1dpdGgoXCI6XCIpKSB7XG4gICAgICAgIHBhcmFtc1twYXR0ZXJuUGFydHNbaV0uc2xpY2UoMSldID0gdXJsUGFydHNbaV07XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBwYXJhbXM7XG4gIH1cblxuICBwcml2YXRlIGlzUGF0aFBhdHRlcm5NYXRjaChwYXR0ZXJuOiBzdHJpbmcsIHVybDogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgY29uc3QgcGF0dGVyblBhcnRzID0gcGF0dGVybi5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuICAgIGNvbnN0IHVybFBhcnRzID0gdGhpcy5nZXRQYXRobmFtZUZyb21VcmwodXJsKS5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuXG4gICAgaWYgKHBhdHRlcm5QYXJ0cy5sZW5ndGggIT09IHVybFBhcnRzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGF0dGVyblBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBwYXR0ZXJuUGFydCA9IHBhdHRlcm5QYXJ0c1tpXTtcbiAgICAgIGNvbnN0IHVybFBhcnQgPSB1cmxQYXJ0c1tpXTtcbiAgICAgIGlmIChwYXR0ZXJuUGFydC5zdGFydHNXaXRoKFwiOlwiKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmIChwYXR0ZXJuUGFydCAhPT0gdXJsUGFydCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICBwcml2YXRlIGdldFBhdGhuYW1lRnJvbVVybCh1cmw6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHVybC5zcGxpdChcIj9cIilbMF07XG4gIH1cblxuICBwcml2YXRlIHJlc29sdmVQYXRoV2l0aGluQmFzZURpcihiYXNlRGlyOiBzdHJpbmcsIGlucHV0UGF0aDogc3RyaW5nKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGRlY29kZWQgPSBkZWNvZGVVUklDb21wb25lbnQoaW5wdXRQYXRoKS5yZXBsYWNlKC9cXFxcL2csIFwiL1wiKTtcbiAgICAgIGlmIChkZWNvZGVkLmluY2x1ZGVzKFwiXFwwXCIpKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgY29uc3QgcmVsYXRpdmVQYXRoID0gZGVjb2RlZC5yZXBsYWNlKC9eXFwvKy8sIFwiXCIpO1xuICAgICAgY29uc3QgcmVzb2x2ZWRQYXRoID0gcGF0aC5yZXNvbHZlKGJhc2VEaXIsIHJlbGF0aXZlUGF0aCk7XG4gICAgICBjb25zdCByZWxhdGl2ZUZyb21CYXNlID0gcGF0aC5yZWxhdGl2ZShiYXNlRGlyLCByZXNvbHZlZFBhdGgpO1xuICAgICAgaWYgKHJlbGF0aXZlRnJvbUJhc2Uuc3RhcnRzV2l0aChcIi4uXCIpIHx8IHBhdGguaXNBYnNvbHV0ZShyZWxhdGl2ZUZyb21CYXNlKSkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXNvbHZlZFBhdGg7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQVBJIOydkeuLteyXkCDsoIHsmqntlaAgQ2FjaGUtQ29udHJvbCDshKTsoJXsnYQg6rKw7KCV7ZWp64uI64ukLlxuICAgKiDsmrDshKDsiJzsnIQ6IOqwnOuzhCDsp4DsoJUgPiBjYWNoZUNvbnRyb2xIYW5kbGVyXG4gICAqL1xuICBwcml2YXRlIGdldEFwaUNhY2hlQ29udHJvbChcbiAgICBhcGk6IEV4dGVuZGVkQXBpLFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgKSB7XG4gICAgLy8g642w7L2U66CI7J207YSwIOyEpOyglSDsmrDshKBcbiAgICBpZiAoYXBpLm9wdGlvbnMuY2FjaGVDb250cm9sKSB7XG4gICAgICByZXR1cm4gYXBpLm9wdGlvbnMuY2FjaGVDb250cm9sO1xuICAgIH1cblxuICAgIC8vIOyghOyXrSDtlbjrk6Trn6xcbiAgICBpZiAoY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIpIHtcbiAgICAgIGNvbnN0IGNhY2hlUmVxOiBDYWNoZUNvbnRyb2xSZXF1ZXN0ID0ge1xuICAgICAgICB0eXBlOiBcImFwaVwiLFxuICAgICAgICB1cmw6IHJlcXVlc3QudXJsLFxuICAgICAgICBwYXRoOiByZXF1ZXN0LnJvdXRlT3B0aW9ucz8udXJsID8/IHJlcXVlc3QudXJsLnNwbGl0KFwiP1wiKVswXSxcbiAgICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgICAgYXBpLFxuICAgICAgfTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGNvbmZpZy5jYWNoZUNvbnRyb2xIYW5kbGVyKGNhY2hlUmVxKTtcbiAgICAgIGlmIChyZXN1bHQpIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKipcbiAgICogU1NS7JqpIEFQSSDtmLjstpwgKEhUVFAg7Jik67KE7Zek65OcIOyXhuydtCDsp4HsoJEg7Zi47LacKVxuICAgKiBjcmVhdGVBcGlIYW5kbGVy7J2YIOuhnOyngeydhCDsnqzsgqzsmqntlZjrkJgsIHJlcXVlc3Qg7YyM7IuxIOuMgOyLoCBwYXJhbXMg7KeB7KCRIOyCrOyaqVxuICAgKi9cbiAgYXN5bmMgaW52b2tlQXBpRm9yU1NSKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBTU1Lsl5DshJwg64uk7JaR7ZWcIO2DgOyeheydmCBwYXJhbXPrpbwg67Cb7JWE7JW8IO2VqFxuICAgIHBhcmFtczogYW55W10sXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIC8vIENvbnRleHQg7IOd7ISxICjquLDsobQg66mU7IaM65OcIOyerOyCrOyaqSlcbiAgICBjb25zdCBjb250ZXh0ID0gYXdhaXQgdGhpcy5jcmVhdGVDb250ZXh0KGNvbmZpZywgcmVxdWVzdCwgcmVwbHkpO1xuXG4gICAgcmV0dXJuIHRoaXMuYXN5bmNMb2NhbFN0b3JhZ2UucnVuKHsgY29udGV4dCB9LCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBhcmdzIOyDneyEsTogQ29udGV4dCDtjIzrnbzrr7jthLDripQg7KO87J6FLCDrgpjrqLjsp4DripQgcGFyYW1z7JeQ7IScIOqwgOyguOyYpOq4sFxuICAgICAgY29uc3QgeyBBcGlQYXJhbVR5cGUgfSA9IGF3YWl0IGltcG9ydChcIi4uL3R5cGVzL3R5cGVzXCIpO1xuICAgICAgbGV0IHBhcmFtc0luZGV4ID0gMDtcbiAgICAgIGNvbnN0IGFyZ3MgPSBhcGkucGFyYW1ldGVycy5tYXAoKHBhcmFtKSA9PiB7XG4gICAgICAgIGlmIChBcGlQYXJhbVR5cGUuaXNDb250ZXh0KHBhcmFtLnR5cGUpKSB7XG4gICAgICAgICAgcmV0dXJuIGNvbnRleHQ7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHBhcmFtc1twYXJhbXNJbmRleCsrXTtcbiAgICAgIH0pO1xuXG4gICAgICAvLyDrqqjrjbgg66mU7ISc65OcIO2YuOy2nCAo6riw7KG0IOuplOyEnOuTnCDsnqzsgqzsmqkpXG4gICAgICByZXR1cm4gdGhpcy5pbnZva2VNb2RlbE1ldGhvZChhcGksIGFyZ3MsIHJlcGx5KTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIGludm9rZU1vZGVsTWV0aG9kKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgYXJnczogdW5rbm93bltdLFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIGNvbnN0IG1vZGVsID0gdGhpcy5zeW5jZXIubW9kZWxzW2FwaS5tb2RlbE5hbWVdO1xuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9FeHBsaWNpdEFueTogbW9kZWzsnYAg66qo6424IOyduOyKpO2EtOyKpOydtOuvgOuhnCDrqZTshJzrk5wg7Zi47LacIOqwgOuKpVxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IChtb2RlbCBhcyBhbnkpW2FwaS5tZXRob2ROYW1lXS5hcHBseShtb2RlbCwgYXJncyk7XG4gICAgcmVwbHkudHlwZShhcGkub3B0aW9ucy5jb250ZW50VHlwZSA/PyBcImFwcGxpY2F0aW9uL2pzb25cIik7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgYXN5bmMgY3JlYXRlQ29udGV4dChcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICAgcmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsXG4gICAgcmVwbHk6IEZhc3RpZnlSZXBseSxcbiAgKTogUHJvbWlzZTxDb250ZXh0PiB7XG4gICAgLy8gY3JlYXRlU1NFRmFjdG9yeSDtlajsiJjsl5Ag66+466asIHJlcXVlc3TsnZggc29ja2V06rO8IHJlcGx566W8IOuwlOyduOuUqS5cbiAgICBjb25zdCB7IGNyZWF0ZVNTRUZhY3RvcnkgfSA9IGF3YWl0IGltcG9ydChcIi4uL3N0cmVhbS9zc2VcIik7XG4gICAgY29uc3QgY3JlYXRlU1NFID0gKDxUIGV4dGVuZHMgWm9kT2JqZWN0PihcbiAgICAgIF9yZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCxcbiAgICAgIF9yZXBseTogRmFzdGlmeVJlcGx5LFxuICAgICAgX2V2ZW50czogVCxcbiAgICApID0+IGNyZWF0ZVNTRUZhY3RvcnkoX3JlcXVlc3Quc29ja2V0LCBfcmVwbHksIF9ldmVudHMpKS5iaW5kKG51bGwsIHJlcXVlc3QsIHJlcGx5KTtcblxuICAgIC8vIGxvY2FsZSDqsJDsp4BcbiAgICBjb25zdCBsb2NhbGUgPVxuICAgICAgdGhpcy5kZXRlY3RMb2NhbGUocmVxdWVzdC5oZWFkZXJzW1wiYWNjZXB0LWxhbmd1YWdlXCJdLCB0aGlzLmNvbmZpZy5pMThuLnN1cHBvcnRlZExvY2FsZXMpID8/XG4gICAgICB0aGlzLmNvbmZpZy5pMThuLmRlZmF1bHRMb2NhbGU7XG5cbiAgICAvLyBhdXRoIGNvbnRleHQg7LaU6rCAXG4gICAgY29uc3QgaGVhZGVycyA9IGNvbnZlcnRGYXN0aWZ5SGVhZGVyc1RvU3RhbmRhcmQocmVxdWVzdC5oZWFkZXJzKTtcbiAgICBjb25zdCBzZXNzaW9uID0gKGF3YWl0IHRoaXMuX2F1dGg/LmFwaS5nZXRTZXNzaW9uKHsgaGVhZGVycyB9KSkgPz8gbnVsbDtcblxuICAgIGNvbnN0IGNvbnRleHQ6IENvbnRleHQgPSB7XG4gICAgICAuLi4oYXdhaXQgUHJvbWlzZS5yZXNvbHZlKFxuICAgICAgICBjb25maWcuY29udGV4dFByb3ZpZGVyKFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHJlcXVlc3QsXG4gICAgICAgICAgICByZXBseSxcbiAgICAgICAgICAgIGhlYWRlcnM6IHJlcXVlc3QuaGVhZGVycyxcbiAgICAgICAgICAgIGNyZWF0ZVNTRSxcbiAgICAgICAgICAgIG5haXRlU3RvcmU6IE5haXRlLmNyZWF0ZVN0b3JlKCksXG4gICAgICAgICAgICBsb2NhbGUsXG4gICAgICAgICAgICAvLyBhdXRoXG4gICAgICAgICAgICB1c2VyOiBzZXNzaW9uPy51c2VyID8/IG51bGwsXG4gICAgICAgICAgICBzZXNzaW9uOiBzZXNzaW9uPy5zZXNzaW9uID8/IG51bGwsXG4gICAgICAgICAgfSxcbiAgICAgICAgICByZXF1ZXN0LFxuICAgICAgICAgIHJlcGx5LFxuICAgICAgICApLFxuICAgICAgKSksXG4gICAgfTtcbiAgICByZXR1cm4gY29udGV4dDtcbiAgfVxuXG4gIC8qKlxuICAgKiBBY2NlcHQtTGFuZ3VhZ2Ug7Zek642U7JeQ7IScIOyngOybkO2VmOuKlCBsb2NhbGXsnYQg7LC+7Iq164uI64ukLlxuICAgKiBAZXhhbXBsZSBcImtvLUtSLGtvO3E9MC45LGVuO3E9MC44XCIg4oaSIFwia29cIlxuICAgKi9cbiAgcHJpdmF0ZSBkZXRlY3RMb2NhbGUoXG4gICAgYWNjZXB0TGFuZ3VhZ2U6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICBzdXBwb3J0ZWQ6IHN0cmluZ1tdLFxuICApOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAgIGlmICghYWNjZXB0TGFuZ3VhZ2UpIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgICAvLyBBY2NlcHQtTGFuZ3VhZ2U6IGtvLUtSLGtvO3E9MC45LGVuO3E9MC44XG4gICAgY29uc3QgbGFuZ3MgPSBhY2NlcHRMYW5ndWFnZS5zcGxpdChcIixcIikubWFwKChsYW5nKSA9PiB7XG4gICAgICBjb25zdCBbY29kZV0gPSBsYW5nLnNwbGl0KFwiO1wiKTtcbiAgICAgIHJldHVybiBjb2RlLnRyaW0oKS5zcGxpdChcIi1cIilbMF07IC8vIGtvLUtSIOKGkiBrb1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIGxhbmdzLmZpbmQoKGxhbmcpID0+IHN1cHBvcnRlZC5pbmNsdWRlcyhsYW5nKSk7XG4gIH1cblxuICBhc3luYyBzdGFydFdhdGNoZXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgd2F0Y2hQYXRoID0gW3BhdGguam9pbih0aGlzLmFwaVJvb3RQYXRoLCBcInNyY1wiKV07XG5cbiAgICBjb25zdCBjaG9raWRhciA9IChhd2FpdCBpbXBvcnQoXCJjaG9raWRhclwiKSkuZGVmYXVsdDtcbiAgICB0aGlzLndhdGNoZXIgPSBjaG9raWRhci53YXRjaCh3YXRjaFBhdGgsIHtcbiAgICAgIGlnbm9yZWQ6IChwYXRoLCBzdGF0cykgPT5cbiAgICAgICAgISFzdGF0cz8uaXNGaWxlKCkgJiYgIXBhdGguZW5kc1dpdGgoXCIudHNcIikgJiYgIXBhdGguZW5kc1dpdGgoXCIuanNvblwiKSxcbiAgICAgIHBlcnNpc3RlbnQ6IHRydWUsXG4gICAgICBpZ25vcmVJbml0aWFsOiB0cnVlLFxuICAgIH0pO1xuXG4gICAgdGhpcy53YXRjaGVyLm9uKFwiYWxsXCIsIGFzeW5jIChldmVudDogc3RyaW5nLCBmaWxlUGF0aDogc3RyaW5nKSA9PiB7XG4gICAgICBjb25zdCBhYnNvbHV0ZVBhdGggPSBmaWxlUGF0aCBhcyBBYnNvbHV0ZVBhdGg7XG4gICAgICBhc3NlcnQoXG4gICAgICAgIGFic29sdXRlUGF0aC5zdGFydHNXaXRoKHRoaXMuYXBpUm9vdFBhdGgpLFxuICAgICAgICBcIkZpbGUgcGF0aCBpcyBub3Qgd2l0aGluIHRoZSBBUEkgcm9vdCBwYXRoXCIsXG4gICAgICApO1xuXG4gICAgICBpZiAoZXZlbnQgIT09IFwiY2hhbmdlXCIgJiYgZXZlbnQgIT09IFwiYWRkXCIpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICB0cnkge1xuICAgICAgICAvLyBzb25hbXUuY29uZmlnLnRzIOuzgOqyvSDsi5wg7J6s7Iuc7J6RXG4gICAgICAgIGNvbnN0IGlzQ29uZmlnVHMgPSBmaWxlUGF0aCA9PT0gcGF0aC5qb2luKHRoaXMuYXBpUm9vdFBhdGgsIFwic3JjXCIsIFwic29uYW11LmNvbmZpZy50c1wiKTtcblxuICAgICAgICBpZiAoaXNDb25maWdUcykge1xuICAgICAgICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IGZpbGVQYXRoLnJlcGxhY2UodGhpcy5hcGlSb290UGF0aCwgXCJhcGlcIik7XG4gICAgICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBjaGFsay5ib2xkKGBEZXRlY3RlZCgke2V2ZW50fSk6ICR7Y2hhbGsuYmx1ZShyZWxhdGl2ZVBhdGgpfSAtIFJlc3RhcnRpbmcuLi5gKSxcbiAgICAgICAgICApO1xuICAgICAgICAgIHByb2Nlc3Mua2lsbChwcm9jZXNzLnBpZCwgXCJTSUdVU1IyXCIpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IHRoaXMuaGFuZGxlRmlsZUNoYW5nZShldmVudCwgYWJzb2x1dGVQYXRoKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qXG4gICAgIEEgZnVuY3Rpb24gdGhhdCBhdXRvbWF0aWNhbGx5IGhhbmRsZXMgaW5pdCBhbmQgZGVzdHJveSB3aGVuIHVzaW5nIFNvbmFtdSB2aWEgc2NyaXB0cy5cbiAgKi9cbiAgYXN5bmMgcnVuU2NyaXB0KGZuOiAoKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gICAgYXdhaXQgdGhpcy5pbml0KHRydWUsIGZhbHNlLCB1bmRlZmluZWQsIGZhbHNlKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZm4oKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgdGhpcy5kZXN0cm95KCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWdpc3RlclBsdWdpbnMoc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2UsIHBsdWdpbnM6IFNvbmFtdVNlcnZlck9wdGlvbnNbXCJwbHVnaW5zXCJdKSB7XG4gICAgaWYgKCFwbHVnaW5zKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gY29tcHJlc3Mg7ZSM65+s6re47J247J2AIOuLpOuluCDtlIzrn6zqt7jsnbjrs7Tri6Qg66i87KCAIOuTseuhneuQmOyWtOyVvCDtlanri4jri6QuXG4gICAgaWYgKHBsdWdpbnMuY29tcHJlc3MpIHtcbiAgICAgIGNvbnN0IGNvbXByZXNzUGx1Z2luID0gKGF3YWl0IGltcG9ydChcIkBmYXN0aWZ5L2NvbXByZXNzXCIpKS5kZWZhdWx0O1xuICAgICAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSB7XG4gICAgICAgIHRocmVzaG9sZDogMTAyNCxcbiAgICAgICAgZW5jb2RpbmdzOiBbXCJiclwiLCBcImd6aXBcIiwgXCJkZWZsYXRlXCJdIGFzIChcImJyXCIgfCBcImd6aXBcIiB8IFwiZGVmbGF0ZVwiKVtdLFxuICAgICAgfTtcblxuICAgICAgaWYgKHBsdWdpbnMuY29tcHJlc3MgPT09IHRydWUpIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKGNvbXByZXNzUGx1Z2luLCBkZWZhdWx0T3B0aW9ucyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzZXJ2ZXIucmVnaXN0ZXIoY29tcHJlc3NQbHVnaW4sIHtcbiAgICAgICAgICAuLi5kZWZhdWx0T3B0aW9ucyxcbiAgICAgICAgICAuLi5wbHVnaW5zLmNvbXByZXNzLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBwbHVnaW5zTW9kdWxlcyA9IHtcbiAgICAgIGNvcnM6IFwiQGZhc3RpZnkvY29yc1wiLFxuICAgICAgZm9ybWJvZHk6IFwiQGZhc3RpZnkvZm9ybWJvZHlcIixcbiAgICAgIG11bHRpcGFydDogXCJAZmFzdGlmeS9tdWx0aXBhcnRcIixcbiAgICAgIHFzOiBcImZhc3RpZnktcXNcIixcbiAgICAgIHNzZTogXCJmYXN0aWZ5LXNzZS12MlwiLFxuICAgICAgc3RhdGljOiBcIkBmYXN0aWZ5L3N0YXRpY1wiLFxuICAgIH0gYXMgY29uc3Q7XG5cbiAgICBjb25zdCByZWdpc3RlclBsdWdpbiA9IGFzeW5jIDxLIGV4dGVuZHMga2V5b2YgTm9uTnVsbGFibGU8dHlwZW9mIHBsdWdpbnM+PihcbiAgICAgIGtleTogSyxcbiAgICAgIHBsdWdpbk5hbWU6IHN0cmluZyxcbiAgICApID0+IHtcbiAgICAgIGNvbnN0IG9wdGlvbiA9IHBsdWdpbnNba2V5XTtcbiAgICAgIGlmICghb3B0aW9uKSByZXR1cm47XG5cbiAgICAgIGlmIChvcHRpb24gPT09IHRydWUpIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKChhd2FpdCBpbXBvcnQocGx1Z2luTmFtZSkpLmRlZmF1bHQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKChhd2FpdCBpbXBvcnQocGx1Z2luTmFtZSkpLmRlZmF1bHQsIG9wdGlvbik7XG4gICAgICB9XG4gICAgfTtcblxuICAgIGZvciAoY29uc3QgW2tleSwgcGx1Z2luTmFtZV0gb2YgT2JqZWN0LmVudHJpZXMocGx1Z2luc01vZHVsZXMpKSB7XG4gICAgICBhd2FpdCByZWdpc3RlclBsdWdpbihrZXkgYXMga2V5b2YgdHlwZW9mIHBsdWdpbnMsIHBsdWdpbk5hbWUpO1xuICAgIH1cblxuICAgIGlmIChwbHVnaW5zLmN1c3RvbSkge1xuICAgICAgcGx1Z2lucy5jdXN0b20oc2VydmVyKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogYmV0dGVyLWF1dGgg65287Jqw7Yq466W8IOuTseuhne2VqeuLiOuLpC5cbiAgICogL2FwaS9hdXRoLyog6rK966Gc66GcIOyduOymnSBBUEnqsIAg7J6Q64+ZIOuTseuhneuQqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVnaXN0ZXJCZXR0ZXJBdXRoKFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlLFxuICAgIG9wdGlvbnM6IE5vbk51bGxhYmxlPFNvbmFtdVNlcnZlck9wdGlvbnNbXCJhdXRoXCJdPixcbiAgKSB7XG4gICAgaWYgKCFvcHRpb25zKSByZXR1cm47XG5cbiAgICBjb25zdCBiYXNlUGF0aCA9IG9wdGlvbnMuYmFzZVBhdGggPz8gXCIvYXBpL2F1dGhcIjtcblxuICAgIC8vIGJldHRlci1hdXRoIOudvOyasO2KuCDrk7HroZ1cbiAgICBzZXJ2ZXIucm91dGUoe1xuICAgICAgbWV0aG9kOiBbXCJHRVRcIiwgXCJQT1NUXCJdLFxuICAgICAgdXJsOiBgJHtiYXNlUGF0aH0vKmAsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgICAgY29uc3QgdXJsID0gbmV3IFVSTChyZXF1ZXN0LnVybCwgYGh0dHA6Ly8ke3JlcXVlc3QuaGVhZGVycy5ob3N0fWApO1xuICAgICAgICBjb25zdCBoZWFkZXJzID0gY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZChyZXF1ZXN0LmhlYWRlcnMpO1xuICAgICAgICBjb25zdCByZXEgPSBuZXcgUmVxdWVzdCh1cmwudG9TdHJpbmcoKSwge1xuICAgICAgICAgIG1ldGhvZDogcmVxdWVzdC5tZXRob2QsXG4gICAgICAgICAgaGVhZGVycyxcbiAgICAgICAgICAuLi4ocmVxdWVzdC5ib2R5ID8geyBib2R5OiBKU09OLnN0cmluZ2lmeShyZXF1ZXN0LmJvZHkpIH0gOiB7fSksXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5hdXRoLmhhbmRsZXIocmVxKTtcblxuICAgICAgICByZXBseS5zdGF0dXMocmVzcG9uc2Uuc3RhdHVzKTtcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVycy5mb3JFYWNoKCh2YWx1ZTogc3RyaW5nLCBrZXk6IHN0cmluZykgPT4ge1xuICAgICAgICAgIHJlcGx5LmhlYWRlcihrZXksIHZhbHVlKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiByZXBseS5zZW5kKHJlc3BvbnNlLmJvZHkgPyBhd2FpdCByZXNwb25zZS50ZXh0KCkgOiBudWxsKTtcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihg4pyTIGJldHRlci1hdXRoIHJlZ2lzdGVyZWQgYXQgJHtiYXNlUGF0aH0vKmApKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaW5pdGlhbGl6ZUNhY2hlKGNvbmZpZzogQ2FjaGVDb25maWcgfCB1bmRlZmluZWQsIGZvclRlc3Rpbmc6IGJvb2xlYW4pIHtcbiAgICBjb25zdCB7IHNldENhY2hlTWFuYWdlclJlZiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vY2FjaGUvZGVjb3JhdG9yXCIpO1xuXG4gICAgLy8g7YWM7Iqk7Yq4IO2ZmOqyveyXkOyEnCDrqZTrqqjrpqwg65Oc65287J2067KEIOyekOuPmSDsgqzsmqlcbiAgICBpZiAoZm9yVGVzdGluZykge1xuICAgICAgY29uc3QgeyBjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9jYWNoZS9jYWNoZS1tYW5hZ2VyXCIpO1xuICAgICAgdGhpcy5fY2FjaGUgPSBjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyKCk7XG4gICAgICBzZXRDYWNoZU1hbmFnZXJSZWYodGhpcy5fY2FjaGUpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIOyEpOygleydtCDsl4bsnLzrqbQg7LqQ7IucIOu5hO2ZnOyEse2ZlFxuICAgIGlmICghY29uZmlnKSB7XG4gICAgICBzZXRDYWNoZU1hbmFnZXJSZWYobnVsbCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8g7ISk7KCV7JeQIOuUsOudvCBDYWNoZU1hbmFnZXIg7IOd7ISxXG4gICAgY29uc3QgeyBjcmVhdGVDYWNoZU1hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL2NhY2hlL2NhY2hlLW1hbmFnZXJcIik7XG4gICAgdGhpcy5fY2FjaGUgPSBjcmVhdGVDYWNoZU1hbmFnZXIoY29uZmlnKTtcbiAgICBzZXRDYWNoZU1hbmFnZXJSZWYodGhpcy5fY2FjaGUpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBpbml0aWFsaXplV29ya2Zsb3dzKG9wdGlvbnM6IFNvbmFtdVRhc2tPcHRpb25zIHwgdW5kZWZpbmVkKSB7XG4gICAgY29uc3QgeyBXb3JrZmxvd01hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3Rhc2tzL3dvcmtmbG93LW1hbmFnZXJcIik7XG4gICAgLy8gTk9URTogQHNvbmFtdS1raXQvdGFza3Mg7JWI7JeQ7ISgIGtuZXggY29uZmln66W8IOyImOygle2VmOq4sCDrlYzrrLjsl5AgY29ubmVjdGlvbuydtCDslYTri4wgY29uZmlnIOynuOuhnCDrs7Trg4Xri4jri6QuXG4gICAgdGhpcy5fd29ya2Zsb3dzID0gbmV3IFdvcmtmbG93TWFuYWdlcihEQi5nZXREQkNvbmZpZyhcIndcIikpO1xuICAgIGlmICghb3B0aW9ucykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGVuYWJsZVdvcmtlciA9IG9wdGlvbnMuZW5hYmxlV29ya2VyID8/IGlzRGFlbW9uU2VydmVyKCk7XG4gICAgY29uc3QgZGVmYXVsdFdvcmtlck9wdGlvbnMgPSB7XG4gICAgICBjb25jdXJyZW5jeTogb3MuY3B1cygpLmxlbmd0aCAtIDEsXG4gICAgICB1c2VQdWJTdWI6IHRydWUsXG4gICAgICBsaXN0ZW5EZWxheTogNTAwLFxuICAgIH07XG5cbiAgICBpZiAoZW5hYmxlV29ya2VyKSB7XG4gICAgICB0aGlzLndvcmtmbG93cy5zZXR1cFdvcmtlcih7XG4gICAgICAgIC4uLmRlZmF1bHRXb3JrZXJPcHRpb25zLFxuICAgICAgICAuLi5vcHRpb25zLndvcmtlck9wdGlvbnMsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGJvb3Qoc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2UsIG9wdGlvbnM6IFNvbmFtdVNlcnZlck9wdGlvbnMpIHtcbiAgICBjb25zdCBwb3J0ID0gb3B0aW9ucy5saXN0ZW4/LnBvcnQgPz8gMzAwMDtcbiAgICBjb25zdCBob3N0ID0gb3B0aW9ucy5saXN0ZW4/Lmhvc3QgPz8gXCJsb2NhbGhvc3RcIjtcblxuICAgIHNlcnZlci5hZGRIb29rKFwib25DbG9zZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCBvcHRpb25zLmxpZmVjeWNsZT8ub25TaHV0ZG93bj8uKHNlcnZlcik7XG4gICAgICBhd2FpdCB0aGlzLndvcmtmbG93cy5kZXN0cm95KCk7XG4gICAgICBhd2FpdCB0aGlzLmRlc3Ryb3koKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IHNodXRkb3duID0gYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgc2VydmVyLmNsb3NlKCk7XG4gICAgICAgIHByb2Nlc3MuZXhpdCgwKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3IgZHVyaW5nIHNodXRkb3duOlwiLCBlcnIpO1xuICAgICAgICBwcm9jZXNzLmV4aXQoMSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHByb2Nlc3Mub24oXCJTSUdJTlRcIiwgc2h1dGRvd24pO1xuICAgIHByb2Nlc3Mub24oXCJTSUdURVJNXCIsIHNodXRkb3duKTtcblxuICAgIGlmIChvcHRpb25zLmxpZmVjeWNsZT8ub25FcnJvcikge1xuICAgICAgc2VydmVyLnNldEVycm9ySGFuZGxlcihvcHRpb25zLmxpZmVjeWNsZT8ub25FcnJvcik7XG4gICAgfVxuXG4gICAgc2VydmVyXG4gICAgICAubGlzdGVuKHsgcG9ydCwgaG9zdCB9KVxuICAgICAgLnRoZW4oYXN5bmMgKCkgPT4ge1xuICAgICAgICBhd2FpdCB0aGlzLndvcmtmbG93cy5zdGFydFdvcmtlcigpO1xuICAgICAgICBhd2FpdCBvcHRpb25zLmxpZmVjeWNsZT8ub25TdGFydD8uKHNlcnZlcik7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKGFzeW5jIChlcnIpID0+IHtcbiAgICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoY2hhbGsucmVkKFwiRmFpbGVkIHRvIHN0YXJ0IHNlcnZlcjpcIiwgZXJyKSk7XG4gICAgICAgIGF3YWl0IHNodXRkb3duKCk7XG4gICAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlRmlsZUNoYW5nZShldmVudDogc3RyaW5nLCBmaWxlUGF0aDogQWJzb2x1dGVQYXRoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8g7LKrIOuyiOynuCDtjIzsnbzsnbTrqbQgSE1SIOyLnOyekSDsi5zqsIQg6riw66GdXG4gICAgaWYgKHRoaXMucGVuZGluZ0ZpbGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhpcy5obXJTdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIH1cbiAgICB0aGlzLnBlbmRpbmdGaWxlcy5wdXNoKGZpbGVQYXRoKTtcblxuICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHBhdGgucmVsYXRpdmUodGhpcy5hcGlSb290UGF0aCwgZmlsZVBhdGgpO1xuICAgIGNvbnN0IGNoYWxrID0gKGF3YWl0IGltcG9ydChcImNoYWxrXCIpKS5kZWZhdWx0O1xuICAgIGNvbnNvbGUubG9nKGNoYWxrLmJvbGQoYERldGVjdGVkKCR7ZXZlbnR9KTogJHtjaGFsay5ibHVlKHJlbGF0aXZlUGF0aCl9YCkpO1xuXG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuc3luY0Zyb21XYXRjaGVyKGV2ZW50LCBmaWxlUGF0aCk7XG5cbiAgICAvLyDsspjrpqwg7JmE66OM65CcIO2MjOydvOydhCDrjIDquLAg66qp66Gd7JeQ7IScIOygnOqxsFxuICAgIHRoaXMucGVuZGluZ0ZpbGVzID0gdGhpcy5wZW5kaW5nRmlsZXMuc2xpY2UoMSk7XG5cbiAgICAvLyDrqqjrk6Ag7YyM7J28IOyymOumrOqwgCDsmYTro4zrkJjrqbQg7LWc7KKFIOuplOyLnOyngCDstpzroKVcbiAgICBpZiAodGhpcy5wZW5kaW5nRmlsZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBhd2FpdCB0aGlzLmZpbmlzaEhNUigpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmluaXNoSE1SKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLnJlbmV3Q2hlY2tzdW1zKCk7XG5cbiAgICBjb25zdCBlbmRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCB0b3RhbFRpbWUgPSBlbmRUaW1lIC0gdGhpcy5obXJTdGFydFRpbWU7XG4gICAgY29uc3QgW2NoYWxrLCB7IGNlbnRlclRleHQgfV0gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQsXG4gICAgICBpbXBvcnQoXCIuLi91dGlscy9jb25zb2xlLXV0aWxcIiksXG4gICAgXSk7XG4gICAgY29uc3QgbXNnID0gYEhNUiBEb25lISAke2NoYWxrLmJvbGQud2hpdGUoYCR7dG90YWxUaW1lfW1zYCl9YDtcblxuICAgIGNvbnNvbGUubG9nKGNoYWxrLmJsYWNrLmJnR3JlZW4oY2VudGVyVGV4dChtc2cpKSk7XG4gIH1cblxuICBhc3luYyBkZXN0cm95KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgQmFzZU1vZGVsIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9kYXRhYmFzZS9iYXNlLW1vZGVsXCIpO1xuICAgIC8vIOuovOyggCDsspjrpqztlbTslbztlaguXG4gICAgYXdhaXQgQmFzZU1vZGVsLmRlc3Ryb3koKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW1xuICAgICAgdGhpcy5fd29ya2Zsb3dzPy5kZXN0cm95KCkgPz8gUHJvbWlzZS5yZXNvbHZlKCksXG4gICAgICB0aGlzLl9jYWNoZT8uZGlzY29ubmVjdCgpID8/IFByb21pc2UucmVzb2x2ZSgpLFxuICAgICAgdGhpcy5fZGV2Vml0ZXN0TWFuYWdlcj8uc2h1dGRvd24oKSA/PyBQcm9taXNlLnJlc29sdmUoKSxcbiAgICAgIHRoaXMud2F0Y2hlcj8uY2xvc2UoKSA/PyBQcm9taXNlLnJlc29sdmUoKSxcbiAgICAgIGxvZ3RhcGVEaXNwb3NlKCksXG4gICAgXSk7XG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IFNvbmFtdSA9IG5ldyBTb25hbXVDbGFzcygpO1xuXG4vKipcbiAqIHN0cmVhbSDrqqjrk5zsl5DshJwg7YKkIOyDneyEsSDtlajsiJjqsIAg7KeA7KCV65CY7KeAIOyViuyVmOydhCDrlYwg7IKs7Jqp7ZWY64qUIOq4sOuzuCDtlajsiJjsnoXri4jri6QuXG4gKi9cbmZ1bmN0aW9uIGRlZmF1bHRLZXlHZW5lcmF0b3IoZmlsZTogeyBmaWxlbmFtZTogc3RyaW5nOyBtaW1ldHlwZTogc3RyaW5nIH0pOiBzdHJpbmcge1xuICBjb25zdCBleHQgPSBtaW1lLmV4dGVuc2lvbihmaWxlLm1pbWV0eXBlKSB8fCBcImJpblwiO1xuICBjb25zdCB0aW1lc3RhbXAgPSBEYXRlLm5vdygpO1xuICBjb25zdCByYW5kb20gPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zbGljZSgyLCA4KTtcbiAgcmV0dXJuIGB1cGxvYWRzLyR7dGltZXN0YW1wfS0ke3JhbmRvbX0uJHtleHR9YDtcbn1cbiJdLCJuYW1lcyI6WyJkaXNwb3NlIiwibG9ndGFwZURpc3Bvc2UiLCJhc3NlcnQiLCJBc3luY0xvY2FsU3RvcmFnZSIsImZzIiwibWltZSIsImxvb2t1cCIsIm1pbWVMb29rdXAiLCJvcyIsInBhdGgiLCJCQVNFX0ZJRUxEX01BUFBJTkdTIiwiY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZCIsImNyZWF0ZU1vY2tTU0VGYWN0b3J5IiwiREIiLCJpc0RhZW1vblNlcnZlciIsIm1lcmdlIiwiTm90Rm91bmRFeGNlcHRpb24iLCJhcHBseUNhY2hlSGVhZGVycyIsIkNhY2hlUHJlc2V0cyIsInRvRmFzdGlmeUNvbXByZXNzT3B0aW9uIiwiU0QiLCJOYWl0ZSIsIkJ1ZmZlcmVkRmlsZSIsIlVwbG9hZGVkRmlsZSIsImV4aXN0cyIsImZpbGVFeGlzdHMiLCJnZXRTZWNyZXRzIiwiU29uYW11Q2xhc3MiLCJpc0luaXRpYWxpemVkIiwiZm9yVGVzdGluZyIsImFzeW5jTG9jYWxTdG9yYWdlIiwiZ2V0Q29udGV4dCIsInN0b3JlIiwiZ2V0U3RvcmUiLCJjb250ZXh0IiwicHJvY2VzcyIsImVudiIsIk5PREVfRU5WIiwicmVxdWVzdCIsInJlcGx5IiwiaGVhZGVycyIsImNyZWF0ZVNTRSIsInNjaGVtYSIsIm5haXRlU3RvcmUiLCJNYXAiLCJFcnJvciIsIl9hcGlSb290UGF0aCIsImFwaVJvb3RQYXRoIiwiYXBwUm9vdFBhdGgiLCJzcGxpdCIsInNlcCIsInNsaWNlIiwiam9pbiIsIl9kYkNvbmZpZyIsImRiQ29uZmlnIiwiX3N5bmNlciIsInN5bmNlciIsIl9jb25maWciLCJjb25maWciLCJzZWNyZXRzIiwiX3N0b3JhZ2UiLCJzdG9yYWdlIiwiX2NhY2hlIiwiY2FjaGUiLCJfd29ya2Zsb3dzIiwid29ya2Zsb3dzIiwiX2F1dGgiLCJhdXRoIiwiX2RldlZpdGVzdE1hbmFnZXIiLCJkZXZWaXRlc3RNYW5hZ2VyIiwibWFuYWdlciIsIndhdGNoZXIiLCJwZW5kaW5nRmlsZXMiLCJobXJTdGFydFRpbWUiLCJzZXJ2ZXIiLCJpbml0Rm9yVGVzdGluZyIsImluaXQiLCJ1bmRlZmluZWQiLCJkb1NpbGVudCIsImVuYWJsZVN5bmMiLCJjaGFsayIsImRlZmF1bHQiLCJjb25zb2xlIiwidGltZSIsImN5YW4iLCJmaW5kQXBpUm9vdFBhdGgiLCJsb2FkQ29uZmlnIiwiZGF0YWJhc2UiLCJkZWZhdWx0T3B0aW9ucyIsImNsaWVudCIsImNvbmZpZ3VyZUxvZ1RhcGUiLCJsb2dnaW5nIiwiZ2VuZXJhdGVEQkNvbmZpZyIsImxvZyIsImdyZWVuIiwiRW50aXR5TWFuYWdlciIsImF1dG9sb2FkIiwiaW5pdGlhbGl6ZUNhY2hlIiwiYXV0aENvbmZpZyIsIm1lcmdlZEZpZWxkTWFwcGluZ3MiLCJiZXR0ZXJBdXRoIiwic29uYW11S25leEFkYXB0ZXIiLCJpbml0aWFsaXplV29ya2Zsb3dzIiwidGFza3MiLCJTeW5jZXIiLCJhdXRvbG9hZFR5cGVzIiwiYXV0b2xvYWRNb2RlbHMiLCJhdXRvbG9hZEFwaXMiLCJhdXRvbG9hZFdvcmtmbG93cyIsIlRlbXBsYXRlTWFuYWdlciIsImF1dG9sb2FkU1NSUm91dGVzIiwiaXNMb2NhbCIsImlzVGVzdCIsInNldHVwQmlvbWUiLCJpc0hvdFJlbG9hZFNlcnZlciIsInN5bmMiLCJzdGFydFdhdGNoZXIiLCJ0aW1lRW5kIiwiY3JlYXRlU2VydmVyIiwiaW5pdE9wdGlvbnMiLCJvcHRpb25zIiwiZmFzdGlmeSIsImdldExvZ1RhcGVGYXN0aWZ5TG9nZ2VyIiwibG9nZ2VyIiwiY2F0ZWdvcnkiLCJmYXN0aWZ5Q2F0ZWdvcnkiLCJTdG9yYWdlTWFuYWdlciIsInBsdWdpbnMiLCJyZWdpc3RlclBsdWdpbnMiLCJyZWdpc3RlckJldHRlckF1dGgiLCJ3aXRoRmFzdGlmeSIsImFwaUNvbmZpZyIsImJvb3QiLCJ0aW1lem9uZSIsImFwaSIsImZvcm1hdEluVGltZVpvbmUiLCJJU09fREFURV9SRUdFWCIsIkRBVEVfRk9STUFUIiwic2V0UmVwbHlTZXJpYWxpemVyIiwicGF5bG9hZCIsIkpTT04iLCJzdHJpbmdpZnkiLCJfa2V5IiwidmFsdWUiLCJ0ZXN0IiwiRGF0ZSIsImdldCIsInJvdXRlIiwicHJlZml4IiwiX3JlcXVlc3QiLCJfcmVwbHkiLCJhcGlzIiwic29uYW11VUlBcGlQbHVnaW4iLCJyZWdpc3RlciIsImRldlJ1bm5lciIsImVuYWJsZWQiLCJyZWdpc3RlckRldlRlc3RSb3V0ZXMiLCJ3ZWJQYXRoIiwiaGFzV2ViIiwicGx1Z2luQ29tcHJlc3MiLCJjb21wcmVzcyIsImdsb2JhbENvbXByZXNzT3B0aW9ucyIsInRocmVzaG9sZCIsImVuY29kaW5ncyIsImN1c3RvbVR5cGVzIiwiZGlzYWJsZUludGVncmF0ZWRXZWIiLCJTT05BTVVfRElTQUJMRV9JTlRFR1JBVEVEX1dFQiIsInNldHVwRGV2U2VydmVyV2l0aFZpdGUiLCJzZXR1cERldlNlcnZlciIsIm1vZGVscyIsIm1vZGVsTmFtZSIsIm1ldGhvZCIsImh0dHBNZXRob2QiLCJ1cmwiLCJoYW5kbGVyIiwiY3JlYXRlQXBpSGFuZGxlciIsInNldHVwU3RhdGljV2ViU2VydmVyIiwiaGFuZGxlRGV2QXBpUmVxdWVzdCIsImdldFBhdGhuYW1lRnJvbVVybCIsInN0YXJ0c1dpdGgiLCJtYXRjaGVkQXBpIiwiZmluZCIsImFwaU1ldGhvZCIsImZ1bGxQYXRoIiwiaXNQYXRoUGF0dGVybk1hdGNoIiwidml0ZVNlcnZlciIsInZpdGUiLCJyb290IiwibWlkZGxld2FyZU1vZGUiLCJobXIiLCJhcHBUeXBlIiwidXNlIiwicmVxIiwicmVzIiwibmV4dCIsIm1pZGRsZXdhcmVzIiwicmVzdWx0IiwibWF0Y2hTU1JSb3V0ZSIsInJlbmRlclNTUiIsInNzck1hdGNoIiwiaHRtbCIsInBhcmFtcyIsInR5cGUiLCJ0ZW1wbGF0ZSIsInJlYWRGaWxlIiwidHJhbnNmb3JtSW5kZXhIdG1sIiwiZSIsInNzckZpeFN0YWNrdHJhY2UiLCJlcnJvciIsInN0YXR1cyIsIm1lc3NhZ2UiLCJhZGRIb29rIiwiY2xvc2UiLCJ3ZWJEaXN0UGF0aCIsInNzclBhdGgiLCJzc3JFbnRyeVBhdGgiLCJzc3JSb3V0ZXNQYXRoIiwid2FybiIsInNzckF2YWlsYWJsZSIsInJlcXVlc3RlZEZpbGUiLCJmaWxlbmFtZSIsImFzc2V0c0RpciIsInNhZmVGaWxlUGF0aCIsInJlc29sdmVQYXRoV2l0aGluQmFzZURpciIsInNlbmQiLCJub3JtYWxpemVkUmVxdWVzdGVkRmlsZSIsInJlbGF0aXZlIiwicmVwbGFjZSIsImFzc2V0UGF0aCIsImdldENhY2hlQ29udHJvbEZvckFzc2V0IiwiY2FjaGVSZXEiLCJjYWNoZUNvbnRyb2xIYW5kbGVyIiwiaW1tdXRhYmxlIiwiZXh0IiwicG9wIiwiZmlsZXMiLCJyZWFkZGlyIiwiY3VycmVudEZpbGUiLCJmIiwiZW5kc1dpdGgiLCJmaWxlUGF0aCIsImNvbnRlbnQiLCJpbmNsdWRlcyIsImdldFNTUlJvdXRlcyIsInNzclJvdXRlcyIsImV4dHJhY3RQYXRoUGFyYW1zIiwiY3NyQ2FjaGVSZXEiLCJjc3JDYWNoZUNvbmZpZyIsInJlcXVlc3RQYXRoIiwiaW5kZXhQYXRoIiwiY3JlYXRlQ29udGV4dCIsInJ1biIsImd1YXJkcyIsImV2ZXJ5IiwiZ3VhcmQiLCJndWFyZEhhbmRsZXIiLCJnZXRab2RPYmplY3RGcm9tQXBpIiwiUmVxVHlwZSIsInR5cGVzIiwid2hpY2giLCJyZXFCb2R5IiwiYnVmZmVyZWRGaWxlcyIsInVwbG9hZGVkRmlsZXMiLCJib2R5IiwidXBsb2FkT3B0aW9ucyIsInBhcnRzIiwibGltaXRzIiwiZmllbGRzIiwiY29uc3VtZSIsInBhcnQiLCJidWZmZXIiLCJ0b0J1ZmZlciIsInB1c2giLCJmaWVsZG5hbWUiLCJTdHJpbmciLCJkaXNrTmFtZSIsImRlc3RpbmF0aW9uIiwiZGlzayIsImtleUdlbmVyYXRvciIsImRlZmF1bHRLZXlHZW5lcmF0b3IiLCJrZXkiLCJtaW1ldHlwZSIsInB1dFN0cmVhbSIsImZpbGUiLCJjb250ZW50VHlwZSIsImdldFVybCIsInNpZ25lZFVybCIsImdldFNpZ25lZFVybCIsInNpemUiLCJieXRlc1JlYWQiLCJxcyIsInBhcnNlZCIsInBhcnNlIiwiT2JqZWN0IiwiYXNzaWduIiwiZmFzdGlmeUNhc3RlciIsIlpvZEVycm9yIiwiaHVtYW5pemVab2RFcnJvciIsIm1lc3NhZ2VzIiwibWFwIiwiaXNzdWUiLCJCYWRSZXF1ZXN0RXhjZXB0aW9uIiwiem9kRXJyb3IiLCJhcGlDYWNoZUNvbmZpZyIsImdldEFwaUNhY2hlQ29udHJvbCIsIkFwaVBhcmFtVHlwZSIsImFyZ3MiLCJwYXJhbWV0ZXJzIiwicGFyYW0iLCJpc0NvbnRleHQiLCJuYW1lIiwiaW52b2tlTW9kZWxNZXRob2QiLCJwYXR0ZXJuIiwicGF0dGVyblBhcnRzIiwiZmlsdGVyIiwiQm9vbGVhbiIsInVybFBhcnRzIiwiaSIsImxlbmd0aCIsInBhdHRlcm5QYXJ0IiwidXJsUGFydCIsImJhc2VEaXIiLCJpbnB1dFBhdGgiLCJkZWNvZGVkIiwiZGVjb2RlVVJJQ29tcG9uZW50IiwicmVsYXRpdmVQYXRoIiwicmVzb2x2ZWRQYXRoIiwicmVzb2x2ZSIsInJlbGF0aXZlRnJvbUJhc2UiLCJpc0Fic29sdXRlIiwiY2FjaGVDb250cm9sIiwicm91dGVPcHRpb25zIiwiaW52b2tlQXBpRm9yU1NSIiwicGFyYW1zSW5kZXgiLCJtb2RlbCIsIm1ldGhvZE5hbWUiLCJhcHBseSIsImNyZWF0ZVNTRUZhY3RvcnkiLCJfZXZlbnRzIiwic29ja2V0IiwiYmluZCIsImxvY2FsZSIsImRldGVjdExvY2FsZSIsImkxOG4iLCJzdXBwb3J0ZWRMb2NhbGVzIiwiZGVmYXVsdExvY2FsZSIsInNlc3Npb24iLCJnZXRTZXNzaW9uIiwiUHJvbWlzZSIsImNvbnRleHRQcm92aWRlciIsImNyZWF0ZVN0b3JlIiwidXNlciIsImFjY2VwdExhbmd1YWdlIiwic3VwcG9ydGVkIiwibGFuZ3MiLCJsYW5nIiwiY29kZSIsInRyaW0iLCJ3YXRjaFBhdGgiLCJjaG9raWRhciIsIndhdGNoIiwiaWdub3JlZCIsInN0YXRzIiwiaXNGaWxlIiwicGVyc2lzdGVudCIsImlnbm9yZUluaXRpYWwiLCJvbiIsImV2ZW50IiwiYWJzb2x1dGVQYXRoIiwiaXNDb25maWdUcyIsImJvbGQiLCJibHVlIiwia2lsbCIsInBpZCIsImhhbmRsZUZpbGVDaGFuZ2UiLCJydW5TY3JpcHQiLCJmbiIsImRlc3Ryb3kiLCJjb21wcmVzc1BsdWdpbiIsInBsdWdpbnNNb2R1bGVzIiwiY29ycyIsImZvcm1ib2R5IiwibXVsdGlwYXJ0Iiwic3NlIiwic3RhdGljIiwicmVnaXN0ZXJQbHVnaW4iLCJwbHVnaW5OYW1lIiwib3B0aW9uIiwiZW50cmllcyIsImN1c3RvbSIsImJhc2VQYXRoIiwiVVJMIiwiaG9zdCIsIlJlcXVlc3QiLCJ0b1N0cmluZyIsInJlc3BvbnNlIiwiZm9yRWFjaCIsImhlYWRlciIsInRleHQiLCJzZXRDYWNoZU1hbmFnZXJSZWYiLCJjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyIiwiY3JlYXRlQ2FjaGVNYW5hZ2VyIiwiV29ya2Zsb3dNYW5hZ2VyIiwiZ2V0REJDb25maWciLCJlbmFibGVXb3JrZXIiLCJkZWZhdWx0V29ya2VyT3B0aW9ucyIsImNvbmN1cnJlbmN5IiwiY3B1cyIsInVzZVB1YlN1YiIsImxpc3RlbkRlbGF5Iiwic2V0dXBXb3JrZXIiLCJ3b3JrZXJPcHRpb25zIiwicG9ydCIsImxpc3RlbiIsImxpZmVjeWNsZSIsIm9uU2h1dGRvd24iLCJzaHV0ZG93biIsImV4aXQiLCJlcnIiLCJvbkVycm9yIiwic2V0RXJyb3JIYW5kbGVyIiwidGhlbiIsInN0YXJ0V29ya2VyIiwib25TdGFydCIsImNhdGNoIiwicmVkIiwibm93Iiwic3luY0Zyb21XYXRjaGVyIiwiZmluaXNoSE1SIiwicmVuZXdDaGVja3N1bXMiLCJlbmRUaW1lIiwidG90YWxUaW1lIiwiY2VudGVyVGV4dCIsImFsbCIsIm1zZyIsIndoaXRlIiwiYmxhY2siLCJiZ0dyZWVuIiwiQmFzZU1vZGVsIiwiYWxsU2V0dGxlZCIsImRpc2Nvbm5lY3QiLCJTb25hbXUiLCJleHRlbnNpb24iLCJ0aW1lc3RhbXAiLCJyYW5kb20iLCJNYXRoIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxXQUFXQyxjQUFjLFFBQVEsbUJBQW1CO0FBQzdELE9BQU9DLFlBQVksU0FBUztBQUM1QixTQUFTQyxpQkFBaUIsUUFBUSxjQUFjO0FBSWhELE9BQU9DLFFBQVEsbUJBQWM7QUFFN0IsT0FBT0MsUUFBUUMsVUFBVUMsVUFBVSxRQUFRLGFBQWE7QUFDeEQsT0FBT0MsUUFBUSxLQUFLO0FBQ3BCLE9BQU9DLFVBQVUsT0FBTztB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
1014
+ //#endregion
1015
+ init_sonamu();
1016
+ export { Sonamu, init_sonamu };
1017
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic29uYW11LmpzIiwibmFtZXMiOlsiREIiLCJnbG9iYWxDb21wcmVzc09wdGlvbnM6IENvbXByZXNzT3B0aW9ucyB8IHVuZGVmaW5lZCIsImZzIiwiY2FjaGVSZXE6IENhY2hlQ29udHJvbFJlcXVlc3QiLCJmaWxlUGF0aCIsImNzckNhY2hlUmVxOiBDYWNoZUNvbnRyb2xSZXF1ZXN0IiwibWltZUxvb2t1cCIsImNvbnRleHQ6IENvbnRleHQiLCJyZXFCb2R5OiB7XG4gICAgICAgICAgW2tleTogc3RyaW5nXTogdW5rbm93bjtcbiAgICAgICAgfSIsImZpbGVzOiB7XG4gICAgICAgICAgYnVmZmVyZWRGaWxlczogQnVmZmVyZWRGaWxlW107XG4gICAgICAgICAgdXBsb2FkZWRGaWxlczogVXBsb2FkZWRGaWxlW107XG4gICAgICAgIH0iLCJmaWVsZHM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4iLCJrZXlHZW5lcmF0b3I6IEtleUdlbmVyYXRvciIsInBhcmFtczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiIsInBhdGgiLCJsb2d0YXBlRGlzcG9zZSJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvc29uYW11LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBhc3NlcnQgZnJvbSBcImFzc2VydFwiO1xuaW1wb3J0IHsgQXN5bmNMb2NhbFN0b3JhZ2UgfSBmcm9tIFwiYXN5bmNfaG9va3NcIjtcbmltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCB7IHR5cGUgSW5jb21pbmdNZXNzYWdlLCB0eXBlIFNlcnZlciwgdHlwZSBTZXJ2ZXJSZXNwb25zZSB9IGZyb20gXCJodHRwXCI7XG5pbXBvcnQgb3MgZnJvbSBcIm9zXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuXG5pbXBvcnQgeyBkaXNwb3NlIGFzIGxvZ3RhcGVEaXNwb3NlIH0gZnJvbSBcIkBsb2d0YXBlL2xvZ3RhcGVcIjtcbmltcG9ydCB7IHR5cGUgQXV0aCB9IGZyb20gXCJiZXR0ZXItYXV0aFwiO1xuaW1wb3J0IHsgdHlwZSBGU1dhdGNoZXIgfSBmcm9tIFwiY2hva2lkYXJcIjtcbmltcG9ydCB7IHR5cGUgRmFzdGlmeUluc3RhbmNlLCB0eXBlIEZhc3RpZnlSZXBseSwgdHlwZSBGYXN0aWZ5UmVxdWVzdCB9IGZyb20gXCJmYXN0aWZ5XCI7XG5pbXBvcnQgbWltZSwgeyBsb29rdXAgYXMgbWltZUxvb2t1cCB9IGZyb20gXCJtaW1lLXR5cGVzXCI7XG5pbXBvcnQgeyB0eXBlIFpvZE9iamVjdCB9IGZyb20gXCJ6b2RcIjtcblxuaW1wb3J0IHsgQkFTRV9GSUVMRF9NQVBQSU5HUyB9IGZyb20gXCIuLi9hdXRoL2JldHRlci1hdXRoLWVudGl0aWVzXCI7XG5pbXBvcnQgeyBhcHBseUNhY2hlSGVhZGVycywgQ2FjaGVQcmVzZXRzIH0gZnJvbSBcIi4uL2NhY2hlLWNvbnRyb2wvY2FjaGUtY29udHJvbFwiO1xuaW1wb3J0IHsgdHlwZSBDYWNoZUNvbnRyb2xDb25maWcsIHR5cGUgQ2FjaGVDb250cm9sUmVxdWVzdCB9IGZyb20gXCIuLi9jYWNoZS1jb250cm9sL3R5cGVzXCI7XG5pbXBvcnQgeyB0eXBlIENhY2hlQ29uZmlnLCB0eXBlIENhY2hlTWFuYWdlciB9IGZyb20gXCIuLi9jYWNoZS90eXBlc1wiO1xuaW1wb3J0IHsgdG9GYXN0aWZ5Q29tcHJlc3NPcHRpb24gfSBmcm9tIFwiLi4vY29tcHJlc3MvY29tcHJlc3NcIjtcbmltcG9ydCB7IHR5cGUgQ29tcHJlc3NPcHRpb25zIH0gZnJvbSBcIi4uL2NvbXByZXNzL3R5cGVzXCI7XG5pbXBvcnQgeyBEQiB9IGZyb20gXCIuLi9kYXRhYmFzZS9kYlwiO1xuaW1wb3J0IHsgdHlwZSBTb25hbXVEQkNvbmZpZyB9IGZyb20gXCIuLi9kYXRhYmFzZS9kYlwiO1xuaW1wb3J0IHsgU0QsIHNldFNEQ29uZmlnIH0gZnJvbSBcIi4uL2RpY3Qvc2RcIjtcbmltcG9ydCB7IHR5cGUgTG9jYWxpemVkU3RyaW5nIH0gZnJvbSBcIi4uL2RpY3QvdHlwZXNcIjtcbmltcG9ydCB7IE5vdEZvdW5kRXhjZXB0aW9uIH0gZnJvbSBcIi4uL2V4Y2VwdGlvbnMvc28tZXhjZXB0aW9uc1wiO1xuaW1wb3J0IHsgQnVmZmVyZWRGaWxlIH0gZnJvbSBcIi4uL3N0b3JhZ2UvYnVmZmVyZWQtZmlsZVwiO1xuaW1wb3J0IHsgdHlwZSBTdG9yYWdlTWFuYWdlciB9IGZyb20gXCIuLi9zdG9yYWdlL3N0b3JhZ2UtbWFuYWdlclwiO1xuaW1wb3J0IHsgdHlwZSBLZXlHZW5lcmF0b3IgfSBmcm9tIFwiLi4vc3RvcmFnZS90eXBlc1wiO1xuaW1wb3J0IHsgVXBsb2FkZWRGaWxlIH0gZnJvbSBcIi4uL3N0b3JhZ2UvdXBsb2FkZWQtZmlsZVwiO1xuaW1wb3J0IHsgY3JlYXRlTW9ja1NTRUZhY3RvcnkgfSBmcm9tIFwiLi4vc3RyZWFtL3NzZVwiO1xuaW1wb3J0IHsgdHlwZSBTeW5jZXIgfSBmcm9tIFwiLi4vc3luY2VyL3N5bmNlclwiO1xuaW1wb3J0IHsgdHlwZSBXb3JrZmxvd01hbmFnZXIgfSBmcm9tIFwiLi4vdGFza3Mvd29ya2Zsb3ctbWFuYWdlclwiO1xuaW1wb3J0IHsgdHlwZSBEZXZWaXRlc3RNYW5hZ2VyIH0gZnJvbSBcIi4uL3Rlc3RpbmcvZGV2LXZpdGVzdC1tYW5hZ2VyXCI7XG5pbXBvcnQgeyB0eXBlIFNvbmFtdUZhc3RpZnlDb25maWcgfSBmcm9tIFwiLi4vdHlwZXMvdHlwZXNcIjtcbmltcG9ydCB7IGlzRGFlbW9uU2VydmVyIH0gZnJvbSBcIi4uL3V0aWxzL2NvbnRyb2xsZXJcIjtcbmltcG9ydCB7IGV4aXN0cywgZmlsZUV4aXN0cyB9IGZyb20gXCIuLi91dGlscy9mcy11dGlsc1wiO1xuaW1wb3J0IHsgdHlwZSBBYnNvbHV0ZVBhdGggfSBmcm9tIFwiLi4vdXRpbHMvcGF0aC11dGlsc1wiO1xuaW1wb3J0IHsgY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZCwgbWVyZ2UgfSBmcm9tIFwiLi4vdXRpbHMvdXRpbHNcIjtcbmltcG9ydCB7IHR5cGUgU29uYW11Q29uZmlnLCB0eXBlIFNvbmFtdVNlcnZlck9wdGlvbnMsIHR5cGUgU29uYW11VGFza09wdGlvbnMgfSBmcm9tIFwiLi9jb25maWdcIjtcbmltcG9ydCB7IHR5cGUgQ29udGV4dCB9IGZyb20gXCIuL2NvbnRleHRcIjtcbmltcG9ydCB7IHR5cGUgRXh0ZW5kZWRBcGkgfSBmcm9tIFwiLi9kZWNvcmF0b3JzXCI7XG5pbXBvcnQgeyBnZXRTZWNyZXRzIH0gZnJvbSBcIi4vc2VjcmV0XCI7XG5pbXBvcnQgeyB0eXBlIFNvbmFtdVNlY3JldHMgfSBmcm9tIFwiLi9zZWNyZXRcIjtcblxuY2xhc3MgU29uYW11Q2xhc3Mge1xuICBwdWJsaWMgaXNJbml0aWFsaXplZDogYm9vbGVhbiA9IGZhbHNlO1xuICBwdWJsaWMgZm9yVGVzdGluZzogYm9vbGVhbiA9IGZhbHNlO1xuICBwdWJsaWMgYXN5bmNMb2NhbFN0b3JhZ2U6IEFzeW5jTG9jYWxTdG9yYWdlPHtcbiAgICBjb250ZXh0OiBDb250ZXh0O1xuICB9PiA9IG5ldyBBc3luY0xvY2FsU3RvcmFnZSgpO1xuXG4gIHB1YmxpYyBnZXRDb250ZXh0KCk6IENvbnRleHQge1xuICAgIGNvbnN0IHN0b3JlID0gdGhpcy5hc3luY0xvY2FsU3RvcmFnZS5nZXRTdG9yZSgpO1xuICAgIGlmIChzdG9yZT8uY29udGV4dCkge1xuICAgICAgcmV0dXJuIHN0b3JlLmNvbnRleHQ7XG4gICAgfVxuXG4gICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WID09PSBcInRlc3RcIikge1xuICAgICAgLy8g7YWM7Iqk7YyFIO2ZmOqyveyXkOyEnCDsu6jthY3siqTtirjqsIAg7KO87J6F65CY7KeAIOyViuydgCDqsr3smrAg67mIIOy7qO2FjeyKpO2KuCDrpqzthLRcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHJlcXVlc3Q6IG51bGwsXG4gICAgICAgIHJlcGx5OiBudWxsLFxuICAgICAgICBoZWFkZXJzOiB7fSxcbiAgICAgICAgY3JlYXRlU1NFOiAoc2NoZW1hOiBab2RPYmplY3QpID0+IGNyZWF0ZU1vY2tTU0VGYWN0b3J5KHNjaGVtYSksXG4gICAgICAgIC8vIG94bGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55IC0tIO2FjOyKpO2MhSDtmZjqsr3sl5DshJwg7Luo7YWN7Iqk7Yq46rCAIOyjvOyeheuQmOyngCDslYrsnYAg6rK97JqwIOu5iCDsu6jthY3siqTtirgg66as7YS0XG4gICAgICAgIG5haXRlU3RvcmU6IG5ldyBNYXA8c3RyaW5nLCBhbnk+KCksXG4gICAgICB9IGFzIHVua25vd24gYXMgQ29udGV4dDtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGNhbm5vdCBmaW5kIGNvbnRleHRcIik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBfYXBpUm9vdFBhdGg6IEFic29sdXRlUGF0aCB8IG51bGwgPSBudWxsO1xuICBzZXQgYXBpUm9vdFBhdGgoYXBpUm9vdFBhdGg6IEFic29sdXRlUGF0aCkge1xuICAgIHRoaXMuX2FwaVJvb3RQYXRoID0gYXBpUm9vdFBhdGg7XG4gIH1cbiAgZ2V0IGFwaVJvb3RQYXRoKCk6IEFic29sdXRlUGF0aCB7XG4gICAgaWYgKHRoaXMuX2FwaVJvb3RQYXRoID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJTb25hbXUgaGFzIG5vdCBiZWVuIGluaXRpYWxpemVkXCIpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fYXBpUm9vdFBhdGg7XG4gIH1cbiAgZ2V0IGFwcFJvb3RQYXRoKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuYXBpUm9vdFBhdGguc3BsaXQocGF0aC5zZXApLnNsaWNlKDAsIC0xKS5qb2luKHBhdGguc2VwKTtcbiAgfVxuXG4gIHByaXZhdGUgX2RiQ29uZmlnOiBTb25hbXVEQkNvbmZpZyB8IG51bGwgPSBudWxsO1xuICBzZXQgZGJDb25maWcoZGJDb25maWc6IFNvbmFtdURCQ29uZmlnKSB7XG4gICAgdGhpcy5fZGJDb25maWcgPSBkYkNvbmZpZztcbiAgfVxuICBnZXQgZGJDb25maWcoKTogU29uYW11REJDb25maWcge1xuICAgIGlmICh0aGlzLl9kYkNvbmZpZyA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGhhcyBub3QgYmVlbiBpbml0aWFsaXplZFwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2RiQ29uZmlnO1xuICB9XG5cbiAgcHJpdmF0ZSBfc3luY2VyOiBTeW5jZXIgfCBudWxsID0gbnVsbDtcbiAgc2V0IHN5bmNlcihzeW5jZXI6IFN5bmNlcikge1xuICAgIHRoaXMuX3N5bmNlciA9IHN5bmNlcjtcbiAgfVxuICBnZXQgc3luY2VyKCk6IFN5bmNlciB7XG4gICAgaWYgKHRoaXMuX3N5bmNlciA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGhhcyBub3QgYmVlbiBpbml0aWFsaXplZFwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX3N5bmNlcjtcbiAgfVxuXG4gIHByaXZhdGUgX2NvbmZpZzogU29uYW11Q29uZmlnIHwgbnVsbCA9IG51bGw7XG4gIHNldCBjb25maWcoY29uZmlnOiBTb25hbXVDb25maWcpIHtcbiAgICB0aGlzLl9jb25maWcgPSBjb25maWc7XG4gIH1cbiAgZ2V0IGNvbmZpZygpOiBTb25hbXVDb25maWcge1xuICAgIGlmICh0aGlzLl9jb25maWcgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlNvbmFtdSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9jb25maWc7XG4gIH1cblxuICBwdWJsaWMgcmVhZG9ubHkgc2VjcmV0czogU29uYW11U2VjcmV0cyA9IGdldFNlY3JldHMoKTtcblxuICBwcml2YXRlIF9zdG9yYWdlOiBTdG9yYWdlTWFuYWdlciB8IG51bGwgPSBudWxsO1xuICAvKipcbiAgICogU3RvcmFnZU1hbmFnZXIg7J247Iqk7YS07IqkXG4gICAqL1xuICBnZXQgc3RvcmFnZSgpOiBTdG9yYWdlTWFuYWdlciB7XG4gICAgaWYgKCF0aGlzLl9zdG9yYWdlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJTdG9yYWdlIGhhcyBub3QgYmVlbiBpbml0aWFsaXplZC4gQ2hlY2sgc3RvcmFnZSBjb25maWcuXCIpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fc3RvcmFnZTtcbiAgfVxuXG4gIHByaXZhdGUgX2NhY2hlOiBDYWNoZU1hbmFnZXIgfCBudWxsID0gbnVsbDtcbiAgLyoqXG4gICAqIENhY2hlTWFuYWdlciDsnbjsiqTthLTsiqQgKEJlbnRvQ2FjaGUpXG4gICAqL1xuICBnZXQgY2FjaGUoKTogQ2FjaGVNYW5hZ2VyIHtcbiAgICBpZiAoIXRoaXMuX2NhY2hlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDYWNoZSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWQuIENoZWNrIGNhY2hlIGNvbmZpZyBpbiBzb25hbXUuY29uZmlnLnRzLlwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2NhY2hlO1xuICB9XG5cbiAgcHJpdmF0ZSBfd29ya2Zsb3dzOiBXb3JrZmxvd01hbmFnZXIgfCBudWxsID0gbnVsbDtcbiAgZ2V0IHdvcmtmbG93cygpOiBXb3JrZmxvd01hbmFnZXIge1xuICAgIGlmICh0aGlzLl93b3JrZmxvd3MgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlNvbmFtdSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuX3dvcmtmbG93cztcbiAgfVxuXG4gIHByaXZhdGUgX2F1dGg6IEF1dGggfCBudWxsID0gbnVsbDtcbiAgZ2V0IGF1dGgoKTogQXV0aCB7XG4gICAgaWYgKCF0aGlzLl9hdXRoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJBdXRoIGhhcyBub3QgYmVlbiBpbml0aWFsaXplZC4gQ2hlY2sgYXV0aCBjb25maWcgaW4gc29uYW11LmNvbmZpZy50cy5cIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9hdXRoO1xuICB9XG5cbiAgcHJpdmF0ZSBfZGV2Vml0ZXN0TWFuYWdlcjogRGV2Vml0ZXN0TWFuYWdlciB8IG51bGwgPSBudWxsO1xuICBnZXQgZGV2Vml0ZXN0TWFuYWdlcigpOiBEZXZWaXRlc3RNYW5hZ2VyIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuX2RldlZpdGVzdE1hbmFnZXI7XG4gIH1cbiAgc2V0IGRldlZpdGVzdE1hbmFnZXIobWFuYWdlcjogRGV2Vml0ZXN0TWFuYWdlciB8IG51bGwpIHtcbiAgICB0aGlzLl9kZXZWaXRlc3RNYW5hZ2VyID0gbWFuYWdlcjtcbiAgfVxuXG4gIC8vIEhNUiDsspjrpqxcbiAgcHVibGljIHdhdGNoZXI6IEZTV2F0Y2hlciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHBlbmRpbmdGaWxlczogc3RyaW5nW10gPSBbXTtcbiAgcHJpdmF0ZSBobXJTdGFydFRpbWU6IG51bWJlciA9IDA7XG5cbiAgcHVibGljIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlIHwgbnVsbCA9IG51bGw7XG5cbiAgYXN5bmMgaW5pdEZvclRlc3RpbmcoKSB7XG4gICAgYXdhaXQgdGhpcy5pbml0KHRydWUsIGZhbHNlLCB1bmRlZmluZWQsIHRydWUpO1xuICB9XG5cbiAgYXN5bmMgaW5pdChcbiAgICBkb1NpbGVudDogYm9vbGVhbiA9IGZhbHNlLFxuICAgIGVuYWJsZVN5bmM6IGJvb2xlYW4gPSB0cnVlLFxuICAgIGFwaVJvb3RQYXRoPzogQWJzb2x1dGVQYXRoLFxuICAgIGZvclRlc3Rpbmc6IGJvb2xlYW4gPSBmYWxzZSxcbiAgKSB7XG4gICAgdGhpcy5mb3JUZXN0aW5nID0gZm9yVGVzdGluZztcblxuICAgIGlmICh0aGlzLmlzSW5pdGlhbGl6ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoIWRvU2lsZW50KSB7XG4gICAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICAgIGNvbnNvbGUudGltZShjaGFsay5jeWFuKGBTb25hbXUuaW5pdCR7Zm9yVGVzdGluZyA/IFwiIGZvciB0ZXN0aW5nXCIgOiBcIlwifWApKTtcbiAgICB9XG5cbiAgICAvLyBBUEkg66Oo7Yq4IO2MqOyKpFxuICAgIGNvbnN0IHsgZmluZEFwaVJvb3RQYXRoIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy91dGlsc1wiKTtcbiAgICB0aGlzLmFwaVJvb3RQYXRoID0gYXBpUm9vdFBhdGggPz8gZmluZEFwaVJvb3RQYXRoKCk7XG5cbiAgICAvLyDshKTsoJXsnYQg66Gc65Sp7ZWY64qUIOqyg+u2gO2EsCDsi5zsnpFcbiAgICBjb25zdCB7IGxvYWRDb25maWcgfSA9IGF3YWl0IGltcG9ydChcIi4vY29uZmlnXCIpO1xuICAgIHRoaXMuY29uZmlnID0gYXdhaXQgbG9hZENvbmZpZyh0aGlzLmFwaVJvb3RQYXRoKTtcbiAgICBzZXRTRENvbmZpZyh0aGlzLmNvbmZpZy5pMThuKTtcbiAgICAvLyBzb25hbXUuY29uZmlnLnRzIOq4sOuzuOqwkiDshKTsoJVcbiAgICB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kYXRhYmFzZSA9IHRoaXMuY29uZmlnLmRhdGFiYXNlLmRhdGFiYXNlID8/IFwicGdcIjtcbiAgICB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kZWZhdWx0T3B0aW9ucy5jbGllbnQgPSB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kYXRhYmFzZSA/PyBcInBnXCI7XG5cbiAgICAvLyDroZzquYUg7ISk7KCVXG4gICAgY29uc3QgeyBjb25maWd1cmVMb2dUYXBlIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9sb2dnZXIvY29uZmlndXJlXCIpO1xuICAgIGlmICh0aGlzLmNvbmZpZy5sb2dnaW5nICE9PSBmYWxzZSkge1xuICAgICAgYXdhaXQgY29uZmlndXJlTG9nVGFwZSh7XG4gICAgICAgIC4uLnRoaXMuY29uZmlnLmxvZ2dpbmcsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBEQiDroZzrk5xcbiAgICBjb25zdCB7IERCIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9kYXRhYmFzZS9kYlwiKTtcbiAgICB0aGlzLmRiQ29uZmlnID0gREIuZ2VuZXJhdGVEQkNvbmZpZyh0aGlzLmNvbmZpZy5kYXRhYmFzZSk7XG4gICAgREIuc2V0Q29uZmlnKHRoaXMuZGJDb25maWcpO1xuICAgIGlmICghZG9TaWxlbnQpIHtcbiAgICAgIGNvbnN0IGNoYWxrID0gKGF3YWl0IGltcG9ydChcImNoYWxrXCIpKS5kZWZhdWx0O1xuICAgICAgY29uc29sZS5sb2coY2hhbGsuZ3JlZW4oXCJEQiBDb25maWcgTG9hZGVkIVwiKSk7XG4gICAgfVxuXG4gICAgLy8gRW50aXR5IOuhnOuTnFxuICAgIC8vIO2FjOyKpO2KuOyXkOyEnOuPhCBFbnRpdHkg7KCV67O064qUIO2VhOyalO2VqeuLiOuLpC5cbiAgICAvLyB1cHNlcnTqsIAg7KCc64yA66GcIOyekeuPme2VmOugpOuptCBlbnRpdHnsnZggdW5pcXVlIGluZGV4IOygleuztOqwgCDtlYTsmpTtlZjquLAg65WM66y47J6F64uI64ukLlxuICAgIGNvbnN0IHsgRW50aXR5TWFuYWdlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vZW50aXR5L2VudGl0eS1tYW5hZ2VyXCIpO1xuICAgIGF3YWl0IEVudGl0eU1hbmFnZXIuYXV0b2xvYWQoZG9TaWxlbnQpO1xuXG4gICAgLy8gQ2FjaGUg7LSI6riw7ZmUXG4gICAgYXdhaXQgdGhpcy5pbml0aWFsaXplQ2FjaGUodGhpcy5jb25maWcuc2VydmVyLmNhY2hlLCBmb3JUZXN0aW5nKTtcblxuICAgIC8vIEJldHRlckF1dGgg7LSI6riw7ZmUXG4gICAgY29uc3QgYXV0aENvbmZpZyA9IHRoaXMuY29uZmlnLnNlcnZlci5hdXRoO1xuICAgIGlmIChhdXRoQ29uZmlnKSB7XG4gICAgICAvLyDsgqzsmqnsnpAg7ISk7KCV6rO8IOq4sOuzuOqwkuydhCBtZXJnZVxuICAgICAgY29uc3QgbWVyZ2VkRmllbGRNYXBwaW5ncyA9IG1lcmdlKEJBU0VfRklFTERfTUFQUElOR1MsIGF1dGhDb25maWcpO1xuXG4gICAgICAvLyBiZXR0ZXItYXV0aCDsnbjsiqTthLTsiqQg7IOd7ISxXG4gICAgICBjb25zdCB7IGJldHRlckF1dGggfSA9IGF3YWl0IGltcG9ydChcImJldHRlci1hdXRoXCIpO1xuICAgICAgY29uc3QgeyBzb25hbXVLbmV4QWRhcHRlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vYXV0aC9rbmV4LWFkYXB0ZXJcIik7XG5cbiAgICAgIHRoaXMuX2F1dGggPSBiZXR0ZXJBdXRoKHtcbiAgICAgICAgZGF0YWJhc2U6IHNvbmFtdUtuZXhBZGFwdGVyKCksXG4gICAgICAgIC4uLm1lcmdlZEZpZWxkTWFwcGluZ3MsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyDthYzsiqTtjIXsnbgg6rK97JqwIOyLse2BrCDsl4bsnbQg7KSR64uoXG4gICAgaWYgKGZvclRlc3RpbmcpIHtcbiAgICAgIHRoaXMuaXNJbml0aWFsaXplZCA9IHRydWU7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gVGFzayDrk7HroZ1cbiAgICBhd2FpdCB0aGlzLmluaXRpYWxpemVXb3JrZmxvd3ModGhpcy5jb25maWcudGFza3MpO1xuXG4gICAgLy8gU3luY2VyXG4gICAgY29uc3QgeyBTeW5jZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3N5bmNlci9zeW5jZXJcIik7XG4gICAgdGhpcy5zeW5jZXIgPSBuZXcgU3luY2VyKCk7XG5cbiAgICAvLyBBdXRvbG9hZDogTW9kZWxzIC8gVHlwZXMgLyBBUElzIC8gV29ya2Zsb3dzIC8gVGVtcGxhdGVzIC8gU1NSIFJvdXRlc1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkVHlwZXMoKTtcbiAgICBhd2FpdCB0aGlzLnN5bmNlci5hdXRvbG9hZE1vZGVscygpO1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkQXBpcygpO1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkV29ya2Zsb3dzKCk7XG4gICAgY29uc3QgeyBUZW1wbGF0ZU1hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3RlbXBsYXRlXCIpO1xuICAgIGF3YWl0IFRlbXBsYXRlTWFuYWdlci5hdXRvbG9hZCgpO1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkU1NSUm91dGVzKCk7XG5cbiAgICBjb25zdCB7IGlzTG9jYWwsIGlzVGVzdCwgaXNIb3RSZWxvYWRTZXJ2ZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3V0aWxzL2NvbnRyb2xsZXJcIik7XG4gICAgaWYgKGlzTG9jYWwoKSAmJiAhaXNUZXN0KCkgJiYgaXNIb3RSZWxvYWRTZXJ2ZXIoKSAmJiBlbmFibGVTeW5jKSB7XG4gICAgICBhd2FpdCB0aGlzLnN5bmNlci5zeW5jKCk7XG4gICAgICBhd2FpdCB0aGlzLnN0YXJ0V2F0Y2hlcigpO1xuICAgIH1cblxuICAgIHRoaXMuaXNJbml0aWFsaXplZCA9IHRydWU7XG4gICAgaWYgKCFkb1NpbGVudCkge1xuICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICBjb25zb2xlLnRpbWVFbmQoY2hhbGsuY3lhbihcIlNvbmFtdS5pbml0XCIpKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBjcmVhdGVTZXJ2ZXIoaW5pdE9wdGlvbnM/OiB7IGVuYWJsZVN5bmM/OiBib29sZWFuOyBkb1NpbGVudD86IGJvb2xlYW4gfSkge1xuICAgIGlmICghdGhpcy5pc0luaXRpYWxpemVkKSB7XG4gICAgICBhd2FpdCB0aGlzLmluaXQoaW5pdE9wdGlvbnM/LmRvU2lsZW50LCBpbml0T3B0aW9ucz8uZW5hYmxlU3luYyk7XG4gICAgfVxuXG4gICAgY29uc3Qgb3B0aW9ucyA9IHRoaXMuY29uZmlnLnNlcnZlcjtcbiAgICBjb25zdCB7IGRlZmF1bHQ6IGZhc3RpZnkgfSA9IGF3YWl0IGltcG9ydChcImZhc3RpZnlcIik7XG4gICAgY29uc3QgeyBnZXRMb2dUYXBlRmFzdGlmeUxvZ2dlciB9ID0gYXdhaXQgaW1wb3J0KFwiQGxvZ3RhcGUvZmFzdGlmeVwiKTtcbiAgICBjb25zdCBzZXJ2ZXIgPSBmYXN0aWZ5KHtcbiAgICAgIC4uLm9wdGlvbnMuZmFzdGlmeSxcbiAgICAgIGxvZ2dlcjpcbiAgICAgICAgdGhpcy5jb25maWcubG9nZ2luZyAhPT0gZmFsc2VcbiAgICAgICAgICA/IGdldExvZ1RhcGVGYXN0aWZ5TG9nZ2VyKHtcbiAgICAgICAgICAgICAgY2F0ZWdvcnk6IHRoaXMuY29uZmlnLmxvZ2dpbmc/LmZhc3RpZnlDYXRlZ29yeSA/PyBbXCJmYXN0aWZ5XCJdLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICB9KTtcbiAgICB0aGlzLnNlcnZlciA9IHNlcnZlcjtcblxuICAgIC8vIFN0b3JhZ2Ug7ISk7KCVIOKGkiBTdG9yYWdlTWFuYWdlciDsg53shLFcbiAgICBpZiAob3B0aW9ucy5zdG9yYWdlKSB7XG4gICAgICBjb25zdCB7IFN0b3JhZ2VNYW5hZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9zdG9yYWdlL3N0b3JhZ2UtbWFuYWdlclwiKTtcbiAgICAgIHRoaXMuX3N0b3JhZ2UgPSBuZXcgU3RvcmFnZU1hbmFnZXIob3B0aW9ucy5zdG9yYWdlKTtcbiAgICB9XG5cbiAgICAvLyDtlIzrn6zqt7jsnbgg65Ox66GdXG4gICAgaWYgKG9wdGlvbnMucGx1Z2lucykge1xuICAgICAgYXdhaXQgdGhpcy5yZWdpc3RlclBsdWdpbnMoc2VydmVyLCBvcHRpb25zLnBsdWdpbnMpO1xuICAgIH1cblxuICAgIGlmIChvcHRpb25zLmF1dGgpIHtcbiAgICAgIGF3YWl0IHRoaXMucmVnaXN0ZXJCZXR0ZXJBdXRoKHNlcnZlciwgb3B0aW9ucy5hdXRoKTtcbiAgICB9XG5cbiAgICAvLyBBUEkg65287Jqw7YyFIOyEpOyglVxuICAgIGF3YWl0IHRoaXMud2l0aEZhc3RpZnkoc2VydmVyLCBvcHRpb25zLmFwaUNvbmZpZywge1xuICAgICAgZW5hYmxlU3luYzogaW5pdE9wdGlvbnM/LmVuYWJsZVN5bmMsXG4gICAgICBkb1NpbGVudDogaW5pdE9wdGlvbnM/LmRvU2lsZW50LFxuICAgIH0pO1xuXG4gICAgLy8g7ISc67KEIOyLnOyekVxuICAgIGF3YWl0IHRoaXMuYm9vdChzZXJ2ZXIsIG9wdGlvbnMpO1xuXG4gICAgcmV0dXJuIHNlcnZlcjtcbiAgfVxuXG4gIGFzeW5jIHdpdGhGYXN0aWZ5KFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlPFNlcnZlciwgSW5jb21pbmdNZXNzYWdlLCBTZXJ2ZXJSZXNwb25zZT4sXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIG9wdGlvbnM/OiB7XG4gICAgICBlbmFibGVTeW5jPzogYm9vbGVhbjtcbiAgICAgIGRvU2lsZW50PzogYm9vbGVhbjtcbiAgICB9LFxuICApIHtcbiAgICBpZiAoIXRoaXMuaXNJbml0aWFsaXplZCkge1xuICAgICAgYXdhaXQgdGhpcy5pbml0KG9wdGlvbnM/LmRvU2lsZW50LCBvcHRpb25zPy5lbmFibGVTeW5jKTtcbiAgICB9XG5cbiAgICB0aGlzLnNlcnZlciA9IHNlcnZlcjtcblxuICAgIC8vIHRpbWV6b25lIOyEpOyglVxuICAgIGNvbnN0IHRpbWV6b25lID0gdGhpcy5jb25maWcuYXBpLnRpbWV6b25lO1xuICAgIGlmICh0aW1lem9uZSkge1xuICAgICAgLy8g7YOA7J6E7KG07JeQIOunnuqyjCDsnZHri7Ug64Kg7KecIOyKpO2KuOungeydhCDrs4DtmZjtlbTso7zslrTslbwg7ZWp64uI64ukLlxuICAgICAgLy8g6rCA66C5IHRpbWV6b25l7J20IFwiQXNpYS9TZW91bFwiIOydtOuptFxuICAgICAgLy8gXCIyMDI1LTExLTIxVDAwOjAwOjAwLjAwMFpcIiDrpbwgXCIyMDI1LTExLTIxVDA5OjAwOjAwKzA5OjAwXCIg7Jy866GcIOuzgO2ZmO2VtOyjvOyWtOyVvCDtlanri4jri6QuXG4gICAgICBjb25zdCB7IGZvcm1hdEluVGltZVpvbmUgfSA9IGF3YWl0IGltcG9ydChcImRhdGUtZm5zLXR6XCIpO1xuXG4gICAgICAvLyBJU08gODYwMSDrgqDsp5wg7ZiV7IudIOygleq3nOyLnSAo7JiIOiAyMDI0LTAxLTE1VDA5OjMwOjAwLjAwMFopXG4gICAgICBjb25zdCBJU09fREFURV9SRUdFWCA9IC9eXFxkezR9LVxcZHsyfS1cXGR7Mn1UXFxkezJ9OlxcZHsyfTpcXGR7Mn0oXFwuXFxkezN9KT9aJC87XG5cbiAgICAgIC8vIFTrpbwg65GY65+s7Iu8IOyekeydgOuUsOyYtO2RnOqwgCDsl4bri6TrqbQgXCIyMDI1LTExLTE5MTc2MzU0NjE4OTAwMDE4OjU2OjI5KzA5OjAwXCLsmYAg6rCZ7J2AIOqysOqzvOqwgCDrgpjsmLXri4jri6QuXG4gICAgICAvLyDsnbTripQgZGF0ZS1mbnMg7Yq57J6F64uI64ukLlxuICAgICAgLy8g7J2066CH6rKMIO2VtOuPhCDqtJzssK7sirXri4jri6QuIFwiMjAyNS0xMS0xOVQxODo1NjoyOSswOTowMFwiIOuqqOyWkeycvOuhnCDsnpgg64KY7Ji164uI64ukLlxuICAgICAgY29uc3QgREFURV9GT1JNQVQgPSBcInl5eXktTU0tZGQnVCdISDptbTpzc1hYWFwiO1xuXG4gICAgICBzZXJ2ZXIuc2V0UmVwbHlTZXJpYWxpemVyKChwYXlsb2FkKSA9PiB7XG4gICAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShwYXlsb2FkLCAoX2tleSwgdmFsdWUpID0+IHtcbiAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiICYmIElTT19EQVRFX1JFR0VYLnRlc3QodmFsdWUpKSB7XG4gICAgICAgICAgICByZXR1cm4gZm9ybWF0SW5UaW1lWm9uZShcbiAgICAgICAgICAgICAgbmV3IERhdGUodmFsdWUpLFxuICAgICAgICAgICAgICB0aW1lem9uZSBhcyBgJHtzdHJpbmd9LyR7c3RyaW5nfWAsXG4gICAgICAgICAgICAgIERBVEVfRk9STUFULFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgICAgaWYgKCFvcHRpb25zPy5kb1NpbGVudCkge1xuICAgICAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICAgICAgY29uc29sZS5sb2coY2hhbGsuZ3JlZW4oYFRpbWV6b25lIHNldCB0byAke3RpbWV6b25lfWApKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyDsoITssrQg65287Jqw7YyFIOumrOyKpO2KuFxuICAgIHNlcnZlci5nZXQoXG4gICAgICBgJHt0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4fS9yb3V0ZXNgLFxuICAgICAgYXN5bmMgKF9yZXF1ZXN0LCBfcmVwbHkpOiBQcm9taXNlPHR5cGVvZiB0aGlzLnN5bmNlci5hcGlzPiA9PiB7XG4gICAgICAgIHJldHVybiB0aGlzLnN5bmNlci5hcGlzO1xuICAgICAgfSxcbiAgICApO1xuXG4gICAgLy8gSGVhbHRoY2hlY2sgQVBJXG4gICAgc2VydmVyLmdldChcbiAgICAgIGAke3RoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXh9L2hlYWx0aGNoZWNrYCxcbiAgICAgIGFzeW5jIChfcmVxdWVzdCwgX3JlcGx5KTogUHJvbWlzZTxzdHJpbmc+ID0+IHtcbiAgICAgICAgcmV0dXJuIFwib2tcIjtcbiAgICAgIH0sXG4gICAgKTtcblxuICAgIC8vIFNvbmFtdSBVSSBBUEkgKOuhnOy7rCDtmZjqsr3sl5DshJzrp4wpXG4gICAgY29uc3QgeyBpc0xvY2FsIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy9jb250cm9sbGVyXCIpO1xuICAgIGlmIChpc0xvY2FsKCkpIHtcbiAgICAgIGNvbnN0IHsgc29uYW11VUlBcGlQbHVnaW4gfSA9IGF3YWl0IGltcG9ydChcIi4uL3VpL2FwaVwiKTtcbiAgICAgIHNlcnZlci5yZWdpc3Rlcihzb25hbXVVSUFwaVBsdWdpbik7XG4gICAgfVxuXG4gICAgLy8gRGV2UnVubmVyIO2FjOyKpO2KuCDsl5Trk5ztj6zsnbjtirggKOuhnOy7rCDtmZjqsr0gKyBkZXZSdW5uZXIg7Zmc7ISx7ZmUIOyLnClcbiAgICBpZiAoaXNMb2NhbCgpICYmIHRoaXMuY29uZmlnLnRlc3Q/LmRldlJ1bm5lcj8uZW5hYmxlZCkge1xuICAgICAgY29uc3QgeyByZWdpc3RlckRldlRlc3RSb3V0ZXMgfSA9IGF3YWl0IGltcG9ydChcIi4uL3Rlc3RpbmcvZGV2LXRlc3Qtcm91dGVzXCIpO1xuICAgICAgYXdhaXQgcmVnaXN0ZXJEZXZUZXN0Um91dGVzKHNlcnZlciwgdGhpcy5jb25maWcudGVzdC5kZXZSdW5uZXIpO1xuICAgIH1cblxuICAgIGNvbnN0IHdlYlBhdGggPSBwYXRoLmpvaW4odGhpcy5hcHBSb290UGF0aCwgXCJ3ZWJcIik7XG4gICAgY29uc3QgaGFzV2ViID0gYXdhaXQgZXhpc3RzKHdlYlBhdGgpO1xuXG4gICAgLy8g7KCE7JetIGNvbXByZXNzIOyYteyFmCDqs4TsgrAgKHJvdXRlLmNvbXByZXNzOiB0cnVl7J28IOuVjCDsgqzsmqkpXG4gICAgY29uc3QgcGx1Z2luQ29tcHJlc3MgPSB0aGlzLmNvbmZpZy5zZXJ2ZXIucGx1Z2lucz8uY29tcHJlc3M7XG4gICAgY29uc3QgZ2xvYmFsQ29tcHJlc3NPcHRpb25zOiBDb21wcmVzc09wdGlvbnMgfCB1bmRlZmluZWQgPSBwbHVnaW5Db21wcmVzc1xuICAgICAgPyBwbHVnaW5Db21wcmVzcyA9PT0gdHJ1ZVxuICAgICAgICA/IHsgdGhyZXNob2xkOiAxMDI0LCBlbmNvZGluZ3M6IFtcImJyXCIsIFwiZ3ppcFwiLCBcImRlZmxhdGVcIl0gfVxuICAgICAgICA6IHtcbiAgICAgICAgICAgIHRocmVzaG9sZDogcGx1Z2luQ29tcHJlc3MudGhyZXNob2xkLFxuICAgICAgICAgICAgZW5jb2RpbmdzOiBwbHVnaW5Db21wcmVzcy5lbmNvZGluZ3MsXG4gICAgICAgICAgICBjdXN0b21UeXBlczogcGx1Z2luQ29tcHJlc3MuY3VzdG9tVHlwZXMsXG4gICAgICAgICAgfVxuICAgICAgOiB1bmRlZmluZWQ7XG5cbiAgICBpZiAoaXNMb2NhbCgpKSB7XG4gICAgICAvLyDroZzsu6wg6rCc67CcIO2ZmOqyvTogY2F0Y2gtYWxs66GcIEFQSeulvCDrj5nsoIEg66ek7Lmt7ZWY7JesIEhNUuydhCDsp4Dsm5Dtlanri4jri6QuXG4gICAgICAvLyBTT05BTVVfRElTQUJMRV9JTlRFR1JBVEVEX1dFQj15ZXProZwg7ISk7KCV7ZWY66m0IGRldl9hcGkg66qo65Oc7JeQ7IScIFZpdGUg7Ya17ZWp7J2EIOu5hO2ZnOyEse2ZlO2VoCDsiJgg7J6I7Iq164uI64ukLlxuICAgICAgY29uc3QgZGlzYWJsZUludGVncmF0ZWRXZWIgPSBwcm9jZXNzLmVudi5TT05BTVVfRElTQUJMRV9JTlRFR1JBVEVEX1dFQiA9PT0gXCJ5ZXNcIjtcbiAgICAgIGlmIChoYXNXZWIgJiYgIWRpc2FibGVJbnRlZ3JhdGVkV2ViKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuc2V0dXBEZXZTZXJ2ZXJXaXRoVml0ZShzZXJ2ZXIsIHdlYlBhdGgsIGNvbmZpZyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnNldHVwRGV2U2VydmVyKHNlcnZlciwgY29uZmlnKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8g7ZSE66Gc642V7IWYIO2ZmOqyvTog6rCc67OEIEFQSSDrnbzsmrDtirggKyDsoJXsoIEg7YyM7J28IOyEnOu5mVxuICAgICAgZm9yIChjb25zdCBhcGkgb2YgdGhpcy5zeW5jZXIuYXBpcykge1xuICAgICAgICBpZiAodGhpcy5zeW5jZXIubW9kZWxzW2FwaS5tb2RlbE5hbWVdID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYOygleydmOuQmOyngCDslYrsnYAg66qo64247JeQIOygkeq3vCAke2FwaS5tb2RlbE5hbWV9YCk7XG4gICAgICAgIH1cblxuICAgICAgICBzZXJ2ZXIucm91dGUoe1xuICAgICAgICAgIG1ldGhvZDogYXBpLm9wdGlvbnMuaHR0cE1ldGhvZCA/PyBcIkdFVFwiLFxuICAgICAgICAgIHVybDogdGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeCArIGFwaS5wYXRoLFxuICAgICAgICAgIGhhbmRsZXI6IHRoaXMuY3JlYXRlQXBpSGFuZGxlcihhcGksIGNvbmZpZyksXG4gICAgICAgICAgY29tcHJlc3M6IHRvRmFzdGlmeUNvbXByZXNzT3B0aW9uKGFwaS5vcHRpb25zLmNvbXByZXNzLCBnbG9iYWxDb21wcmVzc09wdGlvbnMpLFxuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgLy8g7ZSE66Gc642V7IWY7JeQ7ISc64qUIHdlYiDshozsiqQoYXBwUm9vdC93ZWIpIOycoOustOyZgCDrrLTqtIDtlZjqsowsXG4gICAgICAvLyBhcGkvd2ViLWRpc3Qg7KG07J6sIOyXrOu2gOulvCBzZXR1cFN0YXRpY1dlYlNlcnZlciDrgrTrtoDsl5DshJwg7YyQ64uo7ZWp64uI64ukLlxuICAgICAgYXdhaXQgdGhpcy5zZXR1cFN0YXRpY1dlYlNlcnZlcihzZXJ2ZXIsIGNvbmZpZywgZ2xvYmFsQ29tcHJlc3NPcHRpb25zKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogZGV2IOuqqOuTnCDqs7XthrU6IGNhdGNoLWFsbOyXkOyEnCBzeW5jZXIuYXBpc+ulvCDrj5nsoIHsnLzroZwg7YOQ7IOJ7ZWY7JesIEFQSSDsmpTssq3snYQg7LKY66as7ZWp64uI64ukLlxuICAgKiBzZXJ2ZXIucm91dGUoKeuhnCDqsJzrs4Qg65Ox66Gd7ZWY66m0IGhhbmRsZXLqsIAg6rOg7KCV65CY7Ja0IEhNUuydtCDrj5nsnpHtlZjsp4Ag7JWK7Jy866+A66GcLFxuICAgKiDrp6Qg7JqU7LKt66eI64ukIHN5bmNlci5hcGlz66W8IOyhsO2ajO2VmOuKlCDsnbQg67Cp7Iud7J2EIOyCrOyaqe2VqeuLiOuLpC5cbiAgICpcbiAgICog7JqU7LKt7J20IC9hcGko7KCV7ZmV7Z6I64qUIHRoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXgp66GcIOyLnOyeke2VmOyngCDslYrripQg6rK97Jqw652866m0IG51bGzsnYQg67CY7ZmY7ZWY66mwIOuBneuDheuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgaGFuZGxlRGV2QXBpUmVxdWVzdChcbiAgICByZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCxcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICk6ICgocmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsIHJlcGx5OiBGYXN0aWZ5UmVwbHkpID0+IFByb21pc2U8dW5rbm93bj4pIHwgbnVsbCB7XG4gICAgY29uc3QgdXJsID0gdGhpcy5nZXRQYXRobmFtZUZyb21VcmwocmVxdWVzdC51cmwpO1xuICAgIGNvbnN0IG1ldGhvZCA9IHJlcXVlc3QubWV0aG9kO1xuXG4gICAgaWYgKCF1cmwuc3RhcnRzV2l0aCh0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4KSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgLy8gc3luY2VyLmFwaXPsnZggcGF0aOuKlCA6cGFyYW0g7ZiV7YOc66W8IO2PrO2VqO2VoCDsiJgg7J6I7Jy866+A66GcIOyEuOq3uOuovO2KuCDri6jsnITroZwg66ek7Lmt7ZWp64uI64ukLlxuICAgIC8vIOygleq3nOyLnSDsg53shLEg67Cp7Iud7J2AIHBhdGgg66y47J6Q7Je0IOuCtCDtirnsiJjrrLjsnpAoLiwgKywgKCwgWyDrk7Ep66GcIOyYpOyekeuPme2VoCDsiJgg7J6I7Ja0IOyCrOyaqe2VmOyngCDslYrsirXri4jri6QuXG4gICAgY29uc3QgbWF0Y2hlZEFwaSA9IHRoaXMuc3luY2VyLmFwaXMuZmluZCgoYXBpKSA9PiB7XG4gICAgICBpZiAodGhpcy5zeW5jZXIubW9kZWxzW2FwaS5tb2RlbE5hbWVdID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgY29uc3QgYXBpTWV0aG9kID0gYXBpLm9wdGlvbnMuaHR0cE1ldGhvZCA/PyBcIkdFVFwiO1xuICAgICAgaWYgKGFwaU1ldGhvZCAhPT0gbWV0aG9kKSByZXR1cm4gZmFsc2U7XG5cbiAgICAgIGNvbnN0IGZ1bGxQYXRoID0gdGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeCArIGFwaS5wYXRoO1xuICAgICAgcmV0dXJuIHRoaXMuaXNQYXRoUGF0dGVybk1hdGNoKGZ1bGxQYXRoLCB1cmwpO1xuICAgIH0pO1xuXG4gICAgaWYgKCFtYXRjaGVkQXBpKSB7XG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFeGNlcHRpb24oU0QoXCJlcnJvci5hcGkubm90Rm91bmRcIikpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLmNyZWF0ZUFwaUhhbmRsZXIobWF0Y2hlZEFwaSwgY29uZmlnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBkZXYgYXBpIOuqqOuTnDogVml0ZSDsl4bsnbQgQVBJIOuPmeyggSDrnbzsmrDtjIXrp4wg7KCc6rO17ZWp64uI64ukLlxuICAgKiBITVLsnYQg7JyE7ZW0IGNhdGNoLWFsbOyXkOyEnCDrp6Qg7JqU7LKt66eI64ukIHN5bmNlci5hcGlz66W8IOyhsO2ajO2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgc2V0dXBEZXZTZXJ2ZXIoXG4gICAgc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2U8U2VydmVyLCBJbmNvbWluZ01lc3NhZ2UsIFNlcnZlclJlc3BvbnNlPixcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICk6IHZvaWQge1xuICAgIHNlcnZlci5yb3V0ZSh7XG4gICAgICBtZXRob2Q6IFtcIkdFVFwiLCBcIkhFQURcIiwgXCJQT1NUXCIsIFwiUFVUXCIsIFwiREVMRVRFXCIsIFwiUEFUQ0hcIl0sXG4gICAgICB1cmw6IGAke3RoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXh9LypgLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcXVlc3QsIHJlcGx5KSA9PiB7XG4gICAgICAgIGNvbnN0IGhhbmRsZXIgPSB0aGlzLmhhbmRsZURldkFwaVJlcXVlc3QocmVxdWVzdCwgY29uZmlnKTtcbiAgICAgICAgaWYgKGhhbmRsZXIpIHtcbiAgICAgICAgICByZXR1cm4gaGFuZGxlcihyZXF1ZXN0LCByZXBseSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8g65Ox66Gd65CcIEFQSeyZgCDsnbzsuZjtlZjsp4Ag7JWK64qUIOyalOyyreyXkCDrjIDtlZwgZmFsbGJhY2vsnoXri4jri6QuXG4gICAgICAgIHRocm93IG5ldyBOb3RGb3VuZEV4Y2VwdGlvbihTRChcImVycm9yLmFwaS5ub3RGb3VuZFwiKSk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgLy8gb3hsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkgLS0gVml0ZURldlNlcnZlciDtg4DsnoXsnYQg64+Z7KCB7Jy866GcIOuhnOuTnO2VtOyVvCDtlahcbiAgcHJpdmF0ZSB2aXRlU2VydmVyOiBhbnkgPSBudWxsO1xuXG4gIC8qKlxuICAgKiBkZXYgYWxsIOuqqOuTnDogVml0ZSBEZXYgU2VydmVy66W8IO2Gte2Vqe2VmOyXrCBBUEkgKyBTU1IgKyBDU1LsnYQg66qo65GQIOygnOqzte2VqeuLiOuLpC5cbiAgICogQVBJIOuPmeyggSDrp6Tsua3snYAgaGFuZGxlRGV2QXBpUmVxdWVzdOulvCDqs7XsnKDtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHNldHVwRGV2U2VydmVyV2l0aFZpdGUoXG4gICAgc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2U8U2VydmVyLCBJbmNvbWluZ01lc3NhZ2UsIFNlcnZlclJlc3BvbnNlPixcbiAgICB3ZWJQYXRoOiBzdHJpbmcsXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBAZmFzdGlmeS9taWRkaWUg65Ox66GdIChDb25uZWN0LXN0eWxlIG1pZGRsZXdhcmUg7KeA7JuQKVxuICAgIGF3YWl0IHNlcnZlci5yZWdpc3RlcigoYXdhaXQgaW1wb3J0KFwiQGZhc3RpZnkvbWlkZGllXCIpKS5kZWZhdWx0KTtcblxuICAgIGNvbnN0IHZpdGUgPSBhd2FpdCBpbXBvcnQoXCJ2aXRlXCIpO1xuXG4gICAgdGhpcy52aXRlU2VydmVyID0gYXdhaXQgdml0ZS5jcmVhdGVTZXJ2ZXIoe1xuICAgICAgcm9vdDogd2ViUGF0aCxcbiAgICAgIHNlcnZlcjoge1xuICAgICAgICBtaWRkbGV3YXJlTW9kZTogdHJ1ZSxcbiAgICAgICAgaG1yOiB7XG4gICAgICAgICAgc2VydmVyOiBzZXJ2ZXIuc2VydmVyLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIGFwcFR5cGU6IFwiY3VzdG9tXCIsXG4gICAgfSk7XG5cbiAgICAvLyBWaXRlIG1pZGRsZXdhcmUg65Ox66GdIChWaXRlIOyXkOyFiyDsspjrpqwpXG4gICAgc2VydmVyLnVzZSgocmVxLCByZXMsIG5leHQpID0+IHtcbiAgICAgIC8vIEFQSeyZgCBTb25hbXUgVUnripQgRmFzdGlmeSDrnbzsmrDtirjqsIAg7LKY66as7ZWY64+E66GdIHNraXBcbiAgICAgIGlmIChyZXEudXJsPy5zdGFydHNXaXRoKHRoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXgpIHx8IHJlcS51cmw/LnN0YXJ0c1dpdGgoXCIvc29uYW11LXVpXCIpKSB7XG4gICAgICAgIHJldHVybiBuZXh0KCk7XG4gICAgICB9XG4gICAgICAvLyDrgpjrqLjsp4DripQgVml0ZSBtaWRkbGV3YXJl66GcIOyghOuLrFxuICAgICAgcmV0dXJuIHRoaXMudml0ZVNlcnZlci5taWRkbGV3YXJlcyhyZXEsIHJlcywgbmV4dCk7XG4gICAgfSk7XG5cbiAgICAvLyBjYXRjaC1hbGwg65287Jqw7Yq47JeQ7IScIOuPmeyggeycvOuhnCBBUEkvU1NSIOyymOumrFxuICAgIC8vIOqwnOuwnCDtmZjqsr3sl5DshJzripQg65287Jqw7Yq467OEIGNvbXByZXNzIOyYteyFmOydhCDtj6zquLDtlZjqs6AgSE1SIOydtOygkOydhCDst6jtlanri4jri6QuXG4gICAgc2VydmVyLnJvdXRlKHtcbiAgICAgIG1ldGhvZDogW1wiR0VUXCIsIFwiSEVBRFwiLCBcIlBPU1RcIiwgXCJQVVRcIiwgXCJERUxFVEVcIiwgXCJQQVRDSFwiXSxcbiAgICAgIHVybDogXCIvKlwiLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcXVlc3QsIHJlcGx5KSA9PiB7XG4gICAgICAgIC8vIDEuIEFQSSDsmpTssq0g7LKY66asXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuaGFuZGxlRGV2QXBpUmVxdWVzdChyZXF1ZXN0LCBjb25maWcpO1xuICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgcmV0dXJuIHJlc3VsdChyZXF1ZXN0LCByZXBseSk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB1cmwgPSByZXF1ZXN0LnVybDtcblxuICAgICAgICAvLyAyLiBTU1Ig65287Jqw7Yq4IOyymOumrFxuICAgICAgICBjb25zdCB7IG1hdGNoU1NSUm91dGUsIHJlbmRlclNTUiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vc3NyXCIpO1xuICAgICAgICBjb25zdCBzc3JNYXRjaCA9IG1hdGNoU1NSUm91dGUodXJsKTtcbiAgICAgICAgaWYgKHNzck1hdGNoKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYFtTU1JdIE1hdGNoZWQgcm91dGU6ICR7c3NyTWF0Y2gucm91dGUucGF0aH1gKTtcbiAgICAgICAgICBjb25zdCBodG1sID0gYXdhaXQgcmVuZGVyU1NSKFxuICAgICAgICAgICAgdXJsLFxuICAgICAgICAgICAgc3NyTWF0Y2gucm91dGUsXG4gICAgICAgICAgICBzc3JNYXRjaC5wYXJhbXMsXG4gICAgICAgICAgICByZXF1ZXN0LFxuICAgICAgICAgICAgcmVwbHksXG4gICAgICAgICAgICBjb25maWcsXG4gICAgICAgICAgICB0aGlzLnZpdGVTZXJ2ZXIsXG4gICAgICAgICAgKTtcbiAgICAgICAgICByZXBseS50eXBlKFwidGV4dC9odG1sXCIpO1xuICAgICAgICAgIHJldHVybiBodG1sO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gMy4gQ1NSIGZhbGxiYWNrXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgZnMgPSBhd2FpdCBpbXBvcnQoXCJub2RlOmZzL3Byb21pc2VzXCIpO1xuICAgICAgICAgIGxldCB0ZW1wbGF0ZSA9IGF3YWl0IGZzLnJlYWRGaWxlKFxuICAgICAgICAgICAgcGF0aC5qb2luKHRoaXMudml0ZVNlcnZlci5jb25maWcucm9vdCwgXCJpbmRleC5odG1sXCIpLFxuICAgICAgICAgICAgXCJ1dGYtOFwiLFxuICAgICAgICAgICk7XG4gICAgICAgICAgdGVtcGxhdGUgPSBhd2FpdCB0aGlzLnZpdGVTZXJ2ZXIudHJhbnNmb3JtSW5kZXhIdG1sKHVybCwgdGVtcGxhdGUpO1xuXG4gICAgICAgICAgcmVwbHkudHlwZShcInRleHQvaHRtbFwiKTtcbiAgICAgICAgICByZXR1cm4gdGVtcGxhdGU7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICB0aGlzLnZpdGVTZXJ2ZXIuc3NyRml4U3RhY2t0cmFjZShlIGFzIEVycm9yKTtcbiAgICAgICAgICBjb25zb2xlLmVycm9yKGUpO1xuICAgICAgICAgIHJlcGx5LnN0YXR1cyg1MDApO1xuICAgICAgICAgIHJldHVybiAoZSBhcyBFcnJvcikubWVzc2FnZTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIC8vIOyEnOuyhCDsooXro4wg7IucIFZpdGXrj4Qg7KKF66OMXG4gICAgc2VydmVyLmFkZEhvb2soXCJvbkNsb3NlXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGF3YWl0IHRoaXMudml0ZVNlcnZlci5jbG9zZSgpO1xuICAgIH0pO1xuXG4gICAgY29uc29sZS5sb2coXCLinJMgVml0ZSBkZXYgc2VydmVyIGludGVncmF0ZWRcIik7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHNldHVwU3RhdGljV2ViU2VydmVyKFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlPFNlcnZlciwgSW5jb21pbmdNZXNzYWdlLCBTZXJ2ZXJSZXNwb25zZT4sXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIGdsb2JhbENvbXByZXNzT3B0aW9uczogQ29tcHJlc3NPcHRpb25zIHwgdW5kZWZpbmVkLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyDqsr3roZwg66qF7ZmV7ZmUOiBhcGkvd2ViLWRpc3QvY2xpZW50ICjsoJXsoIEg7YyM7J28KSwgYXBpL3dlYi1kaXN0L3NlcnZlciAoU1NSIGVudHJ5KSwgYXBpL2Rpc3Qvc3NyIChTU1Igcm91dGVzIC0gQVBJIOyGjOycoClcbiAgICBjb25zdCB3ZWJEaXN0UGF0aCA9IHBhdGguam9pbih0aGlzLmFwaVJvb3RQYXRoLCBcIndlYi1kaXN0XCIsIFwiY2xpZW50XCIpO1xuICAgIGNvbnN0IHNzclBhdGggPSBwYXRoLmpvaW4odGhpcy5hcGlSb290UGF0aCwgXCJ3ZWItZGlzdFwiLCBcInNlcnZlclwiKTtcbiAgICBjb25zdCBzc3JFbnRyeVBhdGggPSBwYXRoLmpvaW4oc3NyUGF0aCwgXCJlbnRyeS1zZXJ2ZXIuZ2VuZXJhdGVkLmpzXCIpO1xuICAgIGNvbnN0IHNzclJvdXRlc1BhdGggPSBwYXRoLmpvaW4odGhpcy5hcGlSb290UGF0aCwgXCJkaXN0XCIsIFwic3NyXCIsIFwicm91dGVzLmpzXCIpO1xuXG4gICAgaWYgKCEoYXdhaXQgZXhpc3RzKHdlYkRpc3RQYXRoKSkpIHtcbiAgICAgIGNvbnNvbGUud2Fybihg4pqgIFdlYiBkaXN0IG5vdCBmb3VuZDogJHt3ZWJEaXN0UGF0aH1gKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBTU1IgZW50cnkg7KG07J6sIOyXrOu2gCDtmZXsnbhcbiAgICBjb25zdCBzc3JBdmFpbGFibGUgPSBhd2FpdCBleGlzdHMoc3NyRW50cnlQYXRoKTtcblxuICAgIGlmICghc3NyQXZhaWxhYmxlKSB7XG4gICAgICBjb25zb2xlLndhcm4oYOKaoCBTU1IgZW50cnkgbm90IGZvdW5kOiAke3NzckVudHJ5UGF0aH1gKTtcbiAgICAgIGNvbnNvbGUud2FybihcIiAgU1NSIHdpbGwgYmUgZGlzYWJsZWQuIE9ubHkgQ1NSIHdpbGwgd29yay5cIik7XG4gICAgfVxuXG4gICAgLy8gU1NSIOudvOyasO2KuCDroZzrk5wgKHByb2R1Y3Rpb27sl5DshJzrp4wsIOyCrOyaqeyekCDtlITroZzsoJ3tirjsnZggc3NyL3JvdXRlcy50cylcbiAgICBpZiAoc3NyQXZhaWxhYmxlKSB7XG4gICAgICBpZiAoYXdhaXQgZXhpc3RzKHNzclJvdXRlc1BhdGgpKSB7XG4gICAgICAgIC8vIHRzLWxvYWRlcuudvOuptCBcImZpbGU6Ly9cIuuhnCDsi5zsnpHtlZjripQgZnVsbHktcmVzb2x2ZWQgcGF0aOunjCDrsJvquLDsl5Ag7J2066W8IOyymOumrO2VtOyjvOuKlCBpbXBvcnRNZW1iZXJz66W8IOyCrOyaqe2VtOyVvCDtlojqsqDsp4Drp4wsXG4gICAgICAgIC8vIOyXrOq4sOuKlCDtlITroZzrjZXshZgg7ZmY6rK97JeQ7IScIGxvYWRlciDsl4bsnbQg64+M7JWE6rCA6riwIOuVjOusuOyXkCBcIuynhOynnCBqcyDtjIzsnbxcIuydmCBcIuq3uOuDpVwiIOygiOuMgOqyveuhnOulvCDrsJTroZwgaW1wb3J07ZW064+EIOuQqeuLiOuLpC5cbiAgICAgICAgLy8g7J20IOuCtOyaqeydgCDsnbQg7ZWo7IiYIOuCtOyXkOyEnCDslYTrnpjsl5Ag64KY7JisIOuLpOuluCBpbXBvcnQg7Zi47Lac7JeQ64+EIOuPmeydvO2VmOqyjCDsoIHsmqnrkKnri4jri6QuXG4gICAgICAgIGF3YWl0IGltcG9ydChzc3JSb3V0ZXNQYXRoKTtcbiAgICAgICAgY29uc29sZS5sb2coXCLinJMgU1NSIHJvdXRlcyBsb2FkZWRcIik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLndhcm4oYOKaoCBTU1Igcm91dGVzIG5vdCBmb3VuZDogJHtzc3JSb3V0ZXNQYXRofWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIOuhpOungSDsl4XrjbDsnbTtirgg64yA7J2ROiBhc3NldCBoYXNoIOu2iOydvOy5mCDsi5wg7ZiE7J6sIOuyhOyghCDsp4HsoJEg7ISc67mZXG4gICAgc2VydmVyLmdldChcIi9hc3NldHMvOmZpbGVuYW1lXCIsIGFzeW5jIChyZXF1ZXN0LCByZXBseSkgPT4ge1xuICAgICAgY29uc3QgcmVxdWVzdGVkRmlsZSA9IChyZXF1ZXN0LnBhcmFtcyBhcyB7IGZpbGVuYW1lOiBzdHJpbmcgfSkuZmlsZW5hbWU7XG4gICAgICBjb25zdCBhc3NldHNEaXIgPSBwYXRoLmpvaW4od2ViRGlzdFBhdGgsIFwiYXNzZXRzXCIpO1xuICAgICAgY29uc3Qgc2FmZUZpbGVQYXRoID0gdGhpcy5yZXNvbHZlUGF0aFdpdGhpbkJhc2VEaXIoYXNzZXRzRGlyLCByZXF1ZXN0ZWRGaWxlKTtcbiAgICAgIGlmIChzYWZlRmlsZVBhdGggPT09IG51bGwpIHtcbiAgICAgICAgcmVwbHkuc3RhdHVzKDQwMykuc2VuZCgpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBub3JtYWxpemVkUmVxdWVzdGVkRmlsZSA9IHBhdGgucmVsYXRpdmUoYXNzZXRzRGlyLCBzYWZlRmlsZVBhdGgpLnJlcGxhY2UoL1xcXFwvZywgXCIvXCIpO1xuXG4gICAgICBjb25zdCBhc3NldFBhdGggPSBgL2Fzc2V0cy8ke25vcm1hbGl6ZWRSZXF1ZXN0ZWRGaWxlfWA7XG5cbiAgICAgIC8vIENhY2hlLUNvbnRyb2wg7Zek642UIOqysOyglVxuICAgICAgY29uc3QgZ2V0Q2FjaGVDb250cm9sRm9yQXNzZXQgPSAoKTogQ2FjaGVDb250cm9sQ29uZmlnID0+IHtcbiAgICAgICAgY29uc3QgY2FjaGVSZXE6IENhY2hlQ29udHJvbFJlcXVlc3QgPSB7XG4gICAgICAgICAgdHlwZTogXCJhc3NldHNcIixcbiAgICAgICAgICB1cmw6IHJlcXVlc3QudXJsLFxuICAgICAgICAgIHBhdGg6IGFzc2V0UGF0aCxcbiAgICAgICAgICBtZXRob2Q6IHJlcXVlc3QubWV0aG9kLFxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIOyCrOyaqeyekCDsoJXsnZgg7ZW465Ok65+sIOyasOyEoFxuICAgICAgICBpZiAoY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIpIHtcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBjb25maWcuY2FjaGVDb250cm9sSGFuZGxlcihjYWNoZVJlcSk7XG4gICAgICAgICAgaWYgKHJlc3VsdCkgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOq4sOuzuOqwkjogaW1tdXRhYmxlXG4gICAgICAgIHJldHVybiBDYWNoZVByZXNldHMuaW1tdXRhYmxlO1xuICAgICAgfTtcblxuICAgICAgLy8gaW5kZXgtKi5qcyDrmJDripQgaW5kZXgtKi5jc3Mg7JqU7LKt7J24IOqyveyasFxuICAgICAgaWYgKC9eaW5kZXgtW2EtZjAtOV0rXFwuKGpzfGNzcykkLy50ZXN0KG5vcm1hbGl6ZWRSZXF1ZXN0ZWRGaWxlKSkge1xuICAgICAgICBjb25zdCBleHQgPSBub3JtYWxpemVkUmVxdWVzdGVkRmlsZS5zcGxpdChcIi5cIikucG9wKCk7XG4gICAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcihhc3NldHNEaXIpO1xuICAgICAgICBjb25zdCBjdXJyZW50RmlsZSA9IGZpbGVzLmZpbmQoKGYpID0+IGYuc3RhcnRzV2l0aChcImluZGV4LVwiKSAmJiBmLmVuZHNXaXRoKGAuJHtleHR9YCkpO1xuXG4gICAgICAgIGlmIChjdXJyZW50RmlsZSkge1xuICAgICAgICAgIGNvbnN0IGZpbGVQYXRoID0gcGF0aC5qb2luKGFzc2V0c0RpciwgY3VycmVudEZpbGUpO1xuICAgICAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShmaWxlUGF0aCk7XG4gICAgICAgICAgcmVwbHkudHlwZShleHQgPT09IFwianNcIiA/IFwiYXBwbGljYXRpb24vamF2YXNjcmlwdFwiIDogXCJ0ZXh0L2Nzc1wiKTtcbiAgICAgICAgICBhcHBseUNhY2hlSGVhZGVycyhyZXBseSwgZ2V0Q2FjaGVDb250cm9sRm9yQXNzZXQoKSk7XG4gICAgICAgICAgcmV0dXJuIHJlcGx5LnNlbmQoY29udGVudCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8g7J2867CYIO2MjOydvCDshJzruZlcbiAgICAgIGNvbnN0IGZpbGVQYXRoID0gc2FmZUZpbGVQYXRoO1xuICAgICAgaWYgKGF3YWl0IGV4aXN0cyhmaWxlUGF0aCkpIHtcbiAgICAgICAgY29uc3QgY29udGVudCA9IGF3YWl0IGZzLnJlYWRGaWxlKGZpbGVQYXRoKTtcbiAgICAgICAgY29uc3QgZXh0ID0gbm9ybWFsaXplZFJlcXVlc3RlZEZpbGUuc3BsaXQoXCIuXCIpLnBvcCgpO1xuICAgICAgICByZXBseS50eXBlKGV4dCA9PT0gXCJqc1wiID8gXCJhcHBsaWNhdGlvbi9qYXZhc2NyaXB0XCIgOiBleHQgPT09IFwiY3NzXCIgPyBcInRleHQvY3NzXCIgOiBcIlwiKTtcbiAgICAgICAgaWYgKG5vcm1hbGl6ZWRSZXF1ZXN0ZWRGaWxlLmluY2x1ZGVzKFwiLVwiKSkge1xuICAgICAgICAgIGFwcGx5Q2FjaGVIZWFkZXJzKHJlcGx5LCBnZXRDYWNoZUNvbnRyb2xGb3JBc3NldCgpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVwbHkuc2VuZChjb250ZW50KTtcbiAgICAgIH1cblxuICAgICAgcmVwbHkuc3RhdHVzKDQwNCkuc2VuZCgpO1xuICAgIH0pO1xuXG4gICAgLy8gU1NSIOudvOyasO2KuCDqsJzrs4Qg65Ox66GdIChjb21wcmVzcyDsmLXshZjsnbQg65287Jqw7Yq467OE66GcIOyggeyaqeuQmOuPhOuhnSlcbiAgICBpZiAoc3NyQXZhaWxhYmxlKSB7XG4gICAgICBjb25zdCB7IGdldFNTUlJvdXRlcyB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vc3NyXCIpO1xuICAgICAgY29uc3QgeyByZW5kZXJTU1IgfSA9IGF3YWl0IGltcG9ydChcIi4uL3Nzci9yZW5kZXJlclwiKTtcbiAgICAgIGNvbnN0IHNzclJvdXRlcyA9IGdldFNTUlJvdXRlcygpO1xuXG4gICAgICBmb3IgKGNvbnN0IHJvdXRlIG9mIHNzclJvdXRlcykge1xuICAgICAgICBzZXJ2ZXIucm91dGUoe1xuICAgICAgICAgIG1ldGhvZDogW1wiR0VUXCIsIFwiSEVBRFwiXSxcbiAgICAgICAgICB1cmw6IHJvdXRlLnBhdGgsXG4gICAgICAgICAgY29tcHJlc3M6IHRvRmFzdGlmeUNvbXByZXNzT3B0aW9uKHJvdXRlLmNvbXByZXNzID8/IHRydWUsIGdsb2JhbENvbXByZXNzT3B0aW9ucyksXG4gICAgICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcXVlc3QsIHJlcGx5KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB1cmwgPSByZXF1ZXN0LnVybDtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbU1NSXSBNYXRjaGVkIHJvdXRlOiAke3JvdXRlLnBhdGh9YCk7XG5cbiAgICAgICAgICAgIGNvbnN0IHBhcmFtcyA9IHRoaXMuZXh0cmFjdFBhdGhQYXJhbXMocm91dGUucGF0aCwgdXJsKTtcbiAgICAgICAgICAgIGNvbnN0IGh0bWwgPSBhd2FpdCByZW5kZXJTU1IodXJsLCByb3V0ZSwgcGFyYW1zLCByZXF1ZXN0LCByZXBseSwgY29uZmlnKTtcblxuICAgICAgICAgICAgcmVwbHkudHlwZShcInRleHQvaHRtbFwiKTtcbiAgICAgICAgICAgIHJldHVybiBodG1sO1xuICAgICAgICAgIH0sXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIENTUiBvciBTdGF0aWMgRmlsZSBGYWxsYmFjayAoU1NSIOudvOyasO2KuOyXkCDrp6Tsua3rkJjsp4Ag7JWK64qUIOuqqOuToCDsmpTssq0pXG4gICAgc2VydmVyLnJvdXRlKHtcbiAgICAgIG1ldGhvZDogW1wiR0VUXCIsIFwiSEVBRFwiXSxcbiAgICAgIHVybDogXCIqXCIsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgICAgLy8gL2FwaSwgL3NvbmFtdS11aeuKlCA0MDQg6re464yA66GcXG4gICAgICAgIGlmIChyZXF1ZXN0LnVybC5zdGFydHNXaXRoKFwiL2FwaVwiKSB8fCByZXF1ZXN0LnVybC5zdGFydHNXaXRoKFwiL3NvbmFtdS11aVwiKSkge1xuICAgICAgICAgIHJlcGx5LnN0YXR1cyg0MDQpLnNlbmQoKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDU1LsmqkgQ2FjaGUtQ29udHJvbCDtl6TrjZQg7ISk7KCVXG4gICAgICAgIGlmIChjb25maWcuY2FjaGVDb250cm9sSGFuZGxlcikge1xuICAgICAgICAgIGNvbnN0IGNzckNhY2hlUmVxOiBDYWNoZUNvbnRyb2xSZXF1ZXN0ID0ge1xuICAgICAgICAgICAgdHlwZTogXCJjc3JcIixcbiAgICAgICAgICAgIHVybDogcmVxdWVzdC51cmwsXG4gICAgICAgICAgICBwYXRoOiByZXF1ZXN0LnVybC5zcGxpdChcIj9cIilbMF0sXG4gICAgICAgICAgICBtZXRob2Q6IHJlcXVlc3QubWV0aG9kLFxuICAgICAgICAgIH07XG4gICAgICAgICAgY29uc3QgY3NyQ2FjaGVDb25maWcgPSBjb25maWcuY2FjaGVDb250cm9sSGFuZGxlcihjc3JDYWNoZVJlcSk7XG5cbiAgICAgICAgICBpZiAoY3NyQ2FjaGVDb25maWcpIHtcbiAgICAgICAgICAgIGFwcGx5Q2FjaGVIZWFkZXJzKHJlcGx5LCBjc3JDYWNoZUNvbmZpZyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8g7KCV7KCBIO2MjOydvOydtCDsobTsnqztlaAg6rK97JqwLCDsoJXsoIEg7YyM7J287J2EIOuovOyggCDshJzruZntlbTslbztlahcbiAgICAgICAgY29uc3QgcmVxdWVzdFBhdGggPSB0aGlzLmdldFBhdGhuYW1lRnJvbVVybChyZXF1ZXN0LnVybCk7XG4gICAgICAgIGNvbnN0IHNhZmVGaWxlUGF0aCA9IHRoaXMucmVzb2x2ZVBhdGhXaXRoaW5CYXNlRGlyKHdlYkRpc3RQYXRoLCByZXF1ZXN0UGF0aCk7XG4gICAgICAgIGlmIChzYWZlRmlsZVBhdGggPT09IG51bGwpIHtcbiAgICAgICAgICByZXBseS5zdGF0dXMoNDAzKS5zZW5kKCk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmIChhd2FpdCBmaWxlRXhpc3RzKHNhZmVGaWxlUGF0aCkpIHtcbiAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgZnMucmVhZEZpbGUoc2FmZUZpbGVQYXRoKTtcbiAgICAgICAgICByZXR1cm4gcmVwbHkudHlwZShtaW1lTG9va3VwKHNhZmVGaWxlUGF0aCkgfHwgXCJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW1cIikuc2VuZChjb250ZW50KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENTUiBmYWxsYmFjazogaW5kZXguaHRtbCDshJzruZlcbiAgICAgICAgY29uc3QgaW5kZXhQYXRoID0gcGF0aC5qb2luKHdlYkRpc3RQYXRoLCBcImluZGV4Lmh0bWxcIik7XG4gICAgICAgIHJldHVybiByZXBseS50eXBlKFwidGV4dC9odG1sXCIpLnNlbmQoYXdhaXQgZnMucmVhZEZpbGUoaW5kZXhQYXRoLCBcInV0Zi04XCIpKTtcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zb2xlLmxvZyhg4pyTIFN0YXRpYyB3ZWIgc2VydmVyIGNvbmZpZ3VyZWQgd2l0aCAke3NzckF2YWlsYWJsZSA/IFwiU1NSXCIgOiBcIkNTUiBvbmx5XCJ9IHN1cHBvcnRgKTtcbiAgfVxuXG4gIGNyZWF0ZUFwaUhhbmRsZXIoXG4gICAgYXBpOiBFeHRlbmRlZEFwaSxcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICk6IChyZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCwgcmVwbHk6IEZhc3RpZnlSZXBseSkgPT4gUHJvbWlzZTx1bmtub3duPiB7XG4gICAgcmV0dXJuIGFzeW5jIChyZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCwgcmVwbHk6IEZhc3RpZnlSZXBseSk6IFByb21pc2U8dW5rbm93bj4gPT4ge1xuICAgICAgLy8gQ29udGV4dCDsg53shLFcbiAgICAgIGNvbnN0IGNvbnRleHQ6IENvbnRleHQgPSBhd2FpdCB0aGlzLmNyZWF0ZUNvbnRleHQoY29uZmlnLCByZXF1ZXN0LCByZXBseSk7XG5cbiAgICAgIHJldHVybiB0aGlzLmFzeW5jTG9jYWxTdG9yYWdlLnJ1bih7IGNvbnRleHQgfSwgYXN5bmMgKCkgPT4ge1xuICAgICAgICAvLyBndWFyZHMg7LKY66asXG4gICAgICAgIChhcGkub3B0aW9ucy5ndWFyZHMgPz8gW10pLmV2ZXJ5KChndWFyZCkgPT4gY29uZmlnLmd1YXJkSGFuZGxlcihndWFyZCwgcmVxdWVzdCwgYXBpKSk7XG5cbiAgICAgICAgLy8g7YyM652866+47YSwIOygleuztOuhnCB6b2Qg7Iqk7YKk66eIIOu5jOuTnFxuICAgICAgICBjb25zdCB7IGdldFpvZE9iamVjdEZyb21BcGkgfSA9IGF3YWl0IGltcG9ydChcIi4vY29kZS1jb252ZXJ0ZXJzXCIpO1xuICAgICAgICBjb25zdCBSZXFUeXBlID0gZ2V0Wm9kT2JqZWN0RnJvbUFwaShhcGksIHRoaXMuc3luY2VyLnR5cGVzKTtcblxuICAgICAgICAvLyByZXF1ZXN0IO2MjOyLsVxuICAgICAgICBjb25zdCB3aGljaCA9IGFwaS5vcHRpb25zLmh0dHBNZXRob2QgPT09IFwiR0VUXCIgPyBcInF1ZXJ5XCIgOiBcImJvZHlcIjtcbiAgICAgICAgbGV0IHJlcUJvZHk6IHtcbiAgICAgICAgICBba2V5OiBzdHJpbmddOiB1bmtub3duO1xuICAgICAgICB9O1xuICAgICAgICAvLyDtjIzsnbwg7JeF66Gc65OcIOyeiOuKlCDqsr3smrAg7J6E7IucIOuNsOydtO2EsFxuICAgICAgICBjb25zdCBmaWxlczoge1xuICAgICAgICAgIGJ1ZmZlcmVkRmlsZXM6IEJ1ZmZlcmVkRmlsZVtdO1xuICAgICAgICAgIHVwbG9hZGVkRmlsZXM6IFVwbG9hZGVkRmlsZVtdO1xuICAgICAgICB9ID0ge1xuICAgICAgICAgIGJ1ZmZlcmVkRmlsZXM6IFtdLFxuICAgICAgICAgIHVwbG9hZGVkRmlsZXM6IFtdLFxuICAgICAgICB9O1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgYm9keSA9IChyZXF1ZXN0W3doaWNoXSA/PyB7fSkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgICAgICAgaWYgKGFwaS51cGxvYWRPcHRpb25zKSB7XG4gICAgICAgICAgICBjb25zdCBwYXJ0cyA9IHJlcXVlc3QucGFydHMoe1xuICAgICAgICAgICAgICBsaW1pdHM6IGFwaS51cGxvYWRPcHRpb25zLmxpbWl0cyxcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBGb3JtRGF0YeydmCBmaWVsZOuTpOydhCDsnoTsi5zroZwg7KCA7J6lXG4gICAgICAgICAgICBjb25zdCBmaWVsZHM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICAgICAgICAgICAgaWYgKGFwaS51cGxvYWRPcHRpb25zLmNvbnN1bWUgPT09IFwiYnVmZmVyXCIgfHwgIWFwaS51cGxvYWRPcHRpb25zLmNvbnN1bWUpIHtcbiAgICAgICAgICAgICAgLy8gQnVmZmVyIOuqqOuTnDog66mU66qo66as7JeQIOuhnOuTnFxuICAgICAgICAgICAgICBmb3IgYXdhaXQgKGNvbnN0IHBhcnQgb2YgcGFydHMpIHtcbiAgICAgICAgICAgICAgICBpZiAocGFydC50eXBlID09PSBcImZpbGVcIikge1xuICAgICAgICAgICAgICAgICAgLy8gQ1JJVElDQUw6IO2MjOydvCDsiqTtirjrprzsnYQg7KaJ7IucIGNvbnN1bWXtlbTslbwg64uk7J2MIHBhcnTroZwg64SY7Ja06rCIIOyImCDsnojsnYxcbiAgICAgICAgICAgICAgICAgIC8vIOydtCDtmLjstpzsnbQg7JeG7Jy866m0IOyiheyihSBtdWx0aXBhcnQg7YyM7Iux7J20IHBlbmRpbmcg7IOB7YOc66GcIO2DgOyehOyVhOybgyDrsJzsg51cbiAgICAgICAgICAgICAgICAgIGNvbnN0IGJ1ZmZlciA9IGF3YWl0IHBhcnQudG9CdWZmZXIoKTtcbiAgICAgICAgICAgICAgICAgIGZpbGVzLmJ1ZmZlcmVkRmlsZXMucHVzaChuZXcgQnVmZmVyZWRGaWxlKHBhcnQsIGJ1ZmZlcikpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAocGFydC50eXBlID09PSBcImZpZWxkXCIpIHtcbiAgICAgICAgICAgICAgICAgIGZpZWxkc1twYXJ0LmZpZWxkbmFtZV0gPSBTdHJpbmcocGFydC52YWx1ZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGFwaS51cGxvYWRPcHRpb25zLmNvbnN1bWUgPT09IFwic3RyZWFtXCIpIHtcbiAgICAgICAgICAgICAgLy8gU3RyZWFtIOuqqOuTnDog7KaJ7IucIOyggOyepeyGjOuhnCDsiqTtirjrpqzrsI1cbiAgICAgICAgICAgICAgY29uc3QgZGlza05hbWUgPSBhcGkudXBsb2FkT3B0aW9ucy5kZXN0aW5hdGlvbjtcbiAgICAgICAgICAgICAgY29uc3QgZGlzayA9IHRoaXMuc3RvcmFnZS51c2UoZGlza05hbWUpO1xuXG4gICAgICAgICAgICAgIC8vIOyasOyEoOyInOychDog642w7L2U66CI7J207YSwID4g7KCE7JetIOyEpOyglSA+IOq4sOuzuOqwklxuICAgICAgICAgICAgICBjb25zdCBrZXlHZW5lcmF0b3I6IEtleUdlbmVyYXRvciA9XG4gICAgICAgICAgICAgICAgYXBpLnVwbG9hZE9wdGlvbnMua2V5R2VuZXJhdG9yID8/XG4gICAgICAgICAgICAgICAgdGhpcy5jb25maWcuc2VydmVyLnN0b3JhZ2U/LmtleUdlbmVyYXRvciA/P1xuICAgICAgICAgICAgICAgIGRlZmF1bHRLZXlHZW5lcmF0b3I7XG5cbiAgICAgICAgICAgICAgZm9yIGF3YWl0IChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgICAgICAgICAgICAgaWYgKHBhcnQudHlwZSA9PT0gXCJmaWxlXCIpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGtleSA9IGF3YWl0IGtleUdlbmVyYXRvcih7XG4gICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lOiBwYXJ0LmZpbGVuYW1lLFxuICAgICAgICAgICAgICAgICAgICBtaW1ldHlwZTogcGFydC5taW1ldHlwZSxcbiAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICBhd2FpdCBkaXNrLnB1dFN0cmVhbShrZXksIHBhcnQuZmlsZSwge1xuICAgICAgICAgICAgICAgICAgICBjb250ZW50VHlwZTogcGFydC5taW1ldHlwZSxcbiAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICBjb25zdCB1cmwgPSBhd2FpdCBkaXNrLmdldFVybChrZXkpO1xuICAgICAgICAgICAgICAgICAgY29uc3Qgc2lnbmVkVXJsID0gYXdhaXQgZGlzay5nZXRTaWduZWRVcmwoa2V5KTtcblxuICAgICAgICAgICAgICAgICAgZmlsZXMudXBsb2FkZWRGaWxlcy5wdXNoKFxuICAgICAgICAgICAgICAgICAgICBuZXcgVXBsb2FkZWRGaWxlKHtcbiAgICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZTogcGFydC5maWxlbmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICBtaW1ldHlwZTogcGFydC5taW1ldHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICBzaXplOiBwYXJ0LmZpbGUuYnl0ZXNSZWFkLFxuICAgICAgICAgICAgICAgICAgICAgIHVybCxcbiAgICAgICAgICAgICAgICAgICAgICBzaWduZWRVcmwsXG4gICAgICAgICAgICAgICAgICAgICAga2V5LFxuICAgICAgICAgICAgICAgICAgICAgIGRpc2tOYW1lLFxuICAgICAgICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChwYXJ0LnR5cGUgPT09IFwiZmllbGRcIikge1xuICAgICAgICAgICAgICAgICAgZmllbGRzW3BhcnQuZmllbGRuYW1lXSA9IFN0cmluZyhwYXJ0LnZhbHVlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gcXProZwg7KSR7LKpIOq1rOyhsCDtjIzsi7E6IHBhcmFtc1tjYXRlZ29yeV0g4oaSIHsgcGFyYW1zOiB7IGNhdGVnb3J5OiBcInRlc3RcIiB9IH1cbiAgICAgICAgICAgIGNvbnN0IHFzID0gYXdhaXQgaW1wb3J0KFwicXNcIik7XG4gICAgICAgICAgICBjb25zdCBwYXJzZWQgPSBxcy5kZWZhdWx0LnBhcnNlKGZpZWxkcyk7XG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKGJvZHksIHBhcnNlZCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgeyBmYXN0aWZ5Q2FzdGVyIH0gPSBhd2FpdCBpbXBvcnQoXCIuL2Nhc3RlclwiKTtcbiAgICAgICAgICByZXFCb2R5ID0gZmFzdGlmeUNhc3RlcihSZXFUeXBlKS5wYXJzZShib2R5KTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgIGNvbnN0IHsgWm9kRXJyb3IgfSA9IGF3YWl0IGltcG9ydChcInpvZFwiKTtcbiAgICAgICAgICBpZiAoZSBpbnN0YW5jZW9mIFpvZEVycm9yKSB7XG4gICAgICAgICAgICBjb25zdCB7IGh1bWFuaXplWm9kRXJyb3IgfSA9IGF3YWl0IGltcG9ydChcIi4uL3V0aWxzL3pvZC1lcnJvclwiKTtcbiAgICAgICAgICAgIGNvbnN0IG1lc3NhZ2VzID0gaHVtYW5pemVab2RFcnJvcihlKVxuICAgICAgICAgICAgICAubWFwKChpc3N1ZSkgPT4gaXNzdWUubWVzc2FnZSlcbiAgICAgICAgICAgICAgLmpvaW4oXCIgXCIpO1xuICAgICAgICAgICAgY29uc3QgeyBCYWRSZXF1ZXN0RXhjZXB0aW9uIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9leGNlcHRpb25zL3NvLWV4Y2VwdGlvbnNcIik7XG4gICAgICAgICAgICB0aHJvdyBuZXcgQmFkUmVxdWVzdEV4Y2VwdGlvbihtZXNzYWdlcyBhcyBMb2NhbGl6ZWRTdHJpbmcsIHtcbiAgICAgICAgICAgICAgem9kRXJyb3I6IGUsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhyb3cgZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDb250ZW50LVR5cGVcbiAgICAgICAgcmVwbHkudHlwZShhcGkub3B0aW9ucy5jb250ZW50VHlwZSA/PyBcImFwcGxpY2F0aW9uL2pzb25cIik7XG5cbiAgICAgICAgLy8gQ2FjaGUtQ29udHJvbCDtl6TrjZQg7ISk7KCVXG4gICAgICAgIGNvbnN0IGFwaUNhY2hlQ29uZmlnID0gdGhpcy5nZXRBcGlDYWNoZUNvbnRyb2woYXBpLCByZXF1ZXN0LCBjb25maWcpO1xuICAgICAgICBpZiAoYXBpQ2FjaGVDb25maWcpIHtcbiAgICAgICAgICBhcHBseUNhY2hlSGVhZGVycyhyZXBseSwgYXBpQ2FjaGVDb25maWcpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8g7JeF66Gc65OcIOyYteyFmOydtCDsnojripQg6rK97JqwIO2MjOydvCDrjbDsnbTthLDrpbwgQ29udGV4dOyXkCDstpTqsIBcbiAgICAgICAgaWYgKGFwaS51cGxvYWRPcHRpb25zKSB7XG4gICAgICAgICAgY29uc3QgY29uc3VtZSA9IGFwaS51cGxvYWRPcHRpb25zLmNvbnN1bWUgPz8gXCJidWZmZXJcIjtcbiAgICAgICAgICBpZiAoY29uc3VtZSA9PT0gXCJidWZmZXJcIikge1xuICAgICAgICAgICAgY29udGV4dC5idWZmZXJlZEZpbGVzID0gZmlsZXMuYnVmZmVyZWRGaWxlcztcbiAgICAgICAgICB9IGVsc2UgaWYgKGNvbnN1bWUgPT09IFwic3RyZWFtXCIpIHtcbiAgICAgICAgICAgIGNvbnRleHQudXBsb2FkZWRGaWxlcyA9IGZpbGVzLnVwbG9hZGVkRmlsZXM7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8g66qo6424IOuplOyGjOuTnCBhcmdzIOyDneyEse2VmOyXrCDtmLjstpxcbiAgICAgICAgY29uc3QgeyBBcGlQYXJhbVR5cGUgfSA9IGF3YWl0IGltcG9ydChcIi4uL3R5cGVzL3R5cGVzXCIpO1xuICAgICAgICBjb25zdCBhcmdzID0gYXBpLnBhcmFtZXRlcnMubWFwKChwYXJhbSkgPT4ge1xuICAgICAgICAgIC8vIENvbnRleHQg7J247KCd7IWYXG4gICAgICAgICAgaWYgKEFwaVBhcmFtVHlwZS5pc0NvbnRleHQocGFyYW0udHlwZSkpIHtcbiAgICAgICAgICAgIHJldHVybiBjb250ZXh0O1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gcmVxQm9keVtwYXJhbS5uYW1lXTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiB0aGlzLmludm9rZU1vZGVsTWV0aG9kKGFwaSwgYXJncywgcmVwbHkpO1xuICAgICAgfSk7XG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVUkzsl5DshJwgcGF0aCBwYXJhbXPrpbwg7LaU7Lac7ZWp64uI64ukLlxuICAgKiDsmIg6IHBhdHRlcm49XCIvYWRtaW4vY29tcGFuaWVzLzpjb21wYW55SWRcIiwgdXJsPVwiL2FkbWluL2NvbXBhbmllcy8xMjNcIiDihpIgeyBjb21wYW55SWQ6IFwiMTIzXCIgfVxuICAgKi9cbiAgcHJpdmF0ZSBleHRyYWN0UGF0aFBhcmFtcyhwYXR0ZXJuOiBzdHJpbmcsIHVybDogc3RyaW5nKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gICAgY29uc3QgcGF0dGVyblBhcnRzID0gcGF0dGVybi5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuICAgIGNvbnN0IHVybFBhcnRzID0gdGhpcy5nZXRQYXRobmFtZUZyb21VcmwodXJsKS5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuICAgIGNvbnN0IHBhcmFtczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwYXR0ZXJuUGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChwYXR0ZXJuUGFydHNbaV0uc3RhcnRzV2l0aChcIjpcIikpIHtcbiAgICAgICAgcGFyYW1zW3BhdHRlcm5QYXJ0c1tpXS5zbGljZSgxKV0gPSB1cmxQYXJ0c1tpXTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHBhcmFtcztcbiAgfVxuXG4gIHByaXZhdGUgaXNQYXRoUGF0dGVybk1hdGNoKHBhdHRlcm46IHN0cmluZywgdXJsOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICBjb25zdCBwYXR0ZXJuUGFydHMgPSBwYXR0ZXJuLnNwbGl0KFwiL1wiKS5maWx0ZXIoQm9vbGVhbik7XG4gICAgY29uc3QgdXJsUGFydHMgPSB0aGlzLmdldFBhdGhuYW1lRnJvbVVybCh1cmwpLnNwbGl0KFwiL1wiKS5maWx0ZXIoQm9vbGVhbik7XG5cbiAgICBpZiAocGF0dGVyblBhcnRzLmxlbmd0aCAhPT0gdXJsUGFydHMubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwYXR0ZXJuUGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHBhdHRlcm5QYXJ0ID0gcGF0dGVyblBhcnRzW2ldO1xuICAgICAgY29uc3QgdXJsUGFydCA9IHVybFBhcnRzW2ldO1xuICAgICAgaWYgKHBhdHRlcm5QYXJ0LnN0YXJ0c1dpdGgoXCI6XCIpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKHBhdHRlcm5QYXJ0ICE9PSB1cmxQYXJ0KSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0UGF0aG5hbWVGcm9tVXJsKHVybDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdXJsLnNwbGl0KFwiP1wiKVswXTtcbiAgfVxuXG4gIHByaXZhdGUgcmVzb2x2ZVBhdGhXaXRoaW5CYXNlRGlyKGJhc2VEaXI6IHN0cmluZywgaW5wdXRQYXRoOiBzdHJpbmcpOiBzdHJpbmcgfCBudWxsIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgZGVjb2RlZCA9IGRlY29kZVVSSUNvbXBvbmVudChpbnB1dFBhdGgpLnJlcGxhY2UoL1xcXFwvZywgXCIvXCIpO1xuICAgICAgaWYgKGRlY29kZWQuaW5jbHVkZXMoXCJcXDBcIikpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICBjb25zdCByZWxhdGl2ZVBhdGggPSBkZWNvZGVkLnJlcGxhY2UoL15cXC8rLywgXCJcIik7XG4gICAgICBjb25zdCByZXNvbHZlZFBhdGggPSBwYXRoLnJlc29sdmUoYmFzZURpciwgcmVsYXRpdmVQYXRoKTtcbiAgICAgIGNvbnN0IHJlbGF0aXZlRnJvbUJhc2UgPSBwYXRoLnJlbGF0aXZlKGJhc2VEaXIsIHJlc29sdmVkUGF0aCk7XG4gICAgICBpZiAocmVsYXRpdmVGcm9tQmFzZS5zdGFydHNXaXRoKFwiLi5cIikgfHwgcGF0aC5pc0Fic29sdXRlKHJlbGF0aXZlRnJvbUJhc2UpKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc29sdmVkUGF0aDtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBUEkg7J2R64u17JeQIOyggeyaqe2VoCBDYWNoZS1Db250cm9sIOyEpOygleydhCDqsrDsoJXtlanri4jri6QuXG4gICAqIOyasOyEoOyInOychDog6rCc67OEIOyngOyglSA+IGNhY2hlQ29udHJvbEhhbmRsZXJcbiAgICovXG4gIHByaXZhdGUgZ2V0QXBpQ2FjaGVDb250cm9sKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgcmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICApIHtcbiAgICAvLyDrjbDsvZTroIjsnbTthLAg7ISk7KCVIOyasOyEoFxuICAgIGlmIChhcGkub3B0aW9ucy5jYWNoZUNvbnRyb2wpIHtcbiAgICAgIHJldHVybiBhcGkub3B0aW9ucy5jYWNoZUNvbnRyb2w7XG4gICAgfVxuXG4gICAgLy8g7KCE7JetIO2VuOuTpOufrFxuICAgIGlmIChjb25maWcuY2FjaGVDb250cm9sSGFuZGxlcikge1xuICAgICAgY29uc3QgY2FjaGVSZXE6IENhY2hlQ29udHJvbFJlcXVlc3QgPSB7XG4gICAgICAgIHR5cGU6IFwiYXBpXCIsXG4gICAgICAgIHVybDogcmVxdWVzdC51cmwsXG4gICAgICAgIHBhdGg6IHJlcXVlc3Qucm91dGVPcHRpb25zPy51cmwgPz8gcmVxdWVzdC51cmwuc3BsaXQoXCI/XCIpWzBdLFxuICAgICAgICBtZXRob2Q6IHJlcXVlc3QubWV0aG9kLFxuICAgICAgICBhcGksXG4gICAgICB9O1xuICAgICAgY29uc3QgcmVzdWx0ID0gY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIoY2FjaGVSZXEpO1xuICAgICAgaWYgKHJlc3VsdCkgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTU1LsmqkgQVBJIO2YuOy2nCAoSFRUUCDsmKTrsoTtl6Trk5wg7JeG7J20IOyngeygkSDtmLjstpwpXG4gICAqIGNyZWF0ZUFwaUhhbmRsZXLsnZgg66Gc7KeB7J2EIOyerOyCrOyaqe2VmOuQmCwgcmVxdWVzdCDtjIzsi7Eg64yA7IugIHBhcmFtcyDsp4HsoJEg7IKs7JqpXG4gICAqL1xuICBhc3luYyBpbnZva2VBcGlGb3JTU1IoXG4gICAgYXBpOiBFeHRlbmRlZEFwaSxcbiAgICAvLyBveGxpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueSAtLSBTU1Lsl5DshJwg64uk7JaR7ZWcIO2DgOyeheydmCBwYXJhbXPrpbwg67Cb7JWE7JW8IO2VqFxuICAgIHBhcmFtczogYW55W10sXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIC8vIENvbnRleHQg7IOd7ISxICjquLDsobQg66mU7IaM65OcIOyerOyCrOyaqSlcbiAgICBjb25zdCBjb250ZXh0ID0gYXdhaXQgdGhpcy5jcmVhdGVDb250ZXh0KGNvbmZpZywgcmVxdWVzdCwgcmVwbHkpO1xuXG4gICAgcmV0dXJuIHRoaXMuYXN5bmNMb2NhbFN0b3JhZ2UucnVuKHsgY29udGV4dCB9LCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBhcmdzIOyDneyEsTogQ29udGV4dCDtjIzrnbzrr7jthLDripQg7KO87J6FLCDrgpjrqLjsp4DripQgcGFyYW1z7JeQ7IScIOqwgOyguOyYpOq4sFxuICAgICAgY29uc3QgeyBBcGlQYXJhbVR5cGUgfSA9IGF3YWl0IGltcG9ydChcIi4uL3R5cGVzL3R5cGVzXCIpO1xuICAgICAgbGV0IHBhcmFtc0luZGV4ID0gMDtcbiAgICAgIGNvbnN0IGFyZ3MgPSBhcGkucGFyYW1ldGVycy5tYXAoKHBhcmFtKSA9PiB7XG4gICAgICAgIGlmIChBcGlQYXJhbVR5cGUuaXNDb250ZXh0KHBhcmFtLnR5cGUpKSB7XG4gICAgICAgICAgcmV0dXJuIGNvbnRleHQ7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHBhcmFtc1twYXJhbXNJbmRleCsrXTtcbiAgICAgIH0pO1xuXG4gICAgICAvLyDrqqjrjbgg66mU7ISc65OcIO2YuOy2nCAo6riw7KG0IOuplOyEnOuTnCDsnqzsgqzsmqkpXG4gICAgICByZXR1cm4gdGhpcy5pbnZva2VNb2RlbE1ldGhvZChhcGksIGFyZ3MsIHJlcGx5KTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIGludm9rZU1vZGVsTWV0aG9kKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgYXJnczogdW5rbm93bltdLFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIGNvbnN0IG1vZGVsID0gdGhpcy5zeW5jZXIubW9kZWxzW2FwaS5tb2RlbE5hbWVdO1xuICAgIC8vIG94bGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55IC0tIG1vZGVs7J2AIOuqqOuNuCDsnbjsiqTthLTsiqTsnbTrr4DroZwg66mU7ISc65OcIO2YuOy2nCDqsIDriqVcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCAobW9kZWwgYXMgYW55KVthcGkubWV0aG9kTmFtZV0uYXBwbHkobW9kZWwsIGFyZ3MpO1xuICAgIHJlcGx5LnR5cGUoYXBpLm9wdGlvbnMuY29udGVudFR5cGUgPz8gXCJhcHBsaWNhdGlvbi9qc29uXCIpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGFzeW5jIGNyZWF0ZUNvbnRleHQoXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8Q29udGV4dD4ge1xuICAgIC8vIGNyZWF0ZVNTRUZhY3Rvcnkg7ZWo7IiY7JeQIOuvuOumrCByZXF1ZXN07J2YIHNvY2tldOqzvCByZXBseeulvCDrsJTsnbjrlKkuXG4gICAgY29uc3QgeyBjcmVhdGVTU0VGYWN0b3J5IH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9zdHJlYW0vc3NlXCIpO1xuICAgIGNvbnN0IGNyZWF0ZVNTRSA9ICg8VCBleHRlbmRzIFpvZE9iamVjdD4oXG4gICAgICBfcmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsXG4gICAgICBfcmVwbHk6IEZhc3RpZnlSZXBseSxcbiAgICAgIF9ldmVudHM6IFQsXG4gICAgKSA9PiBjcmVhdGVTU0VGYWN0b3J5KF9yZXF1ZXN0LnNvY2tldCwgX3JlcGx5LCBfZXZlbnRzKSkuYmluZChudWxsLCByZXF1ZXN0LCByZXBseSk7XG5cbiAgICAvLyBsb2NhbGUg6rCQ7KeAXG4gICAgY29uc3QgbG9jYWxlID1cbiAgICAgIHRoaXMuZGV0ZWN0TG9jYWxlKHJlcXVlc3QuaGVhZGVyc1tcImFjY2VwdC1sYW5ndWFnZVwiXSwgdGhpcy5jb25maWcuaTE4bi5zdXBwb3J0ZWRMb2NhbGVzKSA/P1xuICAgICAgdGhpcy5jb25maWcuaTE4bi5kZWZhdWx0TG9jYWxlO1xuXG4gICAgLy8gYXV0aCBjb250ZXh0IOy2lOqwgFxuICAgIGNvbnN0IGhlYWRlcnMgPSBjb252ZXJ0RmFzdGlmeUhlYWRlcnNUb1N0YW5kYXJkKHJlcXVlc3QuaGVhZGVycyk7XG4gICAgY29uc3Qgc2Vzc2lvbiA9IChhd2FpdCB0aGlzLl9hdXRoPy5hcGkuZ2V0U2Vzc2lvbih7IGhlYWRlcnMgfSkpID8/IG51bGw7XG5cbiAgICBjb25zdCBjb250ZXh0OiBDb250ZXh0ID0ge1xuICAgICAgLi4uKGF3YWl0IFByb21pc2UucmVzb2x2ZShcbiAgICAgICAgY29uZmlnLmNvbnRleHRQcm92aWRlcihcbiAgICAgICAgICB7XG4gICAgICAgICAgICByZXF1ZXN0LFxuICAgICAgICAgICAgcmVwbHksXG4gICAgICAgICAgICBoZWFkZXJzOiByZXF1ZXN0LmhlYWRlcnMsXG4gICAgICAgICAgICBjcmVhdGVTU0UsXG4gICAgICAgICAgICBuYWl0ZVN0b3JlOiBuZXcgTWFwKCksXG4gICAgICAgICAgICBsb2NhbGUsXG4gICAgICAgICAgICAvLyBhdXRoXG4gICAgICAgICAgICB1c2VyOiBzZXNzaW9uPy51c2VyID8/IG51bGwsXG4gICAgICAgICAgICBzZXNzaW9uOiBzZXNzaW9uPy5zZXNzaW9uID8/IG51bGwsXG4gICAgICAgICAgfSxcbiAgICAgICAgICByZXF1ZXN0LFxuICAgICAgICAgIHJlcGx5LFxuICAgICAgICApLFxuICAgICAgKSksXG4gICAgfTtcbiAgICByZXR1cm4gY29udGV4dDtcbiAgfVxuXG4gIC8qKlxuICAgKiBBY2NlcHQtTGFuZ3VhZ2Ug7Zek642U7JeQ7IScIOyngOybkO2VmOuKlCBsb2NhbGXsnYQg7LC+7Iq164uI64ukLlxuICAgKiBAZXhhbXBsZSBcImtvLUtSLGtvO3E9MC45LGVuO3E9MC44XCIg4oaSIFwia29cIlxuICAgKi9cbiAgcHJpdmF0ZSBkZXRlY3RMb2NhbGUoXG4gICAgYWNjZXB0TGFuZ3VhZ2U6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICBzdXBwb3J0ZWQ6IHN0cmluZ1tdLFxuICApOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAgIGlmICghYWNjZXB0TGFuZ3VhZ2UpIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgICAvLyBBY2NlcHQtTGFuZ3VhZ2U6IGtvLUtSLGtvO3E9MC45LGVuO3E9MC44XG4gICAgY29uc3QgbGFuZ3MgPSBhY2NlcHRMYW5ndWFnZS5zcGxpdChcIixcIikubWFwKChsYW5nKSA9PiB7XG4gICAgICBjb25zdCBbY29kZV0gPSBsYW5nLnNwbGl0KFwiO1wiKTtcbiAgICAgIHJldHVybiBjb2RlLnRyaW0oKS5zcGxpdChcIi1cIilbMF07IC8vIGtvLUtSIOKGkiBrb1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIGxhbmdzLmZpbmQoKGxhbmcpID0+IHN1cHBvcnRlZC5pbmNsdWRlcyhsYW5nKSk7XG4gIH1cblxuICBhc3luYyBzdGFydFdhdGNoZXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgd2F0Y2hQYXRoID0gW3BhdGguam9pbih0aGlzLmFwaVJvb3RQYXRoLCBcInNyY1wiKV07XG5cbiAgICBjb25zdCBjaG9raWRhciA9IChhd2FpdCBpbXBvcnQoXCJjaG9raWRhclwiKSkuZGVmYXVsdDtcbiAgICB0aGlzLndhdGNoZXIgPSBjaG9raWRhci53YXRjaCh3YXRjaFBhdGgsIHtcbiAgICAgIGlnbm9yZWQ6IChwYXRoLCBzdGF0cykgPT5cbiAgICAgICAgISFzdGF0cz8uaXNGaWxlKCkgJiYgIXBhdGguZW5kc1dpdGgoXCIudHNcIikgJiYgIXBhdGguZW5kc1dpdGgoXCIuanNvblwiKSxcbiAgICAgIHBlcnNpc3RlbnQ6IHRydWUsXG4gICAgICBpZ25vcmVJbml0aWFsOiB0cnVlLFxuICAgIH0pO1xuXG4gICAgdGhpcy53YXRjaGVyLm9uKFwiYWxsXCIsIGFzeW5jIChldmVudDogc3RyaW5nLCBmaWxlUGF0aDogc3RyaW5nKSA9PiB7XG4gICAgICBjb25zdCBhYnNvbHV0ZVBhdGggPSBmaWxlUGF0aCBhcyBBYnNvbHV0ZVBhdGg7XG4gICAgICBhc3NlcnQoXG4gICAgICAgIGFic29sdXRlUGF0aC5zdGFydHNXaXRoKHRoaXMuYXBpUm9vdFBhdGgpLFxuICAgICAgICBcIkZpbGUgcGF0aCBpcyBub3Qgd2l0aGluIHRoZSBBUEkgcm9vdCBwYXRoXCIsXG4gICAgICApO1xuXG4gICAgICBpZiAoZXZlbnQgIT09IFwiY2hhbmdlXCIgJiYgZXZlbnQgIT09IFwiYWRkXCIpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICB0cnkge1xuICAgICAgICAvLyBzb25hbXUuY29uZmlnLnRzIOuzgOqyvSDsi5wg7J6s7Iuc7J6RXG4gICAgICAgIGNvbnN0IGlzQ29uZmlnVHMgPSBmaWxlUGF0aCA9PT0gcGF0aC5qb2luKHRoaXMuYXBpUm9vdFBhdGgsIFwic3JjXCIsIFwic29uYW11LmNvbmZpZy50c1wiKTtcblxuICAgICAgICBpZiAoaXNDb25maWdUcykge1xuICAgICAgICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IGZpbGVQYXRoLnJlcGxhY2UodGhpcy5hcGlSb290UGF0aCwgXCJhcGlcIik7XG4gICAgICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBjaGFsay5ib2xkKGBEZXRlY3RlZCgke2V2ZW50fSk6ICR7Y2hhbGsuYmx1ZShyZWxhdGl2ZVBhdGgpfSAtIFJlc3RhcnRpbmcuLi5gKSxcbiAgICAgICAgICApO1xuICAgICAgICAgIHByb2Nlc3Mua2lsbChwcm9jZXNzLnBpZCwgXCJTSUdVU1IyXCIpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IHRoaXMuaGFuZGxlRmlsZUNoYW5nZShldmVudCwgYWJzb2x1dGVQYXRoKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qXG4gICAgIEEgZnVuY3Rpb24gdGhhdCBhdXRvbWF0aWNhbGx5IGhhbmRsZXMgaW5pdCBhbmQgZGVzdHJveSB3aGVuIHVzaW5nIFNvbmFtdSB2aWEgc2NyaXB0cy5cbiAgKi9cbiAgYXN5bmMgcnVuU2NyaXB0KGZuOiAoKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gICAgYXdhaXQgdGhpcy5pbml0KHRydWUsIGZhbHNlLCB1bmRlZmluZWQsIGZhbHNlKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZm4oKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgdGhpcy5kZXN0cm95KCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWdpc3RlclBsdWdpbnMoc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2UsIHBsdWdpbnM6IFNvbmFtdVNlcnZlck9wdGlvbnNbXCJwbHVnaW5zXCJdKSB7XG4gICAgaWYgKCFwbHVnaW5zKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gY29tcHJlc3Mg7ZSM65+s6re47J247J2AIOuLpOuluCDtlIzrn6zqt7jsnbjrs7Tri6Qg66i87KCAIOuTseuhneuQmOyWtOyVvCDtlanri4jri6QuXG4gICAgaWYgKHBsdWdpbnMuY29tcHJlc3MpIHtcbiAgICAgIGNvbnN0IGNvbXByZXNzUGx1Z2luID0gKGF3YWl0IGltcG9ydChcIkBmYXN0aWZ5L2NvbXByZXNzXCIpKS5kZWZhdWx0O1xuICAgICAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSB7XG4gICAgICAgIHRocmVzaG9sZDogMTAyNCxcbiAgICAgICAgZW5jb2RpbmdzOiBbXCJiclwiLCBcImd6aXBcIiwgXCJkZWZsYXRlXCJdIGFzIChcImJyXCIgfCBcImd6aXBcIiB8IFwiZGVmbGF0ZVwiKVtdLFxuICAgICAgfTtcblxuICAgICAgaWYgKHBsdWdpbnMuY29tcHJlc3MgPT09IHRydWUpIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKGNvbXByZXNzUGx1Z2luLCBkZWZhdWx0T3B0aW9ucyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzZXJ2ZXIucmVnaXN0ZXIoY29tcHJlc3NQbHVnaW4sIHtcbiAgICAgICAgICAuLi5kZWZhdWx0T3B0aW9ucyxcbiAgICAgICAgICAuLi5wbHVnaW5zLmNvbXByZXNzLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBwbHVnaW5zTW9kdWxlcyA9IHtcbiAgICAgIGNvcnM6IFwiQGZhc3RpZnkvY29yc1wiLFxuICAgICAgZm9ybWJvZHk6IFwiQGZhc3RpZnkvZm9ybWJvZHlcIixcbiAgICAgIG11bHRpcGFydDogXCJAZmFzdGlmeS9tdWx0aXBhcnRcIixcbiAgICAgIHFzOiBcImZhc3RpZnktcXNcIixcbiAgICAgIHNzZTogXCJmYXN0aWZ5LXNzZS12MlwiLFxuICAgICAgc3RhdGljOiBcIkBmYXN0aWZ5L3N0YXRpY1wiLFxuICAgIH0gYXMgY29uc3Q7XG5cbiAgICBjb25zdCByZWdpc3RlclBsdWdpbiA9IGFzeW5jIDxLIGV4dGVuZHMga2V5b2YgTm9uTnVsbGFibGU8dHlwZW9mIHBsdWdpbnM+PihcbiAgICAgIGtleTogSyxcbiAgICAgIHBsdWdpbk5hbWU6IHN0cmluZyxcbiAgICApID0+IHtcbiAgICAgIGNvbnN0IG9wdGlvbiA9IHBsdWdpbnNba2V5XTtcbiAgICAgIGlmICghb3B0aW9uKSByZXR1cm47XG5cbiAgICAgIGlmIChvcHRpb24gPT09IHRydWUpIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKChhd2FpdCBpbXBvcnQocGx1Z2luTmFtZSkpLmRlZmF1bHQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKChhd2FpdCBpbXBvcnQocGx1Z2luTmFtZSkpLmRlZmF1bHQsIG9wdGlvbik7XG4gICAgICB9XG4gICAgfTtcblxuICAgIGZvciAoY29uc3QgW2tleSwgcGx1Z2luTmFtZV0gb2YgT2JqZWN0LmVudHJpZXMocGx1Z2luc01vZHVsZXMpKSB7XG4gICAgICBhd2FpdCByZWdpc3RlclBsdWdpbihrZXkgYXMga2V5b2YgdHlwZW9mIHBsdWdpbnMsIHBsdWdpbk5hbWUpO1xuICAgIH1cblxuICAgIGlmIChwbHVnaW5zLmN1c3RvbSkge1xuICAgICAgcGx1Z2lucy5jdXN0b20oc2VydmVyKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogYmV0dGVyLWF1dGgg65287Jqw7Yq466W8IOuTseuhne2VqeuLiOuLpC5cbiAgICogL2FwaS9hdXRoLyog6rK966Gc66GcIOyduOymnSBBUEnqsIAg7J6Q64+ZIOuTseuhneuQqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVnaXN0ZXJCZXR0ZXJBdXRoKFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlLFxuICAgIG9wdGlvbnM6IE5vbk51bGxhYmxlPFNvbmFtdVNlcnZlck9wdGlvbnNbXCJhdXRoXCJdPixcbiAgKSB7XG4gICAgaWYgKCFvcHRpb25zKSByZXR1cm47XG5cbiAgICBjb25zdCBiYXNlUGF0aCA9IG9wdGlvbnMuYmFzZVBhdGggPz8gXCIvYXBpL2F1dGhcIjtcblxuICAgIC8vIGJldHRlci1hdXRoIOudvOyasO2KuCDrk7HroZ1cbiAgICBzZXJ2ZXIucm91dGUoe1xuICAgICAgbWV0aG9kOiBbXCJHRVRcIiwgXCJQT1NUXCJdLFxuICAgICAgdXJsOiBgJHtiYXNlUGF0aH0vKmAsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgICAgY29uc3QgdXJsID0gbmV3IFVSTChyZXF1ZXN0LnVybCwgYGh0dHA6Ly8ke3JlcXVlc3QuaGVhZGVycy5ob3N0fWApO1xuICAgICAgICBjb25zdCBoZWFkZXJzID0gY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZChyZXF1ZXN0LmhlYWRlcnMpO1xuICAgICAgICBjb25zdCByZXEgPSBuZXcgUmVxdWVzdCh1cmwudG9TdHJpbmcoKSwge1xuICAgICAgICAgIG1ldGhvZDogcmVxdWVzdC5tZXRob2QsXG4gICAgICAgICAgaGVhZGVycyxcbiAgICAgICAgICAuLi4ocmVxdWVzdC5ib2R5ID8geyBib2R5OiBKU09OLnN0cmluZ2lmeShyZXF1ZXN0LmJvZHkpIH0gOiB7fSksXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5hdXRoLmhhbmRsZXIocmVxKTtcblxuICAgICAgICByZXBseS5zdGF0dXMocmVzcG9uc2Uuc3RhdHVzKTtcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVycy5mb3JFYWNoKCh2YWx1ZTogc3RyaW5nLCBrZXk6IHN0cmluZykgPT4ge1xuICAgICAgICAgIHJlcGx5LmhlYWRlcihrZXksIHZhbHVlKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiByZXBseS5zZW5kKHJlc3BvbnNlLmJvZHkgPyBhd2FpdCByZXNwb25zZS50ZXh0KCkgOiBudWxsKTtcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihg4pyTIGJldHRlci1hdXRoIHJlZ2lzdGVyZWQgYXQgJHtiYXNlUGF0aH0vKmApKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaW5pdGlhbGl6ZUNhY2hlKGNvbmZpZzogQ2FjaGVDb25maWcgfCB1bmRlZmluZWQsIGZvclRlc3Rpbmc6IGJvb2xlYW4pIHtcbiAgICBjb25zdCB7IHNldENhY2hlTWFuYWdlclJlZiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vY2FjaGUvZGVjb3JhdG9yXCIpO1xuXG4gICAgLy8g7YWM7Iqk7Yq4IO2ZmOqyveyXkOyEnCDrqZTrqqjrpqwg65Oc65287J2067KEIOyekOuPmSDsgqzsmqlcbiAgICBpZiAoZm9yVGVzdGluZykge1xuICAgICAgY29uc3QgeyBjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9jYWNoZS9jYWNoZS1tYW5hZ2VyXCIpO1xuICAgICAgdGhpcy5fY2FjaGUgPSBjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyKCk7XG4gICAgICBzZXRDYWNoZU1hbmFnZXJSZWYodGhpcy5fY2FjaGUpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIOyEpOygleydtCDsl4bsnLzrqbQg7LqQ7IucIOu5hO2ZnOyEse2ZlFxuICAgIGlmICghY29uZmlnKSB7XG4gICAgICBzZXRDYWNoZU1hbmFnZXJSZWYobnVsbCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8g7ISk7KCV7JeQIOuUsOudvCBDYWNoZU1hbmFnZXIg7IOd7ISxXG4gICAgY29uc3QgeyBjcmVhdGVDYWNoZU1hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL2NhY2hlL2NhY2hlLW1hbmFnZXJcIik7XG4gICAgdGhpcy5fY2FjaGUgPSBjcmVhdGVDYWNoZU1hbmFnZXIoY29uZmlnKTtcbiAgICBzZXRDYWNoZU1hbmFnZXJSZWYodGhpcy5fY2FjaGUpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBpbml0aWFsaXplV29ya2Zsb3dzKG9wdGlvbnM6IFNvbmFtdVRhc2tPcHRpb25zIHwgdW5kZWZpbmVkKSB7XG4gICAgY29uc3QgeyBXb3JrZmxvd01hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3Rhc2tzL3dvcmtmbG93LW1hbmFnZXJcIik7XG4gICAgLy8gTk9URTogQHNvbmFtdS1raXQvdGFza3Mg7JWI7JeQ7ISgIGtuZXggY29uZmln66W8IOyImOygle2VmOq4sCDrlYzrrLjsl5AgY29ubmVjdGlvbuydtCDslYTri4wgY29uZmlnIOynuOuhnCDrs7Trg4Xri4jri6QuXG4gICAgdGhpcy5fd29ya2Zsb3dzID0gbmV3IFdvcmtmbG93TWFuYWdlcihEQi5nZXREQkNvbmZpZyhcIndcIikpO1xuICAgIGlmICghb3B0aW9ucykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGVuYWJsZVdvcmtlciA9IG9wdGlvbnMuZW5hYmxlV29ya2VyID8/IGlzRGFlbW9uU2VydmVyKCk7XG4gICAgY29uc3QgZGVmYXVsdFdvcmtlck9wdGlvbnMgPSB7XG4gICAgICBjb25jdXJyZW5jeTogb3MuY3B1cygpLmxlbmd0aCAtIDEsXG4gICAgICB1c2VQdWJTdWI6IHRydWUsXG4gICAgICBsaXN0ZW5EZWxheTogNTAwLFxuICAgIH07XG5cbiAgICBpZiAoZW5hYmxlV29ya2VyKSB7XG4gICAgICB0aGlzLndvcmtmbG93cy5zZXR1cFdvcmtlcih7XG4gICAgICAgIC4uLmRlZmF1bHRXb3JrZXJPcHRpb25zLFxuICAgICAgICAuLi5vcHRpb25zLndvcmtlck9wdGlvbnMsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGJvb3Qoc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2UsIG9wdGlvbnM6IFNvbmFtdVNlcnZlck9wdGlvbnMpIHtcbiAgICBjb25zdCBwb3J0ID0gb3B0aW9ucy5saXN0ZW4/LnBvcnQgPz8gMzAwMDtcbiAgICBjb25zdCBob3N0ID0gb3B0aW9ucy5saXN0ZW4/Lmhvc3QgPz8gXCJsb2NhbGhvc3RcIjtcblxuICAgIHNlcnZlci5hZGRIb29rKFwib25DbG9zZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCBvcHRpb25zLmxpZmVjeWNsZT8ub25TaHV0ZG93bj8uKHNlcnZlcik7XG4gICAgICBhd2FpdCB0aGlzLndvcmtmbG93cy5kZXN0cm95KCk7XG4gICAgICBhd2FpdCB0aGlzLmRlc3Ryb3koKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IHNodXRkb3duID0gYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgc2VydmVyLmNsb3NlKCk7XG4gICAgICAgIHByb2Nlc3MuZXhpdCgwKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3IgZHVyaW5nIHNodXRkb3duOlwiLCBlcnIpO1xuICAgICAgICBwcm9jZXNzLmV4aXQoMSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHByb2Nlc3Mub24oXCJTSUdJTlRcIiwgc2h1dGRvd24pO1xuICAgIHByb2Nlc3Mub24oXCJTSUdURVJNXCIsIHNodXRkb3duKTtcblxuICAgIGlmIChvcHRpb25zLmxpZmVjeWNsZT8ub25FcnJvcikge1xuICAgICAgc2VydmVyLnNldEVycm9ySGFuZGxlcihvcHRpb25zLmxpZmVjeWNsZT8ub25FcnJvcik7XG4gICAgfVxuXG4gICAgc2VydmVyXG4gICAgICAubGlzdGVuKHsgcG9ydCwgaG9zdCB9KVxuICAgICAgLnRoZW4oYXN5bmMgKCkgPT4ge1xuICAgICAgICBhd2FpdCB0aGlzLndvcmtmbG93cy5zdGFydFdvcmtlcigpO1xuICAgICAgICBhd2FpdCBvcHRpb25zLmxpZmVjeWNsZT8ub25TdGFydD8uKHNlcnZlcik7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKGFzeW5jIChlcnIpID0+IHtcbiAgICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoY2hhbGsucmVkKFwiRmFpbGVkIHRvIHN0YXJ0IHNlcnZlcjpcIiwgZXJyKSk7XG4gICAgICAgIGF3YWl0IHNodXRkb3duKCk7XG4gICAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlRmlsZUNoYW5nZShldmVudDogc3RyaW5nLCBmaWxlUGF0aDogQWJzb2x1dGVQYXRoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8g7LKrIOuyiOynuCDtjIzsnbzsnbTrqbQgSE1SIOyLnOyekSDsi5zqsIQg6riw66GdXG4gICAgaWYgKHRoaXMucGVuZGluZ0ZpbGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhpcy5obXJTdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIH1cbiAgICB0aGlzLnBlbmRpbmdGaWxlcy5wdXNoKGZpbGVQYXRoKTtcblxuICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHBhdGgucmVsYXRpdmUodGhpcy5hcGlSb290UGF0aCwgZmlsZVBhdGgpO1xuICAgIGNvbnN0IGNoYWxrID0gKGF3YWl0IGltcG9ydChcImNoYWxrXCIpKS5kZWZhdWx0O1xuICAgIGNvbnNvbGUubG9nKGNoYWxrLmJvbGQoYERldGVjdGVkKCR7ZXZlbnR9KTogJHtjaGFsay5ibHVlKHJlbGF0aXZlUGF0aCl9YCkpO1xuXG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuc3luY0Zyb21XYXRjaGVyKGV2ZW50LCBmaWxlUGF0aCk7XG5cbiAgICAvLyDsspjrpqwg7JmE66OM65CcIO2MjOydvOydhCDrjIDquLAg66qp66Gd7JeQ7IScIOygnOqxsFxuICAgIHRoaXMucGVuZGluZ0ZpbGVzID0gdGhpcy5wZW5kaW5nRmlsZXMuc2xpY2UoMSk7XG5cbiAgICAvLyDrqqjrk6Ag7YyM7J28IOyymOumrOqwgCDsmYTro4zrkJjrqbQg7LWc7KKFIOuplOyLnOyngCDstpzroKVcbiAgICBpZiAodGhpcy5wZW5kaW5nRmlsZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBhd2FpdCB0aGlzLmZpbmlzaEhNUigpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmluaXNoSE1SKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLnJlbmV3Q2hlY2tzdW1zKCk7XG5cbiAgICBjb25zdCBlbmRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCB0b3RhbFRpbWUgPSBlbmRUaW1lIC0gdGhpcy5obXJTdGFydFRpbWU7XG4gICAgY29uc3QgW2NoYWxrLCB7IGNlbnRlclRleHQgfV0gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQsXG4gICAgICBpbXBvcnQoXCIuLi91dGlscy9jb25zb2xlLXV0aWxcIiksXG4gICAgXSk7XG4gICAgY29uc3QgbXNnID0gYEhNUiBEb25lISAke2NoYWxrLmJvbGQud2hpdGUoYCR7dG90YWxUaW1lfW1zYCl9YDtcblxuICAgIGNvbnNvbGUubG9nKGNoYWxrLmJsYWNrLmJnR3JlZW4oY2VudGVyVGV4dChtc2cpKSk7XG4gIH1cblxuICBhc3luYyBkZXN0cm95KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgQmFzZU1vZGVsIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9kYXRhYmFzZS9iYXNlLW1vZGVsXCIpO1xuICAgIC8vIOuovOyggCDsspjrpqztlbTslbztlaguXG4gICAgYXdhaXQgQmFzZU1vZGVsLmRlc3Ryb3koKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW1xuICAgICAgdGhpcy5fd29ya2Zsb3dzPy5kZXN0cm95KCkgPz8gUHJvbWlzZS5yZXNvbHZlKCksXG4gICAgICB0aGlzLl9jYWNoZT8uZGlzY29ubmVjdCgpID8/IFByb21pc2UucmVzb2x2ZSgpLFxuICAgICAgdGhpcy5fZGV2Vml0ZXN0TWFuYWdlcj8uc2h1dGRvd24oKSA/PyBQcm9taXNlLnJlc29sdmUoKSxcbiAgICAgIHRoaXMud2F0Y2hlcj8uY2xvc2UoKSA/PyBQcm9taXNlLnJlc29sdmUoKSxcbiAgICAgIGxvZ3RhcGVEaXNwb3NlKCksXG4gICAgXSk7XG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IFNvbmFtdSA9IG5ldyBTb25hbXVDbGFzcygpO1xuXG4vKipcbiAqIHN0cmVhbSDrqqjrk5zsl5DshJwg7YKkIOyDneyEsSDtlajsiJjqsIAg7KeA7KCV65CY7KeAIOyViuyVmOydhCDrlYwg7IKs7Jqp7ZWY64qUIOq4sOuzuCDtlajsiJjsnoXri4jri6QuXG4gKi9cbmZ1bmN0aW9uIGRlZmF1bHRLZXlHZW5lcmF0b3IoZmlsZTogeyBmaWxlbmFtZTogc3RyaW5nOyBtaW1ldHlwZTogc3RyaW5nIH0pOiBzdHJpbmcge1xuICBjb25zdCBleHQgPSBtaW1lLmV4dGVuc2lvbihmaWxlLm1pbWV0eXBlKSB8fCBcImJpblwiO1xuICBjb25zdCB0aW1lc3RhbXAgPSBEYXRlLm5vdygpO1xuICBjb25zdCByYW5kb20gPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zbGljZSgyLCA4KTtcbiAgcmV0dXJuIGB1cGxvYWRzLyR7dGltZXN0YW1wfS0ke3JhbmRvbX0uJHtleHR9YDtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUErNENBLFNBQVMsb0JBQW9CLE1BQXNEO0NBQ2pGLE1BQU0sTUFBTSxLQUFLLFVBQVUsS0FBSyxTQUFTLElBQUk7Q0FDN0MsTUFBTSxZQUFZLEtBQUssS0FBSztDQUM1QixNQUFNLFNBQVMsS0FBSyxRQUFRLENBQUMsU0FBUyxHQUFHLENBQUMsTUFBTSxHQUFHLEVBQUU7QUFDckQsUUFBTyxXQUFXLFVBQVUsR0FBRyxPQUFPLEdBQUc7Ozs7NEJBcjRDd0I7cUJBQ2M7Z0JBR2xCO1VBRTNCO1VBRVM7cUJBRW1CO3FCQUNSO3FCQUdBO1dBQ0g7a0JBS0E7Z0JBQ0U7YUFFaUI7Y0FJbEM7Q0FHaEMsY0FBTixNQUFrQjtFQUNoQixBQUFPLGdCQUF5QjtFQUNoQyxBQUFPLGFBQXNCO0VBQzdCLEFBQU8sb0JBRUYsSUFBSSxtQkFBbUI7RUFFNUIsQUFBTyxhQUFzQjtHQUMzQixNQUFNLFFBQVEsS0FBSyxrQkFBa0IsVUFBVTtBQUMvQyxPQUFJLE9BQU8sU0FBUztBQUNsQixXQUFPLE1BQU07O0FBR2YsT0FBSSxRQUFRLElBQUksYUFBYSxRQUFRO0FBRW5DLFdBQU87S0FDTCxTQUFTO0tBQ1QsT0FBTztLQUNQLFNB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