@atlasauth/angular 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/esm2022/lib/appearance.mjs +1 -1
  3. package/dist/esm2022/lib/atlas.service.mjs +45 -4
  4. package/dist/esm2022/lib/atlas.ui.mjs +3 -1
  5. package/dist/esm2022/lib/components/attempt-flow.component.mjs +398 -8
  6. package/dist/esm2022/lib/components/flow-controller.mjs +127 -7
  7. package/dist/esm2022/lib/components/impersonation-banner.component.mjs +75 -0
  8. package/dist/esm2022/lib/components/organization-audit-log.component.mjs +115 -0
  9. package/dist/esm2022/lib/components/organization-audit-log.mjs +40 -0
  10. package/dist/esm2022/lib/components/organization-profile.component.mjs +602 -27
  11. package/dist/esm2022/lib/components/recovery-codes.component.mjs +122 -0
  12. package/dist/esm2022/lib/components/sign-in.component.mjs +85 -4
  13. package/dist/esm2022/lib/components/sign-up-form.component.mjs +69 -6
  14. package/dist/esm2022/lib/components/sign-up.component.mjs +83 -7
  15. package/dist/esm2022/lib/i18n.mjs +60 -1
  16. package/dist/esm2022/lib/provide-atlas.mjs +3 -2
  17. package/dist/esm2022/lib/types.mjs +1 -1
  18. package/dist/esm2022/lib/version.mjs +2 -2
  19. package/dist/esm2022/public-api.mjs +4 -1
  20. package/dist/fesm2022/atlasauth-angular.mjs +1798 -65
  21. package/dist/fesm2022/atlasauth-angular.mjs.map +1 -1
  22. package/dist/lib/appearance.d.ts +1 -1
  23. package/dist/lib/atlas.service.d.ts +24 -2
  24. package/dist/lib/atlas.ui.d.ts +2 -1
  25. package/dist/lib/components/attempt-flow.component.d.ts +60 -3
  26. package/dist/lib/components/flow-controller.d.ts +65 -2
  27. package/dist/lib/components/impersonation-banner.component.d.ts +27 -0
  28. package/dist/lib/components/organization-audit-log.component.d.ts +34 -0
  29. package/dist/lib/components/organization-audit-log.d.ts +25 -0
  30. package/dist/lib/components/organization-profile.component.d.ts +55 -6
  31. package/dist/lib/components/recovery-codes.component.d.ts +26 -0
  32. package/dist/lib/components/sign-in.component.d.ts +11 -1
  33. package/dist/lib/components/sign-up-form.component.d.ts +11 -1
  34. package/dist/lib/components/sign-up.component.d.ts +13 -0
  35. package/dist/lib/provide-atlas.d.ts +2 -1
  36. package/dist/lib/types.d.ts +6 -0
  37. package/dist/lib/version.d.ts +1 -1
  38. package/dist/public-api.d.ts +4 -0
  39. package/package.json +3 -3
@@ -1,36 +1,396 @@
1
- import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
1
+ import { ChangeDetectionStrategy, Component, DestroyRef, EventEmitter, Output, computed, effect, inject, signal, untracked, } from '@angular/core';
2
+ import { OrganizationProfileController, localizedErrorMessage, } from '@atlasauth/js';
2
3
  import { AtlasService } from '../atlas.service';
3
- import { AtlasProtectDirective } from '../directives/protect.directive';
4
4
  import { classFor } from '../appearance';
5
5
  import { translate } from '../i18n';
6
6
  import * as i0 from "@angular/core";
7
7
  /**
8
- * `<atlas-organization-profile>` — the active organization's surface. The
9
- * Angular peer of React's `<OrganizationProfile/>`. Management controls are
10
- * gated on the ACTIVE org role via `*atlasProtect`, matching the server's rule
11
- * rather than duplicating a looser one. Renders nothing with no active org.
8
+ * `<atlas-organization-profile>` — the active organization's members (admins
9
+ * change roles and remove), invitations (admins invite, revoke and resend) and
10
+ * settings (leave; an admin deletes after confirming their password). The
11
+ * Angular peer of React's `<OrganizationProfile/>`, on the same headless
12
+ * controller. Every control is gated on the ACTIVE org role, matching the
13
+ * server's rule. Renders nothing with no active org. Emits `exit` with `left`
14
+ * or `deleted`.
12
15
  */
