valtech-components 2.0.417 → 2.0.418

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.
@@ -1,503 +0,0 @@
1
- /**
2
- * Messaging Service (FCM)
3
- *
4
- * Servicio para Firebase Cloud Messaging (Push Notifications).
5
- * Permite solicitar permisos, obtener tokens, escuchar mensajes y manejar
6
- * navegación (deep linking) cuando el usuario toca una notificación.
7
- */
8
- import { inject, Injectable, NgZone, PLATFORM_ID } from '@angular/core';
9
- import { isPlatformBrowser } from '@angular/common';
10
- import { Messaging, getToken, deleteToken, onMessage } from '@angular/fire/messaging';
11
- import { Subject, BehaviorSubject } from 'rxjs';
12
- import { VALTECH_FIREBASE_CONFIG } from './config';
13
- import * as i0 from "@angular/core";
14
- /**
15
- * Servicio para Firebase Cloud Messaging (FCM).
16
- *
17
- * Permite recibir notificaciones push en la aplicación web.
18
- * Requiere VAPID key configurada en ValtechFirebaseConfig.
19
- *
20
- * @example
21
- * ```typescript
22
- * @Component({...})
23
- * export class NotificationComponent {
24
- * private messaging = inject(MessagingService);
25
- *
26
- * token = signal<string | null>(null);
27
- *
28
- * async enableNotifications() {
29
- * // Solicitar permiso y obtener token
30
- * const token = await this.messaging.requestPermission();
31
- *
32
- * if (token) {
33
- * this.token.set(token);
34
- * // Enviar token a tu backend para almacenarlo
35
- * await this.backend.registerDeviceToken(token);
36
- * }
37
- * }
38
- *
39
- * // Escuchar mensajes en foreground
40
- * messages$ = this.messaging.onMessage();
41
- * }
42
- * ```
43
- */
44
- export class MessagingService {
45
- constructor() {
46
- this.messaging = inject(Messaging, { optional: true });
47
- this.config = inject(VALTECH_FIREBASE_CONFIG);
48
- this.platformId = inject(PLATFORM_ID);
49
- this.ngZone = inject(NgZone);
50
- this.messageSubject = new Subject();
51
- this.notificationClickSubject = new Subject();
52
- this.stateSubject = new BehaviorSubject({
53
- token: null,
54
- permission: 'default',
55
- isSupported: false,
56
- });
57
- this.initializeMessaging();
58
- }
59
- // ===========================================================================
60
- // INICIALIZACIÓN
61
- // ===========================================================================
62
- /**
63
- * Inicializa el servicio de messaging
64
- */
65
- async initializeMessaging() {
66
- if (!isPlatformBrowser(this.platformId)) {
67
- return;
68
- }
69
- const supported = await this.checkSupport();
70
- const permission = this.getPermissionState();
71
- this.stateSubject.next({
72
- ...this.stateSubject.value,
73
- isSupported: supported,
74
- permission,
75
- });
76
- // Si ya tiene permiso, configurar listeners
77
- if (supported && permission === 'granted') {
78
- this.setupMessageListener();
79
- }
80
- // Escuchar mensajes del Service Worker (clicks en notificaciones background)
81
- this.setupServiceWorkerListener();
82
- }
83
- /**
84
- * Configura listener para mensajes del Service Worker.
85
- * Recibe eventos cuando el usuario hace click en una notificación background.
86
- */
87
- setupServiceWorkerListener() {
88
- if (!isPlatformBrowser(this.platformId) || !('serviceWorker' in navigator)) {
89
- return;
90
- }
91
- navigator.serviceWorker.addEventListener('message', (event) => {
92
- // Verificar que es un mensaje de notificación click
93
- if (event.data?.type === 'NOTIFICATION_CLICK') {
94
- this.ngZone.run(() => {
95
- const notification = event.data.notification;
96
- const action = this.extractActionFromData(notification.data);
97
- this.notificationClickSubject.next({
98
- notification,
99
- action,
100
- timestamp: new Date(),
101
- });
102
- });
103
- }
104
- });
105
- }
106
- /**
107
- * Verifica si FCM está soportado en el navegador actual
108
- */
109
- async checkSupport() {
110
- if (!isPlatformBrowser(this.platformId)) {
111
- return false;
112
- }
113
- // Verificar APIs necesarias
114
- if (!('Notification' in window)) {
115
- return false;
116
- }
117
- if (!('serviceWorker' in navigator)) {
118
- return false;
119
- }
120
- // Verificar que messaging esté disponible
121
- if (!this.messaging) {
122
- return false;
123
- }
124
- return true;
125
- }
126
- // ===========================================================================
127
- // PERMISOS Y TOKEN
128
- // ===========================================================================
129
- /**
130
- * Solicita permiso de notificaciones y obtiene el token FCM.
131
- *
132
- * @returns Token FCM si se otorgó permiso, null si se denegó
133
- *
134
- * @example
135
- * ```typescript
136
- * const token = await messaging.requestPermission();
137
- * if (token) {
138
- * console.log('Token FCM:', token);
139
- * // Enviar a backend
140
- * } else {
141
- * console.log('Permiso denegado o no soportado');
142
- * }
143
- * ```
144
- */
145
- async requestPermission() {
146
- if (!await this.isSupported()) {
147
- console.warn('FCM no está soportado en este navegador');
148
- return null;
149
- }
150
- try {
151
- // Solicitar permiso de notificaciones
152
- const permission = await Notification.requestPermission();
153
- this.stateSubject.next({
154
- ...this.stateSubject.value,
155
- permission: permission,
156
- });
157
- if (permission !== 'granted') {
158
- console.warn('Permiso de notificaciones denegado');
159
- return null;
160
- }
161
- // Obtener token FCM
162
- const token = await this.getToken();
163
- if (token) {
164
- // Configurar listener de mensajes
165
- this.setupMessageListener();
166
- }
167
- return token;
168
- }
169
- catch (error) {
170
- console.error('Error solicitando permiso de notificaciones:', error);
171
- return null;
172
- }
173
- }
174
- /**
175
- * Obtiene el token FCM actual (sin solicitar permiso).
176
- *
177
- * @returns Token FCM si está disponible, null si no
178
- *
179
- * @example
180
- * ```typescript
181
- * const token = await messaging.getToken();
182
- * ```
183
- */
184
- async getToken() {
185
- if (!this.messaging) {
186
- return null;
187
- }
188
- const vapidKey = this.config.messagingVapidKey;
189
- if (!vapidKey) {
190
- console.warn('VAPID key no configurada. FCM no funcionará.');
191
- return null;
192
- }
193
- try {
194
- const token = await getToken(this.messaging, { vapidKey });
195
- this.stateSubject.next({
196
- ...this.stateSubject.value,
197
- token,
198
- });
199
- return token;
200
- }
201
- catch (error) {
202
- console.error('Error obteniendo token FCM:', error);
203
- return null;
204
- }
205
- }
206
- /**
207
- * Elimina el token FCM actual (unsubscribe de notificaciones).
208
- *
209
- * @example
210
- * ```typescript
211
- * await messaging.deleteToken();
212
- * console.log('Token eliminado, no recibirá más notificaciones');
213
- * ```
214
- */
215
- async deleteToken() {
216
- if (!this.messaging) {
217
- return;
218
- }
219
- try {
220
- await deleteToken(this.messaging);
221
- this.stateSubject.next({
222
- ...this.stateSubject.value,
223
- token: null,
224
- });
225
- // Limpiar listener de mensajes
226
- if (this.unsubscribeOnMessage) {
227
- this.unsubscribeOnMessage();
228
- this.unsubscribeOnMessage = undefined;
229
- }
230
- }
231
- catch (error) {
232
- console.error('Error eliminando token FCM:', error);
233
- throw new Error('No se pudo eliminar el token de notificaciones');
234
- }
235
- }
236
- // ===========================================================================
237
- // MENSAJES
238
- // ===========================================================================
239
- /**
240
- * Observable de mensajes recibidos en foreground.
241
- *
242
- * IMPORTANTE: Los mensajes en background son manejados por el Service Worker.
243
- *
244
- * @returns Observable que emite cuando llega un mensaje en foreground
245
- *
246
- * @example
247
- * ```typescript
248
- * messaging.onMessage().subscribe(payload => {
249
- * console.log('Mensaje recibido:', payload);
250
- * // Mostrar notificación custom o actualizar UI
251
- * });
252
- * ```
253
- */
254
- onMessage() {
255
- return this.messageSubject.asObservable();
256
- }
257
- /**
258
- * Configura el listener de mensajes en foreground
259
- */
260
- setupMessageListener() {
261
- if (!this.messaging || this.unsubscribeOnMessage) {
262
- return;
263
- }
264
- this.unsubscribeOnMessage = onMessage(this.messaging, (payload) => {
265
- const notification = {
266
- title: payload.notification?.title,
267
- body: payload.notification?.body,
268
- image: payload.notification?.image,
269
- data: payload.data,
270
- messageId: payload.messageId,
271
- };
272
- this.messageSubject.next(notification);
273
- });
274
- }
275
- // ===========================================================================
276
- // ESTADO Y UTILIDADES
277
- // ===========================================================================
278
- /**
279
- * Obtiene el estado actual del permiso de notificaciones.
280
- *
281
- * @returns 'granted' | 'denied' | 'default'
282
- *
283
- * @example
284
- * ```typescript
285
- * const permission = messaging.getPermissionState();
286
- * if (permission === 'granted') {
287
- * // Ya tiene permiso
288
- * } else if (permission === 'default') {
289
- * // Puede solicitar permiso
290
- * } else {
291
- * // Denegado, debe habilitar manualmente
292
- * }
293
- * ```
294
- */
295
- getPermissionState() {
296
- if (!isPlatformBrowser(this.platformId)) {
297
- return 'default';
298
- }
299
- if (!('Notification' in window)) {
300
- return 'denied';
301
- }
302
- return Notification.permission;
303
- }
304
- /**
305
- * Verifica si FCM está soportado en el navegador actual.
306
- *
307
- * @returns true si FCM está soportado
308
- *
309
- * @example
310
- * ```typescript
311
- * if (await messaging.isSupported()) {
312
- * // Puede usar notificaciones push
313
- * } else {
314
- * // Navegador no soporta o no tiene Service Worker
315
- * }
316
- * ```
317
- */
318
- async isSupported() {
319
- return this.checkSupport();
320
- }
321
- /**
322
- * Obtiene el token actual sin hacer request.
323
- *
324
- * @returns Token almacenado o null
325
- */
326
- get currentToken() {
327
- return this.stateSubject.value.token;
328
- }
329
- /**
330
- * Observable del estado completo del servicio de messaging.
331
- */
332
- get state$() {
333
- return this.stateSubject.asObservable();
334
- }
335
- /**
336
- * Verifica si el usuario ya otorgó permiso de notificaciones.
337
- */
338
- get hasPermission() {
339
- return this.stateSubject.value.permission === 'granted';
340
- }
341
- // ===========================================================================
342
- // DEEP LINKING / NAVEGACIÓN
343
- // ===========================================================================
344
- /**
345
- * Observable de clicks en notificaciones.
346
- *
347
- * Emite cuando el usuario hace click en una notificación (foreground o background).
348
- * Usa este observable para navegar a la página correspondiente.
349
- *
350
- * @returns Observable que emite NotificationClickEvent
351
- *
352
- * @example
353
- * ```typescript
354
- * @Component({...})
355
- * export class AppComponent {
356
- * private messaging = inject(MessagingService);
357
- * private router = inject(Router);
358
- *
359
- * constructor() {
360
- * this.messaging.onNotificationClick().subscribe(event => {
361
- * if (event.action.route) {
362
- * this.router.navigate([event.action.route], {
363
- * queryParams: event.action.queryParams
364
- * });
365
- * }
366
- * });
367
- * }
368
- * }
369
- * ```
370
- */
371
- onNotificationClick() {
372
- return this.notificationClickSubject.asObservable();
373
- }
374
- /**
375
- * Extrae la acción de navegación de los datos de una notificación.
376
- *
377
- * Busca campos específicos en el payload de datos:
378
- * - `route`: Ruta interna de la app (ej: '/orders/123')
379
- * - `url`: URL externa (ej: 'https://example.com')
380
- * - `action_type`: Tipo de acción personalizada
381
- * - Campos con prefijo `action_`: Datos adicionales
382
- *
383
- * @param data - Datos del payload de la notificación
384
- * @returns Acción de navegación extraída
385
- *
386
- * @example
387
- * ```typescript
388
- * // Payload desde el backend:
389
- * // { route: '/orders/123', action_type: 'view_order', action_orderId: '123' }
390
- *
391
- * const action = messaging.extractActionFromData(notification.data);
392
- * // { route: '/orders/123', actionType: 'view_order', actionData: { orderId: '123' } }
393
- * ```
394
- */
395
- extractActionFromData(data) {
396
- if (!data) {
397
- return {};
398
- }
399
- const action = {};
400
- // Ruta interna
401
- if (data['route']) {
402
- action.route = data['route'];
403
- }
404
- // URL externa
405
- if (data['url']) {
406
- action.url = data['url'];
407
- }
408
- // Tipo de acción
409
- if (data['action_type']) {
410
- action.actionType = data['action_type'];
411
- }
412
- // Query params (puede venir como JSON string)
413
- if (data['query_params']) {
414
- try {
415
- action.queryParams = JSON.parse(data['query_params']);
416
- }
417
- catch {
418
- // Si no es JSON válido, intentar parsear como key=value
419
- action.queryParams = this.parseQueryString(data['query_params']);
420
- }
421
- }
422
- // Datos adicionales con prefijo action_
423
- const actionData = {};
424
- for (const [key, value] of Object.entries(data)) {
425
- if (key.startsWith('action_') && key !== 'action_type') {
426
- const cleanKey = key.replace('action_', '');
427
- // Intentar parsear JSON si es posible
428
- try {
429
- actionData[cleanKey] = JSON.parse(value);
430
- }
431
- catch {
432
- actionData[cleanKey] = value;
433
- }
434
- }
435
- }
436
- if (Object.keys(actionData).length > 0) {
437
- action.actionData = actionData;
438
- }
439
- return action;
440
- }
441
- /**
442
- * Emite manualmente un evento de click en notificación.
443
- *
444
- * Útil para manejar clicks en notificaciones foreground donde
445
- * la app decide mostrar un banner custom.
446
- *
447
- * @param notification - Payload de la notificación
448
- *
449
- * @example
450
- * ```typescript
451
- * messaging.onMessage().subscribe(notification => {
452
- * // Mostrar banner custom
453
- * this.showBanner(notification, () => {
454
- * // Usuario hizo click en el banner
455
- * messaging.handleNotificationClick(notification);
456
- * });
457
- * });
458
- * ```
459
- */
460
- handleNotificationClick(notification) {
461
- const action = this.extractActionFromData(notification.data);
462
- this.notificationClickSubject.next({
463
- notification,
464
- action,
465
- timestamp: new Date(),
466
- });
467
- }
468
- /**
469
- * Verifica si una notificación tiene acción de navegación.
470
- *
471
- * @param data - Datos del payload
472
- * @returns true si tiene route o url
473
- */
474
- hasNavigationAction(data) {
475
- if (!data)
476
- return false;
477
- return !!(data['route'] || data['url']);
478
- }
479
- /**
480
- * Parsea un query string en un objeto.
481
- */
482
- parseQueryString(queryString) {
483
- const params = {};
484
- if (!queryString)
485
- return params;
486
- // Remover ? inicial si existe
487
- const cleanQuery = queryString.startsWith('?') ? queryString.slice(1) : queryString;
488
- for (const pair of cleanQuery.split('&')) {
489
- const [key, value] = pair.split('=');
490
- if (key) {
491
- params[decodeURIComponent(key)] = decodeURIComponent(value || '');
492
- }
493
- }
494
- return params;
495
- }
496
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessagingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
497
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessagingService, providedIn: 'root' }); }
498
- }
499
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessagingService, decorators: [{
500
- type: Injectable,
501
- args: [{ providedIn: 'root' }]
502
- }], ctorParameters: () => [] });
503
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVzc2FnaW5nLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL3NlcnZpY2VzL2ZpcmViYXNlL21lc3NhZ2luZy5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDeEUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDcEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3RGLE9BQU8sRUFBYyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRTVELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLFVBQVUsQ0FBQzs7QUFpQm5EOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTZCRztBQUVILE1BQU0sT0FBTyxnQkFBZ0I7SUFnQjNCO1FBZlEsY0FBUyxHQUFHLE1BQU0sQ0FBQyxTQUFTLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNsRCxXQUFNLEdBQUcsTUFBTSxDQUFDLHVCQUF1QixDQUFDLENBQUM7UUFDekMsZUFBVSxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNqQyxXQUFNLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRXhCLG1CQUFjLEdBQUcsSUFBSSxPQUFPLEVBQXVCLENBQUM7UUFDcEQsNkJBQXdCLEdBQUcsSUFBSSxPQUFPLEVBQTBCLENBQUM7UUFDakUsaUJBQVksR0FBRyxJQUFJLGVBQWUsQ0FBaUI7WUFDekQsS0FBSyxFQUFFLElBQUk7WUFDWCxVQUFVLEVBQUUsU0FBUztZQUNyQixXQUFXLEVBQUUsS0FBSztTQUNu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LENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxrQkFBa0IsQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDLENBQUM7WUFDcEUsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDOytHQS9mVSxnQkFBZ0I7bUhBQWhCLGdCQUFnQixjQURILE1BQU07OzRGQUNuQixnQkFBZ0I7a0JBRDVCLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNZXNzYWdpbmcgU2VydmljZSAoRkNNKVxuICpcbiAqIFNlcnZpY2lvIHBhcmEgRmlyZWJhc2UgQ2xvdWQgTWVzc2FnaW5nIChQdXNoIE5vdGlmaWNhdGlvbnMpLlxuICogUGVybWl0ZSBzb2xpY2l0YXIgcGVybWlzb3MsIG9idGVuZXIgdG9rZW5zLCBlc2N1Y2hhciBtZW5zYWplcyB5IG1hbmVqYXJcbiAqIG5hdmVnYWNpw7NuIChkZWVwIGxpbmtpbmcpIGN1YW5kbyBlbCB1c3VhcmlvIHRvY2EgdW5hIG5vdGlmaWNhY2nDs24uXG4gKi9cblxuaW1wb3J0IHsgaW5qZWN0LCBJbmplY3RhYmxlLCBOZ1pvbmUsIFBMQVRGT1JNX0lEIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBpc1BsYXRmb3JtQnJvd3NlciB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBNZXNzYWdpbmcsIGdldFRva2VuLCBkZWxldGVUb2tlbiwgb25NZXNzYWdlIH0gZnJvbSAnQGFuZ3VsYXIvZmlyZS9tZXNzYWdpbmcnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgU3ViamVjdCwgQmVoYXZpb3JTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5cbmltcG9ydCB7IFZBTFRFQ0hfRklSRUJBU0VfQ09ORklHIH0gZnJvbSAnLi9jb25maWcnO1xuaW1wb3J0IHtcbiAgTm90aWZpY2F0aW9uQWN0aW9uLFxuICBOb3RpZmljYXRpb25DbGlja0V2ZW50LFxuICBOb3RpZmljYXRpb25QYXlsb2FkLFxuICBOb3RpZmljYXRpb25QZXJtaXNzaW9uLFxufSBmcm9tICcuL3R5cGVzJztcblxuLyoqXG4gKiBFc3RhZG8gaW50ZXJubyBkZWwgc2VydmljaW8gZGUgbWVzc2FnaW5nXG4gKi9cbmludGVyZmFjZSBNZXNzYWdpbmdTdGF0ZSB7XG4gIHRva2VuOiBzdHJpbmcgfCBudWxsO1xuICBwZXJtaXNzaW9uOiBOb3RpZmljYXRpb25QZXJtaXNzaW9uO1xuICBpc1N1cHBvcnRlZDogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBTZXJ2aWNpbyBwYXJhIEZpcmViYXNlIENsb3VkIE1lc3NhZ2luZyAoRkNNKS5cbiAqXG4gKiBQZXJtaXRlIHJlY2liaXIgbm90aWZpY2FjaW9uZXMgcHVzaCBlbiBsYSBhcGxpY2FjacOzbiB3ZWIuXG4gKiBSZXF1aWVyZSBWQVBJRCBrZXkgY29uZmlndXJhZGEgZW4gVmFsdGVjaEZpcmViYXNlQ29uZmlnLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBAQ29tcG9uZW50KHsuLi59KVxuICogZXhwb3J0IGNsYXNzIE5vdGlmaWNhdGlvbkNvbXBvbmVudCB7XG4gKiAgIHByaXZhdGUgbWVzc2FnaW5nID0gaW5qZWN0KE1lc3NhZ2luZ1NlcnZpY2UpO1xuICpcbiAqICAgdG9rZW4gPSBzaWduYWw8c3RyaW5nIHwgbnVsbD4obnVsbCk7XG4gKlxuICogICBhc3luYyBlbmFibGVOb3RpZmljYXRpb25zKCkge1xuICogICAgIC8vIFNvbGljaXRhciBwZXJtaXNvIHkgb2J0ZW5lciB0b2tlblxuICogICAgIGNvbnN0IHRva2VuID0gYXdhaXQgdGhpcy5tZXNzYWdpbmcucmVxdWVzdFBlcm1pc3Npb24oKTtcbiAqXG4gKiAgICAgaWYgKHRva2VuKSB7XG4gKiAgICAgICB0aGlzLnRva2VuLnNldCh0b2tlbik7XG4gKiAgICAgICAvLyBFbnZpYXIgdG9rZW4gYSB0dSBiYWNrZW5kIHBhcmEgYWxtYWNlbmFybG9cbiAqICAgICAgIGF3YWl0IHRoaXMuYmFja2VuZC5yZWdpc3RlckRldmljZVRva2VuKHRva2VuKTtcbiAqICAgICB9XG4gKiAgIH1cbiAqXG4gKiAgIC8vIEVzY3VjaGFyIG1lbnNhamVzIGVuIGZvcmVncm91bmRcbiAqICAgbWVzc2FnZXMkID0gdGhpcy5tZXNzYWdpbmcub25NZXNzYWdlKCk7XG4gKiB9XG4gKiBgYGBcbiAqL1xuQEluamVjdGFibGUoeyBwcm92aWRlZEluOiAncm9vdCcgfSlcbmV4cG9ydCBjbGFzcyBNZXNzYWdpbmdTZXJ2aWNlIHtcbiAgcHJpdmF0ZSBtZXNzYWdpbmcgPSBpbmplY3QoTWVzc2FnaW5nLCB7IG9wdGlvbmFsOiB0cnVlIH0pO1xuICBwcml2YXRlIGNvbmZpZyA9IGluamVjdChWQUxURUNIX0ZJUkVCQVNFX0NPTkZJRyk7XG4gIHByaXZhdGUgcGxhdGZvcm1JZCA9IGluamVjdChQTEFURk9STV9JRCk7XG4gIHByaXZhdGUgbmdab25lID0gaW5qZWN0KE5nWm9uZSk7XG5cbiAgcHJpdmF0ZSBtZXNzYWdlU3ViamVjdCA9IG5ldyBTdWJqZWN0PE5vdGlmaWNhdGlvblBheWxvYWQ+KCk7XG4gIHByaXZhdGUgbm90aWZpY2F0aW9uQ2xpY2tTdWJqZWN0ID0gbmV3IFN1YmplY3Q8Tm90aWZpY2F0aW9uQ2xpY2tFdmVudD4oKTtcbiAgcHJpdmF0ZSBzdGF0ZVN1YmplY3QgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PE1lc3NhZ2luZ1N0YXRlPih7XG4gICAgdG9rZW46IG51bGwsXG4gICAgcGVybWlzc2lvbjogJ2RlZmF1bHQnLFxuICAgIGlzU3VwcG9ydGVkOiBmYWxzZSxcbiAgfSk7XG5cbiAgcHJpdmF0ZSB1bnN1YnNjcmliZU9uTWVzc2FnZT86ICgpID0+IHZvaWQ7XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy5pbml0aWFsaXplTWVzc2FnaW5nKCk7XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gSU5JQ0lBTElaQUNJw5NOXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBJbmljaWFsaXphIGVsIHNlcnZpY2lvIGRlIG1lc3NhZ2luZ1xuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBpbml0aWFsaXplTWVzc2FnaW5nKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghaXNQbGF0Zm9ybUJyb3dzZXIodGhpcy5wbGF0Zm9ybUlkKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHN1cHBvcnRlZCA9IGF3YWl0IHRoaXMuY2hlY2tTdXBwb3J0KCk7XG4gICAgY29uc3QgcGVybWlzc2lvbiA9IHRoaXMuZ2V0UGVybWlzc2lvblN0YXRlKCk7XG5cbiAgICB0aGlzLnN0YXRlU3ViamVjdC5uZXh0KHtcbiAgICAgIC4uLnRoaXMuc3RhdGVTdWJqZWN0LnZhbHVlLFxuICAgICAgaXNTdXBwb3J0ZWQ6IHN1cHBvcnRlZCxcbiAgICAgIHBlcm1pc3Npb24sXG4gICAgfSk7XG5cbiAgICAvLyBTaSB5YSB0aWVuZSBwZXJtaXNvLCBjb25maWd1cmFyIGxpc3RlbmVyc1xuICAgIGlmIChzdXBwb3J0ZWQgJiYgcGVybWlzc2lvbiA9PT0gJ2dyYW50ZWQnKSB7XG4gICAgICB0aGlzLnNldHVwTWVzc2FnZUxpc3RlbmVyKCk7XG4gICAgfVxuXG4gICAgLy8gRXNjdWNoYXIgbWVuc2FqZXMgZGVsIFNlcnZpY2UgV29ya2VyIChjbGlja3MgZW4gbm90aWZpY2FjaW9uZXMgYmFja2dyb3VuZClcbiAgICB0aGlzLnNldHVwU2VydmljZVdvcmtlckxpc3RlbmVyKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29uZmlndXJhIGxpc3RlbmVyIHBhcmEgbWVuc2FqZXMgZGVsIFNlcnZpY2UgV29ya2VyLlxuICAgKiBSZWNpYmUgZXZlbnRvcyBjdWFuZG8gZWwgdXN1YXJpbyBoYWNlIGNsaWNrIGVuIHVuYSBub3RpZmljYWNpw7NuIGJhY2tncm91bmQuXG4gICAqL1xuICBwcml2YXRlIHNldHVwU2VydmljZVdvcmtlckxpc3RlbmVyKCk6IHZvaWQge1xuICAgIGlmICghaXNQbGF0Zm9ybUJyb3dzZXIodGhpcy5wbGF0Zm9ybUlkKSB8fCAhKCdzZXJ2aWNlV29ya2VyJyBpbiBuYXZpZ2F0b3IpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbmF2aWdhdG9yLnNlcnZpY2VXb3JrZXIuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIChldmVudCkgPT4ge1xuICAgICAgLy8gVmVyaWZpY2FyIHF1ZSBlcyB1biBtZW5zYWplIGRlIG5vdGlmaWNhY2nDs24gY2xpY2tcbiAgICAgIGlmIChldmVudC5kYXRhPy50eXBlID09PSAnTk9USUZJQ0FUSU9OX0NMSUNLJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICAgIGNvbnN0IG5vdGlmaWNhdGlvbiA9IGV2ZW50LmRhdGEubm90aWZpY2F0aW9uIGFzIE5vdGlmaWNhdGlvblBheWxvYWQ7XG4gICAgICAgICAgY29uc3QgYWN0aW9uID0gdGhpcy5leHRyYWN0QWN0aW9uRnJvbURhdGEobm90aWZpY2F0aW9uLmRhdGEpO1xuXG4gICAgICAgICAgdGhpcy5ub3RpZmljYXRpb25DbGlja1N1YmplY3QubmV4dCh7XG4gICAgICAgICAgICBub3RpZmljYXRpb24sXG4gICAgICAgICAgICBhY3Rpb24sXG4gICAgICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCksXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmaWNhIHNpIEZDTSBlc3TDoSBzb3BvcnRhZG8gZW4gZWwgbmF2ZWdhZG9yIGFjdHVhbFxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBjaGVja1N1cHBvcnQoKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgaWYgKCFpc1BsYXRmb3JtQnJvd3Nlcih0aGlzLnBsYXRmb3JtSWQpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gVmVyaWZpY2FyIEFQSXMgbmVjZXNhcmlhc1xuICAgIGlmICghKCdOb3RpZmljYXRpb24nIGluIHdpbmRvdykpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBpZiAoISgnc2VydmljZVdvcmtlcicgaW4gbmF2aWdhdG9yKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIFZlcmlmaWNhciBxdWUgbWVzc2FnaW5nIGVzdMOpIGRpc3BvbmlibGVcbiAgICBpZiAoIXRoaXMubWVzc2FnaW5nKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gUEVSTUlTT1MgWSBUT0tFTlxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAvKipcbiAgICogU29saWNpdGEgcGVybWlzbyBkZSBub3RpZmljYWNpb25lcyB5IG9idGllbmUgZWwgdG9rZW4gRkNNLlxuICAgKlxuICAgKiBAcmV0dXJucyBUb2tlbiBGQ00gc2kgc2Ugb3RvcmfDsyBwZXJtaXNvLCBudWxsIHNpIHNlIGRlbmVnw7NcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCB0b2tlbiA9IGF3YWl0IG1lc3NhZ2luZy5yZXF1ZXN0UGVybWlzc2lvbigpO1xuICAgKiBpZiAodG9rZW4pIHtcbiAgICogICBjb25zb2xlLmxvZygnVG9rZW4gRkNNOicsIHRva2VuKTtcbiAgICogICAvLyBFbnZpYXIgYSBiYWNrZW5kXG4gICAqIH0gZWxzZSB7XG4gICAqICAgY29uc29sZS5sb2coJ1Blcm1pc28gZGVuZWdhZG8gbyBubyBzb3BvcnRhZG8nKTtcbiAgICogfVxuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIHJlcXVlc3RQZXJtaXNzaW9uKCk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICAgIGlmICghYXdhaXQgdGhpcy5pc1N1cHBvcnRlZCgpKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ0ZDTSBubyBlc3TDoSBzb3BvcnRhZG8gZW4gZXN0ZSBuYXZlZ2Fkb3InKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICAvLyBTb2xpY2l0YXIgcGVybWlzbyBkZSBub3RpZmljYWNpb25lc1xuICAgICAgY29uc3QgcGVybWlzc2lvbiA9IGF3YWl0IE5vdGlmaWNhdGlvbi5yZXF1ZXN0UGVybWlzc2lvbigpO1xuXG4gICAgICB0aGlzLnN0YXRlU3ViamVjdC5uZXh0KHtcbiAgICAgICAgLi4udGhpcy5zdGF0ZVN1YmplY3QudmFsdWUsXG4gICAgICAgIHBlcm1pc3Npb246IHBlcm1pc3Npb24gYXMgTm90aWZpY2F0aW9uUGVybWlzc2lvbixcbiAgICAgIH0pO1xuXG4gICAgICBpZiAocGVybWlzc2lvbiAhPT0gJ2dyYW50ZWQnKSB7XG4gICAgICAgIGNvbnNvbGUud2FybignUGVybWlzbyBkZSBub3RpZmljYWNpb25lcyBkZW5lZ2FkbycpO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cblxuICAgICAgLy8gT2J0ZW5lciB0b2tlbiBGQ01cbiAgICAgIGNvbnN0IHRva2VuID0gYXdhaXQgdGhpcy5nZXRUb2tlbigpO1xuXG4gICAgICBpZiAodG9rZW4pIHtcbiAgICAgICAgLy8gQ29uZmlndXJhciBsaXN0ZW5lciBkZSBtZW5zYWplc1xuICAgICAgICB0aGlzLnNldHVwTWVzc2FnZUxpc3RlbmVyKCk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0b2tlbjtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcignRXJyb3Igc29saWNpdGFuZG8gcGVybWlzbyBkZSBub3RpZmljYWNpb25lczonLCBlcnJvcik7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogT2J0aWVuZSBlbCB0b2tlbiBGQ00gYWN0dWFsIChzaW4gc29saWNpdGFyIHBlcm1pc28pLlxuICAgKlxuICAgKiBAcmV0dXJucyBUb2tlbiBGQ00gc2kgZXN0w6EgZGlzcG9uaWJsZSwgbnVsbCBzaSBub1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHRva2VuID0gYXdhaXQgbWVzc2FnaW5nLmdldFRva2VuKCk7XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgZ2V0VG9rZW4oKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gICAgaWYgKCF0aGlzLm1lc3NhZ2luZykge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgdmFwaWRLZXkgPSB0aGlzLmNvbmZpZy5tZXNzYWdpbmdWYXBpZEtleTtcbiAgICBpZiAoIXZhcGlkS2V5KSB7XG4gICAgICBjb25zb2xlLndhcm4oJ1ZBUElEIGtleSBubyBjb25maWd1cmFkYS4gRkNNIG5vIGZ1bmNpb25hcsOhLicpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHRva2VuID0gYXdhaXQgZ2V0VG9rZW4odGhpcy5tZXNzYWdpbmcsIHsgdmFwaWRLZXkgfSk7XG5cbiAgICAgIHRoaXMuc3RhdGVTdWJqZWN0Lm5leHQoe1xuICAgICAgICAuLi50aGlzLnN0YXRlU3ViamVjdC52YWx1ZSxcbiAgICAgICAgdG9rZW4sXG4gICAgICB9KTtcblxuICAgICAgcmV0dXJuIHRva2VuO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBvYnRlbmllbmRvIHRva2VuIEZDTTonLCBlcnJvcik7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRWxpbWluYSBlbCB0b2tlbiBGQ00gYWN0dWFsICh1bnN1YnNjcmliZSBkZSBub3RpZmljYWNpb25lcykuXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogYXdhaXQgbWVzc2FnaW5nLmRlbGV0ZVRva2VuKCk7XG4gICAqIGNvbnNvbGUubG9nKCdUb2tlbiBlbGltaW5hZG8sIG5vIHJlY2liaXLDoSBtw6FzIG5vdGlmaWNhY2lvbmVzJyk7XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgZGVsZXRlVG9rZW4oKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0aGlzLm1lc3NhZ2luZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBkZWxldGVUb2tlbih0aGlzLm1lc3NhZ2luZyk7XG5cbiAgICAgIHRoaXMuc3RhdGVTdWJqZWN0Lm5leHQoe1xuICAgICAgICAuLi50aGlzLnN0YXRlU3ViamVjdC52YWx1ZSxcbiAgICAgICAgdG9rZW46IG51bGwsXG4gICAgICB9KTtcblxuICAgICAgLy8gTGltcGlhciBsaXN0ZW5lciBkZSBtZW5zYWplc1xuICAgICAgaWYgKHRoaXMudW5zdWJzY3JpYmVPbk1lc3NhZ2UpIHtcbiAgICAgICAgdGhpcy51bnN1YnNjcmliZU9uTWVzc2FnZSgpO1xuICAgICAgICB0aGlzLnVuc3Vic2NyaWJlT25NZXNzYWdlID0gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBlbGltaW5hbmRvIHRva2VuIEZDTTonLCBlcnJvcik7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIHNlIHB1ZG8gZWxpbWluYXIgZWwgdG9rZW4gZGUgbm90aWZpY2FjaW9uZXMnKTtcbiAgICB9XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gTUVOU0FKRVNcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgLyoqXG4gICAqIE9ic2VydmFibGUgZGUgbWVuc2FqZXMgcmVjaWJpZG9zIGVuIGZvcmVncm91bmQuXG4gICAqXG4gICAqIElNUE9SVEFOVEU6IExvcyBtZW5zYWplcyBlbiBiYWNrZ3JvdW5kIHNvbiBtYW5lamFkb3MgcG9yIGVsIFNlcnZpY2UgV29ya2VyLlxuICAgKlxuICAgKiBAcmV0dXJucyBPYnNlcnZhYmxlIHF1ZSBlbWl0ZSBjdWFuZG8gbGxlZ2EgdW4gbWVuc2FqZSBlbiBmb3JlZ3JvdW5kXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogbWVzc2FnaW5nLm9uTWVzc2FnZSgpLnN1YnNjcmliZShwYXlsb2FkID0+IHtcbiAgICogICBjb25zb2xlLmxvZygnTWVuc2FqZSByZWNpYmlkbzonLCBwYXlsb2FkKTtcbiAgICogICAvLyBNb3N0cmFyIG5vdGlmaWNhY2nDs24gY3VzdG9tIG8gYWN0dWFsaXphciBVSVxuICAgKiB9KTtcbiAgICogYGBgXG4gICAqL1xuICBvbk1lc3NhZ2UoKTogT2JzZXJ2YWJsZTxOb3RpZmljYXRpb25QYXlsb2FkPiB7XG4gICAgcmV0dXJuIHRoaXMubWVzc2FnZVN1YmplY3QuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29uZmlndXJhIGVsIGxpc3RlbmVyIGRlIG1lbnNhamVzIGVuIGZvcmVncm91bmRcbiAgICovXG4gIHByaXZhdGUgc2V0dXBNZXNzYWdlTGlzdGVuZXIoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLm1lc3NhZ2luZyB8fCB0aGlzLnVuc3Vic2NyaWJlT25NZXNzYWdlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy51bnN1YnNjcmliZU9uTWVzc2FnZSA9IG9uTWVzc2FnZSh0aGlzLm1lc3NhZ2luZywgKHBheWxvYWQpID0+IHtcbiAgICAgIGNvbnN0IG5vdGlmaWNhdGlvbjogTm90aWZpY2F0aW9uUGF5bG9hZCA9IHtcbiAgICAgICAgdGl0bGU6IHBheWxvYWQubm90aWZpY2F0aW9uPy50aXRsZSxcbiAgICAgICAgYm9keTogcGF5bG9hZC5ub3RpZmljYXRpb24/LmJvZHksXG4gICAgICAgIGltYWdlOiBwYXlsb2FkLm5vdGlmaWNhdGlvbj8uaW1hZ2UsXG4gICAgICAgIGRhdGE6IHBheWxvYWQuZGF0YSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LFxuICAgICAgICBtZXNzYWdlSWQ6IHBheWxvYWQubWVzc2FnZUlkLFxuICAgICAgfTtcblxuICAgICAgdGhpcy5tZXNzYWdlU3ViamVjdC5uZXh0KG5vdGlmaWNhdGlvbik7XG4gICAgfSk7XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gRVNUQURPIFkgVVRJTElEQURFU1xuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAvKipcbiAgICogT2J0aWVuZSBlbCBlc3RhZG8gYWN0dWFsIGRlbCBwZXJtaXNvIGRlIG5vdGlmaWNhY2lvbmVzLlxuICAgKlxuICAgKiBAcmV0dXJucyAnZ3JhbnRlZCcgfCAnZGVuaWVkJyB8ICdkZWZhdWx0J1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHBlcm1pc3Npb24gPSBtZXNzYWdpbmcuZ2V0UGVybWlzc2lvblN0YXRlKCk7XG4gICAqIGlmIChwZXJtaXNzaW9uID09PSAnZ3JhbnRlZCcpIHtcbiAgICogICAvLyBZYSB0aWVuZSBwZXJtaXNvXG4gICAqIH0gZWxzZSBpZiAocGVybWlzc2lvbiA9PT0gJ2RlZmF1bHQnKSB7XG4gICAqICAgLy8gUHVlZGUgc29saWNpdGFyIHBlcm1pc29cbiAgICogfSBlbHNlIHtcbiAgICogICAvLyBEZW5lZ2FkbywgZGViZSBoYWJpbGl0YXIgbWFudWFsbWVudGVcbiAgICogfVxuICAgKiBgYGBcbiAgICovXG4gIGdldFBlcm1pc3Npb25TdGF0ZSgpOiBOb3RpZmljYXRpb25QZXJtaXNzaW9uIHtcbiAgICBpZiAoIWlzUGxhdGZvcm1Ccm93c2VyKHRoaXMucGxhdGZvcm1JZCkpIHtcbiAgICAgIHJldHVybiAnZGVmYXVsdCc7XG4gICAgfVxuXG4gICAgaWYgKCEoJ05vdGlmaWNhdGlvbicgaW4gd2luZG93KSkge1xuICAgICAgcmV0dXJuICdkZW5pZWQnO1xuICAgIH1cblxuICAgIHJldHVybiBOb3RpZmljYXRpb24ucGVybWlzc2lvbiBhcyBOb3RpZmljYXRpb25QZXJtaXNzaW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmaWNhIHNpIEZDTSBlc3TDoSBzb3BvcnRhZG8gZW4gZWwgbmF2ZWdhZG9yIGFjdHVhbC5cbiAgICpcbiAgICogQHJldHVybnMgdHJ1ZSBzaSBGQ00gZXN0w6Egc29wb3J0YWRvXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogaWYgKGF3YWl0IG1lc3NhZ2luZy5pc1N1cHBvcnRlZCgpKSB7XG4gICAqICAgLy8gUHVlZGUgdXNhciBub3RpZmljYWNpb25lcyBwdXNoXG4gICAqIH0gZWxzZSB7XG4gICAqICAgLy8gTmF2ZWdhZG9yIG5vIHNvcG9ydGEgbyBubyB0aWVuZSBTZXJ2aWNlIFdvcmtlclxuICAgKiB9XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgaXNTdXBwb3J0ZWQoKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgcmV0dXJuIHRoaXMuY2hlY2tTdXBwb3J0KCk7XG4gIH1cblxuICAvKipcbiAgICogT2J0aWVuZSBlbCB0b2tlbiBhY3R1YWwgc2luIGhhY2VyIHJlcXVlc3QuXG4gICAqXG4gICAqIEByZXR1cm5zIFRva2VuIGFsbWFjZW5hZG8gbyBudWxsXG4gICAqL1xuICBnZXQgY3VycmVudFRva2VuKCk6IHN0cmluZyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLnN0YXRlU3ViamVjdC52YWx1ZS50b2tlbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBPYnNlcnZhYmxlIGRlbCBlc3RhZG8gY29tcGxldG8gZGVsIHNlcnZpY2lvIGRlIG1lc3NhZ2luZy5cbiAgICovXG4gIGdldCBzdGF0ZSQoKTogT2JzZXJ2YWJsZTxNZXNzYWdpbmdTdGF0ZT4ge1xuICAgIHJldHVybiB0aGlzLnN0YXRlU3ViamVjdC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZmljYSBzaSBlbCB1c3VhcmlvIHlhIG90b3Jnw7MgcGVybWlzbyBkZSBub3RpZmljYWNpb25lcy5cbiAgICovXG4gIGdldCBoYXNQZXJtaXNzaW9uKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnN0YXRlU3ViamVjdC52YWx1ZS5wZXJtaXNzaW9uID09PSAnZ3JhbnRlZCc7XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gREVFUCBMSU5LSU5HIC8gTkFWRUdBQ0nDk05cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgLyoqXG4gICAqIE9ic2VydmFibGUgZGUgY2xpY2tzIGVuIG5vdGlmaWNhY2lvbmVzLlxuICAgKlxuICAgKiBFbWl0ZSBjdWFuZG8gZWwgdXN1YXJpbyBoYWNlIGNsaWNrIGVuIHVuYSBub3RpZmljYWNpw7NuIChmb3JlZ3JvdW5kIG8gYmFja2dyb3VuZCkuXG4gICAqIFVzYSBlc3RlIG9ic2VydmFibGUgcGFyYSBuYXZlZ2FyIGEgbGEgcMOhZ2luYSBjb3JyZXNwb25kaWVudGUuXG4gICAqXG4gICAqIEByZXR1cm5zIE9ic2VydmFibGUgcXVlIGVtaXRlIE5vdGlmaWNhdGlvbkNsaWNrRXZlbnRcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBAQ29tcG9uZW50KHsuLi59KVxuICAgKiBleHBvcnQgY2xhc3MgQXBwQ29tcG9uZW50IHtcbiAgICogICBwcml2YXRlIG1lc3NhZ2luZyA9IGluamVjdChNZXNzYWdpbmdTZXJ2aWNlKTtcbiAgICogICBwcml2YXRlIHJvdXRlciA9IGluamVjdChSb3V0ZXIpO1xuICAgKlxuICAgKiAgIGNvbnN0cnVjdG9yKCkge1xuICAgKiAgICAgdGhpcy5tZXNzYWdpbmcub25Ob3RpZmljYXRpb25DbGljaygpLnN1YnNjcmliZShldmVudCA9PiB7XG4gICAqICAgICAgIGlmIChldmVudC5hY3Rpb24ucm91dGUpIHtcbiAgICogICAgICAgICB0aGlzLnJvdXRlci5uYXZpZ2F0ZShbZXZlbnQuYWN0aW9uLnJvdXRlXSwge1xuICAgKiAgICAgICAgICAgcXVlcnlQYXJhbXM6IGV2ZW50LmFjdGlvbi5xdWVyeVBhcmFtc1xuICAgKiAgICAgICAgIH0pO1xuICAgKiAgICAgICB9XG4gICAqICAgICB9KTtcbiAgICogICB9XG4gICAqIH1cbiAgICogYGBgXG4gICAqL1xuICBvbk5vdGlmaWNhdGlvbkNsaWNrKCk6IE9ic2VydmFibGU8Tm90aWZpY2F0aW9uQ2xpY2tFdmVudD4ge1xuICAgIHJldHVybiB0aGlzLm5vdGlmaWNhdGlvbkNsaWNrU3ViamVjdC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFeHRyYWUgbGEgYWNjacOzbiBkZSBuYXZlZ2FjacOzbiBkZSBsb3MgZGF0b3MgZGUgdW5hIG5vdGlmaWNhY2nDs24uXG4gICAqXG4gICAqIEJ1c2NhIGNhbXBvcyBlc3BlY8OtZmljb3MgZW4gZWwgcGF5bG9hZCBkZSBkYXRvczpcbiAgICogLSBgcm91dGVgOiBSdXRhIGludGVybmEgZGUgbGEgYXBwIChlajogJy9vcmRlcnMvMTIzJylcbiAgICogLSBgdXJsYDogVVJMIGV4dGVybmEgKGVqOiAnaHR0cHM6Ly9leGFtcGxlLmNvbScpXG4gICAqIC0gYGFjdGlvbl90eXBlYDogVGlwbyBkZSBhY2Npw7NuIHBlcnNvbmFsaXphZGFcbiAgICogLSBDYW1wb3MgY29uIHByZWZpam8gYGFjdGlvbl9gOiBEYXRvcyBhZGljaW9uYWxlc1xuICAgKlxuICAgKiBAcGFyYW0gZGF0YSAtIERhdG9zIGRlbCBwYXlsb2FkIGRlIGxhIG5vdGlmaWNhY2nDs25cbiAgICogQHJldHVybnMgQWNjacOzbiBkZSBuYXZlZ2FjacOzbiBleHRyYcOtZGFcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiAvLyBQYXlsb2FkIGRlc2RlIGVsIGJhY2tlbmQ6XG4gICAqIC8vIHsgcm91dGU6ICcvb3JkZXJzLzEyMycsIGFjdGlvbl90eXBlOiAndmlld19vcmRlcicsIGFjdGlvbl9vcmRlcklkOiAnMTIzJyB9XG4gICAqXG4gICAqIGNvbnN0IGFjdGlvbiA9IG1lc3NhZ2luZy5leHRyYWN0QWN0aW9uRnJvbURhdGEobm90aWZpY2F0aW9uLmRhdGEpO1xuICAgKiAvLyB7IHJvdXRlOiAnL29yZGVycy8xMjMnLCBhY3Rpb25UeXBlOiAndmlld19vcmRlcicsIGFjdGlvbkRhdGE6IHsgb3JkZXJJZDogJzEyMycgfSB9XG4gICAqIGBgYFxuICAgKi9cbiAgZXh0cmFjdEFjdGlvbkZyb21EYXRhKGRhdGE/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTogTm90aWZpY2F0aW9uQWN0aW9uIHtcbiAgICBpZiAoIWRhdGEpIHtcbiAgICAgIHJldHVybiB7fTtcbiAgICB9XG5cbiAgICBjb25zdCBhY3Rpb246IE5vdGlmaWNhdGlvbkFjdGlvbiA9IHt9O1xuXG4gICAgLy8gUnV0YSBpbnRlcm5hXG4gICAgaWYgKGRhdGFbJ3JvdXRlJ10pIHtcbiAgICAgIGFjdGlvbi5yb3V0ZSA9IGRhdGFbJ3JvdXRlJ107XG4gICAgfVxuXG4gICAgLy8gVVJMIGV4dGVybmFcbiAgICBpZiAoZGF0YVsndXJsJ10pIHtcbiAgICAgIGFjdGlvbi51cmwgPSBkYXRhWyd1cmwnXTtcbiAgICB9XG5cbiAgICAvLyBUaXBvIGRlIGFjY2nDs25cbiAgICBpZiAoZGF0YVsnYWN0aW9uX3R5cGUnXSkge1xuICAgICAgYWN0aW9uLmFjdGlvblR5cGUgPSBkYXRhWydhY3Rpb25fdHlwZSddO1xuICAgIH1cblxuICAgIC8vIFF1ZXJ5IHBhcmFtcyAocHVlZGUgdmVuaXIgY29tbyBKU09OIHN0cmluZylcbiAgICBpZiAoZGF0YVsncXVlcnlfcGFyYW1zJ10pIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGFjdGlvbi5xdWVyeVBhcmFtcyA9IEpTT04ucGFyc2UoZGF0YVsncXVlcnlfcGFyYW1zJ10pO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIFNpIG5vIGVzIEpTT04gdsOhbGlkbywgaW50ZW50YXIgcGFyc2VhciBjb21vIGtleT12YWx1ZVxuICAgICAgICBhY3Rpb24ucXVlcnlQYXJhbXMgPSB0aGlzLnBhcnNlUXVlcnlTdHJpbmcoZGF0YVsncXVlcnlfcGFyYW1zJ10pO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIERhdG9zIGFkaWNpb25hbGVzIGNvbiBwcmVmaWpvIGFjdGlvbl9cbiAgICBjb25zdCBhY3Rpb25EYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGRhdGEpKSB7XG4gICAgICBpZiAoa2V5LnN0YXJ0c1dpdGgoJ2FjdGlvbl8nKSAmJiBrZXkgIT09ICdhY3Rpb25fdHlwZScpIHtcbiAgICAgICAgY29uc3QgY2xlYW5LZXkgPSBrZXkucmVwbGFjZSgnYWN0aW9uXycsICcnKTtcbiAgICAgICAgLy8gSW50ZW50YXIgcGFyc2VhciBKU09OIHNpIGVzIHBvc2libGVcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhY3Rpb25EYXRhW2NsZWFuS2V5XSA9IEpTT04ucGFyc2UodmFsdWUpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICBhY3Rpb25EYXRhW2NsZWFuS2V5XSA9IHZhbHVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKE9iamVjdC5rZXlzKGFjdGlvbkRhdGEpLmxlbmd0aCA+IDApIHtcbiAgICAgIGFjdGlvbi5hY3Rpb25EYXRhID0gYWN0aW9uRGF0YTtcbiAgICB9XG5cbiAgICByZXR1cm4gYWN0aW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIEVtaXRlIG1hbnVhbG1lbnRlIHVuIGV2ZW50byBkZSBjbGljayBlbiBub3RpZmljYWNpw7NuLlxuICAgKlxuICAgKiDDmnRpbCBwYXJhIG1hbmVqYXIgY2xpY2tzIGVuIG5vdGlmaWNhY2lvbmVzIGZvcmVncm91bmQgZG9uZGVcbiAgICogbGEgYXBwIGRlY2lkZSBtb3N0cmFyIHVuIGJhbm5lciBjdXN0b20uXG4gICAqXG4gICAqIEBwYXJhbSBub3RpZmljYXRpb24gLSBQYXlsb2FkIGRlIGxhIG5vdGlmaWNhY2nDs25cbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBtZXNzYWdpbmcub25NZXNzYWdlKCkuc3Vic2NyaWJlKG5vdGlmaWNhdGlvbiA9PiB7XG4gICAqICAgLy8gTW9zdHJhciBiYW5uZXIgY3VzdG9tXG4gICAqICAgdGhpcy5zaG93QmFubmVyKG5vdGlmaWNhdGlvbiwgKCkgPT4ge1xuICAgKiAgICAgLy8gVXN1YXJpbyBoaXpvIGNsaWNrIGVuIGVsIGJhbm5lclxuICAgKiAgICAgbWVzc2FnaW5nLmhhbmRsZU5vdGlmaWNhdGlvbkNsaWNrKG5vdGlmaWNhdGlvbik7XG4gICAqICAgfSk7XG4gICAqIH0pO1xuICAgKiBgYGBcbiAgICovXG4gIGhhbmRsZU5vdGlmaWNhdGlvbkNsaWNrKG5vdGlmaWNhdGlvbjogTm90aWZpY2F0aW9uUGF5bG9hZCk6IHZvaWQge1xuICAgIGNvbnN0IGFjdGlvbiA9IHRoaXMuZXh0cmFjdEFjdGlvbkZyb21EYXRhKG5vdGlmaWNhdGlvbi5kYXRhKTtcblxuICAgIHRoaXMubm90aWZpY2F0aW9uQ2xpY2tTdWJqZWN0Lm5leHQoe1xuICAgICAgbm90aWZpY2F0aW9uLFxuICAgICAgYWN0aW9uLFxuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmaWNhIHNpIHVuYSBub3RpZmljYWNpw7NuIHRpZW5lIGFjY2nDs24gZGUgbmF2ZWdhY2nDs24uXG4gICAqXG4gICAqIEBwYXJhbSBkYXRhIC0gRGF0b3MgZGVsIHBheWxvYWRcbiAgICogQHJldHVybnMgdHJ1ZSBzaSB0aWVuZSByb3V0ZSBvIHVybFxuICAgKi9cbiAgaGFzTmF2aWdhdGlvbkFjdGlvbihkYXRhPzogUmVjb3JkPHN0cmluZywgc3RyaW5nPik6IGJvb2xlYW4ge1xuICAgIGlmICghZGF0YSkgcmV0dXJuIGZhbHNlO1xuICAgIHJldHVybiAhIShkYXRhWydyb3V0ZSddIHx8IGRhdGFbJ3VybCddKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQYXJzZWEgdW4gcXVlcnkgc3RyaW5nIGVuIHVuIG9iamV0by5cbiAgICovXG4gIHByaXZhdGUgcGFyc2VRdWVyeVN0cmluZyhxdWVyeVN0cmluZzogc3RyaW5nKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gICAgY29uc3QgcGFyYW1zOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgICBpZiAoIXF1ZXJ5U3RyaW5nKSByZXR1cm4gcGFyYW1zO1xuXG4gICAgLy8gUmVtb3ZlciA/IGluaWNpYWwgc2kgZXhpc3RlXG4gICAgY29uc3QgY2xlYW5RdWVyeSA9IHF1ZXJ5U3RyaW5nLnN0YXJ0c1dpdGgoJz8nKSA/IHF1ZXJ5U3RyaW5nLnNsaWNlKDEpIDogcXVlcnlTdHJpbmc7XG5cbiAgICBmb3IgKGNvbnN0IHBhaXIgb2YgY2xlYW5RdWVyeS5zcGxpdCgnJicpKSB7XG4gICAgICBjb25zdCBba2V5LCB2YWx1ZV0gPSBwYWlyLnNwbGl0KCc9Jyk7XG4gICAgICBpZiAoa2V5KSB7XG4gICAgICAgIHBhcmFtc1tkZWNvZGVVUklDb21wb25lbnQoa2V5KV0gPSBkZWNvZGVVUklDb21wb25lbnQodmFsdWUgfHwgJycpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBwYXJhbXM7XG4gIH1cbn1cbiJdfQ==