@push.rocks/smartproxy 19.3.2 → 19.3.3

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 (313) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/forwarding/factory/forwarding-factory.js +29 -1
  3. package/dist_ts/http/index.d.ts +1 -3
  4. package/dist_ts/http/index.js +4 -10
  5. package/dist_ts/http/models/http-types.d.ts +4 -91
  6. package/dist_ts/http/models/http-types.js +5 -60
  7. package/dist_ts/http/router/proxy-router.d.ts +1 -1
  8. package/dist_ts/http/router/route-router.d.ts +1 -1
  9. package/dist_ts/index.d.ts +9 -7
  10. package/dist_ts/index.js +10 -7
  11. package/dist_ts/proxies/{network-proxy → http-proxy}/certificate-manager.d.ts +2 -2
  12. package/dist_ts/proxies/{network-proxy → http-proxy}/certificate-manager.js +1 -1
  13. package/dist_ts/proxies/{network-proxy → http-proxy}/connection-pool.d.ts +2 -2
  14. package/dist_ts/proxies/http-proxy/connection-pool.js +210 -0
  15. package/dist_ts/proxies/http-proxy/context-creator.js +108 -0
  16. package/dist_ts/proxies/{network-proxy → http-proxy}/function-cache.js +1 -1
  17. package/dist_ts/proxies/http-proxy/handlers/index.d.ts +5 -0
  18. package/dist_ts/proxies/http-proxy/handlers/index.js +6 -0
  19. package/dist_ts/proxies/http-proxy/handlers/redirect-handler.d.ts +18 -0
  20. package/dist_ts/proxies/http-proxy/handlers/redirect-handler.js +78 -0
  21. package/dist_ts/proxies/http-proxy/handlers/static-handler.d.ts +19 -0
  22. package/dist_ts/proxies/http-proxy/handlers/static-handler.js +203 -0
  23. package/dist_ts/proxies/{network-proxy/network-proxy.d.ts → http-proxy/http-proxy.d.ts} +10 -9
  24. package/dist_ts/proxies/{network-proxy/network-proxy.js → http-proxy/http-proxy.js} +13 -12
  25. package/dist_ts/proxies/{network-proxy → http-proxy}/http-request-handler.js +1 -1
  26. package/dist_ts/proxies/http-proxy/http2-request-handler.js +201 -0
  27. package/dist_ts/proxies/{network-proxy → http-proxy}/index.d.ts +2 -2
  28. package/dist_ts/proxies/http-proxy/index.js +12 -0
  29. package/dist_ts/proxies/http-proxy/models/http-types.d.ts +119 -0
  30. package/dist_ts/proxies/http-proxy/models/http-types.js +112 -0
  31. package/dist_ts/proxies/http-proxy/models/index.d.ts +5 -0
  32. package/dist_ts/proxies/http-proxy/models/index.js +6 -0
  33. package/dist_ts/proxies/{network-proxy → http-proxy}/models/types.d.ts +2 -2
  34. package/dist_ts/proxies/http-proxy/models/types.js +276 -0
  35. package/dist_ts/proxies/{network-proxy → http-proxy}/request-handler.d.ts +3 -3
  36. package/dist_ts/proxies/{network-proxy → http-proxy}/request-handler.js +2 -2
  37. package/dist_ts/proxies/http-proxy/security-manager.js +255 -0
  38. package/dist_ts/proxies/{network-proxy → http-proxy}/websocket-handler.d.ts +3 -3
  39. package/dist_ts/proxies/{network-proxy → http-proxy}/websocket-handler.js +2 -2
  40. package/dist_ts/proxies/index.d.ts +5 -5
  41. package/dist_ts/proxies/index.js +5 -5
  42. package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +4 -4
  43. package/dist_ts/proxies/smart-proxy/certificate-manager.js +11 -11
  44. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +41 -0
  45. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +121 -0
  46. package/dist_ts/proxies/smart-proxy/index.d.ts +2 -1
  47. package/dist_ts/proxies/smart-proxy/index.js +4 -2
  48. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +2 -2
  49. package/dist_ts/proxies/smart-proxy/port-manager.js +3 -3
  50. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +3 -3
  51. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +24 -265
  52. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
  53. package/dist_ts/proxies/smart-proxy/smart-proxy.js +25 -25
  54. package/dist_ts/routing/index.d.ts +5 -0
  55. package/dist_ts/routing/index.js +8 -0
  56. package/dist_ts/routing/models/http-types.d.ts +6 -0
  57. package/dist_ts/routing/models/http-types.js +7 -0
  58. package/dist_ts/routing/router/index.d.ts +8 -0
  59. package/dist_ts/routing/router/index.js +7 -0
  60. package/dist_ts/{classes.router.d.ts → routing/router/proxy-router.d.ts} +14 -11
  61. package/dist_ts/{classes.router.js → routing/router/proxy-router.js} +2 -2
  62. package/dist_ts/routing/router/route-router.d.ts +108 -0
  63. package/dist_ts/routing/router/route-router.js +393 -0
  64. package/package.json +1 -1
  65. package/readme.md +12 -12
  66. package/readme.plan.md +152 -257
  67. package/ts/00_commitinfo_data.ts +1 -1
  68. package/ts/forwarding/factory/forwarding-factory.ts +28 -0
  69. package/ts/index.ts +13 -9
  70. package/ts/proxies/{network-proxy → http-proxy}/certificate-manager.ts +2 -2
  71. package/ts/proxies/{network-proxy → http-proxy}/connection-pool.ts +2 -2
  72. package/ts/proxies/http-proxy/handlers/index.ts +6 -0
  73. package/ts/proxies/http-proxy/handlers/redirect-handler.ts +105 -0
  74. package/ts/proxies/http-proxy/handlers/static-handler.ts +251 -0
  75. package/ts/proxies/{network-proxy/network-proxy.ts → http-proxy/http-proxy.ts} +15 -14
  76. package/ts/proxies/{network-proxy → http-proxy}/index.ts +3 -3
  77. package/ts/proxies/http-proxy/models/http-types.ts +165 -0
  78. package/ts/proxies/http-proxy/models/index.ts +5 -0
  79. package/ts/proxies/{network-proxy → http-proxy}/models/types.ts +2 -2
  80. package/ts/proxies/{network-proxy → http-proxy}/request-handler.ts +3 -3
  81. package/ts/proxies/{network-proxy → http-proxy}/websocket-handler.ts +3 -3
  82. package/ts/proxies/index.ts +7 -7
  83. package/ts/proxies/smart-proxy/certificate-manager.ts +10 -10
  84. package/ts/proxies/smart-proxy/{network-proxy-bridge.ts → http-proxy-bridge.ts} +44 -44
  85. package/ts/proxies/smart-proxy/index.ts +4 -1
  86. package/ts/proxies/smart-proxy/models/interfaces.ts +3 -3
  87. package/ts/proxies/smart-proxy/port-manager.ts +2 -2
  88. package/ts/proxies/smart-proxy/route-connection-handler.ts +23 -307
  89. package/ts/proxies/smart-proxy/smart-proxy.ts +25 -25
  90. package/ts/routing/index.ts +9 -0
  91. package/ts/routing/models/http-types.ts +6 -0
  92. package/ts/{http → routing}/router/proxy-router.ts +1 -1
  93. package/ts/{http → routing}/router/route-router.ts +1 -1
  94. package/dist_ts/certificate/acme/acme-factory.d.ts +0 -17
  95. package/dist_ts/certificate/acme/acme-factory.js +0 -40
  96. package/dist_ts/certificate/acme/challenge-handler.d.ts +0 -44
  97. package/dist_ts/certificate/acme/challenge-handler.js +0 -92
  98. package/dist_ts/certificate/acme/index.d.ts +0 -4
  99. package/dist_ts/certificate/acme/index.js +0 -5
  100. package/dist_ts/certificate/certificate-manager.d.ts +0 -150
  101. package/dist_ts/certificate/certificate-manager.js +0 -505
  102. package/dist_ts/certificate/events/certificate-events.d.ts +0 -33
  103. package/dist_ts/certificate/events/certificate-events.js +0 -38
  104. package/dist_ts/certificate/events/simplified-events.d.ts +0 -56
  105. package/dist_ts/certificate/events/simplified-events.js +0 -13
  106. package/dist_ts/certificate/index.d.ts +0 -30
  107. package/dist_ts/certificate/index.js +0 -37
  108. package/dist_ts/certificate/models/certificate-errors.d.ts +0 -69
  109. package/dist_ts/certificate/models/certificate-errors.js +0 -141
  110. package/dist_ts/certificate/models/certificate-strategy.d.ts +0 -60
  111. package/dist_ts/certificate/models/certificate-strategy.js +0 -73
  112. package/dist_ts/certificate/models/certificate-types.d.ts +0 -97
  113. package/dist_ts/certificate/models/certificate-types.js +0 -2
  114. package/dist_ts/certificate/providers/cert-provisioner.d.ts +0 -119
  115. package/dist_ts/certificate/providers/cert-provisioner.js +0 -422
  116. package/dist_ts/certificate/providers/index.d.ts +0 -4
  117. package/dist_ts/certificate/providers/index.js +0 -5
  118. package/dist_ts/certificate/simplified-certificate-manager.d.ts +0 -150
  119. package/dist_ts/certificate/simplified-certificate-manager.js +0 -501
  120. package/dist_ts/certificate/storage/file-storage.d.ts +0 -66
  121. package/dist_ts/certificate/storage/file-storage.js +0 -194
  122. package/dist_ts/certificate/storage/index.d.ts +0 -4
  123. package/dist_ts/certificate/storage/index.js +0 -5
  124. package/dist_ts/certificate/utils/certificate-helpers.d.ts +0 -17
  125. package/dist_ts/certificate/utils/certificate-helpers.js +0 -45
  126. package/dist_ts/classes.iptablesproxy.d.ts +0 -112
  127. package/dist_ts/classes.iptablesproxy.js +0 -765
  128. package/dist_ts/classes.networkproxy.d.ts +0 -243
  129. package/dist_ts/classes.networkproxy.js +0 -1424
  130. package/dist_ts/classes.nftablesproxy.d.ts +0 -219
  131. package/dist_ts/classes.nftablesproxy.js +0 -1542
  132. package/dist_ts/classes.port80handler.d.ts +0 -215
  133. package/dist_ts/classes.port80handler.js +0 -736
  134. package/dist_ts/classes.portproxy.d.ts +0 -171
  135. package/dist_ts/classes.portproxy.js +0 -1802
  136. package/dist_ts/classes.pp.acmemanager.d.ts +0 -34
  137. package/dist_ts/classes.pp.acmemanager.js +0 -123
  138. package/dist_ts/classes.pp.connectionhandler.d.ts +0 -39
  139. package/dist_ts/classes.pp.connectionhandler.js +0 -754
  140. package/dist_ts/classes.pp.connectionmanager.d.ts +0 -78
  141. package/dist_ts/classes.pp.connectionmanager.js +0 -378
  142. package/dist_ts/classes.pp.domainconfigmanager.d.ts +0 -55
  143. package/dist_ts/classes.pp.domainconfigmanager.js +0 -103
  144. package/dist_ts/classes.pp.interfaces.d.ts +0 -133
  145. package/dist_ts/classes.pp.interfaces.js +0 -2
  146. package/dist_ts/classes.pp.networkproxybridge.d.ts +0 -57
  147. package/dist_ts/classes.pp.networkproxybridge.js +0 -306
  148. package/dist_ts/classes.pp.portproxy.d.ts +0 -64
  149. package/dist_ts/classes.pp.portproxy.js +0 -567
  150. package/dist_ts/classes.pp.portrangemanager.d.ts +0 -56
  151. package/dist_ts/classes.pp.portrangemanager.js +0 -179
  152. package/dist_ts/classes.pp.securitymanager.d.ts +0 -47
  153. package/dist_ts/classes.pp.securitymanager.js +0 -126
  154. package/dist_ts/classes.pp.snihandler.d.ts +0 -153
  155. package/dist_ts/classes.pp.snihandler.js +0 -1053
  156. package/dist_ts/classes.pp.timeoutmanager.d.ts +0 -47
  157. package/dist_ts/classes.pp.timeoutmanager.js +0 -154
  158. package/dist_ts/classes.pp.tlsalert.d.ts +0 -149
  159. package/dist_ts/classes.pp.tlsalert.js +0 -225
  160. package/dist_ts/classes.pp.tlsmanager.d.ts +0 -57
  161. package/dist_ts/classes.pp.tlsmanager.js +0 -132
  162. package/dist_ts/classes.snihandler.d.ts +0 -198
  163. package/dist_ts/classes.snihandler.js +0 -1210
  164. package/dist_ts/classes.sslredirect.d.ts +0 -8
  165. package/dist_ts/classes.sslredirect.js +0 -28
  166. package/dist_ts/common/acmeFactory.d.ts +0 -9
  167. package/dist_ts/common/acmeFactory.js +0 -20
  168. package/dist_ts/common/port80-adapter.d.ts +0 -11
  169. package/dist_ts/common/port80-adapter.js +0 -87
  170. package/dist_ts/examples/forwarding-example.d.ts +0 -1
  171. package/dist_ts/examples/forwarding-example.js +0 -96
  172. package/dist_ts/forwarding/config/domain-config.d.ts +0 -12
  173. package/dist_ts/forwarding/config/domain-config.js +0 -12
  174. package/dist_ts/forwarding/config/domain-manager.d.ts +0 -86
  175. package/dist_ts/forwarding/config/domain-manager.js +0 -242
  176. package/dist_ts/helpers.certificates.d.ts +0 -5
  177. package/dist_ts/helpers.certificates.js +0 -23
  178. package/dist_ts/http/port80/acme-interfaces.d.ts +0 -108
  179. package/dist_ts/http/port80/acme-interfaces.js +0 -51
  180. package/dist_ts/http/port80/challenge-responder.d.ts +0 -53
  181. package/dist_ts/http/port80/challenge-responder.js +0 -203
  182. package/dist_ts/http/port80/index.d.ts +0 -6
  183. package/dist_ts/http/port80/index.js +0 -9
  184. package/dist_ts/http/port80/port80-handler.d.ts +0 -136
  185. package/dist_ts/http/port80/port80-handler.js +0 -592
  186. package/dist_ts/http/redirects/index.d.ts +0 -4
  187. package/dist_ts/http/redirects/index.js +0 -5
  188. package/dist_ts/networkproxy/classes.np.certificatemanager.d.ts +0 -77
  189. package/dist_ts/networkproxy/classes.np.certificatemanager.js +0 -372
  190. package/dist_ts/networkproxy/classes.np.connectionpool.d.ts +0 -47
  191. package/dist_ts/networkproxy/classes.np.connectionpool.js +0 -210
  192. package/dist_ts/networkproxy/classes.np.networkproxy.d.ts +0 -118
  193. package/dist_ts/networkproxy/classes.np.networkproxy.js +0 -387
  194. package/dist_ts/networkproxy/classes.np.requesthandler.d.ts +0 -56
  195. package/dist_ts/networkproxy/classes.np.requesthandler.js +0 -393
  196. package/dist_ts/networkproxy/classes.np.types.d.ts +0 -83
  197. package/dist_ts/networkproxy/classes.np.types.js +0 -35
  198. package/dist_ts/networkproxy/classes.np.websockethandler.d.ts +0 -38
  199. package/dist_ts/networkproxy/classes.np.websockethandler.js +0 -188
  200. package/dist_ts/networkproxy/index.d.ts +0 -1
  201. package/dist_ts/networkproxy/index.js +0 -4
  202. package/dist_ts/nfttablesproxy/classes.nftablesproxy.d.ts +0 -219
  203. package/dist_ts/nfttablesproxy/classes.nftablesproxy.js +0 -1542
  204. package/dist_ts/port80handler/classes.port80handler.d.ts +0 -10
  205. package/dist_ts/port80handler/classes.port80handler.js +0 -16
  206. package/dist_ts/proxies/network-proxy/connection-pool.js +0 -210
  207. package/dist_ts/proxies/network-proxy/context-creator.js +0 -108
  208. package/dist_ts/proxies/network-proxy/http2-request-handler.js +0 -201
  209. package/dist_ts/proxies/network-proxy/index.js +0 -12
  210. package/dist_ts/proxies/network-proxy/models/index.d.ts +0 -4
  211. package/dist_ts/proxies/network-proxy/models/index.js +0 -5
  212. package/dist_ts/proxies/network-proxy/models/types.js +0 -276
  213. package/dist_ts/proxies/network-proxy/security-manager.js +0 -255
  214. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.d.ts +0 -48
  215. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.js +0 -76
  216. package/dist_ts/proxies/smart-proxy/connection-handler.d.ts +0 -39
  217. package/dist_ts/proxies/smart-proxy/connection-handler.js +0 -894
  218. package/dist_ts/proxies/smart-proxy/domain-config-manager.d.ts +0 -110
  219. package/dist_ts/proxies/smart-proxy/domain-config-manager.js +0 -386
  220. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.d.ts +0 -168
  221. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.js +0 -642
  222. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.d.ts +0 -65
  223. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.js +0 -31
  224. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.d.ts +0 -102
  225. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.js +0 -73
  226. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +0 -41
  227. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +0 -121
  228. package/dist_ts/proxies/smart-proxy/port-range-manager.d.ts +0 -56
  229. package/dist_ts/proxies/smart-proxy/port-range-manager.js +0 -176
  230. package/dist_ts/proxies/smart-proxy/route-helpers/index.d.ts +0 -9
  231. package/dist_ts/proxies/smart-proxy/route-helpers/index.js +0 -11
  232. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +0 -7
  233. package/dist_ts/proxies/smart-proxy/route-helpers.js +0 -9
  234. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.d.ts +0 -41
  235. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.js +0 -132
  236. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.d.ts +0 -51
  237. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.js +0 -124
  238. package/dist_ts/redirect/classes.redirect.d.ts +0 -96
  239. package/dist_ts/redirect/classes.redirect.js +0 -194
  240. package/dist_ts/smartproxy/classes.pp.certprovisioner.d.ts +0 -54
  241. package/dist_ts/smartproxy/classes.pp.certprovisioner.js +0 -179
  242. package/dist_ts/smartproxy/classes.pp.connectionhandler.d.ts +0 -39
  243. package/dist_ts/smartproxy/classes.pp.connectionhandler.js +0 -894
  244. package/dist_ts/smartproxy/classes.pp.connectionmanager.d.ts +0 -78
  245. package/dist_ts/smartproxy/classes.pp.connectionmanager.js +0 -378
  246. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +0 -94
  247. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +0 -255
  248. package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +0 -103
  249. package/dist_ts/smartproxy/classes.pp.interfaces.js +0 -2
  250. package/dist_ts/smartproxy/classes.pp.networkproxybridge.d.ts +0 -62
  251. package/dist_ts/smartproxy/classes.pp.networkproxybridge.js +0 -316
  252. package/dist_ts/smartproxy/classes.pp.portrangemanager.d.ts +0 -56
  253. package/dist_ts/smartproxy/classes.pp.portrangemanager.js +0 -176
  254. package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +0 -64
  255. package/dist_ts/smartproxy/classes.pp.securitymanager.js +0 -149
  256. package/dist_ts/smartproxy/classes.pp.snihandler.d.ts +0 -153
  257. package/dist_ts/smartproxy/classes.pp.snihandler.js +0 -1053
  258. package/dist_ts/smartproxy/classes.pp.timeoutmanager.d.ts +0 -47
  259. package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +0 -154
  260. package/dist_ts/smartproxy/classes.pp.tlsalert.d.ts +0 -149
  261. package/dist_ts/smartproxy/classes.pp.tlsalert.js +0 -225
  262. package/dist_ts/smartproxy/classes.pp.tlsmanager.d.ts +0 -57
  263. package/dist_ts/smartproxy/classes.pp.tlsmanager.js +0 -132
  264. package/dist_ts/smartproxy/classes.smartproxy.d.ts +0 -63
  265. package/dist_ts/smartproxy/classes.smartproxy.js +0 -521
  266. package/dist_ts/smartproxy/forwarding/domain-config.d.ts +0 -12
  267. package/dist_ts/smartproxy/forwarding/domain-config.js +0 -12
  268. package/dist_ts/smartproxy/forwarding/domain-manager.d.ts +0 -86
  269. package/dist_ts/smartproxy/forwarding/domain-manager.js +0 -241
  270. package/dist_ts/smartproxy/forwarding/forwarding.factory.d.ts +0 -24
  271. package/dist_ts/smartproxy/forwarding/forwarding.factory.js +0 -137
  272. package/dist_ts/smartproxy/forwarding/forwarding.handler.d.ts +0 -55
  273. package/dist_ts/smartproxy/forwarding/forwarding.handler.js +0 -94
  274. package/dist_ts/smartproxy/forwarding/http.handler.d.ts +0 -25
  275. package/dist_ts/smartproxy/forwarding/http.handler.js +0 -123
  276. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.d.ts +0 -24
  277. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.js +0 -154
  278. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.d.ts +0 -36
  279. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.js +0 -229
  280. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.d.ts +0 -35
  281. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.js +0 -254
  282. package/dist_ts/smartproxy/forwarding/index.d.ts +0 -16
  283. package/dist_ts/smartproxy/forwarding/index.js +0 -23
  284. package/dist_ts/smartproxy/types/forwarding.types.d.ts +0 -104
  285. package/dist_ts/smartproxy/types/forwarding.types.js +0 -50
  286. package/dist_ts/smartproxy.classes.networkproxy.d.ts +0 -31
  287. package/dist_ts/smartproxy.classes.networkproxy.js +0 -305
  288. package/dist_ts/smartproxy.classes.router.d.ts +0 -13
  289. package/dist_ts/smartproxy.classes.router.js +0 -33
  290. package/dist_ts/smartproxy.classes.sslredirect.d.ts +0 -8
  291. package/dist_ts/smartproxy.classes.sslredirect.js +0 -28
  292. package/dist_ts/smartproxy.helpers.certificates.d.ts +0 -5
  293. package/dist_ts/smartproxy.helpers.certificates.js +0 -23
  294. package/dist_ts/smartproxy.plugins.d.ts +0 -18
  295. package/dist_ts/smartproxy.plugins.js +0 -23
  296. package/dist_ts/smartproxy.portproxy.d.ts +0 -26
  297. package/dist_ts/smartproxy.portproxy.js +0 -295
  298. package/ts/http/index.ts +0 -16
  299. package/ts/http/models/http-types.ts +0 -108
  300. package/ts/http/redirects/index.ts +0 -3
  301. package/ts/proxies/network-proxy/models/index.ts +0 -4
  302. package/ts/redirect/classes.redirect.ts +0 -295
  303. /package/dist_ts/proxies/{network-proxy → http-proxy}/context-creator.d.ts +0 -0
  304. /package/dist_ts/proxies/{network-proxy → http-proxy}/function-cache.d.ts +0 -0
  305. /package/dist_ts/proxies/{network-proxy → http-proxy}/http-request-handler.d.ts +0 -0
  306. /package/dist_ts/proxies/{network-proxy → http-proxy}/http2-request-handler.d.ts +0 -0
  307. /package/dist_ts/proxies/{network-proxy → http-proxy}/security-manager.d.ts +0 -0
  308. /package/ts/proxies/{network-proxy → http-proxy}/context-creator.ts +0 -0
  309. /package/ts/proxies/{network-proxy → http-proxy}/function-cache.ts +0 -0
  310. /package/ts/proxies/{network-proxy → http-proxy}/http-request-handler.ts +0 -0
  311. /package/ts/proxies/{network-proxy → http-proxy}/http2-request-handler.ts +0 -0
  312. /package/ts/proxies/{network-proxy → http-proxy}/security-manager.ts +0 -0
  313. /package/ts/{http → routing}/router/index.ts +0 -0