13
16
  export class OrganizationProfileComponent {
14
17
  atlas = inject(AtlasService);
15
- t(key) {
16
- return translate(this.atlas.localization, key);
18
+ exit = new EventEmitter();
19
+ client = this.atlas.createFapiClient();
20
+ controller = null;
21
+ unsubscribe = null;
22
+ state = signal(null);
23
+ tab = signal('members');
24
+ email = signal('');
25
+ inviteRole = signal('org:member');
26
+ confirm = signal(null);
27
+ needsPassword = signal(false);
28
+ password = signal('');
29
+ // The custom-role editor: null = creating, else the id of the role being edited.
30
+ editingRole = signal(null);
31
+ roleKey = signal('');
32
+ roleName = signal('');
33
+ rolePerms = signal([]);
34
+ customRoles = computed(() => (this.state()?.roles ?? []).filter((r) => Boolean(r.organization_id)));
35
+ userId = computed(() => this.atlas.claims()?.sub ?? null);
36
+ canManage = computed(() => {
37
+ this.atlas.claims();
38
+ return this.atlas.has({ permission: 'org:sys_memberships:manage' });
39
+ });
40
+ isAdmin = computed(() => this.atlas.organization()?.role === 'org:admin');
41
+ tabs = computed(() => ['members', ...(this.canManage() ? ['invitations', 'roles'] : []), 'settings']);
42
+ roles = computed(() => {
43
+ const roles = this.state()?.roles ?? [];
44
+ return roles.length > 0 ? roles : [{ key: 'org:member', name: 'org:member' }];
45
+ });
46
+ errorMessage = computed(() => {
47
+ const err = this.state()?.error;
48
+ if (!err || err.needsReverification)
49
+ return null;
50
+ return localizedErrorMessage(err, this.atlas.errorLocalization, this.atlas.localization);
51
+ });
52
+ constructor() {
53
+ effect(() => {
54
+ const id = this.atlas.organization()?.organization.id ?? null;
55
+ untracked(() => this.bind(id));
56
+ });
57
+ inject(DestroyRef).onDestroy(() => this.unsubscribe?.());
58
+ }
59
+ bind(id) {
60
+ if (this.controller?.organizationId === id)
61
+ return;
62
+ this.unsubscribe?.();
63
+ this.controller = null;
64
+ this.state.set(null);
65
+ if (!id)
66
+ return;
67
+ const controller = new OrganizationProfileController(this.client, id, {
68
+ canManage: this.canManage(),
69
+ onChange: async () => {
70
+ await this.atlas.reload();
71
+ },
72
+ });
73
+ this.controller = controller;
74
+ this.unsubscribe = controller.subscribe((next) => this.state.set(next));
75
+ this.state.set(controller.getState());
76
+ void controller.load();
77
+ }
78
+ t(key, vars) {
79
+ return translate(this.atlas.localization, key, vars);
17
80
  }
18
81
  cls(key, base) {
19
82
  return classFor(key, this.atlas.appearance, base);
20
83
  }
84
+ memberName(m) {
85
+ return [m.user?.first_name, m.user?.last_name].filter(Boolean).join(' ') || this.t('organization.unnamedMember');
86
+ }
87
+ changeRole(userId, role) {
88
+ void this.controller?.changeRole(userId, role);
89
+ }
90
+ removeMember(userId) {
91
+ void this.controller?.removeMember(userId);
92
+ }
93
+ async sendInvite() {
94
+ const address = this.email().trim();
95
+ if (this.controller && address && (await this.controller.invite(address, this.inviteRole())))
96
+ this.email.set('');
97
+ }
98
+ resend(id) {
99
+ void this.controller?.resendInvitation(id);
100
+ }
101
+ revoke(id) {
102
+ void this.controller?.revokeInvitation(id);
103
+ }
104
+ async leave() {
105
+ if (this.controller && (await this.controller.leave()))
106
+ this.exit.emit('left');
107
+ this.confirm.set(null);
108
+ }
109
+ async remove() {
110
+ const controller = this.controller;
111
+ if (!controller)
112
+ return;
113
+ if (this.needsPassword()) {
114
+ try {
115
+ await this.atlas.reauthenticate(this.password());
116
+ }
117
+ catch {
118
+ return;
119
+ }
120
+ this.password.set('');
121
+ this.needsPassword.set(false);
122
+ }
123
+ if (await controller.deleteOrganization()) {
124
+ this.exit.emit('deleted');
125
+ this.confirm.set(null);
126
+ return;
127
+ }
128
+ const err = controller.getState().error;
129
+ if (err?.needsReverification)
130
+ this.needsPassword.set(true);
131
+ }
132
+ editRole(role) {
133
+ this.editingRole.set(role.id ?? null);
134
+ this.roleKey.set(role.key);
135
+ this.roleName.set(role.name);
136
+ this.rolePerms.set([...(role.permissions ?? [])]);
137
+ }
138
+ resetRoleForm() {
139
+ this.editingRole.set(null);
140
+ this.roleKey.set('');
141
+ this.roleName.set('');
142
+ this.rolePerms.set([]);
143
+ }
144
+ togglePermission(key, on) {
145
+ this.rolePerms.update((current) => (on ? [...current, key] : current.filter((k) => k !== key)));
146
+ }
147
+ async saveRole() {
148
+ const controller = this.controller;
149
+ if (!controller)
150
+ return;
151
+ const editing = this.editingRole();
152
+ const out = editing
153
+ ? await controller.updateRole(editing, { name: this.roleName().trim(), permissions: this.rolePerms() })
154
+ : await controller.createRole({ key: this.roleKey().trim(), name: this.roleName().trim(), permissions: this.rolePerms() });
155
+ if (out)
156
+ this.resetRoleForm();
157
+ }
158
+ deleteRole(role) {
159
+ if (role.id)
160
+ void this.controller?.deleteRole(role.id);
161
+ }
162
+ cancel() {
163
+ this.confirm.set(null);
164
+ this.needsPassword.set(false);
165
+ this.password.set('');
166
+ }
21
167
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OrganizationProfileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
22
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: OrganizationProfileComponent, isStandalone: true, selector: "atlas-organization-profile", ngImport: i0, template: `
168
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: OrganizationProfileComponent, isStandalone: true, selector: "atlas-organization-profile", outputs: { exit: "exit" }, ngImport: i0, template: `
23
169
  @if (atlas.organization(); as membership) {
24
- <div [class]="cls('card', 'atlas-card')">
25
- <h1>{{ membership.organization.name }}</h1>
26
- <p>{{ t('organization.roleLabel') }}: {{ membership.role }}</p>
170
+ @if (state(); as st) {
171
+ <div [class]="cls('card', 'atlas-card')" data-atlas="organization-profile">
172
+ <h1>{{ membership.organization.name }}</h1>
173
+ <p>{{ t('organization.roleLabel') }}: {{ membership.role }}</p>
174
+
175
+ <div [class]="cls('menu', 'atlas-menu')" role="tablist">
176
+ @for (key of tabs(); track key) {
177
+ <button
178
+ type="button"
179
+ role="tab"
180
+ [attr.aria-selected]="tab() === key"
181
+ [class]="cls('menuItem', 'atlas-menu-item')"
182
+ [attr.data-atlas]="'organization-tab-' + key"
183
+ (click)="tab.set(key)"
184
+ >
185
+ {{ t('organization.' + key) }}
186
+ </button>
187
+ }
188
+ </div>
189
+
190
+ @if (errorMessage(); as message) {
191
+ <p [class]="cls('formFieldError', 'atlas-error')" role="alert">{{ message }}</p>
192
+ }
193
+
194
+ @if (tab() === 'members') {
195
+ <ul class="atlas-list" data-atlas="organization-members">
196
+ @for (m of st.members; track m.id) {
197
+ <li data-atlas="organization-member">
198
+ <span>{{ memberName(m) }}{{ m.user?.id === userId() ? ' ' + t('organization.you') : '' }}</span>
199
+ @if (canManage() && m.user && m.user.id !== userId()) {
200
+ <select
201
+ [attr.aria-label]="t('organization.roleLabel')"
202
+ [class]="cls('formFieldInput', 'atlas-input')"
203
+ [disabled]="st.busy === 'role:' + m.user.id"
204
+ (change)="changeRole(m.user.id, $any($event.target).value)"
205
+ >
206
+ @for (r of roles(); track r.key) {
207
+ <option [value]="r.key" [selected]="r.key === m.role">{{ r.name }}</option>
208
+ }
209
+ </select>
210
+ <button
211
+ type="button"
212
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
213
+ [disabled]="st.busy === 'remove:' + m.user.id"
214
+ (click)="removeMember(m.user.id)"
215
+ >
216
+ {{ t('organization.remove') }}
217
+ </button>
218
+ } @else {
219
+ <span> · {{ m.role }}</span>
220
+ }
221
+ </li>
222
+ }
223
+ </ul>
224
+ }
225
+
226
+ @if (tab() === 'invitations' && canManage()) {
227
+ <div data-atlas="organization-invitations">
228
+ <form (submit)="$event.preventDefault(); sendInvite()">
229
+ <label [class]="cls('formField', 'atlas-field')">
230
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.emailLabel') }}</span>
231
+ <input
232
+ type="email"
233
+ [class]="cls('formFieldInput', 'atlas-input')"
234
+ [value]="email()"
235
+ (input)="email.set($any($event.target).value)"
236
+ />
237
+ </label>
238
+ <label [class]="cls('formField', 'atlas-field')">
239
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleLabel') }}</span>
240
+ <select [class]="cls('formFieldInput', 'atlas-input')" (change)="inviteRole.set($any($event.target).value)">
241
+ @for (r of roles(); track r.key) {
242
+ <option [value]="r.key" [selected]="r.key === inviteRole()">{{ r.name }}</option>
243
+ }
244
+ </select>
245
+ </label>
246
+ <button
247
+ type="submit"
248
+ [class]="cls('formButtonPrimary', 'atlas-button')"
249
+ [disabled]="st.busy === 'invite' || !email().trim()"
250
+ >
251
+ {{ t('organization.sendInvite') }}
252
+ </button>
253
+ </form>
254
+ @if (st.invitations.length === 0) {
255
+ <p class="atlas-text">{{ t('organization.noInvitations') }}</p>
256
+ } @else {
257
+ <ul class="atlas-list">
258
+ @for (inv of st.invitations; track inv.id) {
259
+ <li data-atlas="organization-invitation">
260
+ <span>{{ inv.email_address }} · {{ inv.role }}</span>
261
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="resend(inv.id)">
262
+ {{ t('organization.resend') }}
263
+ </button>
264
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="revoke(inv.id)">
265
+ {{ t('organization.revoke') }}
266
+ </button>
267
+ </li>
268
+ }
269
+ </ul>
270
+ }
271
+ </div>
272
+ }
27
273
 
28
- <button *atlasProtect="{ permission: 'org:sys_memberships:manage' }">
29
- {{ t('organization.invite') }}
30
- </button>
31
- </div>
274
+ @if (tab() === 'roles' && canManage()) {
275
+ <div data-atlas="organization-roles">
276
+ @if (customRoles().length === 0) {
277
+ <p class="atlas-text">{{ t('organization.noCustomRoles') }}</p>
278
+ } @else {
279
+ <ul class="atlas-list">
280
+ @for (role of customRoles(); track role.key) {
281
+ <li data-atlas="organization-role">
282
+ <span>{{ role.name }} · {{ role.key }}</span>
283
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="editRole(role)">
284
+ {{ t('organization.editRole') }}
285
+ </button>
286
+ <button
287
+ type="button"
288
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
289
+ [disabled]="!role.id || st.busy === 'role:delete:' + role.id"
290
+ (click)="deleteRole(role)"
291
+ >
292
+ {{ t('organization.deleteRole') }}
293
+ </button>
294
+ </li>
295
+ }
296
+ </ul>
297
+ }
298
+ <form data-atlas="organization-role-form" (submit)="$event.preventDefault(); saveRole()">
299
+ @if (!editingRole()) {
300
+ <label [class]="cls('formField', 'atlas-field')">
301
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleKey') }}</span>
302
+ <input
303
+ name="role_key"
304
+ [class]="cls('formFieldInput', 'atlas-input')"
305
+ [value]="roleKey()"
306
+ (input)="roleKey.set($any($event.target).value)"
307
+ />
308
+ </label>
309
+ }
310
+ <label [class]="cls('formField', 'atlas-field')">
311
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleName') }}</span>
312
+ <input
313
+ name="role_name"
314
+ [class]="cls('formFieldInput', 'atlas-input')"
315
+ [value]="roleName()"
316
+ (input)="roleName.set($any($event.target).value)"
317
+ />
318
+ </label>
319
+ <fieldset [class]="cls('formField', 'atlas-field')">
320
+ <legend [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.rolePermissions') }}</legend>
321
+ @for (p of st.permissions; track p.key) {
322
+ <label data-atlas="organization-role-permission">
323
+ <input
324
+ type="checkbox"
325
+ [value]="p.key"
326
+ [checked]="rolePerms().includes(p.key)"
327
+ (change)="togglePermission(p.key, $any($event.target).checked)"
328
+ />
329
+ {{ p.name }}
330
+ </label>
331
+ }
332
+ </fieldset>
333
+ <button
334
+ type="submit"
335
+ [class]="cls('formButtonPrimary', 'atlas-button')"
336
+ [disabled]="!roleName().trim() || (!editingRole() && !roleKey().trim()) || (st.busy ?? '').startsWith('role:')"
337
+ >
338
+ {{ editingRole() ? t('organization.saveRole') : t('organization.createRole') }}
339
+ </button>
340
+ @if (editingRole()) {
341
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="resetRoleForm()">
342
+ {{ t('organization.cancel') }}
343
+ </button>
344
+ }
345
+ </form>
346
+ </div>
347
+ }
348
+
349
+ @if (tab() === 'settings') {
350
+ <div data-atlas="organization-settings">
351
+ @if (confirm(); as action) {
352
+ <p class="atlas-text">
353
+ {{ t(action === 'leave' ? 'organization.leaveConfirm' : 'organization.deleteConfirm', { name: membership.organization.name }) }}
354
+ </p>
355
+ @if (needsPassword()) {
356
+ <label [class]="cls('formField', 'atlas-field')">
357
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.passwordPrompt') }}</span>
358
+ <input
359
+ type="password"
360
+ autocomplete="current-password"
361
+ [class]="cls('formFieldInput', 'atlas-input')"
362
+ [value]="password()"
363
+ (input)="password.set($any($event.target).value)"
364
+ />
365
+ </label>
366
+ }
367
+ <button
368
+ type="button"
369
+ [class]="cls('formButtonPrimary', 'atlas-button')"
370
+ [disabled]="st.busy === 'leave' || st.busy === 'delete'"
371
+ (click)="action === 'leave' ? leave() : remove()"
372
+ >
373
+ {{ t('organization.confirm') }}
374
+ </button>
375
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="cancel()">
376
+ {{ t('organization.cancel') }}
377
+ </button>
378
+ } @else {
379
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="confirm.set('leave')">
380
+ {{ t('organization.leave') }}
381
+ </button>
382
+ @if (isAdmin()) {
383
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="confirm.set('delete')">
384
+ {{ t('organization.delete') }}
385
+ </button>
386
+ }
387
+ }
388
+ </div>
389
+ }
390
+ </div>
391
+ }
32
392
  }
33
- `, isInline: true, dependencies: [{ kind: "directive", type: AtlasProtectDirective, selector: "[atlasProtect]", inputs: ["atlasProtect", "atlasProtectElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
393
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
34
394
  }
35
395
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OrganizationProfileComponent, decorators: [{
36
396
  type: Component,
@@ -38,19 +398,234 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
38
398
  selector: 'atlas-organization-profile',
39
399
  standalone: true,
40
400
  changeDetection: ChangeDetectionStrategy.OnPush,
41
- imports: [AtlasProtectDirective],
42
401
  template: `
43
402
  @if (atlas.organization(); as membership) {
44
- <div [class]="cls('card', 'atlas-card')">
45
- <h1>{{ membership.organization.name }}</h1>
46
- <p>{{ t('organization.roleLabel') }}: {{ membership.role }}</p>
403
+ @if (state(); as st) {
404
+ <div [class]="cls('card', 'atlas-card')" data-atlas="organization-profile">
405
+ <h1>{{ membership.organization.name }}</h1>
406
+ <p>{{ t('organization.roleLabel') }}: {{ membership.role }}</p>
407
+
408
+ <div [class]="cls('menu', 'atlas-menu')" role="tablist">
409
+ @for (key of tabs(); track key) {
410
+ <button
411
+ type="button"
412
+ role="tab"
413
+ [attr.aria-selected]="tab() === key"
414
+ [class]="cls('menuItem', 'atlas-menu-item')"
415
+ [attr.data-atlas]="'organization-tab-' + key"
416
+ (click)="tab.set(key)"
417
+ >
418
+ {{ t('organization.' + key) }}
419
+ </button>
420
+ }
421
+ </div>
422
+
423
+ @if (errorMessage(); as message) {
424
+ <p [class]="cls('formFieldError', 'atlas-error')" role="alert">{{ message }}</p>
425
+ }
426
+
427
+ @if (tab() === 'members') {
428
+ <ul class="atlas-list" data-atlas="organization-members">
429
+ @for (m of st.members; track m.id) {
430
+ <li data-atlas="organization-member">
431
+ <span>{{ memberName(m) }}{{ m.user?.id === userId() ? ' ' + t('organization.you') : '' }}</span>
432
+ @if (canManage() && m.user && m.user.id !== userId()) {
433
+ <select
434
+ [attr.aria-label]="t('organization.roleLabel')"
435
+ [class]="cls('formFieldInput', 'atlas-input')"
436
+ [disabled]="st.busy === 'role:' + m.user.id"
437
+ (change)="changeRole(m.user.id, $any($event.target).value)"
438
+ >
439
+ @for (r of roles(); track r.key) {
440
+ <option [value]="r.key" [selected]="r.key === m.role">{{ r.name }}</option>
441
+ }
442
+ </select>
443
+ <button
444
+ type="button"
445
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
446
+ [disabled]="st.busy === 'remove:' + m.user.id"
447
+ (click)="removeMember(m.user.id)"
448
+ >
449
+ {{ t('organization.remove') }}
450
+ </button>
451
+ } @else {
452
+ <span> · {{ m.role }}</span>
453
+ }
454
+ </li>
455
+ }
456
+ </ul>
457
+ }
458
+
459
+ @if (tab() === 'invitations' && canManage()) {
460
+ <div data-atlas="organization-invitations">
461
+ <form (submit)="$event.preventDefault(); sendInvite()">
462
+ <label [class]="cls('formField', 'atlas-field')">
463
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.emailLabel') }}</span>
464
+ <input
465
+ type="email"
466
+ [class]="cls('formFieldInput', 'atlas-input')"
467
+ [value]="email()"
468
+ (input)="email.set($any($event.target).value)"
469
+ />
470
+ </label>
471
+ <label [class]="cls('formField', 'atlas-field')">
472
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleLabel') }}</span>
473
+ <select [class]="cls('formFieldInput', 'atlas-input')" (change)="inviteRole.set($any($event.target).value)">
474
+ @for (r of roles(); track r.key) {
475
+ <option [value]="r.key" [selected]="r.key === inviteRole()">{{ r.name }}</option>
476
+ }
477
+ </select>
478
+ </label>
479
+ <button
480
+ type="submit"
481
+ [class]="cls('formButtonPrimary', 'atlas-button')"
482
+ [disabled]="st.busy === 'invite' || !email().trim()"
483
+ >
484
+ {{ t('organization.sendInvite') }}
485
+ </button>
486
+ </form>
487
+ @if (st.invitations.length === 0) {
488
+ <p class="atlas-text">{{ t('organization.noInvitations') }}</p>
489
+ } @else {
490
+ <ul class="atlas-list">
491
+ @for (inv of st.invitations; track inv.id) {
492
+ <li data-atlas="organization-invitation">
493
+ <span>{{ inv.email_address }} · {{ inv.role }}</span>
494
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="resend(inv.id)">
495
+ {{ t('organization.resend') }}
496
+ </button>
497
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="revoke(inv.id)">
498
+ {{ t('organization.revoke') }}
499
+ </button>
500
+ </li>
501
+ }
502
+ </ul>
503
+ }
504
+ </div>
505
+ }
506
+
507
+ @if (tab() === 'roles' && canManage()) {
508
+ <div data-atlas="organization-roles">
509
+ @if (customRoles().length === 0) {
510
+ <p class="atlas-text">{{ t('organization.noCustomRoles') }}</p>
511
+ } @else {
512
+ <ul class="atlas-list">
513
+ @for (role of customRoles(); track role.key) {
514
+ <li data-atlas="organization-role">
515
+ <span>{{ role.name }} · {{ role.key }}</span>
516
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="editRole(role)">
517
+ {{ t('organization.editRole') }}
518
+ </button>
519
+ <button
520
+ type="button"
521
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
522
+ [disabled]="!role.id || st.busy === 'role:delete:' + role.id"
523
+ (click)="deleteRole(role)"
524
+ >
525
+ {{ t('organization.deleteRole') }}
526
+ </button>
527
+ </li>
528
+ }
529
+ </ul>
530
+ }
531
+ <form data-atlas="organization-role-form" (submit)="$event.preventDefault(); saveRole()">
532
+ @if (!editingRole()) {
533
+ <label [class]="cls('formField', 'atlas-field')">
534
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleKey') }}</span>
535
+ <input
536
+ name="role_key"
537
+ [class]="cls('formFieldInput', 'atlas-input')"
538
+ [value]="roleKey()"
539
+ (input)="roleKey.set($any($event.target).value)"
540
+ />
541
+ </label>
542
+ }
543
+ <label [class]="cls('formField', 'atlas-field')">
544
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleName') }}</span>
545
+ <input
546
+ name="role_name"
547
+ [class]="cls('formFieldInput', 'atlas-input')"
548
+ [value]="roleName()"
549
+ (input)="roleName.set($any($event.target).value)"
550
+ />
551
+ </label>
552
+ <fieldset [class]="cls('formField', 'atlas-field')">
553
+ <legend [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.rolePermissions') }}</legend>
554
+ @for (p of st.permissions; track p.key) {
555
+ <label data-atlas="organization-role-permission">
556
+ <input
557
+ type="checkbox"
558
+ [value]="p.key"
559
+ [checked]="rolePerms().includes(p.key)"
560
+ (change)="togglePermission(p.key, $any($event.target).checked)"
561
+ />
562
+ {{ p.name }}
563
+ </label>
564
+ }
565
+ </fieldset>
566
+ <button
567
+ type="submit"
568
+ [class]="cls('formButtonPrimary', 'atlas-button')"
569
+ [disabled]="!roleName().trim() || (!editingRole() && !roleKey().trim()) || (st.busy ?? '').startsWith('role:')"
570
+ >
571
+ {{ editingRole() ? t('organization.saveRole') : t('organization.createRole') }}
572
+ </button>
573
+ @if (editingRole()) {
574
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="resetRoleForm()">
575
+ {{ t('organization.cancel') }}
576
+ </button>
577
+ }
578
+ </form>
579
+ </div>
580
+ }
47
581
 
48
- <button *atlasProtect="{ permission: 'org:sys_memberships:manage' }">
49
- {{ t('organization.invite') }}
50
- </button>
51
- </div>
582
+ @if (tab() === 'settings') {
583
+ <div data-atlas="organization-settings">
584
+ @if (confirm(); as action) {
585
+ <p class="atlas-text">
586
+ {{ t(action === 'leave' ? 'organization.leaveConfirm' : 'organization.deleteConfirm', { name: membership.organization.name }) }}
587
+ </p>
588
+ @if (needsPassword()) {
589
+ <label [class]="cls('formField', 'atlas-field')">
590
+ <span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.passwordPrompt') }}</span>
591
+ <input
592
+ type="password"
593
+ autocomplete="current-password"
594
+ [class]="cls('formFieldInput', 'atlas-input')"
595
+ [value]="password()"
596
+ (input)="password.set($any($event.target).value)"
597
+ />
598
+ </label>
599
+ }
600
+ <button
601
+ type="button"
602
+ [class]="cls('formButtonPrimary', 'atlas-button')"
603
+ [disabled]="st.busy === 'leave' || st.busy === 'delete'"
604
+ (click)="action === 'leave' ? leave() : remove()"
605
+ >
606
+ {{ t('organization.confirm') }}
607
+ </button>
608
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="cancel()">
609
+ {{ t('organization.cancel') }}
610
+ </button>
611
+ } @else {
612
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="confirm.set('leave')">
613
+ {{ t('organization.leave') }}
614
+ </button>
615
+ @if (isAdmin()) {
616
+ <button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="confirm.set('delete')">
617
+ {{ t('organization.delete') }}
618
+ </button>
619
+ }
620
+ }
621
+ </div>
622
+ }
623
+ </div>
624
+ }
52
625
  }
53
626
  `,
54
627
  }]
55
- }] });
56
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JnYW5pemF0aW9uLXByb2ZpbGUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL29yZ2FuaXphdGlvbi1wcm9maWxlLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDaEQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDeEUsT0FBTyxFQUFFLFFBQVEsRUFBbUIsTUFBTSxlQUFlLENBQUM7QUFDMUQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLFNBQVMsQ0FBQzs7QUFFcEM7Ozs7O0dBS0c7QUFtQkgsTUFBTSxPQUFPLDRCQUE0QjtJQUNwQixLQUFLLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBRXRDLENBQUMsQ0FBQyxHQUFXO1FBQ3JCLE9BQU8sU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ2pELENBQUM7SUFFUyxHQUFHLENBQUMsR0FBZSxFQUFFLElBQVk7UUFDekMsT0FBTyxRQUFRLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3BELENBQUM7d0dBVFUsNEJBQTRCOzRGQUE1Qiw0QkFBNEIsc0ZBYjdCOzs7Ozs7Ozs7OztHQVdULDREQVpTLHFCQUFxQjs7NEZBY3BCLDRCQUE0QjtrQkFsQnhDLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLDRCQUE0QjtvQkFDdEMsVUFBVSxFQUFFLElBQUk7b0JBQ2hCLGVBQWUsRUFBRSx1QkFBdUIsQ0FBQyxNQUFNO29CQUMvQyxPQUFPLEVBQUUsQ0FBQyxxQkFBcUIsQ0FBQztvQkFDaEMsUUFBUSxFQUFFOzs7Ozs7Ozs7OztHQVdUO2lCQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBBdGxhc1NlcnZpY2UgfSBmcm9tICcuLi9hdGxhcy5zZXJ2aWNlJztcbmltcG9ydCB7IEF0bGFzUHJvdGVjdERpcmVjdGl2ZSB9IGZyb20gJy4uL2RpcmVjdGl2ZXMvcHJvdGVjdC5kaXJlY3RpdmUnO1xuaW1wb3J0IHsgY2xhc3NGb3IsIHR5cGUgRWxlbWVudEtleSB9IGZyb20gJy4uL2FwcGVhcmFuY2UnO1xuaW1wb3J0IHsgdHJhbnNsYXRlIH0gZnJvbSAnLi4vaTE4bic7XG5cbi8qKlxuICogYDxhdGxhcy1vcmdhbml6YXRpb24tcHJvZmlsZT5gIOKAlCB0aGUgYWN0aXZlIG9yZ2FuaXphdGlvbidzIHN1cmZhY2UuIFRoZVxuICogQW5ndWxhciBwZWVyIG9mIFJlYWN0J3MgYDxPcmdhbml6YXRpb25Qcm9maWxlLz5gLiBNYW5hZ2VtZW50IGNvbnRyb2xzIGFyZVxuICogZ2F0ZWQgb24gdGhlIEFDVElWRSBvcmcgcm9sZSB2aWEgYCphdGxhc1Byb3RlY3RgLCBtYXRjaGluZyB0aGUgc2VydmVyJ3MgcnVsZVxuICogcmF0aGVyIHRoYW4gZHVwbGljYXRpbmcgYSBsb29zZXIgb25lLiBSZW5kZXJzIG5vdGhpbmcgd2l0aCBubyBhY3RpdmUgb3JnLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdhdGxhcy1vcmdhbml6YXRpb24tcHJvZmlsZScsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBpbXBvcnRzOiBbQXRsYXNQcm90ZWN0RGlyZWN0aXZlXSxcbiAgdGVtcGxhdGU6IGBcbiAgICBAaWYgKGF0bGFzLm9yZ2FuaXphdGlvbigpOyBhcyBtZW1iZXJzaGlwKSB7XG4gICAgICA8ZGl2IFtjbGFzc109XCJjbHMoJ2NhcmQnLCAnYXRsYXMtY2FyZCcpXCI+XG4gICAgICAgIDxoMT57eyBtZW1iZXJzaGlwLm9yZ2FuaXphdGlvbi5uYW1lIH19PC9oMT5cbiAgICAgICAgPHA+e3sgdCgnb3JnYW5pemF0aW9uLnJvbGVMYWJlbCcpIH19OiB7eyBtZW1iZXJzaGlwLnJvbGUgfX08L3A+XG5cbiAgICAgICAgPGJ1dHRvbiAqYXRsYXNQcm90ZWN0PVwieyBwZXJtaXNzaW9uOiAnb3JnOnN5c19tZW1iZXJzaGlwczptYW5hZ2UnIH1cIj5cbiAgICAgICAgICB7eyB0KCdvcmdhbml6YXRpb24uaW52aXRlJykgfX1cbiAgICAgICAgPC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICB9XG4gIGAsXG59KVxuZXhwb3J0IGNsYXNzIE9yZ2FuaXphdGlvblByb2ZpbGVDb21wb25lbnQge1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgYXRsYXMgPSBpbmplY3QoQXRsYXNTZXJ2aWNlKTtcblxuICBwcm90ZWN0ZWQgdChrZXk6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRyYW5zbGF0ZSh0aGlzLmF0bGFzLmxvY2FsaXphdGlvbiwga2V5KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjbHMoa2V5OiBFbGVtZW50S2V5LCBiYXNlOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBjbGFzc0ZvcihrZXksIHRoaXMuYXRsYXMuYXBwZWFyYW5jZSwgYmFzZSk7XG4gIH1cbn1cbiJdfQ==
628
+ }], ctorParameters: () => [], propDecorators: { exit: [{
629
+ type: Output
630
+ }] } });
631
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JnYW5pemF0aW9uLXByb2ZpbGUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL29yZ2FuaXphdGlvbi1wcm9maWxlLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFDVCxVQUFVLEVBQ1YsWUFBWSxFQUNaLE1BQU0sRUFDTixRQUFRLEVBQ1IsTUFBTSxFQUNOLE1BQU0sRUFDTixNQUFNLEVBQ04sU0FBUyxHQUNWLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFDTCw2QkFBNkIsRUFDN0IscUJBQXFCLEdBR3RCLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUsUUFBUSxFQUFtQixNQUFNLGVBQWUsQ0FBQztBQUMxRCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sU0FBUyxDQUFDOztBQUtwQzs7Ozs7Ozs7R0FRRztBQXdPSCxNQUFNLE9BQU8sNEJBQTRCO0lBQ3BCLEtBQUssR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDN0IsSUFBSSxHQUFHLElBQUksWUFBWSxFQUFzQixDQUFDO0lBRWhELE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGdCQUFnQixFQUFFLENBQUM7SUFDaEQsVUFBVSxHQUF5QyxJQUFJLENBQUM7SUFDeEQsV0FBVyxHQUF3QixJQUFJLENBQUM7SUFFN0IsS0FBSyxHQUFHLE1BQU0sQ0FBa0MsSUFBSSxDQUFDLENBQUM7SUFDdEQsR0FBRyxHQUFHLE1BQU0sQ0FBTSxTQUFTLENBQUMsQ0FBQztJQUM3QixLQUFLLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ25CLFVBQVUsR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDbEMsT0FBTyxHQUFHLE1BQU0sQ0FBNEIsSUFBSSxDQUFDLENBQUM7SUFDbEQsYUFBYSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM5QixRQUFRLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3pDLGlGQUFpRjtJQUM5RCxXQUFXLEdBQUcsTUFBTSxDQUFnQixJQUFJLENBQUMsQ0FBQztJQUMxQyxPQUFPLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3JCLFFBQVEsR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDdEIsU0FBUyxHQUFHLE1BQU0sQ0FBVyxFQUFFLENBQUMsQ0FBQztJQUNqQyxXQUFXLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRXBHLE1BQU0sR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsRUFBRSxHQUEwQixJQUFJLElBQUksQ0FBQyxDQUFDO0lBQ2xGLFNBQVMsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQzNDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDcEIsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLFVBQVUsRUFBRSw0QkFBNEIsRUFBRSxDQUFDLENBQUM7SUFDdEUsQ0FBQyxDQUFDLENBQUM7SUFDZ0IsT0FBTyxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFlBQVksRUFBRSxFQUFFLElBQUksS0FBSyxXQUFXLENBQUMsQ0FBQztJQUMxRSxJQUFJLEdBQUcsUUFBUSxDQUFRLEdBQUcsRUFBRSxDQUFDLENBQUMsU0FBUyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFFLENBQUMsYUFBYSxFQUFFLE9BQU8sQ0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxVQUFVLENBQUMsQ0FBQyxDQUFDO0lBQ3hILEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ3ZDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsRUFBRSxLQUFLLElBQUksRUFBRSxDQUFDO1FBQ3hDLE9BQU8sS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxDQUFDLENBQUM7SUFDaEYsQ0FBQyxDQUFDLENBQUM7SUFDZ0IsWUFBWSxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUU7UUFDOUMsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssRUFBRSxFQUFFLEtBQUssQ0FBQztRQUNoQyxJQUFJLENBQUMsR0FBRyxJQUFLLEdBQWdDLENBQUMsbUJBQW1CO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFDL0UsT0FBTyxxQkFBcUIsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxpQkFBaUIsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQzNGLENBQUMsQ0FBQyxDQUFDO0lBRUg7UUFDRSxNQUFNLENBQUMsR0FBRyxFQUFFO1lBQ1YsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQUUsRUFBRSxZQUFZLENBQUMsRUFBRSxJQUFJLElBQUksQ0FBQztZQUM5RCxTQUFTLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ2pDLENBQUMsQ0FBQyxDQUFDO1FBQ0gsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFFTyxJQUFJLENBQUMsRUFBaUI7UUFDNUIsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLGNBQWMsS0FBSyxFQUFFO1lBQUUsT0FBTztRQUNuRCxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQztRQUNyQixJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQztRQUN2QixJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsRUFBRTtZQUFFLE9BQU87UUFDaEIsTUFBTSxVQUFVLEdBQUcsSUFBSSw2QkFBNkIsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEVBQUUsRUFBRTtZQUNwRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRTtZQUMzQixRQUFRLEVBQUUsS0FBSyxJQUFJLEVBQUU7Z0JBQ25CLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUM1QixDQUFDO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7UUFDN0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3hFLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQ3RDLEtBQUssVUFBVSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFUyxDQUFDLENBQUMsR0FBVyxFQUFFLElBQTZCO1FBQ3BELE9BQU8sU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRVMsR0FBRyxDQUFDLEdBQWUsRUFBRSxJQUFZO1FBQ3pDLE9BQU8sUUFBUSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBRVMsVUFBVSxDQUFDLENBQThDO1FBQ2pFLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsSUFBSSxFQUFFLFNBQVMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO0lBQ25ILENBQUM7SUFFUyxVQUFVLENBQUMsTUFBYyxFQUFFLElBQVk7UUFDL0MsS0FBSyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUVTLFlBQVksQ0FBQyxNQUFjO1FBQ25DLEtBQUssSUFBSSxDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUVTLEtBQUssQ0FBQyxVQUFVO1FBQ3hCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNwQyxJQUFJLElBQUksQ0FBQyxVQUFVLElBQUksT0FBTyxJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUM7WUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNuSCxDQUFDO0lBRVMsTUFBTSxDQUFDLEVBQVU7UUFDekIsS0FBSyxJQUFJLENBQUMsVUFBVSxFQUFFLGdCQUFnQixDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzdDLENBQUM7SUFFUyxNQUFNLENBQUMsRUFBVTtRQUN6QixLQUFLLElBQUksQ0FBQyxVQUFVLEVBQUUsZ0JBQWdCLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUVTLEtBQUssQ0FBQyxLQUFLO1FBQ25CLElBQUksSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQy9FLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3pCLENBQUM7SUFFUyxLQUFLLENBQUMsTUFBTTtRQUNwQixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDO1FBQ25DLElBQUksQ0FBQyxVQUFVO1lBQUUsT0FBTztRQUN4QixJQUFJLElBQUksQ0FBQyxhQUFhLEVBQUUsRUFBRSxDQUFDO1lBQ3pCLElBQUksQ0FBQztnQkFDSCxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1lBQ25ELENBQUM7WUFBQyxNQUFNLENBQUM7Z0JBQ1AsT0FBTztZQUNULENBQUM7WUFDRCxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUN0QixJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNoQyxDQUFDO1FBQ0QsSUFBSSxNQUFNLFVBQVUsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLENBQUM7WUFDMUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDMUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdkIsT0FBTztRQUNULENBQUM7UUFDRCxNQUFNLEdBQUcsR0FBRyxVQUFVLENBQUMsUUFBUSxFQUFFLENBQUMsS0FBd0MsQ0FBQztRQUMzRSxJQUFJLEdBQUcsRUFBRSxtQkFBbUI7WUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM3RCxDQUFDO0lBRVMsUUFBUSxDQUFDLElBQWlCO1FBQ2xDLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksSUFBSSxDQUFDLENBQUM7UUFDdEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzNCLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QixJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBRVMsYUFBYTtRQUNyQixJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMzQixJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN0QixJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUN6QixDQUFDO0lBRVMsZ0JBQWdCLENBQUMsR0FBVyxFQUFFLEVBQVc7UUFDakQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsT0FBTyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2xHLENBQUM7SUFFUyxLQUFLLENBQUMsUUFBUTtRQUN0QixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDO1FBQ25DLElBQUksQ0FBQyxVQUFVO1lBQUUsT0FBTztRQUN4QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDbkMsTUFBTSxHQUFHLEdBQUcsT0FBTztZQUNqQixDQUFDLENBQUMsTUFBTSxVQUFVLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDO1lBQ3ZHLENBQUMsQ0FBQyxNQUFNLFVBQVUsQ0FBQyxVQUFVLENBQUMsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDN0gsSUFBSSxHQUFHO1lBQUUsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO0lBQ2hDLENBQUM7SUFFUyxVQUFVLENBQUMsSUFBaUI7UUFDcEMsSUFBSSxJQUFJLENBQUMsRUFBRTtZQUFFLEtBQUssSUFBSSxDQUFDLFVBQVUsRUFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFFUyxNQUFNO1FBQ2QsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDOUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDeEIsQ0FBQzt3R0FoS1UsNEJBQTRCOzRGQUE1Qiw0QkFBNEIsaUhBbk83Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaU9UOzs0RkFFVSw0QkFBNEI7a0JBdk94QyxTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSw0QkFBNEI7b0JBQ3RDLFVBQVUsRUFBRSxJQUFJO29CQUNoQixlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtvQkFDL0MsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpT1Q7aUJBQ0Y7d0RBR29CLElBQUk7c0JBQXRCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgQ29tcG9uZW50LFxuICBEZXN0cm95UmVmLFxuICBFdmVudEVtaXR0ZXIsXG4gIE91dHB1dCxcbiAgY29tcHV0ZWQsXG4gIGVmZmVjdCxcbiAgaW5qZWN0LFxuICBzaWduYWwsXG4gIHVudHJhY2tlZCxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBPcmdhbml6YXRpb25Qcm9maWxlQ29udHJvbGxlcixcbiAgbG9jYWxpemVkRXJyb3JNZXNzYWdlLFxuICB0eXBlIE9yZ2FuaXphdGlvblByb2ZpbGVTdGF0ZSxcbiAgdHlwZSBPcmdhbml6YXRpb25SZXF1ZXN0RXJyb3IsXG59IGZyb20gJ0BhdGxhc2F1dGgvanMnO1xuaW1wb3J0IHsgQXRsYXNTZXJ2aWNlIH0gZnJvbSAnLi4vYXRsYXMuc2VydmljZSc7XG5pbXBvcnQgeyBjbGFzc0ZvciwgdHlwZSBFbGVtZW50S2V5IH0gZnJvbSAnLi4vYXBwZWFyYW5jZSc7XG5pbXBvcnQgeyB0cmFuc2xhdGUgfSBmcm9tICcuLi9pMThuJztcblxudHlwZSBUYWIgPSAnbWVtYmVycycgfCAnaW52aXRhdGlvbnMnIHwgJ3JvbGVzJyB8ICdzZXR0aW5ncyc7XG50eXBlIFByb2ZpbGVSb2xlID0gT3JnYW5pemF0aW9uUHJvZmlsZVN0YXRlWydyb2xlcyddW251bWJlcl07XG5cbi8qKlxuICogYDxhdGxhcy1vcmdhbml6YXRpb24tcHJvZmlsZT5gIOKAlCB0aGUgYWN0aXZlIG9yZ2FuaXphdGlvbidzIG1lbWJlcnMgKGFkbWluc1xuICogY2hhbmdlIHJvbGVzIGFuZCByZW1vdmUpLCBpbnZpdGF0aW9ucyAoYWRtaW5zIGludml0ZSwgcmV2b2tlIGFuZCByZXNlbmQpIGFuZFxuICogc2V0dGluZ3MgKGxlYXZlOyBhbiBhZG1pbiBkZWxldGVzIGFmdGVyIGNvbmZpcm1pbmcgdGhlaXIgcGFzc3dvcmQpLiBUaGVcbiAqIEFuZ3VsYXIgcGVlciBvZiBSZWFjdCdzIGA8T3JnYW5pemF0aW9uUHJvZmlsZS8+YCwgb24gdGhlIHNhbWUgaGVhZGxlc3NcbiAqIGNvbnRyb2xsZXIuIEV2ZXJ5IGNvbnRyb2wgaXMgZ2F0ZWQgb24gdGhlIEFDVElWRSBvcmcgcm9sZSwgbWF0Y2hpbmcgdGhlXG4gKiBzZXJ2ZXIncyBydWxlLiBSZW5kZXJzIG5vdGhpbmcgd2l0aCBubyBhY3RpdmUgb3JnLiBFbWl0cyBgZXhpdGAgd2l0aCBgbGVmdGBcbiAqIG9yIGBkZWxldGVkYC5cbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYXRsYXMtb3JnYW5pemF0aW9uLXByb2ZpbGUnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgdGVtcGxhdGU6IGBcbiAgICBAaWYgKGF0bGFzLm9yZ2FuaXphdGlvbigpOyBhcyBtZW1iZXJzaGlwKSB7XG4gICAgICBAaWYgKHN0YXRlKCk7IGFzIHN0KSB7XG4gICAgICAgIDxkaXYgW2NsYXNzXT1cImNscygnY2FyZCcsICdhdGxhcy1jYXJkJylcIiBkYXRhLWF0bGFzPVwib3JnYW5pemF0aW9uLXByb2ZpbGVcIj5cbiAgICAgICAgICA8aDE+e3sgbWVtYmVyc2hpcC5vcmdhbml6YXRpb24ubmFtZSB9fTwvaDE+XG4gICAgICAgICAgPHA+e3sgdCgnb3JnYW5pemF0aW9uLnJvbGVMYWJlbCcpIH19OiB7eyBtZW1iZXJzaGlwLnJvbGUgfX08L3A+XG5cbiAgICAgICAgICA8ZGl2IFtjbGFzc109XCJjbHMoJ21lbnUnLCAnYXRsYXMtbWVudScpXCIgcm9sZT1cInRhYmxpc3RcIj5cbiAgICAgICAgICAgIEBmb3IgKGtleSBvZiB0YWJzKCk7IHRyYWNrIGtleSkge1xuICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgcm9sZT1cInRhYlwiXG4gICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1zZWxlY3RlZF09XCJ0YWIoKSA9PT0ga2V5XCJcbiAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdtZW51SXRlbScsICdhdGxhcy1tZW51LWl0ZW0nKVwiXG4gICAgICAgICAgICAgICAgW2F0dHIuZGF0YS1hdGxhc109XCInb3JnYW5pemF0aW9uLXRhYi0nICsga2V5XCJcbiAgICAgICAgICAgICAgICAoY2xpY2spPVwidGFiLnNldChrZXkpXCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIHt7IHQoJ29yZ2FuaXphdGlvbi4nICsga2V5KSB9fVxuICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgIEBpZiAoZXJyb3JNZXNzYWdlKCk7IGFzIG1lc3NhZ2UpIHtcbiAgICAgICAgICAgIDxwIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZEVycm9yJywgJ2F0bGFzLWVycm9yJylcIiByb2xlPVwiYWxlcnRcIj57eyBtZXNzYWdlIH19PC9wPlxuICAgICAgICAgIH1cblxuICAgICAgICAgIEBpZiAodGFiKCkgPT09ICdtZW1iZXJzJykge1xuICAgICAgICAgICAgPHVsIGNsYXNzPVwiYXRsYXMtbGlzdFwiIGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24tbWVtYmVyc1wiPlxuICAgICAgICAgICAgICBAZm9yIChtIG9mIHN0Lm1lbWJlcnM7IHRyYWNrIG0uaWQpIHtcbiAgICAgICAgICAgICAgICA8bGkgZGF0YS1hdGxhcz1cIm9yZ2FuaXphdGlvbi1tZW1iZXJcIj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuPnt7IG1lbWJlck5hbWUobSkgfX17eyBtLnVzZXI/LmlkID09PSB1c2VySWQoKSA/ICcgJyArIHQoJ29yZ2FuaXphdGlvbi55b3UnKSA6ICcnIH19PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgQGlmIChjYW5NYW5hZ2UoKSAmJiBtLnVzZXIgJiYgbS51c2VyLmlkICE9PSB1c2VySWQoKSkge1xuICAgICAgICAgICAgICAgICAgICA8c2VsZWN0XG4gICAgICAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCJ0KCdvcmdhbml6YXRpb24ucm9sZUxhYmVsJylcIlxuICAgICAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZElucHV0JywgJ2F0bGFzLWlucHV0JylcIlxuICAgICAgICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCJzdC5idXN5ID09PSAncm9sZTonICsgbS51c2VyLmlkXCJcbiAgICAgICAgICAgICAgICAgICAgICAoY2hhbmdlKT1cImNoYW5nZVJvbGUobS51c2VyLmlkLCAkYW55KCRldmVudC50YXJnZXQpLnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICBAZm9yIChyIG9mIHJvbGVzKCk7IHRyYWNrIHIua2V5KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICA8b3B0aW9uIFt2YWx1ZV09XCJyLmtleVwiIFtzZWxlY3RlZF09XCJyLmtleSA9PT0gbS5yb2xlXCI+e3sgci5uYW1lIH19PC9vcHRpb24+XG4gICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICA8L3NlbGVjdD5cbiAgICAgICAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25TZWNvbmRhcnknLCAnYXRsYXMtYnV0dG9uLXNlY29uZGFyeScpXCJcbiAgICAgICAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwic3QuYnVzeSA9PT0gJ3JlbW92ZTonICsgbS51c2VyLmlkXCJcbiAgICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwicmVtb3ZlTWVtYmVyKG0udXNlci5pZClcIlxuICAgICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLnJlbW92ZScpIH19XG4gICAgICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuPiDCtyB7eyBtLnJvbGUgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgPC91bD5cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBAaWYgKHRhYigpID09PSAnaW52aXRhdGlvbnMnICYmIGNhbk1hbmFnZSgpKSB7XG4gICAgICAgICAgICA8ZGl2IGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24taW52aXRhdGlvbnNcIj5cbiAgICAgICAgICAgICAgPGZvcm0gKHN1Ym1pdCk9XCIkZXZlbnQucHJldmVudERlZmF1bHQoKTsgc2VuZEludml0ZSgpXCI+XG4gICAgICAgICAgICAgICAgPGxhYmVsIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZCcsICdhdGxhcy1maWVsZCcpXCI+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRMYWJlbCcsICdhdGxhcy1maWVsZC1sYWJlbCcpXCI+e3sgdCgnb3JnYW5pemF0aW9uLmVtYWlsTGFiZWwnKSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICAgICAgICB0eXBlPVwiZW1haWxcIlxuICAgICAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRJbnB1dCcsICdhdGxhcy1pbnB1dCcpXCJcbiAgICAgICAgICAgICAgICAgICAgW3ZhbHVlXT1cImVtYWlsKClcIlxuICAgICAgICAgICAgICAgICAgICAoaW5wdXQpPVwiZW1haWwuc2V0KCRhbnkoJGV2ZW50LnRhcmdldCkudmFsdWUpXCJcbiAgICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgICAgICAgICA8bGFiZWwgW2NsYXNzXT1cImNscygnZm9ybUZpZWxkJywgJ2F0bGFzLWZpZWxkJylcIj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZExhYmVsJywgJ2F0bGFzLWZpZWxkLWxhYmVsJylcIj57eyB0KCdvcmdhbml6YXRpb24ucm9sZUxhYmVsJykgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8c2VsZWN0IFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZElucHV0JywgJ2F0bGFzLWlucHV0JylcIiAoY2hhbmdlKT1cImludml0ZVJvbGUuc2V0KCRhbnkoJGV2ZW50LnRhcmdldCkudmFsdWUpXCI+XG4gICAgICAgICAgICAgICAgICAgIEBmb3IgKHIgb2Ygcm9sZXMoKTsgdHJhY2sgci5rZXkpIHtcbiAgICAgICAgICAgICAgICAgICAgICA8b3B0aW9uIFt2YWx1ZV09XCJyLmtleVwiIFtzZWxlY3RlZF09XCJyLmtleSA9PT0gaW52aXRlUm9sZSgpXCI+e3sgci5uYW1lIH19PC9vcHRpb24+XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIDwvc2VsZWN0PlxuICAgICAgICAgICAgICAgIDwvbGFiZWw+XG4gICAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgICAgdHlwZT1cInN1Ym1pdFwiXG4gICAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uUHJpbWFyeScsICdhdGxhcy1idXR0b24nKVwiXG4gICAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwic3QuYnVzeSA9PT0gJ2ludml0ZScgfHwgIWVtYWlsKCkudHJpbSgpXCJcbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICB7eyB0KCdvcmdhbml6YXRpb24uc2VuZEludml0ZScpIH19XG4gICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgIDwvZm9ybT5cbiAgICAgICAgICAgICAgQGlmIChzdC5pbnZpdGF0aW9ucy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICA8cCBjbGFzcz1cImF0bGFzLXRleHRcIj57eyB0KCdvcmdhbml6YXRpb24ubm9JbnZpdGF0aW9ucycpIH19PC9wPlxuICAgICAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgICAgICA8dWwgY2xhc3M9XCJhdGxhcy1saXN0XCI+XG4gICAgICAgICAgICAgICAgICBAZm9yIChpbnYgb2Ygc3QuaW52aXRhdGlvbnM7IHRyYWNrIGludi5pZCkge1xuICAgICAgICAgICAgICAgICAgICA8bGkgZGF0YS1hdGxhcz1cIm9yZ2FuaXphdGlvbi1pbnZpdGF0aW9uXCI+XG4gICAgICAgICAgICAgICAgICAgICAgPHNwYW4+e3sgaW52LmVtYWlsX2FkZHJlc3MgfX0gwrcge3sgaW52LnJvbGUgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgW2NsYXNzXT1cImNscygnZm9ybUJ1dHRvblNlY29uZGFyeScsICdhdGxhcy1idXR0b24tc2Vjb25kYXJ5JylcIiAoY2xpY2spPVwicmVzZW5kKGludi5pZClcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IHQoJ29yZ2FuaXphdGlvbi5yZXNlbmQnKSB9fVxuICAgICAgICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25TZWNvbmRhcnknLCAnYXRsYXMtYnV0dG9uLXNlY29uZGFyeScpXCIgKGNsaWNrKT1cInJldm9rZShpbnYuaWQpXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICB7eyB0KCdvcmdhbml6YXRpb24ucmV2b2tlJykgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA8L3VsPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBAaWYgKHRhYigpID09PSAncm9sZXMnICYmIGNhbk1hbmFnZSgpKSB7XG4gICAgICAgICAgICA8ZGl2IGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24tcm9sZXNcIj5cbiAgICAgICAgICAgICAgQGlmIChjdXN0b21Sb2xlcygpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgIDxwIGNsYXNzPVwiYXRsYXMtdGV4dFwiPnt7IHQoJ29yZ2FuaXphdGlvbi5ub0N1c3RvbVJvbGVzJykgfX08L3A+XG4gICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgIDx1bCBjbGFzcz1cImF0bGFzLWxpc3RcIj5cbiAgICAgICAgICAgICAgICAgIEBmb3IgKHJvbGUgb2YgY3VzdG9tUm9sZXMoKTsgdHJhY2sgcm9sZS5rZXkpIHtcbiAgICAgICAgICAgICAgICAgICAgPGxpIGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24tcm9sZVwiPlxuICAgICAgICAgICAgICAgICAgICAgIDxzcGFuPnt7IHJvbGUubmFtZSB9fSDCtyB7eyByb2xlLmtleSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uU2Vjb25kYXJ5JywgJ2F0bGFzLWJ1dHRvbi1zZWNvbmRhcnknKVwiIChjbGljayk9XCJlZGl0Um9sZShyb2xlKVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLmVkaXRSb2xlJykgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25TZWNvbmRhcnknLCAnYXRsYXMtYnV0dG9uLXNlY29uZGFyeScpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhcm9sZS5pZCB8fCBzdC5idXN5ID09PSAncm9sZTpkZWxldGU6JyArIHJvbGUuaWRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cImRlbGV0ZVJvbGUocm9sZSlcIlxuICAgICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IHQoJ29yZ2FuaXphdGlvbi5kZWxldGVSb2xlJykgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA8L3VsPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDxmb3JtIGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24tcm9sZS1mb3JtXCIgKHN1Ym1pdCk9XCIkZXZlbnQucHJldmVudERlZmF1bHQoKTsgc2F2ZVJvbGUoKVwiPlxuICAgICAgICAgICAgICAgIEBpZiAoIWVkaXRpbmdSb2xlKCkpIHtcbiAgICAgICAgICAgICAgICAgIDxsYWJlbCBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGQnLCAnYXRsYXMtZmllbGQnKVwiPlxuICAgICAgICAgICAgICAgICAgICA8c3BhbiBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRMYWJlbCcsICdhdGxhcy1maWVsZC1sYWJlbCcpXCI+e3sgdCgnb3JnYW5pemF0aW9uLnJvbGVLZXknKSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICAgICAgICAgICAgbmFtZT1cInJvbGVfa2V5XCJcbiAgICAgICAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRJbnB1dCcsICdhdGxhcy1pbnB1dCcpXCJcbiAgICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwicm9sZUtleSgpXCJcbiAgICAgICAgICAgICAgICAgICAgICAoaW5wdXQpPVwicm9sZUtleS5zZXQoJGFueSgkZXZlbnQudGFyZ2V0KS52YWx1ZSlcIlxuICAgICAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgPGxhYmVsIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZCcsICdhdGxhcy1maWVsZCcpXCI+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRMYWJlbCcsICdhdGxhcy1maWVsZC1sYWJlbCcpXCI+e3sgdCgnb3JnYW5pemF0aW9uLnJvbGVOYW1lJykgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICAgICAgICAgbmFtZT1cInJvbGVfbmFtZVwiXG4gICAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZElucHV0JywgJ2F0bGFzLWlucHV0JylcIlxuICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwicm9sZU5hbWUoKVwiXG4gICAgICAgICAgICAgICAgICAgIChpbnB1dCk9XCJyb2xlTmFtZS5zZXQoJGFueSgkZXZlbnQudGFyZ2V0KS52YWx1ZSlcIlxuICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgICAgICAgIDxmaWVsZHNldCBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGQnLCAnYXRsYXMtZmllbGQnKVwiPlxuICAgICAgICAgICAgICAgICAgPGxlZ2VuZCBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRMYWJlbCcsICdhdGxhcy1maWVsZC1sYWJlbCcpXCI+e3sgdCgnb3JnYW5pemF0aW9uLnJvbGVQZXJtaXNzaW9ucycpIH19PC9sZWdlbmQ+XG4gICAgICAgICAgICAgICAgICBAZm9yIChwIG9mIHN0LnBlcm1pc3Npb25zOyB0cmFjayBwLmtleSkge1xuICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZGF0YS1hdGxhcz1cIm9yZ2FuaXphdGlvbi1yb2xlLXBlcm1pc3Npb25cIj5cbiAgICAgICAgICAgICAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwicC5rZXlcIlxuICAgICAgICAgICAgICAgICAgICAgICAgW2NoZWNrZWRdPVwicm9sZVBlcm1zKCkuaW5jbHVkZXMocC5rZXkpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIChjaGFuZ2UpPVwidG9nZ2xlUGVybWlzc2lvbihwLmtleSwgJGFueSgkZXZlbnQudGFyZ2V0KS5jaGVja2VkKVwiXG4gICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgICB7eyBwLm5hbWUgfX1cbiAgICAgICAgICAgICAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA8L2ZpZWxkc2V0PlxuICAgICAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgICAgIHR5cGU9XCJzdWJtaXRcIlxuICAgICAgICAgICAgICAgICAgW2NsYXNzXT1cImNscygnZm9ybUJ1dHRvblByaW1hcnknLCAnYXRsYXMtYnV0dG9uJylcIlxuICAgICAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cIiFyb2xlTmFtZSgpLnRyaW0oKSB8fCAoIWVkaXRpbmdSb2xlKCkgJiYgIXJvbGVLZXkoKS50cmltKCkpIHx8IChzdC5idXN5ID8/ICcnKS5zdGFydHNXaXRoKCdyb2xlOicpXCJcbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICB7eyBlZGl0aW5nUm9sZSgpID8gdCgnb3JnYW5pemF0aW9uLnNhdmVSb2xlJykgOiB0KCdvcmdhbml6YXRpb24uY3JlYXRlUm9sZScpIH19XG4gICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgICAgQGlmIChlZGl0aW5nUm9sZSgpKSB7XG4gICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uU2Vjb25kYXJ5JywgJ2F0bGFzLWJ1dHRvbi1zZWNvbmRhcnknKVwiIChjbGljayk9XCJyZXNldFJvbGVGb3JtKClcIj5cbiAgICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLmNhbmNlbCcpIH19XG4gICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDwvZm9ybT5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cblxuICAgICAgICAgIEBpZiAodGFiKCkgPT09ICdzZXR0aW5ncycpIHtcbiAgICAgICAgICAgIDxkaXYgZGF0YS1hdGxhcz1cIm9yZ2FuaXphdGlvbi1zZXR0aW5nc1wiPlxuICAgICAgICAgICAgICBAaWYgKGNvbmZpcm0oKTsgYXMgYWN0aW9uKSB7XG4gICAgICAgICAgICAgICAgPHAgY2xhc3M9XCJhdGxhcy10ZXh0XCI+XG4gICAgICAgICAgICAgICAgICB7eyB0KGFjdGlvbiA9PT0gJ2xlYXZlJyA/ICdvcmdhbml6YXRpb24ubGVhdmVDb25maXJtJyA6ICdvcmdhbml6YXRpb24uZGVsZXRlQ29uZmlybScsIHsgbmFtZTogbWVtYmVyc2hpcC5vcmdhbml6YXRpb24ubmFtZSB9KSB9fVxuICAgICAgICAgICAgICAgIDwvcD5cbiAgICAgICAgICAgICAgICBAaWYgKG5lZWRzUGFzc3dvcmQoKSkge1xuICAgICAgICAgICAgICAgICAgPGxhYmVsIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZCcsICdhdGxhcy1maWVsZCcpXCI+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZExhYmVsJywgJ2F0bGFzLWZpZWxkLWxhYmVsJylcIj57eyB0KCdvcmdhbml6YXRpb24ucGFzc3dvcmRQcm9tcHQnKSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICAgICAgICAgICAgdHlwZT1cInBhc3N3b3JkXCJcbiAgICAgICAgICAgICAgICAgICAgICBhdXRvY29tcGxldGU9XCJjdXJyZW50LXBhc3N3b3JkXCJcbiAgICAgICAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRJbnB1dCcsICdhdGxhcy1pbnB1dCcpXCJcbiAgICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwicGFzc3dvcmQoKVwiXG4gICAgICAgICAgICAgICAgICAgICAgKGlucHV0KT1cInBhc3N3b3JkLnNldCgkYW55KCRldmVudC50YXJnZXQpLnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25QcmltYXJ5JywgJ2F0bGFzLWJ1dHRvbicpXCJcbiAgICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCJzdC5idXN5ID09PSAnbGVhdmUnIHx8IHN0LmJ1c3kgPT09ICdkZWxldGUnXCJcbiAgICAgICAgICAgICAgICAgIChjbGljayk9XCJhY3Rpb24gPT09ICdsZWF2ZScgPyBsZWF2ZSgpIDogcmVtb3ZlKClcIlxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgIHt7IHQoJ29yZ2FuaXphdGlvbi5jb25maXJtJykgfX1cbiAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uU2Vjb25kYXJ5JywgJ2F0bGFzLWJ1dHRvbi1zZWNvbmRhcnknKVwiIChjbGljayk9XCJjYW5jZWwoKVwiPlxuICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLmNhbmNlbCcpIH19XG4gICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25TZWNvbmRhcnknLCAnYXRsYXMtYnV0dG9uLXNlY29uZGFyeScpXCIgKGNsaWNrKT1cImNvbmZpcm0uc2V0KCdsZWF2ZScpXCI+XG4gICAgICAgICAgICAgICAgICB7eyB0KCdvcmdhbml6YXRpb24ubGVhdmUnKSB9fVxuICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgICAgIEBpZiAoaXNBZG1pbigpKSB7XG4gICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uU2Vjb25kYXJ5JywgJ2F0bGFzLWJ1dHRvbi1zZWNvbmRhcnknKVwiIChjbGljayk9XCJjb25maXJtLnNldCgnZGVsZXRlJylcIj5cbiAgICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLmRlbGV0ZScpIH19XG4gICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgfVxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBPcmdhbml6YXRpb25Qcm9maWxlQ29tcG9uZW50IHtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGF0bGFzID0gaW5qZWN0KEF0bGFzU2VydmljZSk7XG4gIEBPdXRwdXQoKSByZWFkb25seSBleGl0ID0gbmV3IEV2ZW50RW1pdHRlcjwnbGVmdCcgfCAnZGVsZXRlZCc+KCk7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBjbGllbnQgPSB0aGlzLmF0bGFzLmNyZWF0ZUZhcGlDbGllbnQoKTtcbiAgcHJpdmF0ZSBjb250cm9sbGVyOiBPcmdhbml6YXRpb25Qcm9maWxlQ29udHJvbGxlciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHVuc3Vic2NyaWJlOiAoKCkgPT4gdm9pZCkgfCBudWxsID0gbnVsbDtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc3RhdGUgPSBzaWduYWw8T3JnYW5pemF0aW9uUHJvZmlsZVN0YXRlIHwgbnVsbD4obnVsbCk7XG4gIHByb3RlY3RlZCByZWFkb25seSB0YWIgPSBzaWduYWw8VGFiPignbWVtYmVycycpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZW1haWwgPSBzaWduYWwoJycpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaW52aXRlUm9sZSA9IHNpZ25hbCgnb3JnOm1lbWJlcicpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY29uZmlybSA9IHNpZ25hbDwnbGVhdmUnIHwgJ2RlbGV0ZScgfCBudWxsPihudWxsKTtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IG5lZWRzUGFzc3dvcmQgPSBzaWduYWwoZmFsc2UpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcGFzc3dvcmQgPSBzaWduYWwoJycpO1xuICAvLyBUaGUgY3VzdG9tLXJvbGUgZWRpdG9yOiBudWxsID0gY3JlYXRpbmcsIGVsc2UgdGhlIGlkIG9mIHRoZSByb2xlIGJlaW5nIGVkaXRlZC5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGVkaXRpbmdSb2xlID0gc2lnbmFsPHN0cmluZyB8IG51bGw+KG51bGwpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcm9sZUtleSA9IHNpZ25hbCgnJyk7XG4gIHByb3RlY3RlZCByZWFkb25seSByb2xlTmFtZSA9IHNpZ25hbCgnJyk7XG4gIHByb3RlY3RlZCByZWFkb25seSByb2xlUGVybXMgPSBzaWduYWw8c3RyaW5nW10+KFtdKTtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGN1c3RvbVJvbGVzID0gY29tcHV0ZWQoKCkgPT4gKHRoaXMuc3RhdGUoKT8ucm9sZXMgPz8gW10pLmZpbHRlcigocikgPT4gQm9vbGVhbihyLm9yZ2FuaXphdGlvbl9pZCkpKTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgdXNlcklkID0gY29tcHV0ZWQoKCkgPT4gKHRoaXMuYXRsYXMuY2xhaW1zKCk/LnN1YiBhcyBzdHJpbmcgfCB1bmRlZmluZWQpID8/IG51bGwpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY2FuTWFuYWdlID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIHRoaXMuYXRsYXMuY2xhaW1zKCk7XG4gICAgcmV0dXJuIHRoaXMuYXRsYXMuaGFzKHsgcGVybWlzc2lvbjogJ29yZzpzeXNfbWVtYmVyc2hpcHM6bWFuYWdlJyB9KTtcbiAgfSk7XG4gIHByb3RlY3RlZCByZWFkb25seSBpc0FkbWluID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5hdGxhcy5vcmdhbml6YXRpb24oKT8ucm9sZSA9PT0gJ29yZzphZG1pbicpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgdGFicyA9IGNvbXB1dGVkPFRhYltdPigoKSA9PiBbJ21lbWJlcnMnLCAuLi4odGhpcy5jYW5NYW5hZ2UoKSA/IChbJ2ludml0YXRpb25zJywgJ3JvbGVzJ10gYXMgVGFiW10pIDogW10pLCAnc2V0dGluZ3MnXSk7XG4gIHByb3RlY3RlZCByZWFkb25seSByb2xlcyA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBjb25zdCByb2xlcyA9IHRoaXMuc3RhdGUoKT8ucm9sZXMgPz8gW107XG4gICAgcmV0dXJuIHJvbGVzLmxlbmd0aCA+IDAgPyByb2xlcyA6IFt7IGtleTogJ29yZzptZW1iZXInLCBuYW1lOiAnb3JnOm1lbWJlcicgfV07XG4gIH0pO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZXJyb3JNZXNzYWdlID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIGNvbnN0IGVyciA9IHRoaXMuc3RhdGUoKT8uZXJyb3I7XG4gICAgaWYgKCFlcnIgfHwgKGVyciBhcyBPcmdhbml6YXRpb25SZXF1ZXN0RXJyb3IpLm5lZWRzUmV2ZXJpZmljYXRpb24pIHJldHVybiBudWxsO1xuICAgIHJldHVybiBsb2NhbGl6ZWRFcnJvck1lc3NhZ2UoZXJyLCB0aGlzLmF0bGFzLmVycm9yTG9jYWxpemF0aW9uLCB0aGlzLmF0bGFzLmxvY2FsaXphdGlvbik7XG4gIH0pO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIGVmZmVjdCgoKSA9PiB7XG4gICAgICBjb25zdCBpZCA9IHRoaXMuYXRsYXMub3JnYW5pemF0aW9uKCk/Lm9yZ2FuaXphdGlvbi5pZCA/PyBudWxsO1xuICAgICAgdW50cmFja2VkKCgpID0+IHRoaXMuYmluZChpZCkpO1xuICAgIH0pO1xuICAgIGluamVjdChEZXN0cm95UmVmKS5vbkRlc3Ryb3koKCkgPT4gdGhpcy51bnN1YnNjcmliZT8uKCkpO1xuICB9XG5cbiAgcHJpdmF0ZSBiaW5kKGlkOiBzdHJpbmcgfCBudWxsKSB7XG4gICAgaWYgKHRoaXMuY29udHJvbGxlcj8ub3JnYW5pemF0aW9uSWQgPT09IGlkKSByZXR1cm47XG4gICAgdGhpcy51bnN1YnNjcmliZT8uKCk7XG4gICAgdGhpcy5jb250cm9sbGVyID0gbnVsbDtcbiAgICB0aGlzLnN0YXRlLnNldChudWxsKTtcbiAgICBpZiAoIWlkKSByZXR1cm47XG4gICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBPcmdhbml6YXRpb25Qcm9maWxlQ29udHJvbGxlcih0aGlzLmNsaWVudCwgaWQsIHtcbiAgICAgIGNhbk1hbmFnZTogdGhpcy5jYW5NYW5hZ2UoKSxcbiAgICAgIG9uQ2hhbmdlOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IHRoaXMuYXRsYXMucmVsb2FkKCk7XG4gICAgICB9LFxuICAgIH0pO1xuICAgIHRoaXMuY29udHJvbGxlciA9IGNvbnRyb2xsZXI7XG4gICAgdGhpcy51bnN1YnNjcmliZSA9IGNvbnRyb2xsZXIuc3Vic2NyaWJlKChuZXh0KSA9PiB0aGlzLnN0YXRlLnNldChuZXh0KSk7XG4gICAgdGhpcy5zdGF0ZS5zZXQoY29udHJvbGxlci5nZXRTdGF0ZSgpKTtcbiAgICB2b2lkIGNvbnRyb2xsZXIubG9hZCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHQoa2V5OiBzdHJpbmcsIHZhcnM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTogc3RyaW5nIHtcbiAgICByZXR1cm4gdHJhbnNsYXRlKHRoaXMuYXRsYXMubG9jYWxpemF0aW9uLCBrZXksIHZhcnMpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNscyhrZXk6IEVsZW1lbnRLZXksIGJhc2U6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGNsYXNzRm9yKGtleSwgdGhpcy5hdGxhcy5hcHBlYXJhbmNlLCBiYXNlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtZW1iZXJOYW1lKG06IE9yZ2FuaXphdGlvblByb2ZpbGVTdGF0ZVsnbWVtYmVycyddW251bWJlcl0pOiBzdHJpbmcge1xuICAgIHJldHVybiBbbS51c2VyPy5maXJzdF9uYW1lLCBtLnVzZXI/Lmxhc3RfbmFtZV0uZmlsdGVyKEJvb2xlYW4pLmpvaW4oJyAnKSB8fCB0aGlzLnQoJ29yZ2FuaXphdGlvbi51bm5hbWVkTWVtYmVyJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY2hhbmdlUm9sZSh1c2VySWQ6IHN0cmluZywgcm9sZTogc3RyaW5nKSB7XG4gICAgdm9pZCB0aGlzLmNvbnRyb2xsZXI/LmNoYW5nZVJvbGUodXNlcklkLCByb2xlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZW1vdmVNZW1iZXIodXNlcklkOiBzdHJpbmcpIHtcbiAgICB2b2lkIHRoaXMuY29udHJvbGxlcj8ucmVtb3ZlTWVtYmVyKHVzZXJJZCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgc2VuZEludml0ZSgpIHtcbiAgICBjb25zdCBhZGRyZXNzID0gdGhpcy5lbWFpbCgpLnRyaW0oKTtcbiAgICBpZiAodGhpcy5jb250cm9sbGVyICYmIGFkZHJlc3MgJiYgKGF3YWl0IHRoaXMuY29udHJvbGxlci5pbnZpdGUoYWRkcmVzcywgdGhpcy5pbnZpdGVSb2xlKCkpKSkgdGhpcy5lbWFpbC5zZXQoJycpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlc2VuZChpZDogc3RyaW5nKSB7XG4gICAgdm9pZCB0aGlzLmNvbnRyb2xsZXI/LnJlc2VuZEludml0YXRpb24oaWQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJldm9rZShpZDogc3RyaW5nKSB7XG4gICAgdm9pZCB0aGlzLmNvbnRyb2xsZXI/LnJldm9rZUludml0YXRpb24oaWQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFzeW5jIGxlYXZlKCkge1xuICAgIGlmICh0aGlzLmNvbnRyb2xsZXIgJiYgKGF3YWl0IHRoaXMuY29udHJvbGxlci5sZWF2ZSgpKSkgdGhpcy5leGl0LmVtaXQoJ2xlZnQnKTtcbiAgICB0aGlzLmNvbmZpcm0uc2V0KG51bGwpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFzeW5jIHJlbW92ZSgpIHtcbiAgICBjb25zdCBjb250cm9sbGVyID0gdGhpcy5jb250cm9sbGVyO1xuICAgIGlmICghY29udHJvbGxlcikgcmV0dXJuO1xuICAgIGlmICh0aGlzLm5lZWRzUGFzc3dvcmQoKSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgdGhpcy5hdGxhcy5yZWF1dGhlbnRpY2F0ZSh0aGlzLnBhc3N3b3JkKCkpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMucGFzc3dvcmQuc2V0KCcnKTtcbiAgICAgIHRoaXMubmVlZHNQYXNzd29yZC5zZXQoZmFsc2UpO1xuICAgIH1cbiAgICBpZiAoYXdhaXQgY29udHJvbGxlci5kZWxldGVPcmdhbml6YXRpb24oKSkge1xuICAgICAgdGhpcy5leGl0LmVtaXQoJ2RlbGV0ZWQnKTtcbiAgICAgIHRoaXMuY29uZmlybS5zZXQobnVsbCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGVyciA9IGNvbnRyb2xsZXIuZ2V0U3RhdGUoKS5lcnJvciBhcyBPcmdhbml6YXRpb25SZXF1ZXN0RXJyb3IgfCBudWxsO1xuICAgIGlmIChlcnI/Lm5lZWRzUmV2ZXJpZmljYXRpb24pIHRoaXMubmVlZHNQYXNzd29yZC5zZXQodHJ1ZSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZWRpdFJvbGUocm9sZTogUHJvZmlsZVJvbGUpIHtcbiAgICB0aGlzLmVkaXRpbmdSb2xlLnNldChyb2xlLmlkID8/IG51bGwpO1xuICAgIHRoaXMucm9sZUtleS5zZXQocm9sZS5rZXkpO1xuICAgIHRoaXMucm9sZU5hbWUuc2V0KHJvbGUubmFtZSk7XG4gICAgdGhpcy5yb2xlUGVybXMuc2V0KFsuLi4ocm9sZS5wZXJtaXNzaW9ucyA/PyBbXSldKTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNldFJvbGVGb3JtKCkge1xuICAgIHRoaXMuZWRpdGluZ1JvbGUuc2V0KG51bGwpO1xuICAgIHRoaXMucm9sZUtleS5zZXQoJycpO1xuICAgIHRoaXMucm9sZU5hbWUuc2V0KCcnKTtcbiAgICB0aGlzLnJvbGVQZXJtcy5zZXQoW10pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRvZ2dsZVBlcm1pc3Npb24oa2V5OiBzdHJpbmcsIG9uOiBib29sZWFuKSB7XG4gICAgdGhpcy5yb2xlUGVybXMudXBkYXRlKChjdXJyZW50KSA9PiAob24gPyBbLi4uY3VycmVudCwga2V5XSA6IGN1cnJlbnQuZmlsdGVyKChrKSA9PiBrICE9PSBrZXkpKSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgc2F2ZVJvbGUoKSB7XG4gICAgY29uc3QgY29udHJvbGxlciA9IHRoaXMuY29udHJvbGxlcjtcbiAgICBpZiAoIWNvbnRyb2xsZXIpIHJldHVybjtcbiAgICBjb25zdCBlZGl0aW5nID0gdGhpcy5lZGl0aW5nUm9sZSgpO1xuICAgIGNvbnN0IG91dCA9IGVkaXRpbmdcbiAgICAgID8gYXdhaXQgY29udHJvbGxlci51cGRhdGVSb2xlKGVkaXRpbmcsIHsgbmFtZTogdGhpcy5yb2xlTmFtZSgpLnRyaW0oKSwgcGVybWlzc2lvbnM6IHRoaXMucm9sZVBlcm1zKCkgfSlcbiAgICAgIDogYXdhaXQgY29udHJvbGxlci5jcmVhdGVSb2xlKHsga2V5OiB0aGlzLnJvbGVLZXkoKS50cmltKCksIG5hbWU6IHRoaXMucm9sZU5hbWUoKS50cmltKCksIHBlcm1pc3Npb25zOiB0aGlzLnJvbGVQZXJtcygpIH0pO1xuICAgIGlmIChvdXQpIHRoaXMucmVzZXRSb2xlRm9ybSgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRlbGV0ZVJvbGUocm9sZTogUHJvZmlsZVJvbGUpIHtcbiAgICBpZiAocm9sZS5pZCkgdm9pZCB0aGlzLmNvbnRyb2xsZXI/LmRlbGV0ZVJvbGUocm9sZS5pZCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY2FuY2VsKCkge1xuICAgIHRoaXMuY29uZmlybS5zZXQobnVsbCk7XG4gICAgdGhpcy5uZWVkc1Bhc3N3b3JkLnNldChmYWxzZSk7XG4gICAgdGhpcy5wYXNzd29yZC5zZXQoJycpO1xuICB9XG59XG4iXX0=