http-request-manager 15.0.26 → 18.0.1

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 (232) hide show
  1. package/esm2022/http-request-manager.mjs +5 -0
  2. package/esm2022/lib/http-request-manager.module.mjs +146 -0
  3. package/esm2022/lib/http-request-services-demo/database-data-demo/database-data-demo.component.mjs +71 -0
  4. package/esm2022/lib/http-request-services-demo/http-request-services-demo.component.mjs +41 -0
  5. package/esm2022/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.mjs +173 -0
  6. package/esm2022/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.mjs +80 -0
  7. package/esm2022/lib/http-request-services-demo/request-manager-demo/file-downloader/file-download.module.mjs +42 -0
  8. package/esm2022/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.mjs +88 -0
  9. package/esm2022/lib/http-request-services-demo/request-manager-demo/file-downloader/models/download-labels-model.mjs +11 -0
  10. package/esm2022/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.mjs +29 -0
  11. package/esm2022/lib/http-request-services-demo/request-manager-demo/models/sample-ai-prompt.mjs +9 -0
  12. package/esm2022/lib/http-request-services-demo/request-manager-demo/models/sample-client-info.mjs +12 -0
  13. package/esm2022/lib/http-request-services-demo/request-manager-demo/models/sample-mapper-client-info.mjs +14 -0
  14. package/esm2022/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.mjs +315 -0
  15. package/esm2022/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.mjs +270 -0
  16. package/esm2022/lib/http-request-services-demo/request-manager-state-demo/services/state-manager-demo.service.mjs +67 -0
  17. package/esm2022/lib/index.mjs +4 -0
  18. package/esm2022/lib/interceptors/credentials.interceptor.mjs +14 -0
  19. package/esm2022/lib/interceptors/index.mjs +5 -0
  20. package/esm2022/lib/interceptors/models/error-settings.model.mjs +10 -0
  21. package/esm2022/lib/interceptors/proxy-debugger.interceptor.mjs +47 -0
  22. package/esm2022/lib/interceptors/request-error.interceptor.mjs +49 -0
  23. package/esm2022/lib/interceptors/request-header.interceptor.mjs +41 -0
  24. package/esm2022/lib/models/config-http-options.model.mjs +18 -0
  25. package/esm2022/lib/models/config-local-storage-options.model.mjs +12 -0
  26. package/esm2022/lib/models/config-options.model.mjs +12 -0
  27. package/esm2022/lib/models/config-token.model.mjs +8 -0
  28. package/esm2022/lib/models/data-type.enum.mjs +7 -0
  29. package/esm2022/lib/models/database-storage.model.mjs +10 -0
  30. package/esm2022/lib/models/index.mjs +7 -0
  31. package/esm2022/lib/models/retry-options.model.mjs +10 -0
  32. package/esm2022/lib/services/database-manager-services/database.manager.service.mjs +119 -0
  33. package/esm2022/lib/services/database-manager-services/db.storage.service.mjs +143 -0
  34. package/esm2022/lib/services/database-manager-services/index.mjs +4 -0
  35. package/esm2022/lib/services/database-manager-services/models/table-schema.mjs +20 -0
  36. package/esm2022/lib/services/index.mjs +6 -0
  37. package/esm2022/lib/services/local-storage-manager-service/index.mjs +3 -0
  38. package/esm2022/lib/services/local-storage-manager-service/local-storage-manager.service.mjs +302 -0
  39. package/esm2022/lib/services/local-storage-manager-service/models/global-store-options.model.mjs +13 -0
  40. package/esm2022/lib/services/local-storage-manager-service/models/index.mjs +6 -0
  41. package/esm2022/lib/services/local-storage-manager-service/models/setting-options.model.mjs +13 -0
  42. package/esm2022/lib/services/local-storage-manager-service/models/storage-data.model.mjs +10 -0
  43. package/esm2022/lib/services/local-storage-manager-service/models/storage-option.model.mjs +12 -0
  44. package/esm2022/lib/services/local-storage-manager-service/models/storage-type.enum.mjs +7 -0
  45. package/esm2022/lib/services/request-manager-services/http-manager.service.mjs +207 -0
  46. package/esm2022/lib/services/request-manager-services/index.mjs +5 -0
  47. package/esm2022/lib/services/request-manager-services/request.service.mjs +189 -0
  48. package/esm2022/lib/services/request-manager-services/rxjs-operators/countdown.mjs +9 -0
  49. package/esm2022/lib/services/request-manager-services/rxjs-operators/delay-retry.mjs +10 -0
  50. package/esm2022/lib/services/request-manager-services/rxjs-operators/index.mjs +5 -0
  51. package/esm2022/lib/services/request-manager-services/rxjs-operators/request-polling.mjs +14 -0
  52. package/esm2022/lib/services/request-manager-services/rxjs-operators/request-streaming.mjs +19 -0
  53. package/esm2022/lib/services/request-manager-state-service/http-manager-state.store.mjs +267 -0
  54. package/esm2022/lib/services/request-manager-state-service/index.mjs +3 -0
  55. package/esm2022/lib/services/request-manager-state-service/models/api-request.model.mjs +20 -0
  56. package/esm2022/lib/services/request-manager-state-service/models/index.mjs +3 -0
  57. package/esm2022/lib/services/request-manager-state-service/models/request-options.model.mjs +10 -0
  58. package/esm2022/lib/services/utils/app.service.mjs +26 -0
  59. package/esm2022/lib/services/utils/encryption/asymmetrical-encryption.service.mjs +186 -0
  60. package/esm2022/lib/services/utils/encryption/encryption-test.service.mjs +35 -0
  61. package/esm2022/lib/services/utils/encryption/index.mjs +4 -0
  62. package/esm2022/lib/services/utils/encryption/random.mjs +52 -0
  63. package/esm2022/lib/services/utils/encryption/symmetrical-encryption.service.mjs +77 -0
  64. package/esm2022/lib/services/utils/headers.service.mjs +21 -0
  65. package/esm2022/lib/services/utils/index.mjs +6 -0
  66. package/esm2022/lib/services/utils/object-merger.service.mjs +50 -0
  67. package/esm2022/lib/services/utils/path-query.service.mjs +54 -0
  68. package/esm2022/lib/services/utils/utils.service.mjs +155 -0
  69. package/esm2022/public-api.mjs +11 -0
  70. package/fesm2022/http-request-manager.mjs +3444 -0
  71. package/fesm2022/http-request-manager.mjs.map +1 -0
  72. package/http-request-manager-18.0.1.tgz +0 -0
  73. package/index.d.ts +5 -0
  74. package/lib/http-request-manager.module.d.ts +33 -0
  75. package/lib/http-request-services-demo/database-data-demo/database-data-demo.component.d.ts +19 -0
  76. package/lib/http-request-services-demo/http-request-services-demo.component.d.ts +24 -0
  77. package/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.d.ts +56 -0
  78. package/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.d.ts +26 -0
  79. package/lib/http-request-services-demo/request-manager-demo/file-downloader/file-download.module.d.ts +13 -0
  80. package/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.d.ts +27 -0
  81. package/lib/http-request-services-demo/request-manager-demo/file-downloader/models/download-labels-model.d.ts +12 -0
  82. package/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.d.ts +16 -0
  83. package/lib/http-request-services-demo/request-manager-demo/models/sample-ai-prompt.d.ts +8 -0
  84. package/lib/http-request-services-demo/request-manager-demo/models/sample-client-info.d.ts +14 -0
  85. package/lib/http-request-services-demo/request-manager-demo/models/sample-mapper-client-info.d.ts +14 -0
  86. package/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.d.ts +107 -0
  87. package/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.d.ts +122 -0
  88. package/lib/http-request-services-demo/request-manager-state-demo/services/state-manager-demo.service.d.ts +15 -0
  89. package/lib/index.d.ts +3 -0
  90. package/lib/interceptors/credentials.interceptor.d.ts +8 -0
  91. package/{src/lib/interceptors/index.ts → lib/interceptors/index.d.ts} +0 -2
  92. package/lib/interceptors/models/error-settings.model.d.ts +10 -0
  93. package/lib/interceptors/proxy-debugger.interceptor.d.ts +12 -0
  94. package/lib/interceptors/request-error.interceptor.d.ts +10 -0
  95. package/lib/interceptors/request-header.interceptor.d.ts +15 -0
  96. package/lib/models/config-http-options.model.d.ts +21 -0
  97. package/lib/models/config-local-storage-options.model.d.ts +13 -0
  98. package/lib/models/config-options.model.d.ts +12 -0
  99. package/{src/lib/models/config-token.model.ts → lib/models/config-token.model.d.ts} +1 -2
  100. package/lib/models/data-type.enum.d.ts +5 -0
  101. package/lib/models/database-storage.model.d.ts +10 -0
  102. package/lib/models/index.d.ts +6 -0
  103. package/lib/models/retry-options.model.d.ts +10 -0
  104. package/lib/services/database-manager-services/database.manager.service.d.ts +31 -0
  105. package/lib/services/database-manager-services/db.storage.service.d.ts +26 -0
  106. package/{src/lib/services/database-manager-services/index.ts → lib/services/database-manager-services/index.d.ts} +0 -1
  107. package/lib/services/database-manager-services/models/table-schema.d.ts +11 -0
  108. package/{src/lib/services/index.ts → lib/services/index.d.ts} +0 -4
  109. package/lib/services/local-storage-manager-service/index.d.ts +2 -0
  110. package/lib/services/local-storage-manager-service/local-storage-manager.service.d.ts +86 -0
  111. package/lib/services/local-storage-manager-service/models/global-store-options.model.d.ts +15 -0
  112. package/{src/lib/services/local-storage-manager-service/models/index.ts → lib/services/local-storage-manager-service/models/index.d.ts} +1 -2
  113. package/lib/services/local-storage-manager-service/models/setting-options.model.d.ts +15 -0
  114. package/lib/services/local-storage-manager-service/models/storage-data.model.d.ts +10 -0
  115. package/lib/services/local-storage-manager-service/models/storage-option.model.d.ts +13 -0
  116. package/lib/services/local-storage-manager-service/models/storage-type.enum.d.ts +5 -0
  117. package/lib/services/request-manager-services/http-manager.service.d.ts +41 -0
  118. package/{src/lib/services/request-manager-services/index.ts → lib/services/request-manager-services/index.d.ts} +0 -2
  119. package/lib/services/request-manager-services/request.service.d.ts +27 -0
  120. package/lib/services/request-manager-services/rxjs-operators/countdown.d.ts +2 -0
  121. package/lib/services/request-manager-services/rxjs-operators/delay-retry.d.ts +2 -0
  122. package/lib/services/request-manager-services/rxjs-operators/request-polling.d.ts +7 -0
  123. package/lib/services/request-manager-services/rxjs-operators/request-streaming.d.ts +2 -0
  124. package/lib/services/request-manager-state-service/http-manager-state.store.d.ts +51 -0
  125. package/{src/lib/services/request-manager-state-service/index.ts → lib/services/request-manager-state-service/index.d.ts} +0 -1
  126. package/lib/services/request-manager-state-service/models/api-request.model.d.ts +27 -0
  127. package/{src/lib/services/request-manager-state-service/models/index.ts → lib/services/request-manager-state-service/models/index.d.ts} +0 -1
  128. package/lib/services/request-manager-state-service/models/request-options.model.d.ts +10 -0
  129. package/lib/services/utils/app.service.d.ts +8 -0
  130. package/lib/services/utils/encryption/asymmetrical-encryption.service.d.ts +17 -0
  131. package/lib/services/utils/encryption/encryption-test.service.d.ts +10 -0
  132. package/{src/lib/services/utils/encryption/index.ts → lib/services/utils/encryption/index.d.ts} +0 -2
  133. package/lib/services/utils/encryption/random.d.ts +7 -0
  134. package/lib/services/utils/encryption/symmetrical-encryption.service.d.ts +14 -0
  135. package/lib/services/utils/headers.service.d.ts +10 -0
  136. package/{src/lib/services/utils/index.ts → lib/services/utils/index.d.ts} +0 -2
  137. package/lib/services/utils/object-merger.service.d.ts +12 -0
  138. package/lib/services/utils/path-query.service.d.ts +11 -0
  139. package/lib/services/utils/utils.service.d.ts +24 -0
  140. package/package.json +14 -1
  141. package/{src/public-api.ts → public-api.d.ts} +0 -7
  142. package/ng-package.json +0 -8
  143. package/src/lib/http-request-manager.module.ts +0 -101
  144. package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.html +0 -3
  145. package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.scss +0 -0
  146. package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.ts +0 -105
  147. package/src/lib/http-request-services-demo/http-request-services-demo.component.html +0 -64
  148. package/src/lib/http-request-services-demo/http-request-services-demo.component.scss +0 -6
  149. package/src/lib/http-request-services-demo/http-request-services-demo.component.ts +0 -34
  150. package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.html +0 -195
  151. package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.scss +0 -17
  152. package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.ts +0 -205
  153. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.html +0 -59
  154. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.scss +0 -60
  155. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.ts +0 -71
  156. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-download.module.ts +0 -28
  157. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.html +0 -10
  158. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.scss +0 -29
  159. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.ts +0 -99
  160. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/models/download-labels-model.ts +0 -22
  161. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.html +0 -8
  162. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.scss +0 -19
  163. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.ts +0 -25
  164. package/src/lib/http-request-services-demo/request-manager-demo/models/app-session.model.ts +0 -30
  165. package/src/lib/http-request-services-demo/request-manager-demo/models/app.model.ts +0 -19
  166. package/src/lib/http-request-services-demo/request-manager-demo/models/get-sample.model.ts +0 -25
  167. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-ai-prompt.ts +0 -19
  168. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-details.ts +0 -24
  169. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-info.ts +0 -30
  170. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client.model.ts +0 -49
  171. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-mapper-client-info.ts +0 -33
  172. package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.html +0 -336
  173. package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.scss +0 -24
  174. package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.ts +0 -403
  175. package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.html +0 -328
  176. package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.scss +0 -24
  177. package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.ts +0 -347
  178. package/src/lib/http-request-services-demo/request-manager-state-demo/services/state-manager-demo.service.ts +0 -88
  179. package/src/lib/index.ts +0 -3
  180. package/src/lib/interceptors/credentials.interceptor.ts +0 -18
  181. package/src/lib/interceptors/models/error-settings.model.ts +0 -22
  182. package/src/lib/interceptors/proxy-debugger.interceptor.ts +0 -46
  183. package/src/lib/interceptors/request-error.interceptor.ts +0 -65
  184. package/src/lib/interceptors/request-header.interceptor.ts +0 -58
  185. package/src/lib/models/config-http-options.model.ts +0 -42
  186. package/src/lib/models/config-local-storage-options.model.ts +0 -27
  187. package/src/lib/models/config-options.model.ts +0 -27
  188. package/src/lib/models/data-type.enum.ts +0 -5
  189. package/src/lib/models/database-storage.model.ts +0 -24
  190. package/src/lib/models/index.ts +0 -9
  191. package/src/lib/models/retry-options.model.ts +0 -22
  192. package/src/lib/services/database-manager-services/database.manager.service.ts +0 -193
  193. package/src/lib/services/database-manager-services/db.storage.service.ts +0 -191
  194. package/src/lib/services/database-manager-services/models/table-schema.ts +0 -35
  195. package/src/lib/services/local-storage-manager-service/index.ts +0 -2
  196. package/src/lib/services/local-storage-manager-service/local-storage-manager.service.spec.ts +0 -71
  197. package/src/lib/services/local-storage-manager-service/local-storage-manager.service.ts +0 -421
  198. package/src/lib/services/local-storage-manager-service/models/global-store-options.model.ts +0 -30
  199. package/src/lib/services/local-storage-manager-service/models/setting-options.model.ts +0 -30
  200. package/src/lib/services/local-storage-manager-service/models/storage-data.model.ts +0 -24
  201. package/src/lib/services/local-storage-manager-service/models/storage-option.model.ts +0 -29
  202. package/src/lib/services/local-storage-manager-service/models/storage-type.enum.ts +0 -5
  203. package/src/lib/services/request-manager-services/README.md +0 -268
  204. package/src/lib/services/request-manager-services/http-manager.service.spec.ts +0 -230
  205. package/src/lib/services/request-manager-services/http-manager.service.ts +0 -274
  206. package/src/lib/services/request-manager-services/request.service.ts +0 -261
  207. package/src/lib/services/request-manager-services/rxjs-operators/countdown.ts +0 -17
  208. package/src/lib/services/request-manager-services/rxjs-operators/delay-retry.ts +0 -16
  209. package/src/lib/services/request-manager-services/rxjs-operators/request-polling.ts +0 -21
  210. package/src/lib/services/request-manager-services/rxjs-operators/request-streaming.ts +0 -32
  211. package/src/lib/services/request-manager-state-service/http-manager-state.store.ts +0 -402
  212. package/src/lib/services/request-manager-state-service/models/api-request.model.ts +0 -50
  213. package/src/lib/services/request-manager-state-service/models/request-options.model.ts +0 -22
  214. package/src/lib/services/utils/app.service.spec.ts +0 -25
  215. package/src/lib/services/utils/app.service.ts +0 -21
  216. package/src/lib/services/utils/encryption/README.md +0 -79
  217. package/src/lib/services/utils/encryption/asymmetrical-encryption.service.ts +0 -282
  218. package/src/lib/services/utils/encryption/encryption-test.service.ts +0 -39
  219. package/src/lib/services/utils/encryption/random.ts +0 -69
  220. package/src/lib/services/utils/encryption/symmetrical-encryption.service.ts +0 -93
  221. package/src/lib/services/utils/headers.service.spec.ts +0 -80
  222. package/src/lib/services/utils/headers.service.ts +0 -18
  223. package/src/lib/services/utils/object-merger.service.spec.ts +0 -16
  224. package/src/lib/services/utils/object-merger.service.ts +0 -60
  225. package/src/lib/services/utils/path-query.service.spec.ts +0 -117
  226. package/src/lib/services/utils/path-query.service.ts +0 -69
  227. package/src/lib/services/utils/utils.service.spec.ts +0 -164
  228. package/src/lib/services/utils/utils.service.ts +0 -192
  229. package/tsconfig.lib.json +0 -32
  230. package/tsconfig.lib.prod.json +0 -10
  231. package/tsconfig.spec.json +0 -14
  232. /package/{src/lib/services/request-manager-services/rxjs-operators/index.ts → lib/services/request-manager-services/rxjs-operators/index.d.ts} +0 -0
