@idea-ionic/auth 8.4.2 → 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/esm2022/src/auth.routes.mjs +4 -2
- package/esm2022/src/okta.page.mjs +155 -0
- package/esm2022/src/signIn.page.mjs +238 -173
- package/fesm2022/idea-ionic-auth.mjs +390 -175
- package/fesm2022/idea-ionic-auth.mjs.map +1 -1
- package/package.json +1 -1
- package/src/okta.page.d.ts +20 -0
- package/src/signIn.page.d.ts +7 -0
package/README.md
CHANGED
|
@@ -20,4 +20,6 @@ To make sure the translations are loaded into the project:
|
|
|
20
20
|
Make sure that there is a path (tsconfig file) pointing to `@env` which contains the environment files (Angular standard).
|
|
21
21
|
Finally, add in the environments desired the variables you find in the `environment.ts` file of this module.
|
|
22
22
|
|
|
23
|
-
Additionaly, if you want to allow only one simultaneous session per account, [implement what is suggested in this document](https://www.notion.so/iter-idea/
|
|
23
|
+
Additionaly, if you want to allow only one simultaneous session per account, [implement what is suggested in this document](https://www.notion.so/iter-idea/894fbfce395f4bdeb0a85b3a217bc86b?pvs=4).
|
|
24
|
+
|
|
25
|
+
Additionaly, if you want to configure OKTA integration, [follow this guide](https://www.notion.so/iter-idea/18445eea580780349282e1120e7c444f?pvs=4).
|
|
@@ -6,6 +6,7 @@ import { IDEAForgotPasswordPage } from './forgotPassword.page';
|
|
|
6
6
|
import { IDEAConfirmPasswordPage } from './confirmPassword.page';
|
|
7
7
|
import { IDEAMFAChallengePage } from './mfaChallenge.page';
|
|
8
8
|
import { IDEASetupMFAPage } from './setupMFA.page';
|
|
9
|
+
import { IDEAOktaPage } from './okta.page';
|
|
9
10
|
export const routes = [
|
|
10
11
|
{ path: '', redirectTo: 'sign-in', pathMatch: 'full' },
|
|
11
12
|
{ path: 'sign-in', component: IDEASignInPage },
|
|
@@ -15,6 +16,7 @@ export const routes = [
|
|
|
15
16
|
{ path: 'setup-mfa', component: IDEASetupMFAPage },
|
|
16
17
|
{ path: 'resend-link', component: IDEAResendLinkPage },
|
|
17
18
|
{ path: 'forgot-password', component: IDEAForgotPasswordPage },
|
|
18
|
-
{ path: 'confirm-password', component: IDEAConfirmPasswordPage }
|
|
19
|
+
{ path: 'confirm-password', component: IDEAConfirmPasswordPage },
|
|
20
|
+
{ path: 'okta', component: IDEAOktaPage }
|
|
19
21
|
];
|
|
20
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
22
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC5yb3V0ZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9tb2R1bGVzL2F1dGgvc3JjL2F1dGgucm91dGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDL0MsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMvQyxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN6RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN2RCxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvRCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNqRSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRTNDLE1BQU0sQ0FBQyxNQUFNLE1BQU0sR0FBVztJQUM1QixFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFO0lBQ3RELEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsY0FBYyxFQUFFO0lBQzlDLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsY0FBYyxFQUFFO0lBQzlDLEVBQUUsSUFBSSxFQUFFLGNBQWMsRUFBRSxTQUFTLEVBQUUsbUJBQW1CLEVBQUU7SUFDeEQsRUFBRSxJQUFJLEVBQUUsZUFBZSxFQUFFLFNBQVMsRUFBRSxvQkFBb0IsRUFBRTtJQUMxRCxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFO0lBQ2xELEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxTQUFTLEVBQUUsa0JBQWtCLEVBQUU7SUFDdEQsRUFBRSxJQUFJLEVBQUUsaUJBQWlCLEVBQUUsU0FBUyxFQUFFLHNCQUFzQixFQUFFO0lBQzlELEVBQUUsSUFBSSxFQUFFLGtCQUFrQixFQUFFLFNBQVMsRUFBRSx1QkFBdUIsRUFBRTtJQUNoRSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRTtDQUMxQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgUm91dGVzIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcblxuaW1wb3J0IHsgSURFQVNpZ25JblBhZ2UgfSBmcm9tICcuL3NpZ25Jbi5wYWdlJztcbmltcG9ydCB7IElERUFTaWduVXBQYWdlIH0gZnJvbSAnLi9zaWduVXAucGFnZSc7XG5pbXBvcnQgeyBJREVBTmV3UGFzc3dvcmRQYWdlIH0gZnJvbSAnLi9uZXdQYXNzd29yZC5wYWdlJztcbmltcG9ydCB7IElERUFSZXNlbmRMaW5rUGFnZSB9IGZyb20gJy4vcmVzZW5kTGluay5wYWdlJztcbmltcG9ydCB7IElERUFGb3Jnb3RQYXNzd29yZFBhZ2UgfSBmcm9tICcuL2ZvcmdvdFBhc3N3b3JkLnBhZ2UnO1xuaW1wb3J0IHsgSURFQUNvbmZpcm1QYXNzd29yZFBhZ2UgfSBmcm9tICcuL2NvbmZpcm1QYXNzd29yZC5wYWdlJztcbmltcG9ydCB7IElERUFNRkFDaGFsbGVuZ2VQYWdlIH0gZnJvbSAnLi9tZmFDaGFsbGVuZ2UucGFnZSc7XG5pbXBvcnQgeyBJREVBU2V0dXBNRkFQYWdlIH0gZnJvbSAnLi9zZXR1cE1GQS5wYWdlJztcbmltcG9ydCB7IElERUFPa3RhUGFnZSB9IGZyb20gJy4vb2t0YS5wYWdlJztcblxuZXhwb3J0IGNvbnN0IHJvdXRlczogUm91dGVzID0gW1xuICB7IHBhdGg6ICcnLCByZWRpcmVjdFRvOiAnc2lnbi1pbicsIHBhdGhNYXRjaDogJ2Z1bGwnIH0sXG4gIHsgcGF0aDogJ3NpZ24taW4nLCBjb21wb25lbnQ6IElERUFTaWduSW5QYWdlIH0sXG4gIHsgcGF0aDogJ3NpZ24tdXAnLCBjb21wb25lbnQ6IElERUFTaWduVXBQYWdlIH0sXG4gIHsgcGF0aDogJ25ldy1wYXNzd29yZCcsIGNvbXBvbmVudDogSURFQU5ld1Bhc3N3b3JkUGFnZSB9LFxuICB7IHBhdGg6ICdtZmEtY2hhbGxlbmdlJywgY29tcG9uZW50OiBJREVBTUZBQ2hhbGxlbmdlUGFnZSB9LFxuICB7IHBhdGg6ICdzZXR1cC1tZmEnLCBjb21wb25lbnQ6IElERUFTZXR1cE1GQVBhZ2UgfSxcbiAgeyBwYXRoOiAncmVzZW5kLWxpbmsnLCBjb21wb25lbnQ6IElERUFSZXNlbmRMaW5rUGFnZSB9LFxuICB7IHBhdGg6ICdmb3Jnb3QtcGFzc3dvcmQnLCBjb21wb25lbnQ6IElERUFGb3Jnb3RQYXNzd29yZFBhZ2UgfSxcbiAgeyBwYXRoOiAnY29uZmlybS1wYXNzd29yZCcsIGNvbXBvbmVudDogSURFQUNvbmZpcm1QYXNzd29yZFBhZ2UgfSxcbiAgeyBwYXRoOiAnb2t0YScsIGNvbXBvbmVudDogSURFQU9rdGFQYWdlIH1cbl07XG4iXX0=
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { Component, inject } from '@angular/core';
|
|
2
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
3
|
+
import { IonButton, IonCard, IonCardContent, IonCardHeader, IonCardTitle, IonContent, IonImg } from '@ionic/angular/standalone';
|
|
4
|
+
import { IDEAEnvironment, IDEATranslatePipe } from '@idea-ionic/common';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
export class IDEAOktaPage {
|
|
7
|
+
constructor() {
|
|
8
|
+
this._route = inject(ActivatedRoute);
|
|
9
|
+
this._router = inject(Router);
|
|
10
|
+
this._env = inject(IDEAEnvironment);
|
|
11
|
+
this.go = false;
|
|
12
|
+
this.domain = this._env.aws.cognito.domain;
|
|
13
|
+
this.clientId = this._env.aws.cognito.userPoolClientId;
|
|
14
|
+
this.redirectURI = `${window.location.origin}${window.location.pathname}`; // Cognito client must include this callback
|
|
15
|
+
this.scope = 'email openid profile aws.cognito.signin.user.admin'; // Cognito client config must match
|
|
16
|
+
this.title = this._env.idea.auth.title;
|
|
17
|
+
this.darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
18
|
+
}
|
|
19
|
+
ionViewWillEnter() {
|
|
20
|
+
this.identityProvider = this._route.snapshot.queryParamMap.get('provider');
|
|
21
|
+
this.code = this._route.snapshot.queryParamMap.get('code');
|
|
22
|
+
this.go = !!this._route.snapshot.queryParamMap.get('go');
|
|
23
|
+
this._router.navigate([], { queryParams: {}, replaceUrl: true });
|
|
24
|
+
if (this.code)
|
|
25
|
+
this.endLogin();
|
|
26
|
+
else if (this.identityProvider && this.go)
|
|
27
|
+
this.startLogin();
|
|
28
|
+
}
|
|
29
|
+
startLogin() {
|
|
30
|
+
const url = `https://${this.domain}/oauth2/authorize`;
|
|
31
|
+
const params = new URLSearchParams();
|
|
32
|
+
params.append('identity_provider', this.identityProvider);
|
|
33
|
+
params.append('redirect_uri', this.redirectURI);
|
|
34
|
+
params.append('response_type', 'code');
|
|
35
|
+
params.append('client_id', this.clientId);
|
|
36
|
+
params.append('scope', this.scope);
|
|
37
|
+
window.location.assign(`${url}?${params.toString()}`);
|
|
38
|
+
}
|
|
39
|
+
async endLogin() {
|
|
40
|
+
const url = `https://${this.domain}/oauth2/token`;
|
|
41
|
+
const params = new URLSearchParams();
|
|
42
|
+
params.append('code', this.code);
|
|
43
|
+
params.append('grant_type', 'authorization_code');
|
|
44
|
+
params.append('redirect_uri', this.redirectURI);
|
|
45
|
+
params.append('client_id', this.clientId);
|
|
46
|
+
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
|
47
|
+
const response = await fetch(url, { method: 'POST', headers, body: params.toString() });
|
|
48
|
+
if (!response.ok)
|
|
49
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
50
|
+
const { id_token, access_token, refresh_token } = await response.json();
|
|
51
|
+
storeCognitoSession(id_token, access_token, refresh_token);
|
|
52
|
+
window.location.assign('');
|
|
53
|
+
}
|
|
54
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: IDEAOktaPage, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
55
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.9", type: IDEAOktaPage, isStandalone: true, selector: "okta", ngImport: i0, template: `
|
|
56
|
+
@if (!code && !go) {
|
|
57
|
+
<ion-content>
|
|
58
|
+
<div class="flexBox">
|
|
59
|
+
<ion-card class="authCard">
|
|
60
|
+
<ion-img
|
|
61
|
+
[title]="'IDEA_AUTH.SERVICE_LOGO' | translate"
|
|
62
|
+
[src]="darkMode ? 'assets/icons/icon-auth-alt.svg' : 'assets/icons/icon-auth.svg'"
|
|
63
|
+
/>
|
|
64
|
+
@if (title) {
|
|
65
|
+
<ion-card-header>
|
|
66
|
+
<ion-card-title color="primary">{{ title }}</ion-card-title>
|
|
67
|
+
</ion-card-header>
|
|
68
|
+
}
|
|
69
|
+
<ion-card-content>
|
|
70
|
+
@if (identityProvider) {
|
|
71
|
+
<ion-button expand="block" [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate" (click)="startLogin()">
|
|
72
|
+
{{ 'IDEA_AUTH.SIGN_IN' | translate }}
|
|
73
|
+
</ion-button>
|
|
74
|
+
} @else {
|
|
75
|
+
<p testId="signin.error" class="errorBox">
|
|
76
|
+
<b>{{ 'IDEA_AUTH.UNKNOWN_PROVIDER' | translate }}.</b>
|
|
77
|
+
</p>
|
|
78
|
+
}
|
|
79
|
+
</ion-card-content>
|
|
80
|
+
</ion-card>
|
|
81
|
+
</div>
|
|
82
|
+
</ion-content>
|
|
83
|
+
}
|
|
84
|
+
`, isInline: true, dependencies: [{ kind: "pipe", type: IDEATranslatePipe, name: "translate" }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { 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"] }] }); }
|
|
85
|
+
}
|
|
86
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: IDEAOktaPage, decorators: [{
|
|
87
|
+
type: Component,
|
|
88
|
+
args: [{
|
|
89
|
+
selector: 'okta',
|
|
90
|
+
standalone: true,
|
|
91
|
+
imports: [IDEATranslatePipe, IonContent, IonCard, IonImg, IonCardHeader, IonCardTitle, IonCardContent, IonButton],
|
|
92
|
+
template: `
|
|
93
|
+
@if (!code && !go) {
|
|
94
|
+
<ion-content>
|
|
95
|
+
<div class="flexBox">
|
|
96
|
+
<ion-card class="authCard">
|
|
97
|
+
<ion-img
|
|
98
|
+
[title]="'IDEA_AUTH.SERVICE_LOGO' | translate"
|
|
99
|
+
[src]="darkMode ? 'assets/icons/icon-auth-alt.svg' : 'assets/icons/icon-auth.svg'"
|
|
100
|
+
/>
|
|
101
|
+
@if (title) {
|
|
102
|
+
<ion-card-header>
|
|
103
|
+
<ion-card-title color="primary">{{ title }}</ion-card-title>
|
|
104
|
+
</ion-card-header>
|
|
105
|
+
}
|
|
106
|
+
<ion-card-content>
|
|
107
|
+
@if (identityProvider) {
|
|
108
|
+
<ion-button expand="block" [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate" (click)="startLogin()">
|
|
109
|
+
{{ 'IDEA_AUTH.SIGN_IN' | translate }}
|
|
110
|
+
</ion-button>
|
|
111
|
+
} @else {
|
|
112
|
+
<p testId="signin.error" class="errorBox">
|
|
113
|
+
<b>{{ 'IDEA_AUTH.UNKNOWN_PROVIDER' | translate }}.</b>
|
|
114
|
+
</p>
|
|
115
|
+
}
|
|
116
|
+
</ion-card-content>
|
|
117
|
+
</ion-card>
|
|
118
|
+
</div>
|
|
119
|
+
</ion-content>
|
|
120
|
+
}
|
|
121
|
+
`
|
|
122
|
+
}]
|
|
123
|
+
}] });
|
|
124
|
+
//
|
|
125
|
+
// HELPERS
|
|
126
|
+
//
|
|
127
|
+
/**
|
|
128
|
+
* Store the tokens and the info needed by Cognito's front-end lib to authenticate.
|
|
129
|
+
*/
|
|
130
|
+
const storeCognitoSession = (idToken, accessToken, refreshToken) => {
|
|
131
|
+
const { sub } = decodeJWTToken(idToken);
|
|
132
|
+
const { client_id } = decodeJWTToken(accessToken);
|
|
133
|
+
const prefix = `CognitoIdentityServiceProvider.${client_id}`;
|
|
134
|
+
window.localStorage.setItem(`${prefix}.LastAuthUser`, sub);
|
|
135
|
+
window.localStorage.setItem(`${prefix}.${sub}.accessToken`, accessToken);
|
|
136
|
+
window.localStorage.setItem(`${prefix}.${sub}.idToken`, idToken);
|
|
137
|
+
window.localStorage.setItem(`${prefix}.${sub}.refreshToken`, refreshToken);
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Decode a JWT token and return its payload as a JSON object.
|
|
141
|
+
*/
|
|
142
|
+
const decodeJWTToken = (token) => {
|
|
143
|
+
if (!token)
|
|
144
|
+
return null;
|
|
145
|
+
try {
|
|
146
|
+
const base64Url = token.split('.')[1];
|
|
147
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
148
|
+
return JSON.parse(window.atob(base64));
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
console.error('Error decoding token:', error);
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2t0YS5wYWdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vbW9kdWxlcy9hdXRoL3NyYy9va3RhLnBhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6RCxPQUFPLEVBQ0wsU0FBUyxFQUNULE9BQU8sRUFDUCxjQUFjLEVBQ2QsYUFBYSxFQUNiLFlBQVksRUFDWixVQUFVLEVBQ1YsTUFBTSxFQUNQLE1BQU0sMkJBQTJCLENBQUM7QUFDbkMsT0FBTyxFQUFFLGVBQWUsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLG9CQUFvQixDQUFDOztBQXFDeEUsTUFBTSxPQUFPLFlBQVk7SUFuQ3pCO1FBb0NVLFdBQU0sR0FBRyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDaEMsWUFBTyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN6QixTQUFJLEdBQUcsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBSXZDLE9BQUUsR0FBRyxLQUFLLENBQUM7UUFFWCxXQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQztRQUN0QyxhQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDO1FBQ2xELGdCQUFXLEdBQUcsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUMsNENBQTRDO1FBQ2xILFVBQUssR0FBRyxvREFBb0QsQ0FBQyxDQUFDLG1DQUFtQztRQUVqRyxVQUFLLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQztRQUNsQyxhQUFRLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDLE9BQU8sQ0FBQztLQXVDdEU7SUFyQ0MsZ0JBQWdCO1FBQ2QsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDM0UsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzNELElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLEVBQUUsV0FBVyxFQUFFLEVBQUUsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNqRSxJQUFJLElBQUksQ0FBQyxJQUFJO1lBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO2FBQzFCLElBQUksSUFBSSxDQUFDLGdCQUFnQixJQUFJLElBQUksQ0FBQyxFQUFFO1lBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQy9ELENBQUM7SUFFRCxVQUFVO1FBQ1IsTUFBTSxHQUFHLEdBQUcsV0FBVyxJQUFJLENBQUMsTUFBTSxtQkFBbUIsQ0FBQztRQUN0RCxNQUFNLE1BQU0sR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDO1FBQ3JDLE1BQU0sQ0FBQyxNQUFNLENBQUMsbUJBQW1CLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDMUQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ2hELE1BQU0sQ0FBQyxNQUFNLENBQUMsZUFBZSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3ZDLE1BQU0sQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMxQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFbkMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLElBQUksTUFBTSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUN4RCxDQUFDO0lBRU8sS0FBSyxDQUFDLFFBQVE7UUFDcEIsTUFBTSxHQUFHLEdBQUcsV0FBVyxJQUFJLENBQUMsTUFBTSxlQUFlLENBQUM7UUFDbEQsTUFBTSxNQUFNLEdBQUcsSUFBSSxlQUFlLEVBQUUsQ0FBQztRQUNyQyxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDakMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxZQUFZLEVBQUUsb0JBQW9CLENBQUMsQ0FBQztRQUNsRCxNQUFNLENBQUMsTUFBTSxDQUFDLGNBQWMsRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDaEQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzFDLE1BQU0sT0FBTyxHQUFHLEVBQUUsY0FBYyxFQUFFLG1DQUFtQyxFQUFFLENBQUM7UUFFeEUsTUFBTSxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsR0FBRyxFQUFFLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDeEYsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyx1QkFBdUIsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDNUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFFeEUsbUJBQW1CLENBQUMsUUFBUSxFQUFFLFlBQVksRUFBRSxhQUFhLENBQUMsQ0FBQztRQUMzRCxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM3QixDQUFDOzhHQXJEVSxZQUFZO2tHQUFaLFlBQVksZ0VBL0JiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTZCVCx1REE5QlMsaUJBQWlCLGtEQUFFLFVBQVUsd0tBQUUsT0FBTyx5TEFBRSxNQUFNLDRFQUFFLGFBQWEsc0dBQUUsWUFBWSxzRkFBRSxjQUFjLCtFQUFFLFNBQVM7OzJGQWdDckcsWUFBWTtrQkFuQ3hCLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLE1BQU07b0JBQ2hCLFVBQVUsRUFBRSxJQUFJO29CQUNoQixPQUFPLEVBQUUsQ0FBQyxpQkFBaUIsRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUFFLGNBQWMsRUFBRSxTQUFTLENBQUM7b0JBQ2pILFFBQVEsRUFBRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E2QlQ7aUJBQ0Y7O0FBeURELEVBQUU7QUFDRixVQUFVO0FBQ1YsRUFBRTtBQUVGOztHQUVHO0FBQ0gsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLE9BQWUsRUFBRSxXQUFtQixFQUFFLFlBQW9CLEVBQVEsRUFBRTtJQUMvRixNQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3hDLE1BQU0sRUFBRSxTQUFTLEVBQUUsR0FBRyxjQUFjLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDbEQsTUFBTSxNQUFNLEdBQUcsa0NBQWtDLFNBQVMsRUFBRSxDQUFDO0lBQzdELE1BQU0sQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLEdBQUcsTUFBTSxlQUFlLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDM0QsTUFBTSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsR0FBRyxNQUFNLElBQUksR0FBRyxjQUFjLEVBQUUsV0FBVyxDQUFDLENBQUM7SUFDekUsTUFBTSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsR0FBRyxNQUFNLElBQUksR0FBRyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDakUsTUFBTSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsR0FBRyxNQUFNLElBQUksR0FBRyxlQUFlLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDN0UsQ0FBQyxDQUFDO0FBRUY7O0dBRUc7QUFDSCxNQUFNLGNBQWMsR0FBRyxDQUFDLEtBQWEsRUFBTyxFQUFFO0lBQzVDLElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDeEIsSUFBSSxDQUFDO1FBQ0gsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN0QyxNQUFNLE1BQU0sR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQy9ELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixPQUFPLENBQUMsS0FBSyxDQUFDLHVCQUF1QixFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzlDLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztBQUNILENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBBY3RpdmF0ZWRSb3V0ZSwgUm91dGVyIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcbmltcG9ydCB7XG4gIElvbkJ1dHRvbixcbiAgSW9uQ2FyZCxcbiAgSW9uQ2FyZENvbnRlbnQsXG4gIElvbkNhcmRIZWFkZXIsXG4gIElvbkNhcmRUaXRsZSxcbiAgSW9uQ29udGVudCxcbiAgSW9uSW1nXG59IGZyb20gJ0Bpb25pYy9hbmd1bGFyL3N0YW5kYWxvbmUnO1xuaW1wb3J0IHsgSURFQUVudmlyb25tZW50LCBJREVBVHJhbnNsYXRlUGlwZSB9IGZyb20gJ0BpZGVhLWlvbmljL2NvbW1vbic7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ29rdGEnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbSURFQVRyYW5zbGF0ZVBpcGUsIElvbkNvbnRlbnQsIElvbkNhcmQsIElvbkltZywgSW9uQ2FyZEhlYWRlciwgSW9uQ2FyZFRpdGxlLCBJb25DYXJkQ29udGVudCwgSW9uQnV0dG9uXSxcbiAgdGVtcGxhdGU6IGBcbiAgICBAaWYgKCFjb2RlICYmICFnbykge1xuICAgICAgPGlvbi1jb250ZW50PlxuICAgICAgICA8ZGl2IGNsYXNzPVwiZmxleEJveFwiPlxuICAgICAgICAgIDxpb24tY2FyZCBjbGFzcz1cImF1dGhDYXJkXCI+XG4gICAgICAgICAgICA8aW9uLWltZ1xuICAgICAgICAgICAgICBbdGl0bGVdPVwiJ0lERUFfQVVUSC5TRVJWSUNFX0xPR08nIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgICAgICAgW3NyY109XCJkYXJrTW9kZSA/ICdhc3NldHMvaWNvbnMvaWNvbi1hdXRoLWFsdC5zdmcnIDogJ2Fzc2V0cy9pY29ucy9pY29uLWF1dGguc3ZnJ1wiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgICAgQGlmICh0aXRsZSkge1xuICAgICAgICAgICAgICA8aW9uLWNhcmQtaGVhZGVyPlxuICAgICAgICAgICAgICAgIDxpb24tY2FyZC10aXRsZSBjb2xvcj1cInByaW1hcnlcIj57eyB0aXRsZSB9fTwvaW9uLWNhcmQtdGl0bGU+XG4gICAgICAgICAgICAgIDwvaW9uLWNhcmQtaGVhZGVyPlxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgPGlvbi1jYXJkLWNvbnRlbnQ+XG4gICAgICAgICAgICAgIEBpZiAoaWRlbnRpdHlQcm92aWRlcikge1xuICAgICAgICAgICAgICAgIDxpb24tYnV0dG9uIGV4cGFuZD1cImJsb2NrXCIgW3RpdGxlXT1cIidJREVBX0FVVEguU0lHTl9JTl9ISU5UJyB8IHRyYW5zbGF0ZVwiIChjbGljayk9XCJzdGFydExvZ2luKClcIj5cbiAgICAgICAgICAgICAgICAgIHt7ICdJREVBX0FVVEguU0lHTl9JTicgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgIDxwIHRlc3RJZD1cInNpZ25pbi5lcnJvclwiIGNsYXNzPVwiZXJyb3JCb3hcIj5cbiAgICAgICAgICAgICAgICAgIDxiPnt7ICdJREVBX0FVVEguVU5LTk9XTl9QUk9WSURFUicgfCB0cmFuc2xhdGUgfX0uPC9iPlxuICAgICAgICAgICAgICAgIDwvcD5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgPC9pb24tY2FyZC1jb250ZW50PlxuICAgICAgICAgIDwvaW9uLWNhcmQ+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9pb24tY29udGVudD5cbiAgICB9XG4gIGBcbn0pXG5leHBvcnQgY2xhc3MgSURFQU9rdGFQYWdlIHtcbiAgcHJpdmF0ZSBfcm91dGUgPSBpbmplY3QoQWN0aXZhdGVkUm91dGUpO1xuICBwcml2YXRlIF9yb3V0ZXIgPSBpbmplY3QoUm91dGVyKTtcbiAgcHJpdmF0ZSBfZW52ID0gaW5qZWN0KElERUFFbnZpcm9ubWVudCk7XG5cbiAgaWRlbnRpdHlQcm92aWRlcjogc3RyaW5nO1xuICBjb2RlOiBzdHJpbmc7XG4gIGdvID0gZmFsc2U7XG5cbiAgZG9tYWluID0gdGhpcy5fZW52LmF3cy5jb2duaXRvLmRvbWFpbjtcbiAgY2xpZW50SWQgPSB0aGlzLl9lbnYuYXdzLmNvZ25pdG8udXNlclBvb2xDbGllbnRJZDtcbiAgcmVkaXJlY3RVUkkgPSBgJHt3aW5kb3cubG9jYXRpb24ub3JpZ2lufSR7d2luZG93LmxvY2F0aW9uLnBhdGhuYW1lfWA7IC8vIENvZ25pdG8gY2xpZW50IG11c3QgaW5jbHVkZSB0aGlzIGNhbGxiYWNrXG4gIHNjb3BlID0gJ2VtYWlsIG9wZW5pZCBwcm9maWxlIGF3cy5jb2duaXRvLnNpZ25pbi51c2VyLmFkbWluJzsgLy8gQ29nbml0byBjbGllbnQgY29uZmlnIG11c3QgbWF0Y2hcblxuICB0aXRsZSA9IHRoaXMuX2Vudi5pZGVhLmF1dGgudGl0bGU7XG4gIGRhcmtNb2RlID0gd2luZG93Lm1hdGNoTWVkaWEoJyhwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyayknKS5tYXRjaGVzO1xuXG4gIGlvblZpZXdXaWxsRW50ZXIoKTogdm9pZCB7XG4gICAgdGhpcy5pZGVudGl0eVByb3ZpZGVyID0gdGhpcy5fcm91dGUuc25hcHNob3QucXVlcnlQYXJhbU1hcC5nZXQoJ3Byb3ZpZGVyJyk7XG4gICAgdGhpcy5jb2RlID0gdGhpcy5fcm91dGUuc25hcHNob3QucXVlcnlQYXJhbU1hcC5nZXQoJ2NvZGUnKTtcbiAgICB0aGlzLmdvID0gISF0aGlzLl9yb3V0ZS5zbmFwc2hvdC5xdWVyeVBhcmFtTWFwLmdldCgnZ28nKTtcbiAgICB0aGlzLl9yb3V0ZXIubmF2aWdhdGUoW10sIHsgcXVlcnlQYXJhbXM6IHt9LCByZXBsYWNlVXJsOiB0cnVlIH0pO1xuICAgIGlmICh0aGlzLmNvZGUpIHRoaXMuZW5kTG9naW4oKTtcbiAgICBlbHNlIGlmICh0aGlzLmlkZW50aXR5UHJvdmlkZXIgJiYgdGhpcy5nbykgdGhpcy5zdGFydExvZ2luKCk7XG4gIH1cblxuICBzdGFydExvZ2luKCk6IHZvaWQge1xuICAgIGNvbnN0IHVybCA9IGBodHRwczovLyR7dGhpcy5kb21haW59L29hdXRoMi9hdXRob3JpemVgO1xuICAgIGNvbnN0IHBhcmFtcyA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoKTtcbiAgICBwYXJhbXMuYXBwZW5kKCdpZGVudGl0eV9wcm92aWRlcicsIHRoaXMuaWRlbnRpdHlQcm92aWRlcik7XG4gICAgcGFyYW1zLmFwcGVuZCgncmVkaXJlY3RfdXJpJywgdGhpcy5yZWRpcmVjdFVSSSk7XG4gICAgcGFyYW1zLmFwcGVuZCgncmVzcG9uc2VfdHlwZScsICdjb2RlJyk7XG4gICAgcGFyYW1zLmFwcGVuZCgnY2xpZW50X2lkJywgdGhpcy5jbGllbnRJZCk7XG4gICAgcGFyYW1zLmFwcGVuZCgnc2NvcGUnLCB0aGlzLnNjb3BlKTtcblxuICAgIHdpbmRvdy5sb2NhdGlvbi5hc3NpZ24oYCR7dXJsfT8ke3BhcmFtcy50b1N0cmluZygpfWApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBlbmRMb2dpbigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB1cmwgPSBgaHR0cHM6Ly8ke3RoaXMuZG9tYWlufS9vYXV0aDIvdG9rZW5gO1xuICAgIGNvbnN0IHBhcmFtcyA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoKTtcbiAgICBwYXJhbXMuYXBwZW5kKCdjb2RlJywgdGhpcy5jb2RlKTtcbiAgICBwYXJhbXMuYXBwZW5kKCdncmFudF90eXBlJywgJ2F1dGhvcml6YXRpb25fY29kZScpO1xuICAgIHBhcmFtcy5hcHBlbmQoJ3JlZGlyZWN0X3VyaScsIHRoaXMucmVkaXJlY3RVUkkpO1xuICAgIHBhcmFtcy5hcHBlbmQoJ2NsaWVudF9pZCcsIHRoaXMuY2xpZW50SWQpO1xuICAgIGNvbnN0IGhlYWRlcnMgPSB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJyB9O1xuXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh1cmwsIHsgbWV0aG9kOiAnUE9TVCcsIGhlYWRlcnMsIGJvZHk6IHBhcmFtcy50b1N0cmluZygpIH0pO1xuICAgIGlmICghcmVzcG9uc2Uub2spIHRocm93IG5ldyBFcnJvcihgSFRUUCBlcnJvciEgU3RhdHVzOiAke3Jlc3BvbnNlLnN0YXR1c31gKTtcbiAgICBjb25zdCB7IGlkX3Rva2VuLCBhY2Nlc3NfdG9rZW4sIHJlZnJlc2hfdG9rZW4gfSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTtcblxuICAgIHN0b3JlQ29nbml0b1Nlc3Npb24oaWRfdG9rZW4sIGFjY2Vzc190b2tlbiwgcmVmcmVzaF90b2tlbik7XG4gICAgd2luZG93LmxvY2F0aW9uLmFzc2lnbignJyk7XG4gIH1cbn1cblxuLy9cbi8vIEhFTFBFUlNcbi8vXG5cbi8qKlxuICogU3RvcmUgdGhlIHRva2VucyBhbmQgdGhlIGluZm8gbmVlZGVkIGJ5IENvZ25pdG8ncyBmcm9udC1lbmQgbGliIHRvIGF1dGhlbnRpY2F0ZS5cbiAqL1xuY29uc3Qgc3RvcmVDb2duaXRvU2Vzc2lvbiA9IChpZFRva2VuOiBzdHJpbmcsIGFjY2Vzc1Rva2VuOiBzdHJpbmcsIHJlZnJlc2hUb2tlbjogc3RyaW5nKTogdm9pZCA9PiB7XG4gIGNvbnN0IHsgc3ViIH0gPSBkZWNvZGVKV1RUb2tlbihpZFRva2VuKTtcbiAgY29uc3QgeyBjbGllbnRfaWQgfSA9IGRlY29kZUpXVFRva2VuKGFjY2Vzc1Rva2VuKTtcbiAgY29uc3QgcHJlZml4ID0gYENvZ25pdG9JZGVudGl0eVNlcnZpY2VQcm92aWRlci4ke2NsaWVudF9pZH1gO1xuICB3aW5kb3cubG9jYWxTdG9yYWdlLnNldEl0ZW0oYCR7cHJlZml4fS5MYXN0QXV0aFVzZXJgLCBzdWIpO1xuICB3aW5kb3cubG9jYWxTdG9yYWdlLnNldEl0ZW0oYCR7cHJlZml4fS4ke3N1Yn0uYWNjZXNzVG9rZW5gLCBhY2Nlc3NUb2tlbik7XG4gIHdpbmRvdy5sb2NhbFN0b3JhZ2Uuc2V0SXRlbShgJHtwcmVmaXh9LiR7c3VifS5pZFRva2VuYCwgaWRUb2tlbik7XG4gIHdpbmRvdy5sb2NhbFN0b3JhZ2Uuc2V0SXRlbShgJHtwcmVmaXh9LiR7c3VifS5yZWZyZXNoVG9rZW5gLCByZWZyZXNoVG9rZW4pO1xufTtcblxuLyoqXG4gKiBEZWNvZGUgYSBKV1QgdG9rZW4gYW5kIHJldHVybiBpdHMgcGF5bG9hZCBhcyBhIEpTT04gb2JqZWN0LlxuICovXG5jb25zdCBkZWNvZGVKV1RUb2tlbiA9ICh0b2tlbjogc3RyaW5nKTogYW55ID0+IHtcbiAgaWYgKCF0b2tlbikgcmV0dXJuIG51bGw7XG4gIHRyeSB7XG4gICAgY29uc3QgYmFzZTY0VXJsID0gdG9rZW4uc3BsaXQoJy4nKVsxXTtcbiAgICBjb25zdCBiYXNlNjQgPSBiYXNlNjRVcmwucmVwbGFjZSgvLS9nLCAnKycpLnJlcGxhY2UoL18vZywgJy8nKTtcbiAgICByZXR1cm4gSlNPTi5wYXJzZSh3aW5kb3cuYXRvYihiYXNlNjQpKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zb2xlLmVycm9yKCdFcnJvciBkZWNvZGluZyB0b2tlbjonLCBlcnJvcik7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn07XG4iXX0=
|