@sinequa/assistant 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/chat/charts/chart/chart.component.d.ts +13 -13
  2. package/chat/chat-message/chat-message.component.d.ts +85 -81
  3. package/chat/chat-message/i18n/en.json +11 -0
  4. package/chat/chat-message/i18n/fr.json +11 -0
  5. package/chat/chat-reference/chat-reference.component.d.ts +14 -14
  6. package/chat/chat-reference/i18n/en.json +4 -0
  7. package/chat/chat-reference/i18n/fr.json +4 -0
  8. package/chat/chat-settings-v3/chat-settings-v3.component.d.ts +48 -50
  9. package/chat/chat-settings-v3/i18n/en.json +14 -0
  10. package/chat/chat-settings-v3/i18n/fr.json +14 -0
  11. package/chat/chat.component.d.ts +388 -1405
  12. package/chat/chat.service.d.ts +247 -228
  13. package/chat/debug-message/debug-message.component.d.ts +17 -17
  14. package/chat/debug-message/i18n/en.json +3 -0
  15. package/chat/debug-message/i18n/fr.json +3 -0
  16. package/chat/dialogs/delete-saved-chat.component.d.ts +22 -0
  17. package/chat/dialogs/i18n/en.json +19 -0
  18. package/chat/dialogs/i18n/fr.json +19 -0
  19. package/chat/dialogs/rename-saved-chat.component.d.ts +21 -0
  20. package/chat/dialogs/updates.component.d.ts +15 -0
  21. package/chat/documents-upload/document-list/document-list.component.d.ts +68 -77
  22. package/chat/documents-upload/document-overview/document-overview.component.d.ts +31 -41
  23. package/chat/documents-upload/document-upload/document-upload.component.d.ts +96 -98
  24. package/chat/documents-upload/documents-upload.model.d.ts +66 -66
  25. package/chat/documents-upload/documents-upload.service.d.ts +170 -174
  26. package/chat/documents-upload/i18n/en.json +24 -0
  27. package/chat/documents-upload/i18n/fr.json +24 -0
  28. package/chat/format-icon/format-icon.component.d.ts +10 -10
  29. package/chat/format-icon/icons.d.ts +5 -5
  30. package/chat/i18n/en.json +42 -0
  31. package/chat/i18n/fr.json +42 -0
  32. package/chat/index.d.ts +5 -5
  33. package/chat/initials-avatar/initials-avatar.component.d.ts +35 -35
  34. package/chat/instance-manager.service.d.ts +28 -28
  35. package/chat/pipes/message-content.pipe.d.ts +16 -0
  36. package/chat/prompt.component.d.ts +25 -21
  37. package/chat/public-api.d.ts +17 -17
  38. package/chat/references/i18n/en.json +6 -0
  39. package/chat/references/i18n/fr.json +6 -0
  40. package/chat/references/inline-image-reference.d.ts +21 -0
  41. package/chat/references/inline-page-reference.d.ts +21 -0
  42. package/chat/rest-chat.service.d.ts +31 -33
  43. package/chat/saved-chats/i18n/en.json +4 -0
  44. package/chat/saved-chats/i18n/fr.json +4 -0
  45. package/chat/saved-chats/saved-chats.component.d.ts +30 -36
  46. package/chat/services/app.service.d.ts +8 -0
  47. package/chat/services/dialog.service.d.ts +12 -0
  48. package/chat/services/notification.service.d.ts +10 -0
  49. package/chat/services/principal.service.d.ts +7 -0
  50. package/chat/services/search.service.d.ts +7 -0
  51. package/chat/services/signalR.web.service.d.ts +45 -0
  52. package/chat/services/ui.service.d.ts +13 -0
  53. package/chat/services/user-settings.service.d.ts +7 -0
  54. package/chat/token-progress-bar/i18n/en.json +4 -0
  55. package/chat/token-progress-bar/i18n/fr.json +4 -0
  56. package/chat/token-progress-bar/token-progress-bar.component.d.ts +24 -27
  57. package/chat/tooltip/tooltip.component.d.ts +12 -0
  58. package/chat/tooltip/tooltip.directive.d.ts +81 -0
  59. package/chat/types/message-content.types.d.ts +54 -0
  60. package/chat/types/message-reference.types.d.ts +11 -0
  61. package/chat/types.d.ts +913 -873
  62. package/chat/unified-plugins/embedded-image-reference.plugin.d.ts +3 -0
  63. package/chat/unified-plugins/embedded-page-reference.plugin.d.ts +3 -0
  64. package/chat/utils/assistant-json.d.ts +2 -0
  65. package/chat/websocket-chat.service.d.ts +102 -103
  66. package/esm2022/chat/charts/chart/chart.component.mjs +40 -0
  67. package/esm2022/chat/chat-message/chat-message.component.mjs +351 -0
  68. package/esm2022/chat/chat-reference/chat-reference.component.mjs +40 -0
  69. package/esm2022/chat/chat-settings-v3/chat-settings-v3.component.mjs +118 -0
  70. package/esm2022/chat/chat.component.mjs +1090 -0
  71. package/esm2022/chat/chat.service.mjs +417 -0
  72. package/esm2022/chat/debug-message/debug-message.component.mjs +43 -0
  73. package/esm2022/chat/dialogs/delete-saved-chat.component.mjs +81 -0
  74. package/esm2022/chat/dialogs/rename-saved-chat.component.mjs +84 -0
  75. package/esm2022/chat/dialogs/updates.component.mjs +61 -0
  76. package/esm2022/chat/documents-upload/document-list/document-list.component.mjs +140 -0
  77. package/esm2022/chat/documents-upload/document-overview/document-overview.component.mjs +65 -0
  78. package/esm2022/chat/documents-upload/document-upload/document-upload.component.mjs +256 -0
  79. package/{esm2020 → esm2022}/chat/documents-upload/documents-upload.model.mjs +1 -1
  80. package/esm2022/chat/documents-upload/documents-upload.service.mjs +291 -0
  81. package/{esm2020 → esm2022}/chat/format-icon/format-icon.component.mjs +23 -23
  82. package/{esm2020 → esm2022}/chat/format-icon/icons.mjs +137 -137
  83. package/{esm2020 → esm2022}/chat/initials-avatar/initials-avatar.component.mjs +60 -60
  84. package/esm2022/chat/instance-manager.service.mjs +46 -0
  85. package/esm2022/chat/pipes/message-content.pipe.mjs +34 -0
  86. package/esm2022/chat/prompt.component.mjs +88 -0
  87. package/{esm2020 → esm2022}/chat/public-api.mjs +18 -18
  88. package/esm2022/chat/references/inline-image-reference.mjs +110 -0
  89. package/esm2022/chat/references/inline-page-reference.mjs +110 -0
  90. package/esm2022/chat/rest-chat.service.mjs +296 -0
  91. package/esm2022/chat/saved-chats/saved-chats.component.mjs +82 -0
  92. package/esm2022/chat/services/app.service.mjs +19 -0
  93. package/esm2022/chat/services/dialog.service.mjs +40 -0
  94. package/esm2022/chat/services/notification.service.mjs +25 -0
  95. package/esm2022/chat/services/principal.service.mjs +16 -0
  96. package/esm2022/chat/services/search.service.mjs +13 -0
  97. package/esm2022/chat/services/signalR.web.service.mjs +79 -0
  98. package/esm2022/chat/services/ui.service.mjs +61 -0
  99. package/esm2022/chat/services/user-settings.service.mjs +22 -0
  100. package/{esm2020 → esm2022}/chat/sinequa-assistant-chat.mjs +4 -4
  101. package/esm2022/chat/token-progress-bar/token-progress-bar.component.mjs +52 -0
  102. package/esm2022/chat/tooltip/tooltip.component.mjs +44 -0
  103. package/esm2022/chat/tooltip/tooltip.directive.mjs +203 -0
  104. package/esm2022/chat/types/message-content.types.mjs +2 -0
  105. package/esm2022/chat/types/message-reference.types.mjs +2 -0
  106. package/esm2022/chat/types.mjs +130 -0
  107. package/esm2022/chat/unified-plugins/embedded-image-reference.plugin.mjs +57 -0
  108. package/esm2022/chat/unified-plugins/embedded-page-reference.plugin.mjs +57 -0
  109. package/esm2022/chat/utils/assistant-json.mjs +12 -0
  110. package/esm2022/chat/websocket-chat.service.mjs +654 -0
  111. package/{esm2020 → esm2022}/public-api.mjs +2 -2
  112. package/{esm2020 → esm2022}/sinequa-assistant.mjs +4 -4
  113. package/fesm2022/sinequa-assistant-chat.mjs +5340 -0
  114. package/fesm2022/sinequa-assistant-chat.mjs.map +1 -0
  115. package/{fesm2015 → fesm2022}/sinequa-assistant.mjs +3 -3
  116. package/index.d.ts +5 -5
  117. package/package.json +52 -25
  118. package/public-api.d.ts +1 -1
  119. package/chat/messages/de.d.ts +0 -4
  120. package/chat/messages/en.d.ts +0 -4
  121. package/chat/messages/fr.d.ts +0 -4
  122. package/chat/messages/index.d.ts +0 -4
  123. package/esm2020/chat/charts/chart/chart.component.mjs +0 -40
  124. package/esm2020/chat/chat-message/chat-message.component.mjs +0 -263
  125. package/esm2020/chat/chat-reference/chat-reference.component.mjs +0 -40
  126. package/esm2020/chat/chat-settings-v3/chat-settings-v3.component.mjs +0 -117
  127. package/esm2020/chat/chat.component.mjs +0 -1069
  128. package/esm2020/chat/chat.service.mjs +0 -333
  129. package/esm2020/chat/debug-message/debug-message.component.mjs +0 -43
  130. package/esm2020/chat/documents-upload/document-list/document-list.component.mjs +0 -191
  131. package/esm2020/chat/documents-upload/document-overview/document-overview.component.mjs +0 -80
  132. package/esm2020/chat/documents-upload/document-upload/document-upload.component.mjs +0 -258
  133. package/esm2020/chat/documents-upload/documents-upload.service.mjs +0 -289
  134. package/esm2020/chat/instance-manager.service.mjs +0 -46
  135. package/esm2020/chat/messages/de.mjs +0 -4
  136. package/esm2020/chat/messages/en.mjs +0 -4
  137. package/esm2020/chat/messages/fr.mjs +0 -4
  138. package/esm2020/chat/messages/index.mjs +0 -9
  139. package/esm2020/chat/prompt.component.mjs +0 -88
  140. package/esm2020/chat/rest-chat.service.mjs +0 -241
  141. package/esm2020/chat/saved-chats/saved-chats.component.mjs +0 -175
  142. package/esm2020/chat/token-progress-bar/token-progress-bar.component.mjs +0 -54
  143. package/esm2020/chat/types.mjs +0 -112
  144. package/esm2020/chat/websocket-chat.service.mjs +0 -641
  145. package/fesm2015/sinequa-assistant-chat.mjs +0 -4200
  146. package/fesm2015/sinequa-assistant-chat.mjs.map +0 -1
  147. package/fesm2020/sinequa-assistant-chat.mjs +0 -4171
  148. package/fesm2020/sinequa-assistant-chat.mjs.map +0 -1
  149. package/fesm2020/sinequa-assistant.mjs +0 -9
  150. package/fesm2020/sinequa-assistant.mjs.map +0 -1
  151. /package/{fesm2015 → fesm2022}/sinequa-assistant.mjs.map +0 -0