@@ -1,1424 +0,0 @@
1
- import * as plugins from './plugins.js';
2
- import { ProxyRouter } from './classes.router.js';
3
- import { Port80Handler, Port80HandlerEvents } from './classes.port80handler.js';
4
- import * as fs from 'fs';
5
- import * as path from 'path';
6
- import { fileURLToPath } from 'url';
7
- export class NetworkProxy {
8
- /**
9
- * Creates a new NetworkProxy instance
10
- */
11
- constructor(optionsArg) {
12
- this.proxyConfigs = [];
13
- this.defaultHeaders = {};
14
- // State tracking
15
- this.router = new ProxyRouter();
16
- this.socketMap = new plugins.lik.ObjectMap();
17
- this.activeContexts = new Set();
18
- this.connectedClients = 0;
19
- this.startTime = 0;
20
- this.requestsServed = 0;
21
- this.failedRequests = 0;
22
- // New tracking for PortProxy integration
23
- this.portProxyConnections = 0;
24
- this.tlsTerminatedConnections = 0;
25
- this.certificateCache = new Map();
26
- // Port80Handler for certificate management
27
- this.port80Handler = null;
28
- // New connection pool for backend connections
29
- this.connectionPool = new Map();
30
- // Track round-robin positions for load balancing
31
- this.roundRobinPositions = new Map();
32
- // Set default options
33
- this.options = {
34
- port: optionsArg.port,
35
- maxConnections: optionsArg.maxConnections || 10000,
36
- keepAliveTimeout: optionsArg.keepAliveTimeout || 120000, // 2 minutes
37
- headersTimeout: optionsArg.headersTimeout || 60000, // 1 minute
38
- logLevel: optionsArg.logLevel || 'info',
39
- cors: optionsArg.cors || {
40
- allowOrigin: '*',
41
- allowMethods: 'GET, POST, PUT, DELETE, OPTIONS',
42
- allowHeaders: 'Content-Type, Authorization',
43
- maxAge: 86400
44
- },
45
- // New defaults for PortProxy integration
46
- connectionPoolSize: optionsArg.connectionPoolSize || 50,
47
- portProxyIntegration: optionsArg.portProxyIntegration || false,
48
- // Default ACME options
49
- acme: {
50
- enabled: optionsArg.acme?.enabled || false,
51
- port: optionsArg.acme?.port || 80,
52
- contactEmail: optionsArg.acme?.contactEmail || 'admin@example.com',
53
- useProduction: optionsArg.acme?.useProduction || false, // Default to staging for safety
54
- renewThresholdDays: optionsArg.acme?.renewThresholdDays || 30,
55
- autoRenew: optionsArg.acme?.autoRenew !== false, // Default to true
56
- certificateStore: optionsArg.acme?.certificateStore || './certs',
57
- skipConfiguredCerts: optionsArg.acme?.skipConfiguredCerts || false
58
- }
59
- };
60
- // Set up certificate store directory
61
- this.certificateStoreDir = path.resolve(this.options.acme.certificateStore);
62
- // Ensure certificate store directory exists
63
- try {
64
- if (!fs.existsSync(this.certificateStoreDir)) {
65
- fs.mkdirSync(this.certificateStoreDir, { recursive: true });
66
- this.log('info', `Created certificate store directory: ${this.certificateStoreDir}`);
67
- }
68
- }
69
- catch (error) {
70
- this.log('warn', `Failed to create certificate store directory: ${error}`);
71
- }
72
- this.loadDefaultCertificates();
73
- }
74
- /**
75
- * Loads default certificates from the filesystem
76
- */
77
- loadDefaultCertificates() {
78
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
79
- const certPath = path.join(__dirname, '..', 'assets', 'certs');
80
- try {
81
- this.defaultCertificates = {
82
- key: fs.readFileSync(path.join(certPath, 'key.pem'), 'utf8'),
83
- cert: fs.readFileSync(path.join(certPath, 'cert.pem'), 'utf8')
84
- };
85
- this.log('info', 'Default certificates loaded successfully');
86
- }
87
- catch (error) {
88
- this.log('error', 'Error loading default certificates', error);
89
- // Generate self-signed fallback certificates
90
- try {
91
- // This is a placeholder for actual certificate generation code
92
- // In a real implementation, you would use a library like selfsigned to generate certs
93
- this.defaultCertificates = {
94
- key: "FALLBACK_KEY_CONTENT",
95
- cert: "FALLBACK_CERT_CONTENT"
96
- };
97
- this.log('warn', 'Using fallback self-signed certificates');
98
- }
99
- catch (fallbackError) {
100
- this.log('error', 'Failed to generate fallback certificates', fallbackError);
101
- throw new Error('Could not load or generate SSL certificates');
102
- }
103
- }
104
- }
105
- /**
106
- * Returns the port number this NetworkProxy is listening on
107
- * Useful for PortProxy to determine where to forward connections
108
- */
109
- getListeningPort() {
110
- return this.options.port;
111
- }
112
- /**
113
- * Updates the server capacity settings
114
- * @param maxConnections Maximum number of simultaneous connections
115
- * @param keepAliveTimeout Keep-alive timeout in milliseconds
116
- * @param connectionPoolSize Size of the connection pool per backend
117
- */
118
- updateCapacity(maxConnections, keepAliveTimeout, connectionPoolSize) {
119
- if (maxConnections !== undefined) {
120
- this.options.maxConnections = maxConnections;
121
- this.log('info', `Updated max connections to ${maxConnections}`);
122
- }
123
- if (keepAliveTimeout !== undefined) {
124
- this.options.keepAliveTimeout = keepAliveTimeout;
125
- if (this.httpsServer) {
126
- this.httpsServer.keepAliveTimeout = keepAliveTimeout;
127
- this.log('info', `Updated keep-alive timeout to ${keepAliveTimeout}ms`);
128
- }
129
- }
130
- if (connectionPoolSize !== undefined) {
131
- this.options.connectionPoolSize = connectionPoolSize;
132
- this.log('info', `Updated connection pool size to ${connectionPoolSize}`);
133
- // Cleanup excess connections in the pool if the size was reduced
134
- this.cleanupConnectionPool();
135
- }
136
- }
137
- /**
138
- * Returns current server metrics
139
- * Useful for PortProxy to determine which NetworkProxy to use for load balancing
140
- */
141
- getMetrics() {
142
- return {
143
- activeConnections: this.connectedClients,
144
- totalRequests: this.requestsServed,
145
- failedRequests: this.failedRequests,
146
- portProxyConnections: this.portProxyConnections,
147
- tlsTerminatedConnections: this.tlsTerminatedConnections,
148
- connectionPoolSize: Array.from(this.connectionPool.entries()).reduce((acc, [host, connections]) => {
149
- acc[host] = connections.length;
150
- return acc;
151
- }, {}),
152
- uptime: Math.floor((Date.now() - this.startTime) / 1000),
153
- memoryUsage: process.memoryUsage(),
154
- activeWebSockets: this.wsServer?.clients.size || 0
155
- };
156
- }
157
- /**
158
- * Cleanup the connection pool by removing idle connections
159
- * or reducing pool size if it exceeds the configured maximum
160
- */
161
- cleanupConnectionPool() {
162
- const now = Date.now();
163
- const idleTimeout = this.options.keepAliveTimeout || 120000; // 2 minutes default
164
- for (const [host, connections] of this.connectionPool.entries()) {
165
- // Sort by last used time (oldest first)
166
- connections.sort((a, b) => a.lastUsed - b.lastUsed);
167
- // Remove idle connections older than the idle timeout
168
- let removed = 0;
169
- while (connections.length > 0) {
170
- const connection = connections[0];
171
- // Remove if idle and exceeds timeout, or if pool is too large
172
- if ((connection.isIdle && now - connection.lastUsed > idleTimeout) ||
173
- connections.length > this.options.connectionPoolSize) {
174
- try {
175
- if (!connection.socket.destroyed) {
176
- connection.socket.end();
177
- connection.socket.destroy();
178
- }
179
- }
180
- catch (err) {
181
- this.log('error', `Error destroying pooled connection to ${host}`, err);
182
- }
183
- connections.shift(); // Remove from pool
184
- removed++;
185
- }
186
- else {
187
- break; // Stop removing if we've reached active or recent connections
188
- }
189
- }
190
- if (removed > 0) {
191
- this.log('debug', `Removed ${removed} idle connections from pool for ${host}, ${connections.length} remaining`);
192
- }
193
- // Update the pool with the remaining connections
194
- if (connections.length === 0) {
195
- this.connectionPool.delete(host);
196
- }
197
- else {
198
- this.connectionPool.set(host, connections);
199
- }
200
- }
201
- }
202
- /**
203
- * Get a connection from the pool or create a new one
204
- */
205
- getConnectionFromPool(host, port) {
206
- return new Promise((resolve, reject) => {
207
- const poolKey = `${host}:${port}`;
208
- const connectionList = this.connectionPool.get(poolKey) || [];
209
- // Look for an idle connection
210
- const idleConnectionIndex = connectionList.findIndex(c => c.isIdle);
211
- if (idleConnectionIndex >= 0) {
212
- // Get existing connection from pool
213
- const connection = connectionList[idleConnectionIndex];
214
- connection.isIdle = false;
215
- connection.lastUsed = Date.now();
216
- this.log('debug', `Reusing connection from pool for ${poolKey}`);
217
- // Update the pool
218
- this.connectionPool.set(poolKey, connectionList);
219
- resolve(connection.socket);
220
- return;
221
- }
222
- // No idle connection available, create a new one if pool isn't full
223
- if (connectionList.length < this.options.connectionPoolSize) {
224
- this.log('debug', `Creating new connection to ${host}:${port}`);
225
- try {
226
- const socket = plugins.net.connect({
227
- host,
228
- port,
229
- keepAlive: true,
230
- keepAliveInitialDelay: 30000 // 30 seconds
231
- });
232
- socket.once('connect', () => {
233
- // Add to connection pool
234
- const connection = {
235
- socket,
236
- lastUsed: Date.now(),
237
- isIdle: false
238
- };
239
- connectionList.push(connection);
240
- this.connectionPool.set(poolKey, connectionList);
241
- // Setup cleanup when the connection is closed
242
- socket.once('close', () => {
243
- const idx = connectionList.findIndex(c => c.socket === socket);
244
- if (idx >= 0) {
245
- connectionList.splice(idx, 1);
246
- this.connectionPool.set(poolKey, connectionList);
247
- this.log('debug', `Removed closed connection from pool for ${poolKey}`);
248
- }
249
- });
250
- resolve(socket);
251
- });
252
- socket.once('error', (err) => {
253
- this.log('error', `Error creating connection to ${host}:${port}`, err);
254
- reject(err);
255
- });
256
- }
257
- catch (err) {
258
- this.log('error', `Failed to create connection to ${host}:${port}`, err);
259
- reject(err);
260
- }
261
- }
262
- else {
263
- // Pool is full, wait for an idle connection or reject
264
- this.log('warn', `Connection pool for ${poolKey} is full (${connectionList.length})`);
265
- reject(new Error(`Connection pool for ${poolKey} is full`));
266
- }
267
- });
268
- }
269
- /**
270
- * Return a connection to the pool for reuse
271
- */
272
- returnConnectionToPool(socket, host, port) {
273
- const poolKey = `${host}:${port}`;
274
- const connectionList = this.connectionPool.get(poolKey) || [];
275
- // Find this connection in the pool
276
- const connectionIndex = connectionList.findIndex(c => c.socket === socket);
277
- if (connectionIndex >= 0) {
278
- // Mark as idle and update last used time
279
- connectionList[connectionIndex].isIdle = true;
280
- connectionList[connectionIndex].lastUsed = Date.now();
281
- this.log('debug', `Returned connection to pool for ${poolKey}`);
282
- }
283
- else {
284
- this.log('warn', `Attempted to return unknown connection to pool for ${poolKey}`);
285
- }
286
- }
287
- /**
288
- * Initializes the Port80Handler for ACME certificate management
289
- * @private
290
- */
291
- async initializePort80Handler() {
292
- if (!this.options.acme.enabled) {
293
- return;
294
- }
295
- // Create certificate manager
296
- this.port80Handler = new Port80Handler({
297
- port: this.options.acme.port,
298
- contactEmail: this.options.acme.contactEmail,
299
- useProduction: this.options.acme.useProduction,
300
- renewThresholdDays: this.options.acme.renewThresholdDays,
301
- httpsRedirectPort: this.options.port, // Redirect to our HTTPS port
302
- renewCheckIntervalHours: 24 // Check daily for renewals
303
- });
304
- // Register event handlers
305
- this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, this.handleCertificateIssued.bind(this));
306
- this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, this.handleCertificateIssued.bind(this));
307
- this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_FAILED, this.handleCertificateFailed.bind(this));
308
- this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_EXPIRING, (data) => {
309
- this.log('info', `Certificate for ${data.domain} expires in ${data.daysRemaining} days`);
310
- });
311
- // Start the handler
312
- try {
313
- await this.port80Handler.start();
314
- this.log('info', `Port80Handler started on port ${this.options.acme.port}`);
315
- // Add domains from proxy configs
316
- this.registerDomainsWithPort80Handler();
317
- }
318
- catch (error) {
319
- this.log('error', `Failed to start Port80Handler: ${error}`);
320
- this.port80Handler = null;
321
- }
322
- }
323
- /**
324
- * Registers domains from proxy configs with the Port80Handler
325
- * @private
326
- */
327
- registerDomainsWithPort80Handler() {
328
- if (!this.port80Handler)
329
- return;
330
- // Get all hostnames from proxy configs
331
- this.proxyConfigs.forEach(config => {
332
- const hostname = config.hostName;
333
- // Skip wildcard domains - can't get certs for these with HTTP-01 validation
334
- if (hostname.includes('*')) {
335
- this.log('info', `Skipping wildcard domain for ACME: ${hostname}`);
336
- return;
337
- }
338
- // Skip domains already with certificates if configured to do so
339
- if (this.options.acme.skipConfiguredCerts) {
340
- const cachedCert = this.certificateCache.get(hostname);
341
- if (cachedCert) {
342
- this.log('info', `Skipping domain with existing certificate: ${hostname}`);
343
- return;
344
- }
345
- }
346
- // Check for existing certificate in the store
347
- const certPath = path.join(this.certificateStoreDir, `${hostname}.cert.pem`);
348
- const keyPath = path.join(this.certificateStoreDir, `${hostname}.key.pem`);
349
- try {
350
- if (fs.existsSync(certPath) && fs.existsSync(keyPath)) {
351
- // Load existing certificate and key
352
- const cert = fs.readFileSync(certPath, 'utf8');
353
- const key = fs.readFileSync(keyPath, 'utf8');
354
- // Extract expiry date from certificate if possible
355
- let expiryDate;
356
- try {
357
- const matches = cert.match(/Not After\s*:\s*(.*?)(?:\n|$)/i);
358
- if (matches && matches[1]) {
359
- expiryDate = new Date(matches[1]);
360
- }
361
- }
362
- catch (error) {
363
- this.log('warn', `Failed to extract expiry date from certificate for ${hostname}`);
364
- }
365
- // Update the certificate in the handler
366
- this.port80Handler.setCertificate(hostname, cert, key, expiryDate);
367
- // Also update our own certificate cache
368
- this.updateCertificateCache(hostname, cert, key, expiryDate);
369
- this.log('info', `Loaded existing certificate for ${hostname}`);
370
- }
371
- else {
372
- // Register the domain for certificate issuance with new domain options format
373
- const domainOptions = {
374
- domainName: hostname,
375
- sslRedirect: true,
376
- acmeMaintenance: true
377
- };
378
- this.port80Handler.addDomain(domainOptions);
379
- this.log('info', `Registered domain for ACME certificate issuance: ${hostname}`);
380
- }
381
- }
382
- catch (error) {
383
- this.log('error', `Error registering domain ${hostname} with Port80Handler: ${error}`);
384
- }
385
- });
386
- }
387
- /**
388
- * Handles newly issued or renewed certificates from Port80Handler
389
- * @private
390
- */
391
- handleCertificateIssued(data) {
392
- const { domain, certificate, privateKey, expiryDate } = data;
393
- this.log('info', `Certificate ${this.certificateCache.has(domain) ? 'renewed' : 'issued'} for ${domain}, valid until ${expiryDate.toISOString()}`);
394
- // Update certificate in HTTPS server
395
- this.updateCertificateCache(domain, certificate, privateKey, expiryDate);
396
- // Save the certificate to the filesystem
397
- this.saveCertificateToStore(domain, certificate, privateKey);
398
- }
399
- /**
400
- * Handles certificate issuance failures
401
- * @private
402
- */
403
- handleCertificateFailed(data) {
404
- this.log('error', `Certificate issuance failed for ${data.domain}: ${data.error}`);
405
- }
406
- /**
407
- * Saves certificate and private key to the filesystem
408
- * @private
409
- */
410
- saveCertificateToStore(domain, certificate, privateKey) {
411
- try {
412
- const certPath = path.join(this.certificateStoreDir, `${domain}.cert.pem`);
413
- const keyPath = path.join(this.certificateStoreDir, `${domain}.key.pem`);
414
- fs.writeFileSync(certPath, certificate);
415
- fs.writeFileSync(keyPath, privateKey);
416
- // Ensure private key has restricted permissions
417
- try {
418
- fs.chmodSync(keyPath, 0o600);
419
- }
420
- catch (error) {
421
- this.log('warn', `Failed to set permissions on private key for ${domain}: ${error}`);
422
- }
423
- this.log('info', `Saved certificate for ${domain} to ${certPath}`);
424
- }
425
- catch (error) {
426
- this.log('error', `Failed to save certificate for ${domain}: ${error}`);
427
- }
428
- }
429
- /**
430
- * Handles SNI (Server Name Indication) for TLS connections
431
- * Used by the HTTPS server to select the correct certificate for each domain
432
- * @private
433
- */
434
- handleSNI(domain, cb) {
435
- this.log('debug', `SNI request for domain: ${domain}`);
436
- // Check if we have a certificate for this domain
437
- const certs = this.certificateCache.get(domain);
438
- if (certs) {
439
- try {
440
- // Create TLS context with the cached certificate
441
- const context = plugins.tls.createSecureContext({
442
- key: certs.key,
443
- cert: certs.cert
444
- });
445
- this.log('debug', `Using cached certificate for ${domain}`);
446
- cb(null, context);
447
- return;
448
- }
449
- catch (err) {
450
- this.log('error', `Error creating secure context for ${domain}:`, err);
451
- }
452
- }
453
- // Check if we should trigger certificate issuance
454
- if (this.options.acme?.enabled && this.port80Handler && !domain.includes('*')) {
455
- // Check if this domain is already registered
456
- const certData = this.port80Handler.getCertificate(domain);
457
- if (!certData) {
458
- this.log('info', `No certificate found for ${domain}, registering for issuance`);
459
- // Register with new domain options format
460
- const domainOptions = {
461
- domainName: domain,
462
- sslRedirect: true,
463
- acmeMaintenance: true
464
- };
465
- this.port80Handler.addDomain(domainOptions);
466
- }
467
- }
468
- // Fall back to default certificate
469
- try {
470
- const context = plugins.tls.createSecureContext({
471
- key: this.defaultCertificates.key,
472
- cert: this.defaultCertificates.cert
473
- });
474
- this.log('debug', `Using default certificate for ${domain}`);
475
- cb(null, context);
476
- }
477
- catch (err) {
478
- this.log('error', `Error creating default secure context:`, err);
479
- cb(new Error('Cannot create secure context'), null);
480
- }
481
- }
482
- /**
483
- * Starts the proxy server
484
- */
485
- async start() {
486
- this.startTime = Date.now();
487
- // Initialize Port80Handler if enabled
488
- if (this.options.acme.enabled) {
489
- await this.initializePort80Handler();
490
- }
491
- // Create the HTTPS server
492
- this.httpsServer = plugins.https.createServer({
493
- key: this.defaultCertificates.key,
494
- cert: this.defaultCertificates.cert,
495
- SNICallback: (domain, cb) => this.handleSNI(domain, cb)
496
- }, (req, res) => this.handleRequest(req, res));
497
- // Configure server timeouts
498
- this.httpsServer.keepAliveTimeout = this.options.keepAliveTimeout;
499
- this.httpsServer.headersTimeout = this.options.headersTimeout;
500
- // Setup connection tracking
501
- this.setupConnectionTracking();
502
- // Setup WebSocket support
503
- this.setupWebsocketSupport();
504
- // Start metrics collection
505
- this.setupMetricsCollection();
506
- // Setup connection pool cleanup interval
507
- this.setupConnectionPoolCleanup();
508
- // Start the server
509
- return new Promise((resolve) => {
510
- this.httpsServer.listen(this.options.port, () => {
511
- this.log('info', `NetworkProxy started on port ${this.options.port}`);
512
- resolve();
513
- });
514
- });
515
- }
516
- /**
517
- * Sets up tracking of TCP connections
518
- */
519
- setupConnectionTracking() {
520
- this.httpsServer.on('connection', (connection) => {
521
- // Check if max connections reached
522
- if (this.socketMap.getArray().length >= this.options.maxConnections) {
523
- this.log('warn', `Max connections (${this.options.maxConnections}) reached, rejecting new connection`);
524
- connection.destroy();
525
- return;
526
- }
527
- // Add connection to tracking
528
- this.socketMap.add(connection);
529
- this.connectedClients = this.socketMap.getArray().length;
530
- // Check for connection from PortProxy by inspecting the source port
531
- // This is a heuristic - in a production environment you might use a more robust method
532
- const localPort = connection.localPort;
533
- const remotePort = connection.remotePort;
534
- // If this connection is from a PortProxy (usually indicated by it coming from localhost)
535
- if (this.options.portProxyIntegration && connection.remoteAddress?.includes('127.0.0.1')) {
536
- this.portProxyConnections++;
537
- this.log('debug', `New connection from PortProxy (local: ${localPort}, remote: ${remotePort})`);
538
- }
539
- else {
540
- this.log('debug', `New direct connection (local: ${localPort}, remote: ${remotePort})`);
541
- }
542
- // Setup connection cleanup handlers
543
- const cleanupConnection = () => {
544
- if (this.socketMap.checkForObject(connection)) {
545
- this.socketMap.remove(connection);
546
- this.connectedClients = this.socketMap.getArray().length;
547
- // If this was a PortProxy connection, decrement the counter
548
- if (this.options.portProxyIntegration && connection.remoteAddress?.includes('127.0.0.1')) {
549
- this.portProxyConnections--;
550
- }
551
- this.log('debug', `Connection closed. ${this.connectedClients} connections remaining`);
552
- }
553
- };
554
- connection.on('close', cleanupConnection);
555
- connection.on('error', (err) => {
556
- this.log('debug', 'Connection error', err);
557
- cleanupConnection();
558
- });
559
- connection.on('end', cleanupConnection);
560
- connection.on('timeout', () => {
561
- this.log('debug', 'Connection timeout');
562
- cleanupConnection();
563
- });
564
- });
565
- // Track TLS handshake completions
566
- this.httpsServer.on('secureConnection', (tlsSocket) => {
567
- this.tlsTerminatedConnections++;
568
- this.log('debug', 'TLS handshake completed, connection secured');
569
- });
570
- }
571
- /**
572
- * Sets up WebSocket support
573
- */
574
- setupWebsocketSupport() {
575
- // Create WebSocket server
576
- this.wsServer = new plugins.ws.WebSocketServer({
577
- server: this.httpsServer,
578
- // Add WebSocket specific timeout
579
- clientTracking: true
580
- });
581
- // Handle WebSocket connections
582
- this.wsServer.on('connection', (wsIncoming, reqArg) => {
583
- this.handleWebSocketConnection(wsIncoming, reqArg);
584
- });
585
- // Set up the heartbeat interval (check every 30 seconds, terminate after 2 minutes of inactivity)
586
- this.heartbeatInterval = setInterval(() => {
587
- if (this.wsServer.clients.size === 0) {
588
- return; // Skip if no active connections
589
- }
590
- this.log('debug', `WebSocket heartbeat check for ${this.wsServer.clients.size} clients`);
591
- this.wsServer.clients.forEach((ws) => {
592
- const wsWithHeartbeat = ws;
593
- if (wsWithHeartbeat.isAlive === false) {
594
- this.log('debug', 'Terminating inactive WebSocket connection');
595
- return wsWithHeartbeat.terminate();
596
- }
597
- wsWithHeartbeat.isAlive = false;
598
- wsWithHeartbeat.ping();
599
- });
600
- }, 30000);
601
- }
602
- /**
603
- * Sets up metrics collection
604
- */
605
- setupMetricsCollection() {
606
- this.metricsInterval = setInterval(() => {
607
- const uptime = Math.floor((Date.now() - this.startTime) / 1000);
608
- const metrics = {
609
- uptime,
610
- activeConnections: this.connectedClients,
611
- totalRequests: this.requestsServed,
612
- failedRequests: this.failedRequests,
613
- portProxyConnections: this.portProxyConnections,
614
- tlsTerminatedConnections: this.tlsTerminatedConnections,
615
- activeWebSockets: this.wsServer?.clients.size || 0,
616
- memoryUsage: process.memoryUsage(),
617
- activeContexts: Array.from(this.activeContexts),
618
- connectionPool: Object.fromEntries(Array.from(this.connectionPool.entries()).map(([host, connections]) => [
619
- host,
620
- {
621
- total: connections.length,
622
- idle: connections.filter(c => c.isIdle).length
623
- }
624
- ]))
625
- };
626
- this.log('debug', 'Proxy metrics', metrics);
627
- }, 60000); // Log metrics every minute
628
- }
629
- /**
630
- * Sets up connection pool cleanup
631
- */
632
- setupConnectionPoolCleanup() {
633
- // Clean up idle connections every minute
634
- this.connectionPoolCleanupInterval = setInterval(() => {
635
- this.cleanupConnectionPool();
636
- }, 60000); // 1 minute
637
- }
638
- /**
639
- * Handles an incoming WebSocket connection
640
- */
641
- handleWebSocketConnection(wsIncoming, reqArg) {
642
- const wsPath = reqArg.url;
643
- const wsHost = reqArg.headers.host;
644
- this.log('info', `WebSocket connection for ${wsHost}${wsPath}`);
645
- // Setup heartbeat tracking
646
- wsIncoming.isAlive = true;
647
- wsIncoming.lastPong = Date.now();
648
- wsIncoming.on('pong', () => {
649
- wsIncoming.isAlive = true;
650
- wsIncoming.lastPong = Date.now();
651
- });
652
- // Get the destination configuration
653
- const wsDestinationConfig = this.router.routeReq(reqArg);
654
- if (!wsDestinationConfig) {
655
- this.log('warn', `No route found for WebSocket ${wsHost}${wsPath}`);
656
- wsIncoming.terminate();
657
- return;
658
- }
659
- // Check authentication if required
660
- if (wsDestinationConfig.authentication) {
661
- try {
662
- if (!this.authenticateRequest(reqArg, wsDestinationConfig)) {
663
- this.log('warn', `WebSocket authentication failed for ${wsHost}${wsPath}`);
664
- wsIncoming.terminate();
665
- return;
666
- }
667
- }
668
- catch (error) {
669
- this.log('error', 'WebSocket authentication error', error);
670
- wsIncoming.terminate();
671
- return;
672
- }
673
- }
674
- // Setup outgoing WebSocket connection
675
- let wsOutgoing;
676
- const outGoingDeferred = plugins.smartpromise.defer();
677
- try {
678
- // Select destination IP and port for WebSocket
679
- const wsDestinationIp = this.selectDestinationIp(wsDestinationConfig);
680
- const wsDestinationPort = this.selectDestinationPort(wsDestinationConfig);
681
- const wsTarget = `ws://${wsDestinationIp}:${wsDestinationPort}${reqArg.url}`;
682
- this.log('debug', `Proxying WebSocket to ${wsTarget}`);
683
- wsOutgoing = new plugins.wsDefault(wsTarget);
684
- wsOutgoing.on('open', () => {
685
- this.log('debug', 'Outgoing WebSocket connection established');
686
- outGoingDeferred.resolve();
687
- });
688
- wsOutgoing.on('error', (error) => {
689
- this.log('error', 'Outgoing WebSocket error', error);
690
- outGoingDeferred.reject(error);
691
- if (wsIncoming.readyState === wsIncoming.OPEN) {
692
- wsIncoming.terminate();
693
- }
694
- });
695
- }
696
- catch (err) {
697
- this.log('error', 'Failed to create outgoing WebSocket connection', err);
698
- wsIncoming.terminate();
699
- return;
700
- }
701
- // Handle message forwarding from client to backend
702
- wsIncoming.on('message', async (message, isBinary) => {
703
- try {
704
- // Wait for outgoing connection to be ready
705
- await outGoingDeferred.promise;
706
- // Only forward if both connections are still open
707
- if (wsOutgoing.readyState === wsOutgoing.OPEN) {
708
- wsOutgoing.send(message, { binary: isBinary });
709
- }
710
- }
711
- catch (error) {
712
- this.log('error', 'Error forwarding WebSocket message to backend', error);
713
- }
714
- });
715
- // Handle message forwarding from backend to client
716
- wsOutgoing.on('message', (message, isBinary) => {
717
- try {
718
- // Only forward if the incoming connection is still open
719
- if (wsIncoming.readyState === wsIncoming.OPEN) {
720
- wsIncoming.send(message, { binary: isBinary });
721
- }
722
- }
723
- catch (error) {
724
- this.log('error', 'Error forwarding WebSocket message to client', error);
725
- }
726
- });
727
- // Clean up connections when either side closes
728
- wsIncoming.on('close', (code, reason) => {
729
- this.log('debug', `Incoming WebSocket closed: ${code} - ${reason}`);
730
- if (wsOutgoing && wsOutgoing.readyState !== wsOutgoing.CLOSED) {
731
- try {
732
- // Validate close code (must be 1000-4999) or use 1000 as default
733
- const validCode = (code >= 1000 && code <= 4999) ? code : 1000;
734
- wsOutgoing.close(validCode, reason.toString() || '');
735
- }
736
- catch (error) {
737
- this.log('error', 'Error closing outgoing WebSocket', error);
738
- wsOutgoing.terminate();
739
- }
740
- }
741
- });
742
- wsOutgoing.on('close', (code, reason) => {
743
- this.log('debug', `Outgoing WebSocket closed: ${code} - ${reason}`);
744
- if (wsIncoming && wsIncoming.readyState !== wsIncoming.CLOSED) {
745
- try {
746
- // Validate close code (must be 1000-4999) or use 1000 as default
747
- const validCode = (code >= 1000 && code <= 4999) ? code : 1000;
748
- wsIncoming.close(validCode, reason.toString() || '');
749
- }
750
- catch (error) {
751
- this.log('error', 'Error closing incoming WebSocket', error);
752
- wsIncoming.terminate();
753
- }
754
- }
755
- });
756
- }
757
- /**
758
- * Handles an HTTP/HTTPS request
759
- */
760
- async handleRequest(originRequest, originResponse) {
761
- this.requestsServed++;
762
- const startTime = Date.now();
763
- const reqId = `req_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
764
- try {
765
- const reqPath = plugins.url.parse(originRequest.url).path;
766
- this.log('info', `[${reqId}] ${originRequest.method} ${originRequest.headers.host}${reqPath}`);
767
- // Handle preflight OPTIONS requests for CORS
768
- if (originRequest.method === 'OPTIONS' && this.options.cors) {
769
- this.handleCorsRequest(originRequest, originResponse);
770
- return;
771
- }
772
- // Get destination configuration
773
- const destinationConfig = this.router.routeReq(originRequest);
774
- if (!destinationConfig) {
775
- this.log('warn', `[${reqId}] No route found for ${originRequest.headers.host}`);
776
- this.sendErrorResponse(originResponse, 404, 'Not Found: No matching route');
777
- this.failedRequests++;
778
- return;
779
- }
780
- // Handle authentication if configured
781
- if (destinationConfig.authentication) {
782
- try {
783
- if (!this.authenticateRequest(originRequest, destinationConfig)) {
784
- this.sendErrorResponse(originResponse, 401, 'Unauthorized', {
785
- 'WWW-Authenticate': 'Basic realm="Access to the proxy site", charset="UTF-8"'
786
- });
787
- this.failedRequests++;
788
- return;
789
- }
790
- }
791
- catch (error) {
792
- this.log('error', `[${reqId}] Authentication error`, error);
793
- this.sendErrorResponse(originResponse, 500, 'Internal Server Error: Authentication failed');
794
- this.failedRequests++;
795
- return;
796
- }
797
- }
798
- // Determine if we should use connection pooling
799
- const useConnectionPool = this.options.portProxyIntegration &&
800
- originRequest.socket.remoteAddress?.includes('127.0.0.1');
801
- // Select destination IP and port from the arrays
802
- const destinationIp = this.selectDestinationIp(destinationConfig);
803
- const destinationPort = this.selectDestinationPort(destinationConfig);
804
- // Construct destination URL
805
- const destinationUrl = `http://${destinationIp}:${destinationPort}${originRequest.url}`;
806
- if (useConnectionPool) {
807
- this.log('debug', `[${reqId}] Proxying to ${destinationUrl} (using connection pool)`);
808
- await this.forwardRequestUsingConnectionPool(reqId, originRequest, originResponse, destinationIp, destinationPort, originRequest.url);
809
- }
810
- else {
811
- this.log('debug', `[${reqId}] Proxying to ${destinationUrl}`);
812
- await this.forwardRequest(reqId, originRequest, originResponse, destinationUrl);
813
- }
814
- const processingTime = Date.now() - startTime;
815
- this.log('debug', `[${reqId}] Request completed in ${processingTime}ms`);
816
- }
817
- catch (error) {
818
- this.log('error', `[${reqId}] Unhandled error in request handler`, error);
819
- try {
820
- this.sendErrorResponse(originResponse, 502, 'Bad Gateway: Server error');
821
- }
822
- catch (responseError) {
823
- this.log('error', `[${reqId}] Failed to send error response`, responseError);
824
- }
825
- this.failedRequests++;
826
- }
827
- }
828
- /**
829
- * Handles a CORS preflight request
830
- */
831
- handleCorsRequest(req, res) {
832
- const cors = this.options.cors;
833
- // Set CORS headers
834
- res.setHeader('Access-Control-Allow-Origin', cors.allowOrigin);
835
- res.setHeader('Access-Control-Allow-Methods', cors.allowMethods);
836
- res.setHeader('Access-Control-Allow-Headers', cors.allowHeaders);
837
- res.setHeader('Access-Control-Max-Age', String(cors.maxAge));
838
- // Handle preflight request
839
- res.statusCode = 204;
840
- res.end();
841
- // Count this as a request served
842
- this.requestsServed++;
843
- }
844
- /**
845
- * Authenticates a request against the destination config
846
- */
847
- authenticateRequest(req, config) {
848
- const authInfo = config.authentication;
849
- if (!authInfo) {
850
- return true; // No authentication required
851
- }
852
- switch (authInfo.type) {
853
- case 'Basic': {
854
- const authHeader = req.headers.authorization;
855
- if (!authHeader || !authHeader.includes('Basic ')) {
856
- return false;
857
- }
858
- const authStringBase64 = authHeader.replace('Basic ', '');
859
- const authString = plugins.smartstring.base64.decode(authStringBase64);
860
- const [user, pass] = authString.split(':');
861
- // Use constant-time comparison to prevent timing attacks
862
- const userMatch = user === authInfo.user;
863
- const passMatch = pass === authInfo.pass;
864
- return userMatch && passMatch;
865
- }
866
- default:
867
- throw new Error(`Unsupported authentication method: ${authInfo.type}`);
868
- }
869
- }
870
- /**
871
- * Forwards a request to the destination using connection pool
872
- * for optimized connection reuse from PortProxy
873
- */
874
- async forwardRequestUsingConnectionPool(reqId, originRequest, originResponse, host, port, path) {
875
- try {
876
- // Try to get a connection from the pool
877
- const socket = await this.getConnectionFromPool(host, port);
878
- // Create an HTTP client request using the pooled socket
879
- const reqOptions = {
880
- createConnection: () => socket,
881
- host,
882
- port,
883
- path,
884
- method: originRequest.method,
885
- headers: this.prepareForwardHeaders(originRequest),
886
- timeout: 30000 // 30 second timeout
887
- };
888
- const proxyReq = plugins.http.request(reqOptions);
889
- // Handle timeouts
890
- proxyReq.on('timeout', () => {
891
- this.log('warn', `[${reqId}] Request to ${host}:${port}${path} timed out`);
892
- proxyReq.destroy();
893
- });
894
- // Handle errors
895
- proxyReq.on('error', (err) => {
896
- this.log('error', `[${reqId}] Error in proxy request to ${host}:${port}${path}`, err);
897
- // Check if the client response is still writable
898
- if (!originResponse.writableEnded) {
899
- this.sendErrorResponse(originResponse, 502, 'Bad Gateway: Error communicating with upstream server');
900
- }
901
- // Don't return the socket to the pool on error
902
- try {
903
- if (!socket.destroyed) {
904
- socket.destroy();
905
- }
906
- }
907
- catch (socketErr) {
908
- this.log('error', `[${reqId}] Error destroying socket after request error`, socketErr);
909
- }
910
- });
911
- // Forward request body
912
- originRequest.pipe(proxyReq);
913
- // Handle response
914
- proxyReq.on('response', (proxyRes) => {
915
- // Copy status and headers
916
- originResponse.statusCode = proxyRes.statusCode;
917
- for (const [name, value] of Object.entries(proxyRes.headers)) {
918
- if (value !== undefined) {
919
- originResponse.setHeader(name, value);
920
- }
921
- }
922
- // Forward the response body
923
- proxyRes.pipe(originResponse);
924
- // Return connection to pool when the response completes
925
- proxyRes.on('end', () => {
926
- if (!socket.destroyed) {
927
- this.returnConnectionToPool(socket, host, port);
928
- }
929
- });
930
- proxyRes.on('error', (err) => {
931
- this.log('error', `[${reqId}] Error in proxy response from ${host}:${port}${path}`, err);
932
- // Don't return the socket to the pool on error
933
- try {
934
- if (!socket.destroyed) {
935
- socket.destroy();
936
- }
937
- }
938
- catch (socketErr) {
939
- this.log('error', `[${reqId}] Error destroying socket after response error`, socketErr);
940
- }
941
- });
942
- });
943
- }
944
- catch (error) {
945
- this.log('error', `[${reqId}] Error setting up pooled connection to ${host}:${port}`, error);
946
- this.sendErrorResponse(originResponse, 502, 'Bad Gateway: Unable to reach upstream server');
947
- throw error;
948
- }
949
- }
950
- /**
951
- * Forwards a request to the destination (standard method)
952
- */
953
- async forwardRequest(reqId, originRequest, originResponse, destinationUrl) {
954
- try {
955
- const proxyRequest = await plugins.smartrequest.request(destinationUrl, {
956
- method: originRequest.method,
957
- headers: this.prepareForwardHeaders(originRequest),
958
- keepAlive: true,
959
- timeout: 30000 // 30 second timeout
960
- }, true, // streaming
961
- (proxyRequestStream) => this.setupRequestStreaming(originRequest, proxyRequestStream));
962
- // Handle the response
963
- this.processProxyResponse(reqId, originResponse, proxyRequest);
964
- }
965
- catch (error) {
966
- this.log('error', `[${reqId}] Error forwarding request`, error);
967
- this.sendErrorResponse(originResponse, 502, 'Bad Gateway: Unable to reach upstream server');
968
- throw error; // Let the main handler catch this
969
- }
970
- }
971
- /**
972
- * Prepares headers to forward to the backend
973
- */
974
- prepareForwardHeaders(req) {
975
- const safeHeaders = { ...req.headers };
976
- // Add forwarding headers
977
- safeHeaders['X-Forwarded-Host'] = req.headers.host;
978
- safeHeaders['X-Forwarded-Proto'] = 'https';
979
- safeHeaders['X-Forwarded-For'] = (req.socket.remoteAddress || '').replace(/^::ffff:/, '');
980
- // Add proxy-specific headers
981
- safeHeaders['X-Proxy-Id'] = `NetworkProxy-${this.options.port}`;
982
- // If this is coming from PortProxy, add a header to indicate that
983
- if (this.options.portProxyIntegration && req.socket.remoteAddress?.includes('127.0.0.1')) {
984
- safeHeaders['X-PortProxy-Forwarded'] = 'true';
985
- }
986
- // Remove sensitive headers we don't want to forward
987
- const sensitiveHeaders = ['connection', 'upgrade', 'http2-settings'];
988
- for (const header of sensitiveHeaders) {
989
- delete safeHeaders[header];
990
- }
991
- return safeHeaders;
992
- }
993
- /**
994
- * Sets up request streaming for the proxy
995
- */
996
- setupRequestStreaming(originRequest, proxyRequest) {
997
- // Forward request body data
998
- originRequest.on('data', (chunk) => {
999
- proxyRequest.write(chunk);
1000
- });
1001
- // End the request when done
1002
- originRequest.on('end', () => {
1003
- proxyRequest.end();
1004
- });
1005
- // Handle request errors
1006
- originRequest.on('error', (error) => {
1007
- this.log('error', 'Error in client request stream', error);
1008
- proxyRequest.destroy(error);
1009
- });
1010
- // Handle client abort/timeout
1011
- originRequest.on('close', () => {
1012
- if (!originRequest.complete) {
1013
- this.log('debug', 'Client closed connection before request completed');
1014
- proxyRequest.destroy();
1015
- }
1016
- });
1017
- originRequest.on('timeout', () => {
1018
- this.log('debug', 'Client request timeout');
1019
- proxyRequest.destroy(new Error('Client request timeout'));
1020
- });
1021
- // Handle proxy request errors
1022
- proxyRequest.on('error', (error) => {
1023
- this.log('error', 'Error in outgoing proxy request', error);
1024
- });
1025
- }
1026
- /**
1027
- * Processes a proxy response
1028
- */
1029
- processProxyResponse(reqId, originResponse, proxyResponse) {
1030
- this.log('debug', `[${reqId}] Received upstream response: ${proxyResponse.statusCode}`);
1031
- // Set status code
1032
- originResponse.statusCode = proxyResponse.statusCode;
1033
- // Add default headers
1034
- for (const [headerName, headerValue] of Object.entries(this.defaultHeaders)) {
1035
- originResponse.setHeader(headerName, headerValue);
1036
- }
1037
- // Add CORS headers if enabled
1038
- if (this.options.cors) {
1039
- originResponse.setHeader('Access-Control-Allow-Origin', this.options.cors.allowOrigin);
1040
- }
1041
- // Copy response headers
1042
- for (const [headerName, headerValue] of Object.entries(proxyResponse.headers)) {
1043
- // Skip hop-by-hop headers
1044
- const hopByHopHeaders = ['connection', 'keep-alive', 'transfer-encoding', 'te',
1045
- 'trailer', 'upgrade', 'proxy-authorization', 'proxy-authenticate'];
1046
- if (!hopByHopHeaders.includes(headerName.toLowerCase())) {
1047
- originResponse.setHeader(headerName, headerValue);
1048
- }
1049
- }
1050
- // Stream response body
1051
- proxyResponse.on('data', (chunk) => {
1052
- const canContinue = originResponse.write(chunk);
1053
- // Apply backpressure if needed
1054
- if (!canContinue) {
1055
- proxyResponse.pause();
1056
- originResponse.once('drain', () => {
1057
- proxyResponse.resume();
1058
- });
1059
- }
1060
- });
1061
- // End the response when done
1062
- proxyResponse.on('end', () => {
1063
- originResponse.end();
1064
- });
1065
- // Handle response errors
1066
- proxyResponse.on('error', (error) => {
1067
- this.log('error', `[${reqId}] Error in proxy response stream`, error);
1068
- originResponse.destroy(error);
1069
- });
1070
- originResponse.on('error', (error) => {
1071
- this.log('error', `[${reqId}] Error in client response stream`, error);
1072
- proxyResponse.destroy();
1073
- });
1074
- }
1075
- /**
1076
- * Sends an error response to the client
1077
- */
1078
- sendErrorResponse(res, statusCode = 500, message = 'Internal Server Error', headers = {}) {
1079
- try {
1080
- // If headers already sent, just end the response
1081
- if (res.headersSent) {
1082
- res.end();
1083
- return;
1084
- }
1085
- // Add default headers
1086
- for (const [key, value] of Object.entries(this.defaultHeaders)) {
1087
- res.setHeader(key, value);
1088
- }
1089
- // Add provided headers
1090
- for (const [key, value] of Object.entries(headers)) {
1091
- res.setHeader(key, value);
1092
- }
1093
- // Send error response
1094
- res.writeHead(statusCode, message);
1095
- // Send error body as JSON for API clients
1096
- if (res.getHeader('Content-Type') === 'application/json') {
1097
- res.end(JSON.stringify({ error: { status: statusCode, message } }));
1098
- }
1099
- else {
1100
- // Send as plain text
1101
- res.end(message);
1102
- }
1103
- }
1104
- catch (error) {
1105
- this.log('error', 'Error sending error response', error);
1106
- try {
1107
- res.destroy();
1108
- }
1109
- catch (destroyError) {
1110
- // Last resort - nothing more we can do
1111
- }
1112
- }
1113
- }
1114
- /**
1115
- * Selects a destination IP from the array using round-robin
1116
- * @param config The proxy configuration
1117
- * @returns A destination IP address
1118
- */
1119
- selectDestinationIp(config) {
1120
- // For array-based configs
1121
- if (Array.isArray(config.destinationIps) && config.destinationIps.length > 0) {
1122
- // Get the current position or initialize it
1123
- const key = `ip_${config.hostName}`;
1124
- let position = this.roundRobinPositions.get(key) || 0;
1125
- // Select the IP using round-robin
1126
- const selectedIp = config.destinationIps[position];
1127
- // Update the position for next time
1128
- position = (position + 1) % config.destinationIps.length;
1129
- this.roundRobinPositions.set(key, position);
1130
- return selectedIp;
1131
- }
1132
- // For backward compatibility with test suites that rely on specific behavior
1133
- // Check if there's a proxyConfigs entry that matches this hostname
1134
- const matchingConfig = this.proxyConfigs.find(cfg => cfg.hostName === config.hostName &&
1135
- cfg.destinationIp);
1136
- if (matchingConfig) {
1137
- return matchingConfig.destinationIp;
1138
- }
1139
- // Fallback to localhost
1140
- return 'localhost';
1141
- }
1142
- /**
1143
- * Selects a destination port from the array using round-robin
1144
- * @param config The proxy configuration
1145
- * @returns A destination port number
1146
- */
1147
- selectDestinationPort(config) {
1148
- // For array-based configs
1149
- if (Array.isArray(config.destinationPorts) && config.destinationPorts.length > 0) {
1150
- // Get the current position or initialize it
1151
- const key = `port_${config.hostName}`;
1152
- let position = this.roundRobinPositions.get(key) || 0;
1153
- // Select the port using round-robin
1154
- const selectedPort = config.destinationPorts[position];
1155
- // Update the position for next time
1156
- position = (position + 1) % config.destinationPorts.length;
1157
- this.roundRobinPositions.set(key, position);
1158
- return selectedPort;
1159
- }
1160
- // For backward compatibility with test suites that rely on specific behavior
1161
- // Check if there's a proxyConfigs entry that matches this hostname
1162
- const matchingConfig = this.proxyConfigs.find(cfg => cfg.hostName === config.hostName &&
1163
- cfg.destinationPort);
1164
- if (matchingConfig) {
1165
- return parseInt(matchingConfig.destinationPort, 10);
1166
- }
1167
- // Fallback to port 80
1168
- return 80;
1169
- }
1170
- /**
1171
- * Updates proxy configurations
1172
- */
1173
- async updateProxyConfigs(proxyConfigsArg) {
1174
- this.log('info', `Updating proxy configurations (${proxyConfigsArg.length} configs)`);
1175
- // Update internal configs
1176
- this.proxyConfigs = proxyConfigsArg;
1177
- this.router.setNewProxyConfigs(proxyConfigsArg);
1178
- // Collect all hostnames for cleanup later
1179
- const currentHostNames = new Set();
1180
- // Add/update SSL contexts for each host
1181
- for (const config of proxyConfigsArg) {
1182
- currentHostNames.add(config.hostName);
1183
- try {
1184
- // Check if we need to update the cert
1185
- const currentCert = this.certificateCache.get(config.hostName);
1186
- const shouldUpdate = !currentCert ||
1187
- currentCert.key !== config.privateKey ||
1188
- currentCert.cert !== config.publicKey;
1189
- if (shouldUpdate) {
1190
- this.log('debug', `Updating SSL context for ${config.hostName}`);
1191
- // Update the HTTPS server context
1192
- this.httpsServer.addContext(config.hostName, {
1193
- key: config.privateKey,
1194
- cert: config.publicKey
1195
- });
1196
- // Update the cache
1197
- this.certificateCache.set(config.hostName, {
1198
- key: config.privateKey,
1199
- cert: config.publicKey
1200
- });
1201
- this.activeContexts.add(config.hostName);
1202
- }
1203
- }
1204
- catch (error) {
1205
- this.log('error', `Failed to add SSL context for ${config.hostName}`, error);
1206
- }
1207
- }
1208
- // Clean up removed contexts
1209
- // Note: Node.js doesn't officially support removing contexts
1210
- // This would require server restart in production
1211
- for (const hostname of this.activeContexts) {
1212
- if (!currentHostNames.has(hostname)) {
1213
- this.log('info', `Hostname ${hostname} removed from configuration`);
1214
- this.activeContexts.delete(hostname);
1215
- this.certificateCache.delete(hostname);
1216
- }
1217
- }
1218
- }
1219
- /**
1220
- * Converts PortProxy domain configurations to NetworkProxy configs
1221
- * @param domainConfigs PortProxy domain configs
1222
- * @param sslKeyPair Default SSL key pair to use if not specified
1223
- * @returns Array of NetworkProxy configs
1224
- */
1225
- convertPortProxyConfigs(domainConfigs, sslKeyPair) {
1226
- const proxyConfigs = [];
1227
- // Use default certificates if not provided
1228
- const sslKey = sslKeyPair?.key || this.defaultCertificates.key;
1229
- const sslCert = sslKeyPair?.cert || this.defaultCertificates.cert;
1230
- for (const domainConfig of domainConfigs) {
1231
- // Each domain in the domains array gets its own config
1232
- for (const domain of domainConfig.domains) {
1233
- // Skip non-hostname patterns (like IP addresses)
1234
- if (domain.match(/^\d+\.\d+\.\d+\.\d+$/) || domain === '*' || domain === 'localhost') {
1235
- continue;
1236
- }
1237
- proxyConfigs.push({
1238
- hostName: domain,
1239
- destinationIps: domainConfig.targetIPs || ['localhost'],
1240
- destinationPorts: [this.options.port], // Use the NetworkProxy port
1241
- privateKey: sslKey,
1242
- publicKey: sslCert
1243
- });
1244
- }
1245
- }
1246
- this.log('info', `Converted ${domainConfigs.length} PortProxy configs to ${proxyConfigs.length} NetworkProxy configs`);
1247
- return proxyConfigs;
1248
- }
1249
- /**
1250
- * Adds default headers to be included in all responses
1251
- */
1252
- async addDefaultHeaders(headersArg) {
1253
- this.log('info', 'Adding default headers', headersArg);
1254
- this.defaultHeaders = {
1255
- ...this.defaultHeaders,
1256
- ...headersArg
1257
- };
1258
- }
1259
- /**
1260
- * Stops the proxy server
1261
- */
1262
- async stop() {
1263
- this.log('info', 'Stopping NetworkProxy server');
1264
- // Clear intervals
1265
- if (this.heartbeatInterval) {
1266
- clearInterval(this.heartbeatInterval);
1267
- }
1268
- if (this.metricsInterval) {
1269
- clearInterval(this.metricsInterval);
1270
- }
1271
- if (this.connectionPoolCleanupInterval) {
1272
- clearInterval(this.connectionPoolCleanupInterval);
1273
- }
1274
- // Close WebSocket server if exists
1275
- if (this.wsServer) {
1276
- for (const client of this.wsServer.clients) {
1277
- try {
1278
- client.terminate();
1279
- }
1280
- catch (error) {
1281
- this.log('error', 'Error terminating WebSocket client', error);
1282
- }
1283
- }
1284
- }
1285
- // Close all tracked sockets
1286
- for (const socket of this.socketMap.getArray()) {
1287
- try {
1288
- socket.destroy();
1289
- }
1290
- catch (error) {
1291
- this.log('error', 'Error destroying socket', error);
1292
- }
1293
- }
1294
- // Close all connection pool connections
1295
- for (const [host, connections] of this.connectionPool.entries()) {
1296
- for (const connection of connections) {
1297
- try {
1298
- if (!connection.socket.destroyed) {
1299
- connection.socket.destroy();
1300
- }
1301
- }
1302
- catch (error) {
1303
- this.log('error', `Error destroying pooled connection to ${host}`, error);
1304
- }
1305
- }
1306
- }
1307
- this.connectionPool.clear();
1308
- // Stop Port80Handler if it's running
1309
- if (this.port80Handler) {
1310
- try {
1311
- await this.port80Handler.stop();
1312
- this.log('info', 'Port80Handler stopped');
1313
- }
1314
- catch (error) {
1315
- this.log('error', 'Error stopping Port80Handler', error);
1316
- }
1317
- }
1318
- // Close the HTTPS server
1319
- return new Promise((resolve) => {
1320
- this.httpsServer.close(() => {
1321
- this.log('info', 'NetworkProxy server stopped successfully');
1322
- resolve();
1323
- });
1324
- });
1325
- }
1326
- /**
1327
- * Requests a new certificate for a domain
1328
- * This can be used to manually trigger certificate issuance
1329
- * @param domain The domain to request a certificate for
1330
- * @returns A promise that resolves when the request is submitted (not when the certificate is issued)
1331
- */
1332
- async requestCertificate(domain) {
1333
- if (!this.options.acme.enabled) {
1334
- this.log('warn', 'ACME certificate management is not enabled');
1335
- return false;
1336
- }
1337
- if (!this.port80Handler) {
1338
- this.log('error', 'Port80Handler is not initialized');
1339
- return false;
1340
- }
1341
- // Skip wildcard domains - can't get certs for these with HTTP-01 validation
1342
- if (domain.includes('*')) {
1343
- this.log('error', `Cannot request certificate for wildcard domain: ${domain}`);
1344
- return false;
1345
- }
1346
- try {
1347
- // Use the new domain options format
1348
- const domainOptions = {
1349
- domainName: domain,
1350
- sslRedirect: true,
1351
- acmeMaintenance: true
1352
- };
1353
- this.port80Handler.addDomain(domainOptions);
1354
- this.log('info', `Certificate request submitted for domain: ${domain}`);
1355
- return true;
1356
- }
1357
- catch (error) {
1358
- this.log('error', `Error requesting certificate for domain ${domain}:`, error);
1359
- return false;
1360
- }
1361
- }
1362
- /**
1363
- * Updates the certificate cache for a domain
1364
- * @param domain The domain name
1365
- * @param certificate The certificate (PEM format)
1366
- * @param privateKey The private key (PEM format)
1367
- * @param expiryDate Optional expiry date
1368
- */
1369
- updateCertificateCache(domain, certificate, privateKey, expiryDate) {
1370
- // Update certificate context in HTTPS server if it's running
1371
- if (this.httpsServer) {
1372
- try {
1373
- this.httpsServer.addContext(domain, {
1374
- key: privateKey,
1375
- cert: certificate
1376
- });
1377
- this.log('debug', `Updated SSL context for domain: ${domain}`);
1378
- }
1379
- catch (error) {
1380
- this.log('error', `Error updating SSL context for domain ${domain}:`, error);
1381
- }
1382
- }
1383
- // Update certificate in cache
1384
- this.certificateCache.set(domain, {
1385
- key: privateKey,
1386
- cert: certificate,
1387
- expires: expiryDate
1388
- });
1389
- // Add to active contexts set
1390
- this.activeContexts.add(domain);
1391
- }
1392
- /**
1393
- * Logs a message according to the configured log level
1394
- */
1395
- log(level, message, data) {
1396
- const logLevels = {
1397
- error: 0,
1398
- warn: 1,
1399
- info: 2,
1400
- debug: 3
1401
- };
1402
- // Skip if log level is higher than configured
1403
- if (logLevels[level] > logLevels[this.options.logLevel]) {
1404
- return;
1405
- }
1406
- const timestamp = new Date().toISOString();
1407
- const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
1408
- switch (level) {
1409
- case 'error':
1410
- console.error(`${prefix} ${message}`, data || '');
1411
- break;
1412
- case 'warn':
1413
- console.warn(`${prefix} ${message}`, data || '');
1414
- break;
1415
- case 'info':
1416
- console.log(`${prefix} ${message}`, data || '');
1417
- break;
1418
- case 'debug':
1419
- console.log(`${prefix} ${message}`, data || '');
1420
- break;
1421
- }
1422
- }
1423
- }
1424
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5uZXR3b3JrcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLm5ldHdvcmtwcm94eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxtQkFBbUIsRUFBdUIsTUFBTSw0QkFBNEIsQ0FBQztBQUNyRyxPQUFPLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQztBQUN6QixPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBcUNwQyxNQUFNLE9BQU8sWUFBWTtJQThDdkI7O09BRUc7SUFDSCxZQUFZLFVBQWdDO1FBOUNyQyxpQkFBWSxHQUFrRCxFQUFFLENBQUM7UUFDakUsbUJBQWMsR0FBOEIsRUFBRSxDQUFDO1FBTXRELGlCQUFpQjtRQUNWLFdBQU0sR0FBRyxJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQzNCLGNBQVMsR0FBRyxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFzQixDQUFDO1FBQzVELG1CQUFjLEdBQWdCLElBQUksR0FBRyxFQUFFLENBQUM7UUFDeEMscUJBQWdCLEdBQVcsQ0FBQyxDQUFDO1FBQzdCLGNBQVMsR0FBVyxDQUFDLENBQUM7UUFDdEIsbUJBQWMsR0FBVyxDQUFDLENBQUM7UUFDM0IsbUJBQWMsR0FBVyxDQUFDLENBQUM7UUFFbEMseUNBQXlDO1FBQ2pDLHlCQUFvQixHQUFXLENBQUMsQ0FBQztRQUNqQyw2QkFBd0IsR0FBVyxDQUFDLENBQUM7UUFTckMscUJBQWdCLEdBQStELElBQUksR0FBRyxFQUFFLENBQUM7UUFFakcsMkNBQTJDO1FBQ25DLGtCQUFhLEdBQXlCLElBQUksQ0FBQztRQUduRCw4Q0FBOEM7UUFDdEMsbUJBQWMsR0FJaEIsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUVoQixpREFBaUQ7UUFDekMsd0JBQW1CLEdBQXdCLElBQUksR0FBRyxFQUFFLENBQUM7UUFNM0Qsc0JBQXNCO1FBQ3RCLElBQUksQ0FBQyxPQUFPLEdBQUc7WUFDYixJQUFJLEVBQUUsVUFBVSxDQUFDLElBQUk7WUFDckIsY0FBYyxFQUFFLFVBQVUsQ0FBQyxjQUFjLElBQUksS0FBSztZQUNsRCxnQkFBZ0IsRUFBRSxVQUFVLENBQUMsZ0JBQWdCLElBQUksTUFBTSxFQUFFLGFBQWE7WUFDdEUsY0FBYyxFQUFFLFVBQVUsQ0FBQyxjQUFjLElBQUksS0FBSyxFQUFFLFdBQVc7WUFDL0QsUUFBUSxFQUFFLFVBQVUsQ0FBQyxRQUFRLElBQUksTUFBTTtZQUN2QyxJQUFJLEVBQUUsVUFBVSxDQUFDLElBQUksSUFBSTtnQkFDdkIsV0FBVyxFQUFFLEdBQUc7Z0JBQ2hCLFlBQVksRUFBRSxpQ0FBaUM7Z0JBQy9DLFlBQVksRUFBRSw2QkFBNkI7Z0JBQzNDLE1BQU0sRUFBRSxLQUFLO2FBQ2Q7WUFDRCx5Q0FBeUM7WUFDekMsa0JBQWtCLEVBQUUsVUFBVSxDQUFDLGtCQUFrQixJQUFJLEVBQUU7WUFDdkQsb0JBQW9CLEVBQUUsVUFBVSxDQUFDLG9CQUFvQixJQUFJLEtBQUs7WUFDOUQsdUJBQXVCO1lBQ3ZCLElBQUksRUFBRTtnQkFDSixPQUFPLEVBQUUsVUFBVSxDQUFDLElBQUksRUFBRSxPQUFPLElBQUksS0FBSztnQkFDMUMsSUFBSSxFQUFFLFVBQVUsQ0FBQyxJQUFJLEVBQUUsSUFBSSxJQUFJLEVBQUU7Z0JBQ2pDLFlBQVksRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLFlBQVksSUFBSSxtQkFBbUI7Z0JBQ2xFLGFBQWEsRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLGFBQWEsSUFBSSxLQUFLLEVBQUUsZ0NBQWdDO2dCQUN4RixrQkFBa0IsRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLGtCQUFrQixJQUFJLEVBQUU7Z0JBQzdELFNBQVMsRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLFNBQVMsS0FBSyxLQUFLLEVBQUUsa0JBQWtCO2dCQUNuRSxnQkFBZ0IsRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLGdCQUFnQixJQUFJLFNBQVM7Z0JBQ2hFLG1CQUFtQixFQUFFLFVBQVUsQ0FBQyxJQUFJLEVBQUUsbUJBQW1CLElBQUksS0FBSzthQUNuRTtTQUNGLENBQUM7UUFFRixxQ0FBcUM7UUFDckMsSUFBSSxDQUFDLG1CQUFtQixHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUU1RSw0Q0FBNEM7UUFDNUMsSUFBSSxDQUFDO1lBQ0gsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEVBQUUsQ0FBQztnQkFDN0MsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDNUQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsd0NBQXdDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDLENBQUM7WUFDdkYsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsaURBQWlELEtBQUssRUFBRSxDQUFDLENBQUM7UUFDN0UsQ0FBQztRQUVELElBQUksQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7T0FFRztJQUNLLHVCQUF1QjtRQUM3QixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDL0QsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUUvRCxJQUFJLENBQUM7WUFDSCxJQUFJLENBQUMsbUJBQW1CLEdBQUc7Z0JBQ3pCLEdBQUcsRUFBRSxFQUFFLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQyxFQUFFLE1BQU0sQ0FBQztnQkFDNUQsSUFBSSxFQUFFLEVBQUUsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsVUFBVSxDQUFDLEVBQUUsTUFBTSxDQUFDO2FBQy9ELENBQUM7WUFDRixJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSwwQ0FBMEMsQ0FBQyxDQUFDO1FBQy9ELENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsb0NBQW9DLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFFL0QsNkNBQTZDO1lBQzdDLElBQUksQ0FBQztnQkFDSCwrREFBK0Q7Z0JBQy9ELHNGQUFzRjtnQkFDdEYsSUFBSSxDQUFDLG1CQUFtQixHQUFHO29CQUN6QixHQUFHLEVBQUUsc0JBQXNCO29CQUMzQixJQUFJLEVBQUUsdUJBQXVCO2lCQUM5QixDQUFDO2dCQUNGLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLHlDQUF5QyxDQUFDLENBQUM7WUFDOUQsQ0FBQztZQUFDLE9BQU8sYUFBYSxFQUFFLENBQUM7Z0JBQ3ZCLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDBDQUEwQyxFQUFFLGFBQWEsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLElBQUksS0FBSyxDQUFDLDZDQUE2QyxDQUFDLENBQUM7WUFDakUsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksZ0JBQWdCO1FBQ3JCLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7SUFDM0IsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ksY0FBYyxDQUFDLGNBQXVCLEVBQUUsZ0JBQXlCLEVBQUUsa0JBQTJCO1FBQ25HLElBQUksY0FBYyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ2pDLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxHQUFHLGNBQWMsQ0FBQztZQUM3QyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSw4QkFBOEIsY0FBYyxFQUFFLENBQUMsQ0FBQztRQUNuRSxDQUFDO1FBRUQsSUFBSSxnQkFBZ0IsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNuQyxJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixHQUFHLGdCQUFnQixDQUFDO1lBRWpELElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO2dCQUNyQixJQUFJLENBQUMsV0FBVyxDQUFDLGdCQUFnQixHQUFHLGdCQUFnQixDQUFDO2dCQUNyRCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxpQ0FBaUMsZ0JBQWdCLElBQUksQ0FBQyxDQUFDO1lBQzFFLENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxrQkFBa0IsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNyQyxJQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFrQixHQUFHLGtCQUFrQixDQUFDO1lBQ3JELElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLG1DQUFtQyxrQkFBa0IsRUFBRSxDQUFDLENBQUM7WUFFMUUsaUVBQWlFO1lBQ2pFLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksVUFBVTtRQUNmLE9BQU87WUFDTCxpQkFBaUIsRUFBRSxJQUFJLENBQUMsZ0JBQWdCO1lBQ3hDLGFBQWEsRUFBRSxJQUFJLENBQUMsY0FBYztZQUNsQyxjQUFjLEVBQUUsSUFBSSxDQUFDLGNBQWM7WUFDbkMsb0JBQW9CLEVBQUUsSUFBSSxDQUFDLG9CQUFvQjtZQUMvQyx3QkFBd0IsRUFBRSxJQUFJLENBQUMsd0JBQXdCO1lBQ3ZELGtCQUFrQixFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsRUFBRSxFQUFFO2dCQUNoRyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsV0FBVyxDQUFDLE1BQU0sQ0FBQztnQkFDL0IsT0FBTyxHQUFHLENBQUM7WUFDYixDQUFDLEVBQUUsRUFBNEIsQ0FBQztZQUNoQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsSUFBSSxDQUFDO1lBQ3hELFdBQVcsRUFBRSxPQUFPLENBQUMsV0FBVyxFQUFFO1lBQ2xDLGdCQUFnQixFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLElBQUksSUFBSSxDQUFDO1NBQ25ELENBQUM7SUFDSixDQUFDO0lBRUQ7OztPQUdHO0lBQ0sscUJBQXFCO1FBQzNCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUN2QixNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixJQUFJLE1BQU0sQ0FBQyxDQUFDLG9CQUFvQjtRQUVqRixLQUFLLE1BQU0sQ0FBQyxJQUFJLEVBQUUsV0FBVyxDQUFDLElBQUksSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDO1lBQ2hFLHdDQUF3QztZQUN4QyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUM7WUFFcEQsc0RBQXNEO1lBQ3RELElBQUksT0FBTyxHQUFHLENBQUMsQ0FBQztZQUNoQixPQUFPLFdBQVcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQzlCLE1BQU0sVUFBVSxHQUFHLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFFbEMsOERBQThEO2dCQUM5RCxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sSUFBSSxHQUFHLEdBQUcsVUFBVSxDQUFDLFFBQVEsR0FBRyxXQUFXLENBQUM7b0JBQzlELFdBQVcsQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxrQkFBbUIsRUFBRSxDQUFDO29CQUUxRCxJQUFJLENBQUM7d0JBQ0gsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUM7NEJBQ2pDLFVBQVUsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7NEJBQ3hCLFVBQVUsQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7d0JBQzlCLENBQUM7b0JBQ0gsQ0FBQztvQkFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO3dCQUNiLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHlDQUF5QyxJQUFJLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztvQkFDMUUsQ0FBQztvQkFFRCxXQUFXLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxtQkFBbUI7b0JBQ3hDLE9BQU8sRUFBRSxDQUFDO2dCQUNaLENBQUM7cUJBQU0sQ0FBQztvQkFDTixNQUFNLENBQUMsOERBQThEO2dCQUN2RSxDQUFDO1lBQ0gsQ0FBQztZQUVELElBQUksT0FBTyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUNoQixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxXQUFXLE9BQU8sbUNBQW1DLElBQUksS0FBSyxXQUFXLENBQUMsTUFBTSxZQUFZLENBQUMsQ0FBQztZQUNsSCxDQUFDO1lBRUQsaURBQWlEO1lBQ2pELElBQUksV0FBVyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztnQkFDN0IsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDbkMsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQztZQUM3QyxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLHFCQUFxQixDQUFDLElBQVksRUFBRSxJQUFZO1FBQ3RELE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFDckMsTUFBTSxPQUFPLEdBQUcsR0FBRyxJQUFJLElBQUksSUFBSSxFQUFFLENBQUM7WUFDbEMsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO1lBRTlELDhCQUE4QjtZQUM5QixNQUFNLG1CQUFtQixHQUFHLGNBQWMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUM7WUFFcEUsSUFBSSxtQkFBbUIsSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDN0Isb0NBQW9DO2dCQUNwQyxNQUFNLFVBQVUsR0FBRyxjQUFjLENBQUMsbUJBQW1CLENBQUMsQ0FBQztnQkFDdkQsVUFBVSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7Z0JBQzFCLFVBQVUsQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO2dCQUNqQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxvQ0FBb0MsT0FBTyxFQUFFLENBQUMsQ0FBQztnQkFFakUsa0JBQWtCO2dCQUNsQixJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsY0FBYyxDQUFDLENBQUM7Z0JBRWpELE9BQU8sQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQzNCLE9BQU87WUFDVCxDQUFDO1lBRUQsb0VBQW9FO1lBQ3BFLElBQUksY0FBYyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFtQixFQUFFLENBQUM7Z0JBQzdELElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDhCQUE4QixJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFFaEUsSUFBSSxDQUFDO29CQUNILE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDO3dCQUNqQyxJQUFJO3dCQUNKLElBQUk7d0JBQ0osU0FBUyxFQUFFLElBQUk7d0JBQ2YscUJBQXFCLEVBQUUsS0FBSyxDQUFDLGFBQWE7cUJBQzNDLENBQUMsQ0FBQztvQkFFSCxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxHQUFHLEVBQUU7d0JBQzFCLHlCQUF5Qjt3QkFDekIsTUFBTSxVQUFVLEdBQUc7NEJBQ2pCLE1BQU07NEJBQ04sUUFBUSxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7NEJBQ3BCLE1BQU0sRUFBRSxLQUFLO3lCQUNkLENBQUM7d0JBRUYsY0FBYyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQzt3QkFDaEMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGNBQWMsQ0FBQyxDQUFDO3dCQUVqRCw4Q0FBOEM7d0JBQzlDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRTs0QkFDeEIsTUFBTSxHQUFHLEdBQUcsY0FBYyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDLENBQUM7NEJBQy9ELElBQUksR0FBRyxJQUFJLENBQUMsRUFBRSxDQUFDO2dDQUNiLGNBQWMsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDO2dDQUM5QixJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsY0FBYyxDQUFDLENBQUM7Z0NBQ2pELElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDJDQUEyQyxPQUFPLEVBQUUsQ0FBQyxDQUFDOzRCQUMxRSxDQUFDO3dCQUNILENBQUMsQ0FBQyxDQUFDO3dCQUVILE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztvQkFDbEIsQ0FBQyxDQUFDLENBQUM7b0JBRUgsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRTt3QkFDM0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsZ0NBQWdDLElBQUksSUFBSSxJQUFJLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQzt3QkFDdkUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUNkLENBQUMsQ0FBQyxDQUFDO2dCQUNMLENBQUM7Z0JBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztvQkFDYixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxrQ0FBa0MsSUFBSSxJQUFJLElBQUksRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO29CQUN6RSxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2QsQ0FBQztZQUNILENBQUM7aUJBQU0sQ0FBQztnQkFDTixzREFBc0Q7Z0JBQ3RELElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLHVCQUF1QixPQUFPLGFBQWEsY0FBYyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7Z0JBQ3RGLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyx1QkFBdUIsT0FBTyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQzlELENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNLLHNCQUFzQixDQUFDLE1BQTBCLEVBQUUsSUFBWSxFQUFFLElBQVk7UUFDbkYsTUFBTSxPQUFPLEdBQUcsR0FBRyxJQUFJLElBQUksSUFBSSxFQUFFLENBQUM7UUFDbEMsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO1FBRTlELG1DQUFtQztRQUNuQyxNQUFNLGVBQWUsR0FBRyxjQUFjLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sS0FBSyxNQUFNLENBQUMsQ0FBQztRQUUzRSxJQUFJLGVBQWUsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUN6Qix5Q0FBeUM7WUFDekMsY0FBYyxDQUFDLGVBQWUsQ0FBQyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7WUFDOUMsY0FBYyxDQUFDLGVBQWUsQ0FBQyxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFFdEQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsbUNBQW1DLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFDbEUsQ0FBQzthQUFNLENBQUM7WUFDTixJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxzREFBc0QsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUNwRixDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7T0FHRztJQUNLLEtBQUssQ0FBQyx1QkFBdUI7UUFDbkMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQy9CLE9BQU87UUFDVCxDQUFDO1FBRUQsNkJBQTZCO1FBQzdCLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxhQUFhLENBQUM7WUFDckMsSUFBSSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUk7WUFDNUIsWUFBWSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFlBQVk7WUFDNUMsYUFBYSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLGFBQWE7WUFDOUMsa0JBQWtCLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsa0JBQWtCO1lBQ3hELGlCQUFpQixFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLDZCQUE2QjtZQUNuRSx1QkFBdUIsRUFBRSxFQUFFLENBQUMsMkJBQTJCO1NBQ3hELENBQUMsQ0FBQztRQUVILDBCQUEwQjtRQUMxQixJQUFJLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxrQkFBa0IsRUFBRSxJQUFJLENBQUMsdUJBQXVCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDdkcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUMsbUJBQW1CLENBQUMsbUJBQW1CLEVBQUUsSUFBSSxDQUFDLHVCQUF1QixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3hHLElBQUksQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDLG1CQUFtQixDQUFDLGtCQUFrQixFQUFFLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUN2RyxJQUFJLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxvQkFBb0IsRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFO1lBQ3ZFLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLG1CQUFtQixJQUFJLENBQUMsTUFBTSxlQUFlLElBQUksQ0FBQyxhQUFhLE9BQU8sQ0FBQyxDQUFDO1FBQzNGLENBQUMsQ0FBQyxDQUFDO1FBRUgsb0JBQW9CO1FBQ3BCLElBQUksQ0FBQztZQUNILE1BQU0sSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNqQyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxpQ0FBaUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztZQUU1RSxpQ0FBaUM7WUFDakMsSUFBSSxDQUFDLGdDQUFnQyxFQUFFLENBQUM7UUFDMUMsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxrQ0FBa0MsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUM3RCxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQztRQUM1QixDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7T0FHRztJQUNLLGdDQUFnQztRQUN0QyxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWE7WUFBRSxPQUFPO1FBRWhDLHVDQUF1QztRQUN2QyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNqQyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDO1lBRWpDLDRFQUE0RTtZQUM1RSxJQUFJLFFBQVEsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDM0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsc0NBQXNDLFFBQVEsRUFBRSxDQUFDLENBQUM7Z0JBQ25FLE9BQU87WUFDVCxDQUFDO1lBRUQsZ0VBQWdFO1lBQ2hFLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztnQkFDMUMsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQztnQkFDdkQsSUFBSSxVQUFVLEVBQUUsQ0FBQztvQkFDZixJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSw4Q0FBOEMsUUFBUSxFQUFFLENBQUMsQ0FBQztvQkFDM0UsT0FBTztnQkFDVCxDQUFDO1lBQ0gsQ0FBQztZQUVELDhDQUE4QztZQUM5QyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxHQUFHLFFBQVEsV0FBVyxDQUFDLENBQUM7WUFDN0UsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsbUJBQW1CLEVBQUUsR0FBRyxRQUFRLFVBQVUsQ0FBQyxDQUFDO1lBRTNFLElBQUksQ0FBQztnQkFDSCxJQUFJLEVBQUUsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO29CQUN0RCxvQ0FBb0M7b0JBQ3BDLE1BQU0sSUFBSSxHQUFHLEVBQUUsQ0FBQyxZQUFZLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDO29CQUMvQyxNQUFNLEdBQUcsR0FBRyxFQUFFLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztvQkFFN0MsbURBQW1EO29CQUNuRCxJQUFJLFVBQTRCLENBQUM7b0JBQ2pDLElBQUksQ0FBQzt3QkFDSCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGdDQUFnQyxDQUFDLENBQUM7d0JBQzdELElBQUksT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDOzRCQUMxQixVQUFVLEdBQUcsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7d0JBQ3BDLENBQUM7b0JBQ0gsQ0FBQztvQkFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO3dCQUNmLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLHNEQUFzRCxRQUFRLEVBQUUsQ0FBQyxDQUFDO29CQUNyRixDQUFDO29CQUVELHdDQUF3QztvQkFDeEMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxjQUFjLENBQUMsUUFBUSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsVUFBVSxDQUFDLENBQUM7b0JBRW5FLHdDQUF3QztvQkFDeEMsSUFBSSxDQUFDLHNCQUFzQixDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO29CQUU3RCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxtQ0FBbUMsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDbEUsQ0FBQztxQkFBTSxDQUFDO29CQUNOLDhFQUE4RTtvQkFDOUUsTUFBTSxhQUFhLEdBQW1CO3dCQUNwQyxVQUFVLEVBQUUsUUFBUTt3QkFDcEIsV0FBVyxFQUFFLElBQUk7d0JBQ2pCLGVBQWUsRUFBRSxJQUFJO3FCQUN0QixDQUFDO29CQUVGLElBQUksQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxDQUFDO29CQUM1QyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxvREFBb0QsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDbkYsQ0FBQztZQUNILENBQUM7WUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO2dCQUNmLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDRCQUE0QixRQUFRLHdCQUF3QixLQUFLLEVBQUUsQ0FBQyxDQUFDO1lBQ3pGLENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7O09BR0c7SUFDSyx1QkFBdUIsQ0FBQyxJQUFtRjtRQUNqSCxNQUFNLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLEdBQUcsSUFBSSxDQUFDO1FBRTdELElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGVBQWUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxRQUFRLFFBQVEsTUFBTSxpQkFBaUIsVUFBVSxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUVuSixxQ0FBcUM7UUFDckMsSUFBSSxDQUFDLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLFVBQVUsQ0FBQyxDQUFDO1FBRXpFLHlDQUF5QztRQUN6QyxJQUFJLENBQUMsc0JBQXNCLENBQUMsTUFBTSxFQUFFLFdBQVcsRUFBRSxVQUFVLENBQUMsQ0FBQztJQUMvRCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ssdUJBQXVCLENBQUMsSUFBdUM7UUFDckUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsbUNBQW1DLElBQUksQ0FBQyxNQUFNLEtBQUssSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7SUFDckYsQ0FBQztJQUVEOzs7T0FHRztJQUNLLHNCQUFzQixDQUFDLE1BQWMsRUFBRSxXQUFtQixFQUFFLFVBQWtCO1FBQ3BGLElBQUksQ0FBQztZQUNILE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLG1CQUFtQixFQUFFLEdBQUcsTUFBTSxXQUFXLENBQUMsQ0FBQztZQUMzRSxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxHQUFHLE1BQU0sVUFBVSxDQUFDLENBQUM7WUFFekUsRUFBRSxDQUFDLGFBQWEsQ0FBQyxRQUFRLEVBQUUsV0FBVyxDQUFDLENBQUM7WUFDeEMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7WUFFdEMsZ0RBQWdEO1lBQ2hELElBQUksQ0FBQztnQkFDSCxFQUFFLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUMvQixDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxnREFBZ0QsTUFBTSxLQUFLLEtBQUssRUFBRSxDQUFDLENBQUM7WUFDdkYsQ0FBQztZQUVELElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLHlCQUF5QixNQUFNLE9BQU8sUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNyRSxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGtDQUFrQyxNQUFNLEtBQUssS0FBSyxFQUFFLENBQUMsQ0FBQztRQUMxRSxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxTQUFTLENBQUMsTUFBYyxFQUFFLEVBQStEO1FBQy9GLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDJCQUEyQixNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBRXZELGlEQUFpRDtRQUNqRCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRWhELElBQUksS0FBSyxFQUFFLENBQUM7WUFDVixJQUFJLENBQUM7Z0JBQ0gsaURBQWlEO2dCQUNqRCxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixDQUFDO29CQUM5QyxHQUFHLEVBQUUsS0FBSyxDQUFDLEdBQUc7b0JBQ2QsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO2lCQUNqQixDQUFDLENBQUM7Z0JBRUgsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsZ0NBQWdDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzVELEVBQUUsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2xCLE9BQU87WUFDVCxDQUFDO1lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDYixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxxQ0FBcUMsTUFBTSxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDekUsQ0FBQztRQUNILENBQUM7UUFFRCxrREFBa0Q7UUFDbEQsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxPQUFPLElBQUksSUFBSSxDQUFDLGFBQWEsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM5RSw2Q0FBNkM7WUFDN0MsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7WUFFM0QsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUNkLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLDRCQUE0QixNQUFNLDRCQUE0QixDQUFDLENBQUM7Z0JBRWpGLDBDQUEwQztnQkFDMUMsTUFBTSxhQUFhLEdBQW1CO29CQUNwQyxVQUFVLEVBQUUsTUFBTTtvQkFDbEIsV0FBVyxFQUFFLElBQUk7b0JBQ2pCLGVBQWUsRUFBRSxJQUFJO2lCQUN0QixDQUFDO2dCQUVGLElBQUksQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxDQUFDO1lBQzlDLENBQUM7UUFDSCxDQUFDO1FBRUQsbUNBQW1DO1FBQ25DLElBQUksQ0FBQztZQUNILE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUJBQW1CLENBQUM7Z0JBQzlDLEdBQUcsRUFBRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsR0FBRztnQkFDakMsSUFBSSxFQUFFLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJO2FBQ3BDLENBQUMsQ0FBQztZQUVILElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGlDQUFpQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQzdELEVBQUUsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDcEIsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSx3Q0FBd0MsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUNqRSxFQUFFLENBQUMsSUFBSSxLQUFLLENBQUMsOEJBQThCLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUN0RCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLEtBQUs7UUFDaEIsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFFNUIsc0NBQXNDO1FBQ3RDLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDOUIsTUFBTSxJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztRQUN2QyxDQUFDO1FBRUQsMEJBQTBCO1FBQzFCLElBQUksQ0FBQyxXQUFXLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQzNDO1lBQ0UsR0FBRyxFQUFFLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHO1lBQ2pDLElBQUksRUFBRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSTtZQUNuQyxXQUFXLEVBQUUsQ0FBQyxNQUFNLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7U0FDeEQsRUFDRCxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUMzQyxDQUFDO1FBRUYsNEJBQTRCO1FBQzVCLElBQUksQ0FBQyxXQUFXLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQztRQUNsRSxJQUFJLENBQUMsV0FBVyxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQztRQUU5RCw0QkFBNEI7UUFDNUIsSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7UUFFL0IsMEJBQTBCO1FBQzFCLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBRTdCLDJCQUEyQjtRQUMzQixJQUFJLENBQUMsc0JBQXNCLEVBQUUsQ0FBQztRQUU5Qix5Q0FBeUM7UUFDekMsSUFBSSxDQUFDLDBCQUEwQixFQUFFLENBQUM7UUFFbEMsbUJBQW1CO1FBQ25CLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUM3QixJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLEVBQUU7Z0JBQzlDLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGdDQUFnQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7Z0JBQ3RFLE9BQU8sRUFBRSxDQUFDO1lBQ1osQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNLLHVCQUF1QjtRQUM3QixJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxZQUFZLEVBQUUsQ0FBQyxVQUE4QixFQUFFLEVBQUU7WUFDbkUsbUNBQW1DO1lBQ25DLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDcEUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsb0JBQW9CLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxxQ0FBcUMsQ0FBQyxDQUFDO2dCQUN2RyxVQUFVLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ3JCLE9BQU87WUFDVCxDQUFDO1lBRUQsNkJBQTZCO1lBQzdCLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQy9CLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUV6RCxvRUFBb0U7WUFDcEUsdUZBQXVGO1lBQ3ZGLE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxTQUFTLENBQUM7WUFDdkMsTUFBTSxVQUFVLEdBQUcsVUFBVSxDQUFDLFVBQVUsQ0FBQztZQUV6Qyx5RkFBeUY7WUFDekYsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLG9CQUFvQixJQUFJLFVBQVUsQ0FBQyxhQUFhLEVBQUUsUUFBUSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3pGLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO2dCQUM1QixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSx5Q0FBeUMsU0FBUyxhQUFhLFVBQVUsR0FBRyxDQUFDLENBQUM7WUFDbEcsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGlDQUFpQyxTQUFTLGFBQWEsVUFBVSxHQUFHLENBQUMsQ0FBQztZQUMxRixDQUFDO1lBRUQsb0NBQW9DO1lBQ3BDLE1BQU0saUJBQWlCLEdBQUcsR0FBRyxFQUFFO2dCQUM3QixJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsY0FBYyxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7b0JBQzlDLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO29CQUNsQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxNQUFNLENBQUM7b0JBRXpELDREQUE0RDtvQkFDNUQsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLG9CQUFvQixJQUFJLFVBQVUsQ0FBQyxhQUFhLEVBQUUsUUFBUSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7d0JBQ3pGLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO29CQUM5QixDQUFDO29CQUVELElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHNCQUFzQixJQUFJLENBQUMsZ0JBQWdCLHdCQUF3QixDQUFDLENBQUM7Z0JBQ3pGLENBQUM7WUFDSCxDQUFDLENBQUM7WUFFRixVQUFVLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO1lBQzFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7Z0JBQzdCLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUMzQyxpQkFBaUIsRUFBRSxDQUFDO1lBQ3RCLENBQUMsQ0FBQyxDQUFDO1lBQ0gsVUFBVSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztZQUN4QyxVQUFVLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxHQUFHLEVBQUU7Z0JBQzVCLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLG9CQUFvQixDQUFDLENBQUM7Z0JBQ3hDLGlCQUFpQixFQUFFLENBQUM7WUFDdEIsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztRQUVILGtDQUFrQztRQUNsQyxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDLFNBQVMsRUFBRSxFQUFFO1lBQ3BELElBQUksQ0FBQyx3QkFBd0IsRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDZDQUE2QyxDQUFDLENBQUM7UUFDbkUsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxxQkFBcUI7UUFDM0IsMEJBQTBCO1FBQzFCLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxPQUFPLENBQUMsRUFBRSxDQUFDLGVBQWUsQ0FBQztZQUM3QyxNQUFNLEVBQUUsSUFBSSxDQUFDLFdBQVc7WUFDeEIsaUNBQWlDO1lBQ2pDLGNBQWMsRUFBRSxJQUFJO1NBQ3JCLENBQUMsQ0FBQztRQUVILCtCQUErQjtRQUMvQixJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxZQUFZLEVBQUUsQ0FBQyxVQUFtQyxFQUFFLE1BQW9DLEVBQUUsRUFBRTtZQUMzRyxJQUFJLENBQUMseUJBQXlCLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JELENBQUMsQ0FBQyxDQUFDO1FBRUgsa0dBQWtHO1FBQ2xHLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFO1lBQ3hDLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsSUFBSSxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUNyQyxPQUFPLENBQUMsZ0NBQWdDO1lBQzFDLENBQUM7WUFFRCxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxpQ0FBaUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsSUFBSSxVQUFVLENBQUMsQ0FBQztZQUN6RixJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFxQixFQUFFLEVBQUU7Z0JBQ3RELE1BQU0sZUFBZSxHQUFHLEVBQTZCLENBQUM7Z0JBRXRELElBQUksZUFBZSxDQUFDLE9BQU8sS0FBSyxLQUFLLEVBQUUsQ0FBQztvQkFDdEMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsMkNBQTJDLENBQUMsQ0FBQztvQkFDL0QsT0FBTyxlQUFlLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ3JDLENBQUM7Z0JBRUQsZUFBZSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUM7Z0JBQ2hDLGVBQWUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUN6QixDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNaLENBQUM7SUFFRDs7T0FFRztJQUNLLHNCQUFzQjtRQUM1QixJQUFJLENBQUMsZUFBZSxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUU7WUFDdEMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUM7WUFDaEUsTUFBTSxPQUFPLEdBQUc7Z0JBQ2QsTUFBTTtnQkFDTixpQkFBaUIsRUFBRSxJQUFJLENBQUMsZ0JBQWdCO2dCQUN4QyxhQUFhLEVBQUUsSUFBSSxDQUFDLGNBQWM7Z0JBQ2xDLGNBQWMsRUFBRSxJQUFJLENBQUMsY0FBYztnQkFDbkMsb0JBQW9CLEVBQUUsSUFBSSxDQUFDLG9CQUFvQjtnQkFDL0Msd0JBQXdCLEVBQUUsSUFBSSxDQUFDLHdCQUF3QjtnQkFDdkQsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsSUFBSSxJQUFJLENBQUM7Z0JBQ2xELFdBQVcsRUFBRSxPQUFPLENBQUMsV0FBVyxFQUFFO2dCQUNsQyxjQUFjLEVBQUUsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDO2dCQUMvQyxjQUFjLEVBQUUsTUFBTSxDQUFDLFdBQVcsQ0FDaEMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsV0FBVyxDQUFDLEVBQUUsRUFBRSxDQUFDO29CQUNyRSxJQUFJO29CQUNKO3dCQUNFLEtBQUssRUFBRSxXQUFXLENBQUMsTUFBTTt3QkFDekIsSUFBSSxFQUFFLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsTUFBTTtxQkFDL0M7aUJBQ0YsQ0FBQyxDQUNIO2FBQ0YsQ0FBQztZQUVGLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM5QyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQywyQkFBMkI7SUFDeEMsQ0FBQztJQUVEOztPQUVHO0lBQ0ssMEJBQTBCO1FBQ2hDLHlDQUF5QztRQUN6QyxJQUFJLENBQUMsNkJBQTZCLEdBQUcsV0FBVyxDQUFDLEdBQUcsRUFBRTtZQUNwRCxJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUMvQixDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxXQUFXO0lBQ3hCLENBQUM7SUFFRDs7T0FFRztJQUNLLHlCQUF5QixDQUFDLFVBQW1DLEVBQUUsTUFBb0M7UUFDekcsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQztRQUMxQixNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQztRQUVuQyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSw0QkFBNEIsTUFBTSxHQUFHLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFFaEUsMkJBQTJCO1FBQzNCLFVBQVUsQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO1FBQzFCLFVBQVUsQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ2pDLFVBQVUsQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRTtZQUN6QixVQUFVLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztZQUMxQixVQUFVLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNuQyxDQUFDLENBQUMsQ0FBQztRQUVILG9DQUFvQztRQUNwQyxNQUFNLG1CQUFtQixHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3pELElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1lBQ3pCLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGdDQUFnQyxNQUFNLEdBQUcsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUNwRSxVQUFVLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDdkIsT0FBTztRQUNULENBQUM7UUFFRCxtQ0FBbUM7UUFDbkMsSUFBSSxtQkFBbUIsQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN2QyxJQUFJLENBQUM7Z0JBQ0gsSUFBSSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsbUJBQW1CLENBQUMsRUFBRSxDQUFDO29CQUMzRCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSx1Q0FBdUMsTUFBTSxHQUFHLE1BQU0sRUFBRSxDQUFDLENBQUM7b0JBQzNFLFVBQVUsQ0FBQyxTQUFTLEVBQUUsQ0FBQztvQkFDdkIsT0FBTztnQkFDVCxDQUFDO1lBQ0gsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsZ0NBQWdDLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQzNELFVBQVUsQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDdkIsT0FBTztZQUNULENBQUM7UUFDSCxDQUFDO1FBRUQsc0NBQXNDO1FBQ3RDLElBQUksVUFBNkIsQ0FBQztRQUNsQyxNQUFNLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsS0FBSyxFQUFFLENBQUM7UUFFdEQsSUFBSSxDQUFDO1lBQ0gsK0NBQStDO1lBQy9DLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1lBQ3RFLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxDQUFDLHFCQUFxQixDQUFDLG1CQUFtQixDQUFDLENBQUM7WUFDMUUsTUFBTSxRQUFRLEdBQUcsUUFBUSxlQUFlLElBQUksaUJBQWlCLEdBQUcsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQzdFLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHlCQUF5QixRQUFRLEVBQUUsQ0FBQyxDQUFDO1lBRXZELFVBQVUsR0FBRyxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUM7WUFFN0MsVUFBVSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFO2dCQUN6QixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSwyQ0FBMkMsQ0FBQyxDQUFDO2dCQUMvRCxnQkFBZ0IsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUM3QixDQUFDLENBQUMsQ0FBQztZQUVILFVBQVUsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxFQUFFLEVBQUU7Z0JBQy9CLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDBCQUEwQixFQUFFLEtBQUssQ0FBQyxDQUFDO2dCQUNyRCxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQy9CLElBQUksVUFBVSxDQUFDLFVBQVUsS0FBSyxVQUFVLENBQUMsSUFBSSxFQUFFLENBQUM7b0JBQzlDLFVBQVUsQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDekIsQ0FBQztZQUNILENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxnREFBZ0QsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUN6RSxVQUFVLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDdkIsT0FBTztRQUNULENBQUM7UUFFRCxtREFBbUQ7UUFDbkQsVUFBVSxDQUFDLEVBQUUsQ0FBQyxTQUFTLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsRUFBRTtZQUNuRCxJQUFJLENBQUM7Z0JBQ0gsMkNBQTJDO2dCQUMzQyxNQUFNLGdCQUFnQixDQUFDLE9BQU8sQ0FBQztnQkFFL0Isa0RBQWtEO2dCQUNsRCxJQUFJLFVBQVUsQ0FBQyxVQUFVLEtBQUssVUFBVSxDQUFDLElBQUksRUFBRSxDQUFDO29CQUM5QyxVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO2dCQUNqRCxDQUFDO1lBQ0gsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsK0NBQStDLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDNUUsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO1FBRUgsbURBQW1EO1FBQ25ELFVBQVUsQ0FBQyxFQUFFLENBQUMsU0FBUyxFQUFFLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxFQUFFO1lBQzdDLElBQUksQ0FBQztnQkFDSCx3REFBd0Q7Z0JBQ3hELElBQUksVUFBVSxDQUFDLFVBQVUsS0FBSyxVQUFVLENBQUMsSUFBSSxFQUFFLENBQUM7b0JBQzlDLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUM7Z0JBQ2pELENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSw4Q0FBOEMsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUMzRSxDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7UUFFSCwrQ0FBK0M7UUFDL0MsVUFBVSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFDdEMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsOEJBQThCLElBQUksTUFBTSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQ3BFLElBQUksVUFBVSxJQUFJLFVBQVUsQ0FBQyxVQUFVLEtBQUssVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUM5RCxJQUFJLENBQUM7b0JBQ0gsaUVBQWlFO29CQUNqRSxNQUFNLFNBQVMsR0FBRyxDQUFDLElBQUksSUFBSSxJQUFJLElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztvQkFDL0QsVUFBVSxDQUFDLEtBQUssQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO2dCQUN2RCxDQUFDO2dCQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7b0JBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsa0NBQWtDLEVBQUUsS0FBSyxDQUFDLENBQUM7b0JBQzdELFVBQVUsQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDekIsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztRQUVILFVBQVUsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsSUFBSSxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQ3RDLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDhCQUE4QixJQUFJLE1BQU0sTUFBTSxFQUFFLENBQUMsQ0FBQztZQUNwRSxJQUFJLFVBQVUsSUFBSSxVQUFVLENBQUMsVUFBVSxLQUFLLFVBQVUsQ0FBQyxNQUFNLEVBQUUsQ0FBQztnQkFDOUQsSUFBSSxDQUFDO29CQUNILGlFQUFpRTtvQkFDakUsTUFBTSxTQUFTLEdBQUcsQ0FBQyxJQUFJLElBQUksSUFBSSxJQUFJLElBQUksSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7b0JBQy9ELFVBQVUsQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDdkQsQ0FBQztnQkFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO29CQUNmLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGtDQUFrQyxFQUFFLEtBQUssQ0FBQyxDQUFDO29CQUM3RCxVQUFVLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ3pCLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxLQUFLLENBQUMsYUFBYSxDQUN6QixhQUEyQyxFQUMzQyxjQUEyQztRQUUzQyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDdEIsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQzdCLE1BQU0sS0FBSyxHQUFHLE9BQU8sSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDO1FBRWhGLElBQUksQ0FBQztZQUNILE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDMUQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsSUFBSSxLQUFLLEtBQUssYUFBYSxDQUFDLE1BQU0sSUFBSSxhQUFhLENBQUMsT0FBTyxDQUFDLElBQUksR0FBRyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBRS9GLDZDQUE2QztZQUM3QyxJQUFJLGFBQWEsQ0FBQyxNQUFNLEtBQUssU0FBUyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBQzVELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxhQUFhLEVBQUUsY0FBYyxDQUFDLENBQUM7Z0JBQ3RELE9BQU87WUFDVCxDQUFDO1lBRUQsZ0NBQWdDO1lBQ2hDLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUM7WUFDOUQsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7Z0JBQ3ZCLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLElBQUksS0FBSyx3QkFBd0IsYUFBYSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO2dCQUNoRixJQUFJLENBQUMsaUJBQWlCLENBQUMsY0FBYyxFQUFFLEdBQUcsRUFBRSw4QkFBOEIsQ0FBQyxDQUFDO2dCQUM1RSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7Z0JBQ3RCLE9BQU87WUFDVCxDQUFDO1lBRUQsc0NBQXNDO1lBQ3RDLElBQUksaUJBQWlCLENBQUMsY0FBYyxFQUFFLENBQUM7Z0JBQ3JDLElBQUksQ0FBQztvQkFDSCxJQUFJLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGFBQWEsRUFBRSxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7d0JBQ2hFLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxjQUFjLEVBQUUsR0FBRyxFQUFFLGNBQWMsRUFBRTs0QkFDMUQsa0JBQWtCLEVBQUUseURBQXlEO3lCQUM5RSxDQUFDLENBQUM7d0JBQ0gsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO3dCQUN0QixPQUFPO29CQUNULENBQUM7Z0JBQ0gsQ0FBQztnQkFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO29CQUNmLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLElBQUksS0FBSyx3QkFBd0IsRUFBRSxLQUFLLENBQUMsQ0FBQztvQkFDNUQsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGNBQWMsRUFBRSxHQUFHLEVBQUUsOENBQThDLENBQUMsQ0FBQztvQkFDNUYsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO29CQUN0QixPQUFPO2dCQUNULENBQUM7WUFDSCxDQUFDO1lBRUQsZ0RBQWdEO1lBQ2hELE1BQU0saUJBQWlCLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxvQkFBb0I7Z0JBQ2pDLGFBQWEsQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUVwRixpREFBaUQ7WUFDakQsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGlCQUFpQixDQUFDLENBQUM7WUFDbEUsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLHFCQUFxQixDQUFDLGlCQUFpQixDQUFDLENBQUM7WUFFdEUsNEJBQTRCO1lBQzVCLE1BQU0sY0FBYyxHQUFHLFVBQVUsYUFBYSxJQUFJLGVBQWUsR0FBRyxhQUFhLENBQUMsR0FBRyxFQUFFLENBQUM7WUFFeEYsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO2dCQUN0QixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLEtBQUssaUJBQWlCLGNBQWMsMEJBQTBCLENBQUMsQ0FBQztnQkFDdEYsTUFBTSxJQUFJLENBQUMsaUNBQWlDLENBQzFDLEtBQUssRUFDTCxhQUFhLEVBQ2IsY0FBYyxFQUNkLGFBQWEsRUFDYixlQUFlLEVBQ2YsYUFBYSxDQUFDLEdBQUcsQ0FDbEIsQ0FBQztZQUNKLENBQUM7aUJBQU0sQ0FBQztnQkFDTixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLEtBQUssaUJBQWlCLGNBQWMsRUFBRSxDQUFDLENBQUM7Z0JBQzlELE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsYUFBYSxFQUFFLGNBQWMsRUFBRSxjQUFjLENBQUMsQ0FBQztZQUNsRixDQUFDO1lBRUQsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLFNBQVMsQ0FBQztZQUM5QyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLEtBQUssMEJBQTBCLGNBQWMsSUFBSSxDQUFDLENBQUM7UUFDM0UsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLEtBQUssc0NBQXNDLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDMUUsSUFBSSxDQUFDO2dCQUNILElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxjQUFjLEVBQUUsR0FBRyxFQUFFLDJCQUEyQixDQUFDLENBQUM7WUFDM0UsQ0FBQztZQUFDLE9BQU8sYUFBYSxFQUFFLENBQUM7Z0JBQ3ZCLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLElBQUksS0FBSyxpQ0FBaUMsRUFBRSxhQUFhLENBQUMsQ0FBQztZQUMvRSxDQUFDO1lBQ0QsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3hCLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxpQkFBaUIsQ0FDdkIsR0FBaUMsRUFDakMsR0FBZ0M7UUFFaEMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7UUFFL0IsbUJBQW1CO1FBQ25CLEdBQUcsQ0FBQyxTQUFTLENBQUMsNkJBQTZCLEVBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQy9ELEdBQUcsQ0FBQyxTQUFTLENBQUMsOEJBQThCLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ2pFLEdBQUcsQ0FBQyxTQUFTLENBQUMsOEJBQThCLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ2pFLEdBQUcsQ0FBQyxTQUFTLENBQUMsd0JBQXdCLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBRTdELDJCQUEyQjtRQUMzQixHQUFHLENBQUMsVUFBVSxHQUFHLEdBQUcsQ0FBQztRQUNyQixHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7UUFFVixpQ0FBaUM7UUFDakMsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7T0FFRztJQUNLLG1CQUFtQixDQUN6QixHQUFpQyxFQUNqQyxNQUFtRDtRQUVuRCxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsY0FBYyxDQUFDO1FBQ3ZDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNkLE9BQU8sSUFBSSxDQUFDLENBQUMsNkJBQTZCO1FBQzVDLENBQUM7UUFFRCxRQUFRLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUN0QixLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUM7Z0JBQ2IsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUM7Z0JBQzdDLElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7b0JBQ2xELE9BQU8sS0FBSyxDQUFDO2dCQUNmLENBQUM7Z0JBRUQsTUFBTSxnQkFBZ0IsR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDMUQsTUFBTSxVQUFVLEdBQVcsT0FBTyxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7Z0JBQy9FLE1BQU0sQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFFM0MseURBQXlEO2dCQUN6RCxNQUFNLFNBQVMsR0FBRyxJQUFJLEtBQUssUUFBUSxDQUFDLElBQUksQ0FBQztnQkFDekMsTUFBTSxTQUFTLEdBQUcsSUFBSSxLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUM7Z0JBRXpDLE9BQU8sU0FBUyxJQUFJLFNBQVMsQ0FBQztZQUNoQyxDQUFDO1lBQ0Q7Z0JBQ0UsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQ0FBc0MsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDM0UsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSyxLQUFLLENBQUMsaUNBQWlDLENBQzdDLEtBQWEsRUFDYixhQUEyQyxFQUMzQyxjQUEyQyxFQUMzQyxJQUFZLEVBQ1osSUFBWSxFQUNaLElBQVk7UUFFWixJQUFJLENBQUM7WUFDSCx3Q0FBd0M7WUFDeEMsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMscUJBQXFCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO1lBRTVELHdEQUF3RDtZQUN4RCxNQUFNLFVBQVUsR0FBRztnQkFDakIsZ0JBQWdCLEVBQUUsR0FBRyxFQUFFLENBQUMsTUFBTTtnQkFDOUIsSUFBSTtnQkFDSixJQUFJO2dCQUNKLElBQUk7Z0JBQ0osTUFBTSxFQUFFLGFBQWEsQ0FBQyxNQUFNO2dCQUM1QixPQUFPLEVBQUUsSUFBSSxDQUFDLHFCQUFxQixDQUFDLGFBQWEsQ0FBQztnQkFDbEQsT0FBTyxFQUFFLEtBQUssQ0FBQyxvQkFBb0I7YUFDcEMsQ0FBQztZQUVGLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBRWxELGtCQUFrQjtZQUNsQixRQUFRLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxHQUFHLEVBQUU7Z0JBQzFCLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLElBQUksS0FBSyxnQkFBZ0IsSUFBSSxJQUFJLElBQUksR0FBRyxJQUFJLFlBQVksQ0FBQyxDQUFDO2dCQUMzRSxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDckIsQ0FBQyxDQUFDLENBQUM7WUFFSCxnQkFBZ0I7WUFDaEIsUUFBUSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRTtnQkFDM0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsSUFBSSxLQUFLLCtCQUErQixJQUFJLElBQUksSUFBSSxHQUFHLElBQUksRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUV0RixpREFBaUQ7Z0JBQ2pELElBQUksQ0FBQyxjQUFjLENBQUMsYUFBYSxFQUFFLENBQUM7b0JBQ2xDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxjQUFjLEVBQUUsR0FBRyxFQUFFLHVEQUF1RCxDQUFDLENBQUM7Z0JBQ3ZHLENBQUM7Z0JBRUQsK0NBQStDO2dCQUMvQyxJQUFJLENBQUM7b0JBQ0gsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsQ0FBQzt3QkFDdEIsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO29CQUNuQixDQUFDO2dCQUNILENBQUM7Z0JBQUMsT0FBTyxTQUFTLEVBQUUsQ0FBQztvQkFDbkIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsSUFBSSxLQUFLLCtDQUErQyxFQUFFLFNBQVMsQ0FBQyxDQUFDO2dCQUN6RixDQUFDO1lBQ0gsQ0FBQyxDQUFDLENBQUM7WUFFSCx1QkFBdUI7WUFDdkIsYUFBYSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUU3QixrQkFBa0I7WUFDbEIsUUFBUSxDQUFDLEVBQUUsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxRQUFRLEVBQUUsRUFBRTtnQkFDbkMsMEJBQTBCO2dCQUMxQixjQUFjLENBQUMsVUFBVSxHQUFHLFFBQVEsQ0FBQyxVQUFVLENBQUM7Z0JBRWhELEtBQUssTUFBTSxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO29CQUM3RCxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQzt3QkFDeEIsY0FBYyxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUM7b0JBQ3hDLENBQUM7Z0JBQ0gsQ0FBQztnQkFFRCw0QkFBNEI7Z0JBQzVCLFFBQVEsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7Z0JBRTlCLHdEQUF3RDtnQkFDeEQsUUFBUSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFO29CQUN0QixJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDO3dCQUN0QixJQUFJLENBQUMsc0JBQXNCLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztvQkFDbEQsQ0FBQztnQkFDSCxDQUFDLENBQUMsQ0FBQztnQkFFSCxRQUFRLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO29CQUMzQixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLEtBQUssa0NBQWtDLElBQUksSUFBSSxJQUFJLEdBQUcsSUFBSSxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUM7b0JBRXpGLCtDQUErQztvQkFDL0MsSUFBSSxDQUFDO3dCQUNILElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUM7NEJBQ3RCLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQzt3QkFDbkIsQ0FBQztvQkFDSCxDQUFDO29CQUFDLE9BQU8sU0FBUyxFQUFFLENBQUM7d0JBQ25CLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLElBQUksS0FBSyxnREFBZ0QsRUFBRSxTQUFTLENBQUMsQ0FBQztvQkFDMUYsQ0FBQztnQkFDSCxDQUFDLENBQUMsQ0FBQztZQUNMLENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLEtBQUssMkNBQTJDLElBQUksSUFBSSxJQUFJLEVBQUUsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUM3RixJQUFJLENBQUMsaUJBQWlCLENBQUMsY0FBYyxFQUFFLEdBQUcsRUFBRSw4Q0FBOEMsQ0FBQyxDQUFDO1lBQzVGLE1BQU0sS0FBSyxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxjQUFjLENBQzFCLEtBQWEsRUFDYixhQUEyQyxFQUMzQyxjQUEyQyxFQUMzQyxjQUFzQjtRQUV0QixJQUFJLENBQUM7WUFDSCxNQUFNLFlBQVksR0FBRyxNQUFNLE9BQU8sQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUNyRCxjQUFjLEVBQ2Q7Z0JBQ0UsTUFBTSxFQUFFLGFBQWEsQ0FBQyxNQUFNO2dCQUM1QixPQUFPLEVBQUUsSUFBSSxDQUFDLHFCQUFxQixDQUFDLGFBQWEsQ0FBQztnQkFDbEQsU0FBUyxFQUFFLElBQUk7Z0JBQ2YsT0FBTyxFQUFFLEtBQUssQ0FBQyxvQkFBb0I7YUFDcEMsRUFDRCxJQUFJLEVBQUUsWUFBWTtZQUNsQixDQUFDLGtCQUFrQixFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMscUJBQXFCLENBQUMsYUFBYSxFQUFFLGtCQUFrQixDQUFDLENBQ3RGLENBQUM7WUFFRixzQkFBc0I7WUFDdEIsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEtBQUssRUFBRSxjQUFjLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFDakUsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLEtBQUssNEJBQTRCLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDaEUsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGNBQWMsRUFBRSxHQUFHLEVBQUUsOENBQThDLENBQUMsQ0FBQztZQUM1RixNQUFNLEtBQUssQ0FBQyxDQUFDLGtDQUFrQztRQUNqRCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0sscUJBQXFCLENBQUMsR0FBaUM7UUFDN0QsTUFBTSxXQUFXLEdBQUcsRUFBRSxHQUFHLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUV2Qyx5QkFBeUI7UUFDekIsV0FBVyxDQUFDLGtCQUFrQixDQUFDLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7UUFDbkQsV0FBVyxDQUFDLG1CQUFtQixDQUFDLEdBQUcsT0FBTyxDQUFDO1FBQzNDLFdBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxhQUFhLElBQUksRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUUxRiw2QkFBNkI7UUFDN0IsV0FBVyxDQUFDLFlBQVksQ0FBQyxHQUFHLGdCQUFnQixJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO1FBRWhFLGtFQUFrRTtRQUNsRSxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsb0JBQW9CLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxhQUFhLEVBQUUsUUFBUSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7WUFDekYsV0FBVyxDQUFDLHVCQUF1QixDQUFDLEdBQUcsTUFBTSxDQUFDO1FBQ2hELENBQUM7UUFFRCxvREFBb0Q7UUFDcEQsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLFlBQVksRUFBRSxTQUFTLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztRQUNyRSxLQUFLLE1BQU0sTUFBTSxJQUFJLGdCQUFnQixFQUFFLENBQUM7WUFDdEMsT0FBTyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDN0IsQ0FBQztRQUVELE9BQU8sV0FBVyxDQUFDO0lBQ3JCLENBQUM7SUFFRDs7T0FFRztJQUNLLHFCQUFxQixDQUMzQixhQUEyQyxFQUMzQyxZQUF3QztRQUV4Qyw0QkFBNEI7UUFDNUIsYUFBYSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUNqQyxZQUFZLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzVCLENBQUMsQ0FBQyxDQUFDO1FBRUgsNEJBQTRCO1FBQzVCLGFBQWEsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRTtZQUMzQixZQUFZLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDckIsQ0FBQyxDQUFDLENBQUM7UUFFSCx3QkFBd0I7UUFDeEIsYUFBYSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUNsQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxnQ0FBZ0MsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUMzRCxZQUFZLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzlCLENBQUMsQ0FBQyxDQUFDO1FBRUgsOEJBQThCO1FBQzlCLGFBQWEsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRTtZQUM3QixJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUM1QixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxtREFBbUQsQ0FBQyxDQUFDO2dCQUN2RSxZQUFZLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDekIsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO1FBRUgsYUFBYSxDQUFDLEVBQUUsQ0FBQyxTQUFTLEVBQUUsR0FBRyxFQUFFO1lBQy9CLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHdCQUF3QixDQUFDLENBQUM7WUFDNUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEtBQUssQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDLENBQUM7UUFDNUQsQ0FBQyxDQUFDLENBQUM7UUFFSCw4QkFBOEI7UUFDOUIsWUFBWSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUNqQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxpQ0FBaUMsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUM5RCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNLLG9CQUFvQixDQUMxQixLQUFhLEVBQ2IsY0FBMkMsRUFDM0MsYUFBMkM7UUFFM0MsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsSUFBSSxLQUFLLGlDQUFpQyxhQUFhLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQztRQUV4RixrQkFBa0I7UUFDbEIsY0FBYyxDQUFDLFVBQVUsR0FBRyxhQUFhLENBQUMsVUFBVSxDQUFDO1FBRXJELHNCQUFzQjtRQUN0QixLQUFLLE1BQU0sQ0FBQyxVQUFVLEVBQUUsV0FBVyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztZQUM1RSxjQUFjLENBQUMsU0FBUyxDQUFDLFVBQVUsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUNwRCxDQUFDO1FBRUQsOEJBQThCO1FBQzlCLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUN0QixjQUFjLENBQUMsU0FBUyxDQUFDLDZCQUE2QixFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3pGLENBQUM7UUFFRCx3QkFBd0I7UUFDeEIsS0FBSyxNQUFNLENBQUMsVUFBVSxFQUFFLFdBQVcsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDOUUsMEJBQTBCO1lBQzFCLE1BQU0sZUFBZSxHQUFHLENBQUMsWUFBWSxFQUFFLFlBQVksRUFBRSxtQkFBbUIsRUFBRSxJQUFJO2dCQUNyRCxTQUFTLEVBQUUsU0FBUyxFQUFFLHFCQUFxQixFQUFFLG9CQUFvQixDQUFDLENBQUM7WUFDNUYsSUFBSSxDQUFDLGVBQWUsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLFdBQVcsRUFBRSxDQUFDLEVBQUUsQ0FBQztnQkFDeEQsY0FBYyxDQUFDLFNBQVMsQ0FBQyxVQUFVLEVBQUUsV0FBVyxDQUFDLENBQUM7WUFDcEQsQ0FBQztRQUNILENBQUM7UUFFRCx1QkFBdUI7UUFDdkIsYUFBYSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUNqQyxNQUFNLFdBQVcsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBRWhELCtCQUErQjtZQUMvQixJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7Z0JBQ2pCLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDdEIsY0FBYyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFO29CQUNoQyxhQUFhLENBQUMsTUFBTSxFQUFFLENBQUM7Z0JBQ3pCLENBQUMsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO1FBRUgsNkJBQTZCO1FBQzdCLGFBQWEsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRTtZQUMzQixjQUFjLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDdkIsQ0FBQyxDQUFDLENBQUM7UUFFSCx5QkFBeUI7UUFDekIsYUFBYSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUNsQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLEtBQUssa0NBQWtDLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDdEUsY0FBYyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNoQyxDQUFDLENBQUMsQ0FBQztRQUVILGNBQWMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDbkMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsSUFBSSxLQUFLLG1DQUFtQyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ3ZFLGFBQWEsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMxQixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNLLGlCQUFpQixDQUN2QixHQUFnQyxFQUNoQyxhQUFxQixHQUFHLEVBQ3hCLFVBQWtCLHVCQUF1QixFQUN6QyxVQUE0QyxFQUFFO1FBRTlDLElBQUksQ0FBQztZQUNILGlEQUFpRDtZQUNqRCxJQUFJLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDcEIsR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDO2dCQUNWLE9BQU87WUFDVCxDQUFDO1lBRUQsc0JBQXNCO1lBQ3RCLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDO2dCQUMvRCxHQUFHLENBQUMsU0FBUyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUM1QixDQUFDO1lBRUQsdUJBQXVCO1lBQ3ZCLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQ25ELEdBQUcsQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzVCLENBQUM7WUFFRCxzQkFBc0I7WUFDdEIsR0FBRyxDQUFDLFNBQVMsQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFFbkMsMENBQTBDO1lBQzFDLElBQUksR0FBRyxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsS0FBSyxrQkFBa0IsRUFBRSxDQUFDO2dCQUN6RCxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEVBQUUsRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ3RFLENBQUM7aUJBQU0sQ0FBQztnQkFDTixxQkFBcUI7Z0JBQ3JCLEdBQUcsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDbkIsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsOEJBQThCLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDekQsSUFBSSxDQUFDO2dCQUNILEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNoQixDQUFDO1lBQUMsT0FBTyxZQUFZLEVBQUUsQ0FBQztnQkFDdEIsdUNBQXVDO1lBQ3pDLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxtQkFBbUIsQ0FBQyxNQUFtRDtRQUM3RSwwQkFBMEI7UUFDMUIsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxNQUFNLENBQUMsY0FBYyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM3RSw0Q0FBNEM7WUFDNUMsTUFBTSxHQUFHLEdBQUcsTUFBTSxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDcEMsSUFBSSxRQUFRLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7WUFFdEQsa0NBQWtDO1lBQ2xDLE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLENBQUM7WUFFbkQsb0NBQW9DO1lBQ3BDLFFBQVEsR0FBRyxDQUFDLFFBQVEsR0FBRyxDQUFDLENBQUMsR0FBRyxNQUFNLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQztZQUN6RCxJQUFJLENBQUMsbUJBQW1CLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxRQUFRLENBQUMsQ0FBQztZQUU1QyxPQUFPLFVBQVUsQ0FBQztRQUNwQixDQUFDO1FBRUQsNkVBQTZFO1FBQzdFLG1FQUFtRTtRQUNuRSxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUNsRCxHQUFHLENBQUMsUUFBUSxLQUFLLE1BQU0sQ0FBQyxRQUFRO1lBQy9CLEdBQVcsQ0FBQyxhQUFhLENBQzNCLENBQUM7UUFFRixJQUFJLGNBQWMsRUFBRSxDQUFDO1lBQ25CLE9BQVEsY0FBc0IsQ0FBQyxhQUFhLENBQUM7UUFDL0MsQ0FBQztRQUVELHdCQUF3QjtRQUN4QixPQUFPLFdBQVcsQ0FBQztJQUNyQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLHFCQUFxQixDQUFDLE1BQW1EO1FBQy9FLDBCQUEwQjtRQUMxQixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLElBQUksTUFBTSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNqRiw0Q0FBNEM7WUFDNUMsTUFBTSxHQUFHLEdBQUcsUUFBUSxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDdEMsSUFBSSxRQUFRLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7WUFFdEQsb0NBQW9DO1lBQ3BDLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUV2RCxvQ0FBb0M7WUFDcEMsUUFBUSxHQUFHLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUM7WUFDM0QsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFFNUMsT0FBTyxZQUFZLENBQUM7UUFDdEIsQ0FBQztRQUVELDZFQUE2RTtRQUM3RSxtRUFBbUU7UUFDbkUsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FDbEQsR0FBRyxDQUFDLFFBQVEsS0FBSyxNQUFNLENBQUMsUUFBUTtZQUMvQixHQUFXLENBQUMsZUFBZSxDQUM3QixDQUFDO1FBRUYsSUFBSSxjQUFjLEVBQUUsQ0FBQztZQUNuQixPQUFPLFFBQVEsQ0FBRSxjQUFzQixDQUFDLGVBQWUsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUMvRCxDQUFDO1FBRUQsc0JBQXNCO1FBQ3RCLE9BQU8sRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLGtCQUFrQixDQUM3QixlQUE4RDtRQUU5RCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxrQ0FBa0MsZUFBZSxDQUFDLE1BQU0sV0FBVyxDQUFDLENBQUM7UUFFdEYsMEJBQTBCO1FBQzFCLElBQUksQ0FBQyxZQUFZLEdBQUcsZUFBZSxDQUFDO1FBQ3BDLElBQUksQ0FBQyxNQUFNLENBQUMsa0JBQWtCLENBQUMsZUFBZSxDQUFDLENBQUM7UUFFaEQsMENBQTBDO1FBQzFDLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztRQUUzQyx3Q0FBd0M7UUFDeEMsS0FBSyxNQUFNLE1BQU0sSUFBSSxlQUFlLEVBQUUsQ0FBQztZQUNyQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBRXRDLElBQUksQ0FBQztnQkFDSCxzQ0FBc0M7Z0JBQ3RDLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO2dCQUMvRCxNQUFNLFlBQVksR0FBRyxDQUFDLFdBQVc7b0JBQ2IsV0FBVyxDQUFDLEdBQUcsS0FBSyxNQUFNLENBQUMsVUFBVTtvQkFDckMsV0FBVyxDQUFDLElBQUksS0FBSyxNQUFNLENBQUMsU0FBUyxDQUFDO2dCQUUxRCxJQUFJLFlBQVksRUFBRSxDQUFDO29CQUNqQixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSw0QkFBNEIsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7b0JBRWpFLGtDQUFrQztvQkFDbEMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRTt3QkFDM0MsR0FBRyxFQUFFLE1BQU0sQ0FBQyxVQUFVO3dCQUN0QixJQUFJLEVBQUUsTUFBTSxDQUFDLFNBQVM7cUJBQ3ZCLENBQUMsQ0FBQztvQkFFSCxtQkFBbUI7b0JBQ25CLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRTt3QkFDekMsR0FBRyxFQUFFLE1BQU0sQ0FBQyxVQUFVO3dCQUN0QixJQUFJLEVBQUUsTUFBTSxDQUFDLFNBQVM7cUJBQ3ZCLENBQUMsQ0FBQztvQkFFSCxJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQzNDLENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxpQ0FBaUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQy9FLENBQUM7UUFDSCxDQUFDO1FBRUQsNEJBQTRCO1FBQzVCLDZEQUE2RDtRQUM3RCxrREFBa0Q7UUFDbEQsS0FBSyxNQUFNLFFBQVEsSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDM0MsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO2dCQUNwQyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxZQUFZLFFBQVEsNkJBQTZCLENBQUMsQ0FBQztnQkFDcEUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQ3JDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDekMsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSx1QkFBdUIsQ0FDNUIsYUFJRSxFQUNGLFVBQTBDO1FBRTFDLE1BQU0sWUFBWSxHQUFrRCxFQUFFLENBQUM7UUFFdkUsMkNBQTJDO1FBQzNDLE1BQU0sTUFBTSxHQUFHLFVBQVUsRUFBRSxHQUFHLElBQUksSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQztRQUMvRCxNQUFNLE9BQU8sR0FBRyxVQUFVLEVBQUUsSUFBSSxJQUFJLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUM7UUFFbEUsS0FBSyxNQUFNLFlBQVksSUFBSSxhQUFhLEVBQUUsQ0FBQztZQUN6Qyx1REFBdUQ7WUFDdkQsS0FBSyxNQUFNLE1BQU0sSUFBSSxZQUFZLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQzFDLGlEQUFpRDtnQkFDakQsSUFBSSxNQUFNLENBQUMsS0FBSyxDQUFDLHNCQUFzQixDQUFDLElBQUksTUFBTSxLQUFLLEdBQUcsSUFBSSxNQUFNLEtBQUssV0FBVyxFQUFFLENBQUM7b0JBQ3JGLFNBQVM7Z0JBQ1gsQ0FBQztnQkFFRCxZQUFZLENBQUMsSUFBSSxDQUFDO29CQUNoQixRQUFRLEVBQUUsTUFBTTtvQkFDaEIsY0FBYyxFQUFFLFlBQVksQ0FBQyxTQUFTLElBQUksQ0FBQyxXQUFXLENBQUM7b0JBQ3ZELGdCQUFnQixFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSw0QkFBNEI7b0JBQ25FLFVBQVUsRUFBRSxNQUFNO29CQUNsQixTQUFTLEVBQUUsT0FBTztpQkFDbkIsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztRQUNILENBQUM7UUFFRCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxhQUFhLGFBQWEsQ0FBQyxNQUFNLHlCQUF5QixZQUFZLENBQUMsTUFBTSx1QkFBdUIsQ0FBQyxDQUFDO1FBQ3ZILE9BQU8sWUFBWSxDQUFDO0lBQ3RCLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxVQUFxQztRQUNsRSxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSx3QkFBd0IsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUN2RCxJQUFJLENBQUMsY0FBYyxHQUFHO1lBQ3BCLEdBQUcsSUFBSSxDQUFDLGNBQWM7WUFDdEIsR0FBRyxVQUFVO1NBQ2QsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxJQUFJO1FBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsOEJBQThCLENBQUMsQ0FBQztRQUVqRCxrQkFBa0I7UUFDbEIsSUFBSSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUMzQixhQUFhLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDeEMsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQ3pCLGFBQWEsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDdEMsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLDZCQUE2QixFQUFFLENBQUM7WUFDdkMsYUFBYSxDQUFDLElBQUksQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO1FBQ3BELENBQUM7UUFFRCxtQ0FBbUM7UUFDbkMsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDbEIsS0FBSyxNQUFNLE1BQU0sSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUMzQyxJQUFJLENBQUM7b0JBQ0gsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDO2dCQUNyQixDQUFDO2dCQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7b0JBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsb0NBQW9DLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQ2pFLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELDRCQUE0QjtRQUM1QixLQUFLLE1BQU0sTUFBTSxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQztZQUMvQyxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ25CLENBQUM7WUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO2dCQUNmLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHlCQUF5QixFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ3RELENBQUM7UUFDSCxDQUFDO1FBRUQsd0NBQXdDO1FBQ3hDLEtBQUssTUFBTSxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUM7WUFDaEUsS0FBSyxNQUFNLFVBQVUsSUFBSSxXQUFXLEVBQUUsQ0FBQztnQkFDckMsSUFBSSxDQUFDO29CQUNILElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDO3dCQUNqQyxVQUFVLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO29CQUM5QixDQUFDO2dCQUNILENBQUM7Z0JBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztvQkFDZixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSx5Q0FBeUMsSUFBSSxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQzVFLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELElBQUksQ0FBQyxjQUFjLENBQUMsS0FBSyxFQUFFLENBQUM7UUFFNUIscUNBQXFDO1FBQ3JDLElBQUksSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQztnQkFDSCxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBQ2hDLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLHVCQUF1QixDQUFDLENBQUM7WUFDNUMsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsOEJBQThCLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDM0QsQ0FBQztRQUNILENBQUM7UUFFRCx5QkFBeUI7UUFDekIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzdCLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRTtnQkFDMUIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsMENBQTBDLENBQUMsQ0FBQztnQkFDN0QsT0FBTyxFQUFFLENBQUM7WUFDWixDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ksS0FBSyxDQUFDLGtCQUFrQixDQUFDLE1BQWM7UUFDNUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQy9CLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLDRDQUE0QyxDQUFDLENBQUM7WUFDL0QsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBRUQsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUN4QixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxrQ0FBa0MsQ0FBQyxDQUFDO1lBQ3RELE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztRQUVELDRFQUE0RTtRQUM1RSxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN6QixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxtREFBbUQsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUMvRSxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFFRCxJQUFJLENBQUM7WUFDSCxvQ0FBb0M7WUFDcEMsTUFBTSxhQUFhLEdBQW1CO2dCQUNwQyxVQUFVLEVBQUUsTUFBTTtnQkFDbEIsV0FBVyxFQUFFLElBQUk7Z0JBQ2pCLGVBQWUsRUFBRSxJQUFJO2FBQ3RCLENBQUM7WUFFRixJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsQ0FBQztZQUM1QyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSw2Q0FBNkMsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUN4RSxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsMkNBQTJDLE1BQU0sR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQy9FLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSyxzQkFBc0IsQ0FBQyxNQUFjLEVBQUUsV0FBbUIsRUFBRSxVQUFrQixFQUFFLFVBQWlCO1FBQ3ZHLDZEQUE2RDtRQUM3RCxJQUFJLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNyQixJQUFJLENBQUM7Z0JBQ0gsSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFO29CQUNsQyxHQUFHLEVBQUUsVUFBVTtvQkFDZixJQUFJLEVBQUUsV0FBVztpQkFDbEIsQ0FBQyxDQUFDO2dCQUNILElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLG1DQUFtQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQ2pFLENBQUM7WUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO2dCQUNmLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHlDQUF5QyxNQUFNLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUMvRSxDQUFDO1FBQ0gsQ0FBQztRQUVELDhCQUE4QjtRQUM5QixJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRTtZQUNoQyxHQUFHLEVBQUUsVUFBVTtZQUNmLElBQUksRUFBRSxXQUFXO1lBQ2pCLE9BQU8sRUFBRSxVQUFVO1NBQ3BCLENBQUMsQ0FBQztRQUVILDZCQUE2QjtRQUM3QixJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNsQyxDQUFDO0lBRUQ7O09BRUc7SUFDSyxHQUFHLENBQUMsS0FBMEMsRUFBRSxPQUFlLEVBQUUsSUFBVTtRQUNqRixNQUFNLFNBQVMsR0FBRztZQUNoQixLQUFLLEVBQUUsQ0FBQztZQUNSLElBQUksRUFBRSxDQUFDO1lBQ1AsSUFBSSxFQUFFLENBQUM7WUFDUCxLQUFLLEVBQUUsQ0FBQztTQUNULENBQUM7UUFFRiw4Q0FBOEM7UUFDOUMsSUFBSSxTQUFTLENBQUMsS0FBSyxDQUFDLEdBQUcsU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUN4RCxPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sU0FBUyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDM0MsTUFBTSxNQUFNLEdBQUcsSUFBSSxTQUFTLE1BQU0sS0FBSyxDQUFDLFdBQVcsRUFBRSxHQUFHLENBQUM7UUFFekQsUUFBUSxLQUFLLEVBQUUsQ0FBQztZQUNkLEtBQUssT0FBTztnQkFDVixPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsTUFBTSxJQUFJLE9BQU8sRUFBRSxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDbEQsTUFBTTtZQUNSLEtBQUssTUFBTTtnQkFDVCxPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSxJQUFJLE9BQU8sRUFBRSxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDakQsTUFBTTtZQUNSLEtBQUssTUFBTTtnQkFDVCxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsTUFBTSxJQUFJLE9BQU8sRUFBRSxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDaEQsTUFBTTtZQUNSLEtBQUssT0FBTztnQkFDVixPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsTUFBTSxJQUFJLE9BQU8sRUFBRSxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDaEQsTUFBTTtRQUNWLENBQUM7SUFDSCxDQUFDO0NBQ0YifQ==