valtech-components 2.0.834 → 2.0.836

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.
@@ -0,0 +1,465 @@
1
+ import { Component, computed, EventEmitter, inject, Input, Output, signal } from '@angular/core';
2
+ import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
3
+ import { IonButton, IonButtons, IonContent, IonHeader, IonIcon, IonInput, IonItem, IonLabel, IonModal, IonRadio, IonRadioGroup, IonSpinner, IonToolbar, } from '@ionic/angular/standalone';
4
+ import { addIcons } from 'ionicons';
5
+ import { closeOutline, informationCircleOutline } from 'ionicons/icons';
6
+ import { SolidDefaultBlock } from '../../atoms/button/factory';
7
+ import { QrCodeComponent } from '../../atoms/qr-code/qr-code.component';
8
+ import { PinInputComponent } from '../../molecules/pin-input/pin-input.component';
9
+ import { ComponentStates, InputType } from '../../types';
10
+ import { FormComponent } from '../form/form.component';
11
+ import { AuthService } from '../../../services/auth';
12
+ import { I18nService, InputI18nHelper } from '../../../services/i18n';
13
+ import { QrGeneratorService } from '../../../services/qr-generator/qr-generator.service';
14
+ import { ToastService } from '../../../services/toast.service';
15
+ import * as i0 from "@angular/core";
16
+ import * as i1 from "@angular/forms";
17
+ /** Segundos de espera antes de poder reenviar el código EMAIL/SMS. */
18
+ const RESEND_COOLDOWN_SECONDS = 30;
19
+ /**
20
+ * `val-mfa-modal` — modal de gestión de autenticación de dos factores (MFA)
21
+ * para un usuario autenticado. Mismo patrón que `val-change-password-modal`.
22
+ *
23
+ * Flujo (máquina de estados interna):
24
+ * - `loading` → `getProfile()` para conocer el estado MFA.
25
+ * - `status` → muestra MFA habilitado/deshabilitado. Si está habilitado:
26
+ * gestión de backup codes (TOTP) + deshabilitar. Si no: botón habilitar.
27
+ * - `method-select` → elegir TOTP / EMAIL / SMS.
28
+ * - `totp-setup` → QR + secreto manual + backup codes → verificar código.
29
+ * - `code-confirm` → (EMAIL/SMS) ingresar código recibido, con reenvío.
30
+ * - `disable` → contraseña para deshabilitar MFA.
31
+ *
32
+ * El QR se genera **client-side** (`QrGeneratorService`) — el secreto TOTP
33
+ * nunca sale del navegador.
34
+ *
35
+ * Self-contained: inyecta `AuthService` y llama los endpoints directo. La app
36
+ * controla `[isOpen]` y reacciona a `(changed)` / `(dismissed)`.
37
+ *
38
+ * i18n: namespace compartido `_auth`.
39
+ *
40
+ * @example
41
+ * ```html
42
+ * <val-mfa-modal
43
+ * [isOpen]="isModalOpen()"
44
+ * (dismissed)="isModalOpen.set(false)"
45
+ * />
46
+ * ```
47
+ */
48
+ export class MfaModalComponent {
49
+ /** Controla la visibilidad. Cada apertura re-resuelve el estado MFA. */
50
+ set isOpen(value) {
51
+ const opening = value && !this._isOpen;
52
+ this._isOpen = value;
53
+ if (opening) {
54
+ this.open();
55
+ }
56
+ }
57
+ get isOpen() {
58
+ return this._isOpen;
59
+ }
60
+ constructor() {
61
+ this._isOpen = false;
62
+ /** Emite cuando el estado MFA cambia (habilitado / deshabilitado). */
63
+ this.changed = new EventEmitter();
64
+ /** Emite cuando el user cierra el modal (botón X o backdrop). */
65
+ this.dismissed = new EventEmitter();
66
+ this.auth = inject(AuthService);
67
+ this.toast = inject(ToastService);
68
+ this.i18n = inject(I18nService);
69
+ this.i18nHelper = inject(InputI18nHelper);
70
+ this.qrGen = inject(QrGeneratorService);
71
+ this._step = signal('loading');
72
+ /** Paso actual del flujo. */
73
+ this.step = this._step.asReadonly();
74
+ /** `true` mientras una llamada al backend está en curso. */
75
+ this.working = signal(false);
76
+ // Estado MFA actual.
77
+ this.mfaEnabled = signal(false);
78
+ this.mfaMethod = signal(null);
79
+ this.userPhone = signal(null);
80
+ this.backupCodesCount = signal(0);
81
+ // Estado del flujo de habilitación.
82
+ this.selectedMethod = signal('TOTP');
83
+ this.totpSetup = signal(null);
84
+ this.totpQr = signal(null);
85
+ /** Códigos de respaldo recién regenerados — se muestran una sola vez. */
86
+ this.regeneratedCodes = signal(null);
87
+ this.resendCooldown = signal(0);
88
+ this.pinControl = new FormControl('', [Validators.required, Validators.minLength(6), Validators.maxLength(6)]);
89
+ this.phoneControl = new FormControl('', [Validators.required, Validators.pattern(/^\+[1-9]\d{6,14}$/)]);
90
+ this.pinInputProps = {
91
+ control: this.pinControl,
92
+ token: 'mfa-code',
93
+ length: 6,
94
+ allowNumbersOnly: true,
95
+ autoFocus: true,
96
+ };
97
+ /** Form de deshabilitación — `val-form` con un campo de contraseña. */
98
+ this.disableFormProps = computed(() => this.i18nHelper.resolveForm({
99
+ nameKey: 'mfaDisableTitle',
100
+ i18nNamespace: '_auth',
101
+ sections: [
102
+ {
103
+ name: this.t('mfaDisablePrompt'),
104
+ order: 0,
105
+ fields: [
106
+ {
107
+ type: InputType.PASSWORD,
108
+ name: 'password',
109
+ token: 'mfa-disable-password',
110
+ labelKey: 'mfaPasswordLabel',
111
+ hint: '',
112
+ placeholderKey: 'passwordPlaceholder',
113
+ errorKeys: { required: 'mfaPasswordRequired' },
114
+ validators: [Validators.required],
115
+ order: 0,
116
+ state: ComponentStates.ENABLED,
117
+ },
118
+ ],
119
+ },
120
+ ],
121
+ actions: {
122
+ ...SolidDefaultBlock('', 'submit'),
123
+ token: 'mfa-disable-submit',
124
+ textKey: 'mfaDisableButton',
125
+ },
126
+ state: this.working() ? ComponentStates.WORKING : ComponentStates.ENABLED,
127
+ }));
128
+ this.resendTimer = null;
129
+ addIcons({ closeOutline, informationCircleOutline });
130
+ }
131
+ ngOnDestroy() {
132
+ this.stopCooldown();
133
+ }
134
+ /** Traduce una clave del namespace `_auth`. */
135
+ t(key) {
136
+ return this.i18n.t(key, '_auth');
137
+ }
138
+ /** Cierre iniciado por el user (X / backdrop). */
139
+ close() {
140
+ this.dismissed.emit();
141
+ }
142
+ /**
143
+ * Punto de entrada al abrir el modal. Con `prefillCode` (deep-link del email
144
+ * de setup MFA-email) salta directo a la confirmación; si no, resuelve el
145
+ * estado MFA actual.
146
+ */
147
+ open() {
148
+ if (this.prefillCode) {
149
+ this.resetFlow();
150
+ this.selectedMethod.set('EMAIL');
151
+ this.pinControl.setValue(this.prefillCode);
152
+ this._step.set('code-confirm');
153
+ return;
154
+ }
155
+ this.resolveStatus();
156
+ }
157
+ // ===========================================================================
158
+ // Carga de estado
159
+ // ===========================================================================
160
+ /** Consulta el perfil para conocer el estado MFA y posicionar el flujo. */
161
+ resolveStatus() {
162
+ this._step.set('loading');
163
+ this.resetFlow();
164
+ this.auth.getProfile().subscribe({
165
+ next: profile => {
166
+ this.mfaEnabled.set(profile.mfaEnabled);
167
+ this.mfaMethod.set(profile.mfaMethod ?? null);
168
+ this.userPhone.set(profile.phone ?? null);
169
+ if (profile.mfaEnabled && profile.mfaMethod === 'TOTP') {
170
+ this.loadBackupCount();
171
+ }
172
+ this._step.set('status');
173
+ },
174
+ error: () => {
175
+ // Fallback: usar el signal de usuario en sesión.
176
+ const user = this.auth.user();
177
+ this.mfaEnabled.set(user?.mfaEnabled ?? false);
178
+ this.mfaMethod.set(user?.mfaMethod ?? null);
179
+ this._step.set('status');
180
+ },
181
+ });
182
+ }
183
+ loadBackupCount() {
184
+ this.auth.getBackupCodesCount().subscribe({
185
+ next: res => this.backupCodesCount.set(res.count),
186
+ error: () => this.backupCodesCount.set(0),
187
+ });
188
+ }
189
+ // ===========================================================================
190
+ // Navegación entre pasos
191
+ // ===========================================================================
192
+ goToMethodSelect() {
193
+ this.regeneratedCodes.set(null);
194
+ this._step.set('method-select');
195
+ }
196
+ goToDisable() {
197
+ this._step.set('disable');
198
+ }
199
+ backToStatus() {
200
+ this.stopCooldown();
201
+ this.resolveStatus();
202
+ }
203
+ // ===========================================================================
204
+ // Habilitar MFA
205
+ // ===========================================================================
206
+ /** Continúa desde el selector de método al setup correspondiente. */
207
+ proceedWithMethod() {
208
+ const method = this.selectedMethod();
209
+ if (method === 'TOTP') {
210
+ this.setupTotp();
211
+ return;
212
+ }
213
+ let phone;
214
+ if (method === 'SMS' && !this.userPhone()) {
215
+ if (this.phoneControl.invalid) {
216
+ this.phoneControl.markAsTouched();
217
+ this.showToast(this.t('mfaPhoneInvalid'));
218
+ return;
219
+ }
220
+ phone = this.phoneControl.value ?? undefined;
221
+ }
222
+ this.working.set(true);
223
+ this.auth.setupMFA(method, phone).subscribe({
224
+ next: res => {
225
+ this.working.set(false);
226
+ if (res.codeSent) {
227
+ this.pinControl.reset();
228
+ this._step.set('code-confirm');
229
+ this.startCooldown();
230
+ }
231
+ },
232
+ error: err => {
233
+ this.working.set(false);
234
+ this.showToast(this.resolveError(err));
235
+ },
236
+ });
237
+ }
238
+ setupTotp() {
239
+ this.working.set(true);
240
+ this.auth.setupTOTP().subscribe({
241
+ next: res => {
242
+ this.totpSetup.set(res);
243
+ this.pinControl.reset();
244
+ this.qrGen
245
+ .generate({ data: res.qrCodeUrl, width: 220 })
246
+ .then(qr => this.totpQr.set(qr))
247
+ .catch(() => this.totpQr.set(null));
248
+ this.working.set(false);
249
+ this._step.set('totp-setup');
250
+ },
251
+ error: err => {
252
+ this.working.set(false);
253
+ this.showToast(this.resolveError(err));
254
+ },
255
+ });
256
+ }
257
+ /** Verifica el código TOTP de la app de autenticación y activa MFA. */
258
+ verifyTotp() {
259
+ const code = this.pinControl.value;
260
+ if (!code || code.length !== 6) {
261
+ this.showToast(this.t('mfaCodeInvalid'));
262
+ return;
263
+ }
264
+ this.working.set(true);
265
+ this.auth.verifyTOTPSetup(code).subscribe({
266
+ next: res => {
267
+ this.working.set(false);
268
+ if (res.enabled) {
269
+ this.showToast(this.t('mfaEnabledOk'));
270
+ this.changed.emit();
271
+ this.resolveStatus();
272
+ }
273
+ },
274
+ error: err => {
275
+ this.working.set(false);
276
+ this.showToast(this.resolveError(err));
277
+ },
278
+ });
279
+ }
280
+ /** Confirma el código EMAIL/SMS y activa MFA. */
281
+ confirmCode() {
282
+ const code = this.pinControl.value;
283
+ if (!code || code.length !== 6) {
284
+ this.showToast(this.t('mfaCodeInvalid'));
285
+ return;
286
+ }
287
+ this.working.set(true);
288
+ this.auth.confirmMFA(code).subscribe({
289
+ next: res => {
290
+ this.working.set(false);
291
+ if (res.mfaEnabled) {
292
+ this.showToast(this.t('mfaEnabledOk'));
293
+ this.changed.emit();
294
+ this.resolveStatus();
295
+ }
296
+ },
297
+ error: err => {
298
+ this.working.set(false);
299
+ this.showToast(this.resolveError(err));
300
+ },
301
+ });
302
+ }
303
+ /** Reenvía el código EMAIL/SMS (re-ejecuta el setup). */
304
+ resendCode() {
305
+ if (this.resendCooldown() > 0) {
306
+ return;
307
+ }
308
+ this.proceedWithMethod();
309
+ }
310
+ // ===========================================================================
311
+ // Gestión (MFA habilitado)
312
+ // ===========================================================================
313
+ /** Regenera los códigos de respaldo TOTP y los muestra una vez. */
314
+ regenerateBackupCodes() {
315
+ this.working.set(true);
316
+ this.auth.regenerateBackupCodes().subscribe({
317
+ next: res => {
318
+ this.working.set(false);
319
+ this.backupCodesCount.set(res.backupCodes.length);
320
+ this.regeneratedCodes.set(res.backupCodes);
321
+ },
322
+ error: err => {
323
+ this.working.set(false);
324
+ this.showToast(this.resolveError(err));
325
+ },
326
+ });
327
+ }
328
+ /** Deshabilita MFA — requiere la contraseña de la cuenta. */
329
+ onDisableSubmit(event) {
330
+ const password = event.fields['password'];
331
+ if (!password) {
332
+ this.showToast(this.t('mfaPasswordRequired'));
333
+ return;
334
+ }
335
+ this.working.set(true);
336
+ this.auth.disableMFA(password).subscribe({
337
+ next: res => {
338
+ this.working.set(false);
339
+ if (res.mfaDisabled) {
340
+ this.showToast(this.t('mfaDisabledOk'));
341
+ this.changed.emit();
342
+ this.resolveStatus();
343
+ }
344
+ },
345
+ error: err => {
346
+ this.working.set(false);
347
+ this.showToast(this.resolveError(err));
348
+ },
349
+ });
350
+ }
351
+ /** Copia una lista de códigos de respaldo al portapapeles. */
352
+ async copyCodes(codes) {
353
+ try {
354
+ await navigator.clipboard.writeText(codes.join('\n'));
355
+ this.showToast(this.t('mfaCodesCopied'));
356
+ }
357
+ catch {
358
+ /* sin recurso de copia disponible */
359
+ }
360
+ }
361
+ /** Etiqueta i18n legible para un método MFA. */
362
+ methodLabel(method) {
363
+ switch (method) {
364
+ case 'TOTP':
365
+ return this.t('mfaMethodTotp');
366
+ case 'EMAIL':
367
+ return this.t('mfaMethodEmail');
368
+ case 'SMS':
369
+ return this.t('mfaMethodSms');
370
+ default:
371
+ return '';
372
+ }
373
+ }
374
+ // ===========================================================================
375
+ // Helpers
376
+ // ===========================================================================
377
+ resetFlow() {
378
+ this.pinControl.reset();
379
+ this.phoneControl.reset();
380
+ this.totpSetup.set(null);
381
+ this.totpQr.set(null);
382
+ this.regeneratedCodes.set(null);
383
+ this.selectedMethod.set('TOTP');
384
+ this.stopCooldown();
385
+ }
386
+ startCooldown() {
387
+ this.stopCooldown();
388
+ this.resendCooldown.set(RESEND_COOLDOWN_SECONDS);
389
+ this.resendTimer = setInterval(() => {
390
+ this.resendCooldown.update(v => v - 1);
391
+ if (this.resendCooldown() <= 0) {
392
+ this.stopCooldown();
393
+ }
394
+ }, 1000);
395
+ }
396
+ stopCooldown() {
397
+ if (this.resendTimer) {
398
+ clearInterval(this.resendTimer);
399
+ this.resendTimer = null;
400
+ }
401
+ this.resendCooldown.set(0);
402
+ }
403
+ /** Mapea los códigos de error MFA del backend a mensajes del namespace `_auth`. */
404
+ resolveError(err) {
405
+ const code = err?.code;
406
+ switch (code) {
407
+ case 'AUTHV2_MFA_INVALID_CODE':
408
+ return this.t('mfaErrorInvalidCode');
409
+ case 'AUTHV2_EXPIRED_CODE':
410
+ return this.t('mfaErrorExpiredCode');
411
+ case 'AUTHV2_CODE_USED':
412
+ return this.t('mfaErrorCodeUsed');
413
+ case 'AUTHV2_MFA_ALREADY_ACTIVE':
414
+ return this.t('mfaErrorAlreadyActive');
415
+ case 'AUTHV2_MFA_NOT_ENABLED':
416
+ return this.t('mfaErrorNotEnabled');
417
+ case 'AUTHV2_PHONE_REQUIRED':
418
+ return this.t('mfaErrorPhoneRequired');
419
+ case 'AUTHV2_PHONE_EXISTS':
420
+ return this.t('mfaErrorPhoneExists');
421
+ case 'AUTHV2_TOO_MANY_ATTEMPTS':
422
+ return this.t('errorTooManyAttempts');
423
+ case 'AUTHV2_INVALID_CURRENT_PASSWORD':
424
+ return this.t('errorCurrentPasswordWrong');
425
+ default:
426
+ return this.t('errorGeneric');
427
+ }
428
+ }
429
+ showToast(message) {
430
+ this.toast.show({ message, duration: 3500 });
431
+ }
432
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MfaModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
433
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: MfaModalComponent, isStandalone: true, selector: "val-mfa-modal", inputs: { isOpen: "isOpen", prefillCode: "prefillCode" }, outputs: { changed: "changed", dismissed: "dismissed" }, ngImport: i0, template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"close()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n\n <ion-content class=\"ion-padding\">\n <section class=\"mfa-modal\">\n @switch (step()) { @case ('loading') {\n <div class=\"mfa-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @case ('status') {\n <h2 class=\"mfa-title\">{{ t('mfaManageTitle') }}</h2>\n\n @if (mfaEnabled()) {\n <p class=\"mfa-status mfa-status--on\">{{ t('mfaEnabledLabel') }} \u00B7 {{ methodLabel(mfaMethod()) }}</p>\n\n @if (mfaMethod() === 'TOTP') {\n <div class=\"mfa-block\">\n <h3>{{ t('mfaBackupCodesTitle') }}</h3>\n <p class=\"mfa-text\">{{ t('mfaBackupCodesAvailable') }}: {{ backupCodesCount() }}</p>\n\n @if (regeneratedCodes(); as codes) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of codes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"copyCodes(codes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n } @else { @if (backupCodesCount() < 3) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesLow') }}</p>\n </div>\n }\n <ion-button\n expand=\"block\"\n color=\"dark\"\n fill=\"outline\"\n [disabled]=\"working()\"\n (click)=\"regenerateBackupCodes()\"\n >\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaRegenerateCodes') }} }\n </ion-button>\n }\n </div>\n }\n\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"goToDisable()\">\n {{ t('mfaDisableButton') }}\n </ion-button>\n } @else {\n <p class=\"mfa-status mfa-status--off\">{{ t('mfaDisabledLabel') }}</p>\n <p class=\"mfa-text\">{{ t('mfaDisabledHint') }}</p>\n <ion-button expand=\"block\" (click)=\"goToMethodSelect()\"> {{ t('mfaEnableButton') }} </ion-button>\n } } @case ('method-select') {\n <h2 class=\"mfa-title\">{{ t('mfaEnableTitle') }}</h2>\n <p class=\"mfa-text\">{{ t('mfaMethodPrompt') }}</p>\n\n <ion-radio-group [value]=\"selectedMethod()\" (ionChange)=\"selectedMethod.set($event.detail.value)\">\n <ion-item>\n <ion-radio slot=\"start\" value=\"TOTP\"></ion-radio>\n <ion-label>\n <strong>{{ t('mfaMethodTotp') }}</strong>\n <p>{{ t('mfaMethodTotpHint') }}</p>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-radio slot=\"start\" value=\"EMAIL\"></ion-radio>\n <ion-label>\n <strong>{{ t('mfaMethodEmail') }}</strong>\n <p>{{ t('mfaMethodEmailHint') }}</p>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-radio slot=\"start\" value=\"SMS\"></ion-radio>\n <ion-label>\n <strong>{{ t('mfaMethodSms') }}</strong>\n <p>{{ t('mfaMethodSmsHint') }}</p>\n </ion-label>\n </ion-item>\n </ion-radio-group>\n\n @if (selectedMethod() === 'SMS' && !userPhone()) {\n <ion-input\n label=\"{{ t('mfaPhoneLabel') }}\"\n labelPlacement=\"floating\"\n fill=\"outline\"\n type=\"tel\"\n placeholder=\"+56912345678\"\n [formControl]=\"phoneControl\"\n ></ion-input>\n @if (phoneControl.invalid && phoneControl.touched) {\n <p class=\"mfa-error\">{{ t('mfaPhoneInvalid') }}</p>\n } } @if (selectedMethod() === 'SMS' && userPhone(); as phone) {\n <p class=\"mfa-text\">{{ t('mfaPhoneRegistered') }}: {{ phone }}</p>\n }\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"proceedWithMethod()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaContinue') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('totp-setup') {\n <h2 class=\"mfa-title\">{{ t('mfaTotpSetupTitle') }}</h2>\n <p class=\"mfa-text\">{{ t('mfaTotpStep1') }}</p>\n\n @if (totpQr(); as qr) {\n <div class=\"mfa-qr\">\n <val-qr-code [props]=\"{ qr: qr }\" />\n </div>\n } @if (totpSetup(); as setup) {\n <p class=\"mfa-text\">{{ t('mfaTotpManualEntry') }}</p>\n <code class=\"mfa-secret\">{{ setup.secret }}</code>\n\n <p class=\"mfa-text\">{{ t('mfaTotpStep2') }}</p>\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"verifyTotp()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaTotpVerify') }} }\n </ion-button>\n\n <div class=\"mfa-backup\">\n <h3>{{ t('mfaBackupCodesTitle') }}</h3>\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of setup.backupCodes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"copyCodes(setup.backupCodes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n </div>\n }\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('code-confirm') {\n <h2 class=\"mfa-title\">{{ t('mfaConfirmTitle') }}</h2>\n <p class=\"mfa-text\">\n {{ selectedMethod() === 'EMAIL' ? t('mfaConfirmPromptEmail') : t('mfaConfirmPromptSms') }}\n </p>\n\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"confirmCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaConfirmButton') }} }\n </ion-button>\n\n <p class=\"mfa-resend\">\n {{ t('mfaNoCode') }} @if (resendCooldown() > 0) {\n <span class=\"mfa-text\">{{ t('mfaResendIn') }} {{ resendCooldown() }}s</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('mfaResend') }}</a>\n }\n </p>\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('disable') {\n <val-form [props]=\"disableFormProps()\" (onSubmit)=\"onDisableSubmit($event)\" />\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".mfa-modal{display:flex;flex-direction:column;gap:14px;padding-top:4px}.mfa-loading{display:flex;justify-content:center;padding:40px 0}.mfa-title{font-size:18px;font-weight:700;margin:0;color:var(--ion-color-dark)}.mfa-status{font-weight:600;margin:0}.mfa-status--on{color:var(--ion-color-success-shade)}.mfa-status--off{color:var(--ion-color-dark)}.mfa-text{color:var(--ion-color-dark);font-size:14px;margin:0}.mfa-error{color:var(--ion-color-danger);font-size:13px;margin:4px 0 0}.mfa-block,.mfa-backup{display:flex;flex-direction:column;gap:12px}.mfa-block{padding:16px;border-radius:14px;background:var(--ion-color-light)}.mfa-block h3,.mfa-backup h3{font-size:15px;font-weight:600;margin:0;color:var(--ion-color-dark)}.mfa-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;background:var(--ion-color-light);border-left:3px solid var(--ion-color-primary)}.mfa-alert ion-icon{font-size:20px;color:var(--ion-color-primary);flex-shrink:0;margin-top:1px}.mfa-alert p{margin:0;font-size:13px;line-height:1.45;color:var(--ion-color-dark)}.mfa-qr{display:flex;justify-content:center;padding:8px 0}.mfa-secret{display:block;padding:10px 12px;border-radius:8px;background:var(--ion-color-light);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:15px;letter-spacing:.04em;word-break:break-all;text-align:center}.mfa-pin{display:flex;justify-content:center;padding:8px 0}.mfa-codes{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:12px;border-radius:12px;background:var(--ion-color-light)}.mfa-code{padding:8px;border-radius:6px;background:var(--ion-background-color, #fff);color:var(--ion-color-dark);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;text-align:center}.mfa-resend{text-align:center;font-size:14px;color:var(--ion-color-dark);margin:4px 0}.mfa-resend a{color:var(--ion-color-primary);cursor:pointer;font-weight:600}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonRadio, selector: "ion-radio", inputs: ["alignment", "color", "disabled", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonRadioGroup, selector: "ion-radio-group", inputs: ["allowEmptySelection", "compareWith", "errorText", "helperText", "name", "value"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: QrCodeComponent, selector: "val-qr-code", inputs: ["props"], outputs: ["actionComplete", "imageLoad", "imageError"] }, { kind: "component", type: PinInputComponent, selector: "val-pin-input", inputs: ["props"] }] }); }
434
+ }
435
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MfaModalComponent, decorators: [{
436
+ type: Component,
437
+ args: [{ selector: 'val-mfa-modal', standalone: true, imports: [
438
+ ReactiveFormsModule,
439
+ IonButton,
440
+ IonButtons,
441
+ IonContent,
442
+ IonHeader,
443
+ IonIcon,
444
+ IonInput,
445
+ IonItem,
446
+ IonLabel,
447
+ IonModal,
448
+ IonRadio,
449
+ IonRadioGroup,
450
+ IonSpinner,
451
+ IonToolbar,
452
+ FormComponent,
453
+ QrCodeComponent,
454
+ PinInputComponent,
455
+ ], template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"close()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n\n <ion-content class=\"ion-padding\">\n <section class=\"mfa-modal\">\n @switch (step()) { @case ('loading') {\n <div class=\"mfa-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @case ('status') {\n <h2 class=\"mfa-title\">{{ t('mfaManageTitle') }}</h2>\n\n @if (mfaEnabled()) {\n <p class=\"mfa-status mfa-status--on\">{{ t('mfaEnabledLabel') }} \u00B7 {{ methodLabel(mfaMethod()) }}</p>\n\n @if (mfaMethod() === 'TOTP') {\n <div class=\"mfa-block\">\n <h3>{{ t('mfaBackupCodesTitle') }}</h3>\n <p class=\"mfa-text\">{{ t('mfaBackupCodesAvailable') }}: {{ backupCodesCount() }}</p>\n\n @if (regeneratedCodes(); as codes) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of codes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"copyCodes(codes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n } @else { @if (backupCodesCount() < 3) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesLow') }}</p>\n </div>\n }\n <ion-button\n expand=\"block\"\n color=\"dark\"\n fill=\"outline\"\n [disabled]=\"working()\"\n (click)=\"regenerateBackupCodes()\"\n >\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaRegenerateCodes') }} }\n </ion-button>\n }\n </div>\n }\n\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"goToDisable()\">\n {{ t('mfaDisableButton') }}\n </ion-button>\n } @else {\n <p class=\"mfa-status mfa-status--off\">{{ t('mfaDisabledLabel') }}</p>\n <p class=\"mfa-text\">{{ t('mfaDisabledHint') }}</p>\n <ion-button expand=\"block\" (click)=\"goToMethodSelect()\"> {{ t('mfaEnableButton') }} </ion-button>\n } } @case ('method-select') {\n <h2 class=\"mfa-title\">{{ t('mfaEnableTitle') }}</h2>\n <p class=\"mfa-text\">{{ t('mfaMethodPrompt') }}</p>\n\n <ion-radio-group [value]=\"selectedMethod()\" (ionChange)=\"selectedMethod.set($event.detail.value)\">\n <ion-item>\n <ion-radio slot=\"start\" value=\"TOTP\"></ion-radio>\n <ion-label>\n <strong>{{ t('mfaMethodTotp') }}</strong>\n <p>{{ t('mfaMethodTotpHint') }}</p>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-radio slot=\"start\" value=\"EMAIL\"></ion-radio>\n <ion-label>\n <strong>{{ t('mfaMethodEmail') }}</strong>\n <p>{{ t('mfaMethodEmailHint') }}</p>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-radio slot=\"start\" value=\"SMS\"></ion-radio>\n <ion-label>\n <strong>{{ t('mfaMethodSms') }}</strong>\n <p>{{ t('mfaMethodSmsHint') }}</p>\n </ion-label>\n </ion-item>\n </ion-radio-group>\n\n @if (selectedMethod() === 'SMS' && !userPhone()) {\n <ion-input\n label=\"{{ t('mfaPhoneLabel') }}\"\n labelPlacement=\"floating\"\n fill=\"outline\"\n type=\"tel\"\n placeholder=\"+56912345678\"\n [formControl]=\"phoneControl\"\n ></ion-input>\n @if (phoneControl.invalid && phoneControl.touched) {\n <p class=\"mfa-error\">{{ t('mfaPhoneInvalid') }}</p>\n } } @if (selectedMethod() === 'SMS' && userPhone(); as phone) {\n <p class=\"mfa-text\">{{ t('mfaPhoneRegistered') }}: {{ phone }}</p>\n }\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"proceedWithMethod()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaContinue') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('totp-setup') {\n <h2 class=\"mfa-title\">{{ t('mfaTotpSetupTitle') }}</h2>\n <p class=\"mfa-text\">{{ t('mfaTotpStep1') }}</p>\n\n @if (totpQr(); as qr) {\n <div class=\"mfa-qr\">\n <val-qr-code [props]=\"{ qr: qr }\" />\n </div>\n } @if (totpSetup(); as setup) {\n <p class=\"mfa-text\">{{ t('mfaTotpManualEntry') }}</p>\n <code class=\"mfa-secret\">{{ setup.secret }}</code>\n\n <p class=\"mfa-text\">{{ t('mfaTotpStep2') }}</p>\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"verifyTotp()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaTotpVerify') }} }\n </ion-button>\n\n <div class=\"mfa-backup\">\n <h3>{{ t('mfaBackupCodesTitle') }}</h3>\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of setup.backupCodes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"copyCodes(setup.backupCodes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n </div>\n }\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('code-confirm') {\n <h2 class=\"mfa-title\">{{ t('mfaConfirmTitle') }}</h2>\n <p class=\"mfa-text\">\n {{ selectedMethod() === 'EMAIL' ? t('mfaConfirmPromptEmail') : t('mfaConfirmPromptSms') }}\n </p>\n\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"confirmCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaConfirmButton') }} }\n </ion-button>\n\n <p class=\"mfa-resend\">\n {{ t('mfaNoCode') }} @if (resendCooldown() > 0) {\n <span class=\"mfa-text\">{{ t('mfaResendIn') }} {{ resendCooldown() }}s</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('mfaResend') }}</a>\n }\n </p>\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('disable') {\n <val-form [props]=\"disableFormProps()\" (onSubmit)=\"onDisableSubmit($event)\" />\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".mfa-modal{display:flex;flex-direction:column;gap:14px;padding-top:4px}.mfa-loading{display:flex;justify-content:center;padding:40px 0}.mfa-title{font-size:18px;font-weight:700;margin:0;color:var(--ion-color-dark)}.mfa-status{font-weight:600;margin:0}.mfa-status--on{color:var(--ion-color-success-shade)}.mfa-status--off{color:var(--ion-color-dark)}.mfa-text{color:var(--ion-color-dark);font-size:14px;margin:0}.mfa-error{color:var(--ion-color-danger);font-size:13px;margin:4px 0 0}.mfa-block,.mfa-backup{display:flex;flex-direction:column;gap:12px}.mfa-block{padding:16px;border-radius:14px;background:var(--ion-color-light)}.mfa-block h3,.mfa-backup h3{font-size:15px;font-weight:600;margin:0;color:var(--ion-color-dark)}.mfa-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;background:var(--ion-color-light);border-left:3px solid var(--ion-color-primary)}.mfa-alert ion-icon{font-size:20px;color:var(--ion-color-primary);flex-shrink:0;margin-top:1px}.mfa-alert p{margin:0;font-size:13px;line-height:1.45;color:var(--ion-color-dark)}.mfa-qr{display:flex;justify-content:center;padding:8px 0}.mfa-secret{display:block;padding:10px 12px;border-radius:8px;background:var(--ion-color-light);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:15px;letter-spacing:.04em;word-break:break-all;text-align:center}.mfa-pin{display:flex;justify-content:center;padding:8px 0}.mfa-codes{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:12px;border-radius:12px;background:var(--ion-color-light)}.mfa-code{padding:8px;border-radius:6px;background:var(--ion-background-color, #fff);color:var(--ion-color-dark);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;text-align:center}.mfa-resend{text-align:center;font-size:14px;color:var(--ion-color-dark);margin:4px 0}.mfa-resend a{color:var(--ion-color-primary);cursor:pointer;font-weight:600}\n"] }]
456
+ }], ctorParameters: () => [], propDecorators: { isOpen: [{
457
+ type: Input
458
+ }], prefillCode: [{
459
+ type: Input
460
+ }], changed: [{
461
+ type: Output
462
+ }], dismissed: [{
463
+ type: Output
464
+ }] } });
465
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWZhLW1vZGFsLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9vcmdhbmlzbXMvbWZhLW1vZGFsL21mYS1tb2RhbC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvb3JnYW5pc21zL21mYS1tb2RhbC9tZmEtbW9kYWwuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQWEsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM1RyxPQUFPLEVBQUUsV0FBVyxFQUFFLG1CQUFtQixFQUFFLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzlFLE9BQU8sRUFDTCxTQUFTLEVBQ1QsVUFBVSxFQUNWLFVBQVUsRUFDVixTQUFTLEVBQ1QsT0FBTyxFQUNQLFFBQVEsRUFDUixPQUFPLEVBQ1AsUUFBUSxFQUNSLFFBQVEsRUFDUixRQUFRLEVBQ1IsYUFBYSxFQUNiLFVBQVUsRUFDVixVQUFVLEdBQ1gsTUFBTSwyQkFBMkIsQ0FBQztBQUNuQyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxZQUFZLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4RSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUMvRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFDeEUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFDbEYsT0FBTyxFQUFFLGVBQWUsRUFBNEIsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ25GLE9BQU8sRUFBRSxhQUFhLEVB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LFFBQVE7d0JBQ1IsYUFBYTt3QkFDYixVQUFVO3dCQUNWLFVBQVU7d0JBQ1YsYUFBYTt3QkFDYixlQUFlO3dCQUNmLGlCQUFpQjtxQkFDbEI7d0RBU0csTUFBTTtzQkFEVCxLQUFLO2dCQWlCRyxXQUFXO3NCQUFuQixLQUFLO2dCQUdJLE9BQU87c0JBQWhCLE1BQU07Z0JBR0csU0FBUztzQkFBbEIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgY29tcHV0ZWQsIEV2ZW50RW1pdHRlciwgaW5qZWN0LCBJbnB1dCwgT25EZXN0cm95LCBPdXRwdXQsIHNpZ25hbCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRm9ybUNvbnRyb2wsIFJlYWN0aXZlRm9ybXNNb2R1bGUsIFZhbGlkYXRvcnMgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQge1xuICBJb25CdXR0b24sXG4gIElvbkJ1dHRvbnMsXG4gIElvbkNvbnRlbnQsXG4gIElvbkhlYWRlcixcbiAgSW9uSWNvbixcbiAgSW9uSW5wdXQsXG4gIElvbkl0ZW0sXG4gIElvbkxhYmVsLFxuICBJb25Nb2RhbCxcbiAgSW9uUmFkaW8sXG4gIElvblJhZGlvR3JvdXAsXG4gIElvblNwaW5uZXIsXG4gIElvblRvb2xiYXIsXG59IGZyb20gJ0Bpb25pYy9hbmd1bGFyL3N0YW5kYWxvbmUnO1xuaW1wb3J0IHsgYWRkSWNvbnMgfSBmcm9tICdpb25pY29ucyc7XG5pbXBvcnQgeyBjbG9zZU91dGxpbmUsIGluZm9ybWF0aW9uQ2lyY2xlT3V0bGluZSB9IGZyb20gJ2lvbmljb25zL2ljb25zJztcblxuaW1wb3J0IHsgU29saWREZWZhdWx0QmxvY2sgfSBmcm9tICcuLi8uLi9hdG9tcy9idXR0b24vZmFjdG9yeSc7XG5pbXBvcnQgeyBRckNvZGVDb21wb25lbnQgfSBmcm9tICcuLi8uLi9hdG9tcy9xci1jb2RlL3FyLWNvZGUuY29tcG9uZW50JztcbmltcG9ydCB7IFBpbklucHV0Q29tcG9uZW50IH0gZnJvbSAnLi4vLi4vbW9sZWN1bGVzL3Bpbi1pbnB1dC9waW4taW5wdXQuY29tcG9uZW50JztcbmltcG9ydCB7IENvbXBvbmVudFN0YXRlcywgRm9ybU1ldGFkYXRhLCBGb3JtU3VibWl0LCBJbnB1dFR5cGUgfSBmcm9tICcuLi8uLi90eXBlcyc7XG5pbXBvcnQgeyBGb3JtQ29tcG9uZW50IH0gZnJvbSAnLi4vZm9ybS9mb3JtLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBBdXRoU2VydmljZSB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL2F1dGgnO1xuaW1wb3J0IHsgTUZBTWV0aG9kLCBUT1RQU2V0dXBSZXNwb25zZSB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL2F1dGgvdHlwZXMnO1xuaW1wb3J0IHsgSTE4blNlcnZpY2UsIElucHV0STE4bkhlbHBlciB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL2kxOG4nO1xuaW1wb3J0IHsgUXJHZW5lcmF0b3JTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vLi4vc2VydmljZXMvcXItZ2VuZXJhdG9yL3FyLWdlbmVyYXRvci5zZXJ2aWNlJztcbmltcG9ydCB7IFFyUmVzdWx0IH0gZnJvbSAnLi4vLi4vLi4vc2VydmljZXMvcXItZ2VuZXJhdG9yL3R5cGVzJztcbmltcG9ydCB7IFRvYXN0U2VydmljZSB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL3RvYXN0LnNlcnZpY2UnO1xuXG4vKiogUGFzbyBkZWwgZmx1am8gZGVsIG1vZGFsLiAqL1xudHlwZSBNZmFTdGVwID0gJ2xvYWRpbmcnIHwgJ3N0YXR1cycgfCAnbWV0aG9kLXNlbGVjdCcgfCAndG90cC1zZXR1cCcgfCAnY29kZS1jb25maXJtJyB8ICdkaXNhYmxlJztcblxuLyoqIFNlZ3VuZG9zIGRlIGVzcGVyYSBhbnRlcyBkZSBwb2RlciByZWVudmlhciBlbCBjw7NkaWdvIEVNQUlML1NNUy4gKi9cbmNvbnN0IFJFU0VORF9DT09MRE9XTl9TRUNPTkRTID0gMzA7XG5cbi8qKlxuICogYHZhbC1tZmEtbW9kYWxgIOKAlCBtb2RhbCBkZSBnZXN0acOzbiBkZSBhdXRlbnRpY2FjacOzbiBkZSBkb3MgZmFjdG9yZXMgKE1GQSlcbiAqIHBhcmEgdW4gdXN1YXJpbyBhdXRlbnRpY2Fkby4gTWlzbW8gcGF0csOzbiBxdWUgYHZhbC1jaGFuZ2UtcGFzc3dvcmQtbW9kYWxgLlxuICpcbiAqIEZsdWpvIChtw6FxdWluYSBkZSBlc3RhZG9zIGludGVybmEpOlxuICogIC0gYGxvYWRpbmdgIOKGkiBgZ2V0UHJvZmlsZSgpYCBwYXJhIGNvbm9jZXIgZWwgZXN0YWRvIE1GQS5cbiAqICAtIGBzdGF0dXNgIOKGkiBtdWVzdHJhIE1GQSBoYWJpbGl0YWRvL2Rlc2hhYmlsaXRhZG8uIFNpIGVzdMOhIGhhYmlsaXRhZG86XG4gKiAgICBnZXN0acOzbiBkZSBiYWNrdXAgY29kZXMgKFRPVFApICsgZGVzaGFiaWxpdGFyLiBTaSBubzogYm90w7NuIGhhYmlsaXRhci5cbiAqICAtIGBtZXRob2Qtc2VsZWN0YCDihpIgZWxlZ2lyIFRPVFAgLyBFTUFJTCAvIFNNUy5cbiAqICAtIGB0b3RwLXNldHVwYCDihpIgUVIgKyBzZWNyZXRvIG1hbnVhbCArIGJhY2t1cCBjb2RlcyDihpIgdmVyaWZpY2FyIGPDs2RpZ28uXG4gKiAgLSBgY29kZS1jb25maXJtYCDihpIgKEVNQUlML1NNUykgaW5ncmVzYXIgY8OzZGlnbyByZWNpYmlkbywgY29uIHJlZW52w61vLlxuICogIC0gYGRpc2FibGVgIOKGkiBjb250cmFzZcOxYSBwYXJhIGRlc2hhYmlsaXRhciBNRkEuXG4gKlxuICogRWwgUVIgc2UgZ2VuZXJhICoqY2xpZW50LXNpZGUqKiAoYFFyR2VuZXJhdG9yU2VydmljZWApIOKAlCBlbCBzZWNyZXRvIFRPVFBcbiAqIG51bmNhIHNhbGUgZGVsIG5hdmVnYWRvci5cbiAqXG4gKiBTZWxmLWNvbnRhaW5lZDogaW55ZWN0YSBgQXV0aFNlcnZpY2VgIHkgbGxhbWEgbG9zIGVuZHBvaW50cyBkaXJlY3RvLiBMYSBhcHBcbiAqIGNvbnRyb2xhIGBbaXNPcGVuXWAgeSByZWFjY2lvbmEgYSBgKGNoYW5nZWQpYCAvIGAoZGlzbWlzc2VkKWAuXG4gKlxuICogaTE4bjogbmFtZXNwYWNlIGNvbXBhcnRpZG8gYF9hdXRoYC5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgaHRtbFxuICogPHZhbC1tZmEtbW9kYWxcbiAqICAgW2lzT3Blbl09XCJpc01vZGFsT3BlbigpXCJcbiAqICAgKGRpc21pc3NlZCk9XCJpc01vZGFsT3Blbi5zZXQoZmFsc2UpXCJcbiAqIC8+XG4gKiBgYGBcbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAndmFsLW1mYS1tb2RhbCcsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBSZWFjdGl2ZUZvcm1zTW9kdWxlLFxuICAgIElvbkJ1dHRvbixcbiAgICBJb25CdXR0b25zLFxuICAgIElvbkNvbnRlbnQsXG4gICAgSW9uSGVhZGVyLFxuICAgIElvbkljb24sXG4gICAgSW9uSW5wdXQsXG4gICAgSW9uSXRlbSxcbiAgICBJb25MYWJlbCxcbiAgICBJb25Nb2RhbCxcbiAgICBJb25SYWRpbyxcbiAgICBJb25SYWRpb0dyb3VwLFxuICAgIElvblNwaW5uZXIsXG4gICAgSW9uVG9vbGJhcixcbiAgICBGb3JtQ29tcG9uZW50LFxuICAgIFFyQ29kZUNvbXBvbmVudCxcbiAgICBQaW5JbnB1dENvbXBvbmVudCxcbiAgXSxcbiAgdGVtcGxhdGVVcmw6ICcuL21mYS1tb2RhbC5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL21mYS1tb2RhbC5jb21wb25lbnQuc2NzcyddLFxufSlcbmV4cG9ydCBjbGFzcyBNZmFNb2RhbENvbXBvbmVudCBpbXBsZW1lbnRzIE9uRGVzdHJveSB7XG4gIHByaXZhdGUgX2lzT3BlbiA9IGZhbHNlO1xuXG4gIC8qKiBDb250cm9sYSBsYSB2aXNpYmlsaWRhZC4gQ2FkYSBhcGVydHVyYSByZS1yZXN1ZWx2ZSBlbCBlc3RhZG8gTUZBLiAqL1xuICBASW5wdXQoKVxuICBzZXQgaXNPcGVuKHZhbHVlOiBib29sZWFuKSB7XG4gICAgY29uc3Qgb3BlbmluZyA9IHZhbHVlICYmICF0aGlzLl9pc09wZW47XG4gICAgdGhpcy5faXNPcGVuID0gdmFsdWU7XG4gICAgaWYgKG9wZW5pbmcpIHtcbiAgICAgIHRoaXMub3BlbigpO1xuICAgIH1cbiAgfVxuICBnZXQgaXNPcGVuKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLl9pc09wZW47XG4gIH1cblxuICAvKipcbiAgICogQ8OzZGlnbyBNRkEgcHJlLWNhcmdhZG8gKGRlZXAtbGluayBkZXNkZSBlbCBlbWFpbCBkZSBzZXR1cCBNRkEtZW1haWwpLiBTaVxuICAgKiBlc3TDoSBwcmVzZW50ZSBhbCBhYnJpciwgZWwgbW9kYWwgc2FsdGEgZGlyZWN0byBhbCBwYXNvIGRlIGNvbmZpcm1hY2nDs25cbiAgICogY29uIGVsIGPDs2RpZ28gcmVsbGVuYWRvLlxuICAgKi9cbiAgQElucHV0KCkgcHJlZmlsbENvZGU/OiBzdHJpbmc7XG5cbiAgLyoqIEVtaXRlIGN1YW5kbyBlbCBlc3RhZG8gTUZBIGNhbWJpYSAoaGFiaWxpdGFkbyAvIGRlc2hhYmlsaXRhZG8pLiAqL1xuICBAT3V0cHV0KCkgY2hhbmdlZCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcblxuICAvKiogRW1pdGUgY3VhbmRvIGVsIHVzZXIgY2llcnJhIGVsIG1vZGFsIChib3TDs24gWCBvIGJhY2tkcm9wKS4gKi9cbiAgQE91dHB1dCgpIGRpc21pc3NlZCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcblxuICBwcml2YXRlIGF1dGggPSBpbmplY3QoQXV0aFNlcnZpY2UpO1xuICBwcml2YXRlIHRvYXN0ID0gaW5qZWN0KFRvYXN0U2VydmljZSk7XG4gIHByaXZhdGUgaTE4biA9IGluamVjdChJMThuU2VydmljZSk7XG4gIHByaXZhdGUgaTE4bkhlbHBlciA9IGluamVjdChJbnB1dEkxOG5IZWxwZXIpO1xuICBwcml2YXRlIHFyR2VuID0gaW5qZWN0KFFyR2VuZXJhdG9yU2VydmljZSk7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBfc3RlcCA9IHNpZ25hbDxNZmFTdGVwPignbG9hZGluZycpO1xuICAvKiogUGFzbyBhY3R1YWwgZGVsIGZsdWpvLiAqL1xuICByZWFkb25seSBzdGVwID0gdGhpcy5fc3RlcC5hc1JlYWRvbmx5KCk7XG5cbiAgLyoqIGB0cnVlYCBtaWVudHJhcyB1bmEgbGxhbWFkYSBhbCBiYWNrZW5kIGVzdMOhIGVuIGN1cnNvLiAqL1xuICByZWFkb25seSB3b3JraW5nID0gc2lnbmFsKGZhbHNlKTtcblxuICAvLyBFc3RhZG8gTUZBIGFjdHVhbC5cbiAgcmVhZG9ubHkgbWZhRW5hYmxlZCA9IHNpZ25hbChmYWxzZSk7XG4gIHJlYWRvbmx5IG1mYU1ldGhvZCA9IHNpZ25hbDxNRkFNZXRob2QgfCBudWxsPihudWxsKTtcbiAgcmVhZG9ubHkgdXNlclBob25lID0gc2lnbmFsPHN0cmluZyB8IG51bGw+KG51bGwpO1xuICByZWFkb25seSBiYWNrdXBDb2Rlc0NvdW50ID0gc2lnbmFsKDApO1xuXG4gIC8vIEVzdGFkbyBkZWwgZmx1am8gZGUgaGFiaWxpdGFjacOzbi5cbiAgcmVhZG9ubHkgc2VsZWN0ZWRNZXRob2QgPSBzaWduYWw8TUZBTWV0aG9kPignVE9UUCcpO1xuICByZWFkb25seSB0b3RwU2V0dXAgPSBzaWduYWw8VE9UUFNldHVwUmVzcG9uc2UgfCBudWxsPihudWxsKTtcbiAgcmVhZG9ubHkgdG90cFFyID0gc2lnbmFsPFFyUmVzdWx0IHwgbnVsbD4obnVsbCk7XG4gIC8qKiBDw7NkaWdvcyBkZSByZXNwYWxkbyByZWNpw6luIHJlZ2VuZXJhZG9zIOKAlCBzZSBtdWVzdHJhbiB1bmEgc29sYSB2ZXouICovXG4gIHJlYWRvbmx5IHJlZ2VuZXJhdGVkQ29kZXMgPSBzaWduYWw8c3RyaW5nW10gfCBudWxsPihudWxsKTtcbiAgcmVhZG9ubHkgcmVzZW5kQ29vbGRvd24gPSBzaWduYWwoMCk7XG5cbiAgcmVhZG9ubHkgcGluQ29udHJvbCA9IG5ldyBGb3JtQ29udHJvbCgnJywgW1ZhbGlkYXRvcnMucmVxdWlyZWQsIFZhbGlkYXRvcnMubWluTGVuZ3RoKDYpLCBWYWxpZGF0b3JzLm1heExlbmd0aCg2KV0pO1xuICByZWFkb25seSBwaG9uZUNvbnRyb2wgPSBuZXcgRm9ybUNvbnRyb2woJycsIFtWYWxpZGF0b3JzLnJlcXVpcmVkLCBWYWxpZGF0b3JzLnBhdHRlcm4oL15cXCtbMS05XVxcZHs2LDE0fSQvKV0pO1xuXG4gIHJlYWRvbmx5IHBpbklucHV0UHJvcHMgPSB7XG4gICAgY29udHJvbDogdGhpcy5waW5Db250cm9sLFxuICAgIHRva2VuOiAnbWZhLWNvZGUnLFxuICAgIGxlbmd0aDogNixcbiAgICBhbGxvd051bWJlcnNPbmx5OiB0cnVlLFxuICAgIGF1dG9Gb2N1czogdHJ1ZSxcbiAgfTtcblxuICAvKiogRm9ybSBkZSBkZXNoYWJpbGl0YWNpw7NuIOKAlCBgdmFsLWZvcm1gIGNvbiB1biBjYW1wbyBkZSBjb250cmFzZcOxYS4gKi9cbiAgcmVhZG9ubHkgZGlzYWJsZUZvcm1Qcm9wcyA9IGNvbXB1dGVkPEZvcm1NZXRhZGF0YT4oKCkgPT5cbiAgICB0aGlzLmkxOG5IZWxwZXIucmVzb2x2ZUZvcm0oe1xuICAgICAgbmFtZUtleTogJ21mYURpc2FibGVUaXRsZScsXG4gICAgICBpMThuTmFtZXNwYWNlOiAnX2F1dGgnLFxuICAgICAgc2VjdGlvbnM6IFtcbiAgICAgICAge1xuICAgICAgICAgIG5hbWU6IHRoaXMudCgnbWZhRGlzYWJsZVByb21wdCcpLFxuICAgICAgICAgIG9yZGVyOiAwLFxuICAgICAgICAgIGZpZWxkczogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICB0eXBlOiBJbnB1dFR5cGUuUEFTU1dPUkQsXG4gICAgICAgICAgICAgIG5hbWU6ICdwYXNzd29yZCcsXG4gICAgICAgICAgICAgIHRva2VuOiAnbWZhLWRpc2FibGUtcGFzc3dvcmQnLFxuICAgICAgICAgICAgICBsYWJlbEtleTogJ21mYVBhc3N3b3JkTGFiZWwnLFxuICAgICAgICAgICAgICBoaW50OiAnJyxcbiAgICAgICAgICAgICAgcGxhY2Vob2xkZXJLZXk6ICdwYXNzd29yZFBsYWNlaG9sZGVyJyxcbiAgICAgICAgICAgICAgZXJyb3JLZXlzOiB7IHJlcXVpcmVkOiAnbWZhUGFzc3dvcmRSZXF1aXJlZCcgfSxcbiAgICAgICAgICAgICAgdmFsaWRhdG9yczogW1ZhbGlkYXRvcnMucmVxdWlyZWRdLFxuICAgICAgICAgICAgICBvcmRlcjogMCxcbiAgICAgICAgICAgICAgc3RhdGU6IENvbXBvbmVudFN0YXRlcy5FTkFCTEVELFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICAgIGFjdGlvbnM6IHtcbiAgICAgICAgLi4uU29saWREZWZhdWx0QmxvY2soJycsICdzdWJtaXQnKSxcbiAgICAgICAgdG9rZW46ICdtZmEtZGlzYWJsZS1zdWJtaXQnLFxuICAgICAgICB0ZXh0S2V5OiAnbWZhRGlzYWJsZUJ1dHRvbicsXG4gICAgICB9LFxuICAgICAgc3RhdGU6IHRoaXMud29ya2luZygpID8gQ29tcG9uZW50U3RhdGVzLldPUktJTkcgOiBDb21wb25lbnRTdGF0ZXMuRU5BQkxFRCxcbiAgICB9KVxuICApO1xuXG4gIHByaXZhdGUgcmVzZW5kVGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldEludGVydmFsPiB8IG51bGwgPSBudWxsO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIGFkZEljb25zKHsgY2xvc2VPdXRsaW5lLCBpbmZvcm1hdGlvbkNpcmNsZU91dGxpbmUgfSk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLnN0b3BDb29sZG93bigpO1xuICB9XG5cbiAgLyoqIFRyYWR1Y2UgdW5hIGNsYXZlIGRlbCBuYW1lc3BhY2UgYF9hdXRoYC4gKi9cbiAgdChrZXk6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuaTE4bi50KGtleSwgJ19hdXRoJyk7XG4gIH1cblxuICAvKiogQ2llcnJlIGluaWNpYWRvIHBvciBlbCB1c2VyIChYIC8gYmFja2Ryb3ApLiAqL1xuICBjbG9zZSgpOiB2b2lkIHtcbiAgICB0aGlzLmRpc21pc3NlZC5lbWl0KCk7XG4gIH1cblxuICAvKipcbiAgICogUHVudG8gZGUgZW50cmFkYSBhbCBhYnJpciBlbCBtb2RhbC4gQ29uIGBwcmVmaWxsQ29kZWAgKGRlZXAtbGluayBkZWwgZW1haWxcbiAgICogZGUgc2V0dXAgTUZBLWVtYWlsKSBzYWx0YSBkaXJlY3RvIGEgbGEgY29uZmlybWFjacOzbjsgc2kgbm8sIHJlc3VlbHZlIGVsXG4gICAqIGVzdGFkbyBNRkEgYWN0dWFsLlxuICAgKi9cbiAgcHJpdmF0ZSBvcGVuKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnByZWZpbGxDb2RlKSB7XG4gICAgICB0aGlzLnJlc2V0RmxvdygpO1xuICAgICAgdGhpcy5zZWxlY3RlZE1ldGhvZC5zZXQoJ0VNQUlMJyk7XG4gICAgICB0aGlzLnBpbkNvbnRyb2wuc2V0VmFsdWUodGhpcy5wcmVmaWxsQ29kZSk7XG4gICAgICB0aGlzLl9zdGVwLnNldCgnY29kZS1jb25maXJtJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMucmVzb2x2ZVN0YXR1cygpO1xuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIENhcmdhIGRlIGVzdGFkb1xuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAvKiogQ29uc3VsdGEgZWwgcGVyZmlsIHBhcmEgY29ub2NlciBlbCBlc3RhZG8gTUZBIHkgcG9zaWNpb25hciBlbCBmbHVqby4gKi9cbiAgcHJpdmF0ZSByZXNvbHZlU3RhdHVzKCk6IHZvaWQge1xuICAgIHRoaXMuX3N0ZXAuc2V0KCdsb2FkaW5nJyk7XG4gICAgdGhpcy5yZXNldEZsb3coKTtcbiAgICB0aGlzLmF1dGguZ2V0UHJvZmlsZSgpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiBwcm9maWxlID0+IHtcbiAgICAgICAgdGhpcy5tZmFFbmFibGVkLnNldChwcm9maWxlLm1mYUVuYWJsZWQpO1xuICAgICAgICB0aGlzLm1mYU1ldGhvZC5zZXQocHJvZmlsZS5tZmFNZXRob2QgPz8gbnVsbCk7XG4gICAgICAgIHRoaXMudXNlclBob25lLnNldChwcm9maWxlLnBob25lID8/IG51bGwpO1xuICAgICAgICBpZiAocHJvZmlsZS5tZmFFbmFibGVkICYmIHByb2ZpbGUubWZhTWV0aG9kID09PSAnVE9UUCcpIHtcbiAgICAgICAgICB0aGlzLmxvYWRCYWNrdXBDb3VudCgpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuX3N0ZXAuc2V0KCdzdGF0dXMnKTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogKCkgPT4ge1xuICAgICAgICAvLyBGYWxsYmFjazogdXNhciBlbCBzaWduYWwgZGUgdXN1YXJpbyBlbiBzZXNpw7NuLlxuICAgICAgICBjb25zdCB1c2VyID0gdGhpcy5hdXRoLnVzZXIoKTtcbiAgICAgICAgdGhpcy5tZmFFbmFibGVkLnNldCh1c2VyPy5tZmFFbmFibGVkID8/IGZhbHNlKTtcbiAgICAgICAgdGhpcy5tZmFNZXRob2Quc2V0KHVzZXI/Lm1mYU1ldGhvZCA/PyBudWxsKTtcbiAgICAgICAgdGhpcy5fc3RlcC5zZXQoJ3N0YXR1cycpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgbG9hZEJhY2t1cENvdW50KCk6IHZvaWQge1xuICAgIHRoaXMuYXV0aC5nZXRCYWNrdXBDb2Rlc0NvdW50KCkuc3Vic2NyaWJlKHtcbiAgICAgIG5leHQ6IHJlcyA9PiB0aGlzLmJhY2t1cENvZGVzQ291bnQuc2V0KHJlcy5jb3VudCksXG4gICAgICBlcnJvcjogKCkgPT4gdGhpcy5iYWNrdXBDb2Rlc0NvdW50LnNldCgwKSxcbiAgICB9KTtcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBOYXZlZ2FjacOzbiBlbnRyZSBwYXNvc1xuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICBnb1RvTWV0aG9kU2VsZWN0KCk6IHZvaWQge1xuICAgIHRoaXMucmVnZW5lcmF0ZWRDb2Rlcy5zZXQobnVsbCk7XG4gICAgdGhpcy5fc3RlcC5zZXQoJ21ldGhvZC1zZWxlY3QnKTtcbiAgfVxuXG4gIGdvVG9EaXNhYmxlKCk6IHZvaWQge1xuICAgIHRoaXMuX3N0ZXAuc2V0KCdkaXNhYmxlJyk7XG4gIH1cblxuICBiYWNrVG9TdGF0dXMoKTogdm9pZCB7XG4gICAgdGhpcy5zdG9wQ29vbGRvd24oKTtcbiAgICB0aGlzLnJlc29sdmVTdGF0dXMoKTtcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBIYWJpbGl0YXIgTUZBXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKiBDb250aW7DumEgZGVzZGUgZWwgc2VsZWN0b3IgZGUgbcOpdG9kbyBhbCBzZXR1cCBjb3JyZXNwb25kaWVudGUuICovXG4gIHByb2NlZWRXaXRoTWV0aG9kKCk6IHZvaWQge1xuICAgIGNvbnN0IG1ldGhvZCA9IHRoaXMuc2VsZWN0ZWRNZXRob2QoKTtcbiAgICBpZiAobWV0aG9kID09PSAnVE9UUCcpIHtcbiAgICAgIHRoaXMuc2V0dXBUb3RwKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbGV0IHBob25lOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gICAgaWYgKG1ldGhvZCA9PT0gJ1NNUycgJiYgIXRoaXMudXNlclBob25lKCkpIHtcbiAgICAgIGlmICh0aGlzLnBob25lQ29udHJvbC5pbnZhbGlkKSB7XG4gICAgICAgIHRoaXMucGhvbmVDb250cm9sLm1hcmtBc1RvdWNoZWQoKTtcbiAgICAgICAgdGhpcy5zaG93VG9hc3QodGhpcy50KCdtZmFQaG9uZUludmFsaWQnKSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHBob25lID0gdGhpcy5waG9uZUNvbnRyb2wudmFsdWUgPz8gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIHRoaXMud29ya2luZy5zZXQodHJ1ZSk7XG4gICAgdGhpcy5hdXRoLnNldHVwTUZBKG1ldGhvZCwgcGhvbmUpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiByZXMgPT4ge1xuICAgICAgICB0aGlzLndvcmtpbmcuc2V0KGZhbHNlKTtcbiAgICAgICAgaWYgKHJlcy5jb2RlU2VudCkge1xuICAgICAgICAgIHRoaXMucGluQ29udHJvbC5yZXNldCgpO1xuICAgICAgICAgIHRoaXMuX3N0ZXAuc2V0KCdjb2RlLWNvbmZpcm0nKTtcbiAgICAgICAgICB0aGlzLnN0YXJ0Q29vbGRvd24oKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIGVycm9yOiBlcnIgPT4ge1xuICAgICAgICB0aGlzLndvcmtpbmcuc2V0KGZhbHNlKTtcbiAgICAgICAgdGhpcy5zaG93VG9hc3QodGhpcy5yZXNvbHZlRXJyb3IoZXJyKSk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXR1cFRvdHAoKTogdm9pZCB7XG4gICAgdGhpcy53b3JraW5nLnNldCh0cnVlKTtcbiAgICB0aGlzLmF1dGguc2V0dXBUT1RQKCkuc3Vic2NyaWJlKHtcbiAgICAgIG5leHQ6IHJlcyA9PiB7XG4gICAgICAgIHRoaXMudG90cFNldHVwLnNldChyZXMpO1xuICAgICAgICB0aGlzLnBpbkNvbnRyb2wucmVzZXQoKTtcbiAgICAgICAgdGhpcy5xckdlblxuICAgICAgICAgIC5nZW5lcmF0ZSh7IGRhdGE6IHJlcy5xckNvZGVVcmwsIHdpZHRoOiAyMjAgfSlcbiAgICAgICAgICAudGhlbihxciA9PiB0aGlzLnRvdHBRci5zZXQocXIpKVxuICAgICAgICAgIC5jYXRjaCgoKSA9PiB0aGlzLnRvdHBRci5zZXQobnVsbCkpO1xuICAgICAgICB0aGlzLndvcmtpbmcuc2V0KGZhbHNlKTtcbiAgICAgICAgdGhpcy5fc3RlcC5zZXQoJ3RvdHAtc2V0dXAnKTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogZXJyID0+IHtcbiAgICAgICAgdGhpcy53b3JraW5nLnNldChmYWxzZSk7XG4gICAgICAgIHRoaXMuc2hvd1RvYXN0KHRoaXMucmVzb2x2ZUVycm9yKGVycikpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBWZXJpZmljYSBlbCBjw7NkaWdvIFRPVFAgZGUgbGEgYXBwIGRlIGF1dGVudGljYWNpw7NuIHkgYWN0aXZhIE1GQS4gKi9cbiAgdmVyaWZ5VG90cCgpOiB2b2lkIHtcbiAgICBjb25zdCBjb2RlID0gdGhpcy5waW5Db250cm9sLnZhbHVlO1xuICAgIGlmICghY29kZSB8fCBjb2RlLmxlbmd0aCAhPT0gNikge1xuICAgICAgdGhpcy5zaG93VG9hc3QodGhpcy50KCdtZmFDb2RlSW52YWxpZCcpKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy53b3JraW5nLnNldCh0cnVlKTtcbiAgICB0aGlzLmF1dGgudmVyaWZ5VE9UUFNldHVwKGNvZGUpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiByZXMgPT4ge1xuICAgICAgICB0aGlzLndvcmtpbmcuc2V0KGZhbHNlKTtcbiAgICAgICAgaWYgKHJlcy5lbmFibGVkKSB7XG4gICAgICAgICAgdGhpcy5zaG93VG9hc3QodGhpcy50KCdtZmFFbmFibGVkT2snKSk7XG4gICAgICAgICAgdGhpcy5jaGFuZ2VkLmVtaXQoKTtcbiAgICAgICAgICB0aGlzLnJlc29sdmVTdGF0dXMoKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIGVycm9yOiBlcnIgPT4ge1xuICAgICAgICB0aGlzLndvcmtpbmcuc2V0KGZhbHNlKTtcbiAgICAgICAgdGhpcy5zaG93VG9hc3QodGhpcy5yZXNvbHZlRXJyb3IoZXJyKSk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIENvbmZpcm1hIGVsIGPDs2RpZ28gRU1BSUwvU01TIHkgYWN0aXZhIE1GQS4gKi9cbiAgY29uZmlybUNvZGUoKTogdm9pZCB7XG4gICAgY29uc3QgY29kZSA9IHRoaXMucGluQ29udHJvbC52YWx1ZTtcbiAgICBpZiAoIWNvZGUgfHwgY29kZS5sZW5ndGggIT09IDYpIHtcbiAgICAgIHRoaXMuc2hvd1RvYXN0KHRoaXMudCgnbWZhQ29kZUludmFsaWQnKSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMud29ya2luZy5zZXQodHJ1ZSk7XG4gICAgdGhpcy5hdXRoLmNvbmZpcm1NRkEoY29kZSkuc3Vic2NyaWJlKHtcbiAgICAgIG5leHQ6IHJlcyA9PiB7XG4gICAgICAgIHRoaXMud29ya2luZy5zZXQoZmFsc2UpO1xuICAgICAgICBpZiAocmVzLm1mYUVuYWJsZWQpIHtcbiAgICAgICAgICB0aGlzLnNob3dUb2FzdCh0aGlzLnQoJ21mYUVuYWJsZWRPaycpKTtcbiAgICAgICAgICB0aGlzLmNoYW5nZWQuZW1pdCgpO1xuICAgICAgICAgIHRoaXMucmVzb2x2ZVN0YXR1cygpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgZXJyb3I6IGVyciA9PiB7XG4gICAgICAgIHRoaXMud29ya2luZy5zZXQoZmFsc2UpO1xuICAgICAgICB0aGlzLnNob3dUb2FzdCh0aGlzLnJlc29sdmVFcnJvcihlcnIpKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICAvKiogUmVlbnbDrWEgZWwgY8OzZGlnbyBFTUFJTC9TTVMgKHJlLWVqZWN1dGEgZWwgc2V0dXApLiAqL1xuICByZXNlbmRDb2RlKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnJlc2VuZENvb2xkb3duKCkgPiAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMucHJvY2VlZFdpdGhNZXRob2QoKTtcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBHZXN0acOzbiAoTUZBIGhhYmlsaXRhZG8pXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKiBSZWdlbmVyYSBsb3MgY8OzZGlnb3MgZGUgcmVzcGFsZG8gVE9UUCB5IGxvcyBtdWVzdHJhIHVuYSB2ZXouICovXG4gIHJlZ2VuZXJhdGVCYWNrdXBDb2RlcygpOiB2b2lkIHtcbiAgICB0aGlzLndvcmtpbmcuc2V0KHRydWUpO1xuICAgIHRoaXMuYXV0aC5yZWdlbmVyYXRlQmFja3VwQ29kZXMoKS5zdWJzY3JpYmUoe1xuICAgICAgbmV4dDogcmVzID0+IHtcbiAgICAgICAgdGhpcy53b3JraW5nLnNldChmYWxzZSk7XG4gICAgICAgIHRoaXMuYmFja3VwQ29kZXNDb3VudC5zZXQocmVzLmJhY2t1cENvZGVzLmxlbmd0aCk7XG4gICAgICAgIHRoaXMucmVnZW5lcmF0ZWRDb2Rlcy5zZXQocmVzLmJhY2t1cENvZGVzKTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogZXJyID0+IHtcbiAgICAgICAgdGhpcy53b3JraW5nLnNldChmYWxzZSk7XG4gICAgICAgIHRoaXMuc2hvd1RvYXN0KHRoaXMucmVzb2x2ZUVycm9yKGVycikpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBEZXNoYWJpbGl0YSBNRkEg4oCUIHJlcXVpZXJlIGxhIGNvbnRyYXNlw7FhIGRlIGxhIGN1ZW50YS4gKi9cbiAgb25EaXNhYmxlU3VibWl0KGV2ZW50OiBGb3JtU3VibWl0KTogdm9pZCB7XG4gICAgY29uc3QgcGFzc3dvcmQgPSBldmVudC5maWVsZHNbJ3Bhc3N3b3JkJ107XG4gICAgaWYgKCFwYXNzd29yZCkge1xuICAgICAgdGhpcy5zaG93VG9hc3QodGhpcy50KCdtZmFQYXNzd29yZFJlcXVpcmVkJykpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLndvcmtpbmcuc2V0KHRydWUpO1xuICAgIHRoaXMuYXV0aC5kaXNhYmxlTUZBKHBhc3N3b3JkKS5zdWJzY3JpYmUoe1xuICAgICAgbmV4dDogcmVzID0+IHtcbiAgICAgICAgdGhpcy53b3JraW5nLnNldChmYWxzZSk7XG4gICAgICAgIGlmIChyZXMubWZhRGlzYWJsZWQpIHtcbiAgICAgICAgICB0aGlzLnNob3dUb2FzdCh0aGlzLnQoJ21mYURpc2FibGVkT2snKSk7XG4gICAgICAgICAgdGhpcy5jaGFuZ2VkLmVtaXQoKTtcbiAgICAgICAgICB0aGlzLnJlc29sdmVTdGF0dXMoKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIGVycm9yOiBlcnIgPT4ge1xuICAgICAgICB0aGlzLndvcmtpbmcuc2V0KGZhbHNlKTtcbiAgICAgICAgdGhpcy5zaG93VG9hc3QodGhpcy5yZXNvbHZlRXJyb3IoZXJyKSk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIENvcGlhIHVuYSBsaXN0YSBkZSBjw7NkaWdvcyBkZSByZXNwYWxkbyBhbCBwb3J0YXBhcGVsZXMuICovXG4gIGFzeW5jIGNvcHlDb2Rlcyhjb2Rlczogc3RyaW5nW10pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgbmF2aWdhdG9yLmNsaXBib2FyZC53cml0ZVRleHQoY29kZXMuam9pbignXFxuJykpO1xuICAgICAgdGhpcy5zaG93VG9hc3QodGhpcy50KCdtZmFDb2Rlc0NvcGllZCcpKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8qIHNpbiByZWN1cnNvIGRlIGNvcGlhIGRpc3BvbmlibGUgKi9cbiAgICB9XG4gIH1cblxuICAvKiogRXRpcXVldGEgaTE4biBsZWdpYmxlIHBhcmEgdW4gbcOpdG9kbyBNRkEuICovXG4gIG1ldGhvZExhYmVsKG1ldGhvZDogTUZBTWV0aG9kIHwgbnVsbCk6IHN0cmluZyB7XG4gICAgc3dpdGNoIChtZXRob2QpIHtcbiAgICAgIGNhc2UgJ1RPVFAnOlxuICAgICAgICByZXR1cm4gdGhpcy50KCdtZmFNZXRob2RUb3RwJyk7XG4gICAgICBjYXNlICdFTUFJTCc6XG4gICAgICAgIHJldHVybiB0aGlzLnQoJ21mYU1ldGhvZEVtYWlsJyk7XG4gICAgICBjYXNlICdTTVMnOlxuICAgICAgICByZXR1cm4gdGhpcy50KCdtZmFNZXRob2RTbXMnKTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiAnJztcbiAgICB9XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gSGVscGVyc1xuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICBwcml2YXRlIHJlc2V0RmxvdygpOiB2b2lkIHtcbiAgICB0aGlzLnBpbkNvbnRyb2wucmVzZXQoKTtcbiAgICB0aGlzLnBob25lQ29udHJvbC5yZXNldCgpO1xuICAgIHRoaXMudG90cFNldHVwLnNldChudWxsKTtcbiAgICB0aGlzLnRvdHBRci5zZXQobnVsbCk7XG4gICAgdGhpcy5yZWdlbmVyYXRlZENvZGVzLnNldChudWxsKTtcbiAgICB0aGlzLnNlbGVjdGVkTWV0aG9kLnNldCgnVE9UUCcpO1xuICAgIHRoaXMuc3RvcENvb2xkb3duKCk7XG4gIH1cblxuICBwcml2YXRlIHN0YXJ0Q29vbGRvd24oKTogdm9pZCB7XG4gICAgdGhpcy5zdG9wQ29vbGRvd24oKTtcbiAgICB0aGlzLnJlc2VuZENvb2xkb3duLnNldChSRVNFTkRfQ09PTERPV05fU0VDT05EUyk7XG4gICAgdGhpcy5yZXNlbmRUaW1lciA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgIHRoaXMucmVzZW5kQ29vbGRvd24udXBkYXRlKHYgPT4gdiAtIDEpO1xuICAgICAgaWYgKHRoaXMucmVzZW5kQ29vbGRvd24oKSA8PSAwKSB7XG4gICAgICAgIHRoaXMuc3RvcENvb2xkb3duKCk7XG4gICAgICB9XG4gICAgfSwgMTAwMCk7XG4gIH1cblxuICBwcml2YXRlIHN0b3BDb29sZG93bigpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5yZXNlbmRUaW1lcikge1xuICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLnJlc2VuZFRpbWVyKTtcbiAgICAgIHRoaXMucmVzZW5kVGltZXIgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLnJlc2VuZENvb2xkb3duLnNldCgwKTtcbiAgfVxuXG4gIC8qKiBNYXBlYSBsb3MgY8OzZGlnb3MgZGUgZXJyb3IgTUZBIGRlbCBiYWNrZW5kIGEgbWVuc2FqZXMgZGVsIG5hbWVzcGFjZSBgX2F1dGhgLiAqL1xuICBwcml2YXRlIHJlc29sdmVFcnJvcihlcnI6IHVua25vd24pOiBzdHJpbmcge1xuICAgIGNvbnN0IGNvZGUgPSAoZXJyIGFzIHsgY29kZT86IHN0cmluZyB9KT8uY29kZTtcbiAgICBzd2l0Y2ggKGNvZGUpIHtcbiAgICAgIGNhc2UgJ0FVVEhWMl9NRkFfSU5WQUxJRF9DT0RFJzpcbiAgICAgICAgcmV0dXJuIHRoaXMudCgnbWZhRXJyb3JJbnZhbGlkQ29kZScpO1xuICAgICAgY2FzZSAnQVVUSFYyX0VYUElSRURfQ09ERSc6XG4gICAgICAgIHJldHVybiB0aGlzLnQoJ21mYUVycm9yRXhwaXJlZENvZGUnKTtcbiAgICAgIGNhc2UgJ0FVVEhWMl9DT0RFX1VTRUQnOlxuICAgICAgICByZXR1cm4gdGhpcy50KCdtZmFFcnJvckNvZGVVc2VkJyk7XG4gICAgICBjYXNlICdBVVRIVjJfTUZBX0FMUkVBRFlfQUNUSVZFJzpcbiAgICAgICAgcmV0dXJuIHRoaXMudCgnbWZhRXJyb3JBbHJlYWR5QWN0aXZlJyk7XG4gICAgICBjYXNlICdBVVRIVjJfTUZBX05PVF9FTkFCTEVEJzpcbiAgICAgICAgcmV0dXJuIHRoaXMudCgnbWZhRXJyb3JOb3RFbmFibGVkJyk7XG4gICAgICBjYXNlICdBVVRIVjJfUEhPTkVfUkVRVUlSRUQnOlxuICAgICAgICByZXR1cm4gdGhpcy50KCdtZmFFcnJvclBob25lUmVxdWlyZWQnKTtcbiAgICAgIGNhc2UgJ0FVVEhWMl9QSE9ORV9FWElTVFMnOlxuICAgICAgICByZXR1cm4gdGhpcy50KCdtZmFFcnJvclBob25lRXhpc3RzJyk7XG4gICAgICBjYXNlICdBVVRIVjJfVE9PX01BTllfQVRURU1QVFMnOlxuICAgICAgICByZXR1cm4gdGhpcy50KCdlcnJvclRvb01hbnlBdHRlbXB0cycpO1xuICAgICAgY2FzZSAnQVVUSFYyX0lOVkFMSURfQ1VSUkVOVF9QQVNTV09SRCc6XG4gICAgICAgIHJldHVybiB0aGlzLnQoJ2Vycm9yQ3VycmVudFBhc3N3b3JkV3JvbmcnKTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiB0aGlzLnQoJ2Vycm9yR2VuZXJpYycpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc2hvd1RvYXN0KG1lc3NhZ2U6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMudG9hc3Quc2hvdyh7IG1lc3NhZ2UsIGR1cmF0aW9uOiAzNTAwIH0pO1xuICB9XG59XG4iLCI8aW9uLW1vZGFsIFtpc09wZW5dPVwiaXNPcGVuXCIgKGRpZERpc21pc3MpPVwiY2xvc2UoKVwiPlxuICA8bmctdGVtcGxhdGU+XG4gICAgPGlvbi1oZWFkZXI+XG4gICAgICA8aW9uLXRvb2xiYXI+XG4gICAgICAgIDxpb24tYnV0dG9ucyBzbG90PVwiZW5kXCI+XG4gICAgICAgICAgPGlvbi1idXR0b24gZmlsbD1cImNsZWFyXCIgKGNsaWNrKT1cImNsb3NlKClcIj5cbiAgICAgICAgICAgIDxpb24taWNvbiBuYW1lPVwiY2xvc2Utb3V0bGluZVwiPjwvaW9uLWljb24+XG4gICAgICAgICAgPC9pb24tYnV0dG9uPlxuICAgICAgICA8L2lvbi1idXR0b25zPlxuICAgICAgPC9pb24tdG9vbGJhcj5cbiAgICA8L2lvbi1oZWFkZXI+XG5cbiAgICA8aW9uLWNvbnRlbnQgY2xhc3M9XCJpb24tcGFkZGluZ1wiPlxuICAgICAgPHNlY3Rpb24gY2xhc3M9XCJtZmEtbW9kYWxcIj5cbiAgICAgICAgQHN3aXRjaCAoc3RlcCgpKSB7IEBjYXNlICgnbG9hZGluZycpIHtcbiAgICAgICAgPGRpdiBjbGFzcz1cIm1mYS1sb2FkaW5nXCI+XG4gICAgICAgICAgPGlvbi1zcGlubmVyIG5hbWU9XCJjcmVzY2VudFwiPjwvaW9uLXNwaW5uZXI+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICB9IEBjYXNlICgnc3RhdHVzJykge1xuICAgICAgICA8aDIgY2xhc3M9XCJtZmEtdGl0bGVcIj57eyB0KCdtZmFNYW5hZ2VUaXRsZScpIH19PC9oMj5cblxuICAgICAgICBAaWYgKG1mYUVuYWJsZWQoKSkge1xuICAgICAgICA8cCBjbGFzcz1cIm1mYS1zdGF0dXMgbWZhLXN0YXR1cy0tb25cIj57eyB0KCdtZmFFbmFibGVkTGFiZWwnKSB9fSDCtyB7eyBtZXRob2RMYWJlbChtZmFNZXRob2QoKSkgfX08L3A+XG5cbiAgICAgICAgQGlmIChtZmFNZXRob2QoKSA9PT0gJ1RPVFAnKSB7XG4gICAgICAgIDxkaXYgY2xhc3M9XCJtZmEtYmxvY2tcIj5cbiAgICAgICAgICA8aDM+e3sgdCgnbWZhQmFja3VwQ29kZXNUaXRsZScpIH19PC9oMz5cbiAgICAgICAgICA8cCBjbGFzcz1cIm1mYS10ZXh0XCI+e3sgdCgnbWZhQmFja3VwQ29kZXNBdmFpbGFibGUnKSB9fToge3sgYmFja3VwQ29kZXNDb3VudCgpIH19PC9wPlxuXG4gICAgICAgICAgQGlmIChyZWdlbmVyYXRlZENvZGVzKCk7IGFzIGNvZGVzKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm1mYS1hbGVydFwiPlxuICAgICAgICAgICAgPGlvbi1pY29uIG5hbWU9XCJpbmZvcm1hdGlvbi1jaXJjbGUtb3V0bGluZVwiPjwvaW9uLWljb24+XG4gICAgICAgICAgICA8cD57eyB0KCdtZmFCYWNrdXBDb2Rlc1NhdmVXYXJuaW5nJykgfX0ge3sgdCgnbWZhQmFja3VwQ29kZXNFeHBsYWluJykgfX08L3A+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm1mYS1jb2Rlc1wiPlxuICAgICAgICAgICAgQGZvciAoY29kZSBvZiBjb2RlczsgdHJhY2sgY29kZSkge1xuICAgICAgICAgICAgPGNvZGUgY2xhc3M9XCJtZmEtY29kZVwiPnt7IGNvZGUgfX08L2NvZGU+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGlvbi1idXR0b24gZXhwYW5kPVwiYmxvY2tcIiBjb2xvcj1cImRhcmtcIiBmaWxsPVwib3V0bGluZVwiIChjbGljayk9XCJjb3B5Q29kZXMoY29kZXMpXCI+XG4gICAgICAgICAgICB7eyB0KCdtZmFDb3B5Q29kZXMnKSB9fVxuICAgICAgICAgIDwvaW9uLWJ1dHRvbj5cbiAgICAgICAgICB9IEBlbHNlIHsgQGlmIChiYWNrdXBDb2Rlc0NvdW50KCkgPCAzKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm1mYS1hbGVydFwiPlxuICAgICAgICAgICAgPGlvbi1pY29uIG5hbWU9XCJpbmZvcm1hdGlvbi1jaXJjbGUtb3V0bGluZVwiPjwvaW9uLWljb24+XG4gICAgICAgICAgICA8cD57eyB0KCdtZmFCYWNrdXBDb2Rlc0xvdycpIH19PC9wPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgICA8aW9uLWJ1dHRvblxuICAgICAgICAgICAgZXhwYW5kPVwiYmxvY2tcIlxuICAgICAgICAgICAgY29sb3I9XCJkYXJrXCJcbiAgICAgICAgICAgIGZpbGw9XCJvdXRsaW5lXCJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCJ3b3JraW5nKClcIlxuICAgICAgICAgICAgKGNsaWNrKT1cInJlZ2VuZXJhdGVCYWNrdXBDb2RlcygpXCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICBAaWYgKHdvcmtpbmcoKSkge1xuICAgICAgICAgICAgPGlvbi1zcGlubmVyIG5hbWU9XCJjcmVzY2VudFwiPjwvaW9uLXNwaW5uZXI+XG4gICAgICAgICAgICB9IEBlbHNlIHsge3sgdCgnbWZhUmVnZW5lcmF0ZUNvZGVzJykgfX0gfVxuICAgICAgICAgIDwvaW9uLWJ1dHRvbj5cbiAgICAgICAgICB9XG4gICAgICAgIDwvZGl2PlxuICAgICAgICB9XG5cbiAgICAgICAgPGlvbi1idXR0b24gZXhwYW5kPVwiYmxvY2tcIiBjb2xvcj1cImRhcmtcIiBmaWxsPVwib3V0bGluZVwiIChjbGljayk9XCJnb1RvRGlzYWJsZSgpXCI+XG4gICAgICAgICAge3sgdCgnbWZhRGlzYWJsZUJ1dHRvbicpIH19XG4gICAgICAgIDwvaW9uLWJ1dHRvbj5cbiAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgIDxwIGNsYXNzPVwibWZhLXN0YXR1cyBtZmEtc3RhdHVzLS1vZmZcIj57eyB0KCdtZmFEaXNhYmxlZExhYmVsJykgfX08L3A+XG4gICAgICAgIDxwIGNsYXNzPVwibWZhLXRleHRcIj57eyB0KCdtZmFEaXNhYmxlZEhpbnQnKSB9fTwvcD5cbiAgICAgICAgPGlvbi1idXR0b24gZXhwYW5kPVwiYmxvY2tcIiAoY2xpY2spPVwiZ29Ub01ldGhvZFNlbGVjdCgpXCI+IHt7IHQoJ21mYUVuYWJsZUJ1dHRvbicpIH19IDwvaW9uLWJ1dHRvbj5cbiAgICAgICAgfSB9IEBjYXNlICgnbWV0aG9kLXNlbGVjdCcpIHtcbiAgICAgICAgPGgyIGNsYXNzPVwibWZhLXRpdGxlXCI+e3sgdCgnbWZhRW5hYmxlVGl0bGUnKSB9fTwvaDI+XG4gICAgICAgIDxwIGNsYXNzPVwibWZhLXRleHRcIj57eyB0KCdtZmFNZXRob2RQcm9tcHQnKSB9fTwvcD5cblxuICAgICAgICA8aW9uLXJhZGlvLWdyb3VwIFt2YWx1ZV09XCJzZWxlY3RlZE1ldGhvZCgpXCIgKGlvbkNoYW5nZSk9XCJzZWxlY3RlZE1ldGhvZC5zZXQoJGV2ZW50LmRldGFpbC52YWx1ZSlcIj5cbiAgICAgICAgICA8aW9uLWl0ZW0+XG4gICAgICAgICAgICA8aW9uLXJhZGlvIHNsb3Q9XCJzdGFydFwiIHZhbHVlPVwiVE9UUFwiPjwvaW9uLXJhZGlvPlxuICAgICAgICAgICAgPGlvbi1sYWJlbD5cbiAgICAgICAgICAgICAgPHN0cm9uZz57eyB0KCdtZmFNZXRob2RUb3RwJykgfX08L3N0cm9uZz5cbiAgICAgICAgICAgICAgPHA+e3sgdCgnbWZhTWV0aG9kVG90cEhpbnQnKSB9fTwvcD5cbiAgICAgICAgICAgIDwvaW9uLWxhYmVsPlxuICAgICAgICAgIDwvaW9uLWl0ZW0+XG4gICAgICAgICAgPGlvbi1pdGVtPlxuICAgICAgICAgICAgPGlvbi1yYWRpbyBzbG90PVwic3RhcnRcIiB2YWx1ZT1cIkVNQUlMXCI+PC9pb24tcmFkaW8+XG4gICAgICAgICAgICA8aW9uLWxhYmVsPlxuICAgICAgICAgICAgICA8c3Ryb25nPnt7IHQoJ21mYU1ldGhvZEVtYWlsJykgfX08L3N0cm9uZz5cbiAgICAgICAgICAgICAgPHA+e3sgdCgnbWZhTWV0aG9kRW1haWxIaW50JykgfX08L3A+XG4gICAgICAgICAgICA8L2lvbi1sYWJlbD5cbiAgICAgICAgICA8L2lvbi1pdGVtPlxuICAgICAgICAgIDxpb24taXRlbT5cbiAgICAgICAgICAgIDxpb24tcmFkaW8gc2xvdD1cInN0YXJ0XCIgdmFsdWU9XCJTTVNcIj48L2lvbi1yYWRpbz5cbiAgICAgICAgICAgIDxpb24tbGFiZWw+XG4gICAgICAgICAgICAgIDxzdHJvbmc+e3sgdCgnbWZhTWV0aG9kU21zJykgfX08L3N0cm9uZz5cbiAgICAgICAgICAgICAgPHA+e3sgdCgnbWZhTWV0aG9kU21zSGludCcpIH19PC9wPlxuICAgICAgICAgICAgPC9pb24tbGFiZWw+XG4gICAgICAgICAgPC9pb24taXRlbT5cbiAgICAgICAgPC9pb24tcmFkaW8tZ3JvdXA+XG5cbiAgICAgICAgQGlmIChzZWxlY3RlZE1ldGhvZCgpID09PSAnU01TJyAmJiAhdXNlclBob25lKCkpIHtcbiAgICAgICAgPGlvbi1pbnB1dFxuICAgICAgICAgIGxhYmVsPVwie3sgdCgnbWZhUGhvbmVMYWJlbCcpIH19XCJcbiAgICAgICAgICBsYWJlbFBsYWNlbWVudD1cImZsb2F0aW5nXCJcbiAgICAgICAgICBmaWxsPVwib3V0bGluZVwiXG4gICAgICAgICAgdHlwZT1cInRlbFwiXG4gICAgICAgICAgcGxhY2Vob2xkZXI9XCIrNTY5MTIzNDU2NzhcIlxuICAgICAgICAgIFtmb3JtQ29udHJvbF09XCJwaG9uZUNvbnRyb2xcIlxuICAgICAgICA+PC9pb24taW5wdXQ+XG4gICAgICAgIEBpZiAocGhvbmVDb250cm9sLmludmFsaWQgJiYgcGhvbmVDb250cm9sLnRvdWNoZWQpIHtcbiAgICAgICAgPHAgY2xhc3M9XCJtZmEtZXJyb3JcIj57eyB0KCdtZmFQaG9uZUludmFsaWQnKSB9fTwvcD5cbiAgICAgICAgfSB9IEBpZiAoc2VsZWN0ZWRNZXRob2QoKSA9PT0gJ1NNUycgJiYgdXNlclBob25lKCk7IGFzIHBob25lKSB7XG4gICAgICAgIDxwIGNsYXNzPVwibWZhLXRleHRcIj57eyB0KCdtZmFQaG9uZVJlZ2lzdGVyZWQnKSB9fToge3sgcGhvbmUgfX08L3A+XG4gICAgICAgIH1cblxuICAgICAgICA8aW9uLWJ1dHRvbiBleHBhbmQ9XCJibG9ja1wiIFtkaXNhYmxlZF09XCJ3b3JraW5nKClcIiAoY2xpY2spPVwicHJvY2VlZFdpdGhNZXRob2QoKVwiPlxuICAgICAgICAgIEBpZiAod29ya2luZygpKSB7XG4gICAgICAgICAgPGlvbi1zcGlubmVyIG5hbWU9XCJjcmVzY2VudFwiPjwvaW9uLXNwaW5uZXI+XG4gICAgICAgICAgfSBAZWxzZSB7IHt7IHQoJ21mYUNvbnRpbnVlJykgfX0gfVxuICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgIDxpb24tYnV0dG9uIGV4cGFuZD1cImJsb2NrXCIgZmlsbD1cImNsZWFyXCIgY29sb3I9XCJkYXJrXCIgKGNsaWNrKT1cImJhY2tUb1N0YXR1cygpXCI+XG4gICAgICAgICAge3sgdCgnbWZhQ2FuY2VsJykgfX1cbiAgICAgICAgPC9pb24tYnV0dG9uPlxuICAgICAgICB9IEBjYXNlICgndG90cC1zZXR1cCcpIHtcbiAgICAgICAgPGgyIGNsYXNzPVwibWZhLXRpdGxlXCI+e3sgdCgnbWZhVG90cFNldHVwVGl0bGUnKSB9fTwvaDI+XG4gICAgICAgIDxwIGNsYXNzPVwibWZhLXRleHRcIj57eyB0KCdtZmFUb3RwU3RlcDEnKSB9fTwvcD5cblxuICAgICAgICBAaWYgKHRvdHBRcigpOyBhcyBxcikge1xuICAgICAgICA8ZGl2IGNsYXNzPVwibWZhLXFyXCI+XG4gICAgICAgICAgPHZhbC1xci1jb2RlIFtwcm9wc109XCJ7IHFyOiBxciB9XCIgLz5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIH0gQGlmICh0b3RwU2V0dXAoKTsgYXMgc2V0dXApIHtcbiAgICAgICAgPHAgY2xhc3M9XCJtZmEtdGV4dFwiPnt7IHQoJ21mYVRvdHBNYW51YWxFbnRyeScpIH19PC9wPlxuICAgICAgICA8Y29kZSBjbGFzcz1cIm1mYS1zZWNyZXRcIj57eyBzZXR1cC5zZWNyZXQgfX08L2NvZGU+XG5cbiAgICAgICAgPHAgY2xhc3M9XCJtZmEtdGV4dFwiPnt7IHQoJ21mYVRvdHBTdGVwMicpIH19PC9wPlxuICAgICAgICA8ZGl2IGNsYXNzPVwibWZhLXBpblwiPlxuICAgICAgICAgIDx2YWwtcGluLWlucHV0IFtwcm9wc109XCJwaW5JbnB1dFByb3BzXCIgLz5cbiAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgPGlvbi1idXR0b24gZXhwYW5kPVwiYmxvY2tcIiBbZGlzYWJsZWRdPVwid29ya2luZygpXCIgKGNsaWNrKT1cInZlcmlmeVRvdHAoKVwiPlxuICAgICAgICAgIEBpZiAod29ya2luZygpKSB7XG4gICAgICAgICAgPGlvbi1zcGlubmVyIG5hbWU9XCJjcmVzY2VudFwiPjwvaW9uLXNwaW5uZXI+XG4gICAgICAgICAgfSBAZWxzZSB7IHt7IHQoJ21mYVRvdHBWZXJpZnknKSB9fSB9XG4gICAgICAgIDwvaW9uLWJ1dHRvbj5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwibWZhLWJhY2t1cFwiPlxuICAgICAgICAgIDxoMz57eyB0KCdtZmFCYWNrdXBDb2Rlc1RpdGxlJykgfX08L2gzPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJtZmEtYWxlcnRcIj5cbiAgICAgICAgICAgIDxpb24taWNvbiBuYW1lPVwiaW5mb3JtYXRpb24tY2lyY2xlLW91dGxpbmVcIj48L2lvbi1pY29uPlxuICAgICAgICAgICAgPHA+e3sgdCgnbWZhQmFja3VwQ29kZXNTYXZlV2FybmluZycpIH19IHt7IHQoJ21mYUJhY2t1cENvZGVzRXhwbGFpbicpIH19PC9wPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJtZmEtY29kZXNcIj5cbiAgICAgICAgICAgIEBmb3IgKGNvZGUgb2Ygc2V0dXAuYmFja3VwQ29kZXM7IHRyYWNrIGNvZGUpIHtcbiAgICAgICAgICAgIDxjb2RlIGNsYXNzPVwibWZhLWNvZGVcIj57eyBjb2RlIH19PC9jb2RlPlxuICAgICAgICAgICAgfVxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDxpb24tYnV0dG9uIGV4cGFuZD1cImJsb2NrXCIgY29sb3I9XCJkYXJrXCIgZmlsbD1cIm91dGxpbmVcIiAoY2xpY2spPVwiY29weUNvZGVzKHNldHVwLmJhY2t1cENvZGVzKVwiPlxuICAgICAgICAgICAge3sgdCgnbWZhQ29weUNvZGVzJykgfX1cbiAgICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICB9XG5cbiAgICAgICAgPGlvbi1idXR0b24gZXhwYW5kPVwiYmxvY2tcIiBmaWxsPVwiY2xlYXJcIiBjb2xvcj1cImRhcmtcIiAoY2xpY2spPVwiYmFja1RvU3RhdHVzKClcIj5cbiAgICAgICAgICB7eyB0KCdtZmFDYW5jZWwnKSB9fVxuICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgIH0gQGNhc2UgKCdjb2RlLWNvbmZpcm0nKSB7XG4gICAgICAgIDxoMiBjbGFzcz1cIm1mYS10aXRsZVwiPnt7IHQoJ21mYUNvbmZpcm1UaXRsZScpIH19PC9oMj5cbiAgICAgICAgPHAgY2xhc3M9XCJtZmEtdGV4dFwiPlxuICAgICAgICAgIHt7IHNlbGVjdGVkTWV0aG9kKCkgPT09ICdFTUFJTCcgPyB0KCdtZmFDb25maXJtUHJvbXB0RW1haWwnKSA6IHQoJ21mYUNvbmZpcm1Qcm9tcHRTbXMnKSB9fVxuICAgICAgICA8L3A+XG5cbiAgICAgICAgPGRpdiBjbGFzcz1cIm1mYS1waW5cIj5cbiAgICAgICAgICA8dmFsLXBpbi1pbnB1dCBbcHJvcHNdPVwicGluSW5wdXRQcm9wc1wiIC8+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxpb24tYnV0dG9uIGV4cGFuZD1cImJsb2NrXCIgW2Rpc2FibGVkXT1cIndvcmtpbmcoKVwiIChjbGljayk9XCJjb25maXJtQ29kZSgpXCI+XG4gICAgICAgICAgQGlmICh3b3JraW5nKCkpIHtcbiAgICAgICAgICA8aW9uLXNwaW5uZXIgbmFtZT1cImNyZXNjZW50XCI+PC9pb24tc3Bpbm5lcj5cbiAgICAgICAgICB9IEBlbHNlIHsge3sgdCgnbWZhQ29uZmlybUJ1dHRvbicpIH19IH1cbiAgICAgICAgPC9pb24tYnV0dG9uPlxuXG4gICAgICAgIDxwIGNsYXNzPVwibWZhLXJlc2VuZFwiPlxuICAgICAgICAgIHt7IHQoJ21mYU5vQ29kZScpIH19IEBpZiAocmVzZW5kQ29vbGRvd24oKSA+IDApIHtcbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm1mYS10ZXh0XCI+e3sgdCgnbWZhUmVzZW5kSW4nKSB9fSB7eyByZXNlbmRDb29sZG93bigpIH19czwvc3Bhbj5cbiAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICA8YSAoY2xpY2spPVwicmVzZW5kQ29kZSgpXCI+e3sgdCgnbWZhUmVzZW5kJykgfX08L2E+XG4gICAgICAgICAgfVxuICAgICAgICA8L3A+XG5cbiAgICAgICAgPGlvbi1idXR0b24gZXhwYW5kPVwiYmxvY2tcIiBmaWxsPVwiY2xlYXJcIiBjb2xvcj1cImRhcmtcIiAoY2xpY2spPVwiYmFja1RvU3RhdHVzKClcIj5cbiAgICAgICAgICB7eyB0KCdtZmFDYW5jZWwnKSB9fVxuICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgIH0gQGNhc2UgKCdkaXNhYmxlJykge1xuICAgICAgICA8dmFsLWZvcm0gW3Byb3BzXT1cImRpc2FibGVGb3JtUHJvcHMoKVwiIChvblN1Ym1pdCk9XCJvbkRpc2FibGVTdWJtaXQoJGV2ZW50KVwiIC8+XG4gICAgICAgIDxpb24tYnV0dG9uIGV4cGFuZD1cImJsb2NrXCIgZmlsbD1cImNsZWFyXCIgY29sb3I9XCJkYXJrXCIgKGNsaWNrKT1cImJhY2tUb1N0YXR1cygpXCI+XG4gICAgICAgICAge3sgdCgnbWZhQ2FuY2VsJykgfX1cbiAgICAgICAgPC9pb24tYnV0dG9uPlxuICAgICAgICB9IH1cbiAgICAgIDwvc2VjdGlvbj5cbiAgICA8L2lvbi1jb250ZW50PlxuICA8L25nLXRlbXBsYXRlPlxuPC9pb24tbW9kYWw+XG4iXX0=