@@ -1,1069 +0,0 @@
1
- import { CommonModule } from "@angular/common";
2
- import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, Input, Output, ViewChild, inject } from "@angular/core";
3
- import { FormsModule } from "@angular/forms";
4
- import { HubConnectionState } from "@microsoft/signalr";
5
- import { BehaviorSubject, Subscription, combineLatest, filter, fromEvent, map, merge, switchMap, take, tap } from "rxjs";
6
- import { Action } from "@sinequa/components/action";
7
- import { AbstractFacet } from "@sinequa/components/facet";
8
- import { SearchService } from "@sinequa/components/search";
9
- import { UtilsModule } from "@sinequa/components/utils";
10
- import { AppService } from "@sinequa/core/app-utils";
11
- import { LoginService } from "@sinequa/core/login";
12
- import { NotificationsService } from "@sinequa/core/notification";
13
- import { PrincipalWebService } from "@sinequa/core/web-services";
14
- import { ChatMessageComponent } from "./chat-message/chat-message.component";
15
- import { ChatService } from "./chat.service";
16
- import { DebugMessageComponent } from "./debug-message/debug-message.component";
17
- import { InstanceManagerService } from "./instance-manager.service";
18
- import { RestChatService } from "./rest-chat.service";
19
- import { TokenProgressBarComponent } from "./token-progress-bar/token-progress-bar.component";
20
- import { WebSocketChatService } from "./websocket-chat.service";
21
- import * as i0 from "@angular/core";
22
- import * as i1 from "@angular/common";
23
- import * as i2 from "@angular/forms";
24
- import * as i3 from "@sinequa/components/utils";
25
- export class ChatComponent extends AbstractFacet {
26
- constructor() {
27
- super();
28
- this.loginService = inject(LoginService);
29
- this.websocketService = inject(WebSocketChatService);
30
- this.restService = inject(RestChatService);
31
- this.instanceManagerService = inject(InstanceManagerService);
32
- this.searchService = inject(SearchService);
33
- this.principalService = inject(PrincipalWebService);
34
- this.cdr = inject(ChangeDetectorRef);
35
- this.appService = inject(AppService);
36
- this.notificationsService = inject(NotificationsService);
37
- /** Define the query to use to fetch answers */
38
- this.query = this.searchService.query;
39
- /** Define the protocol to be used for this chat instance*/
40
- this.protocol = "WEBSOCKET";
41
- /** Map of listeners overriding default registered ones*/
42
- this.messageHandlers = new Map();
43
- /** When the assistant answer a user question, automatically scroll down to the bottom of the discussion */
44
- this.automaticScrollToLastResponse = false;
45
- /** When the assistant answer a user question, automatically focus to the chat input */
46
- this.focusAfterResponse = false;
47
- /** Icon to use for the assistant messages */
48
- this.assistantMessageIcon = 'sq-sinequa';
49
- /** Event emitter triggered once the signalR connection is established */
50
- this.connection = new EventEmitter();
51
- /** Event emitter triggered each time the assistant updates the current chat */
52
- /** Event emitter triggered when the chat is loading new content */
53
- this.loading$ = new EventEmitter(false);
54
- /** Emits the assistant configuration used when instantiating the component */
55
- this._config = new EventEmitter();
56
- this.data = new EventEmitter();
57
- /** Event emitter triggered when the user clicks to open the original document representing the context attachment*/
58
- this.openDocument = new EventEmitter();
59
- /** Event emitter triggered when the user clicks to open the preview of a document representing the context attachment */
60
- this.openPreview = new EventEmitter();
61
- /** Event emitter triggered when the user clicks on a suggested action */
62
- this.suggestAction = new EventEmitter();
63
- this.messages$ = new BehaviorSubject(undefined);
64
- this.isAdminOrDeletedAdmin = false;
65
- this.question = '';
66
- this._actions = [];
67
- this._resetChatAction = new Action({
68
- icon: 'fas fa-sync',
69
- title: "Reset assistant",
70
- action: () => this.newChat()
71
- });
72
- this._sub = new Subscription();
73
- this.changes$ = new BehaviorSubject(undefined);
74
- this.firstChangesHandled = false;
75
- this.isAtBottom = true;
76
- this.initializationError = false;
77
- this.enabledUserInput = false;
78
- this.isConnected = true; // By default, the chat is considered connected
79
- // Flag to track whether the 'reconnected' listener is already registered
80
- this._isReconnectedListenerRegistered = false;
81
- this.defaultIssueTypes = [
82
- 'User Interface bug',
83
- 'Incorrect or misleading response',
84
- 'Incomplete response',
85
- 'Technical issue',
86
- 'Privacy/data security issue',
87
- 'Other'
88
- ];
89
- this.issueType = '';
90
- this.reportType = 'dislike';
91
- this.showReport = false;
92
- this.showDebugMessages = false;
93
- this._reloadSubscription = undefined;
94
- this._actions.push(this._resetChatAction);
95
- }
96
- ngOnInit() {
97
- this._sub.add(this.loginService.events.pipe(filter(e => e.type === 'login-complete'), tap(_ => this.instantiateChatService()), map(_ => this.chatService.initChatConfig()), switchMap(() => this.chatService.initConfig$), filter(initConfig => !!initConfig), switchMap(_ => this.chatService.init()), switchMap(_ => this.chatService.initProcess$), filter(success => !!success), tap(_ => {
98
- if (this.chatService instanceof WebSocketChatService) {
99
- this.connection.emit(this.chatService.connection);
100
- }
101
- this.onLoadChat();
102
- }), tap(_ => this.chatService.overrideUser()), switchMap(_ => this.chatService.userOverride$), switchMap(_ => this.chatService.assistantConfig$), tap(config => {
103
- // Setup admin status
104
- this.isAdminOrDeletedAdmin = this.principalService.principal.isAdministrator || this.principalService.principal.isDelegatedAdmin || false;
105
- this.config = config;
106
- this.enabledUserInput = this.config.modeSettings.enabledUserInput;
107
- this.issueTypes = this.config.auditSettings?.issueTypes?.length ? this.config.auditSettings.issueTypes : undefined;
108
- this._config.emit(config);
109
- try {
110
- this.updateModelDescription();
111
- if (!this.firstChangesHandled) {
112
- this._previousQuery = JSON.parse(JSON.stringify(this.query)); // Initialize the previous query
113
- this._handleChanges();
114
- this._addScrollListener();
115
- this.firstChangesHandled = true;
116
- }
117
- }
118
- catch (error) {
119
- this.initializationError = true;
120
- throw error;
121
- }
122
- })).subscribe());
123
- this._sub.add(combineLatest([
124
- this.chatService.streaming$,
125
- this.chatService.stoppingGeneration$
126
- ]).pipe(map(([streaming, stoppingGeneration]) => !!(streaming || stoppingGeneration))).subscribe((result) => {
127
- this._resetChatAction.disabled = result;
128
- }));
129
- }
130
- ngOnChanges(changes) {
131
- this.changes$.next(changes);
132
- if (this.config) {
133
- this._handleChanges();
134
- }
135
- }
136
- ngOnDestroy() {
137
- this._sub.unsubscribe();
138
- this._dataSubscription?.unsubscribe();
139
- this._reloadSubscription?.unsubscribe();
140
- if (this.chatService instanceof WebSocketChatService) {
141
- this.chatService.stopConnection();
142
- }
143
- }
144
- get isAdmin() {
145
- return this.principalService.principal?.isAdministrator || false;
146
- }
147
- /**
148
- * Instantiate the chat service based on the provided @input protocol
149
- * This chat service instance will then be stored in the instanceManagerService with provided @input instanceId as a key
150
- */
151
- instantiateChatService() {
152
- switch (this.protocol) {
153
- case 'REST':
154
- this.chatService = this.restService;
155
- break;
156
- case 'WEBSOCKET':
157
- this.chatService = this.websocketService;
158
- break;
159
- default:
160
- throw new Error(`Could not found a ChatService implementation corresponding to the provided protocol: '${this.protocol}'`);
161
- }
162
- this.chatService.setChatInstanceId(this.instanceId);
163
- this.instanceManagerService.storeInstance(this.instanceId, this.chatService);
164
- }
165
- get actions() { return this._actions; }
166
- /**
167
- * Handles the changes in the chat component.
168
- * If the chat service is a WebSocketChatService, it handles the override of the message handlers if they exist.
169
- * Initializes the chat with the provided chat messages if they exist, otherwise loads the default chat.
170
- * If the chat is initialized, the initialization event is "Query", the query changes, and the queryChangeShouldTriggerReload function is provided,
171
- * then the chat should be reloaded if the function returns true. Otherwise, the chat should be reloaded by default.
172
- * It takes into account the ongoing streaming process and the ongoing stopping process to trigger that conditionally define the logic
173
- * of the reload :
174
- * - If the chat is streaming, then stop the generation and wait for the fetch to complete before reloading the chat.
175
- * - If the chat is stopping the generation, then wait for the fetch to complete before reloading the chat.
176
- */
177
- _handleChanges() {
178
- const changes = this.changes$.value;
179
- // If the chat service is a WebSocketChatService, handle the override of the message handlers if exists
180
- if (changes?.messageHandlers && this.messageHandlers && this.chatService instanceof WebSocketChatService) {
181
- this.chatService.overrideMessageHandlers(this.messageHandlers);
182
- }
183
- /**
184
- * Initialize the chat with the provided chat messages if exists, otherwise load the default chat
185
- * Once the chat is initialized (firstChangesHandled is true), allow opening the chat with the new provided messages (if exists)
186
- */
187
- if (!this.firstChangesHandled || changes?.chat) {
188
- const openChat = () => {
189
- if (this.messages$.value) {
190
- this.chatService.listSavedChat(); // Refresh the list of saved chats
191
- }
192
- this.openChat(this.chat.messages);
193
- };
194
- this.chatService.generateChatId();
195
- if (this.chat) {
196
- this.chatService.generateAuditEvent('new-chat', { 'configuration': JSON.stringify(this.chatService.assistantConfig$.value), 'chat-init': JSON.stringify(this.chat) });
197
- openChat();
198
- }
199
- else {
200
- this.chatService.generateAuditEvent('new-chat', { 'configuration': JSON.stringify(this.chatService.assistantConfig$.value) });
201
- this.loadDefaultChat();
202
- }
203
- }
204
- /**
205
- * If the chat is initialized, the initialization event is "Query", the query changes and the queryChangeShouldTriggerReload function is provided,
206
- * then the chat should be reloaded if the function returns true
207
- * Otherwise, the chat should be reloaded by default
208
- */
209
- if (this.firstChangesHandled && changes?.query && this.config.modeSettings.initialization.event === 'Query') {
210
- if (this.queryChangeShouldTriggerReload ? this.queryChangeShouldTriggerReload(this._previousQuery, this.query) : true) {
211
- if (!!this.chatService.stoppingGeneration$.value) {
212
- if (!this._reloadSubscription) {
213
- // Create a subscription to wait for both streaming$ and stoppingGeneration$ to be false
214
- this._reloadSubscription = combineLatest([
215
- this.chatService.streaming$,
216
- this.chatService.stoppingGeneration$
217
- ])
218
- .pipe(filter(([streaming, stopping]) => !streaming && !stopping), // Wait until both are false
219
- take(1) // Complete after the first match
220
- ).subscribe(() => {
221
- // Execute the reload after the query change
222
- this._triggerReloadAfterQueryChange();
223
- // Update _previousQuery with the current query
224
- this._previousQuery = JSON.parse(JSON.stringify(this.query));
225
- // Clean up subscription and reset its value
226
- this._reloadSubscription = undefined;
227
- });
228
- }
229
- }
230
- else if (!!this.chatService.streaming$.value) {
231
- if (!this._reloadSubscription) {
232
- this._reloadSubscription = this.chatService.stopGeneration()
233
- .subscribe({
234
- next: () => { },
235
- error: () => {
236
- // Clean up subscription and reset its value
237
- this._reloadSubscription?.unsubscribe();
238
- this._reloadSubscription = undefined;
239
- },
240
- complete: () => {
241
- // Wait for the ongoing fetch to complete, then trigger the reload
242
- this.chatService.streaming$.pipe(filter((streaming) => !streaming), take(1)).subscribe(() => {
243
- // Execute the reload after the query change
244
- this._triggerReloadAfterQueryChange();
245
- // Update _previousQuery with the current query
246
- this._previousQuery = JSON.parse(JSON.stringify(this.query));
247
- // Clean up subscription and reset its value
248
- this._reloadSubscription.unsubscribe();
249
- this._reloadSubscription = undefined;
250
- });
251
- }
252
- });
253
- }
254
- }
255
- else {
256
- // Execute the reload after the query change
257
- this._triggerReloadAfterQueryChange();
258
- // Update _previousQuery with the current query
259
- this._previousQuery = JSON.parse(JSON.stringify(this.query));
260
- }
261
- }
262
- else {
263
- // Update _previousQuery with the current query
264
- this._previousQuery = JSON.parse(JSON.stringify(this.query));
265
- }
266
- }
267
- }
268
- /**
269
- * Triggers a reload after the query change.
270
- * This method performs the necessary operations to reload the chat after a query change.
271
- * It sets the system and user messages, resets the savedChatId, generates a new chatId,
272
- * generates a new chat audit event, and handles the query mode.
273
- */
274
- _triggerReloadAfterQueryChange() {
275
- const systemMsg = { role: 'system', content: this.config.defaultValues.systemPrompt, additionalProperties: { display: false } };
276
- const userMsg = { role: 'user', content: ChatService.formatPrompt(this.config.defaultValues.userPrompt, { principal: this.principalService.principal }), additionalProperties: { display: this.config.modeSettings.displayUserPrompt } };
277
- this.chatService.setSavedChatId(undefined); // Reset the savedChatId
278
- this.chatService.generateChatId(); // Generate a new chatId
279
- this.chatService.generateAuditEvent('new-chat', { 'configuration': JSON.stringify(this.chatService.assistantConfig$.value) }); // Generate a new chat audit event
280
- this._handleQueryMode(systemMsg, userMsg);
281
- }
282
- /**
283
- * Adds a scroll listener to the message list element.
284
- * The listener is triggered when any of the following events occur:
285
- * - Loading state changes
286
- * - Messages change
287
- * - Streaming state changes
288
- * - Scroll event occurs on the message list element
289
- *
290
- * When the listener is triggered, it updates the `isAtBottom` property.
291
- */
292
- _addScrollListener() {
293
- this._sub.add(merge(this.loading$, this.messages$, this.chatService.streaming$, fromEvent(this.messageList.nativeElement, 'scroll')).subscribe(() => {
294
- this.isAtBottom = this._toggleScrollButtonVisibility();
295
- this.cdr.detectChanges();
296
- }));
297
- }
298
- /**
299
- * Get the model description based on the defaultValues service_id and model_id
300
- */
301
- updateModelDescription() {
302
- this.modelDescription = this.chatService.getModel(this.config.defaultValues.service_id, this.config.defaultValues.model_id);
303
- this.cdr.detectChanges();
304
- }
305
- /**
306
- * Submits a question from the user.
307
- * If the user is editing a previous message, removes all subsequent messages from the chat history.
308
- * Triggers the fetch of the answer for the submitted question by calling _fetchAnswer().
309
- * Clears the input value in the UI.
310
- * ⚠️ If the chat is streaming or stopping the generation, the operation is not allowed.
311
- */
312
- submitQuestion() {
313
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
314
- return;
315
- }
316
- if (this.question.trim() && this.messages$.value && this.chatService.chatHistory) {
317
- // When the user submits a question, if the user is editing a previous message, remove all subsequent messages from the chat history
318
- if (this.messageToEdit !== undefined) {
319
- // Update the messages in the UI
320
- this.messages$.next(this.messages$.value.slice(0, this.messageToEdit));
321
- // Update the raw messages in the chat history which is the clean version used to make the next request
322
- this.chatService.chatHistory = this.chatService.chatHistory.slice(0, this.remappedMessageToEdit);
323
- this.messageToEdit = undefined;
324
- this.remappedMessageToEdit = undefined;
325
- }
326
- // Remove the search warning message if exists
327
- if (this.chatService.chatHistory.at(-1)?.role === 'search-warning') {
328
- this.chatService.chatHistory.pop();
329
- }
330
- // Fetch the answer
331
- this._fetchAnswer(this.question.trim(), this.chatService.chatHistory);
332
- // Clear the input value in the UI
333
- this.questionInput.nativeElement.value = '';
334
- this.questionInput.nativeElement.style.height = `auto`;
335
- }
336
- }
337
- /**
338
- * Triggers the fetch of the answer for the given question and updates the conversation.
339
- * Generates an audit event for the user input.
340
- *
341
- * @param question - The question asked by the user.
342
- * @param conversation - The current conversation messages.
343
- */
344
- _fetchAnswer(question, conversation) {
345
- const userMsg = { role: 'user', content: question, additionalProperties: { display: true, isUserInput: true, additionalWorkflowProperties: this.config.additionalWorkflowProperties } };
346
- const messages = [...conversation, userMsg];
347
- this.messages$.next(messages);
348
- this.fetch(messages);
349
- this.chatService.generateAuditEvent('message', { ...this._defineMessageAuditDetails(userMsg, messages.length - 1), 'query': JSON.stringify(this.query), 'is-user-input': true, 'enabled-functions': this.config.defaultValues.functions?.filter(func => func.enabled).map(func => func.name), 'additional-workflow-properties': JSON.stringify(this.config.additionalWorkflowProperties) });
350
- }
351
- /**
352
- * Depending on the connection's state :
353
- * - If connected => given a list of messages, the chat endpoint is invoked for a continuation and updates the list of messages accordingly.
354
- * - If any other state => a connection error message is displayed in the chat.
355
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
356
- * @param messages The list of messages to invoke the chat endpoint with
357
- */
358
- fetch(messages) {
359
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
360
- return;
361
- }
362
- this._updateConnectionStatus();
363
- this.cdr.detectChanges();
364
- if (this.isConnected) {
365
- this.loading$.next(true);
366
- this._dataSubscription?.unsubscribe();
367
- this._dataSubscription = this.chatService.fetch(messages, this.query)
368
- .subscribe({
369
- next: res => this.updateData(res.history),
370
- error: () => {
371
- this._updateConnectionStatus();
372
- if (!this.isConnected) {
373
- const message = { role: 'connection-error', content: this.config.connectionSettings.connectionErrorMessage, additionalProperties: { display: true } };
374
- this.messages$.next([...messages, message]);
375
- }
376
- this.terminateFetch();
377
- },
378
- complete: () => {
379
- // Remove the last message if it's an empty message
380
- // This is due to the manner in which the chat service handles consecutive messages
381
- const lastMessage = this.messages$.value?.at(-1);
382
- if (this.isEmptyAssistantMessage(lastMessage)) {
383
- this.messages$.next(this.messages$.value?.slice(0, -1));
384
- }
385
- this.terminateFetch();
386
- }
387
- });
388
- }
389
- else {
390
- const message = { role: 'connection-error', content: this.config.connectionSettings.connectionErrorMessage, additionalProperties: { display: true } };
391
- this.messages$.next([...messages, message]);
392
- }
393
- if (this.automaticScrollToLastResponse) {
394
- this.scrollDown();
395
- }
396
- }
397
- /**
398
- * Retry to fetch the messages if the connection issues.
399
- * - If reconnecting => keep display the connection error message even when clicking on the "retry" button and increasing the number of retrial attempts, until the connection is re-established
400
- * - If disconnected => On click on the "retry" button, start the connection process while displaying the connection error message :
401
- * * If successful => given a list of messages, the chat endpoint is invoked for a continuation and updates the list of messages accordingly.
402
- * * If failed => increase the number of retrial attempts
403
- */
404
- retryFetch() {
405
- if (this.chatService instanceof WebSocketChatService) {
406
- // A one-time listener for reconnected event
407
- const onReconnectedHandler = () => {
408
- // Get the messages without the last one (the connection error message)
409
- const messages = this.messages$.value.slice(0, -1);
410
- // Find the last "user" message in the messages list
411
- let index = messages.length - 1;
412
- while (index >= 0 && messages[index].role !== 'user') {
413
- index--;
414
- }
415
- // If a user message is found (and it should always be the case), remove all subsequent messages from the chat history
416
- // Update the messages in the UI
417
- // and fetch the answer from the assistant
418
- if (index >= 0) {
419
- this.messages$.next(this.messages$.value.slice(0, index + 1));
420
- const remappedIndex = this._remapIndexInChatHistory(index);
421
- this.chatService.chatHistory = this.chatService.chatHistory.slice(0, remappedIndex + 1);
422
- this.fetch(this.chatService.chatHistory);
423
- }
424
- this.retrialAttempts = undefined; // Reset the number of retrial attempts
425
- /**
426
- * To remove the handler for onreconnected() after it's been registered,cannot directly use off() like you would for normal events registered with connection.on().
427
- * Instead, you need to explicitly remove or reset the handler by assigning it to null or an empty function
428
- */
429
- this.chatService.connection.onreconnected(() => { });
430
- // Reset the flag to ensure the handler is registered again when needed
431
- this._isReconnectedListenerRegistered = false;
432
- };
433
- // Depending on the connection's state, take the appropriate action
434
- switch (this.chatService.connection.state) {
435
- case HubConnectionState.Connected:
436
- // If the connection is re-established in the meantime, fetch the messages
437
- onReconnectedHandler();
438
- break;
439
- case HubConnectionState.Reconnecting:
440
- // Attach the reconnected listener if not already registered
441
- if (!this._isReconnectedListenerRegistered) {
442
- this.chatService.connection.onreconnected(onReconnectedHandler);
443
- this._isReconnectedListenerRegistered = true;
444
- }
445
- // Increase the number of retrial attempts
446
- this.retrialAttempts = this.retrialAttempts ? this.retrialAttempts + 1 : 1;
447
- break;
448
- case HubConnectionState.Disconnected:
449
- // Start the new connection
450
- this.chatService.startConnection()
451
- .then(() => onReconnectedHandler())
452
- .catch(() => {
453
- this.retrialAttempts = this.retrialAttempts ? this.retrialAttempts + 1 : 1;
454
- });
455
- break;
456
- default:
457
- break;
458
- }
459
- }
460
- }
461
- /**
462
- * Check if the signalR connection is connected.
463
- * For the REST protocol, the connection is always considered connected (for the moment).
464
- */
465
- _updateConnectionStatus() {
466
- this.isConnected = (this.chatService instanceof WebSocketChatService) ? this.chatService.connection.state === HubConnectionState.Connected : true;
467
- }
468
- /**
469
- * Update the UI with the new messages
470
- * @param messages
471
- */
472
- updateData(messages) {
473
- this.messages$.next(messages);
474
- this.data.emit(messages);
475
- this.loading$.next(false);
476
- this.question = '';
477
- if (this.automaticScrollToLastResponse) {
478
- this.scrollDown();
479
- }
480
- }
481
- /**
482
- * @returns true if the chat discussion is scrolled down to the bottom, false otherwise
483
- */
484
- _toggleScrollButtonVisibility() {
485
- if (this.messageList?.nativeElement) {
486
- return Math.round(this.messageList?.nativeElement.scrollHeight - this.messageList?.nativeElement.scrollTop - 1) <= this.messageList?.nativeElement.clientHeight;
487
- }
488
- return true;
489
- }
490
- /**
491
- * Scroll down to the bottom of the chat discussion
492
- */
493
- scrollDown() {
494
- setTimeout(() => {
495
- if (this.messageList?.nativeElement) {
496
- this.messageList.nativeElement.scrollTop = this.messageList.nativeElement.scrollHeight;
497
- this.cdr.detectChanges();
498
- }
499
- }, 10);
500
- }
501
- /**
502
- * Start a new chat with the defaultValues settings.
503
- * The savedChatId in the chat service will be reset, so that the upcoming saved chat operations will be performed on the fresh new chat.
504
- * If the savedChat feature is enabled, the list of saved chats will be refreshed.
505
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
506
- */
507
- newChat() {
508
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
509
- return;
510
- }
511
- this.chatService.setSavedChatId(undefined); // Reset the savedChatId
512
- this.chatService.generateChatId(); // Generate a new chatId
513
- this.chatService.listSavedChat(); // Refresh the list of saved chats
514
- this.chatService.generateAuditEvent('new-chat', { 'configuration': JSON.stringify(this.chatService.assistantConfig$.value) }); // Generate a new chat audit event
515
- this.loadDefaultChat(); // Start a new chat
516
- }
517
- /**
518
- * Attaches the specified document IDs to the assistant.
519
- * If no document IDs are provided, the operation is not allowed.
520
- * If the action for attaching a document is not defined at the application customization level, an error is logged.
521
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
522
- * @param ids - An array of document IDs to attach.
523
- */
524
- attachToChat(ids) {
525
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
526
- return;
527
- }
528
- if (!ids || ids?.length < 1) {
529
- return;
530
- }
531
- const attachDocAction = this.config.modeSettings.actions?.["attachDocAction"];
532
- if (!attachDocAction) {
533
- console.error(`No action is defined for attaching a document to the assistant "${this.instanceId}"`);
534
- return;
535
- }
536
- const userMsg = { role: 'user', content: '', additionalProperties: { display: false, isUserInput: false, type: "Action", forcedWorkflow: attachDocAction.forcedWorkflow, forcedWorkflowProperties: { ...(attachDocAction.forcedWorkflowProperties || {}), ids }, additionalWorkflowProperties: this.config.additionalWorkflowProperties } };
537
- // Remove the search warning message if exists
538
- if (this.chatService.chatHistory.at(-1)?.role === 'search-warning') {
539
- this.chatService.chatHistory.pop();
540
- }
541
- const messages = [...this.chatService.chatHistory, userMsg];
542
- this.messages$.next(messages);
543
- this.fetch(messages);
544
- this.chatService.generateAuditEvent('ActionRequested', {
545
- 'detail': 'attachDocAction',
546
- 'forced-workflow': 'SinequaAddSelectedDocumentsWorkflow',
547
- 'forced-workflow-properties': JSON.stringify(ids),
548
- });
549
- }
550
- /**
551
- * Start the default chat with the defaultValues settings
552
- * If the chat is meant to be initialized with event === "Query", the corresponding user query message will be added to the chat history
553
- */
554
- loadDefaultChat() {
555
- // Define the default system prompt and user prompt messages
556
- const systemMsg = { role: 'system', content: this.config.defaultValues.systemPrompt, additionalProperties: { display: false } };
557
- const userMsg = { role: 'user', content: ChatService.formatPrompt(this.config.defaultValues.userPrompt, { principal: this.principalService.principal }), additionalProperties: { display: this.config.modeSettings.displayUserPrompt } };
558
- if (this.config.modeSettings.initialization.event === 'Query') {
559
- this._handleQueryMode(systemMsg, userMsg);
560
- }
561
- else {
562
- this._handlePromptMode(systemMsg, userMsg);
563
- }
564
- }
565
- /**
566
- * Handles the prompt mode of the chat component.
567
- * If `sendUserPrompt` is true, it opens the chat with both system and user messages,
568
- * and generates audit events for both messages.
569
- * If `sendUserPrompt` is false, it opens the chat with only the system message,
570
- * and generates an audit event for the system message.
571
- *
572
- * @param systemMsg - The system message to be displayed in the chat.
573
- * @param userMsg - The user message to be displayed in the chat (optional).
574
- */
575
- _handlePromptMode(systemMsg, userMsg) {
576
- if (this.config.modeSettings.sendUserPrompt) {
577
- this.openChat([systemMsg, userMsg]);
578
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
579
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(userMsg, 1));
580
- }
581
- else {
582
- this.openChat([systemMsg]);
583
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
584
- }
585
- }
586
- /**
587
- * Handles the query mode by displaying the system message, user message, and user query message.
588
- * If the provided query text is not empty, then add the user query message to the chat history and invoke the assistant
589
- * Otherwise, just start a new chat with a warning message inviting the user to perform a full text search to retrieve some results
590
- * @param systemMsg - The system message to be displayed.
591
- * @param userMsg - The user message to be displayed.
592
- */
593
- _handleQueryMode(systemMsg, userMsg) {
594
- if (!!this.query.text) {
595
- const userQueryMsg = { role: 'user', content: this.query.text, additionalProperties: { display: this.config.modeSettings.initialization.displayUserQuery, query: this.query, forcedWorkflow: this.config.modeSettings.initialization.forcedWorkflow, forcedFunction: this.config.modeSettings.initialization.forcedFunction, isUserInput: true, additionalWorkflowProperties: this.config.additionalWorkflowProperties } };
596
- if (this.config.modeSettings.sendUserPrompt) {
597
- this.openChat([systemMsg, userMsg, userQueryMsg]);
598
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
599
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(userMsg, 1));
600
- this.chatService.generateAuditEvent('message', { ...this._defineMessageAuditDetails(userQueryMsg, 2), 'query': JSON.stringify(this.query), 'is-user-input': true, 'forced-workflow': this.config.modeSettings.initialization.forcedWorkflow, 'forced-function': this.config.modeSettings.initialization.forcedFunction, 'enabled-functions': this.config.defaultValues.functions?.filter(func => func.enabled).map(func => func.name), 'additional-workflow-properties': JSON.stringify(this.config.additionalWorkflowProperties) });
601
- }
602
- else {
603
- this.openChat([systemMsg, userQueryMsg]);
604
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(systemMsg, 0));
605
- this.chatService.generateAuditEvent('message', { ...this._defineMessageAuditDetails(userQueryMsg, 1), 'query': JSON.stringify(this.query), 'is-user-input': true, 'forced-workflow': this.config.modeSettings.initialization.forcedWorkflow, 'forced-function': this.config.modeSettings.initialization.forcedFunction, 'enabled-functions': this.config.defaultValues.functions?.filter(func => func.enabled).map(func => func.name), 'additional-workflow-properties': JSON.stringify(this.config.additionalWorkflowProperties) });
606
- }
607
- }
608
- else {
609
- const warningMsg = { role: 'search-warning', content: this.config.globalSettings.searchWarningMessage, additionalProperties: { display: true } };
610
- this.openChat([systemMsg, warningMsg]);
611
- this.chatService.generateAuditEvent('message', this._defineMessageAuditDetails(warningMsg, 0));
612
- }
613
- }
614
- _defineMessageAuditDetails(message, rank) {
615
- return {
616
- 'duration': 0,
617
- 'text': message.content,
618
- 'role': message.role,
619
- 'rank': rank
620
- };
621
- }
622
- /**
623
- * Start/open a new chat with the provided messages and chatId
624
- * If the last message is from the user, a request to the assistant is made to get an answer
625
- * If the last message is from the assistant, the conversation is loaded right away
626
- * @param messages The list of messages of the chat
627
- * @param savedChatId The id of the saved chat. If provided (ie. an existing discussion in the saved chat index), update the savedChatId in the chat service for the upcoming saved chat operations
628
- */
629
- openChat(messages, savedChatId) {
630
- if (!messages || !Array.isArray(messages)) {
631
- console.error('Error occurs while trying to load the discussion. Invalid messages received :', messages);
632
- return;
633
- }
634
- if (savedChatId) {
635
- this.chatService.setSavedChatId(savedChatId);
636
- this.chatService.generateChatId(savedChatId);
637
- }
638
- this.resetChat();
639
- this.messages$.next(messages);
640
- this.chatService.chatHistory = messages;
641
- const lastMessage = messages.at(-1);
642
- if (lastMessage && lastMessage.role === 'user') {
643
- this.fetch(messages); // If the last message if from a user, an answer from the assistant is expected
644
- }
645
- else {
646
- this.updateData(messages); // If the last message if from the assistant, we can load the conversation right away
647
- this.terminateFetch();
648
- }
649
- this._addScrollListener();
650
- }
651
- /**
652
- * Reset the chat by clearing the chat history and the UI accordingly
653
- * The user input will be cleared
654
- * The fetch subscription will be terminated
655
- */
656
- resetChat() {
657
- if (this.messages$.value) {
658
- this.messages$.next(undefined); // Reset chat
659
- }
660
- this.chatService.chatHistory = undefined; // Reset chat history
661
- this.question = '';
662
- this.terminateFetch();
663
- }
664
- /**
665
- * Fetch and Load the saved chat from the saved chat index.
666
- * If the saved chat is found, the chat discussion will be loaded with the provided messages and chatId
667
- */
668
- onLoadChat() {
669
- this.loading$.next(true);
670
- this._sub.add(this.chatService.loadSavedChat$
671
- .pipe(filter(savedChat => !!savedChat), switchMap(savedChat => this.chatService.getSavedChat(savedChat.id)), filter(savedChatHistory => !!savedChatHistory), tap(savedChatHistory => this.openChat(savedChatHistory.history, savedChatHistory.id))).subscribe());
672
- }
673
- /**
674
- * Stop the generation of the current assistant's answer.
675
- * The fetch subscription will be terminated.
676
- */
677
- stopGeneration() {
678
- this.chatService.stopGeneration().subscribe(() => this.terminateFetch());
679
- }
680
- /**
681
- * Terminate the fetch process by unsubscribing from the data subscription and updating the loading status to false.
682
- * Additionally, focus on the chat input if the focusAfterResponse flag is set to true.
683
- */
684
- terminateFetch() {
685
- this._dataSubscription?.unsubscribe();
686
- this._dataSubscription = undefined;
687
- this.loading$.next(false);
688
- this.cdr.detectChanges();
689
- if (this.focusAfterResponse) {
690
- setTimeout(() => {
691
- this.questionInput?.nativeElement.focus();
692
- });
693
- }
694
- }
695
- /**
696
- * Copy a previous user message of the chat history to the chat user input.
697
- * Thus, the user can edit and resubmit the message.
698
- * Once the edited message is submitted, all subsequent messages starting from @param index will be removed from the history and the UI will be updated accordingly.
699
- * The assistant will regenerate a new answer based on the updated chat history.
700
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
701
- * @param index The index of the user's message to edit
702
- */
703
- editMessage(index) {
704
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
705
- return;
706
- }
707
- this.messageToEdit = index;
708
- this.remappedMessageToEdit = this._remapIndexInChatHistory(index);
709
- this.question = this.chatService.chatHistory[this._remapIndexInChatHistory(index)].content;
710
- this.chatService.generateAuditEvent('edit.click', { 'rank': this._remapIndexInChatHistory(index) });
711
- }
712
- /**
713
- * Copy a previous assistant message of the chat history to the clipboard.
714
- * @param index The index of the assistant's message to edit
715
- */
716
- copyMessage(index) {
717
- // Remap the index in the chat history
718
- const idx = this._remapIndexInChatHistory(index);
719
- this.chatService.generateAuditEvent('copy.click', { 'rank': idx });
720
- }
721
- /**
722
- * Starting from the provided index, remove all subsequent messages from the chat history and the UI accordingly.
723
- * The assistant will regenerate a new answer based on the updated chat history.
724
- * ⚠️ If the assistant is streaming or stopping the generation, the operation is not allowed.
725
- * @param index The index of the assistant's message to regenerate
726
- */
727
- regenerateMessage(index) {
728
- if (!!this.chatService.streaming$.value || !!this.chatService.stoppingGeneration$.value) {
729
- return;
730
- }
731
- // Update the messages in the UI by removing all subsequent 'assistant' messages starting from the provided index until the first previous 'user' message
732
- let i = index;
733
- while (i >= 0 && (this.messages$.value)[i].role !== 'user') {
734
- i--;
735
- }
736
- // It should always be the case that i > 0
737
- if (i >= 0) {
738
- this.messages$.next(this.messages$.value.slice(0, i + 1));
739
- // Remap the index of this found first previous 'user' message in the chat history
740
- const idx = this._remapIndexInChatHistory(i);
741
- // Define and Update the chat history based on which the assistant will generate a new answer
742
- this.chatService.chatHistory = this.chatService.chatHistory.slice(0, idx + 1);
743
- // Fetch the answer
744
- this.fetch(this.chatService.chatHistory);
745
- this.chatService.generateAuditEvent('regenerate.click', { 'rank': idx });
746
- }
747
- }
748
- /**
749
- * Remaps the index in the chat history.
750
- * The chat history is a list of messages where some messages can be hidden (display set to false).
751
- * The index provided as input is the index of the message in the chat history displayed in the UI.
752
- * This function should be removed once the backend is updated to add the ids of the messages in the chat history
753
- * @param index - The index to be remapped.
754
- */
755
- _remapIndexInChatHistory(index) {
756
- // a copy of the chat history is created to avoid modifying the original chat history.
757
- // Additionally, a rank is giving to each message.
758
- // All messages having role 'user' are updated with the display property set to true, this is mandatory to get the correct rank of the message in the chat history when the user message to remap is hidden
759
- const history = this.chatService.chatHistory
760
- .slice()
761
- .map((message, idx) => ({ ...message, additionalProperties: { ...message.additionalProperties, rank: idx } }))
762
- .map((message) => {
763
- if (message.role === "user") {
764
- return { ...message, additionalProperties: { ...message.additionalProperties, display: true } };
765
- }
766
- return message;
767
- });
768
- // Count the number of hidden messages (of role different then "user") in messages$ before the provided index
769
- // All messages having role 'user' are updated with the display property set to true, this is mandatory to get the correct rank of the message in the chat history when the user message to remap is hidden
770
- // This is mandatory to get the correct rank of the message in the chat history
771
- // Since some hidden messages (like 'system' messages) are not displayed in the UI but have been counted in the provided index
772
- const numberOfHiddenMessagesInMessages$BeforeIndex = this.messages$.value
773
- .slice(0, index)
774
- .map((message) => {
775
- if (message.role === "user") {
776
- return { ...message, additionalProperties: { ...message.additionalProperties, display: true } };
777
- }
778
- return message;
779
- })
780
- .filter(message => !message.additionalProperties.display).length;
781
- // remove all messages that have display set to false
782
- // this is mandatory since at the point of time when the assistant answers a question,
783
- // it might have some hidden messages (for example contextMessages) that are available in the chat history but don't figure in messages$ unless a new question is asked
784
- const filteredHistory = history.filter(message => message.additionalProperties.display);
785
- // return the index of the message in the filtered history
786
- return filteredHistory[index - numberOfHiddenMessagesInMessages$BeforeIndex].additionalProperties.rank;
787
- }
788
- /**
789
- * Handles the key up event for 'Backspace' and 'Enter' keys.
790
- * @param event - The keyboard event.
791
- */
792
- onKeyUp(event) {
793
- switch (event.key) {
794
- case 'Backspace':
795
- this.calculateHeight();
796
- break;
797
- case 'Enter':
798
- if (!event.shiftKey) {
799
- event.preventDefault();
800
- this.submitQuestion();
801
- }
802
- this.calculateHeight();
803
- break;
804
- default:
805
- break;
806
- }
807
- }
808
- /**
809
- * Calculates and adjusts the height of the question input element based on its content.
810
- * If the Enter key is pressed without the Shift key, it prevents the default behavior.
811
- * @param event The keyboard event
812
- */
813
- calculateHeight(event) {
814
- if (event?.key === 'Enter' && !event.shiftKey) {
815
- event?.preventDefault();
816
- }
817
- const maxHeight = 170;
818
- const el = this.questionInput.nativeElement;
819
- el.style.maxHeight = `${maxHeight}px`;
820
- el.style.height = 'auto';
821
- el.style.height = `${el.scrollHeight}px`;
822
- el.style.overflowY = el.scrollHeight >= maxHeight ? 'scroll' : 'hidden';
823
- }
824
- /**
825
- * Send a "like" event on clicking on the thumb-up icon of an assistant's message
826
- * @param message The assistant message to like
827
- * @param rank The rank of the message to like
828
- */
829
- onLike(message, rank) {
830
- // Remap the index in the chat history
831
- const idx = this._remapIndexInChatHistory(rank);
832
- this.chatService.generateAuditEvent('thumb-up.click', { rank: idx });
833
- this.reportType = 'like';
834
- this.messageToReport = message;
835
- this.reportComment = undefined;
836
- this.reportRank = rank;
837
- this.showReport = true;
838
- this.chatService.chatHistory[this._remapIndexInChatHistory(rank)].additionalProperties.$liked = true;
839
- this._updateChatHistory();
840
- }
841
- /**
842
- * Send a "dislike" event on clicking on the thumb-down icon of an assistant's message.
843
- * It also opens the issue reporting dialog.
844
- * @param message The assistant message to dislike
845
- * @param index The rank of the message to dislike
846
- */
847
- onDislike(message, rank) {
848
- // Remap the index in the chat history
849
- const idx = this._remapIndexInChatHistory(rank);
850
- this.chatService.generateAuditEvent('thumb-down.click', { rank: idx });
851
- this.reportType = 'dislike';
852
- this.messageToReport = message;
853
- this.issueType = '';
854
- this.reportComment = undefined;
855
- this.reportRank = rank;
856
- this.showReport = true;
857
- this.chatService.chatHistory[this._remapIndexInChatHistory(rank)].additionalProperties.$disliked = true;
858
- this._updateChatHistory();
859
- }
860
- _updateChatHistory() {
861
- this.messages$.next(this.chatService.chatHistory);
862
- if (this.chatService.savedChatId) {
863
- this.chatService.updateSavedChat(this.chatService.savedChatId, undefined, this.chatService.chatHistory).subscribe();
864
- }
865
- }
866
- /**
867
- * Report an issue related to the assistant's message.
868
- */
869
- sendReport() {
870
- const details = {
871
- 'comment': this.reportComment,
872
- 'text': this.messageToReport.content,
873
- 'rank': this.reportRank,
874
- };
875
- if (this.reportType === 'dislike') {
876
- details['report-type'] = this.issueType;
877
- this.chatService.generateAuditEvent('negative-report.send', details);
878
- }
879
- else {
880
- this.chatService.generateAuditEvent('positive-report.send', details);
881
- }
882
- this.notificationsService.success('Your report has been successfully sent');
883
- this.showReport = false;
884
- }
885
- /**
886
- * Close the reporting dialog.
887
- */
888
- ignoreReport() {
889
- this.showReport = false;
890
- }
891
- /**
892
- * Handle the click on a reference's 'open preview'.
893
- * @param data
894
- */
895
- openAttachmentPreview(data) {
896
- this.openPreview.emit(data.reference);
897
- const details = {
898
- 'doc-id': data.reference.recordId,
899
- 'title': data.reference.record.title,
900
- 'source': data.reference.record.treepath,
901
- 'collection': data.reference.record.collection,
902
- 'index': data.reference.record.databasealias,
903
- };
904
- if (!!data.partId)
905
- details['part-id'] = data.partId;
906
- this.chatService.generateAuditEvent('attachment.preview.click', details);
907
- }
908
- /**
909
- * Handle the click on a reference's 'open original document'.
910
- * @param data
911
- */
912
- openOriginalAttachment(data) {
913
- this.openDocument.emit(data.reference.record);
914
- const details = {
915
- 'doc-id': data.reference.recordId,
916
- 'title': data.reference.record.title,
917
- 'source': data.reference.record.treepath,
918
- 'collection': data.reference.record.collection,
919
- 'index': data.reference.record.databasealias,
920
- };
921
- if (!!data.partId)
922
- details['part-id'] = data.partId;
923
- this.chatService.generateAuditEvent('attachment.link.click', details);
924
- }
925
- /**
926
- * Handle the click on a suggested action.
927
- * @param action Suggested action.
928
- * @param index Rank of the message in the chatHistory related to the suggested action.
929
- */
930
- suggestActionClick(action, index) {
931
- this.suggestAction.emit(action);
932
- this.chatService.generateAuditEvent('suggestedAction.click', { 'text': action.content, 'suggestedAction-type': action.type });
933
- }
934
- /**
935
- * It looks for the debug messages available in the current group of "assistant" messages.
936
- * By design, the debug messages are only available in the first visible message among the group "assistant" messages.
937
- * @param index The rank of the message
938
- * @returns The debug messages available in the current group of "assistant" messages
939
- */
940
- getDebugMessages(index) {
941
- // If it is not an assistant message, return
942
- if ((this.messages$.value)[index].role !== 'assistant') {
943
- return [];
944
- }
945
- // Get the array of messages up to the indicated index
946
- const array = this.messages$.value.slice(0, index + 1);
947
- // If it is an assistant message, look for the debug messages available in the current group of "assistant" messages
948
- // By design, the debug messages are only available in the first visible message among the group "assistant" messages.
949
- const idx = this.chatService.firstVisibleAssistantMessageIndex(array);
950
- if (idx > -1) {
951
- return (this.messages$.value)[idx].additionalProperties.$debug || [];
952
- }
953
- return [];
954
- }
955
- /**
956
- * Handle the click on the 'show log info' button of a message.
957
- * @param index The rank of the message
958
- */
959
- showDebug(index) {
960
- this.debugMessages = this.getDebugMessages(index);
961
- this.showDebugMessages = true;
962
- this.cdr.detectChanges();
963
- }
964
- /**
965
- * Verify whether the current message is an assistant message and that all following messages are assistant ones
966
- * Used to keep the "View progress" opened even though the assistant is sending additional messages after the current one
967
- * @param messages the list of current messages
968
- * @param index the index of the current message
969
- * @returns if this messages and the following ones (if any) are the last ones
970
- */
971
- isAssistantLastMessages(messages, index) {
972
- for (let i = index; i < messages.length; i++) {
973
- if (messages[i].role !== 'assistant')
974
- return false;
975
- }
976
- return true;
977
- }
978
- /**
979
- * Checks if the given message is an empty assistant message.
980
- * An empty assistant message is defined as a message with the role 'assistant',
981
- * an empty content, and no additional properties such as attachments, progress,
982
- * debug information, or suggested actions.
983
- *
984
- * @param message - The message to check.
985
- * @returns `true` if the message is an empty assistant message, `false` otherwise.
986
- */
987
- isEmptyAssistantMessage(message) {
988
- if (message?.role === 'assistant'
989
- && message?.content === ""
990
- && !message?.additionalProperties?.$attachment
991
- && !message?.additionalProperties?.$progress
992
- && !message?.additionalProperties?.$debug
993
- && !message?.additionalProperties?.$suggestedAction) {
994
- return true;
995
- }
996
- return false;
997
- }
998
- }
999
- ChatComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1000
- ChatComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: ChatComponent, isStandalone: true, selector: "sq-chat-v3", inputs: { instanceId: "instanceId", query: "query", queryChangeShouldTriggerReload: "queryChangeShouldTriggerReload", protocol: "protocol", messageHandlers: "messageHandlers", automaticScrollToLastResponse: "automaticScrollToLastResponse", focusAfterResponse: "focusAfterResponse", chat: "chat", assistantMessageIcon: "assistantMessageIcon", userMessageIcon: "userMessageIcon", connectionErrorMessageIcon: "connectionErrorMessageIcon", searchWarningMessageIcon: "searchWarningMessageIcon" }, outputs: { connection: "connection", loading$: "loading", _config: "config", data: "data", openDocument: "openDocument", openPreview: "openPreview", suggestAction: "suggestAction" }, providers: [
1001
- RestChatService,
1002
- WebSocketChatService
1003
- ], queries: [{ propertyName: "loadingTpl", first: true, predicate: ["loadingTpl"], descendants: true }, { propertyName: "reportTpl", first: true, predicate: ["reportTpl"], descendants: true }, { propertyName: "tokenConsumptionTpl", first: true, predicate: ["tokenConsumptionTpl"], descendants: true }, { propertyName: "debugMessagesTpl", first: true, predicate: ["debugMessagesTpl"], descendants: true }], viewQueries: [{ propertyName: "messageList", first: true, predicate: ["messageList"], descendants: true }, { propertyName: "questionInput", first: true, predicate: ["questionInput"], descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"!initializationError\">\n <div *ngIf=\"messages$ | async as messages; else loadingTpl || loadingTplDefault\" class=\"h-100 d-flex flex-column\">\n <!-- Token consumption -->\n <div class=\"ms-1\" *ngIf=\"config?.globalSettings?.displayUserQuotaConsumption || config?.globalSettings?.displayChatTokensConsumption\">\n <ng-container *ngTemplateOutlet=\"tokenConsumptionTpl || defaultTokenConsumptionTpl; context: { $implicit: instanceId }\"></ng-container>\n </div>\n\n <!-- Chat Messages -->\n <ul class=\"list-group list-group-flush overflow-auto flex-grow-1 pe-2 pb-2\" #messageList>\n <ng-container *ngFor=\"let message of messages; let index = index; let last = last\">\n <!-- Regular messages -->\n <li class=\"list-group-item\"\n *ngIf=\"message.additionalProperties.display && !isEmptyAssistantMessage(message)\"\n [style.--bs-list-group-item-padding-y.rem]=\"'0.6'\"\n [class.opacity-50]=\"messageToEdit && (messageToEdit < (index + 1))\">\n <sq-chat-message\n [class.sq-user-message]=\"message.role === 'user'\"\n [class.last-message]=\"last\"\n [message]=\"message\"\n [conversation]=\"messages\"\n [suggestedActions]=\"last ? message.additionalProperties.$suggestedAction : undefined\"\n [assistantMessageIcon]=\"assistantMessageIcon\"\n [userMessageIcon]=\"userMessageIcon\"\n [connectionErrorMessageIcon]=\"connectionErrorMessageIcon\"\n [searchWarningMessageIcon]=\"searchWarningMessageIcon\"\n [streaming]=\"(chatService.streaming$ | async) && (last || isAssistantLastMessages(messages, index))\"\n [canEdit]=\"(chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'user'\"\n [canCopy]=\"((chatService.streaming$ | async) === false || !last) && messageToEdit === undefined && message.role !== 'connection-error' && message.role !== 'search-warning'\"\n [canLike]=\"((chatService.streaming$ | async) === false || !last) && message.role === 'assistant'\"\n [canDislike]=\"((chatService.streaming$ | async) === false || !last) && message.role === 'assistant'\"\n [canDebug]=\"(((chatService.streaming$ | async) === false && last) || (!last && messages[index+1].role !== 'assistant')) && message.role === 'assistant' && (getDebugMessages(index).length > 0) && ((isAdminOrDeletedAdmin || (chatService.userOverride$ | async)) && config?.defaultValues.debug)\"\n [canRegenerate]=\"(chatService.streaming$ | async) === false && (last || (!last && messages[index+1].role !== 'assistant')) && message.role === 'assistant' && messageToEdit === undefined\"\n (edit)=\"editMessage(index)\"\n (copy)=\"copyMessage(index)\"\n (regenerate)=\"regenerateMessage(index)\"\n (openDocument)=\"openOriginalAttachment($event)\"\n (openPreview)=\"openAttachmentPreview($event)\"\n (suggestAction)=\"suggestActionClick($event, index)\"\n (like)=\"onLike(message, index)\"\n (dislike)=\"onDislike(message, index)\"\n (debug)=\"showDebug(index)\">\n </sq-chat-message>\n </li>\n </ng-container>\n <!-- Loading spinner -->\n <li class=\"list-group-item\" *ngIf=\"(loading$ | async) === true\">\n <ng-container *ngTemplateOutlet=\"loadingTpl || loadingTplDefault\"></ng-container>\n </li>\n </ul>\n\n <!-- Reporting a feedback form -->\n <div class=\"issue-report pt-3 pb-2\" *ngIf=\"showReport\">\n <ng-container *ngTemplateOutlet=\"reportTpl || reportTplDefault; context: { $implicit: messageToReport, rank: reportRank, type: reportType }\"></ng-container>\n </div>\n\n <!-- User text input -->\n <div class=\"user-input mt-auto\" *ngIf=\"!showReport\">\n <div class=\"py-2\">\n <div [hidden]=\"!isConnected\">\n <ng-container *ngIf=\"enabledUserInput\" [ngTemplateOutlet]=\"inputTpl\"></ng-container>\n </div>\n <!-- Retry button -->\n <button [hidden]=\"isConnected\" class=\"btn mb-4 ast-error ast-btn sq-retry\" (click)=\"retryFetch()\">\n <span>Try again</span>\n <span *ngIf=\"retrialAttempts\" class=\"ms-2 attempts\">{{ retrialAttempts }}</span>\n </button>\n <div class=\"text-end small text-muted px-3\" *ngIf=\"!!config?.globalSettings?.disclaimer\">\n {{ config?.globalSettings?.disclaimer }}\n </div>\n </div>\n </div>\n\n <!-- Floating scroll button -->\n <div *ngIf=\"!isAtBottom && !showReport\" class=\"sq-floating-scroll\" [ngClass]=\"enabledUserInput ? 'sq-floating-scroll--when-user-input' : 'sq-floating-scroll--without-user-input'\">\n <button class=\"btn shadow\" (click)=\"scrollDown()\">\n <i class=\"fas fa-angle-double-down\"></i>\n </button>\n </div>\n </div>\n</ng-container>\n\n<!-- NG TEMPLATES-->\n\n<ng-template #loadingTplDefault>\n <div class=\"spinner-grow text-primary d-block mx-auto my-5\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n</ng-template>\n\n<ng-template #inputTpl>\n <div class=\"px-3 py-1\">\n <div class=\"ast-input-container\">\n <button disabled class=\"btn btn-light\">\n <i class=\"fas fa-search\"></i>\n </button>\n <textarea #questionInput rows=\"1\"\n type=\"text\" class=\"form-control\"\n placeholder=\"Ask something\" autofocus\n [(ngModel)]=\"question\"\n (keyup)=\"onKeyUp($event)\"\n (keydown)=\"calculateHeight($event)\"\n [disabled]=\"(loading$ | async) || (chatService.streaming$ | async) || (chatService.stoppingGeneration$ | async)\">\n </textarea>\n <button\n *ngIf=\"!(chatService.streaming$ | async) && !(loading$ | async) && !(chatService.stoppingGeneration$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Send message\"\n (click)=\"submitQuestion()\">\n <i class=\"fas fa-paper-plane\"></i>\n </button>\n <button\n *ngIf=\"messageToEdit\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Cancel edition\"\n (click)=\"messageToEdit = undefined; question = ''\">\n <i class=\"fas fa-undo-alt\"></i>\n </button>\n <span *ngIf=\"(chatService.streaming$ | async) && !(chatService.stoppingGeneration$ | async)\" class=\"processing ms-2\">\n Generating <i class=\"ms-1 fas fa-spinner fa-pulse\"></i>\n </span>\n <span *ngIf=\"(chatService.stoppingGeneration$ | async)\" class=\"processing ms-2\">\n Stopping <i class=\"ms-1 fas fa-spinner fa-pulse\"></i>\n </span>\n <button\n *ngIf=\"(chatService.streaming$ | async) && !(chatService.stoppingGeneration$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Stop generating\"\n (click)=\"stopGeneration()\">\n <i class=\"fas fa-stop\"></i>\n </button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #reportTplDefault let-message let-rank=\"rank\" let-type=\"type\">\n <div class=\"px-3\">\n <ng-container *ngIf=\"type === 'dislike'\">\n <h5>Issue type</h5>\n <select class=\"form-select mb-4\" [(ngModel)]=\"issueType\">\n <option [value]=\"''\">Choose an issue type</option>\n <option *ngFor=\"let type of (issueTypes ?? defaultIssueTypes)\">{{type}}</option>\n </select>\n <h5>What was unsatisfying about this response? (optional)</h5>\n </ng-container>\n <ng-container *ngIf=\"type === 'like'\">\n <h5>Why did you like this answer? (optional)</h5>\n </ng-container>\n <textarea class=\"form-control\" [(ngModel)]=\"reportComment\" placeholder=\"Write your comment\"></textarea>\n <div class=\"d-flex flex-row-reverse gap-1 mt-2\">\n <button class=\"btn btn-primary\" [disabled]=\"type === 'dislike' && !issueType\" (click)=\"sendReport()\">Send</button>\n <button class=\"btn btn-light\" (click)=\"ignoreReport()\">Do not send report</button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #defaultTokenConsumptionTpl let-instanceId>\n <sq-token-progress-bar\n [instanceId]=\"instanceId\">\n </sq-token-progress-bar>\n</ng-template>\n\n<div class=\"debug-messages\" [class.displayed]=\"showDebugMessages\">\n <button *ngIf=\"showDebugMessages\" class=\"btn btn-light shadow back-btn\" (click)=\"showDebugMessages=false\">\n <i class=\"fas fa-chevron-right\"></i>\n </button>\n <ng-container *ngTemplateOutlet=\"debugMessagesTpl || defaultDebugMessagesTpl; context: { $implicit: debugMessages }\">\n </ng-container>\n</div>\n\n<ng-template #defaultDebugMessagesTpl let-debugMessages>\n <sq-debug-message [data]=\"debugMessages\"></sq-debug-message>\n</ng-template>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-uploaded-doc-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference,:host ::ng-deep .attachment .reference{position:relative;bottom:var(--ast-reference-bottom, .3em);font-weight:var(--ast-reference-font-weight, bold);padding:var(--ast-reference-padding, 0 .2em);margin:var(--ast-reference-margin, 0 .1em);border-radius:var(--ast-reference-border-radius, .2em);background-color:var(--ast-reference-background-color, lightblue);color:var(--ast-reference-color, black)}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference{font-size:var(--ast-reference-message-font-size, .7em)}:host ::ng-deep .attachment .reference{font-size:var(--ast-reference-attachment-font-size, 13px)}:host{font-size:.875rem}:host>div>.user-input>div:not(.progress),:host>div>.issue-report>div,:host>div>ul>li{width:var(--ast-chat-container-width, 100%);max-width:100%;margin-left:auto;margin-right:auto}:host>div>ul{padding-top:var(--ast-chat-padding-top, 0);padding-bottom:var(--ast-chat-padding-bottom, 0)}li.attachment>p{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;-webkit-line-clamp:3}li.attachment.expanded>p{display:block}.progress{--bs-progress-height: 3px}.progress.disabled{--bs-progress-height: 20px;--bs-progress-bar-bg: var(--bs-danger)}.user-input{z-index:1}.user-input ul.list-group{max-height:30vh}.form-control:disabled{background-color:#ededed}a.disabled{cursor:default;opacity:.5}.no-max-height{max-height:initial!important}.sq-floating-scroll{position:absolute;right:50%;text-align:center}.sq-floating-scroll--when-user-input{bottom:75px}.sq-floating-scroll--without-user-input{bottom:15px}.sq-floating-scroll .btn{background-color:#fff}.sq-floating-scroll .btn:hover{background-color:#fff;opacity:.9}.ast-input-container{display:flex;align-items:center;background-color:var(--ast-input-bg, #F8F8F8);border-radius:var(--ast-size-3, .75rem)}.ast-input-container>i{padding-left:var(--ast-size-3, .75rem);color:var(--ast-muted-color, rgba(33, 37, 41, .75))}.ast-input-container textarea{padding-left:var(--ast-size-3, .75rem);padding-right:var(--ast-size-3, .75rem);resize:none}.ast-input-container textarea,.ast-input-container button,.ast-input-container button:hover{background-color:transparent;border:0}.ast-input-container button:hover{color:var(--ast-primary-color, #005DA7)}.ast-input-container button:not(:hover){color:var(--ast-muted-color, rgba(33, 37, 41, .75))}.ast-input-container .processing{display:flex;align-items:center;color:var(--ast-secondary-color, #FF732E)}sq-chat-message.sq-user-message{float:var(--ast-user-message-float, none)}sq-token-progress-bar{z-index:10;position:absolute;top:0;right:0}.debug-messages{position:fixed;z-index:999999;right:-60%;top:0;width:60%;height:100%;transition:all .5s ease;background-color:var(--bs-body-bg);overflow:auto}.debug-messages .back-btn{position:fixed;right:0%;transition:all .5s ease}.debug-messages.displayed{right:0}.debug-messages.displayed .back-btn{right:60%}.debug-messages sq-debug-message:first-of-type{display:block;width:100%}.sq-retry{display:flex;margin:auto;font-weight:var(--font-weight-bold, 500)}.sq-retry .attempts{display:flex;border-radius:100%;background:#fff;height:20px;width:20px;place-content:center;align-items:center}.issue-report{background-color:var(--ast-report-bg, white)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ChatMessageComponent, selector: "sq-chat-message", inputs: ["message", "conversation", "suggestedActions", "assistantMessageIcon", "userMessageIcon", "connectionErrorMessageIcon", "searchWarningMessageIcon", "streaming", "canEdit", "canRegenerate", "canCopy", "canDebug", "canLike", "canDislike"], outputs: ["openDocument", "openPreview", "suggestAction", "edit", "copy", "regenerate", "like", "dislike", "debug"] }, { kind: "component", type: TokenProgressBarComponent, selector: "sq-token-progress-bar", inputs: ["instanceId"] }, { kind: "component", type: DebugMessageComponent, selector: "sq-debug-message", inputs: ["data", "level", "parentColor"] }, { kind: "ngmodule", type: UtilsModule }, { kind: "directive", type: i3.TooltipDirective, selector: "[sqTooltip]", inputs: ["sqTooltip", "sqTooltipData", "sqTooltipTemplate", "placement", "fallbackPlacements", "delay", "hoverableTooltip", "tooltipClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1004
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ChatComponent, decorators: [{
1005
- type: Component,
1006
- args: [{ selector: 'sq-chat-v3', providers: [
1007
- RestChatService,
1008
- WebSocketChatService
1009
- ], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, FormsModule, ChatMessageComponent, TokenProgressBarComponent, DebugMessageComponent, UtilsModule], template: "<ng-container *ngIf=\"!initializationError\">\n <div *ngIf=\"messages$ | async as messages; else loadingTpl || loadingTplDefault\" class=\"h-100 d-flex flex-column\">\n <!-- Token consumption -->\n <div class=\"ms-1\" *ngIf=\"config?.globalSettings?.displayUserQuotaConsumption || config?.globalSettings?.displayChatTokensConsumption\">\n <ng-container *ngTemplateOutlet=\"tokenConsumptionTpl || defaultTokenConsumptionTpl; context: { $implicit: instanceId }\"></ng-container>\n </div>\n\n <!-- Chat Messages -->\n <ul class=\"list-group list-group-flush overflow-auto flex-grow-1 pe-2 pb-2\" #messageList>\n <ng-container *ngFor=\"let message of messages; let index = index; let last = last\">\n <!-- Regular messages -->\n <li class=\"list-group-item\"\n *ngIf=\"message.additionalProperties.display && !isEmptyAssistantMessage(message)\"\n [style.--bs-list-group-item-padding-y.rem]=\"'0.6'\"\n [class.opacity-50]=\"messageToEdit && (messageToEdit < (index + 1))\">\n <sq-chat-message\n [class.sq-user-message]=\"message.role === 'user'\"\n [class.last-message]=\"last\"\n [message]=\"message\"\n [conversation]=\"messages\"\n [suggestedActions]=\"last ? message.additionalProperties.$suggestedAction : undefined\"\n [assistantMessageIcon]=\"assistantMessageIcon\"\n [userMessageIcon]=\"userMessageIcon\"\n [connectionErrorMessageIcon]=\"connectionErrorMessageIcon\"\n [searchWarningMessageIcon]=\"searchWarningMessageIcon\"\n [streaming]=\"(chatService.streaming$ | async) && (last || isAssistantLastMessages(messages, index))\"\n [canEdit]=\"(chatService.streaming$ | async) === false && messageToEdit === undefined && message.role === 'user'\"\n [canCopy]=\"((chatService.streaming$ | async) === false || !last) && messageToEdit === undefined && message.role !== 'connection-error' && message.role !== 'search-warning'\"\n [canLike]=\"((chatService.streaming$ | async) === false || !last) && message.role === 'assistant'\"\n [canDislike]=\"((chatService.streaming$ | async) === false || !last) && message.role === 'assistant'\"\n [canDebug]=\"(((chatService.streaming$ | async) === false && last) || (!last && messages[index+1].role !== 'assistant')) && message.role === 'assistant' && (getDebugMessages(index).length > 0) && ((isAdminOrDeletedAdmin || (chatService.userOverride$ | async)) && config?.defaultValues.debug)\"\n [canRegenerate]=\"(chatService.streaming$ | async) === false && (last || (!last && messages[index+1].role !== 'assistant')) && message.role === 'assistant' && messageToEdit === undefined\"\n (edit)=\"editMessage(index)\"\n (copy)=\"copyMessage(index)\"\n (regenerate)=\"regenerateMessage(index)\"\n (openDocument)=\"openOriginalAttachment($event)\"\n (openPreview)=\"openAttachmentPreview($event)\"\n (suggestAction)=\"suggestActionClick($event, index)\"\n (like)=\"onLike(message, index)\"\n (dislike)=\"onDislike(message, index)\"\n (debug)=\"showDebug(index)\">\n </sq-chat-message>\n </li>\n </ng-container>\n <!-- Loading spinner -->\n <li class=\"list-group-item\" *ngIf=\"(loading$ | async) === true\">\n <ng-container *ngTemplateOutlet=\"loadingTpl || loadingTplDefault\"></ng-container>\n </li>\n </ul>\n\n <!-- Reporting a feedback form -->\n <div class=\"issue-report pt-3 pb-2\" *ngIf=\"showReport\">\n <ng-container *ngTemplateOutlet=\"reportTpl || reportTplDefault; context: { $implicit: messageToReport, rank: reportRank, type: reportType }\"></ng-container>\n </div>\n\n <!-- User text input -->\n <div class=\"user-input mt-auto\" *ngIf=\"!showReport\">\n <div class=\"py-2\">\n <div [hidden]=\"!isConnected\">\n <ng-container *ngIf=\"enabledUserInput\" [ngTemplateOutlet]=\"inputTpl\"></ng-container>\n </div>\n <!-- Retry button -->\n <button [hidden]=\"isConnected\" class=\"btn mb-4 ast-error ast-btn sq-retry\" (click)=\"retryFetch()\">\n <span>Try again</span>\n <span *ngIf=\"retrialAttempts\" class=\"ms-2 attempts\">{{ retrialAttempts }}</span>\n </button>\n <div class=\"text-end small text-muted px-3\" *ngIf=\"!!config?.globalSettings?.disclaimer\">\n {{ config?.globalSettings?.disclaimer }}\n </div>\n </div>\n </div>\n\n <!-- Floating scroll button -->\n <div *ngIf=\"!isAtBottom && !showReport\" class=\"sq-floating-scroll\" [ngClass]=\"enabledUserInput ? 'sq-floating-scroll--when-user-input' : 'sq-floating-scroll--without-user-input'\">\n <button class=\"btn shadow\" (click)=\"scrollDown()\">\n <i class=\"fas fa-angle-double-down\"></i>\n </button>\n </div>\n </div>\n</ng-container>\n\n<!-- NG TEMPLATES-->\n\n<ng-template #loadingTplDefault>\n <div class=\"spinner-grow text-primary d-block mx-auto my-5\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n</ng-template>\n\n<ng-template #inputTpl>\n <div class=\"px-3 py-1\">\n <div class=\"ast-input-container\">\n <button disabled class=\"btn btn-light\">\n <i class=\"fas fa-search\"></i>\n </button>\n <textarea #questionInput rows=\"1\"\n type=\"text\" class=\"form-control\"\n placeholder=\"Ask something\" autofocus\n [(ngModel)]=\"question\"\n (keyup)=\"onKeyUp($event)\"\n (keydown)=\"calculateHeight($event)\"\n [disabled]=\"(loading$ | async) || (chatService.streaming$ | async) || (chatService.stoppingGeneration$ | async)\">\n </textarea>\n <button\n *ngIf=\"!(chatService.streaming$ | async) && !(loading$ | async) && !(chatService.stoppingGeneration$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Send message\"\n (click)=\"submitQuestion()\">\n <i class=\"fas fa-paper-plane\"></i>\n </button>\n <button\n *ngIf=\"messageToEdit\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Cancel edition\"\n (click)=\"messageToEdit = undefined; question = ''\">\n <i class=\"fas fa-undo-alt\"></i>\n </button>\n <span *ngIf=\"(chatService.streaming$ | async) && !(chatService.stoppingGeneration$ | async)\" class=\"processing ms-2\">\n Generating <i class=\"ms-1 fas fa-spinner fa-pulse\"></i>\n </span>\n <span *ngIf=\"(chatService.stoppingGeneration$ | async)\" class=\"processing ms-2\">\n Stopping <i class=\"ms-1 fas fa-spinner fa-pulse\"></i>\n </span>\n <button\n *ngIf=\"(chatService.streaming$ | async) && !(chatService.stoppingGeneration$ | async)\"\n type=\"button\"\n class=\"btn btn-light ms-2\"\n sqTooltip=\"Stop generating\"\n (click)=\"stopGeneration()\">\n <i class=\"fas fa-stop\"></i>\n </button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #reportTplDefault let-message let-rank=\"rank\" let-type=\"type\">\n <div class=\"px-3\">\n <ng-container *ngIf=\"type === 'dislike'\">\n <h5>Issue type</h5>\n <select class=\"form-select mb-4\" [(ngModel)]=\"issueType\">\n <option [value]=\"''\">Choose an issue type</option>\n <option *ngFor=\"let type of (issueTypes ?? defaultIssueTypes)\">{{type}}</option>\n </select>\n <h5>What was unsatisfying about this response? (optional)</h5>\n </ng-container>\n <ng-container *ngIf=\"type === 'like'\">\n <h5>Why did you like this answer? (optional)</h5>\n </ng-container>\n <textarea class=\"form-control\" [(ngModel)]=\"reportComment\" placeholder=\"Write your comment\"></textarea>\n <div class=\"d-flex flex-row-reverse gap-1 mt-2\">\n <button class=\"btn btn-primary\" [disabled]=\"type === 'dislike' && !issueType\" (click)=\"sendReport()\">Send</button>\n <button class=\"btn btn-light\" (click)=\"ignoreReport()\">Do not send report</button>\n </div>\n </div>\n</ng-template>\n\n<ng-template #defaultTokenConsumptionTpl let-instanceId>\n <sq-token-progress-bar\n [instanceId]=\"instanceId\">\n </sq-token-progress-bar>\n</ng-template>\n\n<div class=\"debug-messages\" [class.displayed]=\"showDebugMessages\">\n <button *ngIf=\"showDebugMessages\" class=\"btn btn-light shadow back-btn\" (click)=\"showDebugMessages=false\">\n <i class=\"fas fa-chevron-right\"></i>\n </button>\n <ng-container *ngTemplateOutlet=\"debugMessagesTpl || defaultDebugMessagesTpl; context: { $implicit: debugMessages }\">\n </ng-container>\n</div>\n\n<ng-template #defaultDebugMessagesTpl let-debugMessages>\n <sq-debug-message [data]=\"debugMessages\"></sq-debug-message>\n</ng-template>\n", styles: [".ast-primary{color:var(--ast-primary-color, #005DA7);background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover{background-color:var(--ast-primary-bg, #f2f8fe)}.ast-primary-hover:hover{color:var(--ast-primary-color, #005DA7)}.ast-secondary{color:var(--ast-secondary-color, #FF732E);background-color:var(--ast-secondary-bg, #FFF8F1)}.ast-error{background-color:var(--ast-error-bg, rgba(249, 58, 55, .2));color:var(--ast-action-buttons-color, inherit)}.ast-error:hover{color:var(--ast-error-color, rgba(249, 58, 55, .7))}.ast-btn{border:0;text-align:left;padding-top:.5rem;padding-bottom:.5rem;display:flex;align-items:center}.dark{--ast-primary-bg: #0d0701;--ast-primary-color: #008cd1;--ast-secondary-bg: #00070e;--ast-secondary-color: #ffa258;--ast-input-bg: #070707;--ast-input-color: rgba(222, 218, 218, .75);--ast-muted-color: rgba(222, 218, 218, .75);--ast-saved-chat-hover-background: #262421;--ast-uploaded-doc-hover-background: #262421;--ast-message-table-border-color: #333333;--ast-message-table-tr-bg: #070707;--ast-message-table-tr-border-color: #222222;--ast-reference-icon-color: white;--ast-reference-icon-active-color: black;--ast-reference-passages-color: white;--ast-reference-expanded-hover-bg: #262421;--ast-message-reference-color: black;--ast-action-buttons-color: white;--ast-action-buttons-hover-color: #6dbee6;--ast-report-bg: #070707}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference,:host ::ng-deep .attachment .reference{position:relative;bottom:var(--ast-reference-bottom, .3em);font-weight:var(--ast-reference-font-weight, bold);padding:var(--ast-reference-padding, 0 .2em);margin:var(--ast-reference-margin, 0 .1em);border-radius:var(--ast-reference-border-radius, .2em);background-color:var(--ast-reference-background-color, lightblue);color:var(--ast-reference-color, black)}:host ::ng-deep .reference,:host ::ng-deep .message-content .reference{font-size:var(--ast-reference-message-font-size, .7em)}:host ::ng-deep .attachment .reference{font-size:var(--ast-reference-attachment-font-size, 13px)}:host{font-size:.875rem}:host>div>.user-input>div:not(.progress),:host>div>.issue-report>div,:host>div>ul>li{width:var(--ast-chat-container-width, 100%);max-width:100%;margin-left:auto;margin-right:auto}:host>div>ul{padding-top:var(--ast-chat-padding-top, 0);padding-bottom:var(--ast-chat-padding-bottom, 0)}li.attachment>p{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;-webkit-line-clamp:3}li.attachment.expanded>p{display:block}.progress{--bs-progress-height: 3px}.progress.disabled{--bs-progress-height: 20px;--bs-progress-bar-bg: var(--bs-danger)}.user-input{z-index:1}.user-input ul.list-group{max-height:30vh}.form-control:disabled{background-color:#ededed}a.disabled{cursor:default;opacity:.5}.no-max-height{max-height:initial!important}.sq-floating-scroll{position:absolute;right:50%;text-align:center}.sq-floating-scroll--when-user-input{bottom:75px}.sq-floating-scroll--without-user-input{bottom:15px}.sq-floating-scroll .btn{background-color:#fff}.sq-floating-scroll .btn:hover{background-color:#fff;opacity:.9}.ast-input-container{display:flex;align-items:center;background-color:var(--ast-input-bg, #F8F8F8);border-radius:var(--ast-size-3, .75rem)}.ast-input-container>i{padding-left:var(--ast-size-3, .75rem);color:var(--ast-muted-color, rgba(33, 37, 41, .75))}.ast-input-container textarea{padding-left:var(--ast-size-3, .75rem);padding-right:var(--ast-size-3, .75rem);resize:none}.ast-input-container textarea,.ast-input-container button,.ast-input-container button:hover{background-color:transparent;border:0}.ast-input-container button:hover{color:var(--ast-primary-color, #005DA7)}.ast-input-container button:not(:hover){color:var(--ast-muted-color, rgba(33, 37, 41, .75))}.ast-input-container .processing{display:flex;align-items:center;color:var(--ast-secondary-color, #FF732E)}sq-chat-message.sq-user-message{float:var(--ast-user-message-float, none)}sq-token-progress-bar{z-index:10;position:absolute;top:0;right:0}.debug-messages{position:fixed;z-index:999999;right:-60%;top:0;width:60%;height:100%;transition:all .5s ease;background-color:var(--bs-body-bg);overflow:auto}.debug-messages .back-btn{position:fixed;right:0%;transition:all .5s ease}.debug-messages.displayed{right:0}.debug-messages.displayed .back-btn{right:60%}.debug-messages sq-debug-message:first-of-type{display:block;width:100%}.sq-retry{display:flex;margin:auto;font-weight:var(--font-weight-bold, 500)}.sq-retry .attempts{display:flex;border-radius:100%;background:#fff;height:20px;width:20px;place-content:center;align-items:center}.issue-report{background-color:var(--ast-report-bg, white)}\n"] }]
1010
- }], ctorParameters: function () { return []; }, propDecorators: { instanceId: [{
1011
- type: Input
1012
- }], query: [{
1013
- type: Input
1014
- }], queryChangeShouldTriggerReload: [{
1015
- type: Input
1016
- }], protocol: [{
1017
- type: Input
1018
- }], messageHandlers: [{
1019
- type: Input
1020
- }], automaticScrollToLastResponse: [{
1021
- type: Input
1022
- }], focusAfterResponse: [{
1023
- type: Input
1024
- }], chat: [{
1025
- type: Input
1026
- }], assistantMessageIcon: [{
1027
- type: Input
1028
- }], userMessageIcon: [{
1029
- type: Input
1030
- }], connectionErrorMessageIcon: [{
1031
- type: Input
1032
- }], searchWarningMessageIcon: [{
1033
- type: Input
1034
- }], connection: [{
1035
- type: Output
1036
- }], loading$: [{
1037
- type: Output,
1038
- args: ["loading"]
1039
- }], _config: [{
1040
- type: Output,
1041
- args: ["config"]
1042
- }], data: [{
1043
- type: Output
1044
- }], openDocument: [{
1045
- type: Output
1046
- }], openPreview: [{
1047
- type: Output
1048
- }], suggestAction: [{
1049
- type: Output
1050
- }], messageList: [{
1051
- type: ViewChild,
1052
- args: ['messageList']
1053
- }], questionInput: [{
1054
- type: ViewChild,
1055
- args: ['questionInput']
1056
- }], loadingTpl: [{
1057
- type: ContentChild,
1058
- args: ['loadingTpl']
1059
- }], reportTpl: [{
1060
- type: ContentChild,
1061
- args: ['reportTpl']
1062
- }], tokenConsumptionTpl: [{
1063
- type: ContentChild,
1064
- args: ['tokenConsumptionTpl']
1065
- }], debugMessagesTpl: [{
1066
- type: ContentChild,
1067
- args: ['debugMessagesTpl']
1068
- }] } });
1069
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhdC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9hc3Npc3RhbnQvY2hhdC9jaGF0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uL3Byb2plY3RzL2Fzc2lzdGFudC9jaGF0L2NoYXQuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxpQkFBaUIsRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFjLFlBQVksRUFBRSxLQUFLLEVBQWdDLE1BQU0sRUFBOEIsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMxTixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0MsT0FBTyxFQUFpQixrQkFBa0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFekgsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDM0QsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxVQUFVLEVBQVMsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbkQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFD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QkFBb0I7c0JBQTVCLEtBQUs7Z0JBRUcsZUFBZTtzQkFBdkIsS0FBSztnQkFFRywwQkFBMEI7c0JBQWxDLEtBQUs7Z0JBRUcsd0JBQXdCO3NCQUFoQyxLQUFLO2dCQUVJLFVBQVU7c0JBQW5CLE1BQU07Z0JBR1ksUUFBUTtzQkFBMUIsTUFBTTt1QkFBQyxTQUFTO2dCQUVDLE9BQU87c0JBQXhCLE1BQU07dUJBQUMsUUFBUTtnQkFDTixJQUFJO3NCQUFiLE1BQU07Z0JBRUcsWUFBWTtzQkFBckIsTUFBTTtnQkFFRyxXQUFXO3NCQUFwQixNQUFNO2dCQUVHLGFBQWE7c0JBQXRCLE1BQU07Z0JBRW1CLFdBQVc7c0JBQXBDLFNBQVM7dUJBQUMsYUFBYTtnQkFDSSxhQUFhO3NCQUF4QyxTQUFTO3VCQUFDLGVBQWU7Z0JBRUUsVUFBVTtzQkFBckMsWUFBWTt1QkFBQyxZQUFZO2dCQUNDLFNBQVM7c0JBQW5DLFlBQVk7dUJBQUMsV0FBVztnQkFDWSxtQkFBbUI7c0JBQXZELFlBQVk7dUJBQUMscUJBQXFCO2dCQUNELGdCQUFnQjtzQkFBakQsWUFBWTt1QkFBQyxrQkFBa0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tIFwiQGFuZ3VsYXIvY29tbW9uXCI7XG5pbXBvcnQgeyBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ2hhbmdlRGV0ZWN0b3JSZWYsIENvbXBvbmVudCwgQ29udGVudENoaWxkLCBFbGVtZW50UmVmLCBFdmVudEVtaXR0ZXIsIElucHV0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSwgT25Jbml0LCBPdXRwdXQsIFNpbXBsZUNoYW5nZXMsIFRlbXBsYXRlUmVmLCBWaWV3Q2hpbGQsIGluamVjdCB9IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5pbXBvcnQgeyBGb3Jtc01vZHVsZSB9IGZyb20gXCJAYW5ndWxhci9mb3Jtc1wiO1xuaW1wb3J0IHsgSHViQ29ubmVjdGlvbiwgSHViQ29ubmVjdGlvblN0YXRlIH0gZnJvbSBcIkBtaWNyb3NvZnQvc2lnbmFsclwiO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBTdWJzY3JpcHRpb24sIGNvbWJpbmVMYXRlc3QsIGZpbHRlciwgZnJvbUV2ZW50LCBtYXAsIG1lcmdlLCBzd2l0Y2hNYXAsIHRha2UsIHRhcCB9IGZyb20gXCJyeGpzXCI7XG5cbmltcG9ydCB7IEFjdGlvbiB9IGZyb20gXCJAc2luZXF1YS9jb21wb25lbnRzL2FjdGlvblwiO1xuaW1wb3J0IHsgQWJzdHJhY3RGYWNldCB9IGZyb20gXCJAc2luZXF1YS9jb21wb25lbnRzL2ZhY2V0XCI7XG5pbXBvcnQgeyBTZWFyY2hTZXJ2aWNlIH0gZnJvbSBcIkBzaW5lcXVhL2NvbXBvbmVudHMvc2VhcmNoXCI7XG5pbXBvcnQgeyBVdGlsc01vZHVsZSB9IGZyb20gXCJAc2luZXF1YS9jb21wb25lbnRzL3V0aWxzXCI7XG5pbXBvcnQgeyBBcHBTZXJ2aWNlLCBRdWVyeSB9IGZyb20gXCJAc2luZXF1YS9jb3JlL2FwcC11dGlsc1wiO1xuaW1wb3J0IHsgTG9naW5TZXJ2aWNlIH0gZnJvbSBcIkBzaW5lcXVhL2NvcmUvbG9naW5cIjtcbmltcG9ydCB7IE5vdGlmaWNhdGlvbnNTZXJ2aWNlIH0gZnJvbSBcIkBzaW5lcXVhL2NvcmUvbm90aWZpY2F0aW9uXCI7XG5pbXBvcnQgeyBSZWNvcmQgYXMgQXJ0aWNsZSwgUHJpbmNpcGFsV2ViU2VydmljZSB9IGZyb20gXCJAc2luZXF1YS9jb3JlL3dlYi1zZXJ2aWNlc1wiO1xuXG5pbXBvcnQgeyBDaGF0TWVzc2FnZUNvbXBvbmVudCB9IGZyb20gXCIuL2NoYXQtbWVzc2FnZS9jaGF0LW1lc3NhZ2UuY29tcG9uZW50XCI7XG5pbXBvcnQgeyBDaGF0U2VydmljZSB9IGZyb20gXCIuL2NoYXQuc2VydmljZVwiO1xuaW1wb3J0IHsgRGVidWdNZXNzYWdlQ29tcG9uZW50IH0gZnJvbSBcIi4vZGVidWctbWVzc2FnZS9kZWJ1Zy1tZXNzYWdlLmNvbXBvbmVudFwiO1xuaW1wb3J0IHsgSW5zdGFuY2VNYW5hZ2VyU2VydmljZSB9IGZyb20gXCIuL2luc3RhbmNlLW1hbmFnZXIuc2VydmljZVwiO1xuaW1wb3J0IHsgUmVzdENoYXRTZXJ2aWNlIH0gZnJvbSBcIi4vcmVzdC1jaGF0LnNlcnZpY2VcIjtcbmltcG9ydCB7IFRva2VuUHJvZ3Jlc3NCYXJDb21wb25lbnQgfSBmcm9tIFwiLi90b2tlbi1wcm9ncmVzcy1iYXIvdG9rZW4tcHJvZ3Jlc3MtYmFyLmNvbXBvbmVudFwiO1xuaW1wb3J0IHsgQ2hhdENvbmZpZywgQ2hhdENvbnRleHRBdHRhY2htZW50LCBDaGF0TWVzc2FnZSwgRGVidWdNZXNzYWdlLCBHbGxtTW9kZWxEZXNjcmlwdGlvbiwgSW5pdENoYXQsIE1lc3NhZ2VIYW5kbGVyLCBSYXdNZXNzYWdlLCBTdWdnZXN0ZWRBY3Rpb24gfSBmcm9tIFwiLi90eXBlc1wiO1xuaW1wb3J0IHsgV2ViU29ja2V0Q2hhdFNlcnZpY2UgfSBmcm9tIFwiLi93ZWJzb2NrZXQtY2hhdC5zZXJ2aWNlXCI7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3NxLWNoYXQtdjMnLCAvLyBtYW5kYXRvcnkgc2luY2UgQHNpbmVxdWEvY29tcG9uZW50cyBhbHJlYWR5IGhhcyB0aGUgc2FtZSB0YWctbmFtZSBcInNxLWNoYXRcIlxuICB0ZW1wbGF0ZVVybDogJy4vY2hhdC5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2NoYXQuY29tcG9uZW50LnNjc3MnXSxcbiAgcHJvdmlkZXJzOiBbXG4gICAgUmVzdENoYXRTZXJ2aWNlLFxuICAgIFdlYlNvY2tldENoYXRTZXJ2aWNlXG4gIF0sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlLCBGb3Jtc01vZHVsZSwgQ2hhdE1lc3NhZ2VDb21wb25lbnQsIFRva2VuUHJvZ3Jlc3NCYXJDb21wb25lbnQsIERlYnVnTWVzc2FnZUNvbXBvbmVudCwgVXRpbHNNb2R1bGVdXG59KVxuZXhwb3J0IGNsYXNzIENoYXRDb21wb25lbnQgZXh0ZW5kcyBBYnN0cmFjdEZhY2V0IGltcGxlbWVudHMgT25Jbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSB7XG5cbiAgcHVibGljIGxvZ2luU2VydmljZSA9IGluamVjdChMb2dpblNlcnZpY2UpO1xuICBwdWJsaWMgd2Vic29ja2V0U2VydmljZSA9IGluamVjdChXZWJTb2NrZXRDaGF0U2VydmljZSk7XG4gIHB1YmxpYyByZXN0U2VydmljZSA9IGluamVjdChSZXN0Q2hhdFNlcnZpY2UpO1xuICBwdWJsaWMgaW5zdGFuY2VNYW5hZ2VyU2VydmljZSA9IGluamVjdChJbnN0YW5jZU1hbmFnZXJTZXJ2aWNlKTtcbiAgcHVibGljIHNlYXJjaFNlcnZpY2UgPSBpbmplY3QoU2VhcmNoU2VydmljZSk7XG4gIHB1YmxpYyBwcmluY2lwYWxTZXJ2aWNlID0gaW5qZWN0KFByaW5jaXBhbFdlYlNlcnZpY2UpO1xuICBwdWJsaWMgY2RyID0gaW5qZWN0KENoYW5nZURldGVjdG9yUmVmKTtcbiAgcHVibGljIGFwcFNlcnZpY2UgPSBpbmplY3QoQXBwU2VydmljZSk7XG4gIHB1YmxpYyBub3RpZmljYXRpb25zU2VydmljZSA9IGluamVjdChOb3RpZmljYXRpb25zU2VydmljZSk7XG5cbiAgLyoqIERlZmluZSB0aGUga2V5IGJhc2VkIG9uIGl0LCB0aGUgY2hhdCBzZXJ2aWNlIGluc3RhbmNlIHdpbGwgYmUgc3RvcmVkICovXG4gIEBJbnB1dCgpIGluc3RhbmNlSWQ6IHN0cmluZztcbiAgLyoqIERlZmluZSB0aGUgcXVlcnkgdG8gdXNlIHRvIGZldGNoIGFuc3dlcnMgKi9cbiAgQElucHV0KCkgcXVlcnk6IFF1ZXJ5ID0gdGhpcy5zZWFyY2hTZXJ2aWNlLnF1ZXJ5O1xuICAvKiogRnVuY3Rpb24gdGhhdCBkZXRlcm1pbmVzIHdoZXRoZXIgdGhlIGNoYXQgc2hvdWxkIGJlIHJlbG9hZGVkIGFmdGVyIHRoZSBxdWVyeSBjaGFuZ2VzXG4gICAqIElmIG5vdCBwcm92aWRlZCwgdGhlIGNoYXQgd2lsbCBiZSByZWxvYWRlZCBieSBkZWZhdWx0XG4gICAqIEBwYXJhbSBwcmV2UXVlcnkgVGhlIHByZXZpb3VzIHF1ZXJ5XG4gICAqIEBwYXJhbSBuZXdRdWVyeSBUaGUgbmV3IHF1ZXJ5XG4gICAqIEByZXR1cm5zIHRydWUgaWYgdGhlIGNoYXQgc2hvdWxkIGJlIHJlbG9hZGVkLCBmYWxzZSBvdGhlcndpc2VcbiAgICovXG4gIEBJbnB1dCgpIHF1ZXJ5Q2hhbmdlU2hvdWxkVHJpZ2dlclJlbG9hZDogKHByZXZRdWVyeTogUXVlcnksIG5ld1F1ZXJ5OiBRdWVyeSkgPT4gYm9vbGVhbjtcbiAgLyoqIERlZmluZSB0aGUgcHJvdG9jb2wgdG8gYmUgdXNlZCBmb3IgdGhpcyBjaGF0IGluc3RhbmNlKi9cbiAgQElucHV0KCkgcHJvdG9jb2w6ICdSRVNUJyB8ICdXRUJTT0NLRVQnID0gXCJXRUJTT0NLRVRcIjtcbiAgLyoqIE1hcCBvZiBsaXN0ZW5lcnMgb3ZlcnJpZGluZyBkZWZhdWx0IHJlZ2lzdGVyZWQgb25lcyovXG4gIEBJbnB1dCgpIG1lc3NhZ2VIYW5kbGVyczogTWFwPHN0cmluZywgTWVzc2FnZUhhbmRsZXI8YW55Pj4gPSBuZXcgTWFwKCk7XG4gIC8qKiBXaGVuIHRoZSBhc3Npc3RhbnQgYW5zd2VyIGEgdXNlciBxdWVzdGlvbiwgYXV0b21hdGljYWxseSBzY3JvbGwgZG93biB0byB0aGUgYm90dG9tIG9mIHRoZSBkaXNjdXNzaW9uICovXG4gIEBJbnB1dCgpIGF1dG9tYXRpY1Njcm9sbFRvTGFzdFJlc3BvbnNlID0gZmFsc2U7XG4gIC8qKiBXaGVuIHRoZSBhc3Npc3RhbnQgYW5zd2VyIGEgdXNlciBxdWVzdGlvbiwgYXV0b21hdGljYWxseSBmb2N1cyB0byB0aGUgY2hhdCBpbnB1dCAqL1xuICBASW5wdXQoKSBmb2N1c0FmdGVyUmVzcG9uc2UgPSBmYWxzZTtcbiAgLyoqIEEgY2hhdCBkaXNjdXNzaW9uIHRoYXQgdGhlIGNvbXBvbmVudCBzaG91bGQgZ2V0IGluaXRpYWxpemVkIHdpdGggaXQgKi9cbiAgQElucHV0KCkgY2hhdD86IEluaXRDaGF0O1xuICAvKiogSWNvbiB0byB1c2UgZm9yIHRoZSBhc3Npc3RhbnQgbWVzc2FnZXMgKi9cbiAgQElucHV0KCkgYXNzaXN0YW50TWVzc2FnZUljb24gPSAnc3Etc2luZXF1YSc7XG4gIC8qKiBJY29uIHRvIHVzZSBmb3IgdGhlIHVzZXIgbWVzc2FnZXMgKi9cbiAgQElucHV0KCkgdXNlck1lc3NhZ2VJY29uOiBzdHJpbmc7XG4gIC8qKiBJY29uIHRvIHVzZSBmb3IgdGhlIGNvbm5lY3Rpb24gZXJyb3IgbWVzc2FnZXMgKi9cbiAgQElucHV0KCkgY29ubmVjdGlvbkVycm9yTWVzc2FnZUljb246IHN0cmluZztcbiAgLyoqIEljb24gdG8gdXNlIGZvciB0aGUgc2VhcmNoIHdhcm5pbmcgbWVzc2FnZXMgKi9cbiAgQElucHV0KCkgc2VhcmNoV2FybmluZ01lc3NhZ2VJY29uOiBzdHJpbmc7XG4gIC8qKiBFdmVudCBlbWl0dGVyIHRyaWdnZXJlZCBvbmNlIHRoZSBzaWduYWxSIGNvbm5lY3Rpb24gaXMgZXN0YWJsaXNoZWQgICovXG4gIEBPdXRwdXQoKSBjb25uZWN0aW9uID0gbmV3IEV2ZW50RW1pdHRlcjxIdWJDb25uZWN0aW9uPigpO1xuICAvKiogRXZlbnQgZW1pdHRlciB0cmlnZ2VyZWQgZWFjaCB0aW1lIHRoZSBhc3Npc3RhbnQgdXBkYXRlcyB0aGUgY3VycmVudCBjaGF0ICovXG4gIC8qKiBFdmVudCBlbWl0dGVyIHRyaWdnZXJlZCB3aGVuIHRoZSBjaGF0IGlzIGxvYWRpbmcgbmV3IGNvbnRlbnQgKi9cbiAgQE91dHB1dChcImxvYWRpbmdcIikgbG9hZGluZyQgPSBuZXcgRXZlbnRFbWl0dGVyPGJvb2xlYW4+KGZhbHNlKTtcbiAgLyoqIEVtaXRzIHRoZSBhc3Npc3RhbnQgY29uZmlndXJhdGlvbiB1c2VkIHdoZW4gaW5zdGFudGlhdGluZyB0aGUgY29tcG9uZW50ICovXG4gIEBPdXRwdXQoXCJjb25maWdcIikgX2NvbmZpZyA9IG5ldyBFdmVudEVtaXR0ZXI8Q2hhdENvbmZpZz4oKTtcbiAgQE91dHB1dCgpIGRhdGEgPSBuZXcgRXZlbnRFbWl0dGVyPENoYXRNZXNzYWdlW10+KCk7XG4gIC8qKiBFdmVudCBlbWl0dGVyIHRyaWdnZXJlZCB3aGVuIHRoZSB1c2VyIGNsaWNrcyB0byBvcGVuIHRoZSBvcmlnaW5hbCBkb2N1bWVudCByZXByZXNlbnRpbmcgdGhlIGNvbnRleHQgYXR0YWNobWVudCovXG4gIEBPdXRwdXQoKSBvcGVuRG9jdW1lbnQgPSBuZXcgRXZlbnRFbWl0dGVyPEFydGljbGU+KCk7XG4gIC8qKiBFdmVudCBlbWl0dGVyIHRyaWdnZXJlZCB3aGVuIHRoZSB1c2VyIGNsaWNrcyB0byBvcGVuIHRoZSBwcmV2aWV3IG9mIGEgZG9jdW1lbnQgcmVwcmVzZW50aW5nIHRoZSBjb250ZXh0IGF0dGFjaG1lbnQgKi9cbiAgQE91dHB1dCgpIG9wZW5QcmV2aWV3ID0gbmV3IEV2ZW50RW1pdHRlcjxDaGF0Q29udGV4dEF0dGFjaG1lbnQ+KCk7XG4gIC8qKiBFdmVudCBlbWl0dGVyIHRyaWdnZXJlZCB3aGVuIHRoZSB1c2VyIGNsaWNrcyBvbiBhIHN1Z2dlc3RlZCBhY3Rpb24gKi9cbiAgQE91dHB1dCgpIHN1Z2dlc3RBY3Rpb24gPSBuZXcgRXZlbnRFbWl0dGVyPFN1Z2dlc3RlZEFjdGlvbj4oKTtcbiAgLyoqIFZpZXdDaGlsZCBkZWNvcmF0b3JzIHRvIGFjY2VzcyB0aGUgdGVtcGxhdGUgZWxlbWVudHMgKi9cbiAgQFZpZXdDaGlsZCgnbWVzc2FnZUxpc3QnKSBtZXNzYWdlTGlzdD86IEVsZW1lbnRSZWY8SFRNTFVMaXN0RWxlbWVudD47XG4gIEBWaWV3Q2hpbGQoJ3F1ZXN0aW9uSW5wdXQnKSBxdWVzdGlvbklucHV0PzogRWxlbWVudFJlZjxIVE1MVGV4dEFyZWFFbGVtZW50PjtcbiAgLyoqIENvbnRlbnRDaGlsZCBkZWNvcmF0b3JzIGFsbG93aW5nIHRoZSBvdmVycmlkZSBvZiB0aGUgZGVmYXVsdCB0ZW1wbGF0ZXMgZnJvbSB0aGUgcGFyZW50IGNvbXBvbmVudCAqL1xuICBAQ29udGVudENoaWxkKCdsb2FkaW5nVHBsJykgbG9hZGluZ1RwbD86IFRlbXBsYXRlUmVmPGFueT47XG4gIEBDb250ZW50Q2hpbGQoJ3JlcG9ydFRwbCcpIHJlcG9ydFRwbD86IFRlbXBsYXRlUmVmPGFueT47XG4gIEBDb250ZW50Q2hpbGQoJ3Rva2VuQ29uc3VtcHRpb25UcGwnKSB0b2tlbkNvbnN1bXB0aW9uVHBsPzogVGVtcGxhdGVSZWY8YW55PjtcbiAgQENvbnRlbnRDaGlsZCgnZGVidWdNZXNzYWdlc1RwbCcpIGRlYnVnTWVzc2FnZXNUcGw/OiBUZW1wbGF0ZVJlZjxhbnk+O1xuXG4gIGNoYXRTZXJ2aWNlOiBDaGF0U2VydmljZTtcbiAgY29uZmlnOiBDaGF0Q29uZmlnO1xuICBtZXNzYWdlcyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PENoYXRNZXNzYWdlW10gfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG4gIGlzQWRtaW5PckRlbGV0ZWRBZG1pbiA9IGZhbHNlO1xuICBxdWVzdGlvbiA9ICcnO1xuXG4gIF9hY3Rpb25zOiBBY3Rpb25bXSA9IFtdO1xuICBwcml2YXRlIF9yZXNldENoYXRBY3Rpb24gPSBuZXcgQWN0aW9uKHtcbiAgICBpY29uOiAnZmFzIGZhLXN5bmMnLFxuICAgIHRpdGxlOiBcIlJlc2V0IGFzc2lzdGFudFwiLFxuICAgIGFjdGlvbjogKCkgPT4gdGhpcy5uZXdDaGF0KClcbiAgfSk7XG5cbiAgcHJpdmF0ZSBfc3ViID0gbmV3IFN1YnNjcmlwdGlvbigpO1xuICBwcml2YXRlIF9kYXRhU3Vic2NyaXB0aW9uOiBTdWJzY3JpcHRpb24gfCB1bmRlZmluZWQ7XG5cbiAgLyoqIFZhcmlhYmxlcyB0aGF0IGRlcGVuZCBvbiB0aGUgdHlwZSBvZiBtb2RlbCBpbiB1c2UgKi9cbiAgbW9kZWxEZXNjcmlwdGlvbj86IEdsbG1Nb2RlbERlc2NyaXB0aW9uO1xuXG4gIG1lc3NhZ2VUb0VkaXQ/OiBudW1iZXI7XG4gIHJlbWFwcGVkTWVzc2FnZVRvRWRpdD86IG51bWJlcjtcbiAgY2hhbmdlcyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PFNpbXBsZUNoYW5nZXMgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG4gIGN1cnJlbnRNZXNzYWdlSW5kZXg6IG51bWJlciB8IHVuZGVmaW5lZDtcbiAgZmlyc3RDaGFuZ2VzSGFuZGxlZCA9IGZhbHNlO1xuICBpc0F0Qm90dG9tID0gdHJ1ZTtcbiAgaW5pdGlhbGl6YXRpb25FcnJvciA9IGZhbHNlO1xuICBlbmFibGVkVXNlcklucHV0ID0gZmFsc2U7XG4gIGlzQ29ubmVjdGVkID0gdHJ1ZTsgLy8gQnkgZGVmYXVsdCwgdGhlIGNoYXQgaXMgY29uc2lkZXJlZCBjb25uZWN0ZWRcbiAgcmV0cmlhbEF0dGVtcHRzOiBudW1iZXIgfCB1bmRlZmluZWQ7XG4gIC8vIEZsYWcgdG8gdHJhY2sgd2hldGhlciB0aGUgJ3JlY29ubmVjdGVkJyBsaXN0ZW5lciBpcyBhbHJlYWR5IHJlZ2lzdGVyZWRcbiAgcHJpdmF0ZSBfaXNSZWNvbm5lY3RlZExpc3RlbmVyUmVnaXN0ZXJlZCA9IGZhbHNlO1xuXG4gIC8vIElzc3VlIHJlcG9ydGluZ1xuICBpc3N1ZVR5cGVzPzogc3RyaW5nW107XG4gIGRlZmF1bHRJc3N1ZVR5cGVzOiBzdHJpbmdbXSA9IFtcbiAgICAnVXNlciBJbnRlcmZhY2UgYnVnJyxcbiAgICAnSW5jb3JyZWN0IG9yIG1pc2xlYWRpbmcgcmVzcG9uc2UnLFxuICAgICdJbmNvbXBsZXRlIHJlc3BvbnNlJyxcbiAgICAnVGVjaG5pY2FsIGlzc3VlJyxcbiAgICAnUHJpdmFjeS9kYXRhIHNlY3VyaXR5IGlzc3VlJyxcbiAgICAnT3RoZXInXG4gIF07XG4gIGlzc3VlVHlwZTogc3RyaW5nID0gJyc7XG4gIHJlcG9ydENvbW1lbnQ/OiBzdHJpbmc7XG4gIG1lc3NhZ2VUb1JlcG9ydD86IENoYXRNZXNzYWdlO1xuICByZXBvcnRSYW5rPzogbnVtYmVyO1xuICByZXBvcnRUeXBlOiAnbGlrZScgfCAnZGlzbGlrZScgPSAnZGlzbGlrZSc7XG4gIHNob3dSZXBvcnQgPSBmYWxzZTtcblxuICAvLyBEZWJ1ZyBtZXNzYWdlc1xuICBkZWJ1Z01lc3NhZ2VzOiBEZWJ1Z01lc3NhZ2VbXSB8IHVuZGVmaW5lZDtcbiAgc2hvd0RlYnVnTWVzc2FnZXMgPSBmYWxzZTtcblxuICBwcml2YXRlIF9wcmV2aW91c1F1ZXJ5OiBRdWVyeTtcbiAgcHJpdmF0ZSBfcmVsb2FkU3Vic2NyaXB0aW9uOiBTdWJzY3JpcHRpb24gfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLl9hY3Rpb25zLnB1c2godGhpcy5fcmVzZXRDaGF0QWN0aW9uKTtcbiAgfVxuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIHRoaXMuX3N1Yi5hZGQoXG4gICAgICB0aGlzLmxvZ2luU2VydmljZS5ldmVudHMucGlwZShcbiAgICAgICAgZmlsdGVyKGUgPT4gZS50eXBlID09PSAnbG9naW4tY29tcGxldGUnKSxcbiAgICAgICAgdGFwKF8gPT4gdGhpcy5pbnN0YW50aWF0ZUNoYXRTZXJ2aWNlKCkpLFxuICAgICAgICBtYXAoXyA9PiB0aGlzLmNoYXRTZXJ2aWNlLmluaXRDaGF0Q29uZmlnKCkpLFxuICAgICAgICBzd2l0Y2hNYXAoKCkgPT4gdGhpcy5jaGF0U2VydmljZS5pbml0Q29uZmlnJCksXG4gICAgICAgIGZpbHRlcihpbml0Q29uZmlnID0+ICEhaW5pdENvbmZpZyksXG4gICAgICAgIHN3aXRjaE1hcChfID0+IHRoaXMuY2hhdFNlcnZpY2UuaW5pdCgpKSxcbiAgICAgICAgc3dpdGNoTWFwKF8gPT4gdGhpcy5jaGF0U2VydmljZS5pbml0UHJvY2VzcyQpLFxuICAgICAgICBmaWx0ZXIoc3VjY2VzcyA9PiAhIXN1Y2Nlc3MpLFxuICAgICAgICB0YXAoXyA9PiB7XG4gICAgICAgICAgaWYgKHRoaXMuY2hhdFNlcnZpY2UgaW5zdGFuY2VvZiBXZWJTb2NrZXRDaGF0U2VydmljZSkge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmVtaXQodGhpcy5jaGF0U2VydmljZS5jb25uZWN0aW9uKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhpcy5vbkxvYWRDaGF0KCk7XG4gICAgICAgIH0pLFxuICAgICAgICB0YXAoXyA9PiB0aGlzLmNoYXRTZXJ2aWNlLm92ZXJyaWRlVXNlcigpKSxcbiAgICAgICAgc3dpdGNoTWFwKF8gPT4gdGhpcy5jaGF0U2VydmljZS51c2VyT3ZlcnJpZGUkKSxcbiAgICAgICAgc3dpdGNoTWFwKF8gPT4gdGhpcy5jaGF0U2VydmljZS5hc3Npc3RhbnRDb25maWckKSxcbiAgICAgICAgdGFwKGNvbmZpZyA9PiB7XG4gICAgICAgICAgLy8gU2V0dXAgYWRtaW4gc3RhdHVzXG4gICAgICAgICAgdGhpcy5pc0FkbWluT3JEZWxldGVkQWRtaW4gPSB0aGlzLnByaW5jaXBhbFNlcnZpY2UucHJpbmNpcGFsIS5pc0FkbWluaXN0cmF0b3IgfHwgdGhpcy5wcmluY2lwYWxTZXJ2aWNlLnByaW5jaXBhbCEuaXNEZWxlZ2F0ZWRBZG1pbiB8fCBmYWxzZTtcbiAgICAgICAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZyE7XG4gICAgICAgICAgdGhpcy5lbmFibGVkVXNlcklucHV0ID0gdGhpcy5jb25maWcubW9kZVNldHRpbmdzLmVuYWJsZWRVc2VySW5wdXQ7XG4gICAgICAgICAgdGhpcy5pc3N1ZVR5cGVzID0gdGhpcy5jb25maWcuYXVkaXRTZXR0aW5ncz8uaXNzdWVUeXBlcz8ubGVuZ3RoID8gdGhpcy5jb25maWcuYXVkaXRTZXR0aW5ncyEuaXNzdWVUeXBlcyA6IHVuZGVmaW5lZDtcbiAgICAgICAgICB0aGlzLl9jb25maWcuZW1pdChjb25maWcpO1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICB0aGlzLnVwZGF0ZU1vZGVsRGVzY3JpcHRpb24oKTtcbiAgICAgICAgICAgIGlmKCF0aGlzLmZpcnN0Q2hhbmdlc0hhbmRsZWQpIHtcbiAgICAgICAgICAgICAgdGhpcy5fcHJldmlvdXNRdWVyeSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkodGhpcy5xdWVyeSkpOyAvLyBJbml0aWFsaXplIHRoZSBwcmV2aW91cyBxdWVyeVxuICAgICAgICAgICAgICB0aGlzLl9oYW5kbGVDaGFuZ2VzKCk7XG4gICAgICAgICAgICAgIHRoaXMuX2FkZFNjcm9sbExpc3RlbmVyKCk7XG4gICAgICAgICAgICAgIHRoaXMuZmlyc3RDaGFuZ2VzSGFuZGxlZCA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIHRoaXMuaW5pdGlhbGl6YXRpb25FcnJvciA9IHRydWVcbiAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICkuc3Vic2NyaWJlKClcbiAgICApO1xuXG4gICAgdGhpcy5fc3ViLmFkZChcbiAgICAgIGNvbWJpbmVMYXRlc3QoW1xuICAgICAgICB0aGlzLmNoYXRTZXJ2aWNlLnN0cmVhbWluZyQsXG4gICAgICAgIHRoaXMuY2hhdFNlcnZpY2Uuc3RvcHBpbmdHZW5lcmF0aW9uJFxuICAgICAgXSkucGlwZShcbiAgICAgICAgbWFwKChbc3RyZWFtaW5nLCBzdG9wcGluZ0dlbmVyYXRpb25dKSA9PiAhIShzdHJlYW1pbmcgfHwgc3RvcHBpbmdHZW5lcmF0aW9uKSlcbiAgICAgICkuc3Vic2NyaWJlKChyZXN1bHQpID0+IHtcbiAgICAgICAgdGhpcy5fcmVzZXRDaGF0QWN0aW9uLmRpc2FibGVkID0gcmVzdWx0O1xuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcykge1xuICAgIHRoaXMuY2hhbmdlcyQubmV4dChjaGFuZ2VzKTtcbiAgICBpZiAodGhpcy5jb25maWcpIHtcbiAgICAgIHRoaXMuX2hhbmRsZUNoYW5nZXMoKTtcbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLl9zdWIudW5zdWJzY3JpYmUoKTtcbiAgICB0aGlzLl9kYXRhU3Vic2NyaXB0aW9uPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuX3JlbG9hZFN1YnNjcmlwdGlvbj8udW5zdWJzY3JpYmUoKTtcbiAgICBpZiAodGhpcy5jaGF0U2VydmljZSBpbnN0YW5jZW9mIFdlYlNvY2tldENoYXRTZXJ2aWNlKSB7XG4gICAgICB0aGlzLmNoYXRTZXJ2aWNlLnN0b3BDb25uZWN0aW9uKCk7XG4gICAgfVxuICB9XG5cbiAgZ2V0IGlzQWRtaW4oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMucHJpbmNpcGFsU2VydmljZS5wcmluY2lwYWw/LmlzQWRtaW5pc3RyYXRvciB8fCBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnN0YW50aWF0ZSB0aGUgY2hhdCBzZXJ2aWNlIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBAaW5wdXQgcHJvdG9jb2xcbiAgICogVGhpcyBjaGF0IHNlcnZpY2UgaW5zdGFuY2Ugd2lsbCB0aGVuIGJlIHN0b3JlZCBpbiB0aGUgaW5zdGFuY2VNYW5hZ2VyU2VydmljZSB3aXRoIHByb3ZpZGVkIEBpbnB1dCBpbnN0YW5jZUlkIGFzIGEga2V5XG4gICAqL1xuICBpbnN0YW50aWF0ZUNoYXRTZXJ2aWNlKCk6IHZvaWQge1xuICAgIHN3aXRjaCAodGhpcy5wcm90b2NvbCkge1xuICAgICAgY2FzZSAnUkVTVCc6XG4gICAgICAgIHRoaXMuY2hhdFNlcnZpY2UgPSB0aGlzLnJlc3RTZXJ2aWNlO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ1dFQlNPQ0tFVCc6XG4gICAgICAgIHRoaXMuY2hhdFNlcnZpY2UgPSB0aGlzLndlYnNvY2tldFNlcnZpY2U7XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3QgZm91bmQgYSBDaGF0U2VydmljZSBpbXBsZW1lbnRhdGlvbiBjb3JyZXNwb25kaW5nIHRvIHRoZSBwcm92aWRlZCBwcm90b2NvbDogJyR7dGhpcy5wcm90b2NvbH0nYCk7XG4gICAgfVxuICAgIHRoaXMuY2hhdFNlcnZpY2Uuc2V0Q2hhdEluc3RhbmNlSWQodGhpcy5pbnN0YW5jZUlkKTtcbiAgICB0aGlzLmluc3RhbmNlTWFuYWdlclNlcnZpY2Uuc3RvcmVJbnN0YW5jZSh0aGlzLmluc3RhbmNlSWQsIHRoaXMuY2hhdFNlcnZpY2UpO1xuICB9XG5cbiAgb3ZlcnJpZGUgZ2V0IGFjdGlvbnMoKSB7IHJldHVybiB0aGlzLl9hY3Rpb25zOyB9XG5cblxuICAvKipcbiAgICogSGFuZGxlcyB0aGUgY2hhbmdlcyBpbiB0aGUgY2hhdCBjb21wb25lbnQuXG4gICAqIElmIHRoZSBjaGF0IHNlcnZpY2UgaXMgYSBXZWJTb2NrZXRDaGF0U2VydmljZSwgaXQgaGFuZGxlcyB0aGUgb3ZlcnJpZGUgb2YgdGhlIG1lc3NhZ2UgaGFuZGxlcnMgaWYgdGhleSBleGlzdC5cbiAgICogSW5pdGlhbGl6ZXMgdGhlIGNoYXQgd2l0aCB0aGUgcHJvdmlkZWQgY2hhdCBtZXNzYWdlcyBpZiB0aGV5IGV4aXN0LCBvdGhlcndpc2UgbG9hZHMgdGhlIGRlZmF1bHQgY2hhdC5cbiAgICogSWYgdGhlIGNoYXQgaXMgaW5pdGlhbGl6ZWQsIHRoZSBpbml0aWFsaXphdGlvbiBldmVudCBpcyBcIlF1ZXJ5XCIsIHRoZSBxdWVyeSBjaGFuZ2VzLCBhbmQgdGhlIHF1ZXJ5Q2hhbmdlU2hvdWxkVHJpZ2dlclJlbG9hZCBmdW5jdGlvbiBpcyBwcm92aWRlZCxcbiAgICogdGhlbiB0aGUgY2hhdCBzaG91bGQgYmUgcmVsb2FkZWQgaWYgdGhlIGZ1bmN0aW9uIHJldHVybnMgdHJ1ZS4gT3RoZXJ3aXNlLCB0aGUgY2hhdCBzaG91bGQgYmUgcmVsb2FkZWQgYnkgZGVmYXVsdC5cbiAgICogSXQgdGFrZXMgaW50byBhY2NvdW50IHRoZSBvbmdvaW5nIHN0cmVhbWluZyBwcm9jZXNzIGFuZCB0aGUgb25nb2luZyBzdG9wcGluZyBwcm9jZXNzIHRvIHRyaWdnZXIgdGhhdCBjb25kaXRpb25hbGx5IGRlZmluZSB0aGUgbG9naWNcbiAgICogb2YgdGhlIHJlbG9hZCA6XG4gICAqIC0gSWYgdGhlIGNoYXQgaXMgc3RyZWFtaW5nLCB0aGVuIHN0b3AgdGhlIGdlbmVyYXRpb24gYW5kIHdhaXQgZm9yIHRoZSBmZXRjaCB0byBjb21wbGV0ZSBiZWZvcmUgcmVsb2FkaW5nIHRoZSBjaGF0LlxuICAgKiAtIElmIHRoZSBjaGF0IGlzIHN0b3BwaW5nIHRoZSBnZW5lcmF0aW9uLCB0aGVuIHdhaXQgZm9yIHRoZSBmZXRjaCB0byBjb21wbGV0ZSBiZWZvcmUgcmVsb2FkaW5nIHRoZSBjaGF0LlxuICAgKi9cbiAgcHJpdmF0ZSBfaGFuZGxlQ2hhbmdlcygpIHtcbiAgICBjb25zdCBjaGFuZ2VzID0gdGhpcy5jaGFuZ2VzJC52YWx1ZTtcbiAgICAvLyBJZiB0aGUgY2hhdCBzZXJ2aWNlIGlzIGEgV2ViU29ja2V0Q2hhdFNlcnZpY2UsIGhhbmRsZSB0aGUgb3ZlcnJpZGUgb2YgdGhlIG1lc3NhZ2UgaGFuZGxlcnMgaWYgZXhpc3RzXG4gICAgaWYgKGNoYW5nZXM/Lm1lc3NhZ2VIYW5kbGVycyAmJiB0aGlzLm1lc3NhZ2VIYW5kbGVycyAmJiB0aGlzLmNoYXRTZXJ2aWNlIGluc3RhbmNlb2YgV2ViU29ja2V0Q2hhdFNlcnZpY2UpIHtcbiAgICAgIHRoaXMuY2hhdFNlcnZpY2Uub3ZlcnJpZGVNZXNzYWdlSGFuZGxlcnModGhpcy5tZXNzYWdlSGFuZGxlcnMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBJbml0aWFsaXplIHRoZSBjaGF0IHdpdGggdGhlIHByb3ZpZGVkIGNoYXQgbWVzc2FnZXMgaWYgZXhpc3RzLCBvdGhlcndpc2UgbG9hZCB0aGUgZGVmYXVsdCBjaGF0XG4gICAgICogT25jZSB0aGUgY2hhdCBpcyBpbml0aWFsaXplZCAoZmlyc3RDaGFuZ2VzSGFuZGxlZCBpcyB0cnVlKSwgYWxsb3cgb3BlbmluZyB0aGUgY2hhdCB3aXRoIHRoZSBuZXcgcHJvdmlkZWQgbWVzc2FnZXMgKGlmIGV4aXN0cylcbiAgICAgKi9cbiAgICBpZiAoIXRoaXMuZmlyc3RDaGFuZ2VzSGFuZGxlZCB8fCBjaGFuZ2VzPy5jaGF0KSB7XG4gICAgICBjb25zdCBvcGVuQ2hhdCA9ICgpID0+IHtcbiAgICAgICAgaWYgKHRoaXMubWVzc2FnZXMkLnZhbHVlKSB7XG4gICAgICAgICAgdGhpcy5jaGF0U2VydmljZS5saXN0U2F2ZWRDaGF0KCk7IC8vIFJlZnJlc2ggdGhlIGxpc3Qgb2Ygc2F2ZWQgY2hhdHNcbiAgICAgICAgfVxuICAgICAgICB0aGlzLm9wZW5DaGF0KHRoaXMuY2hhdCEubWVzc2FnZXMpO1xuICAgICAgfTtcbiAgICAgIHRoaXMuY2hhdFNlcnZpY2UuZ2VuZXJhdGVDaGF0SWQoKTtcbiAgICAgIGlmICh0aGlzLmNoYXQpIHtcbiAgICAgICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ25ldy1jaGF0Jywgeydjb25maWd1cmF0aW9uJzogSlNPTi5zdHJpbmdpZnkodGhpcy5jaGF0U2VydmljZS5hc3Npc3RhbnRDb25maWckLnZhbHVlKSwnY2hhdC1pbml0JzogSlNPTi5zdHJpbmdpZnkodGhpcy5jaGF0KX0pO1xuICAgICAgICBvcGVuQ2hhdCgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ25ldy1jaGF0Jywgeydjb25maWd1cmF0aW9uJzogSlNPTi5zdHJpbmdpZnkodGhpcy5jaGF0U2VydmljZS5hc3Npc3RhbnRDb25maWckLnZhbHVlKX0pO1xuICAgICAgICB0aGlzLmxvYWREZWZhdWx0Q2hhdCgpO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBJZiB0aGUgY2hhdCBpcyBpbml0aWFsaXplZCwgdGhlIGluaXRpYWxpemF0aW9uIGV2ZW50IGlzIFwiUXVlcnlcIiwgdGhlIHF1ZXJ5IGNoYW5nZXMgYW5kIHRoZSBxdWVyeUNoYW5nZVNob3VsZFRyaWdnZXJSZWxvYWQgZnVuY3Rpb24gaXMgcHJvdmlkZWQsXG4gICAgICogdGhlbiB0aGUgY2hhdCBzaG91bGQgYmUgcmVsb2FkZWQgaWYgdGhlIGZ1bmN0aW9uIHJldHVybnMgdHJ1ZVxuICAgICAqIE90aGVyd2lzZSwgdGhlIGNoYXQgc2hvdWxkIGJlIHJlbG9hZGVkIGJ5IGRlZmF1bHRcbiAgICAgKi9cbiAgICBpZiAodGhpcy5maXJzdENoYW5nZXNIYW5kbGVkICYmIGNoYW5nZXM/LnF1ZXJ5ICYmIHRoaXMuY29uZmlnLm1vZGVTZXR0aW5ncy5pbml0aWFsaXphdGlvbi5ldmVudCA9PT0gJ1F1ZXJ5Jykge1xuICAgICAgaWYgKHRoaXMucXVlcnlDaGFuZ2VTaG91bGRUcmlnZ2VyUmVsb2FkID8gdGhpcy5xdWVyeUNoYW5nZVNob3VsZFRyaWdnZXJSZWxvYWQodGhpcy5fcHJldmlvdXNRdWVyeSwgdGhpcy5xdWVyeSkgOiB0cnVlKSB7XG4gICAgICAgIGlmICghIXRoaXMuY2hhdFNlcnZpY2Uuc3RvcHBpbmdHZW5lcmF0aW9uJC52YWx1ZSkge1xuICAgICAgICAgIGlmICghdGhpcy5fcmVsb2FkU3Vic2NyaXB0aW9uKSB7XG4gICAgICAgICAgICAvLyBDcmVhdGUgYSBzdWJzY3JpcHRpb24gdG8gd2FpdCBmb3IgYm90aCBzdHJlYW1pbmckIGFuZCBzdG9wcGluZ0dlbmVyYXRpb24kIHRvIGJlIGZhbHNlXG4gICAgICAgICAgICB0aGlzLl9yZWxvYWRTdWJzY3JpcHRpb24gPSBjb21iaW5lTGF0ZXN0KFtcbiAgICAgICAgICAgICAgdGhpcy5jaGF0U2VydmljZS5zdHJlYW1pbmckLFxuICAgICAgICAgICAgICB0aGlzLmNoYXRTZXJ2aWNlLnN0b3BwaW5nR2VuZXJhdGlvbiRcbiAgICAgICAgICAgIF0pXG4gICAgICAgICAgICAucGlwZShcbiAgICAgICAgICAgICAgZmlsdGVyKChbc3RyZWFtaW5nLCBzdG9wcGluZ10pID0+ICFzdHJlYW1pbmcgJiYgIXN0b3BwaW5nKSwgLy8gV2FpdCB1bnRpbCBib3RoIGFyZSBmYWxzZVxuICAgICAgICAgICAgICB0YWtlKDEpIC8vIENvbXBsZXRlIGFmdGVyIHRoZSBmaXJzdCBtYXRjaFxuICAgICAgICAgICAgKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgICAgICAgICAvLyBFeGVjdXRlIHRoZSByZWxvYWQgYWZ0ZXIgdGhlIHF1ZXJ5IGNoYW5nZVxuICAgICAgICAgICAgICB0aGlzLl90cmlnZ2VyUmVsb2FkQWZ0ZXJRdWVyeUNoYW5nZSgpO1xuICAgICAgICAgICAgICAvLyBVcGRhdGUgX3ByZXZpb3VzUXVlcnkgd2l0aCB0aGUgY3VycmVudCBxdWVyeVxuICAgICAgICAgICAgICB0aGlzLl9wcmV2aW91c1F1ZXJ5ID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeSh0aGlzLnF1ZXJ5KSk7XG4gICAgICAgICAgICAgIC8vIENsZWFuIHVwIHN1YnNjcmlwdGlvbiBhbmQgcmVzZXQgaXRzIHZhbHVlXG4gICAgICAgICAgICAgIHRoaXMuX3JlbG9hZFN1YnNjcmlwdGlvbiA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmICghIXRoaXMuY2hhdFNlcnZpY2Uuc3RyZWFtaW5nJC52YWx1ZSkge1xuICAgICAgICAgIGlmICghdGhpcy5fcmVsb2FkU3Vic2NyaXB0aW9uKSB7XG4gICAgICAgICAgICB0aGlzLl9yZWxvYWRTdWJzY3JpcHRpb24gPSB0aGlzLmNoYXRTZXJ2aWNlLnN0b3BHZW5lcmF0aW9uKClcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoe1xuICAgICAgICAgICAgICBuZXh0OiAoKSA9PiB7fSxcbiAgICAgICAgICAgICAgZXJyb3I6ICgpID0+IHtcbiAgICAgICAgICAgICAgICAvLyBDbGVhbiB1cCBzdWJzY3JpcHRpb24gYW5kIHJlc2V0IGl0cyB2YWx1ZVxuICAgICAgICAgICAgICAgIHRoaXMuX3JlbG9hZFN1YnNjcmlwdGlvbj8udW5zdWJzY3JpYmUoKTtcbiAgICAgICAgICAgICAgICB0aGlzLl9yZWxvYWRTdWJzY3JpcHRpb24gPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIGNvbXBsZXRlOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8gV2FpdCBmb3IgdGhlIG9uZ29pbmcgZmV0Y2ggdG8gY29tcGxldGUsIHRoZW4gdHJpZ2dlciB0aGUgcmVsb2FkXG4gICAgICAgICAgICAgICAgdGhpcy5jaGF0U2VydmljZS5zdHJlYW1pbmckLnBpcGUoXG4gICAgICAgICAgICAgICAgICBmaWx0ZXIoKHN0cmVhbWluZykgPT4gIXN0cmVhbWluZyksXG4gICAgICAgICAgICAgICAgICB0YWtlKDEpXG4gICAgICAgICAgICAgICAgKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgLy8gRXhlY3V0ZSB0aGUgcmVsb2FkIGFmdGVyIHRoZSBxdWVyeSBjaGFuZ2VcbiAgICAgICAgICAgICAgICAgIHRoaXMuX3RyaWdnZXJSZWxvYWRBZnRlclF1ZXJ5Q2hhbmdlKCk7XG4gICAgICAgICAgICAgICAgICAvLyBVcGRhdGUgX3ByZXZpb3VzUXVlcnkgd2l0aCB0aGUgY3VycmVudCBxdWVyeVxuICAgICAgICAgICAgICAgICAgdGhpcy5fcHJldmlvdXNRdWVyeSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkodGhpcy5xdWVyeSkpO1xuICAgICAgICAgICAgICAgICAgLy8gQ2xlYW4gdXAgc3Vic2NyaXB0aW9uIGFuZCByZXNldCBpdHMgdmFsdWVcbiAgICAgICAgICAgICAgICAgIHRoaXMuX3JlbG9hZFN1YnNjcmlwdGlvbiEudW5zdWJzY3JpYmUoKTtcbiAgICAgICAgICAgICAgICAgIHRoaXMuX3JlbG9hZFN1YnNjcmlwdGlvbiA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIEV4ZWN1dGUgdGhlIHJlbG9hZCBhZnRlciB0aGUgcXVlcnkgY2hhbmdlXG4gICAgICAgICAgdGhpcy5fdHJpZ2dlclJlbG9hZEFmdGVyUXVlcnlDaGFuZ2UoKTtcbiAgICAgICAgICAvLyBVcGRhdGUgX3ByZXZpb3VzUXVlcnkgd2l0aCB0aGUgY3VycmVudCBxdWVyeVxuICAgICAgICAgIHRoaXMuX3ByZXZpb3VzUXVlcnkgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KHRoaXMucXVlcnkpKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gVXBkYXRlIF9wcmV2aW91c1F1ZXJ5IHdpdGggdGhlIGN1cnJlbnQgcXVlcnlcbiAgICAgICAgdGhpcy5fcHJldmlvdXNRdWVyeSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkodGhpcy5xdWVyeSkpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUcmlnZ2VycyBhIHJlbG9hZCBhZnRlciB0aGUgcXVlcnkgY2hhbmdlLlxuICAgKiBUaGlzIG1ldGhvZCBwZXJmb3JtcyB0aGUgbmVjZXNzYXJ5IG9wZXJhdGlvbnMgdG8gcmVsb2FkIHRoZSBjaGF0IGFmdGVyIGEgcXVlcnkgY2hhbmdlLlxuICAgKiBJdCBzZXRzIHRoZSBzeXN0ZW0gYW5kIHVzZXIgbWVzc2FnZXMsIHJlc2V0cyB0aGUgc2F2ZWRDaGF0SWQsIGdlbmVyYXRlcyBhIG5ldyBjaGF0SWQsXG4gICAqIGdlbmVyYXRlcyBhIG5ldyBjaGF0IGF1ZGl0IGV2ZW50LCBhbmQgaGFuZGxlcyB0aGUgcXVlcnkgbW9kZS5cbiAgICovXG4gIHByaXZhdGUgX3RyaWdnZXJSZWxvYWRBZnRlclF1ZXJ5Q2hhbmdlKCkge1xuICAgIGNvbnN0IHN5c3RlbU1zZyA9IHtyb2xlOiAnc3lzdGVtJywgY29udGVudDogdGhpcy5jb25maWcuZGVmYXVsdFZhbHVlcy5zeXN0ZW1Qcm9tcHQsIGFkZGl0aW9uYWxQcm9wZXJ0aWVzOiB7ZGlzcGxheTogZmFsc2V9fTtcbiAgICBjb25zdCB1c2VyTXNnID0ge3JvbGU6ICd1c2VyJywgY29udGVudDogQ2hhdFNlcnZpY2UuZm9ybWF0UHJvbXB0KHRoaXMuY29uZmlnLmRlZmF1bHRWYWx1ZXMudXNlclByb21wdCwge3ByaW5jaXBhbDogdGhpcy5wcmluY2lwYWxTZXJ2aWNlLnByaW5jaXBhbH0pLCBhZGRpdGlvbmFsUHJvcGVydGllczoge2Rpc3BsYXk6IHRoaXMuY29uZmlnLm1vZGVTZXR0aW5ncy5kaXNwbGF5VXNlclByb21wdH19O1xuICAgIHRoaXMuY2hhdFNlcnZpY2Uuc2V0U2F2ZWRDaGF0SWQodW5kZWZpbmVkKTsgLy8gUmVzZXQgdGhlIHNhdmVkQ2hhdElkXG4gICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUNoYXRJZCgpOyAvLyBHZW5lcmF0ZSBhIG5ldyBjaGF0SWRcbiAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQXVkaXRFdmVudCgnbmV3LWNoYXQnLCB7J2NvbmZpZ3VyYXRpb24nOiBKU09OLnN0cmluZ2lmeSh0aGlzLmNoYXRTZXJ2aWNlLmFzc2lzdGFudENvbmZpZyQudmFsdWUpfSk7IC8vIEdlbmVyYXRlIGEgbmV3IGNoYXQgYXVkaXQgZXZlbnRcbiAgICB0aGlzLl9oYW5kbGVRdWVyeU1vZGUoc3lzdGVtTXNnLCB1c2VyTXNnKTtcbiAgfVxuXG5cbiAgLyoqXG4gICAqIEFkZHMgYSBzY3JvbGwgbGlzdGVuZXIgdG8gdGhlIG1lc3NhZ2UgbGlzdCBlbGVtZW50LlxuICAgKiBUaGUgbGlzdGVuZXIgaXMgdHJpZ2dlcmVkIHdoZW4gYW55IG9mIHRoZSBmb2xsb3dpbmcgZXZlbnRzIG9jY3VyOlxuICAgKiAtIExvYWRpbmcgc3RhdGUgY2hhbmdlc1xuICAgKiAtIE1lc3NhZ2VzIGNoYW5nZVxuICAgKiAtIFN0cmVhbWluZyBzdGF0ZSBjaGFuZ2VzXG4gICAqIC0gU2Nyb2xsIGV2ZW50IG9jY3VycyBvbiB0aGUgbWVzc2FnZSBsaXN0IGVsZW1lbnRcbiAgICpcbiAgICogV2hlbiB0aGUgbGlzdGVuZXIgaXMgdHJpZ2dlcmVkLCBpdCB1cGRhdGVzIHRoZSBgaXNBdEJvdHRvbWAgcHJvcGVydHkuXG4gICAqL1xuICBwcml2YXRlIF9hZGRTY3JvbGxMaXN0ZW5lcigpIHtcbiAgICB0aGlzLl9zdWIuYWRkKFxuICAgICAgbWVyZ2UodGhpcy5sb2FkaW5nJCwgdGhpcy5tZXNzYWdlcyQsIHRoaXMuY2hhdFNlcnZpY2Uuc3RyZWFtaW5nJCwgZnJvbUV2ZW50KHRoaXMubWVzc2FnZUxpc3QhLm5hdGl2ZUVsZW1lbnQsICdzY3JvbGwnKSkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgdGhpcy5pc0F0Qm90dG9tID0gdGhpcy5fdG9nZ2xlU2Nyb2xsQnV0dG9uVmlzaWJpbGl0eSgpO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBtb2RlbCBkZXNjcmlwdGlvbiBiYXNlZCBvbiB0aGUgZGVmYXVsdFZhbHVlcyBzZXJ2aWNlX2lkIGFuZCBtb2RlbF9pZFxuICAgKi9cbiAgdXBkYXRlTW9kZWxEZXNjcmlwdGlvbigpIHtcbiAgICB0aGlzLm1vZGVsRGVzY3JpcHRpb24gPSB0aGlzLmNoYXRTZXJ2aWNlLmdldE1vZGVsKHRoaXMuY29uZmlnLmRlZmF1bHRWYWx1ZXMuc2VydmljZV9pZCwgdGhpcy5jb25maWcuZGVmYXVsdFZhbHVlcy5tb2RlbF9pZCk7XG4gICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFN1Ym1pdHMgYSBxdWVzdGlvbiBmcm9tIHRoZSB1c2VyLlxuICAgKiBJZiB0aGUgdXNlciBpcyBlZGl0aW5nIGEgcHJldmlvdXMgbWVzc2FnZSwgcmVtb3ZlcyBhbGwgc3Vic2VxdWVudCBtZXNzYWdlcyBmcm9tIHRoZSBjaGF0IGhpc3RvcnkuXG4gICAqIFRyaWdnZXJzIHRoZSBmZXRjaCBvZiB0aGUgYW5zd2VyIGZvciB0aGUgc3VibWl0dGVkIHF1ZXN0aW9uIGJ5IGNhbGxpbmcgX2ZldGNoQW5zd2VyKCkuXG4gICAqIENsZWFycyB0aGUgaW5wdXQgdmFsdWUgaW4gdGhlIFVJLlxuICAgKiDimqDvuI8gSWYgdGhlIGNoYXQgaXMgc3RyZWFtaW5nIG9yIHN0b3BwaW5nIHRoZSBnZW5lcmF0aW9uLCB0aGUgb3BlcmF0aW9uIGlzIG5vdCBhbGxvd2VkLlxuICAgKi9cbiAgc3VibWl0UXVlc3Rpb24oKSB7XG4gICAgaWYgKCEhdGhpcy5jaGF0U2VydmljZS5zdHJlYW1pbmckLnZhbHVlIHx8ICEhdGhpcy5jaGF0U2VydmljZS5zdG9wcGluZ0dlbmVyYXRpb24kLnZhbHVlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmKHRoaXMucXVlc3Rpb24udHJpbSgpICYmIHRoaXMubWVzc2FnZXMkLnZhbHVlICYmIHRoaXMuY2hhdFNlcnZpY2UuY2hhdEhpc3RvcnkpIHtcbiAgICAgIC8vIFdoZW4gdGhlIHVzZXIgc3VibWl0cyBhIHF1ZXN0aW9uLCBpZiB0aGUgdXNlciBpcyBlZGl0aW5nIGEgcHJldmlvdXMgbWVzc2FnZSwgcmVtb3ZlIGFsbCBzdWJzZXF1ZW50IG1lc3NhZ2VzIGZyb20gdGhlIGNoYXQgaGlzdG9yeVxuICAgICAgaWYgKHRoaXMubWVzc2FnZVRvRWRpdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgbWVzc2FnZXMgaW4gdGhlIFVJXG4gICAgICAgIHRoaXMubWVzc2FnZXMkLm5leHQodGhpcy5tZXNzYWdlcyQudmFsdWUuc2xpY2UoMCwgdGhpcy5tZXNzYWdlVG9FZGl0KSk7XG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgcmF3IG1lc3NhZ2VzIGluIHRoZSBjaGF0IGhpc3Rvcnkgd2hpY2ggaXMgdGhlIGNsZWFuIHZlcnNpb24gdXNlZCB0byBtYWtlIHRoZSBuZXh0IHJlcXVlc3RcbiAgICAgICAgdGhpcy5jaGF0U2VydmljZS5jaGF0SGlzdG9yeSA9IHRoaXMuY2hhdFNlcnZpY2UuY2hhdEhpc3Rvcnkuc2xpY2UoMCwgdGhpcy5yZW1hcHBlZE1lc3NhZ2VUb0VkaXQpO1xuICAgICAgICB0aGlzLm1lc3NhZ2VUb0VkaXQgPSB1bmRlZmluZWQ7XG4gICAgICAgIHRoaXMucmVtYXBwZWRNZXNzYWdlVG9FZGl0ID0gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgICAgLy8gUmVtb3ZlIHRoZSBzZWFyY2ggd2FybmluZyBtZXNzYWdlIGlmIGV4aXN0c1xuICAgICAgaWYgKHRoaXMuY2hhdFNlcnZpY2UuY2hhdEhpc3RvcnkuYXQoLTEpPy5yb2xlID09PSAnc2VhcmNoLXdhcm5pbmcnKSB7XG4gICAgICAgIHRoaXMuY2hhdFNlcnZpY2UuY2hhdEhpc3RvcnkucG9wKCk7XG4gICAgICB9XG4gICAgICAvLyBGZXRjaCB0aGUgYW5zd2VyXG4gICAgICB0aGlzLl9mZXRjaEFuc3dlcih0aGlzLnF1ZXN0aW9uLnRyaW0oKSwgdGhpcy5jaGF0U2VydmljZS5jaGF0SGlzdG9yeSk7XG4gICAgICAvLyBDbGVhciB0aGUgaW5wdXQgdmFsdWUgaW4gdGhlIFVJXG4gICAgICB0aGlzLnF1ZXN0aW9uSW5wdXQhLm5hdGl2ZUVsZW1lbnQudmFsdWUgPSAnJztcbiAgICAgIHRoaXMucXVlc3Rpb25JbnB1dCEubmF0aXZlRWxlbWVudC5zdHlsZS5oZWlnaHQgPSBgYXV0b2A7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRyaWdnZXJzIHRoZSBmZXRjaCBvZiB0aGUgYW5zd2VyIGZvciB0aGUgZ2l2ZW4gcXVlc3Rpb24gYW5kIHVwZGF0ZXMgdGhlIGNvbnZlcnNhdGlvbi5cbiAgICogR2VuZXJhdGVzIGFuIGF1ZGl0IGV2ZW50IGZvciB0aGUgdXNlciBpbnB1dC5cbiAgICpcbiAgICogQHBhcmFtIHF1ZXN0aW9uIC0gVGhlIHF1ZXN0aW9uIGFza2VkIGJ5IHRoZSB1c2VyLlxuICAgKiBAcGFyYW0gY29udmVyc2F0aW9uIC0gVGhlIGN1cnJlbnQgY29udmVyc2F0aW9uIG1lc3NhZ2VzLlxuICAgKi9cbiAgcHJpdmF0ZSBfZmV0Y2hBbnN3ZXIocXVlc3Rpb246IHN0cmluZywgY29udmVyc2F0aW9uOiBDaGF0TWVzc2FnZVtdKSB7XG4gICAgY29uc3QgdXNlck1zZyA9IHtyb2xlOiAndXNlcicsIGNvbnRlbnQ6IHF1ZXN0aW9uLCBhZGRpdGlvbmFsUHJvcGVydGllczoge2Rpc3BsYXk6IHRydWUsIGlzVXNlcklucHV0OiB0cnVlLCBhZGRpdGlvbmFsV29ya2Zsb3dQcm9wZXJ0aWVzOiB0aGlzLmNvbmZpZy5hZGRpdGlvbmFsV29ya2Zsb3dQcm9wZXJ0aWVzfX07XG4gICAgY29uc3QgbWVzc2FnZXMgPSBbLi4uY29udmVyc2F0aW9uLCB1c2VyTXNnXTtcbiAgICB0aGlzLm1lc3NhZ2VzJC5uZXh0KG1lc3NhZ2VzKTtcbiAgICB0aGlzLmZldGNoKG1lc3NhZ2VzKTtcbiAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQXVkaXRFdmVudCgnbWVzc2FnZScsIHsuLi50aGlzLl9kZWZpbmVNZXNzYWdlQXVkaXREZXRhaWxzKHVzZXJNc2csIG1lc3NhZ2VzLmxlbmd0aCAtIDEpLCAncXVlcnknOiBKU09OLnN0cmluZ2lmeSh0aGlzLnF1ZXJ5KSwgJ2lzLXVzZXItaW5wdXQnOiB0cnVlLCAnZW5hYmxlZC1mdW5jdGlvbnMnOiB0aGlzLmNvbmZpZy5kZWZhdWx0VmFsdWVzLmZ1bmN0aW9ucz8uZmlsdGVyKGZ1bmMgPT4gZnVuYy5lbmFibGVkKS5tYXAoZnVuYyA9PiBmdW5jLm5hbWUpLCAnYWRkaXRpb25hbC13b3JrZmxvdy1wcm9wZXJ0aWVzJzogSlNPTi5zdHJpbmdpZnkodGhpcy5jb25maWcuYWRkaXRpb25hbFdvcmtmbG93UHJvcGVydGllcyl9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEZXBlbmRpbmcgb24gdGhlIGNvbm5lY3Rpb24ncyBzdGF0ZSA6XG4gICAqICAtIElmIGNvbm5lY3RlZCA9PiBnaXZlbiBhIGxpc3Qgb2YgbWVzc2FnZXMsIHRoZSBjaGF0IGVuZHBvaW50IGlzIGludm9rZWQgZm9yIGEgY29udGludWF0aW9uIGFuZCB1cGRhdGVzIHRoZSBsaXN0IG9mIG1lc3NhZ2VzIGFjY29yZGluZ2x5LlxuICAgKiAgLSBJZiBhbnkgb3RoZXIgc3RhdGUgPT4gYSBjb25uZWN0aW9uIGVycm9yIG1lc3NhZ2UgaXMgZGlzcGxheWVkIGluIHRoZSBjaGF0LlxuICAgKiDimqDvuI8gSWYgdGhlIGFzc2lzdGFudCBpcyBzdHJlYW1pbmcgb3Igc3RvcHBpbmcgdGhlIGdlbmVyYXRpb24sIHRoZSBvcGVyYXRpb24gaXMgbm90IGFsbG93ZWQuXG4gICAqIEBwYXJhbSBtZXNzYWdlcyBUaGUgbGlzdCBvZiBtZXNzYWdlcyB0byBpbnZva2UgdGhlIGNoYXQgZW5kcG9pbnQgd2l0aFxuICAgKi9cbiAgcHVibGljIGZldGNoKG1lc3NhZ2VzOiBDaGF0TWVzc2FnZVtdKSB7XG4gICAgaWYgKCEhdGhpcy5jaGF0U2VydmljZS5zdHJlYW1pbmckLnZhbHVlIHx8ICEhdGhpcy5jaGF0U2VydmljZS5zdG9wcGluZ0dlbmVyYXRpb24kLnZhbHVlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuX3VwZGF0ZUNvbm5lY3Rpb25TdGF0dXMoKTtcbiAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG5cbiAgICBpZiAodGhpcy5pc0Nvbm5lY3RlZCkge1xuICAgICAgdGhpcy5sb2FkaW5nJC5uZXh0KHRydWUpO1xuICAgICAgdGhpcy5fZGF0YVN1YnNjcmlwdGlvbj8udW5zdWJzY3JpYmUoKTtcbiAgICAgIHRoaXMuX2RhdGFTdWJzY3JpcHRpb24gPSB0aGlzLmNoYXRTZXJ2aWNlLmZldGNoKG1lc3NhZ2VzLCB0aGlzLnF1ZXJ5KVxuICAgICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgICBuZXh0OiByZXMgPT4gdGhpcy51cGRhdGVEYXRhKHJlcy5oaXN0b3J5KSxcbiAgICAgICAgICBlcnJvcjogKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5fdXBkYXRlQ29ubmVjdGlvblN0YXR1cygpO1xuICAgICAgICAgICAgaWYgKCF0aGlzLmlzQ29ubmVjdGVkKSB7XG4gICAgICAgICAgICAgIGNvbnN0IG1lc3NhZ2UgPSB7cm9sZTogJ2Nvbm5lY3Rpb24tZXJyb3InLCBjb250ZW50OiB0aGlzLmNvbmZpZy5jb25uZWN0aW9uU2V0dGluZ3MuY29ubmVjdGlvbkVycm9yTWVzc2FnZSwgYWRkaXRpb25hbFByb3BlcnRpZXM6IHtkaXNwbGF5OiB0cnVlfX07XG4gICAgICAgICAgICAgIHRoaXMubWVzc2FnZXMkLm5leHQoWy4uLm1lc3NhZ2VzLCBtZXNzYWdlXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLnRlcm1pbmF0ZUZldGNoKCk7XG4gICAgICAgICAgfSxcbiAgICAgICAgICBjb21wbGV0ZTogKCkgPT4ge1xuICAgICAgICAgICAgLy8gUmVtb3ZlIHRoZSBsYXN0IG1lc3NhZ2UgaWYgaXQncyBhbiBlbXB0eSBtZXNzYWdlXG4gICAgICAgICAgICAvLyBUaGlzIGlzIGR1ZSB0byB0aGUgbWFubmVyIGluIHdoaWNoIHRoZSBjaGF0IHNlcnZpY2UgaGFuZGxlcyBjb25zZWN1dGl2ZSBtZXNzYWdlc1xuICAgICAgICAgICAgY29uc3QgbGFzdE1lc3NhZ2UgPSB0aGlzLm1lc3NhZ2VzJC52YWx1ZT8uYXQoLTEpO1xuICAgICAgICAgICAgaWYgKHRoaXMuaXNFbXB0eUFzc2lzdGFudE1lc3NhZ2UobGFzdE1lc3NhZ2UpKSB7XG4gICAgICAgICAgICAgIHRoaXMubWVzc2FnZXMkLm5leHQodGhpcy5tZXNzYWdlcyQudmFsdWU/LnNsaWNlKDAsIC0xKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLnRlcm1pbmF0ZUZldGNoKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgbWVzc2FnZSA9IHtyb2xlOiAnY29ubmVjdGlvbi1lcnJvcicsIGNvbnRlbnQ6IHRoaXMuY29uZmlnLmNvbm5lY3Rpb25TZXR0aW5ncy5jb25uZWN0aW9uRXJyb3JNZXNzYWdlLCBhZGRpdGlvbmFsUHJvcGVydGllczoge2Rpc3BsYXk6IHRydWV9fTtcbiAgICAgIHRoaXMubWVzc2FnZXMkLm5leHQoWy4uLm1lc3NhZ2VzLCBtZXNzYWdlXSk7XG4gICAgfVxuXG4gICAgaWYodGhpcy5hdXRvbWF0aWNTY3JvbGxUb0xhc3RSZXNwb25zZSkge1xuICAgICAgdGhpcy5zY3JvbGxEb3duKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHJ5IHRvIGZldGNoIHRoZSBtZXNzYWdlcyBpZiB0aGUgY29ubmVjdGlvbiBpc3N1ZXMuXG4gICAqICAtIElmIHJlY29ubmVjdGluZyA9PiBrZWVwIGRpc3BsYXkgdGhlIGNvbm5lY3Rpb24gZXJyb3IgbWVzc2FnZSBldmVuIHdoZW4gY2xpY2tpbmcgb24gdGhlIFwicmV0cnlcIiBidXR0b24gYW5kIGluY3JlYXNpbmcgdGhlIG51bWJlciBvZiByZXRyaWFsIGF0dGVtcHRzLCB1bnRpbCB0aGUgY29ubmVjdGlvbiBpcyByZS1lc3RhYmxpc2hlZFxuICAgKiAgLSBJZiBkaXNjb25uZWN0ZWQgPT4gT24gY2xpY2sgb24gdGhlIFwicmV0cnlcIiBidXR0b24sIHN0YXJ0IHRoZSBjb25uZWN0aW9uIHByb2Nlc3Mgd2hpbGUgZGlzcGxheWluZyB0aGUgY29ubmVjdGlvbiBlcnJvciBtZXNzYWdlIDpcbiAgICogICAgICAqIElmIHN1Y2Nlc3NmdWwgPT4gZ2l2ZW4gYSBsaXN0IG9mIG1lc3NhZ2VzLCB0aGUgY2hhdCBlbmRwb2ludCBpcyBpbnZva2VkIGZvciBhIGNvbnRpbnVhdGlvbiBhbmQgdXBkYXRlcyB0aGUgbGlzdCBvZiBtZXNzYWdlcyBhY2NvcmRpbmdseS5cbiAgICogICAgICAqIElmIGZhaWxlZCA9PiBpbmNyZWFzZSB0aGUgbnVtYmVyIG9mIHJldHJpYWwgYXR0ZW1wdHNcbiAgICovXG4gIHJldHJ5RmV0Y2goKSB7XG4gICAgaWYodGhpcy5jaGF0U2VydmljZSBpbnN0YW5jZW9mIFdlYlNvY2tldENoYXRTZXJ2aWNlKSB7XG4gICAgICAvLyBBIG9uZS10aW1lIGxpc3RlbmVyIGZvciByZWNvbm5lY3RlZCBldmVudFxuICAgICAgY29uc3Qgb25SZWNvbm5lY3RlZEhhbmRsZXIgPSAoKSA9PiB7XG4gICAgICAgIC8vIEdldCB0aGUgbWVzc2FnZXMgd2l0aG91dCB0aGUgbGFzdCBvbmUgKHRoZSBjb25uZWN0aW9uIGVycm9yIG1lc3NhZ2UpXG4gICAgICAgIGNvbnN0IG1lc3NhZ2VzID0gdGhpcy5tZXNzYWdlcyQudmFsdWUhLnNsaWNlKDAsIC0xKTtcbiAgICAgICAgLy8gRmluZCB0aGUgbGFzdCBcInVzZXJcIiBtZXNzYWdlIGluIHRoZSBtZXNzYWdlcyBsaXN0XG4gICAgICAgIGxldCBpbmRleCA9IG1lc3NhZ2VzLmxlbmd0aCAtIDE7XG4gICAgICAgIHdoaWxlIChpbmRleCA+PSAwICYmIG1lc3NhZ2VzW2luZGV4XS5yb2xlICE9PSAndXNlcicpIHtcbiAgICAgICAgICBpbmRleC0tO1xuICAgICAgICB9XG4gICAgICAgIC8vIElmIGEgdXNlciBtZXNzYWdlIGlzIGZvdW5kIChhbmQgaXQgc2hvdWxkIGFsd2F5cyBiZSB0aGUgY2FzZSksIHJlbW92ZSBhbGwgc3Vic2VxdWVudCBtZXNzYWdlcyBmcm9tIHRoZSBjaGF0IGhpc3RvcnlcbiAgICAgICAgLy8gVXBkYXRlIHRoZSBtZXNzYWdlcyBpbiB0aGUgVUlcbiAgICAgICAgLy8gYW5kIGZldGNoIHRoZSBhbnN3ZXIgZnJvbSB0aGUgYXNzaXN0YW50XG4gICAgICAgIGlmIChpbmRleCA+PSAwKSB7XG4gICAgICAgICAgdGhpcy5tZXNzYWdlcyQubmV4dCh0aGlzLm1lc3NhZ2VzJC52YWx1ZSEuc2xpY2UoMCwgaW5kZXgrMSkpO1xuICAgICAgICAgIGNvbnN0IHJlbWFwcGVkSW5kZXggPSB0aGlzLl9yZW1hcEluZGV4SW5DaGF0SGlzdG9yeShpbmRleCk7XG4gICAgICAgICAgdGhpcy5jaGF0U2VydmljZS5jaGF0SGlzdG9yeSA9IHRoaXMuY2hhdFNlcnZpY2UuY2hhdEhpc3RvcnkhLnNsaWNlKDAsIHJlbWFwcGVkSW5kZXgrMSk7XG4gICAgICAgICAgdGhpcy5mZXRjaCh0aGlzLmNoYXRTZXJ2aWNlLmNoYXRIaXN0b3J5KTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnJldHJpYWxBdHRlbXB0cyA9IHVuZGVmaW5lZDsgLy8gUmVzZXQgdGhlIG51bWJlciBvZiByZXRyaWFsIGF0dGVtcHRzXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUbyByZW1vdmUgdGhlIGhhbmRsZXIgZm9yIG9ucmVjb25uZWN0ZWQoKSBhZnRlciBpdCdzIGJlZW4gcmVnaXN0ZXJlZCxjYW5ub3QgZGlyZWN0bHkgdXNlIG9mZigpIGxpa2UgeW91IHdvdWxkIGZvciBub3JtYWwgZXZlbnRzIHJlZ2lzdGVyZWQgd2l0aCBjb25uZWN0aW9uLm9uKCkuXG4gICAgICAgICAqIEluc3RlYWQsIHlvdSBuZWVkIHRvIGV4cGxpY2l0bHkgcmVtb3ZlIG9yIHJlc2V0IHRoZSBoYW5kbGVyIGJ5IGFzc2lnbmluZyBpdCB0byBudWxsIG9yIGFuIGVtcHR5IGZ1bmN0aW9uXG4gICAgICAgICAqL1xuICAgICAgICAodGhpcy5jaGF0U2VydmljZSBhcyBXZWJTb2NrZXRDaGF0U2VydmljZSkuY29ubmVjdGlvbiEub25yZWNvbm5lY3RlZCgoKSA9PiB7fSk7XG4gICAgICAgIC8vIFJlc2V0IHRoZSBmbGFnIHRvIGVuc3VyZSB0aGUgaGFuZGxlciBpcyByZWdpc3RlcmVkIGFnYWluIHdoZW4gbmVlZGVkXG4gICAgICAgIHRoaXMuX2lzUmVjb25uZWN0ZWRMaXN0ZW5lclJlZ2lzdGVyZWQgPSBmYWxzZTtcbiAgICAgIH1cbiAgICAgIC8vIERlcGVuZGluZyBvbiB0aGUgY29ubmVjdGlvbidzIHN0YXRlLCB0YWtlIHRoZSBhcHByb3ByaWF0ZSBhY3Rpb25cbiAgICAgIHN3aXRjaCAodGhpcy5jaGF0U2VydmljZS5jb25uZWN0aW9uIS5zdGF0ZSkge1xuICAgICAgICBjYXNlIEh1YkNvbm5lY3Rpb25TdGF0ZS5Db25uZWN0ZWQ6XG4gICAgICAgICAgLy8gSWYgdGhlIGNvbm5lY3Rpb24gaXMgcmUtZXN0YWJsaXNoZWQgaW4gdGhlIG1lYW50aW1lLCBmZXRjaCB0aGUgbWVzc2FnZXNcbiAgICAgICAgICBvblJlY29ubmVjdGVkSGFuZGxlcigpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIEh1YkNvbm5lY3Rpb25TdGF0ZS5SZWNvbm5lY3Rpbmc6XG4gICAgICAgICAgLy8gQXR0YWNoIHRoZSByZWNvbm5lY3RlZCBsaXN0ZW5lciBpZiBub3QgYWxyZWFkeSByZWdpc3RlcmVkXG4gICAgICAgICAgaWYgKCF0aGlzLl9pc1JlY29ubmVjdGVkTGlzdGVuZXJSZWdpc3RlcmVkKSB7XG4gICAgICAgICAgICB0aGlzLmNoYXRTZXJ2aWNlLmNvbm5lY3Rpb24hLm9ucmVjb25uZWN0ZWQob25SZWNvbm5lY3RlZEhhbmRsZXIpO1xuICAgICAgICAgICAgdGhpcy5faXNSZWNvbm5lY3RlZExpc3RlbmVyUmVnaXN0ZXJlZCA9IHRydWU7XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIEluY3JlYXNlIHRoZSBudW1iZXIgb2YgcmV0cmlhbCBhdHRlbXB0c1xuICAgICAgICAgIHRoaXMucmV0cmlhbEF0dGVtcHRzID0gdGhpcy5yZXRyaWFsQXR0ZW1wdHMgPyB0aGlzLnJldHJpYWxBdHRlbXB0cysxIDogMTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBIdWJDb25uZWN0aW9uU3RhdGUuRGlzY29ubmVjdGVkOlxuICAgICAgICAgIC8vIFN0YXJ0IHRoZSBuZXcgY29ubmVjdGlvblxuICAgICAgICAgIHRoaXMuY2hhdFNlcnZpY2Uuc3RhcnRDb25uZWN0aW9uKClcbiAgICAgICAgICAudGhlbigoKSA9PiBvblJlY29ubmVjdGVkSGFuZGxlcigpKVxuICAgICAgICAgIC5jYXRjaCgoKSA9PiB7IC8vIElmIHRoZSBjb25uZWN0aW9uIGZhaWxzLCBpbmNyZWFzZSB0aGUgbnVtYmVyIG9mIHJldHJpYWwgYXR0ZW1wdHNcbiAgICAgICAgICAgIHRoaXMucmV0cmlhbEF0dGVtcHRzID0gdGhpcy5yZXRyaWFsQXR0ZW1wdHMgPyB0aGlzLnJldHJpYWxBdHRlbXB0cysxIDogMTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgdGhlIHNpZ25hbFIgY29ubmVjdGlvbiBpcyBjb25uZWN0ZWQuXG4gICAqIEZvciB0aGUgUkVTVCBwcm90b2NvbCwgdGhlIGNvbm5lY3Rpb24gaXMgYWx3YXlzIGNvbnNpZGVyZWQgY29ubmVjdGVkIChmb3IgdGhlIG1vbWVudCkuXG4gICAqL1xuICBwcml2YXRlIF91cGRhdGVDb25uZWN0aW9uU3RhdHVzKCkge1xuICAgIHRoaXMuaXNDb25uZWN0ZWQgPSAodGhpcy5jaGF0U2VydmljZSBpbnN0YW5jZW9mIFdlYlNvY2tldENoYXRTZXJ2aWNlKSA/IHRoaXMuY2hhdFNlcnZpY2UuY29ubmVjdGlvbiEuc3RhdGUgPT09IEh1YkNvbm5lY3Rpb25TdGF0ZS5Db25uZWN0ZWQgOiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSB0aGUgVUkgd2l0aCB0aGUgbmV3IG1lc3NhZ2VzXG4gICAqIEBwYXJhbSBtZXNzYWdlc1xuICAgKi9cbiAgdXBkYXRlRGF0YShtZXNzYWdlczogQ2hhdE1lc3NhZ2VbXSkge1xuICAgIHRoaXMubWVzc2FnZXMkLm5leHQobWVzc2FnZXMpO1xuICAgIHRoaXMuZGF0YS5lbWl0KG1lc3NhZ2VzKTtcbiAgICB0aGlzLmxvYWRpbmckLm5leHQoZmFsc2UpO1xuICAgIHRoaXMucXVlc3Rpb24gPSAnJztcbiAgICBpZih0aGlzLmF1dG9tYXRpY1Njcm9sbFRvTGFzdFJlc3BvbnNlKSB7XG4gICAgICB0aGlzLnNjcm9sbERvd24oKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHJldHVybnMgdHJ1ZSBpZiB0aGUgY2hhdCBkaXNjdXNzaW9uIGlzIHNjcm9sbGVkIGRvd24gdG8gdGhlIGJvdHRvbSwgZmFsc2Ugb3RoZXJ3aXNlXG4gICAqL1xuICBwcml2YXRlIF90b2dnbGVTY3JvbGxCdXR0b25WaXNpYmlsaXR5KCk6IGJvb2xlYW4ge1xuICAgIGlmKHRoaXMubWVzc2FnZUxpc3Q/Lm5hdGl2ZUVsZW1lbnQpIHtcbiAgICAgIHJldHVybiBNYXRoLnJvdW5kKHRoaXMubWVzc2FnZUxpc3Q/Lm5hdGl2ZUVsZW1lbnQuc2Nyb2xsSGVpZ2h0IC0gdGhpcy5tZXNzYWdlTGlzdD8ubmF0aXZlRWxlbWVudC5zY3JvbGxUb3AgLSAxKSA8PSB0aGlzLm1lc3NhZ2VMaXN0Py5uYXRpdmVFbGVtZW50LmNsaWVudEhlaWdodDtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogU2Nyb2xsIGRvd24gdG8gdGhlIGJvdHRvbSBvZiB0aGUgY2hhdCBkaXNjdXNzaW9uXG4gICAqL1xuICBzY3JvbGxEb3duKCkge1xuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgaWYodGhpcy5tZXNzYWdlTGlzdD8ubmF0aXZlRWxlbWVudCkge1xuICAgICAgICB0aGlzLm1lc3NhZ2VMaXN0Lm5hdGl2ZUVsZW1lbnQuc2Nyb2xsVG9wID0gdGhpcy5tZXNzYWdlTGlzdC5uYXRpdmVFbGVtZW50LnNjcm9sbEhlaWdodDtcbiAgICAgICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgfVxuICAgIH0sIDEwKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydCBhIG5ldyBjaGF0IHdpdGggdGhlIGRlZmF1bHRWYWx1ZXMgc2V0dGluZ3MuXG4gICAqIFRoZSBzYXZlZENoYXRJZCBpbiB0aGUgY2hhdCBzZXJ2aWNlIHdpbGwgYmUgcmVzZXQsIHNvIHRoYXQgdGhlIHVwY29taW5nIHNhdmVkIGNoYXQgb3BlcmF0aW9ucyB3aWxsIGJlIHBlcmZvcm1lZCBvbiB0aGUgZnJlc2ggbmV3IGNoYXQuXG4gICAqIElmIHRoZSBzYXZlZENoYXQgZmVhdHVyZSBpcyBlbmFibGVkLCB0aGUgbGlzdCBvZiBzYXZlZCBjaGF0cyB3aWxsIGJlIHJlZnJlc2hlZC5cbiAgICog4pqg77iPIElmIHRoZSBhc3Npc3RhbnQgaXMgc3RyZWFtaW5nIG9yIHN0b3BwaW5nIHRoZSBnZW5lcmF0aW9uLCB0aGUgb3BlcmF0aW9uIGlzIG5vdCBhbGxvd2VkLlxuICAgKi9cbiAgbmV3Q2hhdCgpIHtcbiAgICBpZiAoISF0aGlzLmNoYXRTZXJ2aWNlLnN0cmVhbWluZyQudmFsdWUgfHwgISF0aGlzLmNoYXRTZXJ2aWNlLnN0b3BwaW5nR2VuZXJhdGlvbiQudmFsdWUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5jaGF0U2VydmljZS5zZXRTYXZlZENoYXRJZCh1bmRlZmluZWQpOyAvLyBSZXNldCB0aGUgc2F2ZWRDaGF0SWRcbiAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQ2hhdElkKCk7IC8vIEdlbmVyYXRlIGEgbmV3IGNoYXRJZFxuICAgIHRoaXMuY2hhdFNlcnZpY2UubGlzdFNhdmVkQ2hhdCgpOyAvLyBSZWZyZXNoIHRoZSBsaXN0IG9mIHNhdmVkIGNoYXRzXG4gICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ25ldy1jaGF0Jywgeydjb25maWd1cmF0aW9uJzogSlNPTi5zdHJpbmdpZnkodGhpcy5jaGF0U2VydmljZS5hc3Npc3RhbnRDb25maWckLnZhbHVlKX0pOyAvLyBHZW5lcmF0ZSBhIG5ldyBjaGF0IGF1ZGl0IGV2ZW50XG4gICAgdGhpcy5sb2FkRGVmYXVsdENoYXQoKTsgLy8gU3RhcnQgYSBuZXcgY2hhdFxuICB9XG5cbiAgLyoqXG4gICAqIEF0dGFjaGVzIHRoZSBzcGVjaWZpZWQgZG9jdW1lbnQgSURzIHRvIHRoZSBhc3Npc3RhbnQuXG4gICAqIElmIG5vIGRvY3VtZW50IElEcyBhcmUgcHJvdmlkZWQsIHRoZSBvcGVyYXRpb24gaXMgbm90IGFsbG93ZWQuXG4gICAqIElmIHRoZSBhY3Rpb24gZm9yIGF0dGFjaGluZyBhIGRvY3VtZW50IGlzIG5vdCBkZWZpbmVkIGF0IHRoZSBhcHBsaWNhdGlvbiBjdXN0b21pemF0aW9uIGxldmVsLCBhbiBlcnJvciBpcyBsb2dnZWQuXG4gICAqIOKaoO+4jyBJZiB0aGUgYXNzaXN0YW50IGlzIHN0cmVhbWluZyBvciBzdG9wcGluZyB0aGUgZ2VuZXJhdGlvbiwgdGhlIG9wZXJhdGlvbiBpcyBub3QgYWxsb3dlZC5cbiAgICogQHBhcmFtIGlkcyAtIEFuIGFycmF5IG9mIGRvY3VtZW50IElEcyB0byBhdHRhY2guXG4gICAqL1xuICBhdHRhY2hUb0NoYXQoaWRzOiBzdHJpbmdbXSkge1xuICAgIGlmICghIXRoaXMuY2hhdFNlcnZpY2Uuc3RyZWFtaW5nJC52YWx1ZSB8fCAhIXRoaXMuY2hhdFNlcnZpY2Uuc3RvcHBpbmdHZW5lcmF0aW9uJC52YWx1ZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoIWlkcyB8fCBpZHM/Lmxlbmd0aCA8IDEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgYXR0YWNoRG9jQWN0aW9uID0gdGhpcy5jb25maWcubW9kZVNldHRpbmdzLmFjdGlvbnM/LltcImF0dGFjaERvY0FjdGlvblwiXTtcbiAgICBpZiAoIWF0dGFjaERvY0FjdGlvbikge1xuICAgICAgY29uc29sZS5lcnJvcihgTm8gYWN0aW9uIGlzIGRlZmluZWQgZm9yIGF0dGFjaGluZyBhIGRvY3VtZW50IHRvIHRoZSBhc3Npc3RhbnQgXCIke3RoaXMuaW5zdGFuY2VJZH1cImApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB1c2VyTXNnID0geyByb2xlOiAndXNlcicsIGNvbnRlbnQ6ICcnLCBhZGRpdGlvbmFsUHJvcGVydGllczoge2Rpc3BsYXk6IGZhbHNlLCBpc1VzZXJJbnB1dDogZmFsc2UsIHR5cGU6IFwiQWN0aW9uXCIsIGZvcmNlZFdvcmtmbG93OiBhdHRhY2hEb2NBY3Rpb24uZm9yY2VkV29ya2Zsb3csIGZvcmNlZFdvcmtmbG93UHJvcGVydGllczogey4uLihhdHRhY2hEb2NBY3Rpb24uZm9yY2VkV29ya2Zsb3dQcm9wZXJ0aWVzIHx8IHt9KSwgaWRzfSwgYWRkaXRpb25hbFdvcmtmbG93UHJvcGVydGllczogdGhpcy5jb25maWcuYWRkaXRpb25hbFdvcmtmbG93UHJvcGVydGllc319O1xuICAgIC8vIFJlbW92ZSB0aGUgc2VhcmNoIHdhcm5pbmcgbWVzc2FnZSBpZiBleGlzdHNcbiAgICBpZiAodGhpcy5jaGF0U2VydmljZS5jaGF0SGlzdG9yeSEuYXQoLTEpPy5yb2xlID09PSAnc2VhcmNoLXdhcm5pbmcnKSB7XG4gICAgICB0aGlzLmNoYXRTZXJ2aWNlLmNoYXRIaXN0b3J5IS5wb3AoKTtcbiAgICB9XG4gICAgY29uc3QgbWVzc2FnZXMgPSBbLi4udGhpcy5jaGF0U2VydmljZS5jaGF0SGlzdG9yeSEsIHVzZXJNc2ddO1xuICAgIHRoaXMubWVzc2FnZXMkLm5leHQobWVzc2FnZXMpO1xuICAgIHRoaXMuZmV0Y2gobWVzc2FnZXMpXG5cbiAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQXVkaXRFdmVudCgnQWN0aW9uUmVxdWVzdGVkJywge1xuICAgICAgJ2RldGFpbCc6ICdhdHRhY2hEb2NBY3Rpb24nLFxuICAgICAgJ2ZvcmNlZC13b3JrZmxvdyc6ICdTaW5lcXVhQWRkU2VsZWN0ZWREb2N1bWVudHNXb3JrZmxvdycsXG4gICAgICAnZm9yY2VkLXdvcmtmbG93LXByb3BlcnRpZXMnOiBKU09OLnN0cmluZ2lmeShpZHMpLFxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogU3RhcnQgdGhlIGRlZmF1bHQgY2hhdCB3aXRoIHRoZSBkZWZhdWx0VmFsdWVzIHNldHRpbmdzXG4gICAqIElmIHRoZSBjaGF0IGlzIG1lYW50IHRvIGJlIGluaXRpYWxpemVkIHdpdGggZXZlbnQgPT09IFwiUXVlcnlcIiwgdGhlIGNvcnJlc3BvbmRpbmcgdXNlciBxdWVyeSBtZXNzYWdlIHdpbGwgYmUgYWRkZWQgdG8gdGhlIGNoYXQgaGlzdG9yeVxuICAgKi9cbiAgbG9hZERlZmF1bHRDaGF0KCkge1xuICAgIC8vIERlZmluZSB0aGUgZGVmYXVsdCBzeXN0ZW0gcHJvbXB0IGFuZCB1c2VyIHByb21wdCBtZXNzYWdlc1xuICAgIGNvbnN0IHN5c3RlbU1zZyA9IHtyb2xlOiAnc3lzdGVtJywgY29udGVudDogdGhpcy5jb25maWcuZGVmYXVsdFZhbHVlcy5zeXN0ZW1Qcm9tcHQsIGFkZGl0aW9uYWxQcm9wZXJ0aWVzOiB7ZGlzcGxheTogZmFsc2V9fTtcbiAgICBjb25zdCB1c2VyTXNnID0ge3JvbGU6ICd1c2VyJywgY29udGVudDogQ2hhdFNlcnZpY2UuZm9ybWF0UHJvbXB0KHRoaXMuY29uZmlnLmRlZmF1bHRWYWx1ZXMudXNlclByb21wdCwge3ByaW5jaXBhbDogdGhpcy5wcmluY2lwYWxTZXJ2aWNlLnByaW5jaXBhbH0pLCBhZGRpdGlvbmFsUHJvcGVydGllczoge2Rpc3BsYXk6IHRoaXMuY29uZmlnLm1vZGVTZXR0aW5ncy5kaXNwbGF5VXNlclByb21wdH19O1xuXG4gICAgaWYgKHRoaXMuY29uZmlnLm1vZGVTZXR0aW5ncy5pbml0aWFsaXphdGlvbi5ldmVudCA9PT0gJ1F1ZXJ5Jykge1xuICAgICAgdGhpcy5faGFuZGxlUXVlcnlNb2RlKHN5c3RlbU1zZywgdXNlck1zZyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuX2hhbmRsZVByb21wdE1vZGUoc3lzdGVtTXNnLCB1c2VyTXNnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlcyB0aGUgcHJvbXB0IG1vZGUgb2YgdGhlIGNoYXQgY29tcG9uZW50LlxuICAgKiBJZiBgc2VuZFVzZXJQcm9tcHRgIGlzIHRydWUsIGl0IG9wZW5zIHRoZSBjaGF0IHdpdGggYm90aCBzeXN0ZW0gYW5kIHVzZXIgbWVzc2FnZXMsXG4gICAqIGFuZCBnZW5lcmF0ZXMgYXVkaXQgZXZlbnRzIGZvciBib3RoIG1lc3NhZ2VzLlxuICAgKiBJZiBgc2VuZFVzZXJQcm9tcHRgIGlzIGZhbHNlLCBpdCBvcGVucyB0aGUgY2hhdCB3aXRoIG9ubHkgdGhlIHN5c3RlbSBtZXNzYWdlLFxuICAgKiBhbmQgZ2VuZXJhdGVzIGFuIGF1ZGl0IGV2ZW50IGZvciB0aGUgc3lzdGVtIG1lc3NhZ2UuXG4gICAqXG4gICAqIEBwYXJhbSBzeXN0ZW1Nc2cgLSBUaGUgc3lzdGVtIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkIGluIHRoZSBjaGF0LlxuICAgKiBAcGFyYW0gdXNlck1zZyAtIFRoZSB1c2VyIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkIGluIHRoZSBjaGF0IChvcHRpb25hbCkuXG4gICAqL1xuICBwcml2YXRlIF9oYW5kbGVQcm9tcHRNb2RlKHN5c3RlbU1zZzogQ2hhdE1lc3NhZ2UsIHVzZXJNc2c6IENoYXRNZXNzYWdlKSB7XG4gICAgaWYgKHRoaXMuY29uZmlnLm1vZGVTZXR0aW5ncy5zZW5kVXNlclByb21wdCkge1xuICAgICAgdGhpcy5vcGVuQ2hhdChbc3lzdGVtTXNnLCB1c2VyTXNnXSk7XG4gICAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQXVkaXRFdmVudCgnbWVzc2FnZScsIHRoaXMuX2RlZmluZU1lc3NhZ2VBdWRpdERldGFpbHMoc3lzdGVtTXNnLCAwKSk7XG4gICAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQXVkaXRFdmVudCgnbWVzc2FnZScsIHRoaXMuX2RlZmluZU1lc3NhZ2VBdWRpdERldGFpbHModXNlck1zZywgMSkpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLm9wZW5DaGF0KFtzeXN0ZW1Nc2ddKTtcbiAgICAgIHRoaXMuY2hhdFNlcnZpY2UuZ2VuZXJhdGVBdWRpdEV2ZW50KCdtZXNzYWdlJywgdGhpcy5fZGVmaW5lTWVzc2FnZUF1ZGl0RGV0YWlscyhzeXN0ZW1Nc2csIDApKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlcyB0aGUgcXVlcnkgbW9kZSBieSBkaXNwbGF5aW5nIHRoZSBzeXN0ZW0gbWVzc2FnZSwgdXNlciBtZXNzYWdlLCBhbmQgdXNlciBxdWVyeSBtZXNzYWdlLlxuICAgKiBJZiB0aGUgcHJvdmlkZWQgcXVlcnkgdGV4dCBpcyBub3QgZW1wdHksIHRoZW4gYWRkIHRoZSB1c2VyIHF1ZXJ5IG1lc3NhZ2UgdG8gdGhlIGNoYXQgaGlzdG9yeSBhbmQgaW52b2tlIHRoZSBhc3Npc3RhbnRcbiAgICogT3RoZXJ3aXNlLCBqdXN0IHN0YXJ0IGEgbmV3IGNoYXQgd2l0aCBhIHdhcm5pbmcgbWVzc2FnZSBpbnZpdGluZyB0aGUgdXNlciB0byBwZXJmb3JtIGEgZnVsbCB0ZXh0IHNlYXJjaCB0byByZXRyaWV2ZSBzb21lIHJlc3VsdHNcbiAgICogQHBhcmFtIHN5c3RlbU1zZyAtIFRoZSBzeXN0ZW0gbWVzc2FnZSB0byBiZSBkaXNwbGF5ZWQuXG4gICAqIEBwYXJhbSB1c2VyTXNnIC0gVGhlIHVzZXIgbWVzc2FnZSB0byBiZSBkaXNwbGF5ZWQuXG4gICAqL1xuICBwcml2YXRlIF9oYW5kbGVRdWVyeU1vZGUoc3lzdGVtTXNnOiBDaGF0TWVzc2FnZSwgdXNlck1zZzogQ2hhdE1lc3NhZ2UpIHtcbiAgICBpZiAoISF0aGlzLnF1ZXJ5LnRleHQpIHtcbiAgICAgIGNvbnN0IHVzZXJRdWVyeU1zZyA9IHtyb2xlOiAndXNlcicsIGNvbnRlbnQ6IHRoaXMucXVlcnkudGV4dCwgYWRkaXRpb25hbFByb3BlcnRpZXM6IHtkaXNwbGF5OiB0aGlzLmNvbmZpZy5tb2RlU2V0dGluZ3MuaW5pdGlhbGl6YXRpb24uZGlzcGxheVVzZXJRdWVyeSwgcXVlcnk6IHRoaXMucXVlcnksIGZvcmNlZFdvcmtmbG93OiB0aGlzLmNvbmZpZy5tb2RlU2V0dGluZ3MuaW5pdGlhbGl6YXRpb24uZm9yY2VkV29ya2Zsb3csIGZvcmNlZEZ1bmN0aW9uOiB0aGlzLmNvbmZpZy5tb2RlU2V0dGluZ3MuaW5pdGlhbGl6YXRpb24uZm9yY2VkRnVuY3Rpb24sIGlzVXNlcklucHV0OiB0cnVlLCBhZGRpdGlvbmFsV29ya2Zsb3dQcm9wZXJ0aWVzOiB0aGlzLmNvbmZpZy5hZGRpdGlvbmFsV29ya2Zsb3dQcm9wZXJ0aWVzfX07XG4gICAgICBpZiAodGhpcy5jb25maWcubW9kZVNldHRpbmdzLnNlbmRVc2VyUHJvbXB0KSB7XG4gICAgICAgIHRoaXMub3BlbkNoYXQoW3N5c3RlbU1zZywgdXNlck1zZywgdXNlclF1ZXJ5TXNnXSk7XG4gICAgICAgIHRoaXMuY2hhdFNlcnZpY2UuZ2VuZXJhdGVBdWRpdEV2ZW50KCdtZXNzYWdlJywgdGhpcy5fZGVmaW5lTWVzc2FnZUF1ZGl0RGV0YWlscyhzeXN0ZW1Nc2csIDApKTtcbiAgICAgICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ21lc3NhZ2UnLCB0aGlzLl9kZWZpbmVNZXNzYWdlQXVkaXREZXRhaWxzKHVzZXJNc2csIDEpKTtcbiAgICAgICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ21lc3NhZ2UnLCB7Li4udGhpcy5fZGVmaW5lTWVzc2FnZUF1ZGl0RGV0YWlscyh1c2VyUXVlcnlNc2csIDIpLCAncXVlcnknOiBKU09OLnN0cmluZ2lmeSh0aGlzLnF1ZXJ5KSAsJ2lzLXVzZXItaW5wdXQnOiB0cnVlLCAnZm9yY2VkLXdvcmtmbG93JzogdGhpcy5jb25maWcubW9kZVNldHRpbmdzLmluaXRpYWxpemF0aW9uLmZvcmNlZFdvcmtmbG93LCAnZm9yY2VkLWZ1bmN0aW9uJzogdGhpcy5jb25maWcubW9kZVNldHRpbmdzLmluaXRpYWxpemF0aW9uLmZvcmNlZEZ1bmN0aW9uLCAnZW5hYmxlZC1mdW5jdGlvbnMnOiB0aGlzLmNvbmZpZy5kZWZhdWx0VmFsdWVzLmZ1bmN0aW9ucz8uZmlsdGVyKGZ1bmMgPT4gZnVuYy5lbmFibGVkKS5tYXAoZnVuYyA9PiBmdW5jLm5hbWUpLCAnYWRkaXRpb25hbC13b3JrZmxvdy1wcm9wZXJ0aWVzJzpKU09OLnN0cmluZ2lmeSh0aGlzLmNvbmZpZy5hZGRpdGlvbmFsV29ya2Zsb3dQcm9wZXJ0aWVzKX0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5vcGVuQ2hhdChbc3lzdGVtTXNnLCB1c2VyUXVlcnlNc2ddKTtcbiAgICAgICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ21lc3NhZ2UnLCB0aGlzLl9kZWZpbmVNZXNzYWdlQXVkaXREZXRhaWxzKHN5c3RlbU1zZywgMCkpO1xuICAgICAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQXVkaXRFdmVudCgnbWVzc2FnZScsIHsuLi50aGlzLl9kZWZpbmVNZXNzYWdlQXVkaXREZXRhaWxzKHVzZXJRdWVyeU1zZywgMSksICdxdWVyeSc6IEpTT04uc3RyaW5naWZ5KHRoaXMucXVlcnkpICwnaXMtdXNlci1pbnB1dCc6IHRydWUsICdmb3JjZWQtd29ya2Zsb3cnOiB0aGlzLmNvbmZpZy5tb2RlU2V0dGluZ3MuaW5pdGlhbGl6YXRpb24uZm9yY2VkV29ya2Zsb3csICdmb3JjZWQtZnVuY3Rpb24nOiB0aGlzLmNvbmZpZy5tb2RlU2V0dGluZ3MuaW5pdGlhbGl6YXRpb24uZm9yY2VkRnVuY3Rpb24sICdlbmFibGVkLWZ1bmN0aW9ucyc6IHRoaXMuY29uZmlnLmRlZmF1bHRWYWx1ZXMuZnVuY3Rpb25zPy5maWx0ZXIoZnVuYyA9PiBmdW5jLmVuYWJsZWQpLm1hcChmdW5jID0+IGZ1bmMubmFtZSksICdhZGRpdGlvbmFsLXdvcmtmbG93LXByb3BlcnRpZXMnOiBKU09OLnN0cmluZ2lmeSh0aGlzLmNvbmZpZy5hZGRpdGlvbmFsV29ya2Zsb3dQcm9wZXJ0aWVzKX0pO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCB3YXJuaW5nTXNnID0ge3JvbGU6ICdzZWFyY2gtd2FybmluZycsIGNvbnRlbnQ6IHRoaXMuY29uZmlnLmdsb2JhbFNldHRpbmdzLnNlYXJjaFdhcm5pbmdNZXNzYWdlLCBhZGRpdGlvbmFsUHJvcGVydGllczoge2Rpc3BsYXk6IHRydWV9fTtcbiAgICAgIHRoaXMub3BlbkNoYXQoW3N5c3RlbU1zZywgd2FybmluZ01zZ10pO1xuICAgICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ21lc3NhZ2UnLCB0aGlzLl9kZWZpbmVNZXNzYWdlQXVkaXREZXRhaWxzKHdhcm5pbmdNc2csIDApKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIF9kZWZpbmVNZXNzYWdlQXVkaXREZXRhaWxzKG1lc3NhZ2U6IENoYXRNZXNzYWdlLCByYW5rOiBudW1iZXIpOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHtcbiAgICByZXR1cm4ge1xuICAgICAgJ2R1cmF0aW9uJzogMCxcbiAgICAgICd0ZXh0JzogbWVzc2FnZS5jb250ZW50LFxuICAgICAgJ3JvbGUnOiBtZXNzYWdlLnJvbGUsXG4gICAgICAncmFuayc6IHJhbmtcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0L29wZW4gYSBuZXcgY2hhdCB3aXRoIHRoZSBwcm92aWRlZCBtZXNzYWdlcyBhbmQgY2hhdElkXG4gICAqIElmIHRoZSBsYXN0IG1lc3NhZ2UgaXMgZnJvbSB0aGUgdXNlciwgYSByZXF1ZXN0IHRvIHRoZSBhc3Npc3RhbnQgaXMgbWFkZSB0byBnZXQgYW4gYW5zd2VyXG4gICAqIElmIHRoZSBsYXN0IG1lc3NhZ2UgaXMgZnJvbSB0aGUgYXNzaXN0YW50LCB0aGUgY29udmVyc2F0aW9uIGlzIGxvYWRlZCByaWdodCBhd2F5XG4gICAqIEBwYXJhbSBtZXNzYWdlcyBUaGUgbGlzdCBvZiBtZXNzYWdlcyBvZiB0aGUgY2hhdFxuICAgKiBAcGFyYW0gc2F2ZWRDaGF0SWQgIFRoZSBpZCBvZiB0aGUgc2F2ZWQgY2hhdC4gSWYgcHJvdmlkZWQgKGllLiBhbiBleGlzdGluZyBkaXNjdXNzaW9uIGluIHRoZSBzYXZlZCBjaGF0IGluZGV4KSwgdXBkYXRlIHRoZSBzYXZlZENoYXRJZCBpbiB0aGUgY2hhdCBzZXJ2aWNlIGZvciB0aGUgdXBjb21pbmcgc2F2ZWQgY2hhdCBvcGVyYXRpb25zXG4gICAqL1xuICBvcGVuQ2hhdChtZXNzYWdlczogUmF3TWVzc2FnZVtdLCBzYXZlZENoYXRJZD86IHN0cmluZykge1xuICAgIGlmICghbWVzc2FnZXMgfHwgIUFycmF5LmlzQXJyYXkobWVzc2FnZXMpKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBvY2N1cnMgd2hpbGUgdHJ5aW5nIHRvIGxvYWQgdGhlIGRpc2N1c3Npb24uIEludmFsaWQgbWVzc2FnZXMgcmVjZWl2ZWQgOicsIG1lc3NhZ2VzKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHNhdmVkQ2hhdElkKSB7XG4gICAgICB0aGlzLmNoYXRTZXJ2aWNlLnNldFNhdmVkQ2hhdElkKHNhdmVkQ2hhdElkKTtcbiAgICAgIHRoaXMuY2hhdFNlcnZpY2UuZ2VuZXJhdGVDaGF0SWQoc2F2ZWRDaGF0SWQpO1xuICAgIH1cbiAgICB0aGlzLnJlc2V0Q2hhdCgpO1xuICAgIHRoaXMubWVzc2FnZXMkLm5leHQobWVzc2FnZXMpO1xuICAgIHRoaXMuY2hhdFNlcnZpY2UuY2hhdEhpc3RvcnkgPSBtZXNzYWdlcztcbiAgICBjb25zdCBsYXN0TWVzc2FnZSA9IG1lc3NhZ2VzLmF0KC0xKTtcbiAgICBpZihsYXN0TWVzc2FnZSAmJiBsYXN0TWVzc2FnZS5yb2xlID09PSAndXNlcicpIHtcbiAgICAgIHRoaXMuZmV0Y2gobWVzc2FnZXMpOyAvLyBJZiB0aGUgbGFzdCBtZXNzYWdlIGlmIGZyb20gYSB1c2VyLCBhbiBhbnN3ZXIgZnJvbSB0aGUgYXNzaXN0YW50IGlzIGV4cGVjdGVkXG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgdGhpcy51cGRhdGVEYXRhKG1lc3NhZ2VzKTsgLy8gSWYgdGhlIGxhc3QgbWVzc2FnZSBpZiBmcm9tIHRoZSBhc3Npc3RhbnQsIHdlIGNhbiBsb2FkIHRoZSBjb252ZXJzYXRpb24gcmlnaHQgYXdheVxuICAgICAgdGhpcy50ZXJtaW5hdGVGZXRjaCgpO1xuICAgIH1cbiAgICB0aGlzLl9hZGRTY3JvbGxMaXN0ZW5lcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc2V0IHRoZSBjaGF0IGJ5IGNsZWFyaW5nIHRoZSBjaGF0IGhpc3RvcnkgYW5kIHRoZSBVSSBhY2NvcmRpbmdseVxuICAgKiBUaGUgdXNlciBpbnB1dCB3aWxsIGJlIGNsZWFyZWRcbiAgICogVGhlIGZldGNoIHN1YnNjcmlwdGlvbiB3aWxsIGJlIHRlcm1pbmF0ZWRcbiAgICovXG4gIHJlc2V0Q2hhdCgpIHtcbiAgICBpZih0aGlzLm1lc3NhZ2VzJC52YWx1ZSkge1xuICAgICAgdGhpcy5tZXNzYWdlcyQubmV4dCh1bmRlZmluZWQpOyAvLyBSZXNldCBjaGF0XG4gICAgfVxuICAgIHRoaXMuY2hhdFNlcnZpY2UuY2hhdEhpc3RvcnkgPSB1bmRlZmluZWQ7IC8vIFJlc2V0IGNoYXQgaGlzdG9yeVxuICAgIHRoaXMucXVlc3Rpb24gPSAnJztcbiAgICB0aGlzLnRlcm1pbmF0ZUZldGNoKCk7XG4gIH1cblxuICAvKipcbiAgICogRmV0Y2ggYW5kIExvYWQgdGhlIHNhdmVkIGNoYXQgZnJvbSB0aGUgc2F2ZWQgY2hhdCBpbmRleC5cbiAgICogSWYgdGhlIHNhdmVkIGNoYXQgaXMgZm91bmQsIHRoZSBjaGF0IGRpc2N1c3Npb24gd2lsbCBiZSBsb2FkZWQgd2l0aCB0aGUgcHJvdmlkZWQgbWVzc2FnZXMgYW5kIGNoYXRJZFxuICAgKi9cbiAgb25Mb2FkQ2hhdCgpIHtcbiAgICB0aGlzLmxvYWRpbmckLm5leHQodHJ1ZSk7XG4gICAgdGhpcy5fc3ViLmFkZChcbiAgICAgIHRoaXMuY2hhdFNlcnZpY2UubG9hZFNhdmVkQ2hhdCRcbiAgICAgICAgLnBpcGUoXG4gICAgICAgICAgZmlsdGVyKHNhdmVkQ2hhdCA9PiAhIXNhdmVkQ2hhdCksXG4gICAgICAgICAgc3dpdGNoTWFwKHNhdmVkQ2hhdCA9PiB0aGlzLmNoYXRTZXJ2aWNlLmdldFNhdmVkQ2hhdChzYXZlZENoYXQhLmlkKSksXG4gICAgICAgICAgZmlsdGVyKHNhdmVkQ2hhdEhpc3RvcnkgPT4gISFzYXZlZENoYXRIaXN0b3J5KSxcbiAgICAgICAgICB0YXAoc2F2ZWRDaGF0SGlzdG9yeSA9PiB0aGlzLm9wZW5DaGF0KHNhdmVkQ2hhdEhpc3RvcnkhLmhpc3RvcnksIHNhdmVkQ2hhdEhpc3RvcnkhLmlkKSlcbiAgICAgICAgKS5zdWJzY3JpYmUoKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogU3RvcCB0aGUgZ2VuZXJhdGlvbiBvZiB0aGUgY3VycmVudCBhc3Npc3RhbnQncyBhbnN3ZXIuXG4gICAqIFRoZSBmZXRjaCBzdWJzY3JpcHRpb24gd2lsbCBiZSB0ZXJtaW5hdGVkLlxuICAgKi9cbiAgc3RvcEdlbmVyYXRpb24oKSB7XG4gICAgdGhpcy5jaGF0U2VydmljZS5zdG9wR2VuZXJhdGlvbigpLnN1YnNjcmliZShcbiAgICAgICgpID0+IHRoaXMudGVybWluYXRlRmV0Y2goKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogVGVybWluYXRlIHRoZSBmZXRjaCBwcm9jZXNzIGJ5IHVuc3Vic2NyaWJpbmcgZnJvbSB0aGUgZGF0YSBzdWJzY3JpcHRpb24gYW5kIHVwZGF0aW5nIHRoZSBsb2FkaW5nIHN0YXR1cyB0byBmYWxzZS5cbiAgICogQWRkaXRpb25hbGx5LCBmb2N1cyBvbiB0aGUgY2hhdCBpbnB1dCBpZiB0aGUgZm9jdXNBZnRlclJlc3BvbnNlIGZsYWcgaXMgc2V0IHRvIHRydWUuXG4gICAqL1xuICB0ZXJtaW5hdGVGZXRjaCgpIHtcbiAgICB0aGlzLl9kYXRhU3Vic2NyaXB0aW9uPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuX2RhdGFTdWJzY3JpcHRpb24gPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5sb2FkaW5nJC5uZXh0KGZhbHNlKTtcbiAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG5cbiAgICBpZiAodGhpcy5mb2N1c0FmdGVyUmVzcG9uc2UpIHtcbiAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICB0aGlzLnF1ZXN0aW9uSW5wdXQ/Lm5hdGl2ZUVsZW1lbnQuZm9jdXMoKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDb3B5IGEgcHJldmlvdXMgdXNlciBtZXNzYWdlIG9mIHRoZSBjaGF0IGhpc3RvcnkgdG8gdGhlIGNoYXQgdXNlciBpbnB1dC5cbiAgICogVGh1cywgdGhlIHVzZXIgY2FuIGVkaXQgYW5kIHJlc3VibWl0IHRoZSBtZXNzYWdlLlxuICAgKiBPbmNlIHRoZSBlZGl0ZWQgbWVzc2FnZSBpcyBzdWJtaXR0ZWQsIGFsbCBzdWJzZXF1ZW50IG1lc3NhZ2VzIHN0YXJ0aW5nIGZyb20gQHBhcmFtIGluZGV4IHdpbGwgYmUgcmVtb3ZlZCBmcm9tIHRoZSBoaXN0b3J5IGFuZCB0aGUgVUkgd2lsbCBiZSB1cGRhdGVkIGFjY29yZGluZ2x5LlxuICAgKiBUaGUgYXNzaXN0YW50IHdpbGwgcmVnZW5lcmF0ZSBhIG5ldyBhbnN3ZXIgYmFzZWQgb24gdGhlIHVwZGF0ZWQgY2hhdCBoaXN0b3J5LlxuICAgKiDimqDvuI8gSWYgdGhlIGFzc2lzdGFudCBpcyBzdHJlYW1pbmcgb3Igc3RvcHBpbmcgdGhlIGdlbmVyYXRpb24sIHRoZSBvcGVyYXRpb24gaXMgbm90IGFsbG93ZWQuXG4gICAqIEBwYXJhbSBpbmRleCBUaGUgaW5kZXggb2YgdGhlIHVzZXIncyBtZXNzYWdlIHRvIGVkaXRcbiAgICovXG4gIGVkaXRNZXNzYWdlKGluZGV4OiBudW1iZXIpIHtcbiAgICBpZiAoISF0aGlzLmNoYXRTZXJ2aWNlLnN0cmVhbWluZyQudmFsdWUgfHwgISF0aGlzLmNoYXRTZXJ2aWNlLnN0b3BwaW5nR2VuZXJhdGlvbiQudmFsdWUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5tZXNzYWdlVG9FZGl0ID0gaW5kZXg7XG4gICAgdGhpcy5yZW1hcHBlZE1lc3NhZ2VUb0VkaXQgPSB0aGlzLl9yZW1hcEluZGV4SW5DaGF0SGlzdG9yeShpbmRleCk7XG4gICAgdGhpcy5xdWVzdGlvbiA9IHRoaXMuY2hhdFNlcnZpY2UuY2hhdEhpc3RvcnkhW3RoaXMuX3JlbWFwSW5kZXhJbkNoYXRIaXN0b3J5KGluZGV4KV0uY29udGVudDtcbiAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQXVkaXRFdmVudCgnZWRpdC5jbGljaycsIHsncmFuayc6IHRoaXMuX3JlbWFwSW5kZXhJbkNoYXRIaXN0b3J5KGluZGV4KX0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENvcHkgYSBwcmV2aW91cyBhc3Npc3RhbnQgbWVzc2FnZSBvZiB0aGUgY2hhdCBoaXN0b3J5IHRvIHRoZSBjbGlwYm9hcmQuXG4gICAqIEBwYXJhbSBpbmRleCBUaGUgaW5kZXggb2YgdGhlIGFzc2lzdGFudCdzIG1lc3NhZ2UgdG8gZWRpdFxuICAgKi9cbiAgY29weU1lc3NhZ2UoaW5kZXg6IG51bWJlcikge1xuICAgIC8vIFJlbWFwIHRoZSBpbmRleCBpbiB0aGUgY2hhdCBoaXN0b3J5XG4gICAgY29uc3QgaWR4ID0gdGhpcy5fcmVtYXBJbmRleEluQ2hhdEhpc3RvcnkoaW5kZXgpO1xuICAgIHRoaXMuY2hhdFNlcnZpY2UuZ2VuZXJhdGVBdWRpdEV2ZW50KCdjb3B5LmNsaWNrJywgeydyYW5rJzogaWR4fSk7XG4gIH1cblxuICAvKipcbiAgICogU3RhcnRpbmcgZnJvbSB0aGUgcHJvdmlkZWQgaW5kZXgsIHJlbW92ZSBhbGwgc3Vic2VxdWVudCBtZXNzYWdlcyBmcm9tIHRoZSBjaGF0IGhpc3RvcnkgYW5kIHRoZSBVSSBhY2NvcmRpbmdseS5cbiAgICogVGhlIGFzc2lzdGFudCB3aWxsIHJlZ2VuZXJhdGUgYSBuZXcgYW5zd2VyIGJhc2VkIG9uIHRoZSB1cGRhdGVkIGNoYXQgaGlzdG9yeS5cbiAgICog4pqg77iPIElmIHRoZSBhc3Npc3RhbnQgaXMgc3RyZWFtaW5nIG9yIHN0b3BwaW5nIHRoZSBnZW5lcmF0aW9uLCB0aGUgb3BlcmF0aW9uIGlzIG5vdCBhbGxvd2VkLlxuICAgKiBAcGFyYW0gaW5kZXggVGhlIGluZGV4IG9mIHRoZSBhc3Npc3RhbnQncyBtZXNzYWdlIHRvIHJlZ2VuZXJhdGVcbiAgICovXG4gIHJlZ2VuZXJhdGVNZXNzYWdlKGluZGV4OiBudW1iZXIpIHtcbiAgICBpZiAoISF0aGlzLmNoYXRTZXJ2aWNlLnN0cmVhbWluZyQudmFsdWUgfHwgISF0aGlzLmNoYXRTZXJ2aWNlLnN0b3BwaW5nR2VuZXJhdGlvbiQudmFsdWUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gVXBkYXRlIHRoZSBtZXNzYWdlcyBpbiB0aGUgVUkgYnkgcmVtb3ZpbmcgYWxsIHN1YnNlcXVlbnQgJ2Fzc2lzdGFudCcgbWVzc2FnZXMgc3RhcnRpbmcgZnJvbSB0aGUgcHJvdmlkZWQgaW5kZXggdW50aWwgdGhlIGZpcnN0IHByZXZpb3VzICd1c2VyJyBtZXNzYWdlXG4gICAgbGV0IGkgPSBpbmRleDtcbiAgICB3aGlsZSAoaSA+PSAwICYmICh0aGlzLm1lc3NhZ2VzJC52YWx1ZSEpW2ldLnJvbGUgIT09ICd1c2VyJykge1xuICAgICAgaS0tO1xuICAgIH1cbiAgICAvLyBJdCBzaG91bGQgYWx3YXlzIGJlIHRoZSBjYXNlIHRoYXQgaSA+IDBcbiAgICBpZiAoaSA+PSAwKSB7XG4gICAgICB0aGlzLm1lc3NhZ2VzJC5uZXh0KHRoaXMubWVzc2FnZXMkLnZhbHVlIS5zbGljZSgwLCBpKzEpKTtcbiAgICAgIC8vIFJlbWFwIHRoZSBpbmRleCBvZiB0aGlzIGZvdW5kIGZpcnN0IHByZXZpb3VzICd1c2VyJyBtZXNzYWdlIGluIHRoZSBjaGF0IGhpc3RvcnlcbiAgICAgIGNvbnN0IGlkeCA9IHRoaXMuX3JlbWFwSW5kZXhJbkNoYXRIaXN0b3J5KGkpO1xuICAgICAgLy8gRGVmaW5lIGFuZCBVcGRhdGUgdGhlIGNoYXQgaGlzdG9yeSBiYXNlZCBvbiB3aGljaCB0aGUgYXNzaXN0YW50IHdpbGwgZ2VuZXJhdGUgYSBuZXcgYW5zd2VyXG4gICAgICB0aGlzLmNoYXRTZXJ2aWNlLmNoYXRIaXN0b3J5ID0gdGhpcy5jaGF0U2VydmljZS5jaGF0SGlzdG9yeSEuc2xpY2UoMCwgaWR4KzEpO1xuICAgICAgLy8gRmV0Y2ggdGhlIGFuc3dlclxuICAgICAgdGhpcy5mZXRjaCh0aGlzLmNoYXRTZXJ2aWNlLmNoYXRIaXN0b3J5KTtcbiAgICAgIHRoaXMuY2hhdFNlcnZpY2UuZ2VuZXJhdGVBdWRpdEV2ZW50KCdyZWdlbmVyYXRlLmNsaWNrJywgeydyYW5rJzogaWR4fSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlbWFwcyB0aGUgaW5kZXggaW4gdGhlIGNoYXQgaGlzdG9yeS5cbiAgICogVGhlIGNoYXQgaGlzdG9yeSBpcyBhIGxpc3Qgb2YgbWVzc2FnZXMgd2hlcmUgc29tZSBtZXNzYWdlcyBjYW4gYmUgaGlkZGVuIChkaXNwbGF5IHNldCB0byBmYWxzZSkuXG4gICAqIFRoZSBpbmRleCBwcm92aWRlZCBhcyBpbnB1dCBpcyB0aGUgaW5kZXggb2YgdGhlIG1lc3NhZ2UgaW4gdGhlIGNoYXQgaGlzdG9yeSBkaXNwbGF5ZWQgaW4gdGhlIFVJLlxuICAgKiBUaGlzIGZ1bmN0aW9uIHNob3VsZCBiZSByZW1vdmVkIG9uY2UgdGhlIGJhY2tlbmQgaXMgdXBkYXRlZCB0byBhZGQgdGhlIGlkcyBvZiB0aGUgbWVzc2FnZXMgaW4gdGhlIGNoYXQgaGlzdG9yeVxuICAgKiBAcGFyYW0gaW5kZXggLSBUaGUgaW5kZXggdG8gYmUgcmVtYXBwZWQuXG4gICAqL1xuICBwcml2YXRlIF9yZW1hcEluZGV4SW5DaGF0SGlzdG9yeShpbmRleDogbnVtYmVyKSB7XG4gICAgLy8gYSBjb3B5IG9mIHRoZSBjaGF0IGhpc3RvcnkgaXMgY3JlYXRlZCB0byBhdm9pZCBtb2RpZnlpbmcgdGhlIG9yaWdpbmFsIGNoYXQgaGlzdG9yeS5cbiAgICAvLyBBZGRpdGlvbmFsbHksIGEgcmFuayBpcyBnaXZpbmcgdG8gZWFjaCBtZXNzYWdlLlxuICAgIC8vIEFsbCBtZXNzYWdlcyBoYXZpbmcgcm9sZSAndXNlcicgYXJlIHVwZGF0ZWQgd2l0aCB0aGUgZGlzcGxheSBwcm9wZXJ0eSBzZXQgdG8gdHJ1ZSwgdGhpcyBpcyBtYW5kYXRvcnkgdG8gZ2V0IHRoZSBjb3JyZWN0IHJhbmsgb2YgdGhlIG1lc3NhZ2UgaW4gdGhlIGNoYXQgaGlzdG9yeSB3aGVuIHRoZSB1c2VyIG1lc3NhZ2UgdG8gcmVtYXAgaXMgaGlkZGVuXG4gICAgY29uc3QgaGlzdG9yeSA9IHRoaXMuY2hhdFNlcnZpY2UuY2hhdEhpc3RvcnkhXG4gICAgICAgICAgICAgICAgICAgICAgLnNsaWNlKClcbiAgICAgICAgICAgICAgICAgICAgICAubWFwKChtZXNzYWdlLCBpZHgpID0+ICh7Li4ubWVzc2FnZSwgYWRkaXRpb25hbFByb3BlcnRpZXM6IHsuLi5tZXNzYWdlLmFkZGl0aW9uYWxQcm9wZXJ0aWVzLCByYW5rOiBpZHh9fSkpXG4gICAgICAgICAgICAgICAgICAgICAgLm1hcCgobWVzc2FnZSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1lc3NhZ2Uucm9sZSA9PT0gXCJ1c2VyXCIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsuLi5tZXNzYWdlLCBhZGRpdGlvbmFsUHJvcGVydGllczogey4uLm1lc3NhZ2UuYWRkaXRpb25hbFByb3BlcnRpZXMsIGRpc3BsYXk6IHRydWV9fVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1lc3NhZ2U7XG4gICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAvLyBDb3VudCB0aGUgbnVtYmVyIG9mIGhpZGRlbiBtZXNzYWdlcyAob2Ygcm9sZSBkaWZmZXJlbnQgdGhlbiBcInVzZXJcIikgaW4gbWVzc2FnZXMkIGJlZm9yZSB0aGUgcHJvdmlkZWQgaW5kZXhcbiAgICAvLyBBbGwgbWVzc2FnZXMgaGF2aW5nIHJvbGUgJ3VzZXInIGFyZSB1cGRhdGVkIHdpdGggdGhlIGRpc3BsYXkgcHJvcGVydHkgc2V0IHRvIHRydWUsIHRoaXMgaXMgbWFuZGF0b3J5IHRvIGdldCB0aGUgY29ycmVjdCByYW5rIG9mIHRoZSBtZXNzYWdlIGluIHRoZSBjaGF0IGhpc3Rvcnkgd2hlbiB0aGUgdXNlciBtZXNzYWdlIHRvIHJlbWFwIGlzIGhpZGRlblxuICAgIC8vIFRoaXMgaXMgbWFuZGF0b3J5IHRvIGdldCB0aGUgY29ycmVjdCByYW5rIG9mIHRoZSBtZXNzYWdlIGluIHRoZSBjaGF0IGhpc3RvcnlcbiAgICAvLyBTaW5jZSBzb21lIGhpZGRlbiBtZXNzYWdlcyAobGlrZSAnc3lzdGVtJyBtZXNzYWdlcykgYXJlIG5vdCBkaXNwbGF5ZWQgaW4gdGhlIFVJIGJ1dCBoYXZlIGJlZW4gY291bnRlZCBpbiB0aGUgcHJvdmlkZWQgaW5kZXhcbiAgICBjb25zdCBudW1iZXJPZkhpZGRlbk1lc3NhZ2VzSW5NZXNzYWdlcyRCZWZvcmVJbmRleCA9IHRoaXMubWVzc2FnZXMkLnZhbHVlIVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnNsaWNlKDAsIGluZGV4KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLm1hcCgobWVzc2FnZSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobWVzc2FnZS5yb2xlID09PSBcInVzZXJcIikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB7Li4ubWVzc2FnZSwgYWRkaXRpb25hbFByb3BlcnRpZXM6IHsuLi5tZXNzYWdlLmFkZGl0aW9uYWxQcm9wZXJ0aWVzLCBkaXNwbGF5OiB0cnVlfX1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWVzc2FnZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuZmlsdGVyKG1lc3NhZ2UgPT4gIW1lc3NhZ2UuYWRkaXRpb25hbFByb3BlcnRpZXMuZGlzcGxheSkubGVuZ3RoO1xuICAgIC8vIHJlbW92ZSBhbGwgbWVzc2FnZXMgdGhhdCBoYXZlIGRpc3BsYXkgc2V0IHRvIGZhbHNlXG4gICAgLy8gdGhpcyBpcyBtYW5kYXRvcnkgc2luY2UgYXQgdGhlIHBvaW50IG9mIHRpbWUgd2hlbiB0aGUgYXNzaXN0YW50IGFuc3dlcnMgYSBxdWVzdGlvbixcbiAgICAvLyBpdCBtaWdodCBoYXZlIHNvbWUgaGlkZGVuIG1lc3NhZ2VzIChmb3IgZXhhbXBsZSBjb250ZXh0TWVzc2FnZXMpIHRoYXQgYXJlIGF2YWlsYWJsZSBpbiB0aGUgY2hhdCBoaXN0b3J5IGJ1dCBkb24ndCBmaWd1cmUgaW4gbWVzc2FnZXMkIHVubGVzcyBhIG5ldyBxdWVzdGlvbiBpcyBhc2tlZFxuICAgIGNvbnN0IGZpbHRlcmVkSGlzdG9yeSA9IGhpc3RvcnkuZmlsdGVyKG1lc3NhZ2UgPT4gbWVzc2FnZS5hZGRpdGlvbmFsUHJvcGVydGllcy5kaXNwbGF5KTtcbiAgICAvLyByZXR1cm4gdGhlIGluZGV4IG9mIHRoZSBtZXNzYWdlIGluIHRoZSBmaWx0ZXJlZCBoaXN0b3J5XG4gICAgcmV0dXJuIGZpbHRlcmVkSGlzdG9yeVtpbmRleCAtIG51bWJlck9mSGlkZGVuTWVzc2FnZXNJbk1lc3NhZ2VzJEJlZm9yZUluZGV4XS5hZGRpdGlvbmFsUHJvcGVydGllcy5yYW5rO1xuICB9XG5cbiAgLyoqXG4gICAqIEhhbmRsZXMgdGhlIGtleSB1cCBldmVudCBmb3IgJ0JhY2tzcGFjZScgYW5kICdFbnRlcicga2V5cy5cbiAgICogQHBhcmFtIGV2ZW50IC0gVGhlIGtleWJvYXJkIGV2ZW50LlxuICAgKi9cbiAgb25LZXlVcChldmVudDogS2V5Ym9hcmRFdmVudCk6IHZvaWQge1xuICAgIHN3aXRjaCAoZXZlbnQua2V5KSB7XG4gICAgICBjYXNlICdCYWNrc3BhY2UnOlxuICAgICAgICB0aGlzLmNhbGN1bGF0ZUhlaWdodCgpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ0VudGVyJzpcbiAgICAgICAgaWYgKCFldmVudC5zaGlmdEtleSkge1xuICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgdGhpcy5zdWJtaXRRdWVzdGlvbigpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuY2FsY3VsYXRlSGVpZ2h0KCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZXMgYW5kIGFkanVzdHMgdGhlIGhlaWdodCBvZiB0aGUgcXVlc3Rpb24gaW5wdXQgZWxlbWVudCBiYXNlZCBvbiBpdHMgY29udGVudC5cbiAgICogSWYgdGhlIEVudGVyIGtleSBpcyBwcmVzc2VkIHdpdGhvdXQgdGhlIFNoaWZ0IGtleSwgaXQgcHJldmVudHMgdGhlIGRlZmF1bHQgYmVoYXZpb3IuXG4gICAqIEBwYXJhbSBldmVudCBUaGUga2V5Ym9hcmQgZXZlbnRcbiAgICovXG4gIGNhbGN1bGF0ZUhlaWdodChldmVudD86IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoZXZlbnQ/LmtleSA9PT0gJ0VudGVyJyAmJiAhZXZlbnQuc2hpZnRLZXkpIHtcbiAgICAgIGV2ZW50Py5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH1cbiAgICBjb25zdCBtYXhIZWlnaHQgPSAxNzA7XG4gICAgY29uc3QgZWwgPSB0aGlzLnF1ZXN0aW9uSW5wdXQhLm5hdGl2ZUVsZW1lbnQ7XG4gICAgZWwuc3R5bGUubWF4SGVpZ2h0ID0gYCR7bWF4SGVpZ2h0fXB4YDtcbiAgICBlbC5zdHlsZS5oZWlnaHQgPSAnYXV0byc7XG4gICAgZWwuc3R5bGUuaGVpZ2h0ID0gYCR7ZWwuc2Nyb2xsSGVpZ2h0fXB4YDtcbiAgICBlbC5zdHlsZS5vdmVyZmxvd1kgPSBlbC5zY3JvbGxIZWlnaHQgPj0gbWF4SGVpZ2h0ID8gJ3Njcm9sbCcgOiAnaGlkZGVuJztcbiAgfVxuXG4gIC8qKlxuICAgKiBTZW5kIGEgXCJsaWtlXCIgZXZlbnQgb24gY2xpY2tpbmcgb24gdGhlIHRodW1iLXVwIGljb24gb2YgYW4gYXNzaXN0YW50J3MgbWVzc2FnZVxuICAgKiBAcGFyYW0gbWVzc2FnZSBUaGUgYXNzaXN0YW50IG1lc3NhZ2UgdG8gbGlrZVxuICAgKiBAcGFyYW0gcmFuayBUaGUgcmFuayBvZiB0aGUgbWVzc2FnZSB0byBsaWtlXG4gICAqL1xuICBvbkxpa2UobWVzc2FnZTogQ2hhdE1lc3NhZ2UsIHJhbms6IG51bWJlcik6IHZvaWQge1xuICAgIC8vIFJlbWFwIHRoZSBpbmRleCBpbiB0aGUgY2hhdCBoaXN0b3J5XG4gICAgY29uc3QgaWR4ID0gdGhpcy5fcmVtYXBJbmRleEluQ2hhdEhpc3RvcnkocmFuayk7XG4gICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ3RodW1iLXVwLmNsaWNrJywge3Jhbms6IGlkeH0pO1xuICAgIHRoaXMucmVwb3J0VHlwZSA9ICdsaWtlJztcbiAgICB0aGlzLm1lc3NhZ2VUb1JlcG9ydCA9IG1lc3NhZ2U7XG4gICAgdGhpcy5yZXBvcnRDb21tZW50ID0gdW5kZWZpbmVkO1xuICAgIHRoaXMucmVwb3J0UmFuayA9IHJhbms7XG4gICAgdGhpcy5zaG93UmVwb3J0ID0gdHJ1ZVxuXG4gICAgdGhpcy5jaGF0U2VydmljZS5jaGF0SGlzdG9yeSFbdGhpcy5fcmVtYXBJbmRleEluQ2hhdEhpc3RvcnkocmFuayldLmFkZGl0aW9uYWxQcm9wZXJ0aWVzLiRsaWtlZCA9IHRydWU7XG4gICAgdGhpcy5fdXBkYXRlQ2hhdEhpc3RvcnkoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZW5kIGEgXCJkaXNsaWtlXCIgZXZlbnQgb24gY2xpY2tpbmcgb24gdGhlIHRodW1iLWRvd24gaWNvbiBvZiBhbiBhc3Npc3RhbnQncyBtZXNzYWdlLlxuICAgKiBJdCBhbHNvIG9wZW5zIHRoZSBpc3N1ZSByZXBvcnRpbmcgZGlhbG9nLlxuICAgKiBAcGFyYW0gbWVzc2FnZSBUaGUgYXNzaXN0YW50IG1lc3NhZ2UgdG8gZGlzbGlrZVxuICAgKiBAcGFyYW0gaW5kZXggVGhlIHJhbmsgb2YgdGhlIG1lc3NhZ2UgdG8gZGlzbGlrZVxuICAgKi9cbiAgb25EaXNsaWtlKG1lc3NhZ2U6IENoYXRNZXNzYWdlLCByYW5rOiBudW1iZXIpOiB2b2lkIHtcbiAgICAvLyBSZW1hcCB0aGUgaW5kZXggaW4gdGhlIGNoYXQgaGlzdG9yeVxuICAgIGNvbnN0IGlkeCA9IHRoaXMuX3JlbWFwSW5kZXhJbkNoYXRIaXN0b3J5KHJhbmspO1xuICAgIHRoaXMuY2hhdFNlcnZpY2UuZ2VuZXJhdGVBdWRpdEV2ZW50KCd0aHVtYi1kb3duLmNsaWNrJywge3Jhbms6IGlkeH0pO1xuICAgIHRoaXMucmVwb3J0VHlwZSA9ICdkaXNsaWtlJztcbiAgICB0aGlzLm1lc3NhZ2VUb1JlcG9ydCA9IG1lc3NhZ2U7XG4gICAgdGhpcy5pc3N1ZVR5cGUgPSAnJztcbiAgICB0aGlzLnJlcG9ydENvbW1lbnQgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5yZXBvcnRSYW5rID0gcmFuaztcbiAgICB0aGlzLnNob3dSZXBvcnQgPSB0cnVlXG5cbiAgICB0aGlzLmNoYXRTZXJ2aWNlLmNoYXRIaXN0b3J5IVt0aGlzLl9yZW1hcEluZGV4SW5DaGF0SGlzdG9yeShyYW5rKV0uYWRkaXRpb25hbFByb3BlcnRpZXMuJGRpc2xpa2VkID0gdHJ1ZTtcbiAgICB0aGlzLl91cGRhdGVDaGF0SGlzdG9yeSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBfdXBkYXRlQ2hhdEhpc3RvcnkoKTogdm9pZCB7XG4gICAgdGhpcy5tZXNzYWdlcyQubmV4dCh0aGlzLmNoYXRTZXJ2aWNlLmNoYXRIaXN0b3J5KTtcbiAgICBpZiAodGhpcy5jaGF0U2VydmljZS5zYXZlZENoYXRJZCkge1xuICAgICAgdGhpcy5jaGF0U2VydmljZS51cGRhdGVTYXZlZENoYXQodGhpcy5jaGF0U2VydmljZS5zYXZlZENoYXRJZCwgdW5kZWZpbmVkLCB0aGlzLmNoYXRTZXJ2aWNlLmNoYXRIaXN0b3J5KS5zdWJzY3JpYmUoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVwb3J0IGFuIGlzc3VlIHJlbGF0ZWQgdG8gdGhlIGFzc2lzdGFudCdzIG1lc3NhZ2UuXG4gICAqL1xuICBzZW5kUmVwb3J0KCk6IHZvaWQge1xuICAgIGNvbnN0IGRldGFpbHMgPSB7XG4gICAgICAnY29tbWVudCc6IHRoaXMucmVwb3J0Q29tbWVudCxcbiAgICAgICd0ZXh0JzogdGhpcy5tZXNzYWdlVG9SZXBvcnQhLmNvbnRlbnQsXG4gICAgICAncmFuayc6IHRoaXMucmVwb3J0UmFuayxcbiAgICB9O1xuICAgIGlmICh0aGlzLnJlcG9ydFR5cGUgPT09ICdkaXNsaWtlJykge1xuICAgICAgZGV0YWlsc1sncmVwb3J0LXR5cGUnXSA9IHRoaXMuaXNzdWVUeXBlO1xuICAgICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ25lZ2F0aXZlLXJlcG9ydC5zZW5kJywgZGV0YWlscyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY2hhdFNlcnZpY2UuZ2VuZXJhdGVBdWRpdEV2ZW50KCdwb3NpdGl2ZS1yZXBvcnQuc2VuZCcsIGRldGFpbHMpO1xuICAgIH1cbiAgICB0aGlzLm5vdGlmaWNhdGlvbnNTZXJ2aWNlLnN1Y2Nlc3MoJ1lvdXIgcmVwb3J0IGhhcyBiZWVuIHN1Y2Nlc3NmdWxseSBzZW50Jyk7XG4gICAgdGhpcy5zaG93UmVwb3J0ID0gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogQ2xvc2UgdGhlIHJlcG9ydGluZyBkaWFsb2cuXG4gICAqL1xuICBpZ25vcmVSZXBvcnQoKTogdm9pZCB7XG4gICAgdGhpcy5zaG93UmVwb3J0ID0gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlIHRoZSBjbGljayBvbiBhIHJlZmVyZW5jZSdzICdvcGVuIHByZXZpZXcnLlxuICAgKiBAcGFyYW0gZGF0YVxuICAgKi9cbiAgb3BlbkF0dGFjaG1lbnRQcmV2aWV3KGRhdGE6IHtyZWZlcmVuY2U6IENoYXRDb250ZXh0QXR0YWNobWVudCwgcGFydElkPzogbnVtYmVyfSkge1xuICAgIHRoaXMub3BlblByZXZpZXcuZW1pdChkYXRhLnJlZmVyZW5jZSk7XG4gICAgY29uc3QgZGV0YWlscyA9IHtcbiAgICAgICdkb2MtaWQnOiBkYXRhLnJlZmVyZW5jZS5yZWNvcmRJZCxcbiAgICAgICd0aXRsZSc6IGRhdGEucmVmZXJlbmNlLnJlY29yZC50aXRsZSxcbiAgICAgICdzb3VyY2UnOiBkYXRhLnJlZmVyZW5jZS5yZWNvcmQudHJlZXBhdGgsXG4gICAgICAnY29sbGVjdGlvbic6IGRhdGEucmVmZXJlbmNlLnJlY29yZC5jb2xsZWN0aW9uLFxuICAgICAgJ2luZGV4JzogZGF0YS5yZWZlcmVuY2UucmVjb3JkLmRhdGFiYXNlYWxpYXMsXG4gICAgfTtcbiAgICBpZighIWRhdGEucGFydElkKSBkZXRhaWxzWydwYXJ0LWlkJ10gPSBkYXRhLnBhcnRJZDtcbiAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQXVkaXRFdmVudCgnYXR0YWNobWVudC5wcmV2aWV3LmNsaWNrJywgZGV0YWlscyk7XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlIHRoZSBjbGljayBvbiBhIHJlZmVyZW5jZSdzICdvcGVuIG9yaWdpbmFsIGRvY3VtZW50Jy5cbiAgICogQHBhcmFtIGRhdGFcbiAgICovXG4gIG9wZW5PcmlnaW5hbEF0dGFjaG1lbnQoZGF0YToge3JlZmVyZW5jZTogQ2hhdENvbnRleHRBdHRhY2htZW50LCBwYXJ0SWQ/OiBudW1iZXJ9KSB7XG4gICAgdGhpcy5vcGVuRG9jdW1lbnQuZW1pdChkYXRhLnJlZmVyZW5jZS5yZWNvcmQpO1xuICAgIGNvbnN0IGRldGFpbHMgPSB7XG4gICAgICAnZG9jLWlkJzogZGF0YS5yZWZlcmVuY2UucmVjb3JkSWQsXG4gICAgICAndGl0bGUnOiBkYXRhLnJlZmVyZW5jZS5yZWNvcmQudGl0bGUsXG4gICAgICAnc291cmNlJzogZGF0YS5yZWZlcmVuY2UucmVjb3JkLnRyZWVwYXRoLFxuICAgICAgJ2NvbGxlY3Rpb24nOiBkYXRhLnJlZmVyZW5jZS5yZWNvcmQuY29sbGVjdGlvbixcbiAgICAgICdpbmRleCc6IGRhdGEucmVmZXJlbmNlLnJlY29yZC5kYXRhYmFzZWFsaWFzLFxuICAgIH07XG4gICAgaWYoISFkYXRhLnBhcnRJZCkgZGV0YWlsc1sncGFydC1pZCddID0gZGF0YS5wYXJ0SWQ7XG4gICAgdGhpcy5jaGF0U2VydmljZS5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ2F0dGFjaG1lbnQubGluay5jbGljaycsIGRldGFpbHMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEhhbmRsZSB0aGUgY2xpY2sgb24gYSBzdWdnZXN0ZWQgYWN0aW9uLlxuICAgKiBAcGFyYW0gYWN0aW9uIFN1Z2dlc3RlZCBhY3Rpb24uXG4gICAqIEBwYXJhbSBpbmRleCBSYW5rIG9mIHRoZSBtZXNzYWdlIGluIHRoZSBjaGF0SGlzdG9yeSByZWxhdGVkIHRvIHRoZSBzdWdnZXN0ZWQgYWN0aW9uLlxuICAgKi9cbiAgc3VnZ2VzdEFjdGlvbkNsaWNrKGFjdGlvbjogU3VnZ2VzdGVkQWN0aW9uLCBpbmRleDogbnVtYmVyKSB7XG4gICAgdGhpcy5zdWdnZXN0QWN0aW9uLmVtaXQoYWN0aW9uKTtcbiAgICB0aGlzLmNoYXRTZXJ2aWNlLmdlbmVyYXRlQXVkaXRFdmVudCgnc3VnZ2VzdGVkQWN0aW9uLmNsaWNrJywgeyd0ZXh0JzogYWN0aW9uLmNvbnRlbnQsICdzdWdnZXN0ZWRBY3Rpb24tdHlwZSc6IGFjdGlvbi50eXBlfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBJdCBsb29rcyBmb3IgdGhlIGRlYnVnIG1lc3NhZ2VzIGF2YWlsYWJsZSBpbiB0aGUgY3VycmVudCBncm91cCBvZiBcImFzc2lzdGFudFwiIG1lc3NhZ2VzLlxuICAgKiBCeSBkZXNpZ24sIHRoZSBkZWJ1ZyBtZXNzYWdlcyBhcmUgb25seSBhdmFpbGFibGUgaW4gdGhlIGZpcnN0IHZpc2libGUgbWVzc2FnZSBhbW9uZyB0aGUgZ3JvdXAgXCJhc3Npc3RhbnRcIiBtZXNzYWdlcy5cbiAgICogQHBhcmFtIGluZGV4IFRoZSByYW5rIG9mIHRoZSBtZXNzYWdlXG4gICAqIEByZXR1cm5zIFRoZSBkZWJ1ZyBtZXNzYWdlcyBhdmFpbGFibGUgaW4gdGhlIGN1cnJlbnQgZ3JvdXAgb2YgXCJhc3Npc3RhbnRcIiBtZXNzYWdlc1xuICAgKi9cbiAgZ2V0RGVidWdNZXNzYWdlcyhpbmRleDogbnVtYmVyKTogRGVidWdNZXNzYWdlW10ge1xuICAgIC8vIElmIGl0IGlzIG5vdCBhbiBhc3Npc3RhbnQgbWVzc2FnZSwgcmV0dXJuXG4gICAgaWYgKCh0aGlzLm1lc3NhZ2VzJC52YWx1ZSEpW2luZGV4XS5yb2xlICE9PSAnYXNzaXN0YW50Jykge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICAvLyBHZXQgdGhlIGFycmF5IG9mIG1lc3NhZ2VzIHVwIHRvIHRoZSBpbmRpY2F0ZWQgaW5kZXhcbiAgICBjb25zdCBhcnJheSA9IHRoaXMubWVzc2FnZXMkLnZhbHVlIS5zbGljZSgwLCBpbmRleCArIDEpO1xuICAgIC8vIElmIGl0IGlzIGFuIGFzc2lzdGFudCBtZXNzYWdlLCBsb29rIGZvciB0aGUgZGVidWcgbWVzc2FnZXMgYXZhaWxhYmxlIGluIHRoZSBjdXJyZW50IGdyb3VwIG9mIFwiYXNzaXN0YW50XCIgbWVzc2FnZXNcbiAgICAvLyBCeSBkZXNpZ24sIHRoZSBkZWJ1ZyBtZXNzYWdlcyBhcmUgb25seSBhdmFpbGFibGUgaW4gdGhlIGZpcnN0IHZpc2libGUgbWVzc2FnZSBhbW9uZyB0aGUgZ3JvdXAgXCJhc3Npc3RhbnRcIiBtZXNzYWdlcy5cbiAgICBjb25zdCBpZHggPSB0aGlzLmNoYXRTZXJ2aWNlLmZpcnN0VmlzaWJsZUFzc2lzdGFudE1lc3NhZ2VJbmRleChhcnJheSk7XG4gICAgaWYgKGlkeCA+IC0xKSB7XG4gICAgICByZXR1cm4gKHRoaXMubWVzc2FnZXMkLnZhbHVlISlbaWR4XS5hZGRpdGlvbmFsUHJvcGVydGllcy4kZGVidWcgfHwgW107XG4gICAgfVxuICAgIHJldHVybiBbXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBIYW5kbGUgdGhlIGNsaWNrIG9uIHRoZSAnc2hvdyBsb2cgaW5mbycgYnV0dG9uIG9mIGEgbWVzc2FnZS5cbiAgICogQHBhcmFtIGluZGV4IFRoZSByYW5rIG9mIHRoZSBtZXNzYWdlXG4gICAqL1xuICBzaG93RGVidWcoaW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMuZGVidWdNZXNzYWdlcyA9IHRoaXMuZ2V0RGVidWdNZXNzYWdlcyhpbmRleCk7XG4gICAgdGhpcy5zaG93RGVidWdNZXNzYWdlcyA9IHRydWU7XG4gICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmeSB3aGV0aGVyIHRoZSBjdXJyZW50IG1lc3NhZ2UgaXMgYW4gYXNzaXN0YW50IG1lc3NhZ2UgYW5kIHRoYXQgYWxsIGZvbGxvd2luZyBtZXNzYWdlcyBhcmUgYXNzaXN0YW50IG9uZXNcbiAgICogVXNlZCB0byBrZWVwIHRoZSBcIlZpZXcgcHJvZ3Jlc3NcIiBvcGVuZWQgZXZlbiB0aG91Z2ggdGhlIGFzc2lzdGFudCBpcyBzZW5kaW5nIGFkZGl0aW9uYWwgbWVzc2FnZXMgYWZ0ZXIgdGhlIGN1cnJlbnQgb25lXG4gICAqIEBwYXJhbSBtZXNzYWdlcyB0aGUgbGlzdCBvZiBjdXJyZW50IG1lc3NhZ2VzXG4gICAqIEBwYXJhbSBpbmRleCB0aGUgaW5kZXggb2YgdGhlIGN1cnJlbnQgbWVzc2FnZVxuICAgKiBAcmV0dXJucyBpZiB0aGlzIG1lc3NhZ2VzIGFuZCB0aGUgZm9sbG93aW5nIG9uZXMgKGlmIGFueSkgYXJlIHRoZSBsYXN0IG9uZXNcbiAgICovXG4gIGlzQXNzaXN0YW50TGFzdE1lc3NhZ2VzKG1lc3NhZ2VzOiBDaGF0TWVzc2FnZVtdLCBpbmRleDogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgZm9yIChsZXQgaSA9IGluZGV4OyBpIDwgbWVzc2FnZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChtZXNzYWdlc1tpXS5yb2xlICE9PSAnYXNzaXN0YW50JykgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIGdpdmVuIG1lc3NhZ2UgaXMgYW4gZW1wdHkgYXNzaXN0YW50IG1lc3NhZ2UuXG4gICAqIEFuIGVtcHR5IGFzc2lzdGFudCBtZXNzYWdlIGlzIGRlZmluZWQgYXMgYSBtZXNzYWdlIHdpdGggdGhlIHJvbGUgJ2Fzc2lzdGFudCcsXG4gICAqIGFuIGVtcHR5IGNvbnRlbnQsIGFuZCBubyBhZGRpdGlvbmFsIHByb3BlcnRpZXMgc3VjaCBhcyBhdHRhY2htZW50cywgcHJvZ3Jlc3MsXG4gICAqIGRlYnVnIGluZm9ybWF0aW9uLCBvciBzdWdnZXN0ZWQgYWN0aW9ucy5cbiAgICpcbiAgICogQHBhcmFtIG1lc3NhZ2UgLSBUaGUgbWVzc2FnZSB0byBjaGVjay5cbiAgICogQHJldHVybnMgYHRydWVgIGlmIHRoZSBtZXNzYWdlIGlzIGFuIGVtcHR5IGFzc2lzdGFudCBtZXNzYWdlLCBgZmFsc2VgIG90aGVyd2lzZS5cbiAgICovXG4gIGlzRW1wdHlBc3Npc3RhbnRNZXNzYWdlKG1lc3NhZ2U6IENoYXRNZXNzYWdlIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gICAgaWYgKG1lc3NhZ2U/LnJvbGUgPT09ICdhc3Npc3RhbnQnXG4gICAgICAgICAgJiYgbWVzc2FnZT8uY29udGVudCA9PT0gXCJcIlxuICAgICAgICAgICYmICFtZXNzYWdlPy5hZGRpdGlvbmFsUHJvcGVydGllcz8uJGF0dGFjaG1lbnRcbiAgICAgICAgICAmJiAhbWVzc2FnZT8uYWRkaXRpb25hbFByb3BlcnRpZXM/LiRwcm9ncmVzc1xuICAgICAgICAgICYmICFtZXNzYWdlPy5hZGRpdGlvbmFsUHJvcGVydGllcz8uJGRlYnVnXG4gICAgICAgICAgJiYgIW1lc3NhZ2U/LmFkZGl0aW9uYWxQcm9wZXJ0aWVzPy4kc3VnZ2VzdGVkQWN0aW9uKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG4iLCI8bmctY29udGFpbmVyICpuZ0lmPVwiIWluaXRpYWxpemF0aW9uRXJyb3JcIj5cbiAgPGRpdiAqbmdJZj1cIm1lc3NhZ2VzJCB8IGFzeW5jIGFzIG1lc3NhZ2VzOyBlbHNlIGxvYWRpbmdUcGwgfHwgbG9hZGluZ1RwbERlZmF1bHRcIiBjbGFzcz1cImgtMTAwIGQtZmxleCBmbGV4LWNvbHVtblwiPlxuICAgIDwhLS0gVG9rZW4gY29uc3VtcHRpb24gLS0+XG4gICAgPGRpdiBjbGFzcz1cIm1zLTFcIiAqbmdJZj1cImNvbmZpZz8uZ2xvYmFsU2V0dGluZ3M/LmRpc3BsYXlVc2VyUXVvdGFDb25zdW1wdGlvbiB8fCBjb25maWc/Lmdsb2JhbFNldHRpbmdzPy5kaXNwbGF5Q2hhdFRva2Vuc0NvbnN1bXB0aW9uXCI+XG4gICAgICA8bmctY29udGFpbmVyICpuZ1RlbXBsYXRlT3V0bGV0PVwidG9rZW5Db25zdW1wdGlvblRwbCB8fCBkZWZhdWx0VG9rZW5Db25zdW1wdGlvblRwbDsgY29udGV4dDogeyAkaW1wbGljaXQ6IGluc3RhbmNlSWQgfVwiPjwvbmctY29udGFpbmVyPlxuICAgIDwvZGl2PlxuXG4gICAgPCEtLSBDaGF0IE1lc3NhZ2VzIC0tPlxuICAgIDx1bCBjbGFzcz1cImxpc3QtZ3JvdXAgbGlzdC1ncm91cC1mbHVzaCBvdmVyZmxvdy1hdXRvIGZsZXgtZ3Jvdy0xIHBlLTIgcGItMlwiICNtZXNzYWdlTGlzdD5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IG1lc3NhZ2Ugb2YgbWVzc2FnZXM7IGxldCBpbmRleCA9IGluZGV4OyBsZXQgbGFzdCA9IGxhc3RcIj5cbiAgICAgICAgPCEtLSBSZWd1bGFyIG1lc3NhZ2VzIC0tPlxuICAgICAgICA8bGkgY2xhc3M9XCJsaXN0LWdyb3VwLWl0ZW1cIlxuICAgICAgICAgICpuZ0lmPVwibWVzc2FnZS5hZGRpdGlvbmFsUHJvcGVydGllcy5kaXNwbGF5ICYmICFpc0VtcHR5QXNzaXN0YW50TWVzc2FnZShtZXNzYWdlKVwiXG4gICAgICAgICAgW3N0eWxlLi0tYnMtbGlzdC1ncm91cC1pdGVtLXBhZGRpbmcteS5yZW1dPVwiJzAuNidcIlxuICAgICAgICAgIFtjbGFzcy5vcGFjaXR5LTUwXT1cIm1lc3NhZ2VUb0VkaXQgJiYgKG1lc3NhZ2VUb0VkaXQgPCAoaW5kZXggKyAxKSlcIj5cbiAgICAgICAgICA8c3EtY2hhdC1tZXNzYWdlXG4gICAgICAgICAgICBbY2xhc3Muc3EtdXNlci1tZXNzYWdlXT1cIm1lc3NhZ2Uucm9sZSA9PT0gJ3VzZXInXCJcbiAgICAgICAgICAgIFtjbGFzcy5sYXN0LW1lc3NhZ2VdPVwibGFzdFwiXG4gICAgICAgICAgICBbbWVzc2FnZV09XCJtZXNzYWdlXCJcbiAgICAgICAgICAgIFtjb252ZXJzYXRpb25dPVwibWVzc2FnZXNcIlxuICAgICAgICAgICAgW3N1Z2dlc3RlZEFjdGlvbnNdPVwibGFzdCA/IG1lc3NhZ2UuYWRkaXRpb25hbFByb3BlcnRpZXMuJHN1Z2dlc3RlZEFjdGlvbiA6IHVuZGVmaW5lZFwiXG4gICAgICAgICAgICBbYXNzaXN0YW50TWVzc2FnZUljb25dPVwiYXNzaXN0YW50TWVzc2FnZUljb25cIlxuICAgICAgICAgICAgW3VzZXJNZXNzYWdlSWNvbl09XCJ1c2VyTWVzc2FnZUljb25cIlxuICAgICAgICAgICAgW2Nvbm5lY3Rpb25FcnJvck1lc3NhZ2VJY29uXT1cImNvbm5lY3Rpb25FcnJvck1lc3NhZ2VJY29uXCJcbiAgICAgICAgICAgIFtzZWFyY2hXYXJuaW5nTWVzc2FnZUljb25dPVwic2VhcmNoV2FybmluZ01lc3NhZ2VJY29uXCJcbiAgICAgICAgICAgIFtzdHJlYW1pbmddPVwiKGNoYXRTZXJ2aWNlLnN0cmVhbWluZyQgfCBhc3luYykgJiYgKGxhc3QgfHwgaXNBc3Npc3RhbnRMYXN0TWVzc2FnZXMobWVzc2FnZXMsIGluZGV4KSlcIlxuICAgICAgICAgICAgW2NhbkVkaXRdPVwiKGNoYXRTZXJ2aWNlLnN0cmVhbWluZyQgfCBhc3luYykgPT09IGZhbHNlICYmIG1lc3NhZ2VUb0VkaXQgPT09IHVuZGVmaW5lZCAmJiBtZXNzYWdlLnJvbGUgPT09ICd1c2VyJ1wiXG4gICAgICAgICAgICBbY2FuQ29weV09XCIoKGNoYXRTZXJ2aWNlLnN0cmVhbWluZyQgfCBhc3luYykgPT09IGZhbHNlIHx8ICFsYXN0KSAmJiBtZXNzYWdlVG9FZGl0ID09PSB1bmRlZmluZWQgJiYgbWVzc2FnZS5yb2xlICE9PSAnY29ubmVjdGlvbi1lcnJvcicgJiYgbWVzc2FnZS5yb2xlICE9PSAnc2VhcmNoLXdhcm5pbmcnXCJcbiAgICAgICAgICAgIFtjYW5MaWtlXT1cIigoY2hhdFNlcnZpY2Uuc3RyZWFtaW5nJCB8IGFzeW5jKSA9PT0gZmFsc2UgfHwgIWxhc3QpICYmIG1lc3NhZ2Uucm9sZSA9PT0gJ2Fzc2lzdGFudCdcIlxuICAgICAgICAgICAgW2NhbkRpc2xpa2VdPVwiKChjaGF0U2VydmljZS5zdHJlYW1pbmckIHwgYXN5bmMpID09PSBmYWxzZSB8fCAhbGFzdCkgJiYgbWVzc2FnZS5yb2xlID09PSAnYXNzaXN0YW50J1wiXG4gICAgICAgICAgICBbY2FuRGVidWddPVwiKCgoY2hhdFNlcnZpY2Uuc3RyZWFtaW5nJCB8IGFzeW5jKSA9PT0gZmFsc2UgJiYgbGFzdCkgfHwgKCFsYXN0ICYmIG1lc3NhZ2VzW2luZGV4KzFdLnJvbGUgIT09ICdhc3Npc3RhbnQnKSkgJiYgbWVzc2FnZS5yb2xlID09PSAnYXNzaXN0YW50JyAmJiAoZ2V0RGVidWdNZXNzYWdlcyhpbmRleCkubGVuZ3RoID4gMCkgJiYgKChpc0FkbWluT3JEZWxldGVkQWRtaW4gfHwgKGNoYXRTZXJ2aWNlLnVzZXJPdmVycmlkZSQgfCBhc3luYykpICYmIGNvbmZpZz8uZGVmYXVsdFZhbHVlcy5kZWJ1ZylcIlxuICAgICAgICAgICAgW2NhblJlZ2VuZXJhdGVdPVwiKGNoYXRTZXJ2aWNlLnN0cmVhbWluZyQgfCBhc3luYykgPT09IGZhbHNlICAmJiAobGFzdCB8fCAoIWxhc3QgJiYgbWVzc2FnZXNbaW5kZXgrMV0ucm9sZSAhPT0gJ2Fzc2lzdGFudCcpKSAgJiYgbWVzc2FnZS5yb2xlID09PSAnYXNzaXN0YW50JyAmJiBtZXNzYWdlVG9FZGl0ID09PSB1bmRlZmluZWRcIlxuICAgICAgICAgICAgKGVkaXQpPVwiZWRpdE1lc3NhZ2UoaW5kZXgpXCJcbiAgICAgICAgICAgIChjb3B5KT1cImNvcHlNZXNzYWdlKGluZGV4KVwiXG4gICAgICAgICAgICAocmVnZW5lcmF0ZSk9XCJyZWdlbmVyYXRlTWVzc2FnZShpbmRleClcIlxuICAgICAgICAgICAgKG9wZW5Eb2N1bWVudCk9XCJvcGVuT3JpZ2luYWxBdHRhY2htZW50KCRldmVudClcIlxuICAgICAgICAgICAgKG9wZW5QcmV2aWV3KT1cIm9wZW5BdHRhY2htZW50UHJldmlldygkZXZlbnQpXCJcbiAgICAgICAgICAgIChzdWdnZXN0QWN0aW9uKT1cInN1Z2dlc3RBY3Rpb25DbGljaygkZXZlbnQsIGluZGV4KVwiXG4gICAgICAgICAgICAobGlrZSk9XCJvbkxpa2UobWVzc2FnZSwgaW5kZXgpXCJcbiAgICAgICAgICAgIChkaXNsaWtlKT1cIm9uRGlzbGlrZShtZXNzYWdlLCBpbmRleClcIlxuICAgICAgICAgICAgKGRlYnVnKT1cInNob3dEZWJ1ZyhpbmRleClcIj5cbiAgICAgICAgICA8L3NxLWNoYXQtbWVzc2FnZT5cbiAgICAgICAgPC9saT5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPCEtLSBMb2FkaW5nIHNwaW5uZXIgLS0+XG4gICAgICA8bGkgY2xhc3M9XCJsaXN0LWdyb3VwLWl0ZW1cIiAqbmdJZj1cIihsb2FkaW5nJCB8IGFzeW5jKSA9PT0gdHJ1ZVwiPlxuICAgICAgICA8bmctY29udGFpbmVyICpuZ1RlbXBsYXRlT3V0bGV0PVwibG9hZGluZ1RwbCB8fCBsb2FkaW5nVHBsRGVmYXVsdFwiPjwvbmctY29udGFpbmVyPlxuICAgICAgPC9saT5cbiAgICA8L3VsPlxuXG4gICAgPCEtLSBSZXBvcnRpbmcgYSBmZWVkYmFjayBmb3JtIC0tPlxuICAgIDxkaXYgY2xhc3M9XCJpc3N1ZS1yZXBvcnQgcHQtMyBwYi0yXCIgKm5nSWY9XCJzaG93UmVwb3J0XCI+XG4gICAgICA8bmctY29udGFpbmVyICpuZ1RlbXBsYXRlT3V0bGV0PVwicmVwb3J0VHBsIHx8IHJlcG9ydFRwbERlZmF1bHQ7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBtZXNzYWdlVG9SZXBvcnQsIHJhbms6IHJlcG9ydFJhbmssIHR5cGU6IHJlcG9ydFR5cGUgfVwiPjwvbmctY29udGFpbmVyPlxuICAgIDwvZGl2PlxuXG4gICAgPCEtLSBVc2VyIHRleHQgaW5wdXQgLS0+XG4gICAgPGRpdiBjbGFzcz1cInVzZXItaW5wdXQgbXQtYXV0b1wiICpuZ0lmPVwiIXNob3dSZXBvcnRcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJweS0yXCI+XG4gICAgICAgIDxkaXYgW2hpZGRlbl09XCIhaXNDb25uZWN0ZWRcIj5cbiAgICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiZW5hYmxlZFVzZXJJbnB1dFwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImlucHV0VHBsXCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8IS0tIFJldHJ5IGJ1dHRvbiAtLT5cbiAgICAgICAgPGJ1dHRvbiBbaGlkZGVuXT1cImlzQ29ubmVjdGVkXCIgY2xhc3M9XCJidG4gbWItNCBhc3QtZXJyb3IgYXN0LWJ0biBzcS1yZXRyeVwiIChjbGljayk9XCJyZXRyeUZldGNoKClcIj5cbiAgICAgICAgICA8c3Bhbj5UcnkgYWdhaW48L3NwYW4+XG4gICAgICAgICAgPHNwYW4gKm5nSWY9XCJyZXRyaWFsQXR0ZW1wdHNcIiBjbGFzcz1cIm1zLTIgYXR0ZW1wdHNcIj57eyByZXRyaWFsQXR0ZW1wdHMgfX08L3NwYW4+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgICA8ZGl2IGNsYXNzPVwidGV4dC1lbmQgc21hbGwgdGV4dC1tdXRlZCBweC0zXCIgKm5nSWY9XCIhIWNvbmZpZz8uZ2xvYmFsU2V0dGluZ3M/LmRpc2NsYWltZXJcIj5cbiAgICAgICAgICB7eyBjb25maWc/Lmdsb2JhbFNldHRpbmdzPy5kaXNjbGFpbWVyIH19XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG5cbiAgICA8IS0tIEZsb2F0aW5nIHNjcm9sbCBidXR0b24gLS0+XG4gICAgPGRpdiAqbmdJZj1cIiFpc0F0Qm90dG9tICYmICFzaG93UmVwb3J0XCIgY2xhc3M9XCJzcS1mbG9hdGluZy1zY3JvbGxcIiBbbmdDbGFzc109XCJlbmFibGVkVXNlcklucHV0ID8gJ3NxLWZsb2F0aW5nLXNjcm9sbC0td2hlbi11c2VyLWlucHV0JyA6ICdzcS1mbG9hdGluZy1zY3JvbGwtLXdpdGhvdXQtdXNlci1pbnB1dCdcIj5cbiAgICAgIDxidXR0b24gY2xhc3M9XCJidG4gc2hhZG93XCIgKGNsaWNrKT1cInNjcm9sbERvd24oKVwiPlxuICAgICAgICA8aSBjbGFzcz1cImZhcyBmYS1hbmdsZS1kb3VibGUtZG93blwiPjwvaT5cbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cbjwvbmctY29udGFpbmVyPlxuXG48IS0tIE5HIFRFTVBMQVRFUy0tPlxuXG48bmctdGVtcGxhdGUgI2xvYWRpbmdUcGxEZWZhdWx0PlxuICA8ZGl2IGNsYXNzPVwic3Bpbm5lci1ncm93IHRleHQtcHJpbWFyeSBkLWJsb2NrIG14LWF1dG8gbXktNVwiIHJvbGU9XCJzdGF0dXNcIj5cbiAgICA8c3BhbiBjbGFzcz1cInZpc3VhbGx5LWhpZGRlblwiPkxvYWRpbmcuLi48L3NwYW4+XG4gIDwvZGl2PlxuPC9uZy10ZW1wbGF0ZT5cblxuPG5nLXRlbXBsYXRlICNpbnB1dFRwbD5cbiAgPGRpdiBjbGFzcz1cInB4LTMgcHktMVwiPlxuICAgIDxkaXYgY2xhc3M9XCJhc3QtaW5wdXQtY29udGFpbmVyXCI+XG4gICAgICA8YnV0dG9uIGRpc2FibGVkIGNsYXNzPVwiYnRuIGJ0bi1saWdodFwiPlxuICAgICAgICA8aSBjbGFzcz1cImZhcyBmYS1zZWFyY2hcIj48L2k+XG4gICAgICA8L2J1dHRvbj5cbiAgICAgIDx0ZXh0YXJlYSAjcXVlc3Rpb25JbnB1dCByb3dzPVwiMVwiXG4gICAgICAgIHR5cGU9XCJ0ZXh0XCIgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIlxuICAgICAgICBwbGFjZWhvbGRlcj1cIkFzayBzb21ldGhpbmdcIiBhdXRvZm9jdXNcbiAgICAgICAgWyhuZ01vZGVsKV09XCJxdWVzdGlvblwiXG4gICAgICAgIChrZXl1cCk9XCJvbktleVVwKCRldmVudClcIlxuICAgICAgICAoa2V5ZG93bik9XCJjYWxjdWxhdGVIZWlnaHQoJGV2ZW50KVwiXG4gICAgICAgIFtkaXNhYmxlZF09XCIobG9hZGluZyQgfCBhc3luYykgfHwgKGNoYXRTZXJ2aWNlLnN0cmVhbWluZyQgfCBhc3luYykgfHwgKGNoYXRTZXJ2aWNlLnN0b3BwaW5nR2VuZXJhdGlvbiQgfCBhc3luYylcIj5cbiAgICAgIDwvdGV4dGFyZWE+XG4gICAgICA8YnV0dG9uXG4gICAgICAgICpuZ0lmPVwiIShjaGF0U2VydmljZS5zdHJlYW1pbmckIHwgYXN5bmMpICYmICEobG9hZGluZyQgfCBhc3luYykgJiYgIShjaGF0U2VydmljZS5zdG9wcGluZ0dlbmVyYXRpb24kIHwgYXN5bmMpXCJcbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1saWdodCBtcy0yXCJcbiAgICAgICAgc3FUb29sdGlwPVwiU2VuZCBtZXNzYWdlXCJcbiAgICAgICAgKGNsaWNrKT1cInN1Ym1pdFF1ZXN0aW9uKClcIj5cbiAgICAgICAgPGkgY2xhc3M9XCJmYXMgZmEtcGFwZXItcGxhbmVcIj48L2k+XG4gICAgICA8L2J1dHRvbj5cbiAgICAgIDxidXR0b25cbiAgICAgICAgKm5nSWY9XCJtZXNzYWdlVG9FZGl0XCJcbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1saWdodCBtcy0yXCJcbiAgICAgICAgc3FUb29sdGlwPVwiQ2FuY2VsIGVkaXRpb25cIlxuICAgICAgICAoY2xpY2spPVwibWVzc2FnZVRvRWRpdCA9IHVuZGVmaW5lZDsgcXVlc3Rpb24gPSAnJ1wiPlxuICAgICAgICA8aSBjbGFzcz1cImZhcyBmYS11bmRvLWFsdFwiPjwvaT5cbiAgICAgIDwvYnV0dG9uPlxuICAgICAgPHNwYW4gKm5nSWY9XCIoY2hhdFNlcnZpY2Uuc3RyZWFtaW5nJCB8IGFzeW5jKSAmJiAhKGNoYXRTZXJ2aWNlLnN0b3BwaW5nR2VuZXJhdGlvbiQgfCBhc3luYylcIiBjbGFzcz1cInByb2Nlc3NpbmcgbXMtMlwiPlxuICAgICAgICBHZW5lcmF0aW5nIDxpIGNsYXNzPVwibXMtMSBmYXMgZmEtc3Bpbm5lciBmYS1wdWxzZVwiPjwvaT5cbiAgICAgIDwvc3Bhbj5cbiAgICAgIDxzcGFuICpuZ0lmPVwiKGNoYXRTZXJ2aWNlLnN0b3BwaW5nR2VuZXJhdGlvbiQgfCBhc3luYylcIiBjbGFzcz1cInByb2Nlc3NpbmcgbXMtMlwiPlxuICAgICAgICBTdG9wcGluZyA8aSBjbGFzcz1cIm1zLTEgZmFzIGZhLXNwaW5uZXIgZmEtcHVsc2VcIj48L2k+XG4gICAgICA8L3NwYW4+XG4gICAgICA8YnV0dG9uXG4gICAgICAgICpuZ0lmPVwiKGNoYXRTZXJ2aWNlLnN0cmVhbWluZyQgfCBhc3luYykgJiYgIShjaGF0U2VydmljZS5zdG9wcGluZ0dlbmVyYXRpb24kIHwgYXN5bmMpXCJcbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1saWdodCBtcy0yXCJcbiAgICAgICAgc3FUb29sdGlwPVwiU3RvcCBnZW5lcmF0aW5nXCJcbiAgICAgICAgKGNsaWNrKT1cInN0b3BHZW5lcmF0aW9uKClcIj5cbiAgICAgICAgPGkgY2xhc3M9XCJmYXMgZmEtc3RvcFwiPjwvaT5cbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cbjwvbmctdGVtcGxhdGU+XG5cbjxuZy10ZW1wbGF0ZSAjcmVwb3J0VHBsRGVmYXVsdCBsZXQtbWVzc2FnZSBsZXQtcmFuaz1cInJhbmtcIiBsZXQtdHlwZT1cInR5cGVcIj5cbiAgPGRpdiBjbGFzcz1cInB4LTNcIj5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwidHlwZSA9PT0gJ2Rpc2xpa2UnXCI+XG4gICAgICA8aDU+SXNzdWUgdHlwZTwvaDU+XG4gICAgICA8c2VsZWN0IGNsYXNzPVwiZm9ybS1zZWxlY3QgbWItNFwiIFsobmdNb2RlbCldPVwiaXNzdWVUeXBlXCI+XG4gICAgICAgIDxvcHRpb24gW3ZhbHVlXT1cIicnXCI+Q2hvb3NlIGFuIGlzc3VlIHR5cGU8L29wdGlvbj5cbiAgICAgICAgPG9wdGlvbiAqbmdGb3I9XCJsZXQgdHlwZSBvZiAoaXNzdWVUeXBlcyA/PyBkZWZhdWx0SXNzdWVUeXBlcylcIj57e3R5cGV9fTwvb3B0aW9uPlxuICAgICAgPC9zZWxlY3Q+XG4gICAgICA8aDU+V2hhdCB3YXMgdW5zYXRpc2Z5aW5nIGFib3V0IHRoaXMgcmVzcG9uc2U/IChvcHRpb25hbCk8L2g1PlxuICAgIDwvbmctY29udGFpbmVyPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJ0eXBlID09PSAnbGlrZSdcIj5cbiAgICAgIDxoNT5XaHkgZGlkIHlvdSBsaWtlIHRoaXMgYW5zd2VyPyAob3B0aW9uYWwpPC9oNT5cbiAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8dGV4dGFyZWEgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIiBbKG5nTW9kZWwpXT1cInJlcG9ydENvbW1lbnRcIiBwbGFjZWhvbGRlcj1cIldyaXRlIHlvdXIgY29tbWVudFwiPjwvdGV4dGFyZWE+XG4gICAgPGRpdiBjbGFzcz1cImQtZmxleCBmbGV4LXJvdy1yZXZlcnNlIGdhcC0xIG10LTJcIj5cbiAgICAgIDxidXR0b24gY2xhc3M9XCJidG4gYnRuLXByaW1hcnlcIiBbZGlzYWJsZWRdPVwidHlwZSA9PT0gJ2Rpc2xpa2UnICYmICFpc3N1ZVR5cGVcIiAoY2xpY2spPVwic2VuZFJlcG9ydCgpXCI+U2VuZDwvYnV0dG9uPlxuICAgICAgPGJ1dHRvbiBjbGFzcz1cImJ0biBidG4tbGlnaHRcIiAoY2xpY2spPVwiaWdub3JlUmVwb3J0KClcIj5EbyBub3Qgc2VuZCByZXBvcnQ8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG48L25nLXRlbXBsYXRlPlxuXG48bmctdGVtcGxhdGUgI2RlZmF1bHRUb2tlbkNvbnN1bXB0aW9uVHBsIGxldC1pbnN0YW5jZUlkPlxuICA8c3EtdG9rZW4tcHJvZ3Jlc3MtYmFyXG4gICAgW2luc3RhbmNlSWRdPVwiaW5zdGFuY2VJZFwiPlxuICA8L3NxLXRva2VuLXByb2dyZXNzLWJhcj5cbjwvbmctdGVtcGxhdGU+XG5cbjxkaXYgY2xhc3M9XCJkZWJ1Zy1tZXNzYWdlc1wiIFtjbGFzcy5kaXNwbGF5ZWRdPVwic2hvd0RlYnVnTWVzc2FnZXNcIj5cbiAgPGJ1dHRvbiAqbmdJZj1cInNob3dEZWJ1Z01lc3NhZ2VzXCIgY2xhc3M9XCJidG4gYnRuLWxpZ2h0IHNoYWRvdyBiYWNrLWJ0blwiIChjbGljayk9XCJzaG93RGVidWdNZXNzYWdlcz1mYWxzZVwiPlxuICAgIDxpIGNsYXNzPVwiZmFzIGZhLWNoZXZyb24tcmlnaHRcIj48L2k+XG4gIDwvYnV0dG9uPlxuICA8bmctY29udGFpbmVyICpuZ1RlbXBsYXRlT3V0bGV0PVwiZGVidWdNZXNzYWdlc1RwbCB8fCBkZWZhdWx0RGVidWdNZXNzYWdlc1RwbDsgY29udGV4dDogeyAkaW1wbGljaXQ6IGRlYnVnTWVzc2FnZXMgfVwiPlxuICA8L25nLWNvbnRhaW5lcj5cbjwvZGl2PlxuXG48bmctdGVtcGxhdGUgI2RlZmF1bHREZWJ1Z01lc3NhZ2VzVHBsIGxldC1kZWJ1Z01lc3NhZ2VzPlxuICA8c3EtZGVidWctbWVzc2FnZSBbZGF0YV09XCJkZWJ1Z01lc3NhZ2VzXCI+PC9zcS1kZWJ1Zy1tZXNzYWdlPlxuPC9uZy10ZW1wbGF0ZT5cbiJdfQ==