@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.
- package/chat/charts/chart/chart.component.d.ts +13 -13
- package/chat/chat-message/chat-message.component.d.ts +85 -81
- package/chat/chat-message/i18n/en.json +11 -0
- package/chat/chat-message/i18n/fr.json +11 -0
- package/chat/chat-reference/chat-reference.component.d.ts +14 -14
- package/chat/chat-reference/i18n/en.json +4 -0
- package/chat/chat-reference/i18n/fr.json +4 -0
- package/chat/chat-settings-v3/chat-settings-v3.component.d.ts +48 -50
- package/chat/chat-settings-v3/i18n/en.json +14 -0
- package/chat/chat-settings-v3/i18n/fr.json +14 -0
- package/chat/chat.component.d.ts +388 -1405
- package/chat/chat.service.d.ts +247 -228
- package/chat/debug-message/debug-message.component.d.ts +17 -17
- package/chat/debug-message/i18n/en.json +3 -0
- package/chat/debug-message/i18n/fr.json +3 -0
- package/chat/dialogs/delete-saved-chat.component.d.ts +22 -0
- package/chat/dialogs/i18n/en.json +19 -0
- package/chat/dialogs/i18n/fr.json +19 -0
- package/chat/dialogs/rename-saved-chat.component.d.ts +21 -0
- package/chat/dialogs/updates.component.d.ts +15 -0
- package/chat/documents-upload/document-list/document-list.component.d.ts +68 -77
- package/chat/documents-upload/document-overview/document-overview.component.d.ts +31 -41
- package/chat/documents-upload/document-upload/document-upload.component.d.ts +96 -98
- package/chat/documents-upload/documents-upload.model.d.ts +66 -66
- package/chat/documents-upload/documents-upload.service.d.ts +170 -174
- package/chat/documents-upload/i18n/en.json +24 -0
- package/chat/documents-upload/i18n/fr.json +24 -0
- package/chat/format-icon/format-icon.component.d.ts +10 -10
- package/chat/format-icon/icons.d.ts +5 -5
- package/chat/i18n/en.json +42 -0
- package/chat/i18n/fr.json +42 -0
- package/chat/index.d.ts +5 -5
- package/chat/initials-avatar/initials-avatar.component.d.ts +35 -35
- package/chat/instance-manager.service.d.ts +28 -28
- package/chat/pipes/message-content.pipe.d.ts +16 -0
- package/chat/prompt.component.d.ts +25 -21
- package/chat/public-api.d.ts +17 -17
- package/chat/references/i18n/en.json +6 -0
- package/chat/references/i18n/fr.json +6 -0
- package/chat/references/inline-image-reference.d.ts +21 -0
- package/chat/references/inline-page-reference.d.ts +21 -0
- package/chat/rest-chat.service.d.ts +31 -33
- package/chat/saved-chats/i18n/en.json +4 -0
- package/chat/saved-chats/i18n/fr.json +4 -0
- package/chat/saved-chats/saved-chats.component.d.ts +30 -36
- package/chat/services/app.service.d.ts +8 -0
- package/chat/services/dialog.service.d.ts +12 -0
- package/chat/services/notification.service.d.ts +10 -0
- package/chat/services/principal.service.d.ts +7 -0
- package/chat/services/search.service.d.ts +7 -0
- package/chat/services/signalR.web.service.d.ts +45 -0
- package/chat/services/ui.service.d.ts +13 -0
- package/chat/services/user-settings.service.d.ts +7 -0
- package/chat/token-progress-bar/i18n/en.json +4 -0
- package/chat/token-progress-bar/i18n/fr.json +4 -0
- package/chat/token-progress-bar/token-progress-bar.component.d.ts +24 -27
- package/chat/tooltip/tooltip.component.d.ts +12 -0
- package/chat/tooltip/tooltip.directive.d.ts +81 -0
- package/chat/types/message-content.types.d.ts +54 -0
- package/chat/types/message-reference.types.d.ts +11 -0
- package/chat/types.d.ts +913 -873
- package/chat/unified-plugins/embedded-image-reference.plugin.d.ts +3 -0
- package/chat/unified-plugins/embedded-page-reference.plugin.d.ts +3 -0
- package/chat/utils/assistant-json.d.ts +2 -0
- package/chat/websocket-chat.service.d.ts +102 -103
- package/esm2022/chat/charts/chart/chart.component.mjs +40 -0
- package/esm2022/chat/chat-message/chat-message.component.mjs +351 -0
- package/esm2022/chat/chat-reference/chat-reference.component.mjs +40 -0
- package/esm2022/chat/chat-settings-v3/chat-settings-v3.component.mjs +118 -0
- package/esm2022/chat/chat.component.mjs +1090 -0
- package/esm2022/chat/chat.service.mjs +417 -0
- package/esm2022/chat/debug-message/debug-message.component.mjs +43 -0
- package/esm2022/chat/dialogs/delete-saved-chat.component.mjs +81 -0
- package/esm2022/chat/dialogs/rename-saved-chat.component.mjs +84 -0
- package/esm2022/chat/dialogs/updates.component.mjs +61 -0
- package/esm2022/chat/documents-upload/document-list/document-list.component.mjs +140 -0
- package/esm2022/chat/documents-upload/document-overview/document-overview.component.mjs +65 -0
- package/esm2022/chat/documents-upload/document-upload/document-upload.component.mjs +256 -0
- package/{esm2020 → esm2022}/chat/documents-upload/documents-upload.model.mjs +1 -1
- package/esm2022/chat/documents-upload/documents-upload.service.mjs +291 -0
- package/{esm2020 → esm2022}/chat/format-icon/format-icon.component.mjs +23 -23
- package/{esm2020 → esm2022}/chat/format-icon/icons.mjs +137 -137
- package/{esm2020 → esm2022}/chat/initials-avatar/initials-avatar.component.mjs +60 -60
- package/esm2022/chat/instance-manager.service.mjs +46 -0
- package/esm2022/chat/pipes/message-content.pipe.mjs +34 -0
- package/esm2022/chat/prompt.component.mjs +88 -0
- package/{esm2020 → esm2022}/chat/public-api.mjs +18 -18
- package/esm2022/chat/references/inline-image-reference.mjs +110 -0
- package/esm2022/chat/references/inline-page-reference.mjs +110 -0
- package/esm2022/chat/rest-chat.service.mjs +296 -0
- package/esm2022/chat/saved-chats/saved-chats.component.mjs +82 -0
- package/esm2022/chat/services/app.service.mjs +19 -0
- package/esm2022/chat/services/dialog.service.mjs +40 -0
- package/esm2022/chat/services/notification.service.mjs +25 -0
- package/esm2022/chat/services/principal.service.mjs +16 -0
- package/esm2022/chat/services/search.service.mjs +13 -0
- package/esm2022/chat/services/signalR.web.service.mjs +79 -0
- package/esm2022/chat/services/ui.service.mjs +61 -0
- package/esm2022/chat/services/user-settings.service.mjs +22 -0
- package/{esm2020 → esm2022}/chat/sinequa-assistant-chat.mjs +4 -4
- package/esm2022/chat/token-progress-bar/token-progress-bar.component.mjs +52 -0
- package/esm2022/chat/tooltip/tooltip.component.mjs +44 -0
- package/esm2022/chat/tooltip/tooltip.directive.mjs +203 -0
- package/esm2022/chat/types/message-content.types.mjs +2 -0
- package/esm2022/chat/types/message-reference.types.mjs +2 -0
- package/esm2022/chat/types.mjs +130 -0
- package/esm2022/chat/unified-plugins/embedded-image-reference.plugin.mjs +57 -0
- package/esm2022/chat/unified-plugins/embedded-page-reference.plugin.mjs +57 -0
- package/esm2022/chat/utils/assistant-json.mjs +12 -0
- package/esm2022/chat/websocket-chat.service.mjs +654 -0
- package/{esm2020 → esm2022}/public-api.mjs +2 -2
- package/{esm2020 → esm2022}/sinequa-assistant.mjs +4 -4
- package/fesm2022/sinequa-assistant-chat.mjs +5340 -0
- package/fesm2022/sinequa-assistant-chat.mjs.map +1 -0
- package/{fesm2015 → fesm2022}/sinequa-assistant.mjs +3 -3
- package/index.d.ts +5 -5
- package/package.json +52 -25
- package/public-api.d.ts +1 -1
- package/chat/messages/de.d.ts +0 -4
- package/chat/messages/en.d.ts +0 -4
- package/chat/messages/fr.d.ts +0 -4
- package/chat/messages/index.d.ts +0 -4
- package/esm2020/chat/charts/chart/chart.component.mjs +0 -40
- package/esm2020/chat/chat-message/chat-message.component.mjs +0 -263
- package/esm2020/chat/chat-reference/chat-reference.component.mjs +0 -40
- package/esm2020/chat/chat-settings-v3/chat-settings-v3.component.mjs +0 -117
- package/esm2020/chat/chat.component.mjs +0 -1069
- package/esm2020/chat/chat.service.mjs +0 -333
- package/esm2020/chat/debug-message/debug-message.component.mjs +0 -43
- package/esm2020/chat/documents-upload/document-list/document-list.component.mjs +0 -191
- package/esm2020/chat/documents-upload/document-overview/document-overview.component.mjs +0 -80
- package/esm2020/chat/documents-upload/document-upload/document-upload.component.mjs +0 -258
- package/esm2020/chat/documents-upload/documents-upload.service.mjs +0 -289
- package/esm2020/chat/instance-manager.service.mjs +0 -46
- package/esm2020/chat/messages/de.mjs +0 -4
- package/esm2020/chat/messages/en.mjs +0 -4
- package/esm2020/chat/messages/fr.mjs +0 -4
- package/esm2020/chat/messages/index.mjs +0 -9
- package/esm2020/chat/prompt.component.mjs +0 -88
- package/esm2020/chat/rest-chat.service.mjs +0 -241
- package/esm2020/chat/saved-chats/saved-chats.component.mjs +0 -175
- package/esm2020/chat/token-progress-bar/token-progress-bar.component.mjs +0 -54
- package/esm2020/chat/types.mjs +0 -112
- package/esm2020/chat/websocket-chat.service.mjs +0 -641
- package/fesm2015/sinequa-assistant-chat.mjs +0 -4200
- package/fesm2015/sinequa-assistant-chat.mjs.map +0 -1
- package/fesm2020/sinequa-assistant-chat.mjs +0 -4171
- package/fesm2020/sinequa-assistant-chat.mjs.map +0 -1
- package/fesm2020/sinequa-assistant.mjs +0 -9
- package/fesm2020/sinequa-assistant.mjs.map +0 -1
- /package/{fesm2015 → fesm2022}/sinequa-assistant.mjs.map +0 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { Injectable, LOCALE_ID, inject } from "@angular/core";
|
|
2
|
+
import { TranslocoService } from "@jsverse/transloco";
|
|
3
|
+
import { differenceInDays, differenceInMonths, differenceInYears, endOfYesterday, format, isThisMonth, isThisQuarter, isThisWeek, isThisYear, isToday, isYesterday, parseISO, toDate } from "date-fns";
|
|
4
|
+
import { BehaviorSubject } from "rxjs";
|
|
5
|
+
import { Audit, fetchApp, fetchPrincipal, fetchUserSettings, guid, sha512 } from "@sinequa/atomic";
|
|
6
|
+
import { DialogUpdatesComponent } from "./dialogs/updates.component";
|
|
7
|
+
import { AppService } from "./services/app.service";
|
|
8
|
+
import { DialogService } from "./services/dialog.service";
|
|
9
|
+
import { NotificationsService } from "./services/notification.service";
|
|
10
|
+
import { PrincipalService } from "./services/principal.service";
|
|
11
|
+
import { UserSettingsWebService } from "./services/user-settings.service";
|
|
12
|
+
import { chatConfigSchema } from "./types";
|
|
13
|
+
import { getAssistantJsonFromCCApp } from "./utils/assistant-json";
|
|
14
|
+
import * as i0 from "@angular/core";
|
|
15
|
+
export class ChatService {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.localID = inject(LOCALE_ID, { optional: true });
|
|
18
|
+
/** Emit true once the initialization of the assistant process is done. */
|
|
19
|
+
this.initProcess$ = new BehaviorSubject(false);
|
|
20
|
+
/** Emit true once the initialization of the assistant config is done. */
|
|
21
|
+
this.initConfig$ = new BehaviorSubject(false);
|
|
22
|
+
/** Emit the global configuration of the assistant. */
|
|
23
|
+
this.assistantConfig$ = new BehaviorSubject(undefined);
|
|
24
|
+
/** Emit true if the user has been overridden, false otherwise. */
|
|
25
|
+
this.userOverride$ = new BehaviorSubject(undefined);
|
|
26
|
+
/**
|
|
27
|
+
* Emit true if the fetch of an assistant's response is ongoing (it includes Streaming status of the assistant endpoint AND saving the discussion if save Chat is enabled).
|
|
28
|
+
* This is used to prevent multiple fetches at the same time.
|
|
29
|
+
* Typically, there is no problem chaining fetches, but when forcing a reload after query changes cases, it can't be allowed because it breaks the whole business logic.
|
|
30
|
+
*/
|
|
31
|
+
this.streaming$ = new BehaviorSubject(false);
|
|
32
|
+
/** List of saved chats. */
|
|
33
|
+
this.savedChats$ = new BehaviorSubject([]);
|
|
34
|
+
/** Emit the saved chat to load. */
|
|
35
|
+
this.loadSavedChat$ = new BehaviorSubject(undefined);
|
|
36
|
+
/** Emit the quota each time the chat is invoked. */
|
|
37
|
+
this.quota$ = new BehaviorSubject(undefined);
|
|
38
|
+
/** Emit the calculated user's token consumption based on the quota. */
|
|
39
|
+
this.userTokenConsumption$ = new BehaviorSubject(undefined);
|
|
40
|
+
/** Emit the chat usage metrics each time the generation of the assistant response is completed. */
|
|
41
|
+
this.chatUsageMetrics$ = new BehaviorSubject(undefined);
|
|
42
|
+
/** Emit the calculated chat's token consumption based on the chat usage metrics. */
|
|
43
|
+
this.chatTokenConsumption$ = new BehaviorSubject(undefined);
|
|
44
|
+
/** Emit true if "CancelTasks" is ongoing. */
|
|
45
|
+
this.stoppingGeneration$ = new BehaviorSubject(false);
|
|
46
|
+
this.userSettingsService = inject(UserSettingsWebService);
|
|
47
|
+
this.notificationsService = inject(NotificationsService);
|
|
48
|
+
this.appService = inject(AppService);
|
|
49
|
+
this.modalService = inject(DialogService);
|
|
50
|
+
this.principalService = inject(PrincipalService);
|
|
51
|
+
this.transloco = inject(TranslocoService);
|
|
52
|
+
}
|
|
53
|
+
get assistants() {
|
|
54
|
+
if (!this.userSettingsService.userSettings)
|
|
55
|
+
this.userSettingsService.userSettings = {};
|
|
56
|
+
if (!this.userSettingsService.userSettings["assistants"])
|
|
57
|
+
this.userSettingsService.userSettings["assistants"] = {};
|
|
58
|
+
return this.userSettingsService.userSettings["assistants"];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the instance ID of the chat service
|
|
62
|
+
* @returns The instance ID of the chat service
|
|
63
|
+
*/
|
|
64
|
+
get chatInstanceId() {
|
|
65
|
+
return this._chatInstanceId;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Persist the instance ID of the chat service
|
|
69
|
+
* @param instanceId The instance ID of the chat service
|
|
70
|
+
*/
|
|
71
|
+
setChatInstanceId(instanceId) {
|
|
72
|
+
this._chatInstanceId = instanceId;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the ID of the current chat discussion which is used to save/get/delete it
|
|
76
|
+
* @returns The ID of the current chat discussion
|
|
77
|
+
*/
|
|
78
|
+
get savedChatId() {
|
|
79
|
+
return this._savedChatId;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Persist the ID of the current chat discussion which is used to save/get/delete it
|
|
83
|
+
* @param savedChatId The ID of the current chat discussion which is used to save/get/delete it
|
|
84
|
+
*/
|
|
85
|
+
setSavedChatId(savedChatId) {
|
|
86
|
+
this._savedChatId = savedChatId;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the ID of the current chat discussion which is used to identify audit events
|
|
90
|
+
* @returns The ID of the current chat discussion
|
|
91
|
+
*/
|
|
92
|
+
get chatId() {
|
|
93
|
+
return this._chatId;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Generate an GUID for the current chat discussion which is used to identify audit events
|
|
97
|
+
* If the discussion is saved, the savedChatId is initialized with the value of this chatId
|
|
98
|
+
* @param chatId if provided, it will be considered as the ID of the current chat discussion which is used to identify audit events
|
|
99
|
+
*/
|
|
100
|
+
generateChatId(chatId) {
|
|
101
|
+
this._chatId = chatId || guid();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Initialize the chat config by managing ONLY sub-object **defaultValues** configs of the standard app config (defined in the customization json tab ) and the user preferences.
|
|
105
|
+
* To do so, a tracking mechanism is implemented to notify the user about the available updates in the defaultValues object of the standard app config.
|
|
106
|
+
* The rest of the config object coming from "standard app config" is used as it is without any override.
|
|
107
|
+
* Thus, the user preferences are used only for the defaultValues object.
|
|
108
|
+
* This provide a centralized way to manage the rest of the config object by admins and ensure a unique common behavior for all users.
|
|
109
|
+
*/
|
|
110
|
+
async initChatConfig() {
|
|
111
|
+
// fetch the standard app config to get the defaultValues of the chat config for the given instance
|
|
112
|
+
// Persist the app in the app service
|
|
113
|
+
const capp = await fetchApp();
|
|
114
|
+
this.appService.app = capp;
|
|
115
|
+
const settings = await fetchUserSettings();
|
|
116
|
+
this.userSettingsService.userSettings = settings;
|
|
117
|
+
const key = this.chatInstanceId;
|
|
118
|
+
const userSettingsConfig = this.assistants[key] || {};
|
|
119
|
+
const principal = await fetchPrincipal();
|
|
120
|
+
this.principalService.principal = principal;
|
|
121
|
+
const standardChatConfig = getAssistantJsonFromCCApp(this.appService.app, key);
|
|
122
|
+
try {
|
|
123
|
+
// Validate the whole config object against the schema
|
|
124
|
+
chatConfigSchema.parse(standardChatConfig);
|
|
125
|
+
// If the user preferences do not contain a config's defaultValues object, keep using the standard app config and nothing to store in the user preferences
|
|
126
|
+
if (!userSettingsConfig.defaultValues) {
|
|
127
|
+
this.assistantConfig$.next({ ...standardChatConfig });
|
|
128
|
+
this.initConfig$.next(true);
|
|
129
|
+
}
|
|
130
|
+
else { // If the user has its own defaultValues in its userSettings, then we need to check for potential updates made by admins in the meantime and how he wants to manage them
|
|
131
|
+
// Retrieve already stored hashes in the user settings if exists
|
|
132
|
+
const appliedDefaultValuesHash = userSettingsConfig.hashes?.["applied-defaultValues-hash"];
|
|
133
|
+
const skippedDefaultValuesHash = userSettingsConfig.hashes?.["skipped-defaultValues-hash"];
|
|
134
|
+
// Create a hash of the current defaultValues of the standardChatConfig
|
|
135
|
+
const currentDefaultValuesHash = await sha512(JSON.stringify(standardChatConfig.defaultValues));
|
|
136
|
+
// Implement the tracking mechanism to notify the user about the available updates in the defaultValues object of the standard app config
|
|
137
|
+
const condition = (currentDefaultValuesHash !== appliedDefaultValuesHash) && (currentDefaultValuesHash !== skippedDefaultValuesHash);
|
|
138
|
+
if (condition) {
|
|
139
|
+
this.modalService.open(DialogUpdatesComponent)
|
|
140
|
+
.then(res => {
|
|
141
|
+
if (res === "dialog-confirm") {
|
|
142
|
+
const hashes = { ...userSettingsConfig.hashes, "applied-defaultValues-hash": currentDefaultValuesHash, "skipped-defaultValues-hash": undefined };
|
|
143
|
+
// Update the chat config and store its defaultValues in the user preferences
|
|
144
|
+
this.updateChatConfig({ ...standardChatConfig }, hashes, true);
|
|
145
|
+
this.initConfig$.next(true);
|
|
146
|
+
this.generateAuditEvent("ast-configuration.click", { 'configuration': JSON.stringify({ ...standardChatConfig }) });
|
|
147
|
+
}
|
|
148
|
+
else if (res === "dialog-no") {
|
|
149
|
+
// Do not notify the user about changes while this skipped version is not updated
|
|
150
|
+
const hashes = { ...userSettingsConfig.hashes, "skipped-defaultValues-hash": currentDefaultValuesHash };
|
|
151
|
+
this.updateChatConfig({ ...standardChatConfig, defaultValues: userSettingsConfig.defaultValues }, hashes, false);
|
|
152
|
+
this.initConfig$.next(true);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// Just pick the version in the user settings, nothing to be updated
|
|
156
|
+
this.assistantConfig$.next({ ...standardChatConfig, defaultValues: userSettingsConfig.defaultValues });
|
|
157
|
+
this.initConfig$.next(true);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
else { // No available updates Or updates has been already skipped, then just pick the version in the user settings
|
|
162
|
+
this.assistantConfig$.next({ ...standardChatConfig, defaultValues: userSettingsConfig.defaultValues });
|
|
163
|
+
this.initConfig$.next(true);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
this.notificationsService.error(`Missing valid configuration for the assistant instance '${key}'. See the browser console messages for details on the missing or incorrect properties.`);
|
|
169
|
+
throw new Error(`Missing valid configuration for the assistant instance '${key}' . \n ${JSON.stringify(error.issues, null, 2)}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Update the chat config and store its defaultValues in the user preferences
|
|
174
|
+
* @param config The updated chat config
|
|
175
|
+
* @param hashes The updated hashes to store in the user preferences
|
|
176
|
+
* @param notify Whether to notify the user about the update
|
|
177
|
+
* @param successCallback The callback to execute if the update is successful
|
|
178
|
+
* @param errorCallback The callback to execute if the update fails
|
|
179
|
+
*/
|
|
180
|
+
updateChatConfig(config, hashes, notify = true, successCallback, errorCallback) {
|
|
181
|
+
this.assistantConfig$.next(config);
|
|
182
|
+
const assistants = Object.assign({}, this.assistants);
|
|
183
|
+
assistants[this.chatInstanceId] = { ...assistants[this.chatInstanceId], defaultValues: config.defaultValues };
|
|
184
|
+
if (hashes)
|
|
185
|
+
assistants[this.chatInstanceId].hashes = hashes;
|
|
186
|
+
this.userSettingsService.patch({ assistants }).subscribe(next => { }, error => {
|
|
187
|
+
if (notify) {
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
189
|
+
errorCallback
|
|
190
|
+
? errorCallback()
|
|
191
|
+
: this.notificationsService.error(this.transloco.translate('chat.saveChatConfig.fail', { value: this.chatInstanceId }));
|
|
192
|
+
}
|
|
193
|
+
console.error("Could not patch assistants!", error);
|
|
194
|
+
}, () => {
|
|
195
|
+
if (notify) {
|
|
196
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
197
|
+
successCallback
|
|
198
|
+
? successCallback()
|
|
199
|
+
: this.notificationsService.success(this.transloco.translate('chat.saveChatConfig.success', { value: this.chatInstanceId }));
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* A handler for quota updates each time the chat is invoked.
|
|
205
|
+
* It emits the updated quota to the quota$ subject, emits accordingly the updated user's tokens consumption and notifies the user if the max quota is reached.
|
|
206
|
+
* @param quota The updated quota
|
|
207
|
+
* @param propagateError Whether to propagate the error to the caller
|
|
208
|
+
*/
|
|
209
|
+
updateQuota(quota, propagateError = false) {
|
|
210
|
+
this.quota$.next(quota);
|
|
211
|
+
const nextResetDate = this.formatDateTime(quota.nextResetUTC + "+00:00"); // This +00:00 is to ensure dates will be properly converted to local time
|
|
212
|
+
const consumptionPercentage = Math.round((quota.tokenCount * 100 / quota.periodTokens) * 100) / 100;
|
|
213
|
+
this.userTokenConsumption$.next({ percentage: consumptionPercentage, nextResetDate });
|
|
214
|
+
if (quota.maxQuotaReached) {
|
|
215
|
+
this.generateAuditEvent('ast-quota.exceeded', {});
|
|
216
|
+
const msg = `Sorry, you have exceeded the allowed quota. Please retry starting from ${nextResetDate}.`;
|
|
217
|
+
this.notificationsService.error(msg);
|
|
218
|
+
if (propagateError)
|
|
219
|
+
throw new Error(msg);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* A handler for chat usage metrics each time the generation of the assistant response is completed.
|
|
224
|
+
* It emits the chat usage metrics to the chatUsageMetrics$ subject, emits accordingly the updated chat's tokens consumption
|
|
225
|
+
* @param chatUsageMetrics The chat usage metrics
|
|
226
|
+
*/
|
|
227
|
+
updateChatUsageMetrics(chatUsageMetrics) {
|
|
228
|
+
this.chatUsageMetrics$.next(chatUsageMetrics);
|
|
229
|
+
const currentModel = this.getModel(this.assistantConfig$.value.defaultValues.service_id, this.assistantConfig$.value.defaultValues.model_id);
|
|
230
|
+
const consumptionPercentage = Math.round((chatUsageMetrics.totalTokenCount * 100 / (currentModel.contextWindowSize - currentModel.maxGenerationSize)) * 100) / 100;
|
|
231
|
+
this.chatTokenConsumption$.next({ percentage: consumptionPercentage });
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get the model description for the given (serviceId + modelId)
|
|
235
|
+
* If a model is not found, an error message is returned
|
|
236
|
+
* @param serviceId The serviceId of the model
|
|
237
|
+
* @param modelId The modelId of the model
|
|
238
|
+
* @returns The model description
|
|
239
|
+
*/
|
|
240
|
+
getModel(serviceId, modelId) {
|
|
241
|
+
const model = this.models?.find(m => m.serviceId === serviceId && m.modelId === modelId);
|
|
242
|
+
// Handle obsolete config
|
|
243
|
+
if (!model) {
|
|
244
|
+
this.notificationsService.error(`FATAL ERROR : The model (serviceId = '${serviceId}', modelId = '${modelId}') is no longer available. Please contact an admin for further information.`);
|
|
245
|
+
throw new Error(`FATAL ERROR : The model (serviceId = '${serviceId}', modelId = '${modelId}') is no longer available`);
|
|
246
|
+
}
|
|
247
|
+
return model;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Generate an audit event with the given type and details. The generated audit event is sent afterwards via the AuditWebService
|
|
251
|
+
* @param type Audit event type
|
|
252
|
+
* @param details Audit event details
|
|
253
|
+
* @param id Actions (savedChat delete/rename/...) may occur on a specific chat different than the current one stored in this service, so the chat id can be provided
|
|
254
|
+
*/
|
|
255
|
+
async generateAuditEvent(type, details, id) {
|
|
256
|
+
const baseDetails = {
|
|
257
|
+
"url": decodeURIComponent(window.location.href),
|
|
258
|
+
"app": this.appService.appName,
|
|
259
|
+
"user-id": this.principalService.principal?.userId,
|
|
260
|
+
"instance-id": this.chatInstanceId,
|
|
261
|
+
"chat-id": id || this.chatId,
|
|
262
|
+
"service-id": this.assistantConfig$.value.defaultValues.service_id,
|
|
263
|
+
"model-id": this.assistantConfig$.value.defaultValues.model_id,
|
|
264
|
+
"is-user-input": false
|
|
265
|
+
};
|
|
266
|
+
const audit = {
|
|
267
|
+
type,
|
|
268
|
+
detail: {
|
|
269
|
+
...baseDetails,
|
|
270
|
+
...details
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const response = await Audit.notify(audit);
|
|
274
|
+
console.log("Audit response", response);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Traverse the array from the end and track the first 'assistant' message among the last group of "assistant" messages where display is true
|
|
278
|
+
* @param array The array of ChatMessage to traverse
|
|
279
|
+
* @returns The index of the first visible assistant message among the last group of "assistant" messages in the array
|
|
280
|
+
*/
|
|
281
|
+
firstVisibleAssistantMessageIndex(array) {
|
|
282
|
+
if (!array) {
|
|
283
|
+
return -1;
|
|
284
|
+
}
|
|
285
|
+
let index = array.length - 1;
|
|
286
|
+
let firstVisibleAssistantMessageIndex = -1;
|
|
287
|
+
while (index >= 0 && array[index].role === 'assistant') {
|
|
288
|
+
if (array[index].additionalProperties.display === true) {
|
|
289
|
+
firstVisibleAssistantMessageIndex = index;
|
|
290
|
+
}
|
|
291
|
+
index--;
|
|
292
|
+
}
|
|
293
|
+
return firstVisibleAssistantMessageIndex;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Traverse the array from the end and pick the last 'assistant' message among the last group of "assistant" messages where display is true
|
|
297
|
+
* @param array The array of ChatMessage to traverse
|
|
298
|
+
* @returns The index of the last visible assistant message among the last group of "assistant" messages in the array
|
|
299
|
+
*/
|
|
300
|
+
lastVisibleAssistantMessageIndex(array) {
|
|
301
|
+
if (!array) {
|
|
302
|
+
return -1;
|
|
303
|
+
}
|
|
304
|
+
let index = array.length - 1;
|
|
305
|
+
let lastVisibleAssistantMessageIndex = -1;
|
|
306
|
+
while (index >= 0 && array[index].role === 'assistant') {
|
|
307
|
+
if (array[index].additionalProperties.display === true) {
|
|
308
|
+
lastVisibleAssistantMessageIndex = index;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
index--;
|
|
312
|
+
}
|
|
313
|
+
return lastVisibleAssistantMessageIndex;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Format a date string in UTC to a local date string
|
|
317
|
+
* @param value Date string in UTC to format
|
|
318
|
+
* @returns A formatted local date string
|
|
319
|
+
*/
|
|
320
|
+
formatDateTime(value) {
|
|
321
|
+
const localDate = toDate(parseISO(value));
|
|
322
|
+
const formatter = new Intl.DateTimeFormat(undefined, {
|
|
323
|
+
year: 'numeric',
|
|
324
|
+
month: 'short',
|
|
325
|
+
day: 'numeric',
|
|
326
|
+
hour: '2-digit',
|
|
327
|
+
minute: '2-digit',
|
|
328
|
+
second: '2-digit',
|
|
329
|
+
timeZoneName: 'short'
|
|
330
|
+
});
|
|
331
|
+
return formatter.format(localDate);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Takes a text prompt that may contain placeholders for variables
|
|
335
|
+
* and replaces these placeholders if it finds a match in the given
|
|
336
|
+
* context object.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* const p = "Hello, [[user.name]]! You have [[user.notifications.length]] new notifications.";
|
|
340
|
+
* const context = {
|
|
341
|
+
* user: {
|
|
342
|
+
* name: "Alice",
|
|
343
|
+
* notifications: ["Message from Bob", "Reminder for meeting"]
|
|
344
|
+
* }
|
|
345
|
+
* };
|
|
346
|
+
* const formattedPrompt = formatPrompt(p, context);
|
|
347
|
+
* console.log(formattedPrompt); // Output: "Hello, Alice! You have 2 new notifications."
|
|
348
|
+
*/
|
|
349
|
+
static formatPrompt(prompt, context) {
|
|
350
|
+
return prompt.replace(/\[\[(.*?)\]\]/g, (match, expr) => {
|
|
351
|
+
// Simple dot notation resolver
|
|
352
|
+
const keys = expr.trim().split(".");
|
|
353
|
+
let value = context;
|
|
354
|
+
for (const key of keys) {
|
|
355
|
+
if (value && typeof value === "object" && key in value) {
|
|
356
|
+
value = value[key];
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
return match;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return value ?? match;
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Determines a time-based key for a given date to be used for translations or formatting.
|
|
367
|
+
* The key represents a relative time period such as "today", "yesterday", "this week", etc.
|
|
368
|
+
* If the date does not fall into any predefined relative time period, it returns the year as a string.
|
|
369
|
+
*
|
|
370
|
+
* @param date - The date for which the time key is to be determined.
|
|
371
|
+
* @returns A string representing the time key, which can be used for translation or display purposes.
|
|
372
|
+
*/
|
|
373
|
+
getTimeKey(date) {
|
|
374
|
+
if (isToday(date)) {
|
|
375
|
+
return this.transloco.translate('chat.today');
|
|
376
|
+
}
|
|
377
|
+
else if (isYesterday(date)) {
|
|
378
|
+
return this.transloco.translate('chat.yesterday');
|
|
379
|
+
}
|
|
380
|
+
else if (isThisWeek(date)) {
|
|
381
|
+
return this.transloco.translate('chat.thisWeek');
|
|
382
|
+
}
|
|
383
|
+
else if (differenceInDays(endOfYesterday(), date) <= 7) {
|
|
384
|
+
return this.transloco.translate('chat.lastWeek');
|
|
385
|
+
}
|
|
386
|
+
else if (isThisMonth(date)) {
|
|
387
|
+
return this.transloco.translate('chat.thisMonth');
|
|
388
|
+
}
|
|
389
|
+
else if (differenceInMonths(endOfYesterday(), date) <= 1) {
|
|
390
|
+
return this.transloco.translate('chat.lastMonth');
|
|
391
|
+
}
|
|
392
|
+
else if (isThisQuarter(date)) {
|
|
393
|
+
return this.transloco.translate('chat.thisQuarter');
|
|
394
|
+
}
|
|
395
|
+
else if (differenceInMonths(endOfYesterday(), date) <= 3) {
|
|
396
|
+
return this.transloco.translate('chat.lastQuarter');
|
|
397
|
+
}
|
|
398
|
+
else if (isThisYear(date)) {
|
|
399
|
+
return this.transloco.translate('chat.thisYear');
|
|
400
|
+
}
|
|
401
|
+
else if (differenceInYears(endOfYesterday(), date) === 1) {
|
|
402
|
+
return this.transloco.translate('chat.lastYear');
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
return format(date, 'yyyy');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
getCurrentLocaleName() {
|
|
409
|
+
return this.localID || '';
|
|
410
|
+
}
|
|
411
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
412
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ChatService }); }
|
|
413
|
+
}
|
|
414
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ChatService, decorators: [{
|
|
415
|
+
type: Injectable
|
|
416
|
+
}] });
|
|
417
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhdC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvYXNzaXN0YW50L2NoYXQvY2hhdC5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM5RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN0RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsa0JBQWtCLEVBQUUsaUJBQWlCLEVBQUUsY0FBYyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLFdBQVcsRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQ3ZNLE9BQU8sRUFBRSxlQUFlLEVBQWMsTUFBTSxNQUFNLENBQUM7QUFFbkQsT0FBTyxFQUFFLEtBQUssRUFBUyxRQUFRLEVBQUUsY0FBYyxFQUFFLGlCQUFpQixFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUUxRyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNyRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDcEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzFELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQzFFLE9BQU8sRUFBdU4sZ0JBQWdCLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDaFEsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sd0JBQXdC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Q0FBQyxjQUFjLEVBQUUsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUMzRCxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBQ25ELENBQUM7YUFBTSxDQUFDO1lBQ04sT0FBTyxNQUFNLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQzlCLENBQUM7SUFDSCxDQUFDO0lBRUgsb0JBQW9CO1FBQ2xCLE9BQU8sSUFBSSxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUM7SUFDNUIsQ0FBQzsrR0FoZm1CLFdBQVc7bUhBQVgsV0FBVzs7NEZBQVgsV0FBVztrQkFEaEMsVUFBVSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIExPQ0FMRV9JRCwgaW5qZWN0IH0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7IFRyYW5zbG9jb1NlcnZpY2UgfSBmcm9tIFwiQGpzdmVyc2UvdHJhbnNsb2NvXCI7XG5pbXBvcnQgeyBkaWZmZXJlbmNlSW5EYXlzLCBkaWZmZXJlbmNlSW5Nb250aHMsIGRpZmZlcmVuY2VJblllYXJzLCBlbmRPZlllc3RlcmRheSwgZm9ybWF0LCBpc1RoaXNNb250aCwgaXNUaGlzUXVhcnRlciwgaXNUaGlzV2VlaywgaXNUaGlzWWVhciwgaXNUb2RheSwgaXNZZXN0ZXJkYXksIHBhcnNlSVNPLCB0b0RhdGUgfSBmcm9tIFwiZGF0ZS1mbnNcIjtcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgT2JzZXJ2YWJsZSB9IGZyb20gXCJyeGpzXCI7XG5cbmltcG9ydCB7IEF1ZGl0LCBRdWVyeSwgZmV0Y2hBcHAsIGZldGNoUHJpbmNpcGFsLCBmZXRjaFVzZXJTZXR0aW5ncywgZ3VpZCwgc2hhNTEyIH0gZnJvbSBcIkBzaW5lcXVhL2F0b21pY1wiO1xuXG5pbXBvcnQgeyBEaWFsb2dVcGRhdGVzQ29tcG9uZW50IH0gZnJvbSBcIi4vZGlhbG9ncy91cGRhdGVzLmNvbXBvbmVudFwiO1xuaW1wb3J0IHsgQXBwU2VydmljZSB9IGZyb20gXCIuL3NlcnZpY2VzL2FwcC5zZXJ2aWNlXCI7XG5pbXBvcnQgeyBEaWFsb2dTZXJ2aWNlIH0gZnJvbSBcIi4vc2VydmljZXMvZGlhbG9nLnNlcnZpY2VcIjtcbmltcG9ydCB7IE5vdGlmaWNhdGlvbnNTZXJ2aWNlIH0gZnJvbSBcIi4vc2VydmljZXMvbm90aWZpY2F0aW9uLnNlcnZpY2VcIjtcbmltcG9ydCB7IFByaW5jaXBhbFNlcnZpY2UgfSBmcm9tIFwiLi9zZXJ2aWNlcy9wcmluY2lwYWwuc2VydmljZVwiO1xuaW1wb3J0IHsgVXNlclNldHRpbmdzV2ViU2VydmljZSB9IGZyb20gXCIuL3NlcnZpY2VzL3VzZXItc2V0dGluZ3Muc2VydmljZVwiO1xuaW1wb3J0IHsgQ2hhdENvbmZpZywgQ2hhdE1lc3NhZ2UsIENoYXRSZXNwb25zZSwgQ2hhdFVzYWdlTWV0cmljcywgRGVsZXRlU2F2ZWRDaGF0UmVzcG9uc2UsIEdsbG1GdW5jdGlvbiwgR2xsbU1vZGVsRGVzY3JpcHRpb24sIFF1b3RhLCBTYXZlZENoYXQsIFNhdmVkQ2hhdEhpc3RvcnksIFNhdmVkQ2hhdFJlc3BvbnNlLCBUb2tlbkNvbnN1bXB0aW9uLCBVc2VyVG9rZW5Db25zdW1wdGlvbiwgY2hhdENvbmZpZ1NjaGVtYSB9IGZyb20gXCIuL3R5cGVzXCI7XG5pbXBvcnQgeyBnZXRBc3Npc3RhbnRKc29uRnJvbUNDQXBwIH0gZnJvbSBcIi4vdXRpbHMvYXNzaXN0YW50LWpzb25cIjtcblxuQEluamVjdGFibGUoKVxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIENoYXRTZXJ2aWNlIHtcblxuICByZWFkb25seSBsb2NhbElEID0gaW5qZWN0KExPQ0FMRV9JRCwge29wdGlvbmFsOiB0cnVlfSk7XG5cbiAgLyoqIE5hbWUgb2YgdGhlIGFzc2lzdGFudCBwbHVnaW4gT1Igd2Vic29ja2V0IGVuZHBvaW50LiAqL1xuICBSRVFVRVNUX1VSTDogc3RyaW5nO1xuICAvKiogRW1pdCB0cnVlIG9uY2UgdGhlIGluaXRpYWxpemF0aW9uIG9mIHRoZSBhc3Npc3RhbnQgcHJvY2VzcyBpcyBkb25lLiAqL1xuICBpbml0UHJvY2VzcyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgLyoqIEVtaXQgdHJ1ZSBvbmNlIHRoZSBpbml0aWFsaXphdGlvbiBvZiB0aGUgYXNzaXN0YW50IGNvbmZpZyBpcyBkb25lLiAqL1xuICBpbml0Q29uZmlnJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICAvKiogRW1pdCB0aGUgZ2xvYmFsIGNvbmZpZ3VyYXRpb24gb2YgdGhlIGFzc2lzdGFudC4gKi9cbiAgYXNzaXN0YW50Q29uZmlnJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Q2hhdENvbmZpZyB8IHVuZGVmaW5lZD4odW5kZWZpbmVkKTtcbiAgLyoqIEVtaXQgdHJ1ZSBpZiB0aGUgdXNlciBoYXMgYmVlbiBvdmVycmlkZGVuLCBmYWxzZSBvdGhlcndpc2UuICovXG4gIHVzZXJPdmVycmlkZSQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4gfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG4gIC8qKlxuICAgKiBFbWl0IHRydWUgaWYgdGhlIGZldGNoIG9mIGFuIGFzc2lzdGFudCdzIHJlc3BvbnNlIGlzIG9uZ29pbmcgKGl0IGluY2x1ZGVzIFN0cmVhbWluZyBzdGF0dXMgb2YgdGhlIGFzc2lzdGFudCBlbmRwb2ludCBBTkQgc2F2aW5nIHRoZSBkaXNjdXNzaW9uIGlmIHNhdmUgQ2hhdCBpcyBlbmFibGVkKS5cbiAgICogVGhpcyBpcyB1c2VkIHRvIHByZXZlbnQgbXVsdGlwbGUgZmV0Y2hlcyBhdCB0aGUgc2FtZSB0aW1lLlxuICAgKiBUeXBpY2FsbHksIHRoZXJlIGlzIG5vIHByb2JsZW0gY2hhaW5pbmcgZmV0Y2hlcywgYnV0IHdoZW4gZm9yY2luZyBhIHJlbG9hZCBhZnRlciBxdWVyeSBjaGFuZ2VzIGNhc2VzLCBpdCBjYW4ndCBiZSBhbGxvd2VkIGJlY2F1c2UgaXQgYnJlYWtzIHRoZSB3aG9sZSBidXNpbmVzcyBsb2dpYy5cbiAgKi9cbiAgc3RyZWFtaW5nJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICAvKiogU3RvcmUgdGhlIG1lc3NhZ2VzIGhpc3Rvcnkgb2YgdGhlIGN1cnJlbnQgY2hhdC4gKi9cbiAgY2hhdEhpc3Rvcnk6IENoYXRNZXNzYWdlW10gfCB1bmRlZmluZWQ7XG4gIC8qKiBMaXN0IG9mIG1vZGVscyBhdmFpbGFibGUgb24gdGhlIHNlcnZlci4gKi9cbiAgbW9kZWxzOiBHbGxtTW9kZWxEZXNjcmlwdGlvbltdIHwgdW5kZWZpbmVkO1xuICAvKiogTGlzdCBvZiBmdW5jdGlvbnMgYXZhaWxhYmxlIG9uIHRoZSBzZXJ2ZXIuICovXG4gIGZ1bmN0aW9uczogR2xsbUZ1bmN0aW9uW10gfCB1bmRlZmluZWQ7XG4gIC8qKiBMaXN0IG9mIHNhdmVkIGNoYXRzLiAqL1xuICBzYXZlZENoYXRzJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8U2F2ZWRDaGF0W10+KFtdKTtcbiAgLyoqIEVtaXQgdGhlIHNhdmVkIGNoYXQgdG8gbG9hZC4gKi9cbiAgbG9hZFNhdmVkQ2hhdCQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PFNhdmVkQ2hhdCB8IHVuZGVmaW5lZD4odW5kZWZpbmVkKTtcbiAgLyoqIEVtaXQgdGhlIHF1b3RhIGVhY2ggdGltZSB0aGUgY2hhdCBpcyBpbnZva2VkLiAqL1xuICBxdW90YSQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PFF1b3RhIHwgdW5kZWZpbmVkPih1bmRlZmluZWQpO1xuICAvKiogRW1pdCB0aGUgY2FsY3VsYXRlZCB1c2VyJ3MgdG9rZW4gY29uc3VtcHRpb24gYmFzZWQgb24gdGhlIHF1b3RhLiAqL1xuICB1c2VyVG9rZW5Db25zdW1wdGlvbiQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PFVzZXJUb2tlbkNvbnN1bXB0aW9uIHwgdW5kZWZpbmVkPih1bmRlZmluZWQpO1xuICAvKiogRW1pdCB0aGUgY2hhdCB1c2FnZSBtZXRyaWNzIGVhY2ggdGltZSB0aGUgZ2VuZXJhdGlvbiBvZiB0aGUgYXNzaXN0YW50IHJlc3BvbnNlIGlzIGNvbXBsZXRlZC4gKi9cbiAgY2hhdFVzYWdlTWV0cmljcyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PENoYXRVc2FnZU1ldHJpY3MgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG4gIC8qKiBFbWl0IHRoZSBjYWxjdWxhdGVkIGNoYXQncyB0b2tlbiBjb25zdW1wdGlvbiBiYXNlZCBvbiB0aGUgY2hhdCB1c2FnZSBtZXRyaWNzLiAqL1xuICBjaGF0VG9rZW5Db25zdW1wdGlvbiQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PFRva2VuQ29uc3VtcHRpb24gfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG4gIC8qKiBFbWl0IHRydWUgaWYgXCJDYW5jZWxUYXNrc1wiIGlzIG9uZ29pbmcuICovXG4gIHN0b3BwaW5nR2VuZXJhdGlvbiQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgLyoqIEluc3RhbmNlIElEIG9mIHRoZSBjaGF0IHNlcnZpY2UgZGVmaW5pbmcgdGhlIGFzc2lzdGFudCBpbnN0YW5jZS4gKi9cbiAgcHJpdmF0ZSBfY2hhdEluc3RhbmNlSWQ6IHN0cmluZztcbiAgLyoqIElEIG9mIHRoZSBjdXJyZW50ICoqc2F2ZWQgY2hhdCoqIGRpc2N1c3Npb24gd2hpY2ggaXMgdXNlZCB0byB1cGRhdGUvZ2V0L2RlbGV0ZSBpdC4gKi9cbiAgcHJpdmF0ZSBfc2F2ZWRDaGF0SWQ6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgLyoqIEdlbmVyYXRlZCBHVUlEIGZvciB0aGUgY3VycmVudCBub24tc2F2ZWQgY2hhdCBkaXNjdXNzaW9uIHVzZWQgdG8gaWRlbnRpZnkgYXVkaXQgZXZlbnRzLlxuICAgKiBJZiB0aGUgY2hhdCBpcyBzYXZlZCwgdGhlIHNhdmVkQ2hhdElkIGlzIGluaXRpYWxpemVkIHdpdGggdGhlIHZhbHVlIG9mIHRoaXMgY2hhdElkLlxuICAgKi9cbiAgcHJpdmF0ZSBfY2hhdElkOiBzdHJpbmc7XG5cbiAgcHVibGljIHVzZXJTZXR0aW5nc1NlcnZpY2UgPSBpbmplY3QoVXNlclNldHRpbmdzV2ViU2VydmljZSk7XG4gIHB1YmxpYyBub3RpZmljYXRpb25zU2VydmljZSA9IGluamVjdChOb3RpZmljYXRpb25zU2VydmljZSk7XG4gIHB1YmxpYyBhcHBTZXJ2aWNlID0gaW5qZWN0KEFwcFNlcnZpY2UpO1xuICBwdWJsaWMgbW9kYWxTZXJ2aWNlID0gaW5qZWN0KERpYWxvZ1NlcnZpY2UpO1xuICBwdWJsaWMgcHJpbmNpcGFsU2VydmljZSA9IGluamVjdChQcmluY2lwYWxTZXJ2aWNlKTtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHRyYW5zbG9jbyA9IGluamVjdChUcmFuc2xvY29TZXJ2aWNlKTtcbiAgLyoqXG4gICAqIEluaXRpYWxpemUgdGhlIGNoYXQgcHJvY2Vzc1xuICAgKi9cbiAgYWJzdHJhY3QgaW5pdCgpOiBPYnNlcnZhYmxlPGJvb2xlYW4+O1xuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplIHRoZSBSRVFVRVNUX1VSTFxuICAgKi9cbiAgYWJzdHJhY3QgZ2V0UmVxdWVzdHNVcmwoKTogdm9pZDtcblxuICBnZXQgYXNzaXN0YW50cygpOiBhbnkge1xuICAgIGlmICghdGhpcy51c2VyU2V0dGluZ3NTZXJ2aWNlLnVzZXJTZXR0aW5ncylcbiAgICAgIHRoaXMudXNlclNldHRpbmdzU2VydmljZS51c2VyU2V0dGluZ3MgPSB7fTtcbiAgICBpZiAoIXRoaXMudXNlclNldHRpbmdzU2VydmljZS51c2VyU2V0dGluZ3NbXCJhc3Npc3RhbnRzXCJdKVxuICAgICAgdGhpcy51c2VyU2V0dGluZ3NTZXJ2aWNlLnVzZXJTZXR0aW5nc1tcImFzc2lzdGFudHNcIl0gPSB7fTtcbiAgICByZXR1cm4gIHRoaXMudXNlclNldHRpbmdzU2VydmljZS51c2VyU2V0dGluZ3NbXCJhc3Npc3RhbnRzXCJdO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgaW5zdGFuY2UgSUQgb2YgdGhlIGNoYXQgc2VydmljZVxuICAgKiBAcmV0dXJucyBUaGUgaW5zdGFuY2UgSUQgb2YgdGhlIGNoYXQgc2VydmljZVxuICAgKi9cbiAgZ2V0IGNoYXRJbnN0YW5jZUlkKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2NoYXRJbnN0YW5jZUlkO1xuICB9XG5cbiAgLyoqXG4gICAqIFBlcnNpc3QgdGhlIGluc3RhbmNlIElEIG9mIHRoZSBjaGF0IHNlcnZpY2VcbiAgICogQHBhcmFtIGluc3RhbmNlSWQgVGhlIGluc3RhbmNlIElEIG9mIHRoZSBjaGF0IHNlcnZpY2VcbiAgICovXG4gIHNldENoYXRJbnN0YW5jZUlkKGluc3RhbmNlSWQ6IHN0cmluZykge1xuICAgIHRoaXMuX2NoYXRJbnN0YW5jZUlkID0gaW5zdGFuY2VJZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIElEIG9mIHRoZSBjdXJyZW50IGNoYXQgZGlzY3Vzc2lvbiB3aGljaCBpcyB1c2VkIHRvIHNhdmUvZ2V0L2RlbGV0ZSBpdFxuICAgKiBAcmV0dXJucyBUaGUgSUQgb2YgdGhlIGN1cnJlbnQgY2hhdCBkaXNjdXNzaW9uXG4gICAqL1xuICBnZXQgc2F2ZWRDaGF0SWQoKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5fc2F2ZWRDaGF0SWQ7XG4gIH1cblxuICAvKipcbiAgICogUGVyc2lzdCB0aGUgSUQgb2YgdGhlIGN1cnJlbnQgY2hhdCBkaXNjdXNzaW9uIHdoaWNoIGlzIHVzZWQgdG8gc2F2ZS9nZXQvZGVsZXRlIGl0XG4gICAqIEBwYXJhbSBzYXZlZENoYXRJZCBUaGUgSUQgb2YgdGhlIGN1cnJlbnQgY2hhdCBkaXNjdXNzaW9uIHdoaWNoIGlzIHVzZWQgdG8gc2F2ZS9nZXQvZGVsZXRlIGl0XG4gICAqL1xuICBzZXRTYXZlZENoYXRJZChzYXZlZENoYXRJZDogc3RyaW5nIHwgdW5kZWZpbmVkKSB7XG4gICAgdGhpcy5fc2F2ZWRDaGF0SWQgPSBzYXZlZENoYXRJZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIElEIG9mIHRoZSBjdXJyZW50IGNoYXQgZGlzY3Vzc2lvbiB3aGljaCBpcyB1c2VkIHRvIGlkZW50aWZ5IGF1ZGl0IGV2ZW50c1xuICAgKiBAcmV0dXJucyBUaGUgSUQgb2YgdGhlIGN1cnJlbnQgY2hhdCBkaXNjdXNzaW9uXG4gICAqL1xuICBnZXQgY2hhdElkKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2NoYXRJZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBhbiBHVUlEIGZvciB0aGUgY3VycmVudCBjaGF0IGRpc2N1c3Npb24gd2hpY2ggaXMgdXNlZCB0byBpZGVudGlmeSBhdWRpdCBldmVudHNcbiAgICogSWYgdGhlIGRpc2N1c3Npb24gaXMgc2F2ZWQsIHRoZSBzYXZlZENoYXRJZCBpcyBpbml0aWFsaXplZCB3aXRoIHRoZSB2YWx1ZSBvZiB0aGlzIGNoYXRJZFxuICAgKiBAcGFyYW0gY2hhdElkIGlmIHByb3ZpZGVkLCBpdCB3aWxsIGJlIGNvbnNpZGVyZWQgYXMgdGhlIElEIG9mIHRoZSBjdXJyZW50IGNoYXQgZGlzY3Vzc2lvbiB3aGljaCBpcyB1c2VkIHRvIGlkZW50aWZ5IGF1ZGl0IGV2ZW50c1xuICAgKi9cbiAgZ2VuZXJhdGVDaGF0SWQoY2hhdElkPzogc3RyaW5nKSB7XG4gICAgdGhpcy5fY2hhdElkID0gY2hhdElkIHx8IGd1aWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplIHRoZSBjaGF0IGNvbmZpZyBieSBtYW5hZ2luZyBPTkxZIHN1Yi1vYmplY3QgKipkZWZhdWx0VmFsdWVzKiogY29uZmlncyBvZiB0aGUgc3RhbmRhcmQgYXBwIGNvbmZpZyAoZGVmaW5lZCBpbiB0aGUgY3VzdG9taXphdGlvbiBqc29uIHRhYiApIGFuZCB0aGUgdXNlciBwcmVmZXJlbmNlcy5cbiAgICogVG8gZG8gc28sIGEgdHJhY2tpbmcgbWVjaGFuaXNtIGlzIGltcGxlbWVudGVkIHRvIG5vdGlmeSB0aGUgdXNlciBhYm91dCB0aGUgYXZhaWxhYmxlIHVwZGF0ZXMgaW4gdGhlIGRlZmF1bHRWYWx1ZXMgb2JqZWN0IG9mIHRoZSBzdGFuZGFyZCBhcHAgY29uZmlnLlxuICAgKiBUaGUgcmVzdCBvZiB0aGUgY29uZmlnIG9iamVjdCBjb21pbmcgZnJvbSBcInN0YW5kYXJkIGFwcCBjb25maWdcIiBpcyB1c2VkIGFzIGl0IGlzIHdpdGhvdXQgYW55IG92ZXJyaWRlLlxuICAgKiBUaHVzLCB0aGUgdXNlciBwcmVmZXJlbmNlcyBhcmUgdXNlZCBvbmx5IGZvciB0aGUgZGVmYXVsdFZhbHVlcyBvYmplY3QuXG4gICAqIFRoaXMgcHJvdmlkZSBhIGNlbnRyYWxpemVkIHdheSB0byBtYW5hZ2UgdGhlIHJlc3Qgb2YgdGhlIGNvbmZpZyBvYmplY3QgYnkgYWRtaW5zIGFuZCBlbnN1cmUgYSB1bmlxdWUgY29tbW9uIGJlaGF2aW9yIGZvciBhbGwgdXNlcnMuXG4gICAqL1xuICBhc3luYyBpbml0Q2hhdENvbmZpZygpIHtcbiAgICAvLyBmZXRjaCB0aGUgc3RhbmRhcmQgYXBwIGNvbmZpZyB0byBnZXQgdGhlIGRlZmF1bHRWYWx1ZXMgb2YgdGhlIGNoYXQgY29uZmlnIGZvciB0aGUgZ2l2ZW4gaW5zdGFuY2VcbiAgICAvLyBQZXJzaXN0IHRoZSBhcHAgaW4gdGhlIGFwcCBzZXJ2aWNlXG4gICAgY29uc3QgY2FwcCA9IGF3YWl0IGZldGNoQXBwKCk7XG4gICAgdGhpcy5hcHBTZXJ2aWNlLmFwcCA9IGNhcHA7XG5cbiAgICBjb25zdCBzZXR0aW5ncyA9IGF3YWl0IGZldGNoVXNlclNldHRpbmdzPHt9PigpO1xuICAgIHRoaXMudXNlclNldHRpbmdzU2VydmljZS51c2VyU2V0dGluZ3MgPSBzZXR0aW5ncztcbiAgICBjb25zdCBrZXkgPSB0aGlzLmNoYXRJbnN0YW5jZUlkO1xuICAgIGNvbnN0IHVzZXJTZXR0aW5nc0NvbmZpZyA9IHRoaXMuYXNzaXN0YW50c1trZXldIHx8IHt9O1xuXG4gICAgY29uc3QgcHJpbmNpcGFsID0gYXdhaXQgZmV0Y2hQcmluY2lwYWwoKTtcbiAgICB0aGlzLnByaW5jaXBhbFNlcnZpY2UucHJpbmNpcGFsID0gcHJpbmNpcGFsO1xuXG4gICAgY29uc3Qgc3RhbmRhcmRDaGF0Q29uZmlnID0gZ2V0QXNzaXN0YW50SnNvbkZyb21DQ0FwcCh0aGlzLmFwcFNlcnZpY2UuYXBwLCBrZXkpO1xuXG4gICAgdHJ5IHtcbiAgICAgIC8vIFZhbGlkYXRlIHRoZSB3aG9sZSBjb25maWcgb2JqZWN0IGFnYWluc3QgdGhlIHNjaGVtYVxuICAgICAgY2hhdENvbmZpZ1NjaGVtYS5wYXJzZShzdGFuZGFyZENoYXRDb25maWcpO1xuICAgICAgLy8gSWYgdGhlIHVzZXIgcHJlZmVyZW5jZXMgZG8gbm90IGNvbnRhaW4gYSBjb25maWcncyBkZWZhdWx0VmFsdWVzIG9iamVjdCwga2VlcCB1c2luZyB0aGUgc3RhbmRhcmQgYXBwIGNvbmZpZyBhbmQgbm90aGluZyB0byBzdG9yZSBpbiB0aGUgdXNlciBwcmVmZXJlbmNlc1xuICAgICAgaWYgKCF1c2VyU2V0dGluZ3NDb25maWcuZGVmYXVsdFZhbHVlcykge1xuICAgICAgICB0aGlzLmFzc2lzdGFudENvbmZpZyQubmV4dCh7Li4uc3RhbmRhcmRDaGF0Q29uZmlnfSk7XG4gICAgICAgIHRoaXMuaW5pdENvbmZpZyQubmV4dCh0cnVlKTtcbiAgICAgIH0gZWxzZSB7IC8vIElmIHRoZSB1c2VyIGhhcyBpdHMgb3duIGRlZmF1bHRWYWx1ZXMgaW4gaXRzIHVzZXJTZXR0aW5ncywgdGhlbiB3ZSBuZWVkIHRvIGNoZWNrIGZvciBwb3RlbnRpYWwgdXBkYXRlcyBtYWRlIGJ5IGFkbWlucyBpbiB0aGUgbWVhbnRpbWUgYW5kIGhvdyBoZSB3YW50cyB0byBtYW5hZ2UgdGhlbVxuXG4gICAgICAgIC8vIFJldHJpZXZlIGFscmVhZHkgc3RvcmVkIGhhc2hlcyBpbiB0aGUgdXNlciBzZXR0aW5ncyBpZiBleGlzdHNcbiAgICAgICAgY29uc3QgYXBwbGllZERlZmF1bHRWYWx1ZXNIYXNoID0gdXNlclNldHRpbmdzQ29uZmlnLmhhc2hlcz8uW1wiYXBwbGllZC1kZWZhdWx0VmFsdWVzLWhhc2hcIl07XG4gICAgICAgIGNvbnN0IHNraXBwZWREZWZhdWx0VmFsdWVzSGFzaCA9IHVzZXJTZXR0aW5nc0NvbmZpZy5oYXNoZXM/LltcInNraXBwZWQtZGVmYXVsdFZhbHVlcy1oYXNoXCJdO1xuICAgICAgICAvLyBDcmVhdGUgYSBoYXNoIG9mIHRoZSBjdXJyZW50IGRlZmF1bHRWYWx1ZXMgb2YgdGhlIHN0YW5kYXJkQ2hhdENvbmZpZ1xuICAgICAgICBjb25zdCBjdXJyZW50RGVmYXVsdFZhbHVlc0hhc2ggPSBhd2FpdCBzaGE1MTIoSlNPTi5zdHJpbmdpZnkoc3RhbmRhcmRDaGF0Q29uZmlnLmRlZmF1bHRWYWx1ZXMpKTtcblxuICAgICAgICAvLyBJbXBsZW1lbnQgdGhlIHRyYWNraW5nIG1lY2hhbmlzbSB0byBub3RpZnkgdGhlIHVzZXIgYWJvdXQgdGhlIGF2YWlsYWJsZSB1cGRhdGVzIGluIHRoZSBkZWZhdWx0VmFsdWVzIG9iamVjdCBvZiB0aGUgc3RhbmRhcmQgYXBwIGNvbmZpZ1xuICAgICAgICBjb25zdCBjb25kaXRpb24gPSAoY3VycmVudERlZmF1bHRWYWx1ZXNIYXNoICE9PSBhcHBsaWVkRGVmYXVsdFZhbHVlc0hhc2gpICYmIChjdXJyZW50RGVmYXVsdFZhbHVlc0hhc2ggIT09IHNraXBwZWREZWZhdWx0VmFsdWVzSGFzaCk7XG4gICAgICAgIGlmIChjb25kaXRpb24pIHtcbiAgICAgICAgICB0aGlzLm1vZGFsU2VydmljZS5vcGVuKERpYWxvZ1VwZGF0ZXNDb21wb25lbnQpXG4gICAgICAgICAgICAudGhlbihyZXMgPT4ge1xuICAgICAgICAgICAgICAgIGlmKHJlcyA9PT0gXCJkaWFsb2ctY29uZmlybVwiKSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCBoYXNoZXMgPSB7IC4uLnVzZXJTZXR0aW5nc0NvbmZpZy5oYXNoZXMsIFwiYXBwbGllZC1kZWZhdWx0VmFsdWVzLWhhc2hcIjogY3VycmVudERlZmF1bHRWYWx1ZXNIYXNoLCBcInNraXBwZWQtZGVmYXVsdFZhbHVlcy1oYXNoXCI6IHVuZGVmaW5lZCB9O1xuICAgICAgICAgICAgICAgICAgLy8gVXBkYXRlIHRoZSBjaGF0IGNvbmZpZyBhbmQgc3RvcmUgaXRzIGRlZmF1bHRWYWx1ZXMgaW4gdGhlIHVzZXIgcHJlZmVyZW5jZXNcbiAgICAgICAgICAgICAgICAgIHRoaXMudXBkYXRlQ2hhdENvbmZpZyh7Li4uc3RhbmRhcmRDaGF0Q29uZmlnfSwgaGFzaGVzLCB0cnVlKTtcbiAgICAgICAgICAgICAgICAgIHRoaXMuaW5pdENvbmZpZyQubmV4dCh0cnVlKTtcbiAgICAgICAgICAgICAgICAgIHRoaXMuZ2VuZXJhdGVBdWRpdEV2ZW50KFwiYXN0LWNvbmZpZ3VyYXRpb24uY2xpY2tcIiwgeyAnY29uZmlndXJhdGlvbic6IEpTT04uc3RyaW5naWZ5KHsuLi5zdGFuZGFyZENoYXRDb25maWd9KSB9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYocmVzID09PSBcImRpYWxvZy1ub1wiKSB7XG4gICAgICAgICAgICAgICAgICAvLyBEbyBub3Qgbm90aWZ5IHRoZSB1c2VyIGFib3V0IGNoYW5nZXMgd2hpbGUgdGhpcyBza2lwcGVkIHZlcnNpb24gaXMgbm90IHVwZGF0ZWRcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGhhc2hlcyA9IHsgLi4udXNlclNldHRpbmdzQ29uZmlnLmhhc2hlcywgXCJza2lwcGVkLWRlZmF1bHRWYWx1ZXMtaGFzaFwiOiBjdXJyZW50RGVmYXVsdFZhbHVlc0hhc2ggfTtcbiAgICAgICAgICAgICAgICAgIHRoaXMudXBkYXRlQ2hhdENvbmZpZyh7Li4uc3RhbmRhcmRDaGF0Q29uZmlnLCBkZWZhdWx0VmFsdWVzOiB1c2VyU2V0dGluZ3NDb25maWcuZGVmYXVsdFZhbHVlc30sIGhhc2hlcywgZmFsc2UpO1xuICAgICAgICAgICAgICAgICAgdGhpcy5pbml0Q29uZmlnJC5uZXh0KHRydWUpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAvLyBKdXN0IHBpY2sgdGhlIHZlcnNpb24gaW4gdGhlIHVzZXIgc2V0dGluZ3MsIG5vdGhpbmcgdG8gYmUgdXBkYXRlZFxuICAgICAgICAgICAgICAgICAgdGhpcy5hc3Npc3RhbnRDb25maWckLm5leHQoey4uLnN0YW5kYXJkQ2hhdENvbmZpZywgZGVmYXVsdFZhbHVlczogdXNlclNldHRpbmdzQ29uZmlnLmRlZmF1bHRWYWx1ZXN9KTtcbiAgICAgICAgICAgICAgICAgIHRoaXMuaW5pdENvbmZpZyQubmV4dCh0cnVlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHsgLy8gTm8gYXZhaWxhYmxlIHVwZGF0ZXMgT3IgdXBkYXRlcyBoYXMgYmVlbiBhbHJlYWR5IHNraXBwZWQsIHRoZW4ganVzdCBwaWNrIHRoZSB2ZXJzaW9uIGluIHRoZSB1c2VyIHNldHRpbmdzXG4gICAgICAgICAgdGhpcy5hc3Npc3RhbnRDb25maWckLm5leHQoey4uLnN0YW5kYXJkQ2hhdENvbmZpZywgZGVmYXVsdFZhbHVlczogdXNlclNldHRpbmdzQ29uZmlnLmRlZmF1bHRWYWx1ZXN9KTtcbiAgICAgICAgICB0aGlzLmluaXRDb25maWckLm5leHQodHJ1ZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhpcy5ub3RpZmljYXRpb25zU2VydmljZS5lcnJvcihgTWlzc2luZyB2YWxpZCBjb25maWd1cmF0aW9uIGZvciB0aGUgYXNzaXN0YW50IGluc3RhbmNlICcke2tleX0nLiBTZWUgdGhlIGJyb3dzZXIgY29uc29sZSBtZXNzYWdlcyBmb3IgZGV0YWlscyBvbiB0aGUgbWlzc2luZyBvciBpbmNvcnJlY3QgcHJvcGVydGllcy5gKTtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgTWlzc2luZyB2YWxpZCBjb25maWd1cmF0aW9uIGZvciB0aGUgYXNzaXN0YW50IGluc3RhbmNlICcke2tleX0nIC4gXFxuICR7SlNPTi5zdHJpbmdpZnkoZXJyb3IuaXNzdWVzLCBudWxsLCAyKX1gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIHRoZSBjaGF0IGNvbmZpZyBhbmQgc3RvcmUgaXRzIGRlZmF1bHRWYWx1ZXMgaW4gdGhlIHVzZXIgcHJlZmVyZW5jZXNcbiAgICogQHBhcmFtIGNvbmZpZyAgVGhlIHVwZGF0ZWQgY2hhdCBjb25maWdcbiAgICogQHBhcmFtIGhhc2hlcyAgVGhlIHVwZGF0ZWQgaGFzaGVzIHRvIHN0b3JlIGluIHRoZSB1c2VyIHByZWZlcmVuY2VzXG4gICAqIEBwYXJhbSBub3RpZnkgIFdoZXRoZXIgdG8gbm90aWZ5IHRoZSB1c2VyIGFib3V0IHRoZSB1cGRhdGVcbiAgICogQHBhcmFtIHN1Y2Nlc3NDYWxsYmFjayAgVGhlIGNhbGxiYWNrIHRvIGV4ZWN1dGUgaWYgdGhlIHVwZGF0ZSBpcyBzdWNjZXNzZnVsXG4gICAqIEBwYXJhbSBlcnJvckNhbGxiYWNrICBUaGUgY2FsbGJhY2sgdG8gZXhlY3V0ZSBpZiB0aGUgdXBkYXRlIGZhaWxzXG4gICAqL1xuICB1cGRhdGVDaGF0Q29uZmlnKGNvbmZpZzogQ2hhdENvbmZpZywgaGFzaGVzPzoge1wiYXBwbGllZC1kZWZhdWx0VmFsdWVzLWhhc2hcIj86IHN0cmluZywgXCJza2lwcGVkLWRlZmF1bHRWYWx1ZXMtaGFzaFwiPzogc3RyaW5nfSwgIG5vdGlmeSA9IHRydWUsIHN1Y2Nlc3NDYWxsYmFjaz86ICgpID0+IGFueSwgZXJyb3JDYWxsYmFjaz86ICgpID0+IGFueSkge1xuICAgIHRoaXMuYXNzaXN0YW50Q29uZmlnJC5uZXh0KGNvbmZpZyk7XG4gICAgY29uc3QgYXNzaXN0YW50cyA9IE9iamVjdC5hc3NpZ24oe30sIHRoaXMuYXNzaXN0YW50cyk7XG4gICAgYXNzaXN0YW50c1t0aGlzLmNoYXRJbnN0YW5jZUlkXSA9IHsgLi4uYXNzaXN0YW50c1t0aGlzLmNoYXRJbnN0YW5jZUlkXSwgZGVmYXVsdFZhbHVlczogY29uZmlnLmRlZmF1bHRWYWx1ZXMgfTtcbiAgICBpZihoYXNoZXMpIGFzc2lzdGFudHNbdGhpcy5jaGF0SW5zdGFuY2VJZF0uaGFzaGVzID0gaGFzaGVzO1xuICAgIHRoaXMudXNlclNldHRpbmdzU2VydmljZS5wYXRjaCh7IGFzc2lzdGFudHMgfSkuc3Vic2NyaWJlKFxuICAgICAgbmV4dCA9PiB7fSxcbiAgICAgIGVycm9yID0+IHtcbiAgICAgICAgaWYobm90aWZ5KSB7XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby11bnVzZWQtZXhwcmVzc2lvbnNcbiAgICAgICAgICBlcnJvckNhbGxiYWNrXG4gICAgICAgICAgICA/IGVycm9yQ2FsbGJhY2soKVxuICAgICAgICAgICAgOiB0aGlzLm5vdGlmaWNhdGlvbnNTZXJ2aWNlLmVycm9yKHRoaXMudHJhbnNsb2NvLnRyYW5zbGF0ZSgnY2hhdC5zYXZlQ2hhdENvbmZpZy5mYWlsJywgeyB2YWx1ZTogdGhpcy5jaGF0SW5zdGFuY2VJZCB9KSk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc29sZS5lcnJvcihcIkNvdWxkIG5vdCBwYXRjaCBhc3Npc3RhbnRzIVwiLCBlcnJvcik7XG4gICAgICB9LFxuICAgICAgKCkgPT4ge1xuICAgICAgICBpZihub3RpZnkpIHtcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVudXNlZC1leHByZXNzaW9uc1xuICAgICAgICAgIHN1Y2Nlc3NDYWxsYmFja1xuICAgICAgICAgICAgPyBzdWNjZXNzQ2FsbGJhY2soKVxuICAgICAgICAgICAgOiB0aGlzLm5vdGlmaWNhdGlvbnNTZXJ2aWNlLnN1Y2Nlc3ModGhpcy50cmFuc2xvY28udHJhbnNsYXRlKCdjaGF0LnNhdmVDaGF0Q29uZmlnLnN1Y2Nlc3MnLCB7IHZhbHVlOiB0aGlzLmNoYXRJbnN0YW5jZUlkIH0pKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogT3ZlcnJpZGVzIHRoZSBsb2dnZWQgaW4gdXNlclxuICAgKi9cbiAgYWJzdHJhY3Qgb3ZlcnJpZGVVc2VyKCk6IHZvaWQ7XG5cbiAgLyoqXG4gICAqIENhbGxzIHRoZSBGZXRjaCBBUEkgdG8gcmV0cmlldmUgYSBuZXcgbWVzc2FnZSBnaXZlbiBhbGwgcHJldmlvdXMgbWVzc2FnZXNcbiAgICovXG4gIGFic3RyYWN0IGZldGNoKG1lc3NhZ2VzOiBDaGF0TWVzc2FnZVtdLCBxdWVyeTogUXVlcnkpOiBPYnNlcnZhYmxlPENoYXRSZXNwb25zZT5cblxuICAvKipcbiAgICogUmV0dXJuIHRoZSBsaXN0IG9mIG1vZGVscyBhdmFpbGFibGUgb24gdGhlIHNlcnZlclxuICAgKi9cbiAgYWJzdHJhY3QgbGlzdE1vZGVscygpOiBPYnNlcnZhYmxlPEdsbG1Nb2RlbERlc2NyaXB0aW9uW10gfCB1bmRlZmluZWQ+O1xuXG4gIC8qKlxuICAgKiBSZXR1cm4gdGhlIGxpc3Qgb2YgZnVuY3Rpb25zIGF2YWlsYWJsZSBvbiB0aGUgc2VydmVyIEFORCBtYXRjaGluZyBlbmFibGVkIGZ1bmN0aW9ucyBpbiB0aGUgY2hhdCBjb25maWdcbiAgICovXG4gIGFic3RyYWN0IGxpc3RGdW5jdGlvbnMoKTogT2JzZXJ2YWJsZTxHbGxtRnVuY3Rpb25bXSB8IHVuZGVmaW5lZD47XG5cbiAgLyoqXG4gICAqIFN0b3BzIHRoZSBhc3Npc3RhbnQgYW5zd2VyIGdlbmVyYXRpb24gYW5kIGNhbmNlbHMgYWxsIHRoZSBvbmdvaW5nIGFuZCBwZW5kaW5nIHJlbGF0ZWQgdGFza3NcbiAgICovXG4gIGFic3RyYWN0IHN0b3BHZW5lcmF0aW9uKCk6IE9ic2VydmFibGU8YW55PlxuXG4gIC8qKlxuICAgKiBBIGhhbmRsZXIgZm9yIHF1b3RhIHVwZGF0ZXMgZWFjaCB0aW1lIHRoZSBjaGF0IGlzIGludm9rZWQuXG4gICAqIEl0IGVtaXRzIHRoZSB1cGRhdGVkIHF1b3RhIHRvIHRoZSBxdW90YSQgc3ViamVjdCwgZW1pdHMgYWNjb3JkaW5nbHkgdGhlIHVwZGF0ZWQgdXNlcidzIHRva2VucyBjb25zdW1wdGlvbiBhbmQgbm90aWZpZXMgdGhlIHVzZXIgaWYgdGhlIG1heCBxdW90YSBpcyByZWFjaGVkLlxuICAgKiBAcGFyYW0gcXVvdGEgVGhlIHVwZGF0ZWQgcXVvdGFcbiAgICogQHBhcmFtIHByb3BhZ2F0ZUVycm9yIFdoZXRoZXIgdG8gcHJvcGFnYXRlIHRoZSBlcnJvciB0byB0aGUgY2FsbGVyXG4gICAqL1xuICB1cGRhdGVRdW90YShxdW90YTogUXVvdGEsIHByb3BhZ2F0ZUVycm9yID0gZmFsc2UpIHtcbiAgICB0aGlzLnF1b3RhJC5uZXh0KHF1b3RhKTtcbiAgICBjb25zdCBuZXh0UmVzZXREYXRlID0gdGhpcy5mb3JtYXREYXRlVGltZShxdW90YS5uZXh0UmVzZXRVVEMrXCIrMDA6MDBcIik7IC8vIFRoaXMgKzAwOjAwIGlzIHRvIGVuc3VyZSBkYXRlcyB3aWxsIGJlIHByb3Blcmx5IGNvbnZlcnRlZCB0byBsb2NhbCB0aW1lXG4gICAgY29uc3QgY29uc3VtcHRpb25QZXJjZW50YWdlID0gTWF0aC5yb3VuZCgocXVvdGEudG9rZW5Db3VudCAqIDEwMCAvIHF1b3RhLnBlcmlvZFRva2VucykgKiAxMDApIC8gMTAwO1xuICAgIHRoaXMudXNlclRva2VuQ29uc3VtcHRpb24kLm5leHQoe3BlcmNlbnRhZ2U6IGNvbnN1bXB0aW9uUGVyY2VudGFnZSwgbmV4dFJlc2V0RGF0ZX0pO1xuICAgIGlmKHF1b3RhLm1heFF1b3RhUmVhY2hlZCkge1xuICAgICAgdGhpcy5nZW5lcmF0ZUF1ZGl0RXZlbnQoJ2FzdC1xdW90YS5leGNlZWRlZCcsIHt9KTtcbiAgICAgIGNvbnN0IG1zZyA9IGBTb3JyeSwgeW91IGhhdmUgZXhjZWVkZWQgdGhlIGFsbG93ZWQgcXVvdGEuIFBsZWFzZSByZXRyeSBzdGFydGluZyBmcm9tICR7bmV4dFJlc2V0RGF0ZX0uYDtcbiAgICAgIHRoaXMubm90aWZpY2F0aW9uc1NlcnZpY2UuZXJyb3IobXNnKTtcbiAgICAgIGlmKHByb3BhZ2F0ZUVycm9yKSB0aHJvdyBuZXcgRXJyb3IobXNnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQSBoYW5kbGVyIGZvciBjaGF0IHVzYWdlIG1ldHJpY3MgZWFjaCB0aW1lIHRoZSBnZW5lcmF0aW9uIG9mIHRoZSBhc3Npc3RhbnQgcmVzcG9uc2UgaXMgY29tcGxldGVkLlxuICAgKiBJdCBlbWl0cyB0aGUgY2hhdCB1c2FnZSBtZXRyaWNzIHRvIHRoZSBjaGF0VXNhZ2VNZXRyaWNzJCBzdWJqZWN0LCBlbWl0cyBhY2NvcmRpbmdseSB0aGUgdXBkYXRlZCBjaGF0J3MgdG9rZW5zIGNvbnN1bXB0aW9uXG4gICAqIEBwYXJhbSBjaGF0VXNhZ2VNZXRyaWNzIFRoZSBjaGF0IHVzYWdlIG1ldHJpY3NcbiAgICovXG4gIHVwZGF0ZUNoYXRVc2FnZU1ldHJpY3MoY2hhdFVzYWdlTWV0cmljczogQ2hhdFVzYWdlTWV0cmljcykge1xuICAgIHRoaXMuY2hhdFVzYWdlTWV0cmljcyQubmV4dChjaGF0VXNhZ2VNZXRyaWNzKTtcbiAgICBjb25zdCBjdXJyZW50TW9kZWwgPSB0aGlzLmdldE1vZGVsKHRoaXMuYXNzaXN0YW50Q29uZmlnJC52YWx1ZSEuZGVmYXVsdFZhbHVlcy5zZXJ2aWNlX2lkLCB0aGlzLmFzc2lzdGFudENvbmZpZyQudmFsdWUhLmRlZmF1bHRWYWx1ZXMubW9kZWxfaWQpO1xuICAgIGNvbnN0IGNvbnN1bXB0aW9uUGVyY2VudGFnZSA9IE1hdGgucm91bmQoKGNoYXRVc2FnZU1ldHJpY3MudG90YWxUb2tlbkNvdW50ICogMTAwIC8gKGN1cnJlbnRNb2RlbCEuY29udGV4dFdpbmRvd1NpemUgLSBjdXJyZW50TW9kZWwhLm1heEdlbmVyYXRpb25TaXplKSkgKiAxMDApIC8gMTAwO1xuICAgIHRoaXMuY2hhdFRva2VuQ29uc3VtcHRpb24kLm5leHQoe3BlcmNlbnRhZ2U6IGNvbnN1bXB0aW9uUGVyY2VudGFnZX0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgbW9kZWwgZGVzY3JpcHRpb24gZm9yIHRoZSBnaXZlbiAoc2VydmljZUlkICsgbW9kZWxJZClcbiAgICogSWYgYSBtb2RlbCBpcyBub3QgZm91bmQsIGFuIGVycm9yIG1lc3NhZ2UgaXMgcmV0dXJuZWRcbiAgICogQHBhcmFtIHNlcnZpY2VJZCBUaGUgc2VydmljZUlkIG9mIHRoZSBtb2RlbFxuICAgKiBAcGFyYW0gbW9kZWxJZCBUaGUgbW9kZWxJZCBvZiB0aGUgbW9kZWxcbiAgICogQHJldHVybnMgVGhlIG1vZGVsIGRlc2NyaXB0aW9uXG4gICAqL1xuICBnZXRNb2RlbChzZXJ2aWNlSWQ6IHN0cmluZywgbW9kZWxJZDogc3RyaW5nKTogR2xsbU1vZGVsRGVzY3JpcHRpb24gfCB1bmRlZmluZWR7XG4gICAgY29uc3QgbW9kZWwgPSB0aGlzLm1vZGVscz8uZmluZChtID0+IG0uc2VydmljZUlkID09PSBzZXJ2aWNlSWQgJiYgbS5tb2RlbElkID09PSBtb2RlbElkKTtcbiAgICAvLyBIYW5kbGUgb2Jzb2xldGUgY29uZmlnXG4gICAgaWYoIW1vZGVsKSB7XG4gICAgICB0aGlzLm5vdGlmaWNhdGlvbnNTZXJ2aWNlLmVycm9yKGBGQVRBTCBFUlJPUiA6IFRoZSBtb2RlbCAoc2VydmljZUlkID0gJyR7c2VydmljZUlkfScsIG1vZGVsSWQgPSAnJHttb2RlbElkfScpIGlzIG5vIGxvbmdlciBhdmFpbGFibGUuIFBsZWFzZSBjb250YWN0IGFuIGFkbWluIGZvciBmdXJ0aGVyIGluZm9ybWF0aW9uLmApO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGQVRBTCBFUlJPUiA6IFRoZSBtb2RlbCAoc2VydmljZUlkID0gJyR7c2VydmljZUlkfScsIG1vZGVsSWQgPSAnJHttb2RlbElkfScpIGlzIG5vIGxvbmdlciBhdmFpbGFibGVgKTtcbiAgICB9XG4gICAgcmV0dXJuIG1vZGVsO1xuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoIHRoZSBsaXN0IHNhdmVkIGNoYXRzIGJlbG9uZ2luZyB0byBhIHNwZWNpZmljIGluc3RhbmNlIG9mIHRoZSBhc3Npc3RhbnRcbiAgICovXG4gIGFic3RyYWN0IGxpc3RTYXZlZENoYXQoKTogdm9pZDtcblxuICAvKipcbiAgICogUmV0dXJuIHRoZSBzYXZlZCBjaGF0IHdpdGggdGhlIGdpdmVuIGlkLCBpZiBleGlzdHMuIE90aGVyd2lzZSwgcmV0dXJuIHVuZGVmaW5lZFxuICAgKiBAcGFyYW0gaWQgVGhlIGlkIG9mIHRoZSBzYXZlZCBjaGF0XG4gICAqL1xuICBhYnN0cmFjdCBnZXRTYXZlZENoYXQoaWQ6IHN0cmluZyk6IE9ic2VydmFibGU8U2F2ZWRDaGF0SGlzdG9yeSB8IHVuZGVmaW5lZD47XG5cbiAgLyoqXG4gICAqIFNhdmUgYSBjaGF0IHdpdGggdGhlIGdpdmVuIG1lc3NhZ2VzXG4gICAqIEBwYXJhbSBtZXNzYWdlcyBUaGUgbWVzc2FnZXMgdG8gYWRkIHRvIHRoZSBzYXZlZCBjaGF0IGluZGV4XG4gICAqIEByZXR1cm5zIFRoZSBzYXZlZCBjaGF0XG4gICAqL1xuICBhYnN0cmFjdCBhZGRTYXZlZENoYXQobWVzc2FnZXM6IENoYXRNZXNzYWdlW10pOiBPYnNlcnZhYmxlPFNhdmVkQ2hhdFJlc3BvbnNlPjtcblxuICAvKipcbiAgICogVXBkYXRlIGEgc2F2ZWQgY2hhdCB3aXRoIHRoZSBnaXZlbiBpZC5cbiAgICogQHBhcmFtIGlkIFRoZSBpZCBvZiB0aGUgc2F2ZWQgY2hhdFxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmV3IG5hbWUgb2YgdGhlIHNhdmVkIGNoYXQsIGlmIHByb3ZpZGVkXG4gICAqIEBwYXJhbSBtZXNzYWdlcyBUaGUgbWVzc2FnZXMgdG8gdXBkYXRlIHRoZSBzYXZlZCBjaGF0IGhpc3RvcnksIGlmIHByb3ZpZGVkXG4gICAqIEByZXR1cm5zIFRydWUgaWYgdGhlIHNhdmVkIGNoYXQgaGFzIGJlZW4gc3VjY2Vzc2Z1bGx5IHVwZGF0ZWRcbiAgICovXG4gIGFic3RyYWN0IHVwZGF0ZVNhdmVkQ2hhdChpZDogc3RyaW5nLCBuYW1lPzogc3RyaW5nLCBtZXNzYWdlcz86IENoYXRNZXNzYWdlW10pOiBPYnNlcnZhYmxlPFNhdmVkQ2hhdFJlc3BvbnNlPjtcblxuICAvKipcbiAgICogQnVsayBkZWxldGUgb2Ygc2F2ZWQgY2hhdHMgbWF0Y2hpbmcgdGhlIGdpdmVuIGlkc1xuICAgKiBAcGFyYW0gaWRzIExpc3Qgb2YgaWRzIG9mIHRoZSBzYXZlZCBjaGF0cyB0byBkZWxldGVcbiAgICogQHJldHVybnMgVGhlIG51bWJlciBvZiBkZWxldGVkIGNoYXRzXG4gICAqL1xuICBhYnN0cmFjdCBkZWxldGVTYXZlZENoYXQoaWRzOiBzdHJpbmdbXSk6IE9ic2VydmFibGU8RGVsZXRlU2F2ZWRDaGF0UmVzcG9uc2U+O1xuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBhbiBhdWRpdCBldmVudCB3aXRoIHRoZSBnaXZlbiB0eXBlIGFuZCBkZXRhaWxzLiBUaGUgZ2VuZXJhdGVkIGF1ZGl0IGV2ZW50IGlzIHNlbnQgYWZ0ZXJ3YXJkcyB2aWEgdGhlIEF1ZGl0V2ViU2VydmljZVxuICAgKiBAcGFyYW0gdHlwZSBBdWRpdCBldmVudCB0eXBlXG4gICAqIEBwYXJhbSBkZXRhaWxzIEF1ZGl0IGV2ZW50IGRldGFpbHNcbiAgICogQHBhcmFtIGlkIEFjdGlvbnMgKHNhdmVkQ2hhdCBkZWxldGUvcmVuYW1lLy4uLikgbWF5IG9jY3VyIG9uIGEgc3BlY2lmaWMgY2hhdCBkaWZmZXJlbnQgdGhhbiB0aGUgY3VycmVudCBvbmUgc3RvcmVkIGluIHRoaXMgc2VydmljZSwgc28gdGhlIGNoYXQgaWQgY2FuIGJlIHByb3ZpZGVkXG4gICAqL1xuICBhc3luYyBnZW5lcmF0ZUF1ZGl0RXZlbnQodHlwZTogc3RyaW5nLCBkZXRhaWxzOiBSZWNvcmQ8c3RyaW5nLCBhbnk+LCBpZD86IHN0cmluZykge1xuICAgIGNvbnN0IGJhc2VEZXRhaWxzID0ge1xuICAgICAgXCJ1cmxcIjogZGVjb2RlVVJJQ29tcG9uZW50KHdpbmRvdy5sb2NhdGlvbi5ocmVmKSxcbiAgICAgIFwiYXBwXCI6IHRoaXMuYXBwU2VydmljZS5hcHBOYW1lLFxuICAgICAgXCJ1c2VyLWlkXCI6IHRoaXMucHJpbmNpcGFsU2VydmljZS5wcmluY2lwYWw/LnVzZXJJZCxcbiAgICAgIFwiaW5zdGFuY2UtaWRcIjogdGhpcy5jaGF0SW5zdGFuY2VJZCxcbiAgICAgIFwiY2hhdC1pZFwiOiBpZCB8fCB0aGlzLmNoYXRJZCxcbiAgICAgIFwic2VydmljZS1pZFwiOiB0aGlzLmFzc2lzdGFudENvbmZpZyQudmFsdWUhLmRlZmF1bHRWYWx1ZXMuc2VydmljZV9pZCxcbiAgICAgIFwibW9kZWwtaWRcIjogdGhpcy5hc3Npc3RhbnRDb25maWckLnZhbHVlIS5kZWZhdWx0VmFsdWVzLm1vZGVsX2lkLFxuICAgICAgXCJpcy11c2VyLWlucHV0XCI6IGZhbHNlXG4gICAgfTtcbiAgICBjb25zdCBhdWRpdCA9IHtcbiAgICAgIHR5cGUsXG4gICAgICBkZXRhaWw6IHtcbiAgICAgICAgLi4uYmFzZURldGFpbHMsXG4gICAgICAgIC4uLmRldGFpbHNcbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBBdWRpdC5ub3RpZnkoYXVkaXQpO1xuICAgIGNvbnNvbGUubG9nKFwiQXVkaXQgcmVzcG9uc2VcIiwgcmVzcG9uc2UpO1xuICB9XG5cblxuXG4gIC8qKlxuICAgKiBUcmF2ZXJzZSB0aGUgYXJyYXkgZnJvbSB0aGUgZW5kIGFuZCB0cmFjayB0aGUgZmlyc3QgJ2Fzc2lzdGFudCcgbWVzc2FnZSBhbW9uZyB0aGUgbGFzdCBncm91cCBvZiBcImFzc2lzdGFudFwiIG1lc3NhZ2VzIHdoZXJlIGRpc3BsYXkgaXMgdHJ1ZVxuICAgKiBAcGFyYW0gYXJyYXkgVGhlIGFycmF5IG9mIENoYXRNZXNzYWdlIHRvIHRyYXZlcnNlXG4gICAqIEByZXR1cm5zIFRoZSBpbmRleCBvZiB0aGUgZmlyc3QgdmlzaWJsZSBhc3Npc3RhbnQgbWVzc2FnZSBhbW9uZyB0aGUgbGFzdCBncm91cCBvZiBcImFzc2lzdGFudFwiIG1lc3NhZ2VzIGluIHRoZSBhcnJheVxuICAgKi9cbiAgZmlyc3RWaXNpYmxlQXNzaXN0YW50TWVzc2FnZUluZGV4KGFycmF5OiBDaGF0TWVzc2FnZVtdfCB1bmRlZmluZWQpOiBudW1iZXIge1xuICAgIGlmICghYXJyYXkpIHtcbiAgICAgIHJldHVybiAtMTtcbiAgICB9XG4gICAgbGV0IGluZGV4ID0gYXJyYXkubGVuZ3RoIC0gMTtcbiAgICBsZXQgZmlyc3RWaXNpYmxlQXNzaXN0YW50TWVzc2FnZUluZGV4ID0gLTE7XG4gICAgd2hpbGUgKGluZGV4ID49IDAgJiYgYXJyYXlbaW5kZXhdLnJvbGUgPT09ICdhc3Npc3RhbnQnKSB7XG4gICAgICBpZiAoYXJyYXlbaW5kZXhdLmFkZGl0aW9uYWxQcm9wZXJ0aWVzLmRpc3BsYXkgPT09IHRydWUpIHtcbiAgICAgICAgZmlyc3RWaXNpYmxlQXNzaXN0YW50TWVzc2FnZUluZGV4ID0gaW5kZXg7XG4gICAgICB9XG4gICAgICBpbmRleC0tO1xuICAgIH1cbiAgICByZXR1cm4gZmlyc3RWaXNpYmxlQXNzaXN0YW50TWVzc2FnZUluZGV4O1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYXZlcnNlIHRoZSBhcnJheSBmcm9tIHRoZSBlbmQgYW5kIHBpY2sgdGhlIGxhc3QgJ2Fzc2lzdGFudCcgbWVzc2FnZSBhbW9uZyB0aGUgbGFzdCBncm91cCBvZiBcImFzc2lzdGFudFwiIG1lc3NhZ2VzIHdoZXJlIGRpc3BsYXkgaXMgdHJ1ZVxuICAgKiBAcGFyYW0gYXJyYXkgVGhlIGFycmF5IG9mIENoYXRNZXNzYWdlIHRvIHRyYXZlcnNlXG4gICAqIEByZXR1cm5zIFRoZSBpbmRleCBvZiB0aGUgbGFzdCB2aXNpYmxlIGFzc2lzdGFudCBtZXNzYWdlIGFtb25nIHRoZSBsYXN0IGdyb3VwIG9mIFwiYXNzaXN0YW50XCIgbWVzc2FnZXMgaW4gdGhlIGFycmF5XG4gICAqL1xuICBsYXN0VmlzaWJsZUFzc2lzdGFudE1lc3NhZ2VJbmRleChhcnJheTogQ2hhdE1lc3NhZ2VbXXwgdW5kZWZpbmVkKTogbnVtYmVyIHtcbiAgICBpZiAoIWFycmF5KSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIGxldCBpbmRleCA9IGFycmF5Lmxlbmd0aCAtIDE7XG4gICAgbGV0IGxhc3RWaXNpYmxlQXNzaXN0YW50TWVzc2FnZUluZGV4ID0gLTE7XG4gICAgd2hpbGUgKGluZGV4ID49IDAgJiYgYXJyYXlbaW5kZXhdLnJvbGUgPT09ICdhc3Npc3RhbnQnKSB7XG4gICAgICBpZiAoYXJyYXlbaW5kZXhdLmFkZGl0aW9uYWxQcm9wZXJ0aWVzLmRpc3BsYXkgPT09IHRydWUpIHtcbiAgICAgICAgbGFzdFZpc2libGVBc3Npc3RhbnRNZXNzYWdlSW5kZXggPSBpbmRleDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBpbmRleC0tO1xuICAgIH1cbiAgICByZXR1cm4gbGFzdFZpc2libGVBc3Npc3RhbnRNZXNzYWdlSW5kZXg7XG4gIH1cblxuICAvKipcbiAgICogRm9ybWF0IGEgZGF0ZSBzdHJpbmcgaW4gVVRDIHRvIGEgbG9jYWwgZGF0ZSBzdHJpbmdcbiAgICogQHBhcmFtIHZhbHVlIERhdGUgc3RyaW5nIGluIFVUQyB0byBmb3JtYXRcbiAgICogQHJldHVybnMgQSBmb3JtYXR0ZWQgbG9jYWwgZGF0ZSBzdHJpbmdcbiAgICovXG4gIHByb3RlY3RlZCBmb3JtYXREYXRlVGltZSh2YWx1ZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBsb2NhbERhdGUgPSB0b0RhdGUocGFyc2VJU08odmFsdWUpKTtcbiAgICBjb25zdCBmb3JtYXR0ZXIgPSBuZXcgSW50bC5EYXRlVGltZUZvcm1hdCh1bmRlZmluZWQsIHtcbiAgICAgIHllYXI6ICdudW1lcmljJyxcbiAgICAgIG1vbnRoOiAnc2hvcnQnLFxuICAgICAgZGF5OiAnbnVtZXJpYycsXG4gICAgICBob3VyOiAnMi1kaWdpdCcsXG4gICAgICBtaW51dGU6ICcyLWRpZ2l0JyxcbiAgICAgIHNlY29uZDogJzItZGlnaXQnLFxuICAgICAgdGltZVpvbmVOYW1lOiAnc2hvcnQnXG4gICAgfSk7XG4gICAgcmV0dXJuIGZvcm1hdHRlci5mb3JtYXQobG9jYWxEYXRlKTtcbiAgfVxuXG4gICAgLyoqXG4gICAqIFRha2VzIGEgdGV4dCBwcm9tcHQgdGhhdCBtYXkgY29udGFpbiBwbGFjZWhvbGRlcnMgZm9yIHZhcmlhYmxlc1xuICAgKiBhbmQgcmVwbGFjZXMgdGhlc2UgcGxhY2Vob2xkZXJzIGlmIGl0IGZpbmRzIGEgbWF0Y2ggaW4gdGhlIGdpdmVuXG4gICAqIGNvbnRleHQgb2JqZWN0LlxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBjb25zdCBwID0gXCJIZWxsbywgW1t1c2VyLm5hbWVdXSEgWW91IGhhdmUgW1t1c2VyLm5vdGlmaWNhdGlvbnMubGVuZ3RoXV0gbmV3IG5vdGlmaWNhdGlvbnMuXCI7XG4gICAqIGNvbnN0IGNvbnRleHQgPSB7XG4gICAqICAgICB1c2VyOiB7XG4gICAqICAgICAgICAgbmFtZTogXCJBbGljZVwiLFxuICAgKiAgICAgICAgIG5vdGlmaWNhdGlvbnM6IFtcIk1lc3NhZ2UgZnJvbSBCb2JcIiwgXCJSZW1pbmRlciBmb3IgbWVldGluZ1wiXVxuICAgKiAgICAgfVxuICAgKiB9O1xuICAgKiBjb25zdCBmb3JtYXR0ZWRQcm9tcHQgPSBmb3JtYXRQcm9tcHQocCwgY29udGV4dCk7XG4gICAqIGNvbnNvbGUubG9nKGZvcm1hdHRlZFByb21wdCk7IC8vIE91dHB1dDogXCJIZWxsbywgQWxpY2UhIFlvdSBoYXZlIDIgbmV3IG5vdGlmaWNhdGlvbnMuXCJcbiAgICovXG4gIHN0YXRpYyBmb3JtYXRQcm9tcHQocHJvbXB0OiBzdHJpbmcsIGNvbnRleHQ6IGFueSkge1xuICAgIHJldHVybiBwcm9tcHQucmVwbGFjZShcbiAgICAgIC9cXFtcXFsoLio/KVxcXVxcXS9nLFxuICAgICAgKG1hdGNoLCBleHByKSA9PiB7XG4gICAgICAvLyBTaW1wbGUgZG90IG5vdGF0aW9uIHJlc29sdmVyXG4gICAgICBjb25zdCBrZXlzID0gZXhwci50cmltKCkuc3BsaXQoXCIuXCIpO1xuICAgICAgbGV0IHZhbHVlID0gY29udGV4dDtcbiAgICAgIGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHtcbiAgICAgICAgaWYgKHZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PT0gXCJvYmplY3RcIiAmJiBrZXkgaW4gdmFsdWUpIHtcbiAgICAgICAgdmFsdWUgPSB2YWx1ZVtrZXldO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gbWF0Y2g7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB2YWx1ZSA/PyBtYXRjaDtcbiAgICAgIH1cbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIERldGVybWluZXMgYSB0aW1lLWJhc2VkIGtleSBmb3IgYSBnaXZlbiBkYXRlIHRvIGJlIHVzZWQgZm9yIHRyYW5zbGF0aW9ucyBvciBmb3JtYXR0aW5nLlxuICAgKiBUaGUga2V5IHJlcHJlc2VudHMgYSByZWxhdGl2ZSB0aW1lIHBlcmlvZCBzdWNoIGFzIFwidG9kYXlcIiwgXCJ5ZXN0ZXJkYXlcIiwgXCJ0aGlzIHdlZWtcIiwgZXRjLlxuICAgKiBJZiB0aGUgZGF0ZSBkb2VzIG5vdCBmYWxsIGludG8gYW55IHByZWRlZmluZWQgcmVsYXRpdmUgdGltZSBwZXJpb2QsIGl0IHJldHVybnMgdGhlIHllYXIgYXMgYSBzdHJpbmcuXG4gICAqXG4gICAqIEBwYXJhbSBkYXRlIC0gVGhlIGRhdGUgZm9yIHdoaWNoIHRoZSB0aW1lIGtleSBpcyB0byBiZSBkZXRlcm1pbmVkLlxuICAgKiBAcmV0dXJucyBBIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIHRpbWUga2V5LCB3aGljaCBjYW4gYmUgdXNlZCBmb3IgdHJhbnNsYXRpb24gb3IgZGlzcGxheSBwdXJwb3Nlcy5cbiAgICovXG4gIGdldFRpbWVLZXkoZGF0ZTogRGF0ZSk6IHN0cmluZyB7XG4gICAgICBpZiAoaXNUb2RheShkYXRlKSkge1xuICAgICAgICByZXR1cm4gdGhpcy50cmFuc2xvY28udHJhbnNsYXRlKCdjaGF0LnRvZGF5Jyk7XG4gICAgICB9IGVsc2UgaWYgKGlzWWVzdGVyZGF5KGRhdGUpKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnRyYW5zbG9jby50cmFuc2xhdGUoJ2NoYXQueWVzdGVyZGF5Jyk7XG4gICAgICB9IGVsc2UgaWYgKGlzVGhpc1dlZWsoZGF0ZSkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudHJhbnNsb2NvLnRyYW5zbGF0ZSgnY2hhdC50aGlzV2VlaycpO1xuICAgICAgfSBlbHNlIGlmIChkaWZmZXJlbmNlSW5EYXlzKGVuZE9mWWVzdGVyZGF5KCksIGRhdGUpIDw9IDcpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudHJhbnNsb2NvLnRyYW5zbGF0ZSgnY2hhdC5sYXN0V2VlaycpO1xuICAgICAgfSBlbHNlIGlmIChpc1RoaXNNb250aChkYXRlKSkge1xuICAgICAgICByZXR1cm4gdGhpcy50cmFuc2xvY28udHJhbnNsYXRlKCdjaGF0LnRoaXNNb250aCcpO1xuICAgICAgfSBlbHNlIGlmIChkaWZmZXJlbmNlSW5Nb250aHMoZW5kT2ZZZXN0ZXJkYXkoKSwgZGF0ZSkgPD0gMSkge1xuICAgICAgICByZXR1cm4gdGhpcy50cmFuc2xvY28udHJhbnNsYXRlKCdjaGF0Lmxhc3RNb250aCcpO1xuICAgICAgfSBlbHNlIGlmIChpc1RoaXNRdWFydGVyKGRhdGUpKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnRyYW5zbG9jby50cmFuc2xhdGUoJ2NoYXQudGhpc1F1YXJ0ZXInKTtcbiAgICAgIH0gZWxzZSBpZiAoZGlmZmVyZW5jZUluTW9udGhzKGVuZE9mWWVzdGVyZGF5KCksIGRhdGUpIDw9IDMpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudHJhbnNsb2NvLnRyYW5zbGF0ZSgnY2hhdC5sYXN0UXVhcnRlcicpO1xuICAgICAgfSBlbHNlIGlmIChpc1RoaXNZZWFyKGRhdGUpKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnRyYW5zbG9jby50cmFuc2xhdGUoJ2NoYXQudGhpc1llYXInKTtcbiAgICAgIH0gZWxzZSBpZiAoZGlmZmVyZW5jZUluWWVhcnMoZW5kT2ZZZXN0ZXJkYXkoKSwgZGF0ZSkgPT09IDEpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudHJhbnNsb2NvLnRyYW5zbGF0ZSgnY2hhdC5sYXN0WWVhcicpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIGZvcm1hdChkYXRlLCAneXl5eScpO1xuICAgICAgfVxuICAgIH1cblxuICBnZXRDdXJyZW50TG9jYWxlTmFtZSgpIHtcbiAgICByZXR1cm4gdGhpcy5sb2NhbElEIHx8ICcnO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { CommonModule } from "@angular/common";
|
|
2
|
+
import { Component, Input } from "@angular/core";
|
|
3
|
+
import * as Prism from 'prismjs';
|
|
4
|
+
import { provideTranslocoScope, TranslocoPipe } from '@jsverse/transloco';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
import * as i1 from "../services/ui.service";
|
|
7
|
+
import * as i2 from "@angular/common";
|
|
8
|
+
export class DebugMessageComponent {
|
|
9
|
+
constructor(ui) {
|
|
10
|
+
this.ui = ui;
|
|
11
|
+
this.level = 0; // Track the nesting level
|
|
12
|
+
this.parentColor = ''; // Track the parent row color
|
|
13
|
+
}
|
|
14
|
+
ngAfterViewInit() {
|
|
15
|
+
Prism.highlightAll();
|
|
16
|
+
}
|
|
17
|
+
isObject(value) {
|
|
18
|
+
return value !== null && typeof value === 'object';
|
|
19
|
+
}
|
|
20
|
+
getRowClass(item, index) {
|
|
21
|
+
if (item.isError)
|
|
22
|
+
return 'row-error';
|
|
23
|
+
if (this.level === 0)
|
|
24
|
+
return index % 2 === 0 ? 'row-even' : 'row-odd';
|
|
25
|
+
return this.parentColor;
|
|
26
|
+
}
|
|
27
|
+
copyToClipboard(code) {
|
|
28
|
+
this.ui.copyToClipboard(JSON.stringify(code, null, 2));
|
|
29
|
+
}
|
|
30
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DebugMessageComponent, deps: [{ token: i1.UIService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DebugMessageComponent, isStandalone: true, selector: "sq-debug-message", inputs: { data: "data", level: "level", parentColor: "parentColor" }, providers: [provideTranslocoScope('chat-debug-message')], ngImport: i0, template: "<div *ngIf=\"data\" class=\"table-root\">\n <ng-container *ngFor=\"let item of data; let i = index\">\n <div *ngIf=\"item.type === 'KV'\" [ngClass]=\"getRowClass(item, i)\" class=\"d-flex w-100 kv-object\">\n <div class=\"kv-key\">{{ item.data.key }}</div>\n <div class=\"kv-value\">\n <ng-container *ngIf=\"isObject(item.data.value); else normalValue\">\n <div class=\"card mb-2\">\n <div class=\"card-header\">\n <button class=\"btn btn-light btn-sm\" (click)=\"copyToClipboard(item.data.value)\"><i class=\"far fa-fw fa-clipboard\"></i> {{ 'chatDebugMessage.copyCode' | transloco }}</button>\n </div>\n <pre class=\"language-json my-0 rounded-0 rounded-bottom\"><code class=\"language-json\">{{ item.data.value | json }}</code></pre>\n </div>\n </ng-container>\n <ng-template #normalValue><div class=\"data-value\">{{ item.data.value }}</div></ng-template>\n </div>\n </div>\n <div *ngIf=\"item.type === 'LIST'\" [ngClass]=\"getRowClass(item, i)\" class=\"d-flex w-100 list-object\">\n <div class=\"list-name w-100\" [class.fw-bold]=\"level === 0\" (click)=\"item.expanded=!item.expanded\">\n <i class=\"fas\" [class.fa-chevron-up]=\"item.expanded\" [class.fa-chevron-down]=\"!item.expanded\"></i>\n {{ item.name }}\n </div>\n <div class=\"list-items w-100\" *ngIf=\"item.expanded\">\n <sq-debug-message [data]=\"item.items\" [level]=\"level + 1\" [parentColor]=\"getRowClass(item, i)\"></sq-debug-message>\n </div>\n </div>\n </ng-container>\n</div>\n", styles: [".table-root{display:flex;flex-direction:column;border:1px solid #ccc;width:100%;border-spacing:0}.table-row{display:flex;width:100%}.list-name{width:15%;cursor:pointer}.list-items{width:85%}.kv-key,.kv-value,.list-name{padding:8px;border:1px solid #ccc;box-sizing:border-box;word-wrap:break-word}.kv-key{width:20%}.kv-value{width:80%}.kv-value .data-value{white-space:pre-line}.kv-object,.list-object{display:flex;flex:1}.list-object{flex-direction:column}.row-even{background-color:#fff}.row-odd{background-color:#f2f8fe}.row-error{background-color:#f08080}.table-row:not(:last-child){border-bottom:1px solid #ccc}.kv-key:last-child,.kv-value:last-child,.list-name:last-child{border-right:none}.d-flex{display:flex}.w-100{width:100%}.card{--bs-card-spacer-y: 1rem;--bs-card-spacer-x: 1rem;--bs-card-title-spacer-y: .5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width: var(--bs-border-width);--bs-card-border-color: var(--bs-border-color-translucent);--bs-card-border-radius: var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y: .5rem;--bs-card-cap-padding-x: 1rem;--bs-card-cap-bg: rgba(var(--bs-body-color-rgb), .03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg: var(--bs-body-bg);--bs-card-img-overlay-padding: 1rem;--bs-card-group-margin: .75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.btn{--bs-btn-padding-x: .75rem;--bs-btn-padding-y: .375rem;--bs-btn-font-family: ;--bs-btn-font-size: 1rem;--bs-btn-font-weight: 400;--bs-btn-line-height: 1.5;--bs-btn-color: var(--bs-body-color);--bs-btn-bg: transparent;--bs-btn-border-width: var(--bs-border-width);--bs-btn-border-color: transparent;--bs-btn-border-radius: var(--bs-border-radius);--bs-btn-hover-border-color: transparent;--bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);--bs-btn-disabled-opacity: .65;--bs-btn-focus-box-shadow: 0 0 0 .25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.btn-light{--bs-btn-color: #000;--bs-btn-bg: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #d3d4d5;--bs-btn-hover-border-color: #c6c7c8;--bs-btn-focus-shadow-rgb: 211, 212, 213;--bs-btn-active-color: #000;--bs-btn-active-bg: #c6c7c8;--bs-btn-active-border-color: #babbbc;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #f8f9fa;--bs-btn-disabled-border-color: #f8f9fa}.btn-sm,.btn-group-sm>.btn{--bs-btn-padding-y: .25rem;--bs-btn-padding-x: .5rem;--bs-btn-font-size: .875rem;--bs-btn-border-radius: var(--bs-border-radius-sm)}.rounded-0{border-radius:0!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}\n"], dependencies: [{ kind: "component", type: DebugMessageComponent, selector: "sq-debug-message", inputs: ["data", "level", "parentColor"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2.JsonPipe, name: "json" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] }); }
|
|
32
|
+
}
|
|
33
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DebugMessageComponent, decorators: [{
|
|
34
|
+
type: Component,
|
|
35
|
+
args: [{ selector: "sq-debug-message", standalone: true, imports: [CommonModule, TranslocoPipe], providers: [provideTranslocoScope('chat-debug-message')], template: "<div *ngIf=\"data\" class=\"table-root\">\n <ng-container *ngFor=\"let item of data; let i = index\">\n <div *ngIf=\"item.type === 'KV'\" [ngClass]=\"getRowClass(item, i)\" class=\"d-flex w-100 kv-object\">\n <div class=\"kv-key\">{{ item.data.key }}</div>\n <div class=\"kv-value\">\n <ng-container *ngIf=\"isObject(item.data.value); else normalValue\">\n <div class=\"card mb-2\">\n <div class=\"card-header\">\n <button class=\"btn btn-light btn-sm\" (click)=\"copyToClipboard(item.data.value)\"><i class=\"far fa-fw fa-clipboard\"></i> {{ 'chatDebugMessage.copyCode' | transloco }}</button>\n </div>\n <pre class=\"language-json my-0 rounded-0 rounded-bottom\"><code class=\"language-json\">{{ item.data.value | json }}</code></pre>\n </div>\n </ng-container>\n <ng-template #normalValue><div class=\"data-value\">{{ item.data.value }}</div></ng-template>\n </div>\n </div>\n <div *ngIf=\"item.type === 'LIST'\" [ngClass]=\"getRowClass(item, i)\" class=\"d-flex w-100 list-object\">\n <div class=\"list-name w-100\" [class.fw-bold]=\"level === 0\" (click)=\"item.expanded=!item.expanded\">\n <i class=\"fas\" [class.fa-chevron-up]=\"item.expanded\" [class.fa-chevron-down]=\"!item.expanded\"></i>\n {{ item.name }}\n </div>\n <div class=\"list-items w-100\" *ngIf=\"item.expanded\">\n <sq-debug-message [data]=\"item.items\" [level]=\"level + 1\" [parentColor]=\"getRowClass(item, i)\"></sq-debug-message>\n </div>\n </div>\n </ng-container>\n</div>\n", styles: [".table-root{display:flex;flex-direction:column;border:1px solid #ccc;width:100%;border-spacing:0}.table-row{display:flex;width:100%}.list-name{width:15%;cursor:pointer}.list-items{width:85%}.kv-key,.kv-value,.list-name{padding:8px;border:1px solid #ccc;box-sizing:border-box;word-wrap:break-word}.kv-key{width:20%}.kv-value{width:80%}.kv-value .data-value{white-space:pre-line}.kv-object,.list-object{display:flex;flex:1}.list-object{flex-direction:column}.row-even{background-color:#fff}.row-odd{background-color:#f2f8fe}.row-error{background-color:#f08080}.table-row:not(:last-child){border-bottom:1px solid #ccc}.kv-key:last-child,.kv-value:last-child,.list-name:last-child{border-right:none}.d-flex{display:flex}.w-100{width:100%}.card{--bs-card-spacer-y: 1rem;--bs-card-spacer-x: 1rem;--bs-card-title-spacer-y: .5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width: var(--bs-border-width);--bs-card-border-color: var(--bs-border-color-translucent);--bs-card-border-radius: var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y: .5rem;--bs-card-cap-padding-x: 1rem;--bs-card-cap-bg: rgba(var(--bs-body-color-rgb), .03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg: var(--bs-body-bg);--bs-card-img-overlay-padding: 1rem;--bs-card-group-margin: .75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.btn{--bs-btn-padding-x: .75rem;--bs-btn-padding-y: .375rem;--bs-btn-font-family: ;--bs-btn-font-size: 1rem;--bs-btn-font-weight: 400;--bs-btn-line-height: 1.5;--bs-btn-color: var(--bs-body-color);--bs-btn-bg: transparent;--bs-btn-border-width: var(--bs-border-width);--bs-btn-border-color: transparent;--bs-btn-border-radius: var(--bs-border-radius);--bs-btn-hover-border-color: transparent;--bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);--bs-btn-disabled-opacity: .65;--bs-btn-focus-box-shadow: 0 0 0 .25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.btn-light{--bs-btn-color: #000;--bs-btn-bg: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #d3d4d5;--bs-btn-hover-border-color: #c6c7c8;--bs-btn-focus-shadow-rgb: 211, 212, 213;--bs-btn-active-color: #000;--bs-btn-active-bg: #c6c7c8;--bs-btn-active-border-color: #babbbc;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #f8f9fa;--bs-btn-disabled-border-color: #f8f9fa}.btn-sm,.btn-group-sm>.btn{--bs-btn-padding-y: .25rem;--bs-btn-padding-x: .5rem;--bs-btn-font-size: .875rem;--bs-btn-border-radius: var(--bs-border-radius-sm)}.rounded-0{border-radius:0!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}\n"] }]
|
|
36
|
+
}], ctorParameters: () => [{ type: i1.UIService }], propDecorators: { data: [{
|
|
37
|
+
type: Input
|
|
38
|
+
}], level: [{
|
|
39
|
+
type: Input
|
|
40
|
+
}], parentColor: [{
|
|
41
|
+
type: Input
|
|
42
|
+
}] } });
|
|
43
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVidWctbWVzc2FnZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9hc3Npc3RhbnQvY2hhdC9kZWJ1Zy1tZXNzYWdlL2RlYnVnLW1lc3NhZ2UuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvYXNzaXN0YW50L2NoYXQvZGVidWctbWVzc2FnZS9kZWJ1Zy1tZXNzYWdlLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMvQyxPQUFPLEVBQWlCLFNBQVMsRUFBRSxLQUFLLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDaEUsT0FBTyxLQUFLLEtBQUssTUFBTSxTQUFTLENBQUM7QUFDakMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLGFBQWEsRUFBRSxNQUFNLG9CQUFvQixDQUFDOzs7O0FBYTFFLE1BQU0sT0FBTyxxQkFBcUI7SUFLaEMsWUFBbUIsRUFBYTtRQUFiLE9BQUUsR0FBRixFQUFFLENBQVc7UUFIdkIsVUFBSyxHQUFXLENBQUMsQ0FBQyxDQUFFLDBCQUEwQjtRQUM5QyxnQkFBVyxHQUFXLEVBQUUsQ0FBQyxDQUFFLDZCQUE2QjtJQUU3QixDQUFDO0lBRXJDLGVBQWU7UUFDYixLQUFLLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDdkIsQ0FBQztJQUVELFFBQVEsQ0FBQyxLQUFVO1FBQ2pCLE9BQU8sS0FBSyxLQUFLLElBQUksSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLENBQUE7SUFDcEQsQ0FBQztJQUVELFdBQVcsQ0FBQyxJQUFrQixFQUFFLEtBQWE7UUFDM0MsSUFBSSxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU8sV0FBVyxDQUFDO1FBQ3JDLElBQUksSUFBSSxDQUFDLEtBQUssS0FBSyxDQUFDO1lBQUUsT0FBTyxLQUFLLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7UUFDdEUsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDO0lBQzFCLENBQUM7SUFFRCxlQUFlLENBQUMsSUFBUztRQUN2QixJQUFJLENBQUMsRUFBRSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN6RCxDQUFDOytHQXZCVSxxQkFBcUI7bUdBQXJCLHFCQUFxQixxSUFGckIsQ0FBQyxxQkFBcUIsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLDBCQ2QxRCw0a0RBMkJBLHUvSERYYSxxQkFBcUIsc0dBSHRCLFlBQVksNFlBQUUsYUFBYTs7NEZBRzFCLHFCQUFxQjtrQkFSakMsU0FBUzsrQkFDRSxrQkFBa0IsY0FHaEIsSUFBSSxXQUNQLENBQUMsWUFBWSxFQUFFLGFBQWEsQ0FBQyxhQUMzQixDQUFDLHFCQUFxQixDQUFDLG9CQUFvQixDQUFDLENBQUM7OEVBRy9DLElBQUk7c0JBQVosS0FBSztnQkFDRyxLQUFLO3NCQUFiLEtBQUs7Z0JBQ0csV0FBVztzQkFBbkIsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gXCJAYW5ndWxhci9jb21tb25cIjtcbmltcG9ydCB7IEFmdGVyVmlld0luaXQsIENvbXBvbmVudCwgSW5wdXQgfSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuaW1wb3J0ICogYXMgUHJpc20gZnJvbSAncHJpc21qcyc7XG5pbXBvcnQgeyBwcm92aWRlVHJhbnNsb2NvU2NvcGUsIFRyYW5zbG9jb1BpcGUgfSBmcm9tICdAanN2ZXJzZS90cmFuc2xvY28nO1xuXG5pbXBvcnQgeyBEZWJ1Z01lc3NhZ2UgfSBmcm9tIFwiLi4vdHlwZXNcIjtcbmltcG9ydCB7IFVJU2VydmljZSB9IGZyb20gXCIuLi9zZXJ2aWNlcy91aS5zZXJ2aWNlXCI7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogXCJzcS1kZWJ1Zy1tZXNzYWdlXCIsXG4gIHRlbXBsYXRlVXJsOiBcIi4vZGVidWctbWVzc2FnZS5jb21wb25lbnQuaHRtbFwiLFxuICBzdHlsZVVybHM6IFtcIi4vZGVidWctbWVzc2FnZS5jb21wb25lbnQuc2Nzc1wiXSxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZSwgVHJhbnNsb2NvUGlwZV0sXG4gIHByb3ZpZGVyczogW3Byb3ZpZGVUcmFuc2xvY29TY29wZSgnY2hhdC1kZWJ1Zy1tZXNzYWdlJyldXG59KVxuZXhwb3J0IGNsYXNzIERlYnVnTWVzc2FnZUNvbXBvbmVudCBpbXBsZW1lbnRzIEFmdGVyVmlld0luaXQge1xuICBASW5wdXQoKSBkYXRhOiBEZWJ1Z01lc3NhZ2VbXSB8IHVuZGVmaW5lZDtcbiAgQElucHV0KCkgbGV2ZWw6IG51bWJlciA9IDA7ICAvLyBUcmFjayB0aGUgbmVzdGluZyBsZXZlbFxuICBASW5wdXQoKSBwYXJlbnRDb2xvcjogc3RyaW5nID0gJyc7ICAvLyBUcmFjayB0aGUgcGFyZW50IHJvdyBjb2xvclxuXG4gIGNvbnN0cnVjdG9yKHB1YmxpYyB1aTogVUlTZXJ2aWNlKSB7IH1cblxuICBuZ0FmdGVyVmlld0luaXQoKSB7XG4gICAgUHJpc20uaGlnaGxpZ2h0QWxsKCk7XG4gIH1cblxuICBpc09iamVjdCh2YWx1ZTogYW55KTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHZhbHVlICE9PSBudWxsICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCdcbiAgfVxuXG4gIGdldFJvd0NsYXNzKGl0ZW06IERlYnVnTWVzc2FnZSwgaW5kZXg6IG51bWJlcik6IHN0cmluZyB7XG4gICAgaWYgKGl0ZW0uaXNFcnJvcikgcmV0dXJuICdyb3ctZXJyb3InO1xuICAgIGlmICh0aGlzLmxldmVsID09PSAwKSByZXR1cm4gaW5kZXggJSAyID09PSAwID8gJ3Jvdy1ldmVuJyA6ICdyb3ctb2RkJztcbiAgICByZXR1cm4gdGhpcy5wYXJlbnRDb2xvcjtcbiAgfVxuXG4gIGNvcHlUb0NsaXBib2FyZChjb2RlOiBhbnkpIHtcbiAgICB0aGlzLnVpLmNvcHlUb0NsaXBib2FyZChKU09OLnN0cmluZ2lmeShjb2RlLCBudWxsLCAyKSk7XG4gIH1cbn1cbiIsIjxkaXYgKm5nSWY9XCJkYXRhXCIgY2xhc3M9XCJ0YWJsZS1yb290XCI+XG4gIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IGl0ZW0gb2YgZGF0YTsgbGV0IGkgPSBpbmRleFwiPlxuICAgIDxkaXYgKm5nSWY9XCJpdGVtLnR5cGUgPT09ICdLVidcIiBbbmdDbGFzc109XCJnZXRSb3dDbGFzcyhpdGVtLCBpKVwiIGNsYXNzPVwiZC1mbGV4IHctMTAwIGt2LW9iamVjdFwiPlxuICAgICAgPGRpdiBjbGFzcz1cImt2LWtleVwiPnt7IGl0ZW0uZGF0YS5rZXkgfX08L2Rpdj5cbiAgICAgIDxkaXYgY2xhc3M9XCJrdi12YWx1ZVwiPlxuICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNPYmplY3QoaXRlbS5kYXRhLnZhbHVlKTsgZWxzZSBub3JtYWxWYWx1ZVwiPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJjYXJkIG1iLTJcIj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjYXJkLWhlYWRlclwiPlxuICAgICAgICAgICAgICA8YnV0dG9uIGNsYXNzPVwiYnRuIGJ0bi1saWdodCBidG4tc21cIiAoY2xpY2spPVwiY29weVRvQ2xpcGJvYXJkKGl0ZW0uZGF0YS52YWx1ZSlcIj48aSBjbGFzcz1cImZhciBmYS1mdyBmYS1jbGlwYm9hcmRcIj48L2k+IHt7ICdjaGF0RGVidWdNZXNzYWdlLmNvcHlDb2RlJyB8IHRyYW5zbG9jbyB9fTwvYnV0dG9uPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8cHJlIGNsYXNzPVwibGFuZ3VhZ2UtanNvbiBteS0wIHJvdW5kZWQtMCByb3VuZGVkLWJvdHRvbVwiPjxjb2RlIGNsYXNzPVwibGFuZ3VhZ2UtanNvblwiPnt7IGl0ZW0uZGF0YS52YWx1ZSB8IGpzb24gfX08L2NvZGU+PC9wcmU+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICA8bmctdGVtcGxhdGUgI25vcm1hbFZhbHVlPjxkaXYgY2xhc3M9XCJkYXRhLXZhbHVlXCI+e3sgaXRlbS5kYXRhLnZhbHVlIH19PC9kaXY+PC9uZy10ZW1wbGF0ZT5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICAgIDxkaXYgKm5nSWY9XCJpdGVtLnR5cGUgPT09ICdMSVNUJ1wiIFtuZ0NsYXNzXT1cImdldFJvd0NsYXNzKGl0ZW0sIGkpXCIgY2xhc3M9XCJkLWZsZXggdy0xMDAgbGlzdC1vYmplY3RcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJsaXN0LW5hbWUgdy0xMDBcIiBbY2xhc3MuZnctYm9sZF09XCJsZXZlbCA9PT0gMFwiIChjbGljayk9XCJpdGVtLmV4cGFuZGVkPSFpdGVtLmV4cGFuZGVkXCI+XG4gICAgICAgIDxpIGNsYXNzPVwiZmFzXCIgW2NsYXNzLmZhLWNoZXZyb24tdXBdPVwiaXRlbS5leHBhbmRlZFwiIFtjbGFzcy5mYS1jaGV2cm9uLWRvd25dPVwiIWl0ZW0uZXhwYW5kZWRcIj48L2k+XG4gICAgICAgIHt7IGl0ZW0ubmFtZSB9fVxuICAgICAgPC9kaXY+XG4gICAgICA8ZGl2IGNsYXNzPVwibGlzdC1pdGVtcyB3LTEwMFwiICpuZ0lmPVwiaXRlbS5leHBhbmRlZFwiPlxuICAgICAgICA8c3EtZGVidWctbWVzc2FnZSBbZGF0YV09XCJpdGVtLml0ZW1zXCIgW2xldmVsXT1cImxldmVsICsgMVwiIFtwYXJlbnRDb2xvcl09XCJnZXRSb3dDbGFzcyhpdGVtLCBpKVwiPjwvc3EtZGVidWctbWVzc2FnZT5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICA8L25nLWNvbnRhaW5lcj5cbjwvZGl2PlxuIl19
|