@techextensor/tab-core-utility 2.2.199 → 2.2.201
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/esm2022/index.mjs +4 -3
- package/esm2022/lib/tab-core-utility/app/constants/api-constants.mjs +3 -3
- package/esm2022/lib/tab-core-utility/app/constants/common.mjs +2 -1
- package/esm2022/lib/tab-core-utility/app/crud/tab-crud.service.mjs +8 -18
- package/esm2022/lib/tab-core-utility/app/crud/tab-delete.service.mjs +5 -11
- package/esm2022/lib/tab-core-utility/app/crud/tab-get.service.mjs +1 -3
- package/esm2022/lib/tab-core-utility/app/crud/tab-release.service.mjs +3 -3
- package/esm2022/lib/tab-core-utility/app/crud/tab-workflow.service.mjs +3 -22
- package/esm2022/lib/tab-core-utility/app/interfaces/notification.interface.mjs +52 -0
- package/esm2022/lib/tab-core-utility/app/interfaces/release.interface.mjs +1 -1
- package/esm2022/lib/tab-core-utility/core/service/notification/notification-inbox.service.mjs +299 -0
- package/esm2022/lib/tab-core-utility/core/service/notification/notification-websocket.service.mjs +220 -0
- package/esm2022/lib/tab-core-utility/core/service/signalr/operation-tracker.service.mjs +23 -6
- package/esm2022/lib/tab-core-utility/core/service/signalr/signalr-infrastructure.service.mjs +53 -7
- package/esm2022/lib/tab-core-utility/core/service/signalr/signalr.service.mjs +21 -5
- package/index.d.ts +3 -2
- package/lib/tab-core-utility/app/constants/api-constants.d.ts +2 -2
- package/lib/tab-core-utility/app/constants/common.d.ts +2 -0
- package/lib/tab-core-utility/app/crud/tab-crud.service.d.ts +0 -2
- package/lib/tab-core-utility/app/crud/tab-delete.service.d.ts +0 -1
- package/lib/tab-core-utility/app/crud/tab-get.service.d.ts +0 -1
- package/lib/tab-core-utility/app/crud/tab-workflow.service.d.ts +1 -16
- package/lib/tab-core-utility/app/interfaces/notification.interface.d.ts +156 -0
- package/lib/tab-core-utility/app/interfaces/release.interface.d.ts +49 -18
- package/lib/tab-core-utility/core/service/notification/notification-inbox.service.d.ts +87 -0
- package/lib/tab-core-utility/core/service/notification/notification-websocket.service.d.ts +57 -0
- package/lib/tab-core-utility/core/service/signalr/operation-tracker.service.d.ts +9 -1
- package/lib/tab-core-utility/core/service/signalr/signalr-infrastructure.service.d.ts +11 -0
- package/lib/tab-core-utility/core/service/signalr/signalr.service.d.ts +15 -3
- package/package.json +1 -1
- package/esm2022/lib/tab-core-utility/app/helpers/common/dsq.helpers.mjs +0 -67
- package/esm2022/lib/tab-core-utility/app/helpers/common/req-res.helpers.mjs +0 -44
- package/lib/tab-core-utility/app/helpers/common/dsq.helpers.d.ts +0 -35
- package/lib/tab-core-utility/app/helpers/common/req-res.helpers.d.ts +0 -34
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { inject, Injectable } from '@angular/core';
|
|
2
|
+
import { BehaviorSubject, Subject } from 'rxjs';
|
|
3
|
+
import { NOTIFICATION_WS_CONFIG, WS_MESSAGE_TYPE, } from '../../../app/interfaces/notification.interface';
|
|
4
|
+
import { NotificationWebSocketService } from './notification-websocket.service';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/**
|
|
7
|
+
* Public notification inbox service.
|
|
8
|
+
*
|
|
9
|
+
* Pure — no storage / auth coupling. The caller (SDK facade) supplies all auth
|
|
10
|
+
* identifiers via `connect(params)`, and they're held for the lifetime of the
|
|
11
|
+
* connection (cleared on disconnect).
|
|
12
|
+
*
|
|
13
|
+
* Subscribes to the WebSocket transport, routes incoming messages by type into
|
|
14
|
+
* dedicated streams, and exposes high-level actions for the UI.
|
|
15
|
+
*
|
|
16
|
+
* Components/extensions consume this via the SDK facade (`TabSdk.notification.*`),
|
|
17
|
+
* not directly.
|
|
18
|
+
*
|
|
19
|
+
* Named `NotificationInboxService` to avoid collision with the existing toastr
|
|
20
|
+
* `NotificationService` in `core/service/notification.service.ts`.
|
|
21
|
+
*/
|
|
22
|
+
export class NotificationInboxService {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.ws = inject(NotificationWebSocketService);
|
|
25
|
+
// Held between connect() and disconnect() — every outbound message includes it.
|
|
26
|
+
this.workspaceKey = null;
|
|
27
|
+
// ----------- streams -----------
|
|
28
|
+
this.latestNotifications$ = new Subject();
|
|
29
|
+
this.unreadCount$ = new BehaviorSubject(0);
|
|
30
|
+
this.markAsReadEvent$ = new Subject();
|
|
31
|
+
this.clearEvent$ = new Subject();
|
|
32
|
+
this.loading$ = new BehaviorSubject(false);
|
|
33
|
+
this.totalPages$ = new BehaviorSubject(1);
|
|
34
|
+
this.loadMoreTimer = null;
|
|
35
|
+
// Route inbound messages → streams
|
|
36
|
+
this.ws.messages$.subscribe((message) => this.handleMessage(message));
|
|
37
|
+
}
|
|
38
|
+
// ----------- lifecycle -----------
|
|
39
|
+
/**
|
|
40
|
+
* Connect to the notification WebSocket. Awaitable, idempotent.
|
|
41
|
+
*
|
|
42
|
+
* If a connection is already active for a *different* workspace (tenant/app/env
|
|
43
|
+
* switch without signOut), we tear it down first so the new connection uses
|
|
44
|
+
* the correct URL and workspaceKey. The underlying `ws.connect` is idempotent
|
|
45
|
+
* by connection state, so it would otherwise keep the stale URL.
|
|
46
|
+
*
|
|
47
|
+
* Once connected, an unread-count fetch is fired automatically so the badge populates.
|
|
48
|
+
*/
|
|
49
|
+
async connect(params) {
|
|
50
|
+
if (this.workspaceKey && this.workspaceKey !== params.workspaceKey) {
|
|
51
|
+
await this.disconnect();
|
|
52
|
+
}
|
|
53
|
+
this.workspaceKey = params.workspaceKey;
|
|
54
|
+
await this.ws.connect({
|
|
55
|
+
userId: params.userId,
|
|
56
|
+
appId: params.appId,
|
|
57
|
+
tenantId: params.tenantId,
|
|
58
|
+
});
|
|
59
|
+
if (this.ws.isConnected()) {
|
|
60
|
+
this.requestUnreadCount();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Tear down the connection. Resets all stream state so the UI clears.
|
|
65
|
+
*/
|
|
66
|
+
async disconnect() {
|
|
67
|
+
await this.ws.disconnect();
|
|
68
|
+
this.workspaceKey = null;
|
|
69
|
+
this.unreadCount$.next(0);
|
|
70
|
+
this.totalPages$.next(1);
|
|
71
|
+
this.loading$.next(false);
|
|
72
|
+
if (this.loadMoreTimer) {
|
|
73
|
+
clearTimeout(this.loadMoreTimer);
|
|
74
|
+
this.loadMoreTimer = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// ----------- actions -----------
|
|
78
|
+
/**
|
|
79
|
+
* Fetch a page of notifications. Server replies via GET_NOTIFICATIONS_RESPONSE.
|
|
80
|
+
*
|
|
81
|
+
* Skips entirely when the socket isn't connected — otherwise we'd set
|
|
82
|
+
* `loading$` true, send into a no-op, and the UI would spin forever.
|
|
83
|
+
*
|
|
84
|
+
* Adds a safety timeout so the spinner clears even if the BE accepts the
|
|
85
|
+
* connection but never replies (broken hub, dropped frame, etc.).
|
|
86
|
+
*/
|
|
87
|
+
loadMore(pageNumber, pageSize = NOTIFICATION_WS_CONFIG.DEFAULT_PAGE_SIZE) {
|
|
88
|
+
if (!this.workspaceKey || !this.ws.isConnected())
|
|
89
|
+
return;
|
|
90
|
+
this.loading$.next(true);
|
|
91
|
+
this.ws.send({
|
|
92
|
+
type: WS_MESSAGE_TYPE.GET_NOTIFICATIONS,
|
|
93
|
+
workspaceKey: this.workspaceKey,
|
|
94
|
+
pageNumber,
|
|
95
|
+
pageSize,
|
|
96
|
+
});
|
|
97
|
+
if (this.loadMoreTimer)
|
|
98
|
+
clearTimeout(this.loadMoreTimer);
|
|
99
|
+
this.loadMoreTimer = setTimeout(() => {
|
|
100
|
+
this.loadMoreTimer = null;
|
|
101
|
+
if (this.loading$.value) {
|
|
102
|
+
this.loading$.next(false);
|
|
103
|
+
}
|
|
104
|
+
}, NOTIFICATION_WS_CONFIG.LOAD_TIMEOUT_MS);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Request unread count. Result arrives on `unreadCount$`.
|
|
108
|
+
*/
|
|
109
|
+
requestUnreadCount() {
|
|
110
|
+
if (!this.workspaceKey)
|
|
111
|
+
return;
|
|
112
|
+
this.ws.send({
|
|
113
|
+
type: WS_MESSAGE_TYPE.GET_UNREAD_COUNT,
|
|
114
|
+
workspaceKey: this.workspaceKey,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
markAsRead(notificationId) {
|
|
118
|
+
if (!this.workspaceKey)
|
|
119
|
+
return;
|
|
120
|
+
this.ws.send({
|
|
121
|
+
type: WS_MESSAGE_TYPE.MARK_NOTIFICATION_READ,
|
|
122
|
+
workspaceKey: this.workspaceKey,
|
|
123
|
+
notificationId,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
markAllAsRead() {
|
|
127
|
+
if (!this.workspaceKey)
|
|
128
|
+
return;
|
|
129
|
+
this.ws.send({
|
|
130
|
+
type: WS_MESSAGE_TYPE.MARK_ALL_NOTIFICATION_READ,
|
|
131
|
+
workspaceKey: this.workspaceKey,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
clearOne(notificationId) {
|
|
135
|
+
if (!this.workspaceKey)
|
|
136
|
+
return;
|
|
137
|
+
this.ws.send({
|
|
138
|
+
type: WS_MESSAGE_TYPE.CLEAR_NOTIFICATION,
|
|
139
|
+
workspaceKey: this.workspaceKey,
|
|
140
|
+
notificationId,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
clearAll() {
|
|
144
|
+
if (!this.workspaceKey)
|
|
145
|
+
return;
|
|
146
|
+
this.ws.send({
|
|
147
|
+
type: WS_MESSAGE_TYPE.CLEAR_ALL_NOTIFICATIONS,
|
|
148
|
+
workspaceKey: this.workspaceKey,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// ----------- streams (read-only views) -----------
|
|
152
|
+
getLatestNotifications() {
|
|
153
|
+
return this.latestNotifications$.asObservable();
|
|
154
|
+
}
|
|
155
|
+
getUnreadCount() {
|
|
156
|
+
return this.unreadCount$.asObservable();
|
|
157
|
+
}
|
|
158
|
+
getMarkAsReadEvents() {
|
|
159
|
+
return this.markAsReadEvent$.asObservable();
|
|
160
|
+
}
|
|
161
|
+
getClearEvents() {
|
|
162
|
+
return this.clearEvent$.asObservable();
|
|
163
|
+
}
|
|
164
|
+
getLoadingState() {
|
|
165
|
+
return this.loading$.asObservable();
|
|
166
|
+
}
|
|
167
|
+
getTotalPages() {
|
|
168
|
+
return this.totalPages$.asObservable();
|
|
169
|
+
}
|
|
170
|
+
// ----------- internal -----------
|
|
171
|
+
handleMessage(message) {
|
|
172
|
+
if (!message?.type)
|
|
173
|
+
return;
|
|
174
|
+
switch (message.type) {
|
|
175
|
+
case WS_MESSAGE_TYPE.GET_NOTIFICATIONS_RESPONSE:
|
|
176
|
+
if (message.success) {
|
|
177
|
+
const items = message.notifications ?? [];
|
|
178
|
+
const ui = items.map((n) => this.parseInboxNotification(n));
|
|
179
|
+
this.latestNotifications$.next(ui);
|
|
180
|
+
this.totalPages$.next(message.totalPages ?? 1);
|
|
181
|
+
}
|
|
182
|
+
this.loading$.next(false);
|
|
183
|
+
if (this.loadMoreTimer) {
|
|
184
|
+
clearTimeout(this.loadMoreTimer);
|
|
185
|
+
this.loadMoreTimer = null;
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
case WS_MESSAGE_TYPE.GET_UNREAD_COUNT_RESPONSE:
|
|
189
|
+
if (message.success) {
|
|
190
|
+
this.unreadCount$.next(message.count ?? 0);
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
case WS_MESSAGE_TYPE.NOTIFICATION: {
|
|
194
|
+
const ui = this.transformPushNotification(message);
|
|
195
|
+
this.latestNotifications$.next(ui);
|
|
196
|
+
this.requestUnreadCount();
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case WS_MESSAGE_TYPE.MARK_NOTIFICATION_READ_RESPONSE:
|
|
200
|
+
if (message.success) {
|
|
201
|
+
this.markAsReadEvent$.next(message.notificationId ?? null);
|
|
202
|
+
this.requestUnreadCount();
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
205
|
+
case WS_MESSAGE_TYPE.NOTIFICATION_MARKED_READ:
|
|
206
|
+
// Broadcast from another session of same user. notificationId optional
|
|
207
|
+
// (absent ⇒ "all read").
|
|
208
|
+
this.markAsReadEvent$.next(message.notificationId ?? null);
|
|
209
|
+
this.requestUnreadCount();
|
|
210
|
+
break;
|
|
211
|
+
case WS_MESSAGE_TYPE.MARK_ALL_NOTIFICATION_READ_RESPONSE:
|
|
212
|
+
if (message.success) {
|
|
213
|
+
this.markAsReadEvent$.next(null);
|
|
214
|
+
this.requestUnreadCount();
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
case WS_MESSAGE_TYPE.CLEAR_NOTIFICATION_RESPONSE:
|
|
218
|
+
case WS_MESSAGE_TYPE.NOTIFICATION_CLEARED:
|
|
219
|
+
if (Array.isArray(message.notificationIds) && message.notificationIds.length) {
|
|
220
|
+
this.clearEvent$.next(message.notificationIds);
|
|
221
|
+
this.requestUnreadCount();
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
case WS_MESSAGE_TYPE.CLEAR_ALL_NOTIFICATIONS_RESPONSE:
|
|
225
|
+
if (message.success) {
|
|
226
|
+
this.clearEvent$.next(null);
|
|
227
|
+
this.requestUnreadCount();
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
case WS_MESSAGE_TYPE.ALL_NOTIFICATIONS_CLEARED:
|
|
231
|
+
if (message.success) {
|
|
232
|
+
this.clearEvent$.next(null);
|
|
233
|
+
this.requestUnreadCount();
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
case WS_MESSAGE_TYPE.CONNECTION_ESTABLISHED:
|
|
237
|
+
// Server confirms the connection is live. Fire the post-connect
|
|
238
|
+
// unread-count fetch here — works whether the resolver-time await
|
|
239
|
+
// saw the connection open or timed out early.
|
|
240
|
+
if (this.workspaceKey) {
|
|
241
|
+
this.requestUnreadCount();
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
// PONG, HEARTBEAT_ACK, ERROR are handled at the transport layer
|
|
245
|
+
// (or are no-ops). HEARTBEAT is intercepted by transport and never forwarded.
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Parse an inbox notification's `data` JSON into a UI-shaped object.
|
|
250
|
+
*
|
|
251
|
+
* Read state is surfaced via `status === 'Read'` (confirmed by BE).
|
|
252
|
+
* Inbox response only contains `Delivered` (unread) + `Read` items;
|
|
253
|
+
* `Pending` / `Done` / `Failed` are filtered out server-side.
|
|
254
|
+
*/
|
|
255
|
+
parseInboxNotification(item) {
|
|
256
|
+
let parsedPayload = {};
|
|
257
|
+
try {
|
|
258
|
+
parsedPayload = JSON.parse(item.data);
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
parsedPayload = {};
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
id: item.id,
|
|
265
|
+
createdAt: item.createdAt,
|
|
266
|
+
status: item.status,
|
|
267
|
+
priority: item.priority,
|
|
268
|
+
parsedPayload,
|
|
269
|
+
isRead: item.status === 'Read',
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Wrap a server-pushed live notification into the same UI shape as inbox items.
|
|
274
|
+
* The push event has fields flat at the top level (NOT inside `data`) — synthesize
|
|
275
|
+
* `parsedPayload` from those fields so the component sees a uniform shape.
|
|
276
|
+
*/
|
|
277
|
+
transformPushNotification(push) {
|
|
278
|
+
return {
|
|
279
|
+
id: push.id,
|
|
280
|
+
createdAt: push.createdAt,
|
|
281
|
+
status: 'Delivered',
|
|
282
|
+
priority: push.priority,
|
|
283
|
+
parsedPayload: {
|
|
284
|
+
title: push.title,
|
|
285
|
+
body: push.body,
|
|
286
|
+
category: push.category,
|
|
287
|
+
priority: push.priority,
|
|
288
|
+
},
|
|
289
|
+
isRead: false,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: NotificationInboxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
293
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: NotificationInboxService, providedIn: 'root' }); }
|
|
294
|
+
}
|
|
295
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: NotificationInboxService, decorators: [{
|
|
296
|
+
type: Injectable,
|
|
297
|
+
args: [{ providedIn: 'root' }]
|
|
298
|
+
}], ctorParameters: () => [] });
|
|
299
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm90aWZpY2F0aW9uLWluYm94LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9saWJzL2RhdGEtYWNjZXNzL3NyYy9saWIvdGFiLWNvcmUtdXRpbGl0eS9jb3JlL3NlcnZpY2Uvbm90aWZpY2F0aW9uL25vdGlmaWNhdGlvbi1pbmJveC5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25ELE9BQU8sRUFBRSxlQUFlLEVBQWMsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzVELE9BQU8sRUFDTCxzQkFBc0IsRUFNdEIsZUFBZSxHQUNoQixNQUFNLGdEQUFnRCxDQUFDO0FBQ3hELE9BQU8sRUFBRSw0QkFBNEIsRUFBRSxNQUFNLGtDQUFrQyxDQUFDOztBQUVoRjs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFFSCxNQUFNLE9BQU8sd0JBQXdCO0lBZ0JuQztRQWZpQixPQUFFLEdBQUcsTUFBTSxDQUFDLDRCQUE0QixDQUFDLENBQUM7UUFFM0QsZ0ZBQWdGO1FBQ3hFLGlCQUFZLEdBQWtCLElBQUksQ0FBQztRQUUzQyxrQ0FBa0M7UUFFakIseUJBQW9CLEdBQUcsSUFBSSxPQUFPLEVBQXFDLENBQUM7UUFDeEUsaUJBQVksR0FBRyxJQUFJLGVBQWUsQ0FBUyxDQUFDLENBQUMsQ0FBQztRQUM5QyxxQkFBZ0IsR0FBRyxJQUFJLE9BQU8sRUFBaUIsQ0FBQztRQUNoRCxnQkFBVyxHQUFHLElBQUksT0FBTyxFQUFtQixDQUFDO1FBQzdDLGFBQVEsR0FBRyxJQUFJLGVBQWUsQ0FBVSxLQUFLLENBQUMsQ0FBQztRQUMvQyxnQkFBVyxHQUFHLElBQUksZUFBZSxDQUFTLENBQUMsQ0FBQyxDQUFDO1FBQ3RELGtCQUFhLEdBQXlDLElBQUksQ0FBQztRQUdqRSxtQ0FBbUM7UUFDbkMsSUFBSSxDQUFDLEVB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YmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgT2JzZXJ2YWJsZSwgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xyXG5pbXBvcnQge1xyXG4gIE5PVElGSUNBVElPTl9XU19DT05GSUcsXHJcbiAgTm90aWZpY2F0aW9uSW5ib3hDb25uZWN0UGFyYW1zLFxyXG4gIE5vdGlmaWNhdGlvbkl0ZW0sXHJcbiAgTm90aWZpY2F0aW9uUHVzaFBheWxvYWQsXHJcbiAgUGFyc2VkTm90aWZpY2F0aW9uUGF5bG9hZCxcclxuICBVaU5vdGlmaWNhdGlvbixcclxuICBXU19NRVNTQUdFX1RZUEUsXHJcbn0gZnJvbSAnLi4vLi4vLi4vYXBwL2ludGVyZmFjZXMvbm90aWZpY2F0aW9uLmludGVyZmFjZSc7XHJcbmltcG9ydCB7IE5vdGlmaWNhdGlvbldlYlNvY2tldFNlcnZpY2UgfSBmcm9tICcuL25vdGlmaWNhdGlvbi13ZWJzb2NrZXQuc2VydmljZSc7XHJcblxyXG4vKipcclxuICogUHVibGljIG5vdGlmaWNhdGlvbiBpbmJveCBzZXJ2aWNlLlxyXG4gKlxyXG4gKiBQdXJlIOKAlCBubyBzdG9yYWdlIC8gYXV0aCBjb3VwbGluZy4gVGhlIGNhbGxlciAoU0RLIGZhY2FkZSkgc3VwcGxpZXMgYWxsIGF1dGhcclxuICogaWRlbnRpZmllcnMgdmlhIGBjb25uZWN0KHBhcmFtcylgLCBhbmQgdGhleSdyZSBoZWxkIGZvciB0aGUgbGlmZXRpbWUgb2YgdGhlXHJcbiAqIGNvbm5lY3Rpb24gKGNsZWFyZWQgb24gZGlzY29ubmVjdCkuXHJcbiAqXHJcbiAqIFN1YnNjcmliZXMgdG8gdGhlIFdlYlNvY2tldCB0cmFuc3BvcnQsIHJvdXRlcyBpbmNvbWluZyBtZXNzYWdlcyBieSB0eXBlIGludG9cclxuICogZGVkaWNhdGVkIHN0cmVhbXMsIGFuZCBleHBvc2VzIGhpZ2gtbGV2ZWwgYWN0aW9ucyBmb3IgdGhlIFVJLlxyXG4gKlxyXG4gKiBDb21wb25lbnRzL2V4dGVuc2lvbnMgY29uc3VtZSB0aGlzIHZpYSB0aGUgU0RLIGZhY2FkZSAoYFRhYlNkay5ub3RpZmljYXRpb24uKmApLFxyXG4gKiBub3QgZGlyZWN0bHkuXHJcbiAqXHJcbiAqIE5hbWVkIGBOb3RpZmljYXRpb25JbmJveFNlcnZpY2VgIHRvIGF2b2lkIGNvbGxpc2lvbiB3aXRoIHRoZSBleGlzdGluZyB0b2FzdHJcclxuICogYE5vdGlmaWNhdGlvblNlcnZpY2VgIGluIGBjb3JlL3NlcnZpY2Uvbm90aWZpY2F0aW9uLnNlcnZpY2UudHNgLlxyXG4gKi9cclxuQEluamVjdGFibGUoeyBwcm92aWRlZEluOiAncm9vdCcgfSlcclxuZXhwb3J0IGNsYXNzIE5vdGlmaWNhdGlvbkluYm94U2VydmljZSB7XHJcbiAgcHJpdmF0ZSByZWFkb25seSB3cyA9IGluamVjdChOb3RpZmljYXRpb25XZWJTb2NrZXRTZXJ2aWNlKTtcclxuXHJcbiAgLy8gSGVsZCBiZXR3ZWVuIGNvbm5lY3QoKSBhbmQgZGlzY29ubmVjdCgpIOKAlCBldmVyeSBvdXRib3VuZCBtZXNzYWdlIGluY2x1ZGVzIGl0LlxyXG4gIHByaXZhdGUgd29ya3NwYWNlS2V5OiBzdHJpbmcgfCBudWxsID0gbnVsbDtcclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0gc3RyZWFtcyAtLS0tLS0tLS0tLVxyXG5cclxuICBwcml2YXRlIHJlYWRvbmx5IGxhdGVzdE5vdGlmaWNhdGlvbnMkID0gbmV3IFN1YmplY3Q8VWlOb3RpZmljYXRpb24gfCBVaU5vdGlmaWNhdGlvbltdPigpO1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgdW5yZWFkQ291bnQkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxudW1iZXI+KDApO1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgbWFya0FzUmVhZEV2ZW50JCA9IG5ldyBTdWJqZWN0PHN0cmluZyB8IG51bGw+KCk7XHJcbiAgcHJpdmF0ZSByZWFkb25seSBjbGVhckV2ZW50JCA9IG5ldyBTdWJqZWN0PHN0cmluZ1tdIHwgbnVsbD4oKTtcclxuICBwcml2YXRlIHJlYWRvbmx5IGxvYWRpbmckID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XHJcbiAgcHJpdmF0ZSByZWFkb25seSB0b3RhbFBhZ2VzJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8bnVtYmVyPigxKTtcclxuICBwcml2YXRlIGxvYWRNb3JlVGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgbnVsbCA9IG51bGw7XHJcblxyXG4gIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgLy8gUm91dGUgaW5ib3VuZCBtZXNzYWdlcyDihpIgc3RyZWFtc1xyXG4gICAgdGhpcy53cy5tZXNzYWdlcyQuc3Vic2NyaWJlKChtZXNzYWdlKSA9PiB0aGlzLmhhbmRsZU1lc3NhZ2UobWVzc2FnZSkpO1xyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0gbGlmZWN5Y2xlIC0tLS0tLS0tLS0tXHJcblxyXG4gIC8qKlxyXG4gICAqIENvbm5lY3QgdG8gdGhlIG5vdGlmaWNhdGlvbiBXZWJTb2NrZXQuIEF3YWl0YWJsZSwgaWRlbXBvdGVudC5cclxuICAgKlxyXG4gICAqIElmIGEgY29ubmVjdGlvbiBpcyBhbHJlYWR5IGFjdGl2ZSBmb3IgYSAqZGlmZmVyZW50KiB3b3Jrc3BhY2UgKHRlbmFudC9hcHAvZW52XHJcbiAgICogc3dpdGNoIHdpdGhvdXQgc2lnbk91dCksIHdlIHRlYXIgaXQgZG93biBmaXJzdCBzbyB0aGUgbmV3IGNvbm5lY3Rpb24gdXNlc1xyXG4gICAqIHRoZSBjb3JyZWN0IFVSTCBhbmQgd29ya3NwYWNlS2V5LiBUaGUgdW5kZXJseWluZyBgd3MuY29ubmVjdGAgaXMgaWRlbXBvdGVudFxyXG4gICAqIGJ5IGNvbm5lY3Rpb24gc3RhdGUsIHNvIGl0IHdvdWxkIG90aGVyd2lzZSBrZWVwIHRoZSBzdGFsZSBVUkwuXHJcbiAgICpcclxuICAgKiBPbmNlIGNvbm5lY3RlZCwgYW4gdW5yZWFkLWNvdW50IGZldGNoIGlzIGZpcmVkIGF1dG9tYXRpY2FsbHkgc28gdGhlIGJhZGdlIHBvcHVsYXRlcy5cclxuICAgKi9cclxuICBhc3luYyBjb25uZWN0KHBhcmFtczogTm90aWZpY2F0aW9uSW5ib3hDb25uZWN0UGFyYW1zKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBpZiAodGhpcy53b3Jrc3BhY2VLZXkgJiYgdGhpcy53b3Jrc3BhY2VLZXkgIT09IHBhcmFtcy53b3Jrc3BhY2VLZXkpIHtcclxuICAgICAgYXdhaXQgdGhpcy5kaXNjb25uZWN0KCk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhpcy53b3Jrc3BhY2VLZXkgPSBwYXJhbXMud29ya3NwYWNlS2V5O1xyXG5cclxuICAgIGF3YWl0IHRoaXMud3MuY29ubmVjdCh7XHJcbiAgICAgIHVzZXJJZDogcGFyYW1zLnVzZXJJZCxcclxuICAgICAgYXBwSWQ6IHBhcmFtcy5hcHBJZCxcclxuICAgICAgdGVuYW50SWQ6IHBhcmFtcy50ZW5hbnRJZCxcclxuICAgIH0pO1xyXG5cclxuICAgIGlmICh0aGlzLndzLmlzQ29ubmVjdGVkKCkpIHtcclxuICAgICAgdGhpcy5yZXF1ZXN0VW5yZWFkQ291bnQoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRlYXIgZG93biB0aGUgY29ubmVjdGlvbi4gUmVzZXRzIGFsbCBzdHJlYW0gc3RhdGUgc28gdGhlIFVJIGNsZWFycy5cclxuICAgKi9cclxuICBhc3luYyBkaXNjb25uZWN0KCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgYXdhaXQgdGhpcy53cy5kaXNjb25uZWN0KCk7XHJcbiAgICB0aGlzLndvcmtzcGFjZUtleSA9IG51bGw7XHJcbiAgICB0aGlzLnVucmVhZENvdW50JC5uZXh0KDApO1xyXG4gICAgdGhpcy50b3RhbFBhZ2VzJC5uZXh0KDEpO1xyXG4gICAgdGhpcy5sb2FkaW5nJC5uZXh0KGZhbHNlKTtcclxuICAgIGlmICh0aGlzLmxvYWRNb3JlVGltZXIpIHtcclxuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMubG9hZE1vcmVUaW1lcik7XHJcbiAgICAgIHRoaXMubG9hZE1vcmVUaW1lciA9IG51bGw7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyAtLS0tLS0tLS0tLSBhY3Rpb25zIC0tLS0tLS0tLS0tXHJcblxyXG4gIC8qKlxyXG4gICAqIEZldGNoIGEgcGFnZSBvZiBub3RpZmljYXRpb25zLiBTZXJ2ZXIgcmVwbGllcyB2aWEgR0VUX05PVElGSUNBVElPTlNfUkVTUE9OU0UuXHJcbiAgICpcclxuICAgKiBTa2lwcyBlbnRpcmVseSB3aGVuIHRoZSBzb2NrZXQgaXNuJ3QgY29ubmVjdGVkIOKAlCBvdGhlcndpc2Ugd2UnZCBzZXRcclxuICAgKiBgbG9hZGluZyRgIHRydWUsIHNlbmQgaW50byBhIG5vLW9wLCBhbmQgdGhlIFVJIHdvdWxkIHNwaW4gZm9yZXZlci5cclxuICAgKlxyXG4gICAqIEFkZHMgYSBzYWZldHkgdGltZW91dCBzbyB0aGUgc3Bpbm5lciBjbGVhcnMgZXZlbiBpZiB0aGUgQkUgYWNjZXB0cyB0aGVcclxuICAgKiBjb25uZWN0aW9uIGJ1dCBuZXZlciByZXBsaWVzIChicm9rZW4gaHViLCBkcm9wcGVkIGZyYW1lLCBldGMuKS5cclxuICAgKi9cclxuICBsb2FkTW9yZShwYWdlTnVtYmVyOiBudW1iZXIsIHBhZ2VTaXplOiBudW1iZXIgPSBOT1RJRklDQVRJT05fV1NfQ09ORklHLkRFRkFVTFRfUEFHRV9TSVpFKTogdm9pZCB7XHJcbiAgICBpZiAoIXRoaXMud29ya3NwYWNlS2V5IHx8ICF0aGlzLndzLmlzQ29ubmVjdGVkKCkpIHJldHVybjtcclxuXHJcbiAgICB0aGlzLmxvYWRpbmckLm5leHQodHJ1ZSk7XHJcbiAgICB0aGlzLndzLnNlbmQoe1xyXG4gICAgICB0eXBlOiBXU19NRVNTQUdFX1RZUEUuR0VUX05PVElGSUNBVElPTlMsXHJcbiAgICAgIHdvcmtzcGFjZUtleTogdGhpcy53b3Jrc3BhY2VLZXksXHJcbiAgICAgIHBhZ2VOdW1iZXIsXHJcbiAgICAgIHBhZ2VTaXplLFxyXG4gICAgfSk7XHJcblxyXG4gICAgaWYgKHRoaXMubG9hZE1vcmVUaW1lcikgY2xlYXJUaW1lb3V0KHRoaXMubG9hZE1vcmVUaW1lcik7XHJcbiAgICB0aGlzLmxvYWRNb3JlVGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgdGhpcy5sb2FkTW9yZVRpbWVyID0gbnVsbDtcclxuICAgICAgaWYgKHRoaXMubG9hZGluZyQudmFsdWUpIHtcclxuICAgICAgICB0aGlzLmxvYWRpbmckLm5leHQoZmFsc2UpO1xyXG4gICAgICB9XHJcbiAgICB9LCBOT1RJRklDQVRJT05fV1NfQ09ORklHLkxPQURfVElNRU9VVF9NUyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXF1ZXN0IHVucmVhZCBjb3VudC4gUmVzdWx0IGFycml2ZXMgb24gYHVucmVhZENvdW50JGAuXHJcbiAgICovXHJcbiAgcmVxdWVzdFVucmVhZENvdW50KCk6IHZvaWQge1xyXG4gICAgaWYgKCF0aGlzLndvcmtzcGFjZUtleSkgcmV0dXJuO1xyXG4gICAgdGhpcy53cy5zZW5kKHtcclxuICAgICAgdHlwZTogV1NfTUVTU0FHRV9UWVBFLkdFVF9VTlJFQURfQ09VTlQsXHJcbiAgICAgIHdvcmtzcGFjZUtleTogdGhpcy53b3Jrc3BhY2VLZXksXHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIG1hcmtBc1JlYWQobm90aWZpY2F0aW9uSWQ6IHN0cmluZyk6IHZvaWQge1xyXG4gICAgaWYgKCF0aGlzLndvcmtzcGFjZUtleSkgcmV0dXJuO1xyXG4gICAgdGhpcy53cy5zZW5kKHtcclxuICAgICAgdHlwZTogV1NfTUVTU0FHRV9UWVBFLk1BUktfTk9USUZJQ0FUSU9OX1JFQUQsXHJcbiAgICAgIHdvcmtzcGFjZUtleTogdGhpcy53b3Jrc3BhY2VLZXksXHJcbiAgICAgIG5vdGlmaWNhdGlvbklkLFxyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBtYXJrQWxsQXNSZWFkKCk6IHZvaWQge1xyXG4gICAgaWYgKCF0aGlzLndvcmtzcGFjZUtleSkgcmV0dXJuO1xyXG4gICAgdGhpcy53cy5zZW5kKHtcclxuICAgICAgdHlwZTogV1NfTUVTU0FHRV9UWVBFLk1BUktfQUxMX05PVElGSUNBVElPTl9SRUFELFxyXG4gICAgICB3b3Jrc3BhY2VLZXk6IHRoaXMud29ya3NwYWNlS2V5LFxyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBjbGVhck9uZShub3RpZmljYXRpb25JZDogc3RyaW5nKTogdm9pZCB7XHJcbiAgICBpZiAoIXRoaXMud29ya3NwYWNlS2V5KSByZXR1cm47XHJcbiAgICB0aGlzLndzLnNlbmQoe1xyXG4gICAgICB0eXBlOiBXU19NRVNTQUdFX1RZUEUuQ0xFQVJfTk9USUZJQ0FUSU9OLFxyXG4gICAgICB3b3Jrc3BhY2VLZXk6IHRoaXMud29ya3NwYWNlS2V5LFxyXG4gICAgICBub3RpZmljYXRpb25JZCxcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgY2xlYXJBbGwoKTogdm9pZCB7XHJcbiAgICBpZiAoIXRoaXMud29ya3NwYWNlS2V5KSByZXR1cm47XHJcbiAgICB0aGlzLndzLnNlbmQoe1xyXG4gICAgICB0eXBlOiBXU19NRVNTQUdFX1RZUEUuQ0xFQVJfQUxMX05PVElGSUNBVElPTlMsXHJcbiAgICAgIHdvcmtzcGFjZUtleTogdGhpcy53b3Jrc3BhY2VLZXksXHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tIHN0cmVhbXMgKHJlYWQtb25seSB2aWV3cykgLS0tLS0tLS0tLS1cclxuXHJcbiAgZ2V0TGF0ZXN0Tm90aWZpY2F0aW9ucygpOiBPYnNlcnZhYmxlPFVpTm90aWZpY2F0aW9uIHwgVWlOb3RpZmljYXRpb25bXT4ge1xyXG4gICAgcmV0dXJuIHRoaXMubGF0ZXN0Tm90aWZpY2F0aW9ucyQuYXNPYnNlcnZhYmxlKCk7XHJcbiAgfVxyXG5cclxuICBnZXRVbnJlYWRDb3VudCgpOiBPYnNlcnZhYmxlPG51bWJlcj4ge1xyXG4gICAgcmV0dXJuIHRoaXMudW5yZWFkQ291bnQkLmFzT2JzZXJ2YWJsZSgpO1xyXG4gIH1cclxuXHJcbiAgZ2V0TWFya0FzUmVhZEV2ZW50cygpOiBPYnNlcnZhYmxlPHN0cmluZyB8IG51bGw+IHtcclxuICAgIHJldHVybiB0aGlzLm1hcmtBc1JlYWRFdmVudCQuYXNPYnNlcnZhYmxlKCk7XHJcbiAgfVxyXG5cclxuICBnZXRDbGVhckV2ZW50cygpOiBPYnNlcnZhYmxlPHN0cmluZ1tdIHwgbnVsbD4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY2xlYXJFdmVudCQuYXNPYnNlcnZhYmxlKCk7XHJcbiAgfVxyXG5cclxuICBnZXRMb2FkaW5nU3RhdGUoKTogT2JzZXJ2YWJsZTxib29sZWFuPiB7XHJcbiAgICByZXR1cm4gdGhpcy5sb2FkaW5nJC5hc09ic2VydmFibGUoKTtcclxuICB9XHJcblxyXG4gIGdldFRvdGFsUGFnZXMoKTogT2JzZXJ2YWJsZTxudW1iZXI+IHtcclxuICAgIHJldHVybiB0aGlzLnRvdGFsUGFnZXMkLmFzT2JzZXJ2YWJsZSgpO1xyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0gaW50ZXJuYWwgLS0tLS0tLS0tLS1cclxuXHJcbiAgcHJpdmF0ZSBoYW5kbGVNZXNzYWdlKG1lc3NhZ2U6IGFueSk6IHZvaWQge1xyXG4gICAgaWYgKCFtZXNzYWdlPy50eXBlKSByZXR1cm47XHJcblxyXG4gICAgc3dpdGNoIChtZXNzYWdlLnR5cGUpIHtcclxuICAgICAgY2FzZSBXU19NRVNTQUdFX1RZUEUuR0VUX05PVElGSUNBVElPTlNfUkVTUE9OU0U6XHJcbiAgICAgICAgaWYgKG1lc3NhZ2Uuc3VjY2Vzcykge1xyXG4gICAgICAgICAgY29uc3QgaXRlbXM6IE5vdGlmaWNhdGlvbkl0ZW1bXSA9IG1lc3NhZ2Uubm90aWZpY2F0aW9ucyA/PyBbXTtcclxuICAgICAgICAgIGNvbnN0IHVpID0gaXRlbXMubWFwKChuKSA9PiB0aGlzLnBhcnNlSW5ib3hOb3RpZmljYXRpb24obikpO1xyXG4gICAgICAgICAgdGhpcy5sYXRlc3ROb3RpZmljYXRpb25zJC5uZXh0KHVpKTtcclxuICAgICAgICAgIHRoaXMudG90YWxQYWdlcyQubmV4dChtZXNzYWdlLnRvdGFsUGFnZXMgPz8gMSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMubG9hZGluZyQubmV4dChmYWxzZSk7XHJcbiAgICAgICAgaWYgKHRoaXMubG9hZE1vcmVUaW1lcikge1xyXG4gICAgICAgICAgY2xlYXJUaW1lb3V0KHRoaXMubG9hZE1vcmVUaW1lcik7XHJcbiAgICAgICAgICB0aGlzLmxvYWRNb3JlVGltZXIgPSBudWxsO1xyXG4gICAgICAgIH1cclxuICAgICAgICBicmVhaztcclxuXHJcbiAgICAgIGNhc2UgV1NfTUVTU0FHRV9UWVBFLkdFVF9VTlJFQURfQ09VTlRfUkVTUE9OU0U6XHJcbiAgICAgICAgaWYgKG1lc3NhZ2Uuc3VjY2Vzcykge1xyXG4gICAgICAgICAgdGhpcy51bnJlYWRDb3VudCQubmV4dChtZXNzYWdlLmNvdW50ID8/IDApO1xyXG4gICAgICAgIH1cclxuICAgICAgICBicmVhaztcclxuXHJcbiAgICAgIGNhc2UgV1NfTUVTU0FHRV9UWVBFLk5PVElGSUNBVElPTjoge1xyXG4gICAgICAgIGNvbnN0IHVpID0gdGhpcy50cmFuc2Zvcm1QdXNoTm90aWZpY2F0aW9uKG1lc3NhZ2UpO1xyXG4gICAgICAgIHRoaXMubGF0ZXN0Tm90aWZpY2F0aW9ucyQubmV4dCh1aSk7XHJcbiAgICAgICAgdGhpcy5yZXF1ZXN0VW5yZWFkQ291bnQoKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgfVxyXG5cclxuICAgICAgY2FzZSBXU19NRVNTQUdFX1RZUEUuTUFSS19OT1RJRklDQVRJT05fUkVBRF9SRVNQT05TRTpcclxuICAgICAgICBpZiAobWVzc2FnZS5zdWNjZXNzKSB7XHJcbiAgICAgICAgICB0aGlzLm1hcmtBc1JlYWRFdmVudCQubmV4dChtZXNzYWdlLm5vdGlmaWNhdGlvbklkID8/IG51bGwpO1xyXG4gICAgICAgICAgdGhpcy5yZXF1ZXN0VW5yZWFkQ291bnQoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICBjYXNlIFdTX01FU1NBR0VfVFlQRS5OT1RJRklDQVRJT05fTUFSS0VEX1JFQUQ6XHJcbiAgICAgICAgLy8gQnJvYWRjYXN0IGZyb20gYW5vdGhlciBzZXNzaW9uIG9mIHNhbWUgdXNlci4gbm90aWZpY2F0aW9uSWQgb3B0aW9uYWxcclxuICAgICAgICAvLyAoYWJzZW50IOKHkiBcImFsbCByZWFkXCIpLlxyXG4gICAgICAgIHRoaXMubWFya0FzUmVhZEV2ZW50JC5uZXh0KG1lc3NhZ2Uubm90aWZpY2F0aW9uSWQgPz8gbnVsbCk7XHJcbiAgICAgICAgdGhpcy5yZXF1ZXN0VW5yZWFkQ291bnQoKTtcclxuICAgICAgICBicmVhaztcclxuXHJcbiAgICAgIGNhc2UgV1NfTUVTU0FHRV9UWVBFLk1BUktfQUxMX05PVElGSUNBVElPTl9SRUFEX1JFU1BPTlNFOlxyXG4gICAgICAgIGlmIChtZXNzYWdlLnN1Y2Nlc3MpIHtcclxuICAgICAgICAgIHRoaXMubWFya0FzUmVhZEV2ZW50JC5uZXh0KG51bGwpO1xyXG4gICAgICAgICAgdGhpcy5yZXF1ZXN0VW5yZWFkQ291bnQoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICBjYXNlIFdTX01FU1NBR0VfVFlQRS5DTEVBUl9OT1RJRklDQVRJT05fUkVTUE9OU0U6XHJcbiAgICAgIGNhc2UgV1NfTUVTU0FHRV9UWVBFLk5PVElGSUNBVElPTl9DTEVBUkVEOlxyXG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KG1lc3NhZ2Uubm90aWZpY2F0aW9uSWRzKSAmJiBtZXNzYWdlLm5vdGlmaWNhdGlvbklkcy5sZW5ndGgpIHtcclxuICAgICAgICAgIHRoaXMuY2xlYXJFdmVudCQubmV4dChtZXNzYWdlLm5vdGlmaWNhdGlvbklkcyk7XHJcbiAgICAgICAgICB0aGlzLnJlcXVlc3RVbnJlYWRDb3VudCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBicmVhaztcclxuXHJcbiAgICAgIGNhc2UgV1NfTUVTU0FHRV9UWVBFLkNMRUFSX0FMTF9OT1RJRklDQVRJT05TX1JFU1BPTlNFOlxyXG4gICAgICAgIGlmIChtZXNzYWdlLnN1Y2Nlc3MpIHtcclxuICAgICAgICAgIHRoaXMuY2xlYXJFdmVudCQubmV4dChudWxsKTtcclxuICAgICAgICAgIHRoaXMucmVxdWVzdFVucmVhZENvdW50KCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGJyZWFrO1xyXG5cclxuICAgICAgY2FzZSBXU19NRVNTQUdFX1RZUEUuQUxMX05PVElGSUNBVElPTlNfQ0xFQVJFRDpcclxuICAgICAgICBpZiAobWVzc2FnZS5zdWNjZXNzKSB7XHJcbiAgICAgICAgICB0aGlzLmNsZWFyRXZlbnQkLm5leHQobnVsbCk7XHJcbiAgICAgICAgICB0aGlzLnJlcXVlc3RVbnJlYWRDb3VudCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBicmVhaztcclxuXHJcbiAgICAgIGNhc2UgV1NfTUVTU0FHRV9UWVBFLkNPTk5FQ1RJT05fRVNUQUJMSVNIRUQ6XHJcbiAgICAgICAgLy8gU2VydmVyIGNvbmZpcm1zIHRoZSBjb25uZWN0aW9uIGlzIGxpdmUuIEZpcmUgdGhlIHBvc3QtY29ubmVjdFxyXG4gICAgICAgIC8vIHVucmVhZC1jb3VudCBmZXRjaCBoZXJlIOKAlCB3b3JrcyB3aGV0aGVyIHRoZSByZXNvbHZlci10aW1lIGF3YWl0XHJcbiAgICAgICAgLy8gc2F3IHRoZSBjb25uZWN0aW9uIG9wZW4gb3IgdGltZWQgb3V0IGVhcmx5LlxyXG4gICAgICAgIGlmICh0aGlzLndvcmtzcGFjZUtleSkge1xyXG4gICAgICAgICAgdGhpcy5yZXF1ZXN0VW5yZWFkQ291bnQoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICAvLyBQT05HLCBIRUFSVEJFQVRfQUNLLCBFUlJPUiBhcmUgaGFuZGxlZCBhdCB0aGUgdHJhbnNwb3J0IGxheWVyXHJcbiAgICAgIC8vIChvciBhcmUgbm8tb3BzKS4gSEVBUlRCRUFUIGlzIGludGVyY2VwdGVkIGJ5IHRyYW5zcG9ydCBhbmQgbmV2ZXIgZm9yd2FyZGVkLlxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUGFyc2UgYW4gaW5ib3ggbm90aWZpY2F0aW9uJ3MgYGRhdGFgIEpTT04gaW50byBhIFVJLXNoYXBlZCBvYmplY3QuXHJcbiAgICpcclxuICAgKiBSZWFkIHN0YXRlIGlzIHN1cmZhY2VkIHZpYSBgc3RhdHVzID09PSAnUmVhZCdgIChjb25maXJtZWQgYnkgQkUpLlxyXG4gICAqIEluYm94IHJlc3BvbnNlIG9ubHkgY29udGFpbnMgYERlbGl2ZXJlZGAgKHVucmVhZCkgKyBgUmVhZGAgaXRlbXM7XHJcbiAgICogYFBlbmRpbmdgIC8gYERvbmVgIC8gYEZhaWxlZGAgYXJlIGZpbHRlcmVkIG91dCBzZXJ2ZXItc2lkZS5cclxuICAgKi9cclxuICBwcml2YXRlIHBhcnNlSW5ib3hOb3RpZmljYXRpb24oaXRlbTogTm90aWZpY2F0aW9uSXRlbSk6IFVpTm90aWZpY2F0aW9uIHtcclxuICAgIGxldCBwYXJzZWRQYXlsb2FkOiBQYXJzZWROb3RpZmljYXRpb25QYXlsb2FkID0ge307XHJcbiAgICB0cnkge1xyXG4gICAgICBwYXJzZWRQYXlsb2FkID0gSlNPTi5wYXJzZShpdGVtLmRhdGEpIGFzIFBhcnNlZE5vdGlmaWNhdGlvblBheWxvYWQ7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcGFyc2VkUGF5bG9hZCA9IHt9O1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB7XHJcbiAgICAgIGlkOiBpdGVtLmlkLFxyXG4gICAgICBjcmVhdGVkQXQ6IGl0ZW0uY3JlYXRlZEF0LFxyXG4gICAgICBzdGF0dXM6IGl0ZW0uc3RhdHVzLFxyXG4gICAgICBwcmlvcml0eTogaXRlbS5wcmlvcml0eSxcclxuICAgICAgcGFyc2VkUGF5bG9hZCxcclxuICAgICAgaXNSZWFkOiBpdGVtLnN0YXR1cyA9PT0gJ1JlYWQnLFxyXG4gICAgfTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFdyYXAgYSBzZXJ2ZXItcHVzaGVkIGxpdmUgbm90aWZpY2F0aW9uIGludG8gdGhlIHNhbWUgVUkgc2hhcGUgYXMgaW5ib3ggaXRlbXMuXHJcbiAgICogVGhlIHB1c2ggZXZlbnQgaGFzIGZpZWxkcyBmbGF0IGF0IHRoZSB0b3AgbGV2ZWwgKE5PVCBpbnNpZGUgYGRhdGFgKSDigJQgc3ludGhlc2l6ZVxyXG4gICAqIGBwYXJzZWRQYXlsb2FkYCBmcm9tIHRob3NlIGZpZWxkcyBzbyB0aGUgY29tcG9uZW50IHNlZXMgYSB1bmlmb3JtIHNoYXBlLlxyXG4gICAqL1xyXG4gIHByaXZhdGUgdHJhbnNmb3JtUHVzaE5vdGlmaWNhdGlvbihwdXNoOiBOb3RpZmljYXRpb25QdXNoUGF5bG9hZCk6IFVpTm90aWZpY2F0aW9uIHtcclxuICAgIHJldHVybiB7XHJcbiAgICAgIGlkOiBwdXNoLmlkLFxyXG4gICAgICBjcmVhdGVkQXQ6IHB1c2guY3JlYXRlZEF0LFxyXG4gICAgICBzdGF0dXM6ICdEZWxpdmVyZWQnLFxyXG4gICAgICBwcmlvcml0eTogcHVzaC5wcmlvcml0eSxcclxuICAgICAgcGFyc2VkUGF5bG9hZDoge1xyXG4gICAgICAgIHRpdGxlOiBwdXNoLnRpdGxlLFxyXG4gICAgICAgIGJvZHk6IHB1c2guYm9keSxcclxuICAgICAgICBjYXRlZ29yeTogcHVzaC5jYXRlZ29yeSxcclxuICAgICAgICBwcmlvcml0eTogcHVzaC5wcmlvcml0eSxcclxuICAgICAgfSxcclxuICAgICAgaXNSZWFkOiBmYWxzZSxcclxuICAgIH07XHJcbiAgfVxyXG59XHJcbiJdfQ==
|
package/esm2022/lib/tab-core-utility/core/service/notification/notification-websocket.service.mjs
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Injectable, signal } from '@angular/core';
|
|
2
|
+
import { Subject } from 'rxjs';
|
|
3
|
+
import { webSocket } from 'rxjs/webSocket';
|
|
4
|
+
import { Common } from '../../../app/constants/common';
|
|
5
|
+
import { NOTIFICATION_WS_CONFIG, WS_CONNECTION_STATE, WS_MESSAGE_TYPE, } from '../../../app/interfaces/notification.interface';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
/**
|
|
8
|
+
* Low-level Notification WebSocket transport.
|
|
9
|
+
*
|
|
10
|
+
* Pure — no storage / auth coupling. The caller (SDK facade or app layer)
|
|
11
|
+
* supplies all auth identifiers via `connect(params)`.
|
|
12
|
+
*
|
|
13
|
+
* Mirrors the SignalR infrastructure pattern:
|
|
14
|
+
* - connect() is idempotent, dedupes concurrent calls, tears down stale sockets
|
|
15
|
+
* - disconnect() flips an explicit-disconnected flag so retries don't revive
|
|
16
|
+
* - state exposed as Angular signal
|
|
17
|
+
* - inbound messages forwarded on `messages$` for higher-level service to route
|
|
18
|
+
*
|
|
19
|
+
* Internal use — consumers should use `NotificationInboxService` instead.
|
|
20
|
+
*/
|
|
21
|
+
export class NotificationWebSocketService {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.socket = null;
|
|
24
|
+
this.socketSub = null;
|
|
25
|
+
// In-flight connect promise — used to dedupe concurrent connect() calls
|
|
26
|
+
this.connectingPromise = null;
|
|
27
|
+
// Set true on explicit disconnect() so scheduled retries don't resurrect the connection
|
|
28
|
+
this.explicitlyDisconnected = false;
|
|
29
|
+
// Last connect params — kept so reconnect can rebuild the URL without the caller
|
|
30
|
+
// re-supplying them. Cleared on disconnect().
|
|
31
|
+
this.lastParams = null;
|
|
32
|
+
// Reconnect attempt counter (resets on successful connect)
|
|
33
|
+
this.reconnectAttempt = 0;
|
|
34
|
+
this.reconnectTimer = null;
|
|
35
|
+
// Connection state as Angular signal — for UI indicators / route resolvers
|
|
36
|
+
this.connectionState = signal(WS_CONNECTION_STATE.DISCONNECTED);
|
|
37
|
+
// Inbound message stream — NotificationInboxService subscribes and routes per type
|
|
38
|
+
this.messages$ = new Subject();
|
|
39
|
+
}
|
|
40
|
+
// ----------- public API -----------
|
|
41
|
+
/**
|
|
42
|
+
* Establish the WebSocket connection.
|
|
43
|
+
*
|
|
44
|
+
* Idempotent — if already OPEN, resolves immediately.
|
|
45
|
+
* In-flight calls are de-duplicated.
|
|
46
|
+
* Any stale (non-OPEN) socket is torn down before rebuild.
|
|
47
|
+
*/
|
|
48
|
+
async connect(params) {
|
|
49
|
+
this.explicitlyDisconnected = false;
|
|
50
|
+
this.lastParams = params;
|
|
51
|
+
if (this.connectionState() === WS_CONNECTION_STATE.CONNECTED) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// If a connect is already in flight, wait for it (capped) — don't start a second one.
|
|
55
|
+
if (this.connectingPromise) {
|
|
56
|
+
return this.raceWithTimeout(this.connectingPromise);
|
|
57
|
+
}
|
|
58
|
+
if (this.socket) {
|
|
59
|
+
try {
|
|
60
|
+
this.socket.complete();
|
|
61
|
+
}
|
|
62
|
+
catch { /* ignore */ }
|
|
63
|
+
this.socket = null;
|
|
64
|
+
this.socketSub?.unsubscribe();
|
|
65
|
+
this.socketSub = null;
|
|
66
|
+
}
|
|
67
|
+
this.connectingPromise = this.doConnect(params);
|
|
68
|
+
// Null connectingPromise once the underlying handshake actually settles
|
|
69
|
+
// (open / close / error). We do NOT null it on the timeout — the WS keeps
|
|
70
|
+
// trying in the background, and a concurrent connect() should join the
|
|
71
|
+
// same in-flight attempt instead of starting a new one.
|
|
72
|
+
this.connectingPromise.finally(() => {
|
|
73
|
+
this.connectingPromise = null;
|
|
74
|
+
});
|
|
75
|
+
// Block the caller for at most CONNECT_TIMEOUT_MS so the route resolver
|
|
76
|
+
// doesn't hang for ~21s on a TCP timeout when the BE is unreachable.
|
|
77
|
+
return this.raceWithTimeout(this.connectingPromise);
|
|
78
|
+
}
|
|
79
|
+
raceWithTimeout(p) {
|
|
80
|
+
return Promise.race([
|
|
81
|
+
p,
|
|
82
|
+
new Promise(resolve => setTimeout(resolve, NOTIFICATION_WS_CONFIG.CONNECT_TIMEOUT_MS)),
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
async doConnect(params) {
|
|
86
|
+
const baseUrl = Common.notificationWebSocketUrl;
|
|
87
|
+
if (!baseUrl) {
|
|
88
|
+
this.connectionState.set(WS_CONNECTION_STATE.DISCONNECTED);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!params.userId) {
|
|
92
|
+
console.warn('[NotificationWS] No userId provided — skipping connect');
|
|
93
|
+
this.connectionState.set(WS_CONNECTION_STATE.FAILED);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// BE doc: ws://host/ws?userId=...&appId=...&tenantId=...
|
|
97
|
+
const url = `${baseUrl}?userId=${encodeURIComponent(params.userId)}` +
|
|
98
|
+
(params.appId ? `&appId=${encodeURIComponent(params.appId)}` : '') +
|
|
99
|
+
(params.tenantId ? `&tenantId=${encodeURIComponent(params.tenantId)}` : '');
|
|
100
|
+
this.connectionState.set(WS_CONNECTION_STATE.CONNECTING);
|
|
101
|
+
return new Promise((resolve) => {
|
|
102
|
+
this.socket = webSocket({
|
|
103
|
+
url,
|
|
104
|
+
openObserver: {
|
|
105
|
+
next: () => {
|
|
106
|
+
this.connectionState.set(WS_CONNECTION_STATE.CONNECTED);
|
|
107
|
+
this.reconnectAttempt = 0;
|
|
108
|
+
resolve();
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
closeObserver: {
|
|
112
|
+
next: () => {
|
|
113
|
+
if (this.connectionState() !== WS_CONNECTION_STATE.CLOSED) {
|
|
114
|
+
this.connectionState.set(WS_CONNECTION_STATE.DISCONNECTED);
|
|
115
|
+
}
|
|
116
|
+
this.scheduleReconnect();
|
|
117
|
+
resolve();
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
this.socketSub = this.socket.subscribe({
|
|
122
|
+
next: (msg) => this.handleIncoming(msg),
|
|
123
|
+
error: (err) => {
|
|
124
|
+
console.error('[NotificationWS] Socket error:', err);
|
|
125
|
+
this.connectionState.set(WS_CONNECTION_STATE.FAILED);
|
|
126
|
+
this.scheduleReconnect();
|
|
127
|
+
resolve();
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Send a message over the socket. Silently no-ops if not connected.
|
|
134
|
+
*/
|
|
135
|
+
send(message) {
|
|
136
|
+
if (!this.socket || this.connectionState() !== WS_CONNECTION_STATE.CONNECTED) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
this.socket.next(message);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error('[NotificationWS] Failed to send message:', error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Tear down the socket.
|
|
148
|
+
*
|
|
149
|
+
* Sets explicitlyDisconnected so any pending retry timers become no-ops,
|
|
150
|
+
* then closes the socket and clears state.
|
|
151
|
+
*/
|
|
152
|
+
async disconnect() {
|
|
153
|
+
this.explicitlyDisconnected = true;
|
|
154
|
+
this.connectingPromise = null;
|
|
155
|
+
this.lastParams = null;
|
|
156
|
+
if (this.reconnectTimer) {
|
|
157
|
+
clearTimeout(this.reconnectTimer);
|
|
158
|
+
this.reconnectTimer = null;
|
|
159
|
+
}
|
|
160
|
+
if (this.socket) {
|
|
161
|
+
try {
|
|
162
|
+
this.socket.complete();
|
|
163
|
+
}
|
|
164
|
+
catch { /* ignore */ }
|
|
165
|
+
this.socket = null;
|
|
166
|
+
}
|
|
167
|
+
this.socketSub?.unsubscribe();
|
|
168
|
+
this.socketSub = null;
|
|
169
|
+
this.reconnectAttempt = 0;
|
|
170
|
+
this.connectionState.set(WS_CONNECTION_STATE.CLOSED);
|
|
171
|
+
}
|
|
172
|
+
// ----------- internal -----------
|
|
173
|
+
handleIncoming(message) {
|
|
174
|
+
if (!message || typeof message !== 'object')
|
|
175
|
+
return;
|
|
176
|
+
// Heartbeat: server pings every ~30s; we MUST reply or BE will close the socket.
|
|
177
|
+
if (message.type === WS_MESSAGE_TYPE.HEARTBEAT) {
|
|
178
|
+
this.send({ type: WS_MESSAGE_TYPE.HEARTBEAT });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (message.type === WS_MESSAGE_TYPE.CONNECTION_ESTABLISHED) {
|
|
182
|
+
this.connectionState.set(WS_CONNECTION_STATE.CONNECTED);
|
|
183
|
+
}
|
|
184
|
+
if (message.type === WS_MESSAGE_TYPE.ERROR) {
|
|
185
|
+
console.warn('[NotificationWS] Server error:', message.error);
|
|
186
|
+
}
|
|
187
|
+
this.messages$.next(message);
|
|
188
|
+
}
|
|
189
|
+
scheduleReconnect() {
|
|
190
|
+
if (this.explicitlyDisconnected)
|
|
191
|
+
return;
|
|
192
|
+
if (this.reconnectTimer)
|
|
193
|
+
return;
|
|
194
|
+
if (!this.lastParams)
|
|
195
|
+
return; // can't reconnect without params
|
|
196
|
+
const delays = NOTIFICATION_WS_CONFIG.RECONNECT_DELAYS_MS;
|
|
197
|
+
const delay = delays[Math.min(this.reconnectAttempt, delays.length - 1)];
|
|
198
|
+
this.reconnectAttempt++;
|
|
199
|
+
this.connectionState.set(WS_CONNECTION_STATE.RECONNECTING);
|
|
200
|
+
this.reconnectTimer = setTimeout(() => {
|
|
201
|
+
this.reconnectTimer = null;
|
|
202
|
+
if (!this.explicitlyDisconnected && this.lastParams) {
|
|
203
|
+
this.connect(this.lastParams);
|
|
204
|
+
}
|
|
205
|
+
}, delay);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Snapshot of current connection state — for debugging.
|
|
209
|
+
*/
|
|
210
|
+
isConnected() {
|
|
211
|
+
return this.connectionState() === WS_CONNECTION_STATE.CONNECTED;
|
|
212
|
+
}
|
|
213
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: NotificationWebSocketService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
214
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: NotificationWebSocketService, providedIn: 'root' }); }
|
|
215
|
+
}
|
|
216
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: NotificationWebSocketService, decorators: [{
|
|
217
|
+
type: Injectable,
|
|
218
|
+
args: [{ providedIn: 'root' }]
|
|
219
|
+
}] });
|
|
220
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm90aWZpY2F0aW9uLXdlYnNvY2tldC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vbGlicy9kYXRhLWFjY2Vzcy9zcmMvbGliL3RhYi1jb3JlLXV0aWxpdHkvY29yZS9zZXJ2aWNlL25vdGlmaWNhdGlvbi9ub3RpZmljYXRpb24td2Vic29ja2V0LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbkQsT0FBTyxFQUFFLE9BQU8sRUFBZ0IsTUFBTSxNQUFNLENBQUM7QUFDN0MsT0FBTyxFQUFFLFNBQVMsRUFBb0IsTUFBTSxnQkFBZ0IsQ0FBQztBQUM3RCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDdkQsT0FBTyxFQUNMLHNCQUFzQixFQUV0QixtQkFBbUIsRUFDbkIsZUFBZSxHQUVoQixNQUFNLGdEQUFnRCxDQUFDOztBQUV4RDs7Ozs7Ozs7Ozs7OztHQWFHO0FBRUgsTUFBTSxPQUFPLDRCQUE0QjtJQUR6QztRQUVVLFdBQU0sR0FBaUMsSUFBSSxDQUFDO1FBQzVDLGNBQVMsR0FBd0IsSUFBSSxDQUFDO1FBRTlDLHdFQUF3RTtRQUNoRSxzQkFBaUIsR0FBeUIsSUFBSSxDQUFDO1FBRXZELHdGQUF3RjtRQUNoRiwyQkFBc0IsR0FBRyxLQUFLLENBQUM7UUFFdkMsaUZBQWlGO1FBQ2pGLDhDQUE4QztRQUN0QyxlQUFVLEdBQThDLElBQUksQ0FBQztRQUVyRSwyREFBMkQ7UUFDbkQscUJBQWdCLEdBQUcsQ0FBQyxDQUFDO1FBQ3JCLG1CQUFjLEdBQXlDLElBQUksQ0FBQztRQUVwRSwyRUFBMkU7UUFDbEUsb0JBQWUsR0FBRyxNQUFNLENBQW9CLG1CQUFtQixDQUFDLFlBQVksQ0FBQyxDQUFDO1FBRXZGLG1GQUFtRjtRQUMxRSxjQUFTLEdBQUcsSUFBSSxP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SyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDekUsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFFeEIsSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsbUJBQW1CLENBQUMsWUFBWSxDQUFDLENBQUM7UUFFM0QsSUFBSSxDQUFDLGNBQWMsR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFO1lBQ3BDLElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSxDQUFDO1lBQzNCLElBQUksQ0FBQyxJQUFJLENBQUMsc0JBQXNCLElBQUksSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUNwRCxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUNoQyxDQUFDO1FBQ0gsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ1osQ0FBQztJQUVEOztPQUVHO0lBQ0gsV0FBVztRQUNULE9BQU8sSUFBSSxDQUFDLGVBQWUsRUFBRSxLQUFLLG1CQUFtQixDQUFDLFNBQVMsQ0FBQztJQUNsRSxDQUFDOytHQXpOVSw0QkFBNEI7bUhBQTVCLDRCQUE0QixjQURmLE1BQU07OzRGQUNuQiw0QkFBNEI7a0JBRHhDLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgc2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IFN1YmplY3QsIFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xyXG5pbXBvcnQgeyB3ZWJTb2NrZXQsIFdlYlNvY2tldFN1YmplY3QgfSBmcm9tICdyeGpzL3dlYlNvY2tldCc7XHJcbmltcG9ydCB7IENvbW1vbiB9IGZyb20gJy4uLy4uLy4uL2FwcC9jb25zdGFudHMvY29tbW9uJztcclxuaW1wb3J0IHtcclxuICBOT1RJRklDQVRJT05fV1NfQ09ORklHLFxyXG4gIE5vdGlmaWNhdGlvbldlYlNvY2tldENvbm5lY3RQYXJhbXMsXHJcbiAgV1NfQ09OTkVDVElPTl9TVEFURSxcclxuICBXU19NRVNTQUdFX1RZUEUsXHJcbiAgV3NDb25uZWN0aW9uU3RhdGUsXHJcbn0gZnJvbSAnLi4vLi4vLi4vYXBwL2ludGVyZmFjZXMvbm90aWZpY2F0aW9uLmludGVyZmFjZSc7XHJcblxyXG4vKipcclxuICogTG93LWxldmVsIE5vdGlmaWNhdGlvbiBXZWJTb2NrZXQgdHJhbnNwb3J0LlxyXG4gKlxyXG4gKiBQdXJlIOKAlCBubyBzdG9yYWdlIC8gYXV0aCBjb3VwbGluZy4gVGhlIGNhbGxlciAoU0RLIGZhY2FkZSBvciBhcHAgbGF5ZXIpXHJcbiAqIHN1cHBsaWVzIGFsbCBhdXRoIGlkZW50aWZpZXJzIHZpYSBgY29ubmVjdChwYXJhbXMpYC5cclxuICpcclxuICogTWlycm9ycyB0aGUgU2lnbmFsUiBpbmZyYXN0cnVjdHVyZSBwYXR0ZXJuOlxyXG4gKiAgLSBjb25uZWN0KCkgaXMgaWRlbXBvdGVudCwgZGVkdXBlcyBjb25jdXJyZW50IGNhbGxzLCB0ZWFycyBkb3duIHN0YWxlIHNvY2tldHNcclxuICogIC0gZGlzY29ubmVjdCgpIGZsaXBzIGFuIGV4cGxpY2l0LWRpc2Nvbm5lY3RlZCBmbGFnIHNvIHJldHJpZXMgZG9uJ3QgcmV2aXZlXHJcbiAqICAtIHN0YXRlIGV4cG9zZWQgYXMgQW5ndWxhciBzaWduYWxcclxuICogIC0gaW5ib3VuZCBtZXNzYWdlcyBmb3J3YXJkZWQgb24gYG1lc3NhZ2VzJGAgZm9yIGhpZ2hlci1sZXZlbCBzZXJ2aWNlIHRvIHJvdXRlXHJcbiAqXHJcbiAqIEludGVybmFsIHVzZSDigJQgY29uc3VtZXJzIHNob3VsZCB1c2UgYE5vdGlmaWNhdGlvbkluYm94U2VydmljZWAgaW5zdGVhZC5cclxuICovXHJcbkBJbmplY3RhYmxlKHsgcHJvdmlkZWRJbjogJ3Jvb3QnIH0pXHJcbmV4cG9ydCBjbGFzcyBOb3RpZmljYXRpb25XZWJTb2NrZXRTZXJ2aWNlIHtcclxuICBwcml2YXRlIHNvY2tldDogV2ViU29ja2V0U3ViamVjdDxhbnk+IHwgbnVsbCA9IG51bGw7XHJcbiAgcHJpdmF0ZSBzb2NrZXRTdWI6IFN1YnNjcmlwdGlvbiB8IG51bGwgPSBudWxsO1xyXG5cclxuICAvLyBJbi1mbGlnaHQgY29ubmVjdCBwcm9taXNlIOKAlCB1c2VkIHRvIGRlZHVwZSBjb25jdXJyZW50IGNvbm5lY3QoKSBjYWxsc1xyXG4gIHByaXZhdGUgY29ubmVjdGluZ1Byb21pc2U6IFByb21pc2U8dm9pZD4gfCBudWxsID0gbnVsbDtcclxuXHJcbiAgLy8gU2V0IHRydWUgb24gZXhwbGljaXQgZGlzY29ubmVjdCgpIHNvIHNjaGVkdWxlZCByZXRyaWVzIGRvbid0IHJlc3VycmVjdCB0aGUgY29ubmVjdGlvblxyXG4gIHByaXZhdGUgZXhwbGljaXRseURpc2Nvbm5lY3RlZCA9IGZhbHNlO1xyXG5cclxuICAvLyBMYXN0IGNvbm5lY3QgcGFyYW1zIOKAlCBrZXB0IHNvIHJlY29ubmVjdCBjYW4gcmVidWlsZCB0aGUgVVJMIHdpdGhvdXQgdGhlIGNhbGxlclxyXG4gIC8vIHJlLXN1cHBseWluZyB0aGVtLiBDbGVhcmVkIG9uIGRpc2Nvbm5lY3QoKS5cclxuICBwcml2YXRlIGxhc3RQYXJhbXM6IE5vdGlmaWNhdGlvbldlYlNvY2tldENvbm5lY3RQYXJhbXMgfCBudWxsID0gbnVsbDtcclxuXHJcbiAgLy8gUmVjb25uZWN0IGF0dGVtcHQgY291bnRlciAocmVzZXRzIG9uIHN1Y2Nlc3NmdWwgY29ubmVjdClcclxuICBwcml2YXRlIHJlY29ubmVjdEF0dGVtcHQgPSAwO1xyXG4gIHByaXZhdGUgcmVjb25uZWN0VGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgbnVsbCA9IG51bGw7XHJcblxyXG4gIC8vIENvbm5lY3Rpb24gc3RhdGUgYXMgQW5ndWxhciBzaWduYWwg4oCUIGZvciBVSSBpbmRpY2F0b3JzIC8gcm91dGUgcmVzb2x2ZXJzXHJcbiAgcmVhZG9ubHkgY29ubmVjdGlvblN0YXRlID0gc2lnbmFsPFdzQ29ubmVjdGlvblN0YXRlPihXU19DT05ORUNUSU9OX1NUQVRFLkRJU0NPTk5FQ1RFRCk7XHJcblxyXG4gIC8vIEluYm91bmQgbWVzc2FnZSBzdHJlYW0g4oCUIE5vdGlmaWNhdGlvbkluYm94U2VydmljZSBzdWJzY3JpYmVzIGFuZCByb3V0ZXMgcGVyIHR5cGVcclxuICByZWFkb25seSBtZXNzYWdlcyQgPSBuZXcgU3ViamVjdDxhbnk+KCk7XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tIHB1YmxpYyBBUEkgLS0tLS0tLS0tLS1cclxuXHJcbiAgLyoqXHJcbiAgICogRXN0YWJsaXNoIHRoZSBXZWJTb2NrZXQgY29ubmVjdGlvbi5cclxuICAgKlxyXG4gICAqIElkZW1wb3RlbnQg4oCUIGlmIGFscmVhZHkgT1BFTiwgcmVzb2x2ZXMgaW1tZWRpYXRlbHkuXHJcbiAgICogSW4tZmxpZ2h0IGNhbGxzIGFyZSBkZS1kdXBsaWNhdGVkLlxyXG4gICAqIEFueSBzdGFsZSAobm9uLU9QRU4pIHNvY2tldCBpcyB0b3JuIGRvd24gYmVmb3JlIHJlYnVpbGQuXHJcbiAgICovXHJcbiAgYXN5bmMgY29ubmVjdChwYXJhbXM6IE5vdGlmaWNhdGlvbldlYlNvY2tldENvbm5lY3RQYXJhbXMpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIHRoaXMuZXhwbGljaXRseURpc2Nvbm5lY3RlZCA9IGZhbHNlO1xyXG4gICAgdGhpcy5sYXN0UGFyYW1zID0gcGFyYW1zO1xyXG5cclxuICAgIGlmICh0aGlzLmNvbm5lY3Rpb25TdGF0ZSgpID09PSBXU19DT05ORUNUSU9OX1NUQVRFLkNPTk5FQ1RFRCkge1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgYSBjb25uZWN0IGlzIGFscmVhZHkgaW4gZmxpZ2h0LCB3YWl0IGZvciBpdCAoY2FwcGVkKSDigJQgZG9uJ3Qgc3RhcnQgYSBzZWNvbmQgb25lLlxyXG4gICAgaWYgKHRoaXMuY29ubmVjdGluZ1Byb21pc2UpIHtcclxuICAgICAgcmV0dXJuIHRoaXMucmFjZVdpdGhUaW1lb3V0KHRoaXMuY29ubmVjdGluZ1Byb21pc2UpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICh0aGlzLnNvY2tldCkge1xyXG4gICAgICB0cnkgeyB0aGlzLnNvY2tldC5jb21wbGV0ZSgpOyB9IGNhdGNoIHsgLyogaWdub3JlICovIH1cclxuICAgICAgdGhpcy5zb2NrZXQgPSBudWxsO1xyXG4gICAgICB0aGlzLnNvY2tldFN1Yj8udW5zdWJzY3JpYmUoKTtcclxuICAgICAgdGhpcy5zb2NrZXRTdWIgPSBudWxsO1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuY29ubmVjdGluZ1Byb21pc2UgPSB0aGlzLmRvQ29ubmVjdChwYXJhbXMpO1xyXG4gICAgLy8gTnVsbCBjb25uZWN0aW5nUHJvbWlzZSBvbmNlIHRoZSB1bmRlcmx5aW5nIGhhbmRzaGFrZSBhY3R1YWxseSBzZXR0bGVzXHJcbiAgICAvLyAob3BlbiAvIGNsb3NlIC8gZXJyb3IpLiBXZSBkbyBOT1QgbnVsbCBpdCBvbiB0aGUgdGltZW91dCDigJQgdGhlIFdTIGtlZXBzXHJcbiAgICAvLyB0cnlpbmcgaW4gdGhlIGJhY2tncm91bmQsIGFuZCBhIGNvbmN1cnJlbnQgY29ubmVjdCgpIHNob3VsZCBqb2luIHRoZVxyXG4gICAgLy8gc2FtZSBpbi1mbGlnaHQgYXR0ZW1wdCBpbnN0ZWFkIG9mIHN0YXJ0aW5nIGEgbmV3IG9uZS5cclxuICAgIHRoaXMuY29ubmVjdGluZ1Byb21pc2UuZmluYWxseSgoKSA9PiB7XHJcbiAgICAgIHRoaXMuY29ubmVjdGluZ1Byb21pc2UgPSBudWxsO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQmxvY2sgdGhlIGNhbGxlciBmb3IgYXQgbW9zdCBDT05ORUNUX1RJTUVPVVRfTVMgc28gdGhlIHJvdXRlIHJlc29sdmVyXHJcbiAgICAvLyBkb2Vzbid0IGhhbmcgZm9yIH4yMXMgb24gYSBUQ1AgdGltZW91dCB3aGVuIHRoZSBCRSBpcyB1bnJlYWNoYWJsZS5cclxuICAgIHJldHVybiB0aGlzLnJhY2VXaXRoVGltZW91dCh0aGlzLmNvbm5lY3RpbmdQcm9taXNlKTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgcmFjZVdpdGhUaW1lb3V0KHA6IFByb21pc2U8dm9pZD4pOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIHJldHVybiBQcm9taXNlLnJhY2UoW1xyXG4gICAgICBwLFxyXG4gICAgICBuZXcgUHJvbWlzZTx2b2lkPihyZXNvbHZlID0+XHJcbiAgICAgICAgc2V0VGltZW91dChyZXNvbHZlLCBOT1RJRklDQVRJT05fV1NfQ09ORklHLkNPTk5FQ1RfVElNRU9VVF9NUylcclxuICAgICAgKSxcclxuICAgIF0pO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBhc3luYyBkb0Nvbm5lY3QocGFyYW1zOiBOb3RpZmljYXRpb25XZWJTb2NrZXRDb25uZWN0UGFyYW1zKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBjb25zdCBiYXNlVXJsID0gQ29tbW9uLm5vdGlmaWNhdGlvbldlYlNvY2tldFVybDtcclxuICAgIGlmICghYmFzZVVybCkge1xyXG4gICAgICB0aGlzLmNvbm5lY3Rpb25TdGF0ZS5zZXQoV1NfQ09OTkVDVElPTl9TVEFURS5ESVNDT05ORUNURUQpO1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFwYXJhbXMudXNlcklkKSB7XHJcbiAgICAgIGNvbnNvbGUud2FybignW05vdGlmaWNhdGlvbldTXSBObyB1c2VySWQgcHJvdmlkZWQg4oCUIHNraXBwaW5nIGNvbm5lY3QnKTtcclxuICAgICAgdGhpcy5jb25uZWN0aW9uU3RhdGUuc2V0KFdTX0NPTk5FQ1RJT05fU1RBVEUuRkFJTEVEKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEJFIGRvYzogd3M6Ly9ob3N0L3dzP3VzZXJJZD0uLi4mYXBwSWQ9Li4uJnRlbmFudElkPS4uLlxyXG4gICAgY29uc3QgdXJsID0gYCR7YmFzZVVybH0/dXNlcklkPSR7ZW5jb2RlVVJJQ29tcG9uZW50KHBhcmFtcy51c2VySWQpfWAgK1xyXG4gICAgICAocGFyYW1zLmFwcElkID8gYCZhcHBJZD0ke2VuY29kZVVSSUNvbXBvbmVudChwYXJhbXMuYXBwSWQpfWAgOiAnJykgK1xyXG4gICAgICAocGFyYW1zLnRlbmFudElkID8gYCZ0ZW5hbnRJZD0ke2VuY29kZVVSSUNvbXBvbmVudChwYXJhbXMudGVuYW50SWQpfWAgOiAnJyk7XHJcblxyXG4gICAgdGhpcy5jb25uZWN0aW9uU3RhdGUuc2V0KFdTX0NPTk5FQ1RJT05fU1RBVEUuQ09OTkVDVElORyk7XHJcblxyXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB7XHJcbiAgICAgIHRoaXMuc29ja2V0ID0gd2ViU29ja2V0PGFueT4oe1xyXG4gICAgICAgIHVybCxcclxuICAgICAgICBvcGVuT2JzZXJ2ZXI6IHtcclxuICAgICAgICAgIG5leHQ6ICgpID0+IHtcclxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uU3RhdGUuc2V0KFdTX0NPTk5FQ1RJT05fU1RBVEUuQ09OTkVDVEVEKTtcclxuICAgICAgICAgICAgdGhpcy5yZWNvbm5lY3RBdHRlbXB0ID0gMDtcclxuICAgICAgICAgICAgcmVzb2x2ZSgpO1xyXG4gICAgICAgICAgfSxcclxuICAgICAgICB9LFxyXG4gICAgICAgIGNsb3NlT2JzZXJ2ZXI6IHtcclxuICAgICAgICAgIG5leHQ6ICgpID0+IHtcclxuICAgICAgICAgICAgaWYgKHRoaXMuY29ubmVjdGlvblN0YXRlKCkgIT09IFdTX0NPTk5FQ1RJT05fU1RBVEUuQ0xPU0VEKSB7XHJcbiAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uU3RhdGUuc2V0KFdTX0NPTk5FQ1RJT05fU1RBVEUuRElTQ09OTkVDVEVEKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB0aGlzLnNjaGVkdWxlUmVjb25uZWN0KCk7XHJcbiAgICAgICAgICAgIHJlc29sdmUoKTtcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgfSxcclxuICAgICAgfSk7XHJcblxyXG4gICAgICB0aGlzLnNvY2tldFN1YiA9IHRoaXMuc29ja2V0LnN1YnNjcmliZSh7XHJcbiAgICAgICAgbmV4dDogKG1zZykgPT4gdGhpcy5oYW5kbGVJbmNvbWluZyhtc2cpLFxyXG4gICAgICAgIGVycm9yOiAoZXJyKSA9PiB7XHJcbiAgICAgICAgICBjb25zb2xlLmVycm9yKCdbTm90aWZpY2F0aW9uV1NdIFNvY2tldCBlcnJvcjonLCBlcnIpO1xyXG4gICAgICAgICAgdGhpcy5jb25uZWN0aW9uU3RhdGUuc2V0KFdTX0NPTk5FQ1RJT05fU1RBVEUuRkFJTEVEKTtcclxuICAgICAgICAgIHRoaXMuc2NoZWR1bGVSZWNvbm5lY3QoKTtcclxuICAgICAgICAgIHJlc29sdmUoKTtcclxuICAgICAgICB9LFxyXG4gICAgICB9KTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2VuZCBhIG1lc3NhZ2Ugb3ZlciB0aGUgc29ja2V0LiBTaWxlbnRseSBuby1vcHMgaWYgbm90IGNvbm5lY3RlZC5cclxuICAgKi9cclxuICBzZW5kKG1lc3NhZ2U6IFJlY29yZDxzdHJpbmcsIGFueT4pOiB2b2lkIHtcclxuICAgIGlmICghdGhpcy5zb2NrZXQgfHwgdGhpcy5jb25uZWN0aW9uU3RhdGUoKSAhPT0gV1NfQ09OTkVDVElPTl9TVEFURS5DT05ORUNURUQpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gICAgdHJ5IHtcclxuICAgICAgdGhpcy5zb2NrZXQubmV4dChtZXNzYWdlKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ1tOb3RpZmljYXRpb25XU10gRmFpbGVkIHRvIHNlbmQgbWVzc2FnZTonLCBlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUZWFyIGRvd24gdGhlIHNvY2tldC5cclxuICAgKlxyXG4gICAqIFNldHMgZXhwbGljaXRseURpc2Nvbm5lY3RlZCBzbyBhbnkgcGVuZGluZyByZXRyeSB0aW1lcnMgYmVjb21lIG5vLW9wcyxcclxuICAgKiB0aGVuIGNsb3NlcyB0aGUgc29ja2V0IGFuZCBjbGVhcnMgc3RhdGUuXHJcbiAgICovXHJcbiAgYXN5bmMgZGlzY29ubmVjdCgpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIHRoaXMuZXhwbGljaXRseURpc2Nvbm5lY3RlZCA9IHRydWU7XHJcbiAgICB0aGlzLmNvbm5lY3RpbmdQcm9taXNlID0gbnVsbDtcclxuICAgIHRoaXMubGFzdFBhcmFtcyA9IG51bGw7XHJcblxyXG4gICAgaWYgKHRoaXMucmVjb25uZWN0VGltZXIpIHtcclxuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMucmVjb25uZWN0VGltZXIpO1xyXG4gICAgICB0aGlzLnJlY29ubmVjdFRpbWVyID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICBpZiAodGhpcy5zb2NrZXQpIHtcclxuICAgICAgdHJ5IHsgdGhpcy5zb2NrZXQuY29tcGxldGUoKTsgfSBjYXRjaCB7IC8qIGlnbm9yZSAqLyB9XHJcbiAgICAgIHRoaXMuc29ja2V0ID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLnNvY2tldFN1Yj8udW5zdWJzY3JpYmUoKTtcclxuICAgIHRoaXMuc29ja2V0U3ViID0gbnVsbDtcclxuXHJcbiAgICB0aGlzLnJlY29ubmVjdEF0dGVtcHQgPSAwO1xyXG4gICAgdGhpcy5jb25uZWN0aW9uU3RhdGUuc2V0KFdTX0NPTk5FQ1RJT05fU1RBVEUuQ0xPU0VEKTtcclxuICB9XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tIGludGVybmFsIC0tLS0tLS0tLS0tXHJcblxyXG4gIHByaXZhdGUgaGFuZGxlSW5jb21pbmcobWVzc2FnZTogYW55KTogdm9pZCB7XHJcbiAgICBpZiAoIW1lc3NhZ2UgfHwgdHlwZW9mIG1lc3NhZ2UgIT09ICdvYmplY3QnKSByZXR1cm47XHJcblxyXG4gICAgLy8gSGVhcnRiZWF0OiBzZXJ2ZXIgcGluZ3MgZXZlcnkgfjMwczsgd2UgTVVTVCByZXBseSBvciBCRSB3aWxsIGNsb3NlIHRoZSBzb2NrZXQuXHJcbiAgICBpZiAobWVzc2FnZS50eXBlID09PSBXU19NRVNTQUdFX1RZUEUuSEVBUlRCRUFUKSB7XHJcbiAgICAgIHRoaXMuc2VuZCh7IHR5cGU6IFdTX01FU1NBR0VfVFlQRS5IRUFSVEJFQVQgfSk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICBpZiAobWVzc2FnZS50eXBlID09PSBXU19NRVNTQUdFX1RZUEUuQ09OTkVDVElPTl9FU1RBQkxJU0hFRCkge1xyXG4gICAgICB0aGlzLmNvbm5lY3Rpb25TdGF0ZS5zZXQoV1NfQ09OTkVDVElPTl9TVEFURS5DT05ORUNURUQpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChtZXNzYWdlLnR5cGUgPT09IFdTX01FU1NBR0VfVFlQRS5FUlJPUikge1xyXG4gICAgICBjb25zb2xlLndhcm4oJ1tOb3RpZmljYXRpb25XU10gU2VydmVyIGVycm9yOicsIG1lc3NhZ2UuZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMubWVzc2FnZXMkLm5leHQobWVzc2FnZSk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIHNjaGVkdWxlUmVjb25uZWN0KCk6IHZvaWQge1xyXG4gICAgaWYgKHRoaXMuZXhwbGljaXRseURpc2Nvbm5lY3RlZCkgcmV0dXJuO1xyXG4gICAgaWYgKHRoaXMucmVjb25uZWN0VGltZXIpIHJldHVybjtcclxuICAgIGlmICghdGhpcy5sYXN0UGFyYW1zKSByZXR1cm47IC8vIGNhbid0IHJlY29ubmVjdCB3aXRob3V0IHBhcmFtc1xyXG5cclxuICAgIGNvbnN0IGRlbGF5cyA9IE5PVElGSUNBVElPTl9XU19DT05GSUcuUkVDT05ORUNUX0RFTEFZU19NUztcclxuICAgIGNvbnN0IGRlbGF5ID0gZGVsYXlzW01hdGgubWluKHRoaXMucmVjb25uZWN0QXR0ZW1wdCwgZGVsYXlzLmxlbmd0aCAtIDEpXTtcclxuICAgIHRoaXMucmVjb25uZWN0QXR0ZW1wdCsrO1xyXG5cclxuICAgIHRoaXMuY29ubmVjdGlvblN0YXRlLnNldChXU19DT05ORUNUSU9OX1NUQVRFLlJFQ09OTkVDVElORyk7XHJcblxyXG4gICAgdGhpcy5yZWNvbm5lY3RUaW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICB0aGlzLnJlY29ubmVjdFRpbWVyID0gbnVsbDtcclxuICAgICAgaWYgKCF0aGlzLmV4cGxpY2l0bHlEaXNjb25uZWN0ZWQgJiYgdGhpcy5sYXN0UGFyYW1zKSB7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0KHRoaXMubGFzdFBhcmFtcyk7XHJcbiAgICAgIH1cclxuICAgIH0sIGRlbGF5KTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNuYXBzaG90IG9mIGN1cnJlbnQgY29ubmVjdGlvbiBzdGF0ZSDigJQgZm9yIGRlYnVnZ2luZy5cclxuICAgKi9cclxuICBpc0Nvbm5lY3RlZCgpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLmNvbm5lY3Rpb25TdGF0ZSgpID09PSBXU19DT05ORUNUSU9OX1NUQVRFLkNPTk5FQ1RFRDtcclxuICB9XHJcbn1cclxuIl19
|