@@ -0,0 +1,315 @@
1
+ import { Component, ViewChild, inject } from '@angular/core';
2
+ import { BehaviorSubject, EMPTY, throwError } from 'rxjs';
3
+ import { FormBuilder, Validators } from '@angular/forms';
4
+ import { ClientInfo } from './models/sample-client-info';
5
+ import { ClientInfoMapper } from './models/sample-mapper-client-info';
6
+ import { AIPrompt } from './models/sample-ai-prompt';
7
+ import { ApiRequest, HTTPManagerService } from '../..';
8
+ import { catchError, map, tap } from 'rxjs/operators';
9
+ import { ToastColors, ToastDisplay, ToastMessageDisplayService } from 'toast-message-display';
10
+ import * as i0 from "@angular/core";
11
+ import * as i1 from "@angular/common";
12
+ import * as i2 from "@angular/forms";
13
+ import * as i3 from "@angular/material/button";
14
+ import * as i4 from "@angular/material/form-field";
15
+ import * as i5 from "@angular/material/select";
16
+ import * as i6 from "@angular/material/core";
17
+ import * as i7 from "@angular/material/menu";
18
+ import * as i8 from "@angular/material/icon";
19
+ import * as i9 from "@angular/material/table";
20
+ import * as i10 from "@angular/material/progress-bar";
21
+ import * as i11 from "@angular/material/slide-toggle";
22
+ import * as i12 from "@angular/material/divider";
23
+ import * as i13 from "@angular/material/input";
24
+ import * as i14 from "./file-downloader/file-downloader.component";
25
+ export class RequestManagerDemoComponent {
26
+ get retry() {
27
+ return this.requestForm.get('retry')?.value;
28
+ }
29
+ get headers() {
30
+ return this.requestForm.get('headers');
31
+ }
32
+ get isValid() {
33
+ this.requestForm.markAllAsTouched();
34
+ return this.requestForm.valid;
35
+ }
36
+ constructor() {
37
+ this.displayedColumns = ['id', 'name', 'lastName', 'age'];
38
+ this.fb = inject(FormBuilder);
39
+ this.toastMessage = inject(ToastMessageDisplayService);
40
+ this.httpManagerService = inject(HTTPManagerService);
41
+ this.isPending$ = this.httpManagerService.isPending$;
42
+ this.countdown$ = this.httpManagerService.countdown$;
43
+ this.GET_error$ = new BehaviorSubject('');
44
+ this.POST_error$ = new BehaviorSubject('');
45
+ this.PUT_error$ = new BehaviorSubject('');
46
+ this.DELETE_error$ = new BehaviorSubject('');
47
+ this.STREAM_error$ = new BehaviorSubject('');
48
+ this.STREAM_AI_error$ = new BehaviorSubject('');
49
+ this.requestParams = {
50
+ GET: ApiRequest.adapt(),
51
+ POST: ApiRequest.adapt(),
52
+ PUT: ApiRequest.adapt(),
53
+ DELETE: ApiRequest.adapt(),
54
+ STREAM: ApiRequest.adapt(),
55
+ };
56
+ this.questionControl = this.fb.control("", [Validators.required]);
57
+ this.downloadRequest = ApiRequest.adapt({
58
+ server: 'assets/images',
59
+ path: ['me.jpg'],
60
+ // saveAs: 'john.jpg' // Optional
61
+ });
62
+ // downloadRequest = ApiRequest.adapt({
63
+ // server: 'oidc/ai/file'
64
+ // })
65
+ this.sampleClientData = {
66
+ id: 0,
67
+ name: "Old School Dates",
68
+ domain: "osd.com",
69
+ service: "osd",
70
+ spiffe: "osd.com/osd",
71
+ secret: "SMOPECXP-OS4P-USOG-X2II-3XMD1FQDR3IJX",
72
+ created: 1693003138,
73
+ modified: 1693003138,
74
+ icon: "",
75
+ imageFile: "",
76
+ email: "wavecoders@gmail.com"
77
+ };
78
+ this.requestForm = this.fb.group({
79
+ path: this.fb.control("ai/"),
80
+ headers: this.fb.array([]),
81
+ adapter: [null],
82
+ mapper: [null],
83
+ retry: this.fb.group({
84
+ times: [3],
85
+ delay: [3],
86
+ }),
87
+ polling: [3],
88
+ });
89
+ this.AIType = 0;
90
+ this.sampleAdaptors = [
91
+ { label: "ClientInfo Basic", value: ClientInfo.adapt },
92
+ { label: "AI Prompt", value: AIPrompt.adapt },
93
+ ];
94
+ this.sampleMappers = [
95
+ { label: "Mapper Basic", value: ClientInfoMapper.adapt },
96
+ { label: "AI Prompt", value: AIPrompt.adapt },
97
+ ];
98
+ this.hasId = (arr) => {
99
+ if (arr.length === 0)
100
+ return false;
101
+ return !isNaN(arr[arr.length - 1]);
102
+ };
103
+ this.props = (adapter) => {
104
+ return (adapter) ? adapter() : null;
105
+ };
106
+ // server = `http://sample-endpoint/as/authorization.oauth2`
107
+ this.arrayObjectsToObjects = (arr) => {
108
+ return Array.isArray(arr) ? arr.reduce((obj, item) => Object.assign(obj, { [item.key]: item.value }), {}) : {};
109
+ };
110
+ }
111
+ ngOnInit() {
112
+ // const reqGet2 = ApiRequest.adapt({
113
+ // server,
114
+ // path: ['clients'],
115
+ // headers: { authentication: "Bearer <KEY>" },
116
+ // adapter: ClientInfo,
117
+ // dataType: DataType.OBJECT,
118
+ // // concurrent: false,
119
+ // // polling: 3, //seconds
120
+ // })
121
+ // const req2 = [1024,1025,1026].map(item => {
122
+ // return this.httpManagerService.getRequest<ClientInfo[]>(reqGet2, [item])
123
+ // .pipe(
124
+ // catchError(error => {
125
+ // return throwError(() => new Error(error.error.message))
126
+ // })
127
+ // )
128
+ // })
129
+ // forkJoin(req2)
130
+ // .subscribe(res => console.log(res))
131
+ }
132
+ addHeader() {
133
+ const header = this.fb.group({
134
+ key: ['', Validators.required],
135
+ value: ['']
136
+ });
137
+ this.headers.push(header);
138
+ }
139
+ removeHeader(index) {
140
+ this.headers.removeAt(index);
141
+ }
142
+ compileRequest() {
143
+ const requestParams = this.requestForm.value;
144
+ requestParams.headers = this.arrayObjectsToObjects(requestParams.headers || []);
145
+ const pathReq = (requestParams.path === "") ? [] : (requestParams.path || "").split("/");
146
+ if (!this.pollingState.checked)
147
+ requestParams.polling = 0;
148
+ if (!this.failedState.checked) {
149
+ requestParams.retry = { times: 0, delay: 0 };
150
+ }
151
+ const apiOptions = ApiRequest.adapt(requestParams);
152
+ apiOptions.path = [];
153
+ return { apiOptions: apiOptions, path: pathReq };
154
+ }
155
+ onGetRequest() {
156
+ if (!this.isValid)
157
+ return;
158
+ const reqParams = this.compileRequest();
159
+ this.requestParams.GET = reqParams.apiOptions;
160
+ this.GET$ = EMPTY; //Cancels Previous
161
+ this.GET_error$.next('');
162
+ this.GET$ = this.httpManagerService.getRequest(reqParams.apiOptions, reqParams.path)
163
+ .pipe(
164
+ // tap((data) => console.log("API GET response", data)),
165
+ catchError(error => {
166
+ return throwError(() => this.errorHandling(error, 'GET'));
167
+ }));
168
+ }
169
+ onCreateRequest() {
170
+ if (!this.isValid)
171
+ return;
172
+ const reqParams = this.compileRequest();
173
+ this.requestParams.POST = reqParams.apiOptions;
174
+ this.POST$ = EMPTY; //Cancels Previous
175
+ this.POST_error$.next('');
176
+ console.log("POST", this.sampleClientData);
177
+ console.log("POST", reqParams.apiOptions);
178
+ console.log("POST", reqParams.path);
179
+ this.POST$ = this.httpManagerService.postRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
180
+ .pipe(
181
+ // tap((data) => console.log("API POST response", data)),
182
+ catchError(error => {
183
+ return throwError(() => this.errorHandling(error, 'POST'));
184
+ }));
185
+ }
186
+ onUpdateRequest() {
187
+ if (!this.isValid)
188
+ return;
189
+ const reqParams = this.compileRequest();
190
+ if (!this.hasId(reqParams.path)) {
191
+ console.log("Missing ID");
192
+ return;
193
+ }
194
+ this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
195
+ this.requestParams.PUT = reqParams.apiOptions;
196
+ this.PUT$ = EMPTY; //Cancels Previous
197
+ this.PUT_error$.next('');
198
+ this.PUT$ = this.httpManagerService.putRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
199
+ .pipe(
200
+ // tap((data) => console.log("API PUT response", data)),
201
+ catchError(error => {
202
+ return throwError(() => this.errorHandling(error, 'PUT'));
203
+ }));
204
+ }
205
+ onDeleteRequest() {
206
+ if (!this.isValid)
207
+ return;
208
+ const reqParams = this.compileRequest();
209
+ this.requestParams.DELETE = reqParams.apiOptions;
210
+ if (!this.hasId(reqParams.path)) {
211
+ console.log("Missing ID");
212
+ return;
213
+ }
214
+ this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
215
+ this.requestParams.DELETE = reqParams.apiOptions;
216
+ this.DELETE$ = EMPTY; //Cancels Previous
217
+ this.DELETE_error$.next('');
218
+ this.DELETE$ = this.httpManagerService.deleteRequest(reqParams.apiOptions, reqParams.path)
219
+ .pipe(
220
+ // tap((data) => console.log("API DELETE response", data)),
221
+ catchError(error => {
222
+ return throwError(() => this.errorHandling(error, 'DELETE'));
223
+ }));
224
+ }
225
+ onStreamPostRequest() {
226
+ if (!this.isValid)
227
+ return;
228
+ const reqParams = this.compileRequest();
229
+ let payload = {};
230
+ let apiPath = reqParams.path;
231
+ let apiOptions = reqParams.apiOptions;
232
+ let responseMapper = (items) => items.response;
233
+ if (this.AIType === 0) {
234
+ // API request
235
+ payload = { prompt: this.questionControl.value };
236
+ }
237
+ else {
238
+ // Local Ollama request
239
+ apiOptions.server = "api";
240
+ apiPath = ["generate"];
241
+ apiOptions.stream = true;
242
+ payload = {
243
+ model: "phi3:latest",
244
+ prompt: this.questionControl.value,
245
+ stream: true,
246
+ };
247
+ responseMapper = (items) => items.map((word) => word.response).flat().join('');
248
+ }
249
+ this.requestParams.STREAM = apiOptions;
250
+ this.STREAM_AI$ = EMPTY;
251
+ this.STREAM_AI_error$.next('');
252
+ this.STREAM_AI$ = this.httpManagerService.postRequest(payload, apiOptions, apiPath).pipe(map(responseMapper), tap(() => this.questionControl.reset()), catchError(error => throwError(() => this.errorHandling(error, 'STREAM'))));
253
+ }
254
+ onStreamRequest() {
255
+ if (!this.isValid)
256
+ return;
257
+ const reqParams = this.compileRequest();
258
+ reqParams.apiOptions.stream = true;
259
+ this.requestParams.GET = reqParams.apiOptions;
260
+ this.STREAM$ = this.httpManagerService.getRequest(reqParams.apiOptions, reqParams.path)
261
+ .pipe(
262
+ // tap((data) => console.log("API STREAM response", data)),
263
+ catchError(error => {
264
+ return throwError(() => this.errorHandling(error, 'STREAM'));
265
+ }));
266
+ }
267
+ onDownloadCompleted() {
268
+ const message = "Download Completed";
269
+ const display = ToastDisplay.adapt({
270
+ message,
271
+ action: 'Ok',
272
+ color: ToastColors.SUCCESS,
273
+ icon: 'sentiment_satisfied_alt',
274
+ });
275
+ this.toastMessage.toastMessage(display);
276
+ }
277
+ onDownloadFailed(err) {
278
+ const message = "Download Failed";
279
+ const display = ToastDisplay.adapt({
280
+ message,
281
+ action: 'Ok',
282
+ color: ToastColors.ERROR,
283
+ icon: 'warning',
284
+ });
285
+ this.toastMessage.toastMessage(display);
286
+ }
287
+ errorHandling(err, type) {
288
+ if (type === 'GET')
289
+ this.GET_error$.next(err.message);
290
+ if (type === 'POST')
291
+ this.POST_error$.next(err.message);
292
+ if (type === 'PUT')
293
+ this.PUT_error$.next(err.message);
294
+ if (type === 'DELETE')
295
+ this.DELETE_error$.next(err.message);
296
+ if (type === 'STREAM')
297
+ this.STREAM_error$.next(err.message);
298
+ }
299
+ onSelectAIType(type) {
300
+ this.AIType = type;
301
+ }
302
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestManagerDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
303
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: RequestManagerDemoComponent, selector: "app-request-manager-demo", viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\" class=\"btn\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"pollingState.checked && !(isPending$ | async)\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(GET$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(POST$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(PUT$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(DELETE$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -<span *ngIf=\"AIType === 1\">STREAMING</span> POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n <span *ngIf=\"AIType === 0; else LOCAL\">Server</span>\n <ng-template #LOCAL>\n Local\n </ng-template>\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n <div *ngIf=\"(STREAM_AI_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div *ngIf=\"AIType === 1; else ALTERNATIVE\" style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n <ng-template #ALTERNATIVE>\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n </ng-template>\n\n <div>\n <div *ngIf=\"(STREAM_AI$ | async) as data\" style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{data}}</p>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n <span *ngIf=\"disable.checked; else DISABLE\">\n Enable\n </span>\n <ng-template #DISABLE>Disable</ng-template>\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex", "panelWidth", "hideSingleSelectionIndicator"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i10.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["disabled", "disableRipple", "color", "tabIndex"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i14.FileDownloaderComponent, selector: "app-file-downloader", inputs: ["delayError", "apiRequest", "displayErrorMessage", "saveFileAs", "labels", "disabled"], outputs: ["completed", "failed"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.JsonPipe, name: "json" }] }); }
304
+ }
305
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestManagerDemoComponent, decorators: [{
306
+ type: Component,
307
+ args: [{ selector: 'app-request-manager-demo', template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\" class=\"btn\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"pollingState.checked && !(isPending$ | async)\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(GET$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(POST$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(PUT$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(DELETE$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -<span *ngIf=\"AIType === 1\">STREAMING</span> POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n <span *ngIf=\"AIType === 0; else LOCAL\">Server</span>\n <ng-template #LOCAL>\n Local\n </ng-template>\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n <div *ngIf=\"(STREAM_AI_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div *ngIf=\"AIType === 1; else ALTERNATIVE\" style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n <ng-template #ALTERNATIVE>\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n </ng-template>\n\n <div>\n <div *ngIf=\"(STREAM_AI$ | async) as data\" style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{data}}</p>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n <span *ngIf=\"disable.checked; else DISABLE\">\n Enable\n </span>\n <ng-template #DISABLE>Disable</ng-template>\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"] }]
308
+ }], ctorParameters: function () { return []; }, propDecorators: { failedState: [{
309
+ type: ViewChild,
310
+ args: ["failedState", { static: true }]
311
+ }], pollingState: [{
312
+ type: ViewChild,
313
+ args: ["pollingState", { static: true }]
314
+ }] } });
315
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVxdWVzdC1tYW5hZ2VyLWRlbW8uY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvaHR0cC1yZXF1ZXN0LW1hbmFnZXIvc3JjL2xpYi9odHRwLXJlcXVlc3Qtc2VydmljZXMtZGVtby9yZXF1ZXN0LW1hbmFnZXItZGVtby9yZXF1ZXN0LW1hbmFnZXItZGVtby5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9odHRwLXJlcXVlc3QtbWFuYWdlci9zcmMvbGliL2h0dHAtcmVxdWVzdC1zZXJ2aWNlcy1kZW1vL3JlcXVlc3QtbWFuYWdlci1kZW1vL3JlcXVlc3QtbWFuYWdlci1kZW1vLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQVUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQTtBQUVwRSxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBYyxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUE7QUFDckUsT0FBTyxFQUFhLFdBQVcsRUFBRSxVQUFVLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQTtBQUVuRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sNkJBQTZCLENBQUE7QUFDeEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sb0NBQW9DLENBQUE7QUFDckUsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLDJCQUEyQixDQUFBO0FBQ3BELE9BQU8sRUFBRSxVQUFVLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxPQUFPLENBQUE7QUFDdEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFFckQsT0FBTyxFQUFFLFdBQVcsRUFBRSxZQUFZLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQTs7Ozs7Ozs7Ozs7Ozs7OztBQU83RixNQUFN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SSxFQUFFO2dCQUNHLFlBQVk7c0JBQXhELFNBQVM7dUJBQUMsY0FBYyxFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgT25Jbml0LCBWaWV3Q2hpbGQsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXG5cbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgRU1QVFksIE9ic2VydmFibGUsIHRocm93RXJyb3IgfSBmcm9tICdyeGpzJ1xuaW1wb3J0IHsgRm9ybUFycmF5LCBGb3JtQnVpbGRlciwgVmFsaWRhdG9ycyB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJ1xuXG5pbXBvcnQgeyBDbGllbnRJbmZvIH0gZnJvbSAnLi9tb2RlbHMvc2FtcGxlLWNsaWVudC1pbmZvJ1xuaW1wb3J0IHsgQ2xpZW50SW5mb01hcHBlciB9IGZyb20gJy4vbW9kZWxzL3NhbXBsZS1tYXBwZXItY2xpZW50LWluZm8nXG5pbXBvcnQgeyBBSVByb21wdCB9IGZyb20gJy4vbW9kZWxzL3NhbXBsZS1haS1wcm9tcHQnXG5pbXBvcnQgeyBBcGlSZXF1ZXN0LCBIVFRQTWFuYWdlclNlcnZpY2UgfSBmcm9tICcuLi8uLidcbmltcG9ydCB7IGNhdGNoRXJyb3IsIG1hcCwgdGFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnXG5cbmltcG9ydCB7IFRvYXN0Q29sb3JzLCBUb2FzdERpc3BsYXksIFRvYXN0TWVzc2FnZURpc3BsYXlTZXJ2aWNlIH0gZnJvbSAndG9hc3QtbWVzc2FnZS1kaXNwbGF5J1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdhcHAtcmVxdWVzdC1tYW5hZ2VyLWRlbW8nLFxuICB0ZW1wbGF0ZVVybDogJy4vcmVxdWVzdC1tYW5hZ2VyLWRlbW8uY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9yZXF1ZXN0LW1hbmFnZXItZGVtby5jb21wb25lbnQuc2NzcyddLFxufSlcbmV4cG9ydCBjbGFzcyBSZXF1ZXN0TWFuYWdlckRlbW9Db21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQge1xuXG4gIGRpc3BsYXllZENvbHVtbnMgPSBbJ2lkJywnbmFtZScsICdsYXN0TmFtZScsICdhZ2UnXVxuXG4gIHByaXZhdGUgZmIgPSBpbmplY3QoRm9ybUJ1aWxkZXIpXG4gIHByaXZhdGUgdG9hc3RNZXNzYWdlID0gaW5qZWN0KFRvYXN0TWVzc2FnZURpc3BsYXlTZXJ2aWNlKVxuXG4gIGh0dHBNYW5hZ2VyU2VydmljZSA9IGluamVjdChIVFRQTWFuYWdlclNlcnZpY2UpXG5cbiAgaXNQZW5kaW5nJCA9IHRoaXMuaHR0cE1hbmFnZXJTZXJ2aWNlLmlzUGVuZGluZyRcbiAgY291bnRkb3duJCA9IHRoaXMuaHR0cE1hbmFnZXJTZXJ2aWNlLmNvdW50ZG93biRcblxuICBHRVRfZXJyb3IkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxzdHJpbmc+KCcnKVxuICBQT1NUX2Vycm9yJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8c3RyaW5nPignJylcbiAgUFVUX2Vycm9yJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8c3RyaW5nPignJylcbiAgREVMRVRFX2Vycm9yJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8c3RyaW5nPignJylcblxuICBTVFJFQU1fZXJyb3IkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxzdHJpbmc+KCcnKVxuICBTVFJFQU1fQUlfZXJyb3IkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxzdHJpbmc+KCcnKVxuXG4gIEdFVCQ/OiBPYnNlcnZhYmxlPGFueT5cbiAgUE9TVCQ/OiBPYnNlcnZhYmxlPGFueT5cbiAgUFVUJD86IE9ic2VydmFibGU8YW55PlxuICBERUxFVEUkPzogT2JzZXJ2YWJsZTxhbnk+XG5cbiAgU1RSRUFNX0FJJD86IE9ic2VydmFibGU8YW55PlxuICBTVFJFQU0kPzogT2JzZXJ2YWJsZTxhbnk+XG5cbiAgcmVxdWVzdFBhcmFtcyA9IHtcbiAgICBHRVQ6IEFwaVJlcXVlc3QuYWRhcHQoKSxcbiAgICBQT1NUOiBBcGlSZXF1ZXN0LmFkYXB0KCksXG4gICAgUFVUOiBBcGlSZXF1ZXN0LmFkYXB0KCksXG4gICAgREVMRVRFOiBBcGlSZXF1ZXN0LmFkYXB0KCksXG4gICAgU1RSRUFNOiBBcGlSZXF1ZXN0LmFkYXB0KCksXG4gIH1cblxuICBAVmlld0NoaWxkKFwiZmFpbGVkU3RhdGVcIiwgeyBzdGF0aWM6IHRydWUgfSkgZmFpbGVkU3RhdGU6IGFueVxuICBAVmlld0NoaWxkKFwicG9sbGluZ1N0YXRlXCIsIHsgc3RhdGljOiB0cnVlIH0pIHBvbGxpbmdTdGF0ZTogYW55XG5cbiAgcXVlc3Rpb25Db250cm9sID0gdGhpcy5mYi5jb250cm9sKFwiXCIsIFtWYWxpZGF0b3JzLnJlcXVpcmVkXSlcblxuICBkb3dubG9hZFJlcXVlc3QgPSBBcGlSZXF1ZXN0LmFkYXB0KHtcbiAgICBzZXJ2ZXI6ICdhc3NldHMvaW1hZ2VzJyxcbiAgICBwYXRoOiBbJ21lLmpwZyddLFxuICAgIC8vIHNhdmVBczogJ2pvaG4uanBnJyAvLyBPcHRpb25hbFxuICB9KVxuXG4gIC8vIGRvd25sb2FkUmVxdWVzdCA9IEFwaVJlcXVlc3QuYWRhcHQoe1xuICAvLyAgIHNlcnZlcjogJ29pZGMvYWkvZmlsZSdcbiAgLy8gfSlcblxuICBzYW1wbGVDbGllbnREYXRhID0ge1xuICAgICAgaWQ6IDAsXG4gICAgICBuYW1lOiBcIk9sZCBTY2hvb2wgRGF0ZXNcIixcbiAgICAgIGRvbWFpbjogXCJvc2QuY29tXCIsXG4gICAgICBzZXJ2aWNlOiBcIm9zZFwiLFxuICAgICAgc3BpZmZlOiBcIm9zZC5jb20vb3NkXCIsXG4gICAgICBzZWNyZXQ6IFwiU01PUEVDWFAtT1M0UC1VU09HLVgySUktM1hNRDFGUURSM0lKWFwiLFxuICAgICAgY3JlYXRlZDogMTY5MzAwMzEzOCxcbiAgICAgIG1vZGlmaWVkOiAxNjkzMDAzMTM4LFxuICAgICAgaWNvbjogXCJcIixcbiAgICAgIGltYWdlRmlsZTogXCJcIixcbiAgICAgIGVtYWlsOiBcIndhdmVjb2RlcnNAZ21haWwuY29tXCJcbiAgfVxuXG4gIHJlcXVlc3RGb3JtID0gdGhpcy5mYi5ncm91cCh7XG4gICAgcGF0aDogdGhpcy5mYi5jb250cm9sPHN0cmluZz4oXCJhaS9cIiksXG4gICAgaGVhZGVyczogdGhpcy5mYi5hcnJheShbXSksXG4gICAgYWRhcHRlcjogW251bGxdLFxuICAgIG1hcHBlcjogW251bGxdLFxuICAgIHJldHJ5OiB0aGlzLmZiLmdyb3VwKHtcbiAgICAgIHRpbWVzOiBbM10sXG4gICAgICBkZWxheTogWzNdLFxuICAgIH0pLFxuICAgIHBvbGxpbmc6IFszXSxcbiAgfSlcblxuICBBSVR5cGUgPSAwXG5cbiAgc2FtcGxlQWRhcHRvcnMgPSBbXG4gICAgeyBsYWJlbDogXCJDbGllbnRJbmZvIEJhc2ljXCIsIHZhbHVlOiBDbGllbnRJbmZvLmFkYXB0IH0sXG4gICAgeyBsYWJlbDogXCJBSSBQcm9tcHRcIiwgdmFsdWU6IEFJUHJvbXB0LmFkYXB0IH0sXG4gIF1cblxuICBzYW1wbGVNYXBwZXJzID0gW1xuICAgIHsgbGFiZWw6IFwiTWFwcGVyIEJhc2ljXCIsIHZhbHVlOiBDbGllbnRJbmZvTWFwcGVyLmFkYXB0IH0sXG4gICAgeyBsYWJlbDogXCJBSSBQcm9tcHRcIiwgdmFsdWU6IEFJUHJvbXB0LmFkYXB0IH0sXG4gIF1cblxuICBnZXQgcmV0cnkoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdEZvcm0uZ2V0KCdyZXRyeScpPy52YWx1ZVxuICB9XG5cbiAgZ2V0IGhlYWRlcnMoKTogRm9ybUFycmF5IHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0Rm9ybS5nZXQoJ2hlYWRlcnMnKSBhcyBGb3JtQXJyYXlcbiAgfVxuXG4gIGdldCBpc1ZhbGlkKCkge1xuICAgIHRoaXMucmVxdWVzdEZvcm0ubWFya0FsbEFzVG91Y2hlZCgpXG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdEZvcm0udmFsaWRcbiAgfVxuXG4gIGhhc0lkID0gKGFycjogYW55W10pID0+IHtcbiAgICBpZiAoYXJyLmxlbmd0aCA9PT0gMCkgcmV0dXJuIGZhbHNlXG4gICAgcmV0dXJuICFpc05hTihhcnJbYXJyLmxlbmd0aCAtIDFdKVxuICB9XG5cbiAgcHJvcHMgPSAoYWRhcHRlcjogYW55KSA9PiB7XG4gICAgcmV0dXJuIChhZGFwdGVyKSA/IGFkYXB0ZXIoKSA6IG51bGxcbiAgfVxuXG4gIC8vIHNlcnZlciA9IGBodHRwOi8vc2FtcGxlLWVuZHBvaW50L2FzL2F1dGhvcml6YXRpb24ub2F1dGgyYFxuXG4gIGFycmF5T2JqZWN0c1RvT2JqZWN0cyA9IChhcnI6IGFueVtdKSA9PiB7XG4gICAgcmV0dXJuIEFycmF5LmlzQXJyYXkoYXJyKSA/IGFyci5yZWR1Y2UoKG9iaiwgaXRlbSkgPT4gT2JqZWN0LmFzc2lnbihvYmosIHsgW2l0ZW0ua2V5XTogaXRlbS52YWx1ZSB9KSwge30pIDoge31cbiAgfVxuXG4gIGNvbnN0cnVjdG9yKCkgeyB9XG5cbiAgbmdPbkluaXQoKSB7XG5cbiAgICAvLyBjb25zdCByZXFHZXQyID0gQXBpUmVxdWVzdC5hZGFwdCh7XG4gICAgLy8gICBzZXJ2ZXIsXG4gICAgLy8gICBwYXRoOiBbJ2NsaWVudHMnXSxcbiAgICAvLyAgIGhlYWRlcnM6IHsgYXV0aGVudGljYXRpb246IFwiQmVhcmVyIDxLRVk+XCIgfSxcbiAgICAvLyAgIGFkYXB0ZXI6IENsaWVudEluZm8sXG4gICAgLy8gICBkYXRhVHlwZTogRGF0YVR5cGUuT0JKRUNULFxuICAgIC8vICAgLy8gY29uY3VycmVudDogZmFsc2UsXG4gICAgLy8gICAvLyBwb2xsaW5nOiAzLCAvL3NlY29uZHNcbiAgICAvLyB9KVxuXG4gICAgLy8gY29uc3QgcmVxMiA9IFsxMDI0LDEwMjUsMTAyNl0ubWFwKGl0ZW0gPT4ge1xuICAgIC8vICAgcmV0dXJuIHRoaXMuaHR0cE1hbmFnZXJTZXJ2aWNlLmdldFJlcXVlc3Q8Q2xpZW50SW5mb1tdPihyZXFHZXQyLCBbaXRlbV0pXG4gICAgLy8gICAucGlwZShcbiAgICAvLyAgICAgY2F0Y2hFcnJvcihlcnJvciA9PiB7XG4gICAgLy8gICAgICAgcmV0dXJuIHRocm93RXJyb3IoKCkgPT4gbmV3IEVycm9yKGVycm9yLmVycm9yLm1lc3NhZ2UpKVxuICAgIC8vICAgICB9KVxuICAgIC8vICAgKVxuICAgIC8vIH0pXG5cbiAgICAvLyBmb3JrSm9pbihyZXEyKVxuICAgIC8vIC5zdWJzY3JpYmUocmVzID0+IGNvbnNvbGUubG9nKHJlcykpXG5cbiAgfVxuXG4gIGFkZEhlYWRlcigpIHtcbiAgICBjb25zdCBoZWFkZXIgPSB0aGlzLmZiLmdyb3VwKHtcbiAgICAgIGtleTogWycnLCBWYWxpZGF0b3JzLnJlcXVpcmVkXSxcbiAgICAgIHZhbHVlOiBbJyddXG4gICAgfSlcbiAgICB0aGlzLmhlYWRlcnMucHVzaChoZWFkZXIpXG4gIH1cblxuICByZW1vdmVIZWFkZXIoaW5kZXg6IG51bWJlcikge1xuICAgIHRoaXMuaGVhZGVycy5yZW1vdmVBdChpbmRleClcbiAgfVxuXG4gIGNvbXBpbGVSZXF1ZXN0KCkge1xuXG4gICAgY29uc3QgcmVxdWVzdFBhcmFtcyA9IHRoaXMucmVxdWVzdEZvcm0udmFsdWVcblxuICAgIHJlcXVlc3RQYXJhbXMuaGVhZGVycyA9IHRoaXMuYXJyYXlPYmplY3RzVG9PYmplY3RzKFxuICAgICAgcmVxdWVzdFBhcmFtcy5oZWFkZXJzIHx8IFtdXG4gICAgKVxuXG4gICAgY29uc3QgcGF0aFJlcSA9IChyZXF1ZXN0UGFyYW1zLnBhdGggPT09IFwiXCIpID8gW10gOiAocmVxdWVzdFBhcmFtcy5wYXRoIHx8IFwiXCIpLnNwbGl0KFwiL1wiKVxuXG4gICAgaWYgKCF0aGlzLnBvbGxpbmdTdGF0ZS5jaGVja2VkKSByZXF1ZXN0UGFyYW1zLnBvbGxpbmcgPSAwXG5cbiAgICBpZiAoIXRoaXMuZmFpbGVkU3RhdGUuY2hlY2tlZCkge1xuICAgICAgcmVxdWVzdFBhcmFtcy5yZXRyeSA9IHsgdGltZXM6IDAsZGVsYXk6IDAgfVxuICAgIH1cblxuICAgIGNvbnN0IGFwaU9wdGlvbnMgPSBBcGlSZXF1ZXN0LmFkYXB0KHJlcXVlc3RQYXJhbXMpXG4gICAgYXBpT3B0aW9ucy5wYXRoID0gW11cblxuICAgIHJldHVybiB7IGFwaU9wdGlvbnM6IGFwaU9wdGlvbnMsIHBhdGg6IHBhdGhSZXEgfVxuXG4gIH1cblxuICBvbkdldFJlcXVlc3QoKSB7XG5cbiAgICBpZighdGhpcy5pc1ZhbGlkKSByZXR1cm5cblxuICAgIGNvbnN0IHJlcVBhcmFtcyA9IHRoaXMuY29tcGlsZVJlcXVlc3QoKVxuXG4gICAgdGhpcy5yZXF1ZXN0UGFyYW1zLkdFVCA9IHJlcVBhcmFtcy5hcGlPcHRpb25zXG5cbiAgICB0aGlzLkdFVCQgPSBFTVBUWSAvL0NhbmNlbHMgUHJldmlvdXNcbiAgICB0aGlzLkdFVF9lcnJvciQubmV4dCgnJylcblxuICAgIHRoaXMuR0VUJCA9IHRoaXMuaHR0cE1hbmFnZXJTZXJ2aWNlLmdldFJlcXVlc3Q8Q2xpZW50SW5mb1tdPihyZXFQYXJhbXMuYXBpT3B0aW9ucywgcmVxUGFyYW1zLnBhdGgpXG4gICAgLnBpcGUoXG4gICAgICAvLyB0YXAoKGRhdGEpID0+IGNvbnNvbGUubG9nKFwiQVBJIEdFVCByZXNwb25zZVwiLCBkYXRhKSksXG4gICAgICBjYXRjaEVycm9yKGVycm9yID0+IHtcbiAgICAgICAgcmV0dXJuIHRocm93RXJyb3IoKCkgPT4gdGhpcy5lcnJvckhhbmRsaW5nKGVycm9yLCAnR0VUJykpXG4gICAgICB9KVxuICAgIClcblxuICB9XG5cbiAgb25DcmVhdGVSZXF1ZXN0KCkge1xuXG4gICAgaWYoIXRoaXMuaXNWYWxpZCkgcmV0dXJuXG5cbiAgICBjb25zdCByZXFQYXJhbXMgPSB0aGlzLmNvbXBpbGVSZXF1ZXN0KClcbiAgICB0aGlzLnJlcXVlc3RQYXJhbXMuUE9TVCA9IHJlcVBhcmFtcy5hcGlPcHRpb25zXG5cbiAgICB0aGlzLlBPU1QkID0gRU1QVFkgLy9DYW5jZWxzIFByZXZpb3VzXG4gICAgdGhpcy5QT1NUX2Vycm9yJC5uZXh0KCcnKVxuXG4gICAgY29uc29sZS5sb2coXCJQT1NUXCIsIHRoaXMuc2FtcGxlQ2xpZW50RGF0YSlcbiAgICBjb25zb2xlLmxvZyhcIlBPU1RcIiwgcmVxUGFyYW1zLmFwaU9wdGlvbnMpXG4gICAgY29uc29sZS5sb2coXCJQT1NUXCIsIHJlcVBhcmFtcy5wYXRoKVxuXG4gICAgdGhpcy5QT1NUJCA9IHRoaXMuaHR0cE1hbmFnZXJTZXJ2aWNlLnBvc3RSZXF1ZXN0PENsaWVudEluZm8+KHRoaXMuc2FtcGxlQ2xpZW50RGF0YSwgcmVxUGFyYW1zLmFwaU9wdGlvbnMsIHJlcVBhcmFtcy5wYXRoKVxuICAgIC5waXBlKFxuICAgICAgLy8gdGFwKChkYXRhKSA9PiBjb25zb2xlLmxvZyhcIkFQSSBQT1NUIHJlc3BvbnNlXCIsIGRhdGEpKSxcbiAgICAgIGNhdGNoRXJyb3IoZXJyb3IgPT4ge1xuICAgICAgICByZXR1cm4gdGhyb3dFcnJvcigoKSA9PiB0aGlzLmVycm9ySGFuZGxpbmcoZXJyb3IsICdQT1NUJykpXG4gICAgICB9KVxuICAgIClcblxuICB9XG5cbiAgb25VcGRhdGVSZXF1ZXN0KCkge1xuXG4gICAgaWYoIXRoaXMuaXNWYWxpZCkgcmV0dXJuXG5cbiAgICBjb25zdCByZXFQYXJhbXMgPSB0aGlzLmNvbXBpbGVSZXF1ZXN0KClcblxuICAgIGlmKCF0aGlzLmhhc0lkKHJlcVBhcmFtcy5wYXRoKSkge1xuICAgICAgY29uc29sZS5sb2coXCJNaXNzaW5nIElEXCIpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICB0aGlzLnNhbXBsZUNsaWVudERhdGEuaWQgPSBwYXJzZUludChyZXFQYXJhbXMucGF0aFtyZXFQYXJhbXMucGF0aC5sZW5ndGgtMV0pXG4gICAgdGhpcy5yZXF1ZXN0UGFyYW1zLlBVVCA9IHJlcVBhcmFtcy5hcGlPcHRpb25zXG5cbiAgICB0aGlzLlBVVCQgPSBFTVBUWSAvL0NhbmNlbHMgUHJldmlvdXNcbiAgICB0aGlzLlBVVF9lcnJvciQubmV4dCgnJylcblxuICAgIHRoaXMuUFVUJCA9IHRoaXMuaHR0cE1hbmFnZXJTZXJ2aWNlLnB1dFJlcXVlc3Q8YW55Pih0aGlzLnNhbXBsZUNsaWVudERhdGEsIHJlcVBhcmFtcy5hcGlPcHRpb25zLCByZXFQYXJhbXMucGF0aClcbiAgICAucGlwZShcbiAgICAgIC8vIHRhcCgoZGF0YSkgPT4gY29uc29sZS5sb2coXCJBUEkgUFVUIHJlc3BvbnNlXCIsIGRhdGEpKSxcbiAgICAgIGNhdGNoRXJyb3IoZXJyb3IgPT4ge1xuICAgICAgICByZXR1cm4gdGhyb3dFcnJvcigoKSA9PiB0aGlzLmVycm9ySGFuZGxpbmcoZXJyb3IsICdQVVQnKSlcbiAgICAgIH0pXG4gICAgKVxuXG4gIH1cblxuICBvbkRlbGV0ZVJlcXVlc3QoKSB7XG5cbiAgICBpZighdGhpcy5pc1ZhbGlkKSByZXR1cm5cblxuICAgIGNvbnN0IHJlcVBhcmFtcyA9IHRoaXMuY29tcGlsZVJlcXVlc3QoKVxuICAgIHRoaXMucmVxdWVzdFBhcmFtcy5ERUxFVEUgPSByZXFQYXJhbXMuYXBpT3B0aW9uc1xuXG4gICAgaWYoIXRoaXMuaGFzSWQocmVxUGFyYW1zLnBhdGgpKSB7XG4gICAgICBjb25zb2xlLmxvZyhcIk1pc3NpbmcgSURcIilcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHRoaXMuc2FtcGxlQ2xpZW50RGF0YS5pZCA9IHBhcnNlSW50KHJlcVBhcmFtcy5wYXRoW3JlcVBhcmFtcy5wYXRoLmxlbmd0aC0xXSlcbiAgICB0aGlzLnJlcXVlc3RQYXJhbXMuREVMRVRFID0gcmVxUGFyYW1zLmFwaU9wdGlvbnNcblxuICAgIHRoaXMuREVMRVRFJCA9IEVNUFRZIC8vQ2FuY2VscyBQcmV2aW91c1xuICAgIHRoaXMuREVMRVRFX2Vycm9yJC5uZXh0KCcnKVxuXG4gICAgdGhpcy5ERUxFVEUkID0gdGhpcy5odHRwTWFuYWdlclNlcnZpY2UuZGVsZXRlUmVxdWVzdDxDbGllbnRJbmZvPihyZXFQYXJhbXMuYXBpT3B0aW9ucywgcmVxUGFyYW1zLnBhdGgpXG4gICAgLnBpcGUoXG4gICAgICAvLyB0YXAoKGRhdGEpID0+IGNvbnNvbGUubG9nKFwiQVBJIERFTEVURSByZXNwb25zZVwiLCBkYXRhKSksXG4gICAgICBjYXRjaEVycm9yKGVycm9yID0+IHtcbiAgICAgICAgcmV0dXJuIHRocm93RXJyb3IoKCkgPT4gdGhpcy5lcnJvckhhbmRsaW5nKGVycm9yLCAnREVMRVRFJykpXG4gICAgICB9KVxuICAgIClcblxuICB9XG5cbiAgb25TdHJlYW1Qb3N0UmVxdWVzdCgpIHtcblxuICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm5cblxuICAgIGNvbnN0IHJlcVBhcmFtcyA9IHRoaXMuY29tcGlsZVJlcXVlc3QoKVxuXG4gICAgbGV0IHBheWxvYWQgPSB7fVxuICAgIGxldCBhcGlQYXRoOiBzdHJpbmdbXSA9IHJlcVBhcmFtcy5wYXRoXG4gICAgbGV0IGFwaU9wdGlvbnMgPSByZXFQYXJhbXMuYXBpT3B0aW9uc1xuICAgIGxldCByZXNwb25zZU1hcHBlcjogKGl0ZW1zOiBhbnkpID0+IGFueSA9IChpdGVtcykgPT4gaXRlbXMucmVzcG9uc2VcblxuICAgIGlmICh0aGlzLkFJVHlwZSA9PT0gMCkge1xuXG4gICAgICAvLyBBUEkgcmVxdWVzdFxuICAgICAgcGF5bG9hZCA9IHsgcHJvbXB0OiB0aGlzLnF1ZXN0aW9uQ29udHJvbC52YWx1ZSB9XG5cbiAgICB9IGVsc2Uge1xuXG4gICAgICAvLyBMb2NhbCBPbGxhbWEgcmVxdWVzdFxuICAgICAgYXBpT3B0aW9ucy5zZXJ2ZXIgPSBcImFwaVwiXG4gICAgICBhcGlQYXRoID0gW1wiZ2VuZXJhdGVcIl1cbiAgICAgIGFwaU9wdGlvbnMuc3RyZWFtID0gdHJ1ZVxuICAgICAgcGF5bG9hZCA9IHtcbiAgICAgICAgbW9kZWw6IFwicGhpMzpsYXRlc3RcIixcbiAgICAgICAgcHJvbXB0OiB0aGlzLnF1ZXN0aW9uQ29udHJvbC52YWx1ZSxcbiAgICAgICAgc3RyZWFtOiB0cnVlLFxuICAgICAgfVxuXG4gICAgICByZXNwb25zZU1hcHBlciA9IChpdGVtcykgPT4gaXRlbXMubWFwKCh3b3JkOiBhbnkpID0+IHdvcmQucmVzcG9uc2UpLmZsYXQoKS5qb2luKCcnKVxuXG4gICAgfVxuXG4gICAgdGhpcy5yZXF1ZXN0UGFyYW1zLlNUUkVBTSA9IGFwaU9wdGlvbnNcbiAgICB0aGlzLlNUUkVBTV9BSSQgPSBFTVBUWVxuICAgIHRoaXMuU1RSRUFNX0FJX2Vycm9yJC5uZXh0KCcnKVxuXG4gICAgdGhpcy5TVFJFQU1fQUkkID0gdGhpcy5odHRwTWFuYWdlclNlcnZpY2UucG9zdFJlcXVlc3Q8YW55PihwYXlsb2FkLCBhcGlPcHRpb25zLCBhcGlQYXRoKS5waXBlKFxuICAgICAgbWFwKHJlc3BvbnNlTWFwcGVyKSxcbiAgICAgIHRhcCgoKSA9PiB0aGlzLnF1ZXN0aW9uQ29udHJvbC5yZXNldCgpKSxcbiAgICAgIGNhdGNoRXJyb3IoZXJyb3IgPT4gdGhyb3dFcnJvcigoKSA9PiB0aGlzLmVycm9ySGFuZGxpbmcoZXJyb3IsICdTVFJFQU0nKSkpXG4gICAgKVxuXG4gIH1cblxuXG4gIG9uU3RyZWFtUmVxdWVzdCgpIHtcblxuICAgIGlmKCF0aGlzLmlzVmFsaWQpIHJldHVyblxuXG4gICAgY29uc3QgcmVxUGFyYW1zID0gdGhpcy5jb21waWxlUmVxdWVzdCgpXG5cbiAgICByZXFQYXJhbXMuYXBpT3B0aW9ucy5zdHJlYW0gPSB0cnVlXG5cbiAgICB0aGlzLnJlcXVlc3RQYXJhbXMuR0VUID0gcmVxUGFyYW1zLmFwaU9wdGlvbnNcbiAgICB0aGlzLlNUUkVBTSQgPSB0aGlzLmh0dHBNYW5hZ2VyU2VydmljZS5nZXRSZXF1ZXN0PENsaWVudEluZm9bXT4ocmVxUGFyYW1zLmFwaU9wdGlvbnMsIHJlcVBhcmFtcy5wYXRoKVxuICAgICAgICAucGlwZShcbiAgICAgIC8vIHRhcCgoZGF0YSkgPT4gY29uc29sZS5sb2coXCJBUEkgU1RSRUFNIHJlc3BvbnNlXCIsIGRhdGEpKSxcbiAgICAgIGNhdGNoRXJyb3IoZXJyb3IgPT4ge1xuICAgICAgICByZXR1cm4gdGhyb3dFcnJvcigoKSA9PiB0aGlzLmVycm9ySGFuZGxpbmcoZXJyb3IsICdTVFJFQU0nKSlcbiAgICAgIH0pXG4gICAgKVxuXG4gIH1cblxuICBvbkRvd25sb2FkQ29tcGxldGVkKCkge1xuXG4gICAgY29uc3QgbWVzc2FnZSA9IFwiRG93bmxvYWQgQ29tcGxldGVkXCJcblxuICAgIGNvbnN0IGRpc3BsYXkgPSBUb2FzdERpc3BsYXkuYWRhcHQoe1xuICAgICAgbWVzc2FnZSxcbiAgICAgIGFjdGlvbjogJ09rJyxcbiAgICAgIGNvbG9yOiBUb2FzdENvbG9ycy5TVUNDRVNTLFxuICAgICAgaWNvbjogJ3NlbnRpbWVudF9zYXRpc2ZpZWRfYWx0JyxcbiAgICB9KVxuXG4gICAgdGhpcy50b2FzdE1lc3NhZ2UudG9hc3RNZXNzYWdlKGRpc3BsYXkpXG4gIH1cblxuICBvbkRvd25sb2FkRmFpbGVkKGVycjogc3RyaW5nKSB7XG5cbiAgICBjb25zdCBtZXNzYWdlID0gXCJEb3dubG9hZCBGYWlsZWRcIlxuXG4gICAgY29uc3QgZGlzcGxheSA9IFRvYXN0RGlzcGxheS5hZGFwdCh7XG4gICAgICBtZXNzYWdlLFxuICAgICAgYWN0aW9uOiAnT2snLFxuICAgICAgY29sb3I6IFRvYXN0Q29sb3JzLkVSUk9SLFxuICAgICAgaWNvbjogJ3dhcm5pbmcnLFxuICAgIH0pXG5cbiAgICB0aGlzLnRvYXN0TWVzc2FnZS50b2FzdE1lc3NhZ2UoZGlzcGxheSlcbiAgfVxuXG4gIGVycm9ySGFuZGxpbmcoZXJyOiBhbnksIHR5cGU6IHN0cmluZykge1xuICAgIGlmKHR5cGUgPT09ICdHRVQnKSB0aGlzLkdFVF9lcnJvciQubmV4dChlcnIubWVzc2FnZSlcbiAgICBpZih0eXBlID09PSAnUE9TVCcpIHRoaXMuUE9TVF9lcnJvciQubmV4dChlcnIubWVzc2FnZSlcbiAgICBpZih0eXBlID09PSAnUFVUJykgdGhpcy5QVVRfZXJyb3IkLm5leHQoZXJyLm1lc3NhZ2UpXG4gICAgaWYodHlwZSA9PT0gJ0RFTEVURScpIHRoaXMuREVMRVRFX2Vycm9yJC5uZXh0KGVyci5tZXNzYWdlKVxuICAgIGlmKHR5cGUgPT09ICdTVFJFQU0nKSB0aGlzLlNUUkVBTV9lcnJvciQubmV4dChlcnIubWVzc2FnZSlcbiAgfVxuXG4gIG9uU2VsZWN0QUlUeXBlKHR5cGU6IG51bWJlcikge1xuICAgIHRoaXMuQUlUeXBlID0gdHlwZVxuICB9XG5cbn1cbiIsIjxkaXYgc3R5bGU9XCJtYXJnaW46IDJyZW07XCI+XG5cbiAgPGgyPlxuICAgIEhUVFAgUmVxdWVzdCBNYW5hZ2VyXG4gIDwvaDI+XG5cbiAgPGRpdiBbZm9ybUdyb3VwXT1cInJlcXVlc3RGb3JtXCIgc3R5bGU9XCJtYXJnaW4tdG9wOiAycmVtO1wiPlxuICAgIDxkaXYgc3R5bGU9XCJkaXNwbGF5OiBmbGV4OyBnYXA6IC41cmVtXCI+XG4gICAgICA8bWF0LWZvcm0tZmllbGQgYXBwZWFyYW5jZT1cIm91dGxpbmVcIj5cbiAgICAgICAgPG1hdC1sYWJlbD5BZGFwdGVyIChNb2RlbCk8L21hdC1sYWJlbD5cbiAgICAgICAgPG1hdC1zZWxlY3QgZm9ybUNvbnRyb2xOYW1lPVwiYWRhcHRlclwiICNhZGFwdGVyU2VsZWN0PlxuICAgICAgICAgIDxtYXQtb3B0aW9uPk5vbmU8L21hdC1vcHRpb24+XG4gICAgICAgICAgPG1hdC1vcHRpb24gKm5nRm9yPVwibGV0IGFkYXB0ZXIgb2Ygc2FtcGxlQWRhcHRvcnNcIiBbdmFsdWVdPVwiYWRhcHRlci52YWx1ZVwiPlxuICAgICAgICAgICAge3thZGFwdGVyLmxhYmVsfX1cbiAgICAgICAgICA8L21hdC1vcHRpb24+XG4gICAgICAgIDwvbWF0LXNlbGVjdD5cbiAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgICA8bWF0LWZvcm0tZmllbGQgYXBwZWFyYW5jZT1cIm91dGxpbmVcIj5cbiAgICAgICAgPG1hdC1sYWJlbD5NYXBwZXIgKE1vZGVsKTwvbWF0LWxhYmVsPlxuICAgICAgICA8bWF0LXNlbGVjdCBmb3JtQ29udHJvbE5hbWU9XCJtYXBwZXJcIiAjbWFwcGVyU2VsZWN0PlxuICAgICAgICAgIDxtYXQtb3B0aW9uPk5vbmU8L21hdC1vcHRpb24+XG4gICAgICAgICAgPG1hdC1vcHRpb24gKm5nRm9yPVwibGV0IG1hcHBlciBvZiBzYW1wbGVNYXBwZXJzXCIgW3ZhbHVlXT1cIm1hcHBlci52YWx1ZVwiPlxuICAgICAgICAgICAge3ttYXBwZXIubGFiZWx9fVxuICAgICAgICAgIDwvbWF0LW9wdGlvbj5cbiAgICAgICAgPC9tYXQtc2VsZWN0PlxuICAgICAgPC9tYXQtZm9ybS1maWVsZD5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDsgbWFyZ2luLWJvdHRvbTogMnJlbTtcIiAqbmdJZj1cImFkYXB0ZXJTZWxlY3QudmFsdWUgfHwgbWFwcGVyU2VsZWN0LnZhbHVlXCI+XG4gICAgICA8ZGl2IHN0eWxlPVwiZmxleDoxXCIgY2xhc3M9XCJib3hcIj5cbiAgICAgICAgPGgzPkFkYXB0ZXIgKEluY29taW5nKTwvaDM+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJhZGFwdGVyU2VsZWN0LnZhbHVlOyBlbHNlIE5PX0FEQVBURVJcIj5cbiAgICAgICAgICB7eyBwcm9wcyhhZGFwdGVyU2VsZWN0LnZhbHVlKSB8IGpzb24gfX1cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxuZy10ZW1wbGF0ZSAjTk9fQURBUFRFUj5ObyBUcmFuc2Zvcm1hdGlvbjwvbmctdGVtcGxhdGU+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXYgc3R5bGU9XCJmbGV4OjFcIiBjbGFzcz1cImJveFwiPlxuICAgICAgICA8aDM+TWFwcGVyIChPdXRnb2luZyk8L2gzPlxuICAgICAgICA8ZGl2ICpuZ0lmPVwibWFwcGVyU2VsZWN0LnZhbHVlOyBlbHNlIE5PX01BUFBFUlwiPlxuICAgICAgICAgIHt7IHByb3BzKG1hcHBlclNlbGVjdC52YWx1ZSkgfCBqc29uIH19XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8bmctdGVtcGxhdGUgI05PX01BUFBFUj5ObyBUcmFuc2Zvcm1hdGlvbjwvbmctdGVtcGxhdGU+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2PlxuICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCI+XG4gICAgICAgIDxtYXQtbGFiZWw+UmVzdFBhdGggKC8gZGVsaW1pdGVkKTwvbWF0LWxhYmVsPlxuICAgICAgICA8aW5wdXQgbWF0SW5wdXQgcGxhY2Vob2xkZXI9XCJjbGllbnRzL2xpc3RcIiBmb3JtQ29udHJvbE5hbWU9XCJwYXRoXCI+XG4gICAgICA8L21hdC1mb3JtLWZpZWxkPlxuICAgIDwvZGl2PlxuICAgIDxkaXY+XG4gICAgICA8ZGl2IGZvcm1BcnJheU5hbWU9XCJoZWFkZXJzXCI+XG4gICAgICAgIDxkaXYgKm5nRm9yPVwibGV0IHRhc2sgb2YgaGVhZGVycy5jb250cm9sczsgbGV0IGkgPSBpbmRleFwiIFtmb3JtR3JvdXBOYW1lXT1cImlcIj5cbiAgICAgICAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDsgZ2FwOiAuNXJlbVwiPlxuICAgICAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCI+XG4gICAgICAgICAgICAgIDxtYXQtbGFiZWw+S2V5PC9tYXQtbGFiZWw+XG4gICAgICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCBwbGFjZWhvbGRlcj1cImF1dGhlbnRpY2F0aW9uXCIgZm9ybUNvbnRyb2xOYW1lPVwia2V5XCI+XG4gICAgICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxuICAgICAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3R5bGU9XCJmbGV4OjFcIj5cbiAgICAgICAgICAgICAgPG1hdC1sYWJlbD5WYWx1ZTwvbWF0LWxhYmVsPlxuICAgICAgICAgICAgICA8aW5wdXQgbWF0SW5wdXQgcGxhY2Vob2xkZXI9XCJzYW1wbGVcIiBmb3JtQ29udHJvbE5hbWU9XCJ2YWx1ZVwiPlxuICAgICAgICAgICAgPC9tYXQtZm9ybS1maWVsZD5cbiAgICAgICAgICAgIDxkaXYgc3R5bGU9XCJtYXJnaW4tdG9wOiAuNXJlbTtcIj5cbiAgICAgICAgICAgICAgPGJ1dHRvbiBtYXQtaWNvbi1idXR0b24gKGNsaWNrKT1cInJlbW92ZUhlYWRlcihpKVwiPlxuICAgICAgICAgICAgICAgICAgPG1hdC1pY29uPmNsb3NlPC9tYXQtaWNvbj5cbiAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxidXR0b24gbWF0LXN0cm9rZWQtYnV0dG9uIChjbGljayk9XCJhZGRIZWFkZXIoKVwiIGNsYXNzPVwiYnRuXCI+QWRkIEhlYWRlcjwvYnV0dG9uPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgc3R5bGU9XCJtYXJnaW4tdG9wOiAycmVtOyBkaXNwbGF5OiBmbGV4OyBmbGV4LWRpcmVjdGlvbjpjb2x1bW47IGdhcDogMXJlbTtcIj5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxtYXQtc2xpZGUtdG9nZ2xlICNmYWlsZWRTdGF0ZT5SZXRyeSBvbiBGYWlsZWQ8L21hdC1zbGlkZS10b2dnbGU+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJmYWlsZWRTdGF0ZS5jaGVja2VkXCIgc3R5bGU9XCJkaXNwbGF5OiBmbGV4OyBnYXA6IC41cmVtOyBtYXJnaW4tdG9wOiAxcmVtO1wiIGZvcm1Hcm91cE5hbWU9XCJyZXRyeVwiPlxuICAgICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiPlxuICAgICAgICAgICAgPG1hdC1sYWJlbD4jb2YgVGltZXM8L21hdC1sYWJlbD5cbiAgICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCBwbGFjZWhvbGRlcj1cIjNcIiBmb3JtQ29udHJvbE5hbWU9XCJ0aW1lc1wiIHZhbHVlPVwiM1wiPlxuICAgICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCI+XG4gICAgICAgICAgICA8bWF0LWxhYmVsPkRlbGF5IFVudGlsIE5leHQ8L21hdC1sYWJlbD5cbiAgICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCBwbGFjZWhvbGRlcj1cIjNcIiBmb3JtQ29udHJvbE5hbWU9XCJkZWxheVwiIHZhbHVlPVwiM1wiPlxuICAgICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgICA8ZGl2PlxuICAgICAgICA8bWF0LXNsaWRlLXRvZ2dsZSAjcG9sbGluZ1N0YXRlPlBvbGxpbmc8L21hdC1zbGlkZS10b2dnbGU+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJwb2xsaW5nU3RhdGUuY2hlY2tlZFwiPlxuICAgICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN0eWxlPVwibWFyZ2luLXRvcDogMXJlbVwiPlxuICAgICAgICAgICAgPG1hdC1sYWJlbD4jb2YgU2Vjb25kczwvbWF0LWxhYmVsPlxuICAgICAgICAgICAgPGlucHV0IG1hdElucHV0IHBsYWNlaG9sZGVyPVwiM1wiIGZvcm1Db250cm9sTmFtZT1cInBvbGxpbmdcIiB2YWx1ZT1cIjNcIj5cbiAgICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cblxuICA8ZGl2IHN0eWxlPVwibWFyZ2luLWJvdHRvbTogMXJlbTsgbWFyZ2luLXRvcDogMnJlbTtcIj5cbiAgICA8bWF0LXByb2dyZXNzLWJhciBtb2RlPVwiaW5kZXRlcm1pbmF0ZVwiXG4gICAgICAqbmdJZj1cIihpc1BlbmRpbmckIHwgYXN5bmMpXCJcbiAgICA+PC9tYXQtcHJvZ3Jlc3MtYmFyPlxuICAgIDxtYXQtcHJvZ3Jlc3MtYmFyIG1vZGU9XCJkZXRlcm1pbmF0ZVwiXG4gICAgICAqbmdJZj1cInBvbGxpbmdTdGF0ZS5jaGVja2VkICYmICEoaXNQZW5kaW5nJCB8IGFzeW5jKVwiXG4gICAgICBbdmFsdWVdPVwiKHRoaXMuY291bnRkb3duJCB8IGFzeW5jKVwiXG4gICAgPjwvbWF0LXByb2dyZXNzLWJhcj5cbiAgPC9kaXY+XG5cbiAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDFyZW1cIj5cbiAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cbiAgPC9kaXY+XG5cbiAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDJyZW1cIj5cbiAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDtcIj5cbiAgICAgIDxoMiBzdHlsZT1cImZsZXg6MVwiPkdFVCBSZXF1ZXN0PC9oMj5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxidXR0b24gbWF0LXJhaXNlZC1idXR0b24gKGNsaWNrKT1cIm9uR2V0UmVxdWVzdCgpXCIgY2xhc3M9XCJidG5cIj5SZXF1ZXN0PC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgKm5nSWY9XCIoR0VUX2Vycm9yJCB8IGFzeW5jKSBhcyBnZXRfZXJyb3JcIiBzdHlsZT1cIm1hcmdpbi10b3A6IC41cmVtO1wiPlxuICAgICAgPG1hdC1lcnJvcj57eyBnZXRfZXJyb3IgfX08L21hdC1lcnJvcj5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgc3R5bGU9XCJtYXJnaW4tdG9wOiAxcmVtO1wiICpuZ0lmPVwiKEdFVCQgfCBhc3luYykgYXMgZGF0YVJlY29yZFwiPlxuICAgICAgPCEtLSA8ZGl2IFtpbm5lckhUTUxdPVwiKEdFVCQgfCBhc3luYykgfCBqc29udlwiPjwvZGl2PiAtLT5cbiAgICAgIHt7IGRhdGFSZWNvcmQgfCBqc29uIH19XG4gICAgPC9kaXY+XG5cbiAgPC9kaXY+XG5cbiAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDJyZW1cIj5cbiAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cbiAgPC9kaXY+XG5cbiAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDJyZW1cIj5cbiAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDtcIj5cbiAgICAgIDxoMiBzdHlsZT1cImZsZXg6MVwiPlBPU1QgUmVxdWVzdDwvaDI+XG4gICAgICA8ZGl2PlxuICAgICAgICA8YnV0dG9uIG1hdC1yYWlzZWQtYnV0dG9uIChjbGljayk9XCJvbkNyZWF0ZVJlcXVlc3QoKVwiIGNsYXNzPVwiYnRuXCI+UmVxdWVzdDwvYnV0dG9uPlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG5cbiAgICA8ZGl2ICpuZ0lmPVwiKFBPU1RfZXJyb3IkIHwgYXN5bmMpIGFzIHBvc3RfZXJyb3JcIiBzdHlsZT1cIm1hcmdpbi10b3A6IC41cmVtO1wiPlxuICAgICAgPG1hdC1lcnJvcj57eyBwb3N0X2Vycm9yIH19PC9tYXQtZXJyb3I+XG4gICAgPC9kaXY+XG5cbiAgICA8ZGl2IHN0eWxlPVwibWFyZ2luLXRvcDogMXJlbTtcIiAqbmdJZj1cIihQT1NUJCB8IGFzeW5jKSBhcyBkYXRhUmVjb3JkXCI+XG4gICAgICA8IS0tIDxkaXYgW2lubmVySFRNTF09XCIoUE9TVCQgfCBhc3luYykgfCBqc29udlwiPjwvZGl2PiAtLT5cbiAgICAgIHt7IGRhdGFSZWNvcmQgfCBqc29uIH19XG4gICAgPC9kaXY+XG5cbiAgPC9kaXY+XG5cbiAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDJyZW1cIj5cbiAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cbiAgPC9kaXY+XG5cbiAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDJyZW1cIj5cbiAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDtcIj5cbiAgICAgIDxoMiBzdHlsZT1cImZsZXg6MVwiPlBVVCBSZXF1ZXN0PC9oMj5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxidXR0b24gbWF0LXJhaXNlZC1idXR0b24gKGNsaWNrKT1cIm9uVXBkYXRlUmVxdWVzdCgpXCIgY2xhc3M9XCJidG5cIj5SZXF1ZXN0PC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgKm5nSWY9XCIoUFVUX2Vycm9yJCB8IGFzeW5jKSBhcyBwdXRfZXJyb3JcIiBzdHlsZT1cIm1hcmdpbi10b3A6IC41cmVtO1wiPlxuICAgICAgPG1hdC1lcnJvcj57eyBwdXRfZXJyb3IgfX08L21hdC1lcnJvcj5cbiAgICA8L2Rpdj5cblxuICAgIDxoMz5JbmNsdWRlIFJlY29yZCBJRCBpbiB0aGUgUmVzdFBhdGg8L2gzPlxuXG4gICAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDFyZW07XCIgKm5nSWY9XCIoUFVUJCB8IGFzeW5jKSBhcyBkYXRhUmVjb3JkXCI+XG4gICAgICA8IS0tIDxkaXYgW2lubmVySFRNTF09XCIoUFVUJCB8IGFzeW5jKSB8IGpzb252XCI+PC9kaXY+IC0tPlxuICAgICAgIHt7IGRhdGFSZWNvcmQgfCBqc29uIH19XG4gICAgPC9kaXY+XG5cbiAgPC9kaXY+XG5cbiAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDJyZW1cIj5cbiAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cbiAgPC9kaXY+XG5cbiAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDJyZW1cIj5cbiAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDtcIj5cbiAgICAgIDxoMiBzdHlsZT1cImZsZXg6MVwiPkRFTEVURSBSZXF1ZXN0PC9oMj5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxidXR0b24gbWF0LXJhaXNlZC1idXR0b24gKGNsaWNrKT1cIm9uRGVsZXRlUmVxdWVzdCgpXCIgY2xhc3M9XCJidG5cIj5SZXF1ZXN0PC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxoMz5JbmNsdWRlIFJlY29yZCBJRCBpbiB0aGUgUmVzdFBhdGg8L2gzPlxuXG4gICAgPGRpdiAqbmdJZj1cIihERUxFVEVfZXJyb3IkIHwgYXN5bmMpIGFzIGRlbGV0ZV9lcnJvclwiIHN0eWxlPVwibWFyZ2luLXRvcDogLjVyZW07XCI+XG4gICAgICA8bWF0LWVycm9yPnt7IGRlbGV0ZV9lcnJvciB9fTwvbWF0LWVycm9yPlxuICAgIDwvZGl2PlxuXG4gICAgPGRpdiBzdHlsZT1cIm1hcmdpbi10b3A6IDFyZW07XCIgKm5nSWY9XCIoREVMRVRFJCB8IGFzeW5jKSBhcyBkYXRhUmVjb3JkXCI+XG4gICAgICA8IS0tIDxkaXYgW2lubmVySFRNTF09XCIoREVMRVRFJCB8IGFzeW5jKSB8IGpzb252XCI+PC9kaXY+IC0tPlxuICAgICAge3sgZGF0YVJlY29yZCB8IGpzb24gfX1cbiAgICA8L2Rpdj5cblxuICA8L2Rpdj5cblxuICA8ZGl2IHN0eWxlPVwibWFyZ2luLXRvcDogMnJlbVwiPlxuICAgIDxtYXQtZGl2aWRlcj48L21hdC1kaXZpZGVyPlxuICA8L2Rpdj5cblxuICA8ZGl2IHN0eWxlPVwibWFyZ2luLXRvcDogMnJlbVwiPlxuICAgIDxkaXYgc3R5bGU9XCJkaXNwbGF5OiBmbGV4O1wiPlxuICAgICAgPGgyIHN0eWxlPVwiZmxleDoxXCI+U3RyZWFtaW5nIEdFVCBSZXF1ZXN0PC9oMj5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxidXR0b24gbWF0LXJhaXNlZC1idXR0b24gKGNsaWNrKT1cIm9uU3RyZWFtUmVxdWVzdCgpXCIgY2xhc3M9XCJidG5cIj5SZXF1ZXN0PC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDwhLS0gPGRpdiAqbmdJZj1cIihTVFJFQU1fZXJyb3IkIHwgYXN5bmMpIGFzIHN0cmVhbV9lcnJvclwiIHN0eWxlPVwibWFyZ2luLXRvcDogLjVyZW07XCI+XG4gICAgICA8bWF0LWVycm9yPnt7IHN0cmVhbV9lcnJvciB9fTwvbWF0LWVycm9yPlxuICAgIDwvZGl2PiAtLT5cblxuICAgIDxkaXYgc3R5bGU9XCJtYXJnaW4tdG9wOiAxcmVtO1wiPlxuICAgICAgPGRpdiAqbmdJZj1cIihTVFJFQU0kIHwgYXN5bmMpIGFzIGRhdGFcIiBjbGFzcz1cImNvbnRhaW5lclwiPlxuICAgICAgICA8dGFibGUgbWF0LXRhYmxlIFtkYXRhU291cmNlXT1cImRhdGFcIj5cbiAgICAgICAgICA8bmctY29udGFpbmVyIG1hdENvbHVtbkRlZj1cImlkXCI+XG4gICAgICAgICAgICA8dGggbWF0LWhlYWRlci1jZWxsICptYXRIZWFkZXJDZWxsRGVmPiBJRCA8L3RoPlxuICAgICAgICAgICAgPHRkIG1hdC1jZWxsICptYXRDZWxsRGVmPVwibGV0IGVsZW1lbnRcIj4ge3tlbGVtZW50LmlkfX0gPC90ZD5cbiAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgICAgIDxuZy1jb250YWluZXIgbWF0Q29sdW1uRGVmPVwibmFtZVwiPlxuICAgICAgICAgICAgPHRoIG1hdC1oZWFkZXItY2VsbCAqbWF0SGVhZGVyQ2VsbERlZj4gRmlyc3QgTmFtZSA8L3RoPlxuICAgICAgICAgICAgPHRkIG1hdC1jZWxsICptYXRDZWxsRGVmPVwibGV0IGVsZW1lbnRcIj4ge3tlbGVtZW50Lm5hbWV9fSA8L3RkPlxuICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICAgICAgPG5nLWNvbnRhaW5lciBtYXRDb2x1bW5EZWY9XCJsYXN0TmFtZVwiPlxuICAgICAgICAgICAgPHRoIG1hdC1oZWFkZXItY2VsbCAqbWF0SGVhZGVyQ2VsbERlZj4gTGFzdCBOYW1lIDwvdGg+XG4gICAgICAgICAgICA8dGQgbWF0LWNlbGwgKm1hdENlbGxEZWY9XCJsZXQgZWxlbWVudFwiPiB7e2VsZW1lbnQubGFzdE5hbWV9fSA8L3RkPlxuICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICAgICAgPG5nLWNvbnRhaW5lciBtYXRDb2x1bW5EZWY9XCJhZ2VcIj5cbiAgICAgICAgICAgIDx0aCBtYXQtaGVhZGVyLWNlbGwgKm1hdEhlYWRlckNlbGxEZWY+IEFnZSA8L3RoPlxuICAgICAgICAgICAgPHRkIG1hdC1jZWxsICptYXRDZWxsRGVmPVwibGV0IGVsZW1lbnRcIj4ge3tlbGVtZW50LmFnZX19IDwvdGQ+XG4gICAgICAgICAgPC9uZy1jb250YWluZXI+XG5cbiAgICAgICAgICA8dHIgbWF0LWhlYWRlci1yb3cgKm1hdEhlYWRlclJvd0RlZj1cImRpc3BsYXllZENvbHVtbnNcIj48L3RyPlxuICAgICAgICAgIDx0ciBtYXQtcm93ICptYXRSb3dEZWY9XCJsZXQgcm93OyBjb2x1bW5zOiBkaXNwbGF5ZWRDb2x1bW5zO1wiPjwvdHI+XG4gICAgICAgIDwvdGFibGU+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICA8L2Rpdj5cblxuICA8ZGl2IHN0eWxlPVwibWFyZ2luLXRvcDogMnJlbVwiPlxuICAgIDxtYXQtZGl2aWRlcj48L21hdC1kaXZpZGVyPlxuICA8L2Rpdj5cblxuICA8ZGl2IHN0eWxlPVwibWFyZ2luLXRvcDogMnJlbVwiPlxuICAgIDxkaXYgc3R5bGU9XCJkaXNwbGF5OiBmbGV4O1wiPlxuICAgICAgPGgyIHN0eWxlPVwiZmxleDoxOyBwYWRkaW5nLXRvcDogLjVyZW07XCI+QUkgLTxzcGFuICpuZ0lmPVwiQUlUeXBlID09PSAxXCI+U1RSRUFNSU5HPC9zcGFuPiBQT1NUIFJlcXVlc3Q8L2gyPlxuICAgICAgPGRpdiBzdHlsZT1cImRpc3BsYXk6IGZsZXg7IGdhcDogMXJlbTtcIj5cbiAgICAgICAgPGJ1dHRvbiBtYXQtcmFpc2VkLWJ1dHRvbiBbbWF0TWVudVRyaWdnZXJGb3JdPVwibWVudVwiIHN0eWxlPVwibWluLXdpZHRoOiAxMjBweDtcIj5cbiAgICAgICAgICA8bWF0LWljb24+bGFuPC9tYXQtaWNvbj5cbiAgICAgICAgICAgIDxzcGFuICpuZ0lmPVwiQUlUeXBlID09PSAwOyBlbHNlIExPQ0FMXCI+U2VydmVyPC9zcGFuPlxuICAgICAgICAgICAgPG5nLXRlbXBsYXRlICNMT0NBTD5cbiAgICAgICAgICAgICAgTG9jYWxcbiAgICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgICA8bWF0LW1lbnUgI21lbnU9XCJtYXRNZW51XCI+XG4gICAgICAgICAgPGJ1dHRvbiBtYXQtbWVudS1pdGVtIChjbGljayk9XCJvblNlbGVjdEFJVHlwZSgwKVwiPlNlcnZlcjwvYnV0dG9uPlxuICAgICAgICAgIDxidXR0b24gbWF0LW1lbnUtaXRlbSAoY2xpY2spPVwib25TZWxlY3RBSVR5cGUoMSlcIj5Mb2NhbDwvYnV0dG9uPlxuICAgICAgICA8L21hdC1tZW51PlxuICAgICAgICA8ZGl2PlxuICAgICAgICAgIDxidXR0b24gbWF0LXJhaXNlZC1idXR0b24gKGNsaWNrKT1cIm9uU3RyZWFtUG9zdFJlcXVlc3QoKVwiIGNsYXNzPVwiYnRuXCI+QXNrIE1lPC9idXR0b24+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG5cbiAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDtcIj5cbiAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN0eWxlPVwiZmxleDoxXCI+XG4gICAgICAgIDxtYXQtbGFiZWw+QXNrIG1lIGEgUXVlc3Rpb248L21hdC1sYWJlbD5cbiAgICAgICAgPHRleHRhcmVhIG1hdElucHV0IHBsYWNlaG9sZGVyPVwiV2h5IGlzIHRoZSBza3kgYmx1ZT9cIiBbZm9ybUNvbnRyb2xdPVwicXVlc3Rpb25Db250cm9sXCI+PC90ZXh0YXJlYT5cbiAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgPC9kaXY+XG5cbiAgICA8ZGl2ICpuZ0lmPVwiKFNUUkVBTV9BSV9lcnJvciQgfCBhc3luYykgYXMgc3RyZWFtX2Vycm9yXCIgc3R5bGU9XCJtYXJnaW4tdG9wOiAuNXJlbTtcIj5cbiAgICAgIDxtYXQtZXJyb3I+e3sgc3RyZWFtX2Vycm9yIH19PC9tYXQtZXJyb3I+XG4gICAgPC9kaXY+XG5cbiAgICA8ZGl2ICpuZ0lmPVwiQUlUeXBlID09PSAxOyBlbHNlIEFMVEVSTkFUSVZFXCIgc3R5bGU9XCJjb2xvcjogcmVkO1wiPlxuICAgICAgWW91IG11c3QgaGF2ZSBPbGxhbWEgYWN0aXZlIGFuZCB0aGUgJ3BoaTM6bGF0ZXN0JyBtb2RlbCB0byB1c2UgdGhpcyBmZWF0dXJlLlxuICAgIDwvZGl2PlxuICAgIDxuZy10ZW1wbGF0ZSAjQUxURVJOQVRJVkU+XG4gICAgICA8c3BhbiBzdHlsZT1cImNvbG9yOiBncmF5O1wiPlxuICAgICAgICBEZWZpbmUgdGhlIFJlc3RQYXRoIHRvIHRoZSBBUEkgZW5kcG9pbnQgdGhhdCB3aWxsIGhhbmRsZSB0aGUgQUkgcmVxdWVzdC5cbiAgICAgICAgVXNlOiAnYWkvY2hhdCcgZm9yIHNlcnZlclxuICAgICAgPC9zcGFuPlxuICAgIDwvbmctdGVtcGxhdGU+XG5cbiAgICA8ZGl2PlxuICAgICAgPGRpdiAqbmdJZj1cIihTVFJFQU1fQUkkIHwgYXN5bmMpIGFzIGRhdGFcIiBzdHlsZT1cIm1hcmdpbi10b3A6IDFyZW07IGZvbnQtc2l6ZTogMS4ycmVtOyBib3JkZXItcmFkaXVzOiAxcmVtOyBib3JkZXI6IGJsYWNrIDFweCBzb2xpZDsgcGFkZGluZzogMnJlbTtcIj5cbiAgICAgICAgPHAgc3R5bGU9XCJtYXJnaW4tYm90dG9tOiAuNXJlbTsgd2hpdGUtc3BhY2U6cHJlLXdyYXA7IGxpbmUtaGVpZ2h0OiAxLjZyZW07XCI+e3tkYXRhfX08L3A+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICA8L2Rpdj5cblxuICA8ZGl2IHN0eWxlPVwibWFyZ2luLXRvcDogMS41cmVtOyBtYXJnaW4tYm90dG9tOiAxcmVtOyBsaW5lLWhlaWdodDogMS41cmVtO1wiPlxuICAgIDxtYXQtZGl2aWRlcj48L21hdC1kaXZpZGVyPlxuICA8L2Rpdj5cblxuICA8ZGl2PlxuICAgIDxkaXYgc3R5bGU9XCJkaXNwbGF5OiBmbGV4O1wiPlxuICAgICAgPGgyIHN0eWxlPVwiZmxleDoxOyBtYXJnaW4tYm90dG9tOiAwOyBwYWRkaW5nLXRvcDogLjVyZW07IGRpc3BsYXk6IGZsZXg7XCI+XG4gICAgICAgIDxkaXY+XG4gICAgICAgICAgRG93bmxvYWQgRmlsZVxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPGRpdiBzdHlsZT1cImZsZXg6MTsgbWFyZ2luLWxlZnQ6IDFyZW07XCI+XG4gICAgICAgICAgPG1hdC1zbGlkZS10b2dnbGUgI2Rpc2FibGU+XG4gICAgICAgICAgICA8c3BhbiAqbmdJZj1cImRpc2FibGUuY2hlY2tlZDsgZWxzZSBESVNBQkxFXCI+XG4gICAgICAgICAgICAgIEVuYWJsZVxuICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgPG5nLXRlbXBsYXRlICNESVNBQkxFPkRpc2FibGU8L25nLXRlbXBsYXRlPlxuICAgICAgICAgIDwvbWF0LXNsaWRlLXRvZ2dsZT5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2gyPlxuICAgICAgPGRpdj5cbiAgICAgICAgPGFwcC1maWxlLWRvd25sb2FkZXJcbiAgICAgICAgICBbZGlzYWJsZWRdPVwiZGlzYWJsZS5jaGVja2VkXCJcbiAgICAgICAgICBbZGVsYXlFcnJvcl09XCIzXCJcbiAgICAgICAgICBbYXBpUmVxdWVzdF09XCJkb3dubG9hZFJlcXVlc3RcIlxuICAgICAgICAgIChjb21wbGV0ZWQpPVwib25Eb3dubG9hZENvbXBsZXRlZCgpXCJcbiAgICAgICAgICAoZmFpbGVkKT1cIm9uRG93bmxvYWRGYWlsZWQoJGV2ZW50KVwiXG4gICAgICAgID48L2FwcC1maWxlLWRvd25sb2FkZXI+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbjwvZGl2PlxuIl19