aril 1.2.17 → 2.0.1-dev.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 (126) hide show
  1. package/boot/bridge/src/mfe-bridge.d.ts +42 -2
  2. package/boot/config/apps/index.d.ts +7 -1
  3. package/boot/config/apps/src/custom-reuse-outlet.component.d.ts +37 -0
  4. package/boot/config/apps/src/custom-route-reuse-strategy.class.d.ts +156 -0
  5. package/boot/config/apps/src/nav-link-context-menu.service.d.ts +33 -0
  6. package/boot/config/apps/src/nav-link.directive.d.ts +29 -0
  7. package/boot/config/apps/src/nav.service.d.ts +198 -0
  8. package/boot/config/apps/src/route-close.service.d.ts +9 -0
  9. package/boot/config/apps/src/safe-navigate.d.ts +17 -0
  10. package/boot/config/apps/src/tab-aware-url-serializer.d.ts +22 -0
  11. package/boot/config/plugins/src/getNgZone.d.ts +9 -1
  12. package/boot/mfe/src/app.component.d.ts +15 -4
  13. package/boot/mfe/src/isolated-location-strategy.d.ts +57 -0
  14. package/esm2022/boot/bridge/src/mfe-bridge.mjs +36 -5
  15. package/esm2022/boot/config/api/src/api.service.mjs +12 -3
  16. package/esm2022/boot/config/apps/index.mjs +8 -2
  17. package/esm2022/boot/config/apps/src/apps.service.mjs +14 -6
  18. package/esm2022/boot/config/apps/src/custom-reuse-outlet.component.mjs +207 -0
  19. package/esm2022/boot/config/apps/src/custom-route-reuse-strategy.class.mjs +540 -0
  20. package/esm2022/boot/config/apps/src/nav-link-context-menu.service.mjs +105 -0
  21. package/esm2022/boot/config/apps/src/nav-link.directive.mjs +45 -0
  22. package/esm2022/boot/config/apps/src/nav.service.mjs +675 -0
  23. package/esm2022/boot/config/apps/src/route-close.service.mjs +19 -0
  24. package/esm2022/boot/config/apps/src/safe-navigate.mjs +50 -0
  25. package/esm2022/boot/config/apps/src/tab-aware-url-serializer.mjs +50 -0
  26. package/esm2022/boot/config/plugins/src/getNgZone.mjs +13 -5
  27. package/esm2022/boot/host/src/app.component.mjs +1 -2
  28. package/esm2022/boot/host/src/bootstrap.mjs +22 -7
  29. package/esm2022/boot/mfe/src/app.component.mjs +143 -39
  30. package/esm2022/boot/mfe/src/bootstrap.mjs +197 -20
  31. package/esm2022/boot/mfe/src/isolated-location-strategy.mjs +142 -0
  32. package/esm2022/keycloak/src/auth.interceptor.mjs +17 -2
  33. package/esm2022/provider/src/prodiveHost.mjs +3 -5
  34. package/esm2022/provider/src/prodiveHostRouter.mjs +88 -9
  35. package/esm2022/theme/layout/app/expandableMenu/expandable-menu.component.mjs +81 -19
  36. package/esm2022/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.mjs +6 -4
  37. package/esm2022/theme/layout/app/general-search/general-search.component.mjs +4 -4
  38. package/esm2022/theme/layout/app/history/history-sidebar.component.mjs +6 -4
  39. package/esm2022/theme/layout/app/layout/app.layout.component.mjs +422 -20
  40. package/esm2022/theme/layout/app/layout/mfe.layout.component.mjs +24 -35
  41. package/esm2022/theme/layout/app/site-map/site-map-sidebar.component.mjs +6 -4
  42. package/esm2022/theme/layout/app/static-sidebar/static-sidebar.component.mjs +85 -27
  43. package/esm2022/theme/layout/app/topbar/app.topbar.component.mjs +3 -3
  44. package/esm2022/theme/layout/service/breadcrumb-publisher.service.mjs +86 -0
  45. package/esm2022/theme/layout/service/tab-session.service.mjs +126 -0
  46. package/esm2022/ui/table/src/table.component.mjs +10 -10
  47. package/esm2022/ui-business/ref-value/src/ref-value.component.mjs +15 -7
  48. package/esm2022/util/sync-active-tab-route/src/sync-active-tab-route.directive.mjs +29 -9
  49. package/fesm2022/aril-app.component-s14ruALV.mjs +183 -0
  50. package/fesm2022/aril-app.component-s14ruALV.mjs.map +1 -0
  51. package/fesm2022/aril-boot-bridge.mjs +35 -4
  52. package/fesm2022/aril-boot-bridge.mjs.map +1 -1
  53. package/fesm2022/aril-boot-config-api.mjs +11 -2
  54. package/fesm2022/aril-boot-config-api.mjs.map +1 -1
  55. package/fesm2022/aril-boot-config-apps.mjs +1678 -10
  56. package/fesm2022/aril-boot-config-apps.mjs.map +1 -1
  57. package/fesm2022/aril-boot-config-plugins.mjs +12 -4
  58. package/fesm2022/aril-boot-config-plugins.mjs.map +1 -1
  59. package/fesm2022/aril-boot-host.mjs +21 -7
  60. package/fesm2022/aril-boot-host.mjs.map +1 -1
  61. package/fesm2022/aril-boot-mfe-app.component-a34GeuUv.mjs +183 -0
  62. package/fesm2022/aril-boot-mfe-app.component-a34GeuUv.mjs.map +1 -0
  63. package/fesm2022/aril-boot-mfe-aril-boot-mfe-KFO_X7yR.mjs +631 -0
  64. package/fesm2022/aril-boot-mfe-aril-boot-mfe-KFO_X7yR.mjs.map +1 -0
  65. package/fesm2022/aril-boot-mfe.mjs +5 -3
  66. package/fesm2022/aril-boot-mfe.mjs.map +1 -1
  67. package/fesm2022/aril-keycloak.mjs +16 -1
  68. package/fesm2022/aril-keycloak.mjs.map +1 -1
  69. package/fesm2022/aril-provider.mjs +90 -12
  70. package/fesm2022/aril-provider.mjs.map +1 -1
  71. package/fesm2022/aril-theme-layout.mjs +2630 -2017
  72. package/fesm2022/aril-theme-layout.mjs.map +1 -1
  73. package/fesm2022/aril-ui-business-ref-value.mjs +14 -6
  74. package/fesm2022/aril-ui-business-ref-value.mjs.map +1 -1
  75. package/fesm2022/aril-ui-table.mjs +9 -9
  76. package/fesm2022/aril-ui-table.mjs.map +1 -1
  77. package/fesm2022/aril-util-sync-active-tab-route.mjs +28 -8
  78. package/fesm2022/aril-util-sync-active-tab-route.mjs.map +1 -1
  79. package/fesm2022/aril.mjs +354 -25
  80. package/fesm2022/aril.mjs.map +1 -1
  81. package/keycloak/src/auth.interceptor.d.ts +7 -0
  82. package/package.json +188 -188
  83. package/provider/src/prodiveHost.d.ts +1 -0
  84. package/theme/layout/app/expandableMenu/expandable-menu.component.d.ts +21 -4
  85. package/theme/layout/app/expandableMenu/expandable-menu.component.html +19 -5
  86. package/theme/layout/app/expandableMenu/expandable-menu.component.ts +69 -9
  87. package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.html +1 -0
  88. package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.ts +3 -1
  89. package/theme/layout/app/general-search/general-search.component.html +2 -1
  90. package/theme/layout/app/general-search/general-search.component.ts +2 -2
  91. package/theme/layout/app/history/history-sidebar.component.html +3 -1
  92. package/theme/layout/app/history/history-sidebar.component.ts +3 -1
  93. package/theme/layout/app/layout/app.layout.component.d.ts +105 -5
  94. package/theme/layout/app/layout/app.layout.component.html +102 -1
  95. package/theme/layout/app/layout/app.layout.component.scss +372 -0
  96. package/theme/layout/app/layout/app.layout.component.ts +452 -13
  97. package/theme/layout/app/layout/mfe.layout.component.d.ts +7 -5
  98. package/theme/layout/app/layout/mfe.layout.component.ts +13 -39
  99. package/theme/layout/app/site-map/site-map-sidebar.component.html +1 -0
  100. package/theme/layout/app/site-map/site-map-sidebar.component.ts +3 -1
  101. package/theme/layout/app/static-sidebar/static-sidebar.component.d.ts +26 -5
  102. package/theme/layout/app/static-sidebar/static-sidebar.component.html +11 -5
  103. package/theme/layout/app/static-sidebar/static-sidebar.component.ts +68 -13
  104. package/theme/layout/app/topbar/app.topbar.component.html +0 -1
  105. package/theme/layout/app/topbar/app.topbar.component.scss +1 -1
  106. package/theme/layout/service/breadcrumb-publisher.service.d.ts +24 -0
  107. package/theme/layout/service/breadcrumb-publisher.service.ts +95 -0
  108. package/theme/layout/service/tab-session.service.d.ts +52 -0
  109. package/theme/layout/service/tab-session.service.ts +138 -0
  110. package/theme/styles/layout/_breadcrumb.scss +95 -0
  111. package/theme/styles/layout/_content.scss +2 -2
  112. package/ui/table/src/table.component.d.ts +2 -2
  113. package/ui-business/ref-value/src/ref-value.component.d.ts +4 -2
  114. package/util/sync-active-tab-route/src/sync-active-tab-route.directive.d.ts +15 -2
  115. package/boot/config/apps/src/reuse-strategy.d.ts +0 -4
  116. package/esm2022/boot/config/apps/src/reuse-strategy.mjs +0 -9
  117. package/esm2022/theme/layout/app/breadcrumb/app.breadcrumb.component.mjs +0 -107
  118. package/fesm2022/aril-app.component-wxP3y8dg.mjs +0 -81
  119. package/fesm2022/aril-app.component-wxP3y8dg.mjs.map +0 -1
  120. package/fesm2022/aril-boot-mfe-app.component-7IjAmjz0.mjs +0 -80
  121. package/fesm2022/aril-boot-mfe-app.component-7IjAmjz0.mjs.map +0 -1
  122. package/fesm2022/aril-boot-mfe-aril-boot-mfe-KXDpUyv7.mjs +0 -315
  123. package/fesm2022/aril-boot-mfe-aril-boot-mfe-KXDpUyv7.mjs.map +0 -1
  124. package/theme/layout/app/breadcrumb/app.breadcrumb.component.d.ts +0 -25
  125. package/theme/layout/app/breadcrumb/app.breadcrumb.component.html +0 -8
  126. package/theme/layout/app/breadcrumb/app.breadcrumb.component.ts +0 -127
@@ -0,0 +1,540 @@
1
+ import { DestroyRef, Injectable, effect, inject } from '@angular/core';
2
+ import { RouteCloseService } from './route-close.service';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * Tab-bazlı navigation için custom `RouteReuseStrategy`.
6
+ *
7
+ * ## Tab izolasyonu (`_tab` query param)
8
+ * `NavService` her tab'a unique `tabId` üretir ve URL'e `?_tab=<tabId>` query param
9
+ * ekler. Strategy bu param'ı route key'in parçası yapar:
10
+ * - Matcher (web component) route'lar: `mfe:${remoteName}:${tabId}`
11
+ * - Normal route'lar: `pathFromRoot` + tüm queryParams (zaten `_tab` dahil)
12
+ *
13
+ * Sonuç: aynı sayfanın iki tab'i `storedRoutes` üzerinde farklı slot'lara yazılır
14
+ * → bağımsız componentRef'ler, bağımsız state. `shouldReuseRoute` matcher karşılaştırması
15
+ * `tabId` farkını da dikkate alır — tab değişiminde fresh component oluşur.
16
+ *
17
+ * ## Mimari: iki bağımsız instance
18
+ * Bu strategy hem `aril/boot/host/src/bootstrap.ts`'te hem her MFE remote'unun
19
+ * `aril/boot/mfe/src/bootstrap.ts`'inde provider olarak veriliyor. Class
20
+ * `providedIn: 'root'` olduğu için **her root injector'da AYRI instance** yaşar.
21
+ * İki instance bağımsız `storedRoutes` map'i tutar ve birbirinden habersizdir:
22
+ *
23
+ * - **Host instance**: matcher route'ları yönetir; key `mfe:${remoteName}:${tabId}`.
24
+ * Tab kapanıp `mfe:wdm:abc123` silindiğinde effect remote'a ait `#0/wdm/...`
25
+ * prefix'li VE `_tab=abc123` içeren child key'leri de temizler.
26
+ * - **MFE instance** (her remote'un kendi içinde): normal route'lar için çalışır.
27
+ * Query params zaten `getRouteKey` çıktısına dahil olduğu için tab izolasyonu
28
+ * doğal olarak gelir — ek özel davranış gerekmez.
29
+ *
30
+ * ## Plugin custom element preservation
31
+ * `preserveCustomElementsInTree` detached view'daki `<md-search>` gibi custom
32
+ * element'leri hidden container'a taşır — element document'tan ayrılmaz, Angular
33
+ * Elements `disconnectedCallback` 10ms destroy timer'ı tetiklenmez, plugin
34
+ * componentRef'i ve dahili state'i (form input, search sonuçları) korunur.
35
+ *
36
+ * ## Bilinen sınırlamalar
37
+ * - `shouldDetach` matcher route'lar için tabId yokken false döner (tabsız orphan
38
+ * handle oluşmasın), diğer durumlarda `true` — tüm detached route'lar cache'lenir.
39
+ * Memory leak'i sınırlamak için `NavService` tab sayısını `MAX_TABS` ile kısıtlar
40
+ * ve son tab kapatıldığında effect remote'un tüm child key'lerini temizler. Yine de
41
+ * tab içinde açılan detail/edit gibi parametreli alt sayfaların handle'ları, parent
42
+ * tab kapanana kadar storedRoutes'ta kalır.
43
+ */
44
+ export class CustomRouteReuseStrategy {
45
+ constructor() {
46
+ this.routeCloseService = inject(RouteCloseService);
47
+ this.storedRoutes = {};
48
+ /**
49
+ * `(detachedTree as any).__arilPreservedCustomEls = [...]` runtime property
50
+ * eklemek yerine handle'ı key olarak kullanan bir WeakMap tutuyoruz —
51
+ * type-safe ve handle GC'ye uğradığında bellek otomatik temizlenir.
52
+ */
53
+ this.preservedCustomEls = new WeakMap();
54
+ effect(() => {
55
+ const key = this.routeCloseService.deleteRoute().key;
56
+ if (!key)
57
+ return;
58
+ console.log(`[Strategy] cleanup tetiklendi key="${key}" storedKeys=[${Object.keys(this.storedRoutes).join(',')}]`);
59
+ this.processCleanupKey(key);
60
+ // Cross-injector yayın: `RouteCloseService` `providedIn: 'root'` olduğu için host ve
61
+ // her MFE remote'unda AYRI instance yaşar. Host'tan tetiklenen cleanup MFE strategy'sini
62
+ // otomatik uyandırmaz. Window event ile sinyal yayınlayıp MFE strategy'sinin kendi
63
+ // `storedRoutes`'unu temizlemesini sağlıyoruz (aksi takdirde MFE'de hidden container'a
64
+ // taşınmış plugin element'leri orphan kalır).
65
+ if (key.startsWith('mfe:')) {
66
+ const parsed = this.parseMatcherKey(key);
67
+ if (parsed) {
68
+ window.dispatchEvent(new CustomEvent('aril:remote-cleanup', {
69
+ detail: { remoteName: parsed.remoteName, tabId: parsed.tabId }
70
+ }));
71
+ }
72
+ }
73
+ });
74
+ // Karşı yönde (cross-injector): host strategy'nin yayınladığı `aril:remote-cleanup`
75
+ // event'ini her MFE strategy instance'ı yakalar, kendi `storedRoutes`'undaki o remote'a
76
+ // (ve varsa o tabId'ye) ait key'leri temizler. Host strategy de bu listener'a sahip
77
+ // ama kendi `#0/${remoteName}/...` key'leri olmadığı için no-op çalışır (güvenli).
78
+ const cleanupHandler = (e) => {
79
+ const detail = e.detail;
80
+ const remoteName = detail?.remoteName;
81
+ if (!remoteName)
82
+ return;
83
+ const tabId = detail?.tabId ?? null;
84
+ const prefix = `#0/${remoteName}`;
85
+ const keys = Object.keys(this.storedRoutes).filter((k) => {
86
+ if (!k.startsWith(prefix))
87
+ return false;
88
+ // tabId verildiyse SADECE o tab'a ait child handle'ları temizle. Aksi takdirde
89
+ // aynı remote'un başka tab'lerinin state'i de yanlışlıkla silinir.
90
+ if (tabId && !k.includes(`_tab=${tabId}`))
91
+ return false;
92
+ return true;
93
+ });
94
+ if (!keys.length)
95
+ return;
96
+ console.log(`[Strategy] cross-injector cleanup remoteName="${remoteName}" tabId="${tabId ?? '*'}" keys=[${keys.join(',')}]`);
97
+ for (const k of keys) {
98
+ this.cleanupStoredHandle(this.storedRoutes[k]);
99
+ delete this.storedRoutes[k];
100
+ }
101
+ };
102
+ window.addEventListener('aril:remote-cleanup', cleanupHandler);
103
+ // Strategy `providedIn: 'root'` → root injector destroy edildiğinde listener'ı kaldır.
104
+ // Pratikte uzun ömürlü ama test ortamı ve gelecekteki iframe vb. multi-window
105
+ // senaryolarda orphan listener bırakmamak için defensive.
106
+ inject(DestroyRef).onDestroy(() => window.removeEventListener('aril:remote-cleanup', cleanupHandler));
107
+ }
108
+ /**
109
+ * Injector destroy edildiğinde — hibrit modda her tab'ın child `EnvironmentInjector`'ı tab
110
+ * kapanınca `destroy()` edilir; bu strategy `providedIn:'root'` olduğundan child injector'da
111
+ * (root-scope) materialize olmuş instance da yıkılır — `storedRoutes`'ta kalan TÜM detached
112
+ * handle'ları temizler. Aksi halde `preserveCustomElementsInTree` ile hidden container'a
113
+ * taşınmış plugin custom element'leri (örn. `<md-search>`) orphan kalır (memory leak): MFE
114
+ * internal route key'leri `_tab` taşımadığı için `aril:remote-cleanup` cross-injector temizliği
115
+ * onlara ulaşamaz. Injector-destroy bazlı bu süpürme garantili ve idempotenttir.
116
+ */
117
+ ngOnDestroy() {
118
+ const keys = Object.keys(this.storedRoutes);
119
+ if (keys.length)
120
+ console.log(`[Strategy] ngOnDestroy → ${keys.length} stored handle temizleniyor (tab kapandı)`);
121
+ for (const key of keys) {
122
+ this.cleanupStoredHandle(this.storedRoutes[key]);
123
+ delete this.storedRoutes[key];
124
+ }
125
+ }
126
+ /**
127
+ * Verilen key için direct + child cleanup uygular. Effect içinden ayrıştırılmış helper.
128
+ */
129
+ processCleanupKey(key) {
130
+ // Doğrudan eşleşen key varsa onu sil.
131
+ if (this.storedRoutes[key]) {
132
+ console.log(`[Strategy] cleanup → direct match "${key}"`);
133
+ this.cleanupStoredHandle(this.storedRoutes[key]);
134
+ delete this.storedRoutes[key];
135
+ }
136
+ // Matcher key silindiğinde (`mfe:wdm:abc` gibi) o remote'a ait — varsa o tab'a ait —
137
+ // internal route handle'larını da temizle. Convention: matcher key
138
+ // `mfe:${remoteName}` veya `mfe:${remoteName}:${tabId}`, normal key'ler
139
+ // `#0/${remoteName}/...?...&_tab=${tabId}` formatında.
140
+ if (key.startsWith('mfe:')) {
141
+ const parsed = this.parseMatcherKey(key);
142
+ if (!parsed)
143
+ return;
144
+ const { remoteName, tabId } = parsed;
145
+ const prefix = `#0/${remoteName}`;
146
+ for (const k of Object.keys(this.storedRoutes)) {
147
+ if (!k.startsWith(prefix))
148
+ continue;
149
+ // tabId verildiyse sadece o tab'a ait child handle'ları temizle
150
+ if (tabId && !k.includes(`_tab=${tabId}`))
151
+ continue;
152
+ console.log(`[Strategy] cleanup → child match "${k}"`);
153
+ this.cleanupStoredHandle(this.storedRoutes[k]);
154
+ delete this.storedRoutes[k];
155
+ }
156
+ }
157
+ }
158
+ /**
159
+ * `mfe:${remoteName}` veya `mfe:${remoteName}:${tabId}` formatındaki matcher key'i parse eder.
160
+ * tabId yoksa `null` döner (geriye dönük uyumluluk: legacy çağrılar tabId vermeyebilir).
161
+ */
162
+ parseMatcherKey(key) {
163
+ if (!key.startsWith('mfe:'))
164
+ return null;
165
+ const tail = key.substring(4);
166
+ const colonIdx = tail.indexOf(':');
167
+ return {
168
+ remoteName: colonIdx === -1 ? tail : tail.substring(0, colonIdx),
169
+ tabId: colonIdx === -1 ? null : tail.substring(colonIdx + 1)
170
+ };
171
+ }
172
+ /**
173
+ * Stored handle'ı tamamen temizle: hidden container'daki preserved element'leri (plugin
174
+ * custom element'leri + ArilWebComponentWrapper'ın `<app-xxx>` element'i) DOM'dan kaldır,
175
+ * sonra Angular `componentRef.destroy()` ile view tree'sini destroy et.
176
+ *
177
+ * `componentRef.destroy()` tek başına yeterli değil: Angular sadece view tree'sini bilir,
178
+ * bizim `preserveCustomElementsInTree` ve `preserveWebComponentElement` ile hidden
179
+ * container'a manuel taşınan element'lerden haberi yoktur → onlar orphan kalır.
180
+ */
181
+ cleanupStoredHandle(handle) {
182
+ if (!handle)
183
+ return; // Angular Router'ın `store(route, null)` ile bıraktığı orphan key'ler için defensive guard.
184
+ const hiddenContainer = this.getHiddenContainer();
185
+ // 1. preserveCustomElementsInTree ile taşınan plugin element'leri (örn. <md-search>)
186
+ const preserved = this.preservedCustomEls.get(handle);
187
+ if (preserved) {
188
+ for (const { el } of preserved) {
189
+ if (hiddenContainer.contains(el)) {
190
+ hiddenContainer.removeChild(el);
191
+ }
192
+ }
193
+ this.preservedCustomEls.delete(handle);
194
+ }
195
+ // 2. preserveWebComponentElement ile taşınan wrapper element'i (örn. <app-wdm>)
196
+ const componentRef = handle.componentRef;
197
+ const instance = componentRef?.instance;
198
+ if (instance?.element) {
199
+ // Flag'i temizle ki removeChild sonrası tetiklenen disconnectedCallback Application'ı
200
+ // gerçekten destroy etsin — aksi halde MFEAppElement orphan kalır (memory leak).
201
+ delete instance.element.__arilPreserveDuringDetach;
202
+ if (hiddenContainer.contains(instance.element)) {
203
+ hiddenContainer.removeChild(instance.element);
204
+ }
205
+ }
206
+ // 3. Angular view tree'sini destroy et
207
+ componentRef?.destroy();
208
+ }
209
+ /**
210
+ * Matcher (MFE) route'lar için tabId zorunlu. `getTabIdFromRoute` URL queryParams +
211
+ * history.state'i okur. Hiçbir kaynak tabId vermezse (ilk navigation interim'i) detach
212
+ * edilmez; `firstCheckForRoute` `_tab` ekleyip yeniden navigate edecek.
213
+ */
214
+ shouldDetach(route) {
215
+ if (this.isMatcherRoute(route) && !this.getTabIdFromRoute(route)) {
216
+ console.log(`[Strategy] shouldDetach key="${this.getRouteKey(route)}" → false (matcher route, tabId yok)`);
217
+ return false;
218
+ }
219
+ const routeKey = this.getRouteKey(route);
220
+ console.log(`[Strategy] shouldDetach key="${routeKey}" → true`);
221
+ return true;
222
+ }
223
+ store(route, detachedTree) {
224
+ const routeKey = this.getRouteKey(route);
225
+ console.log(`[Strategy] store key="${routeKey}" handle=`, !!detachedTree);
226
+ if (!detachedTree) {
227
+ delete this.storedRoutes[routeKey];
228
+ return;
229
+ }
230
+ if (this.isMatcherRoute(route) && !this.getTabIdFromRoute(route)) {
231
+ console.log(`[Strategy] store SKIP key="${routeKey}" (matcher route, tabId yok)`);
232
+ return;
233
+ }
234
+ this.storedRoutes[routeKey] = detachedTree;
235
+ this.preserveWebComponentElement(detachedTree);
236
+ this.preserveCustomElementsInTree(detachedTree);
237
+ }
238
+ shouldAttach(route) {
239
+ if (this.isMatcherRoute(route) && !this.getTabIdFromRoute(route)) {
240
+ console.log(`[Strategy] shouldAttach key="${this.getRouteKey(route)}" → false (matcher route, tabId yok)`);
241
+ return false;
242
+ }
243
+ const routeKey = this.getRouteKey(route);
244
+ const shouldAttach = !!this.storedRoutes[routeKey];
245
+ console.log(`[Strategy] shouldAttach key="${routeKey}" → ${shouldAttach} (storedKeys=[${Object.keys(this.storedRoutes).join(',')}])`);
246
+ return shouldAttach;
247
+ }
248
+ retrieve(route) {
249
+ if (this.isMatcherRoute(route) && !this.getTabIdFromRoute(route)) {
250
+ return null;
251
+ }
252
+ const routeKey = this.getRouteKey(route);
253
+ const handle = this.storedRoutes[routeKey];
254
+ console.log(`[Strategy] retrieve key="${routeKey}" → ${handle ? 'HAS' : 'NULL'}`);
255
+ if (handle) {
256
+ this.restoreWebComponentElement(handle);
257
+ this.restoreCustomElementsInTree(handle);
258
+ }
259
+ return handle || null;
260
+ }
261
+ /**
262
+ * Web component element'ini hidden container'a taşır (state korunması için).
263
+ *
264
+ * Element'e `__arilPreserveDuringDetach` flag'i yazılır: `removeChild` + `appendChild`
265
+ * arasında MFE custom element'in `disconnectedCallback`'i tetikleniyor; flag oradaki
266
+ * destroy timer'ını **deterministic** şekilde bypass eder. Eskiden 50ms hardcoded timer
267
+ * yarış koşulu üretiyordu (yavaş cihazlarda Application istemsiz destroy oluyordu).
268
+ */
269
+ preserveWebComponentElement(detachedTree) {
270
+ try {
271
+ const componentRef = detachedTree.componentRef;
272
+ if (!componentRef)
273
+ return;
274
+ const componentInstance = componentRef.instance;
275
+ if (!componentInstance?.element)
276
+ return;
277
+ const element = componentInstance.element;
278
+ const hiddenContainer = this.getHiddenContainer();
279
+ // Flag — disconnectedCallback bunu okuyup destroy'ı atlayacak. removeChild'tan
280
+ // ÖNCE set ediliyor ki callback ilk tetiklendiğinde flag yerinde olsun.
281
+ element.__arilPreserveDuringDetach = true;
282
+ // Eğer element zaten bir parent'a sahipse, ondan ayır
283
+ if (element.parentNode && element.parentNode !== hiddenContainer) {
284
+ element.parentNode.removeChild(element);
285
+ }
286
+ // Hidden container'a ekle (eğer zaten orada değilse)
287
+ if (!hiddenContainer.contains(element)) {
288
+ hiddenContainer.appendChild(element);
289
+ console.log('Web component element hidden container\'a taşındı (state korunuyor)');
290
+ }
291
+ }
292
+ catch (error) {
293
+ console.warn('Web component element preserve edilirken hata:', error);
294
+ }
295
+ }
296
+ /**
297
+ * Web component element'ini hidden container'dan çıkar ve component view'a eklemeyi dene.
298
+ *
299
+ * `__arilPreserveDuringDetach` flag'i temizlenir — eğer bu retrieve sonrası element
300
+ * gerçek anlamda destroy edilirse `disconnectedCallback` flag yok kabul edip Application
301
+ * cleanup'ını yürütecek.
302
+ */
303
+ restoreWebComponentElement(detachedTree) {
304
+ try {
305
+ const componentRef = detachedTree.componentRef;
306
+ if (!componentRef)
307
+ return;
308
+ const componentInstance = componentRef.instance;
309
+ if (!componentInstance?.element)
310
+ return;
311
+ const element = componentInstance.element;
312
+ const hiddenContainer = this.getHiddenContainer();
313
+ // Eğer element hidden container'da ise, çıkar
314
+ if (hiddenContainer.contains(element)) {
315
+ hiddenContainer.removeChild(element);
316
+ delete element.__arilPreserveDuringDetach;
317
+ console.log('Web component element hidden container\'dan çıkarıldı');
318
+ // Component view'ın native element'ine eklemeyi dene
319
+ const tryRestore = () => {
320
+ const vc = componentInstance.vc;
321
+ if (vc?.nativeElement) {
322
+ vc.nativeElement.appendChild(element);
323
+ console.log('Web component element component view\'a eklendi (retrieve sırasında)');
324
+ // Props'ları güncellemek için flag set et (component kendi populateProps'unu çağıracak)
325
+ componentInstance.propsUpdated = false;
326
+ return true;
327
+ }
328
+ return false;
329
+ };
330
+ // Hemen dene
331
+ if (!tryRestore()) {
332
+ // View henüz hazır değilse, bir sonraki tick'te tekrar dene
333
+ setTimeout(() => {
334
+ if (!tryRestore()) {
335
+ // Hala başarısızsa, flag set et (ngAfterViewInit'te tekrar denenecek)
336
+ componentInstance.needsRestore = true;
337
+ console.log('Web component element restore edilecek (view hazır değil, ngAfterViewInit\'te tekrar denenecek)');
338
+ }
339
+ }, 0);
340
+ }
341
+ }
342
+ }
343
+ catch (error) {
344
+ console.warn('Web component element restore edilirken hata:', error);
345
+ }
346
+ }
347
+ /**
348
+ * Global hidden container'ı oluşturur veya döndürür
349
+ */
350
+ getHiddenContainer() {
351
+ const slot = globalThis;
352
+ let container = slot.__arilWebComponentHiddenContainer;
353
+ if (!container) {
354
+ container = document.createElement('div');
355
+ container.style.display = 'none';
356
+ container.style.position = 'absolute';
357
+ container.style.left = '-9999px';
358
+ container.style.top = '-9999px';
359
+ container.style.visibility = 'hidden';
360
+ document.body.appendChild(container);
361
+ slot.__arilWebComponentHiddenContainer = container;
362
+ }
363
+ return container;
364
+ }
365
+ /**
366
+ * Detached route view'ı içindeki plugin/MFE custom element'lerini (tag adında `-`
367
+ * bulunan ve `customElements`'a kayıtlı olanları) hidden container'a taşır ve
368
+ * yerlerine birer Comment placeholder bırakır. Element document'tan ayrılmaz,
369
+ * Angular Elements `disconnectedCallback` 10ms destroy timer'ı kanat çırpmaz —
370
+ * dolayısıyla plugin'in `ComponentRef` ve dahili state'i (form değerleri, search
371
+ * sonuçları vs.) sonraki retrieve'e kadar korunur.
372
+ */
373
+ preserveCustomElementsInTree(detachedTree) {
374
+ try {
375
+ const componentRef = detachedTree.componentRef;
376
+ const rootEl = componentRef?.location?.nativeElement;
377
+ if (!rootEl)
378
+ return;
379
+ const hiddenContainer = this.getHiddenContainer();
380
+ const all = rootEl.querySelectorAll('*');
381
+ const preservedList = [];
382
+ for (const el of Array.from(all)) {
383
+ const tag = el.tagName.toLowerCase();
384
+ if (!tag.includes('-'))
385
+ continue;
386
+ if (!window.customElements?.get(tag))
387
+ continue;
388
+ const node = el;
389
+ const placeholder = document.createComment(`aril-preserved:${tag}`);
390
+ node.parentNode?.insertBefore(placeholder, node);
391
+ hiddenContainer.appendChild(node);
392
+ preservedList.push({ el: node, placeholder });
393
+ }
394
+ if (preservedList.length) {
395
+ this.preservedCustomEls.set(detachedTree, preservedList);
396
+ console.log(`[Strategy] preserveCustomElementsInTree: ${preservedList.length} element taşındı`);
397
+ }
398
+ }
399
+ catch (error) {
400
+ console.warn('Custom element preserve edilirken hata:', error);
401
+ }
402
+ }
403
+ /**
404
+ * preserveCustomElementsInTree ile taşınan custom element'leri orijinal
405
+ * placeholder'larının yerine geri koyar.
406
+ */
407
+ restoreCustomElementsInTree(detachedTree) {
408
+ try {
409
+ const preservedList = this.preservedCustomEls.get(detachedTree);
410
+ if (!preservedList?.length)
411
+ return;
412
+ for (const { el, placeholder } of preservedList) {
413
+ if (placeholder.parentNode) {
414
+ placeholder.parentNode.replaceChild(el, placeholder);
415
+ }
416
+ else {
417
+ // Placeholder ağaçtan koparılmış — parent view tamamen destroy edilmiş olabilir
418
+ // (örn. remote tab kapanmış). Element'i hidden container'da bırakmamak için cleanup.
419
+ console.warn('[Strategy] restore: placeholder parent yok, custom element cleanup ediliyor', el.tagName);
420
+ if (el.parentNode === this.getHiddenContainer()) {
421
+ this.getHiddenContainer().removeChild(el);
422
+ }
423
+ }
424
+ }
425
+ this.preservedCustomEls.delete(detachedTree);
426
+ console.log(`[Strategy] restoreCustomElementsInTree: ${preservedList.length} element geri yerleştirildi`);
427
+ }
428
+ catch (error) {
429
+ console.warn('Custom element restore edilirken hata:', error);
430
+ }
431
+ }
432
+ isMatcherRoute(route) {
433
+ return !!route?.routeConfig?.matcher;
434
+ }
435
+ getRemoteName(route) {
436
+ return route?.routeConfig?.data?.remoteName ?? null;
437
+ }
438
+ /**
439
+ * URL `?_tab=<tabId>` query param'ını oku — `NavService` her tab için unique değer üretir.
440
+ * `IsolatedLocationStrategy` sayesinde host Router'a tabsız navigation gelmez, sadece
441
+ * `tabState({_tab})`'lı tab tıklamaları gelir → `route.queryParams['_tab']` her zaman dolu.
442
+ */
443
+ getTabIdFromRoute(route) {
444
+ const v = route?.queryParams?.['_tab'];
445
+ return typeof v === 'string' && v.length > 0 ? v : null;
446
+ }
447
+ /**
448
+ * Route key'ini oluşturur. Matcher (web component) route'lar için remoteName + tabId
449
+ * bazlı key kullanılır — aynı remote'un her tab'i ayrı MFE custom element instance'ına
450
+ * sahiptir (bağımsız state).
451
+ *
452
+ * Normal route'lar için `pathFromRoot` üzerinden tam yol üretilir, ardından query params
453
+ * eklenir (zaten `_tab` dahil) — nested boş-path (`path: ''`) child route'larının aynı `""`
454
+ * key'iyle çakışmasını engeller ve aynı path'in iki tab'i farklı key alır.
455
+ */
456
+ getRouteKey(route) {
457
+ if (this.isMatcherRoute(route)) {
458
+ const remoteName = this.getRemoteName(route);
459
+ if (remoteName) {
460
+ const tabId = this.getTabIdFromRoute(route);
461
+ return tabId ? `mfe:${remoteName}:${tabId}` : `mfe:${remoteName}`;
462
+ }
463
+ }
464
+ // Tam path: kök → intermediate → self. Boş segmentleri index ile koruyarak hiyerarşi
465
+ // pozisyonunu key'e dahil ederiz (kardeş empty-path route'lar farklı key alır).
466
+ const fullPath = route.pathFromRoot
467
+ .map((r, i) => {
468
+ const seg = r.url.map((s) => s.path).join('/');
469
+ return seg.length > 0 ? seg : `#${i}`;
470
+ })
471
+ .join('/');
472
+ const queryParams = this.serializeQueryParams(route.queryParams);
473
+ const fragment = route.fragment ? '#' + route.fragment : '';
474
+ return fullPath + queryParams + fragment;
475
+ }
476
+ /**
477
+ * Query param'ları URL-safe şekilde serileştirir. `URLSearchParams` constructor'una
478
+ * doğrudan object verirsek array değerler `?ids=1,2,3` gibi tek key olarak yazılır;
479
+ * Angular Router'ın `?ids=1&ids=2&ids=3` davranışıyla uyumsuz olabilir. `append` ile
480
+ * her array elemanı ayrı entry olur ve null/undefined değerler atlanır.
481
+ */
482
+ serializeQueryParams(params) {
483
+ // `sort()` ile determinism — aynı sayfanın farklı insertion-order'lı queryParams'ı
484
+ // (deep link share, server-side render, JS-generated URL'ler) aynı route key üretir.
485
+ // Aksi takdirde `?b=1&a=2` ve `?a=2&b=1` iki ayrı handle olarak cache'lenir → tab
486
+ // izolasyonu aynı sayfanın iki ayrı instance'ını açar.
487
+ const keys = Object.keys(params).sort();
488
+ if (!keys.length)
489
+ return '';
490
+ const out = new URLSearchParams();
491
+ for (const k of keys) {
492
+ const v = params[k];
493
+ if (v == null)
494
+ continue;
495
+ if (Array.isArray(v)) {
496
+ v.forEach((vv) => vv != null && out.append(k, String(vv)));
497
+ }
498
+ else {
499
+ out.append(k, String(v));
500
+ }
501
+ }
502
+ const str = out.toString();
503
+ return str ? '?' + str : '';
504
+ }
505
+ shouldReuseRoute(future, curr) {
506
+ const futureMatcher = this.isMatcherRoute(future);
507
+ const currMatcher = this.isMatcherRoute(curr);
508
+ if (futureMatcher && currMatcher) {
509
+ const fr = this.getRemoteName(future);
510
+ const cr = this.getRemoteName(curr);
511
+ const ft = this.getTabIdFromRoute(future);
512
+ const ct = this.getTabIdFromRoute(curr);
513
+ // Aynı remote olsa bile farklı tab'lerse fresh component instantiate edilmeli —
514
+ // aksi takdirde iki tab tek componentRef'i paylaşır ve state izolasyonu kaybolur.
515
+ const shouldReuse = !!fr && fr === cr && ft === ct;
516
+ console.log(`[Strategy] shouldReuseRoute matcher → ${shouldReuse}. future=${fr}/${ft} curr=${cr}/${ct}`);
517
+ return shouldReuse;
518
+ }
519
+ if (futureMatcher !== currMatcher) {
520
+ console.log('[Strategy] shouldReuseRoute mixed → false');
521
+ return false;
522
+ }
523
+ const shouldReuse = future.routeConfig === curr.routeConfig;
524
+ const futureKey = this.getRouteKey(future);
525
+ const currKey = this.getRouteKey(curr);
526
+ const futureComp = future.routeConfig?.component?.name ?? future.component?.name ?? 'none';
527
+ const currComp = curr.routeConfig?.component?.name ?? curr.component?.name ?? 'none';
528
+ console.log(`[Strategy] shouldReuseRoute normal → ${shouldReuse}. future="${futureKey}"(${futureComp}) curr="${currKey}"(${currComp})`);
529
+ return shouldReuse;
530
+ }
531
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CustomRouteReuseStrategy, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
532
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CustomRouteReuseStrategy, providedIn: 'root' }); }
533
+ }
534
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CustomRouteReuseStrategy, decorators: [{
535
+ type: Injectable,
536
+ args: [{
537
+ providedIn: 'root'
538
+ }]
539
+ }], ctorParameters: () => [] });
540
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tLXJvdXRlLXJldXNlLXN0cmF0ZWd5LmNsYXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvYXJpbC9ib290L2NvbmZpZy9hcHBzL3NyYy9jdXN0b20tcm91dGUtcmV1c2Utc3RyYXRlZ3kuY2xhc3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFnQixVQUFVLEVBQWMsVUFBVSxFQUFhLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFJNUcsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7O0FBNkIxRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBdUNHO0FBSUgsTUFBTSxPQUFPLHdCQUF3QjtJQVdwQztRQVZBLHNCQUFpQixHQUFzQixNQUFNLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUNqRSxpQkFBWSxHQUF3QyxFQUFFLENBQUM7UUFFdkQ7Ozs7V0FJRztRQUNLLHVCQUFrQixHQUFHLElBQUksT0FBTyxFQUE0QyxDQUFDO1FBR3BGLE1BQU0sQ0FBQyxHQUFHLEVBQUU7WUFDWCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsV0FBVyxFQUFFLENBQUMsR0FBRyxDQUFDO1lBQ3JELElBQUksQ0FBQyxHQUFHO2dCQUFFLE9BQU87WUFDakIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzQ0FBc0MsR0FBRyxpQkFBaUIsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNuSCxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLENBQUM7WUFFNUIscUZBQXFGO1lBQ3JGLHlGQUF5RjtZQUN6RixtRkFBbUY7WUFDbkYsdUZBQXVGO1lBQ3ZGLDhDQUE4QztZQUM5QyxJQUFJLEdBQUcsQ0FB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TyxLQUFLLENBQUM7UUFDZCxDQUFDO1FBRUQsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLFdBQVcsS0FBSyxJQUFJLENBQUMsV0FBVyxDQUFDO1FBQzVELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDM0MsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2QyxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsV0FBVyxFQUFFLFNBQVMsRUFBRSxJQUFJLElBQUksTUFBTSxDQUFDLFNBQVMsRUFBRSxJQUFJLElBQUksTUFBTSxDQUFDO1FBQzNGLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUUsU0FBUyxFQUFFLElBQUksSUFBSSxJQUFJLENBQUMsU0FBUyxFQUFFLElBQUksSUFBSSxNQUFNLENBQUM7UUFDckYsT0FBTyxDQUFDLEdBQUcsQ0FDVix3Q0FBd0MsV0FBVyxhQUFhLFNBQVMsS0FBSyxVQUFVLFdBQVcsT0FBTyxLQUFLLFFBQVEsR0FBRyxDQUMxSCxDQUFDO1FBQ0YsT0FBTyxXQUFXLENBQUM7SUFDcEIsQ0FBQzs4R0E3Zlcsd0JBQXdCO2tIQUF4Qix3QkFBd0IsY0FGeEIsTUFBTTs7MkZBRU4sd0JBQXdCO2tCQUhwQyxVQUFVO21CQUFDO29CQUNYLFVBQVUsRUFBRSxNQUFNO2lCQUNsQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudFJlZiwgRGVzdHJveVJlZiwgRWxlbWVudFJlZiwgSW5qZWN0YWJsZSwgT25EZXN0cm95LCBlZmZlY3QsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90LCBEZXRhY2hlZFJvdXRlSGFuZGxlLCBSb3V0ZVJldXNlU3RyYXRlZ3kgfSBmcm9tICdAYW5ndWxhci9yb3V0ZXInO1xyXG5cclxuaW1wb3J0IHsgUGx1Z2luQ29uZmlnIH0gZnJvbSAnLi9pbnRlcmZhY2VzJztcclxuaW1wb3J0IHsgUm91dGVDbG9zZVNlcnZpY2UgfSBmcm9tICcuL3JvdXRlLWNsb3NlLnNlcnZpY2UnO1xyXG5cclxuLyoqXHJcbiAqIEFuZ3VsYXIgUm91dGVyIGBEZXRhY2hlZFJvdXRlSGFuZGxlYCfEsSByZXNtaSBvbGFyYWsgb3BhcXVlIChge31gKSBiaXIgdHlwZS5cclxuICogSW50ZXJuYWwgc2hhcGUnaSBwdWJsaWMgQVBJJ2RhIHlvaywgYW1hIGJpeiBwcmVzZXJ2ZS9yZXN0b3JlIGnDp2luIGNvbXBvbmVudFJlZidlXHJcbiAqIGVyacWfbWVtaXogZ2VyZWtpeW9yLiBCdSB5w7x6ZGVuIGludGVybmFsIHlhcMSxecSxIG1pbmltYWwgxZ9la2lsZGUgbW9kZWxsaXlvcnV6LlxyXG4gKi9cclxudHlwZSBJbnRlcm5hbERldGFjaGVkUm91dGVIYW5kbGUgPSBEZXRhY2hlZFJvdXRlSGFuZGxlICYge1xyXG5cdGNvbXBvbmVudFJlZj86IENvbXBvbmVudFJlZjx1bmtub3duPjtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBBcmlsV2ViQ29tcG9uZW50V3JhcHBlciBpbnN0YW5jZSfEsW5kYSBzdHJhdGVneSduaW4gZ8O2emxleWVjZcSfaSBhbGFubGFyLlxyXG4gKiBXcmFwcGVyJ2EgZG/En3J1ZGFuIGJhxJ/EsW1sxLEgb2xtYW1hayBpw6dpbiBkdWNrLXR5cGluZyBpbGUgbWluaW1hbCBpbnRlcmZhY2UuXHJcbiAqIGBuZWVkc1Jlc3RvcmVgIHZlIGBwcm9wc1VwZGF0ZWRgIGJheXJha2xhcsSxIHdyYXBwZXIgaWxlIHN0cmF0ZWd5IGFyYXPEsW5kYWtpIHPDtnpsZcWfbWU6XHJcbiAqIHN0cmF0ZWd5IGByZXRyaWV2ZWAgc8SxcmFzxLFuZGEgc2V0IGVkZXIsIHdyYXBwZXIgYG5nQWZ0ZXJWaWV3SW5pdGAndGUgdMO8a2V0aXIuXHJcbiAqL1xyXG50eXBlIFdyYXBwZXJJbnN0YW5jZSA9IHtcclxuXHRlbGVtZW50PzogSFRNTEVsZW1lbnQ7XHJcblx0dmM/OiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PjtcclxuXHRwcm9wc1VwZGF0ZWQ/OiBib29sZWFuO1xyXG5cdG5lZWRzUmVzdG9yZT86IGJvb2xlYW47XHJcbn07XHJcblxyXG5pbnRlcmZhY2UgUHJlc2VydmVkQ3VzdG9tRWwge1xyXG5cdGVsOiBIVE1MRWxlbWVudDtcclxuXHRwbGFjZWhvbGRlcjogQ29tbWVudDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFRhYi1iYXpsxLEgbmF2aWdhdGlvbiBpw6dpbiBjdXN0b20gYFJvdXRlUmV1c2VTdHJhdGVneWAuXHJcbiAqXHJcbiAqICMjIFRhYiBpem9sYXN5b251IChgX3RhYmAgcXVlcnkgcGFyYW0pXHJcbiAqIGBOYXZTZXJ2aWNlYCBoZXIgdGFiJ2EgdW5pcXVlIGB0YWJJZGAgw7xyZXRpciB2ZSBVUkwnZSBgP190YWI9PHRhYklkPmAgcXVlcnkgcGFyYW1cclxuICogZWtsZXIuIFN0cmF0ZWd5IGJ1IHBhcmFtJ8SxIHJvdXRlIGtleSdpbiBwYXLDp2FzxLEgeWFwYXI6XHJcbiAqICAtIE1hdGNoZXIgKHdlYiBjb21wb25lbnQpIHJvdXRlJ2xhcjogYG1mZToke3JlbW90ZU5hbWV9OiR7dGFiSWR9YFxyXG4gKiAgLSBOb3JtYWwgcm91dGUnbGFyOiBgcGF0aEZyb21Sb290YCArIHTDvG0gcXVlcnlQYXJhbXMgKHphdGVuIGBfdGFiYCBkYWhpbClcclxuICpcclxuICogU29udcOnOiBheW7EsSBzYXlmYW7EsW4gaWtpIHRhYidpIGBzdG9yZWRSb3V0ZXNgIMO8emVyaW5kZSBmYXJrbMSxIHNsb3QnbGFyYSB5YXrEsWzEsXJcclxuICog4oaSIGJhxJ/EsW1zxLF6IGNvbXBvbmVudFJlZidsZXIsIGJhxJ/EsW1zxLF6IHN0YXRlLiBgc2hvdWxkUmV1c2VSb3V0ZWAgbWF0Y2hlciBrYXLFn8SxbGHFn3TEsXJtYXPEsVxyXG4gKiBgdGFiSWRgIGZhcmvEsW7EsSBkYSBkaWtrYXRlIGFsxLFyIOKAlCB0YWIgZGXEn2nFn2ltaW5kZSBmcmVzaCBjb21wb25lbnQgb2x1xZ91ci5cclxuICpcclxuICogIyMgTWltYXJpOiBpa2kgYmHEn8SxbXPEsXogaW5zdGFuY2VcclxuICogQnUgc3RyYXRlZ3kgaGVtIGBhcmlsL2Jvb3QvaG9zdC9zcmMvYm9vdHN0cmFwLnRzYCd0ZSBoZW0gaGVyIE1GRSByZW1vdGUndW51blxyXG4gKiBgYXJpbC9ib290L21mZS9zcmMvYm9vdHN0cmFwLnRzYCdpbmRlIHByb3ZpZGVyIG9sYXJhayB2ZXJpbGl5b3IuIENsYXNzXHJcbiAqIGBwcm92aWRlZEluOiAncm9vdCdgIG9sZHXEn3UgacOnaW4gKipoZXIgcm9vdCBpbmplY3RvcidkYSBBWVJJIGluc3RhbmNlKiogeWHFn2FyLlxyXG4gKiDEsGtpIGluc3RhbmNlIGJhxJ/EsW1zxLF6IGBzdG9yZWRSb3V0ZXNgIG1hcCdpIHR1dGFyIHZlIGJpcmJpcmluZGVuIGhhYmVyc2l6ZGlyOlxyXG4gKlxyXG4gKiAtICoqSG9zdCBpbnN0YW5jZSoqOiBtYXRjaGVyIHJvdXRlJ2xhcsSxIHnDtm5ldGlyOyBrZXkgYG1mZToke3JlbW90ZU5hbWV9OiR7dGFiSWR9YC5cclxuICogICBUYWIga2FwYW7EsXAgYG1mZTp3ZG06YWJjMTIzYCBzaWxpbmRpxJ9pbmRlIGVmZmVjdCByZW1vdGUnYSBhaXQgYCMwL3dkbS8uLi5gXHJcbiAqICAgcHJlZml4J2xpIFZFIGBfdGFiPWFiYzEyM2AgacOnZXJlbiBjaGlsZCBrZXknbGVyaSBkZSB0ZW1pemxlci5cclxuICogLSAqKk1GRSBpbnN0YW5jZSoqIChoZXIgcmVtb3RlJ3VuIGtlbmRpIGnDp2luZGUpOiBub3JtYWwgcm91dGUnbGFyIGnDp2luIMOnYWzEscWfxLFyLlxyXG4gKiAgIFF1ZXJ5IHBhcmFtcyB6YXRlbiBgZ2V0Um91dGVLZXlgIMOnxLFrdMSxc8SxbmEgZGFoaWwgb2xkdcSfdSBpw6dpbiB0YWIgaXpvbGFzeW9udVxyXG4gKiAgIGRvxJ9hbCBvbGFyYWsgZ2VsaXIg4oCUIGVrIMO2emVsIGRhdnJhbsSxxZ8gZ2VyZWttZXouXHJcbiAqXHJcbiAqICMjIFBsdWdpbiBjdXN0b20gZWxlbWVudCBwcmVzZXJ2YXRpb25cclxuICogYHByZXNlcnZlQ3VzdG9tRWxlbWVudHNJblRyZWVgIGRldGFjaGVkIHZpZXcnZGFraSBgPG1kLXNlYXJjaD5gIGdpYmkgY3VzdG9tXHJcbiAqIGVsZW1lbnQnbGVyaSBoaWRkZW4gY29udGFpbmVyJ2EgdGHFn8SxciDigJQgZWxlbWVudCBkb2N1bWVudCd0YW4gYXlyxLFsbWF6LCBBbmd1bGFyXHJcbiAqIEVsZW1lbnRzIGBkaXNjb25uZWN0ZWRDYWxsYmFja2AgMTBtcyBkZXN0cm95IHRpbWVyJ8SxIHRldGlrbGVubWV6LCBwbHVnaW5cclxuICogY29tcG9uZW50UmVmJ2kgdmUgZGFoaWxpIHN0YXRlJ2kgKGZvcm0gaW5wdXQsIHNlYXJjaCBzb251w6dsYXLEsSkga29ydW51ci5cclxuICpcclxuICogIyMgQmlsaW5lbiBzxLFuxLFybGFtYWxhclxyXG4gKiAtIGBzaG91bGREZXRhY2hgIG1hdGNoZXIgcm91dGUnbGFyIGnDp2luIHRhYklkIHlva2tlbiBmYWxzZSBkw7ZuZXIgKHRhYnPEsXogb3JwaGFuXHJcbiAqICAgaGFuZGxlIG9sdcWfbWFzxLFuKSwgZGnEn2VyIGR1cnVtbGFyZGEgYHRydWVgIOKAlCB0w7xtIGRldGFjaGVkIHJvdXRlJ2xhciBjYWNoZSdsZW5pci5cclxuICogICBNZW1vcnkgbGVhaydpIHPEsW7EsXJsYW1hayBpw6dpbiBgTmF2U2VydmljZWAgdGFiIHNhecSxc8SxbsSxIGBNQVhfVEFCU2AgaWxlIGvEsXPEsXRsYXJcclxuICogICB2ZSBzb24gdGFiIGthcGF0xLFsZMSxxJ/EsW5kYSBlZmZlY3QgcmVtb3RlJ3VuIHTDvG0gY2hpbGQga2V5J2xlcmluaSB0ZW1pemxlci4gWWluZSBkZVxyXG4gKiAgIHRhYiBpw6dpbmRlIGHDp8SxbGFuIGRldGFpbC9lZGl0IGdpYmkgcGFyYW1ldHJlbGkgYWx0IHNheWZhbGFyxLFuIGhhbmRsZSdsYXLEsSwgcGFyZW50XHJcbiAqICAgdGFiIGthcGFuYW5hIGthZGFyIHN0b3JlZFJvdXRlcyd0YSBrYWzEsXIuXHJcbiAqL1xyXG5ASW5qZWN0YWJsZSh7XHJcblx0cHJvdmlkZWRJbjogJ3Jvb3QnXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBDdXN0b21Sb3V0ZVJldXNlU3RyYXRlZ3kgaW1wbGVtZW50cyBSb3V0ZVJldXNlU3RyYXRlZ3ksIE9uRGVzdHJveSB7XHJcblx0cm91dGVDbG9zZVNlcnZpY2U6IFJvdXRlQ2xvc2VTZXJ2aWNlID0gaW5qZWN0KFJvdXRlQ2xvc2VTZXJ2aWNlKTtcclxuXHRzdG9yZWRSb3V0ZXM6IFJlY29yZDxzdHJpbmcsIERldGFjaGVkUm91dGVIYW5kbGU+ID0ge307XHJcblxyXG5cdC8qKlxyXG5cdCAqIGAoZGV0YWNoZWRUcmVlIGFzIGFueSkuX19hcmlsUHJlc2VydmVkQ3VzdG9tRWxzID0gWy4uLl1gIHJ1bnRpbWUgcHJvcGVydHlcclxuXHQgKiBla2xlbWVrIHllcmluZSBoYW5kbGUnxLEga2V5IG9sYXJhayBrdWxsYW5hbiBiaXIgV2Vha01hcCB0dXR1eW9ydXog4oCUXHJcblx0ICogdHlwZS1zYWZlIHZlIGhhbmRsZSBHQyd5ZSB1xJ9yYWTEscSfxLFuZGEgYmVsbGVrIG90b21hdGlrIHRlbWl6bGVuaXIuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBwcmVzZXJ2ZWRDdXN0b21FbHMgPSBuZXcgV2Vha01hcDxEZXRhY2hlZFJvdXRlSGFuZGxlLCBQcmVzZXJ2ZWRDdXN0b21FbFtdPigpO1xyXG5cclxuXHRjb25zdHJ1Y3RvcigpIHtcclxuXHRcdGVmZmVjdCgoKSA9PiB7XHJcblx0XHRcdGNvbnN0IGtleSA9IHRoaXMucm91dGVDbG9zZVNlcnZpY2UuZGVsZXRlUm91dGUoKS5rZXk7XHJcblx0XHRcdGlmICgha2V5KSByZXR1cm47XHJcblx0XHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIGNsZWFudXAgdGV0aWtsZW5kaSBrZXk9XCIke2tleX1cIiBzdG9yZWRLZXlzPVske09iamVjdC5rZXlzKHRoaXMuc3RvcmVkUm91dGVzKS5qb2luKCcsJyl9XWApO1xyXG5cdFx0XHR0aGlzLnByb2Nlc3NDbGVhbnVwS2V5KGtleSk7XHJcblxyXG5cdFx0XHQvLyBDcm9zcy1pbmplY3RvciB5YXnEsW46IGBSb3V0ZUNsb3NlU2VydmljZWAgYHByb3ZpZGVkSW46ICdyb290J2Agb2xkdcSfdSBpw6dpbiBob3N0IHZlXHJcblx0XHRcdC8vIGhlciBNRkUgcmVtb3RlJ3VuZGEgQVlSSSBpbnN0YW5jZSB5YcWfYXIuIEhvc3QndGFuIHRldGlrbGVuZW4gY2xlYW51cCBNRkUgc3RyYXRlZ3knc2luaVxyXG5cdFx0XHQvLyBvdG9tYXRpayB1eWFuZMSxcm1hei4gV2luZG93IGV2ZW50IGlsZSBzaW55YWwgeWF5xLFubGF5xLFwIE1GRSBzdHJhdGVneSdzaW5pbiBrZW5kaVxyXG5cdFx0XHQvLyBgc3RvcmVkUm91dGVzYCd1bnUgdGVtaXpsZW1lc2luaSBzYcSfbMSxeW9ydXogKGFrc2kgdGFrZGlyZGUgTUZFJ2RlIGhpZGRlbiBjb250YWluZXInYVxyXG5cdFx0XHQvLyB0YcWfxLFubcSxxZ8gcGx1Z2luIGVsZW1lbnQnbGVyaSBvcnBoYW4ga2FsxLFyKS5cclxuXHRcdFx0aWYgKGtleS5zdGFydHNXaXRoKCdtZmU6JykpIHtcclxuXHRcdFx0XHRjb25zdCBwYXJzZWQgPSB0aGlzLnBhcnNlTWF0Y2hlcktleShrZXkpO1xyXG5cdFx0XHRcdGlmIChwYXJzZWQpIHtcclxuXHRcdFx0XHRcdHdpbmRvdy5kaXNwYXRjaEV2ZW50KFxyXG5cdFx0XHRcdFx0XHRuZXcgQ3VzdG9tRXZlbnQoJ2FyaWw6cmVtb3RlLWNsZWFudXAnLCB7XHJcblx0XHRcdFx0XHRcdFx0ZGV0YWlsOiB7IHJlbW90ZU5hbWU6IHBhcnNlZC5yZW1vdGVOYW1lLCB0YWJJZDogcGFyc2VkLnRhYklkIH1cclxuXHRcdFx0XHRcdFx0fSlcclxuXHRcdFx0XHRcdCk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9KTtcclxuXHJcblx0XHQvLyBLYXLFn8SxIHnDtm5kZSAoY3Jvc3MtaW5qZWN0b3IpOiBob3N0IHN0cmF0ZWd5J25pbiB5YXnEsW5sYWTEscSfxLEgYGFyaWw6cmVtb3RlLWNsZWFudXBgXHJcblx0XHQvLyBldmVudCdpbmkgaGVyIE1GRSBzdHJhdGVneSBpbnN0YW5jZSfEsSB5YWthbGFyLCBrZW5kaSBgc3RvcmVkUm91dGVzYCd1bmRha2kgbyByZW1vdGUnYVxyXG5cdFx0Ly8gKHZlIHZhcnNhIG8gdGFiSWQneWUpIGFpdCBrZXknbGVyaSB0ZW1pemxlci4gSG9zdCBzdHJhdGVneSBkZSBidSBsaXN0ZW5lcidhIHNhaGlwXHJcblx0XHQvLyBhbWEga2VuZGkgYCMwLyR7cmVtb3RlTmFtZX0vLi4uYCBrZXknbGVyaSBvbG1hZMSxxJ/EsSBpw6dpbiBuby1vcCDDp2FsxLHFn8SxciAoZ8O8dmVubGkpLlxyXG5cdFx0Y29uc3QgY2xlYW51cEhhbmRsZXIgPSAoZTogRXZlbnQpOiB2b2lkID0+IHtcclxuXHRcdFx0Y29uc3QgZGV0YWlsID0gKGUgYXMgQ3VzdG9tRXZlbnQ8eyByZW1vdGVOYW1lPzogc3RyaW5nOyB0YWJJZD86IHN0cmluZyB8IG51bGwgfT4pLmRldGFpbDtcclxuXHRcdFx0Y29uc3QgcmVtb3RlTmFtZSA9IGRldGFpbD8ucmVtb3RlTmFtZTtcclxuXHRcdFx0aWYgKCFyZW1vdGVOYW1lKSByZXR1cm47XHJcblx0XHRcdGNvbnN0IHRhYklkID0gZGV0YWlsPy50YWJJZCA/PyBudWxsO1xyXG5cdFx0XHRjb25zdCBwcmVmaXggPSBgIzAvJHtyZW1vdGVOYW1lfWA7XHJcblx0XHRcdGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyh0aGlzLnN0b3JlZFJvdXRlcykuZmlsdGVyKChrKSA9PiB7XHJcblx0XHRcdFx0aWYgKCFrLnN0YXJ0c1dpdGgocHJlZml4KSkgcmV0dXJuIGZhbHNlO1xyXG5cdFx0XHRcdC8vIHRhYklkIHZlcmlsZGl5c2UgU0FERUNFIG8gdGFiJ2EgYWl0IGNoaWxkIGhhbmRsZSdsYXLEsSB0ZW1pemxlLiBBa3NpIHRha2RpcmRlXHJcblx0XHRcdFx0Ly8gYXluxLEgcmVtb3RlJ3VuIGJhxZ9rYSB0YWInbGVyaW5pbiBzdGF0ZSdpIGRlIHlhbmzEscWfbMSxa2xhIHNpbGluaXIuXHJcblx0XHRcdFx0aWYgKHRhYklkICYmICFrLmluY2x1ZGVzKGBfdGFiPSR7dGFiSWR9YCkpIHJldHVybiBmYWxzZTtcclxuXHRcdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdFx0fSk7XHJcblx0XHRcdGlmICgha2V5cy5sZW5ndGgpIHJldHVybjtcclxuXHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gY3Jvc3MtaW5qZWN0b3IgY2xlYW51cCByZW1vdGVOYW1lPVwiJHtyZW1vdGVOYW1lfVwiIHRhYklkPVwiJHt0YWJJZCA/PyAnKid9XCIga2V5cz1bJHtrZXlzLmpvaW4oJywnKX1dYCk7XHJcblx0XHRcdGZvciAoY29uc3QgayBvZiBrZXlzKSB7XHJcblx0XHRcdFx0dGhpcy5jbGVhbnVwU3RvcmVkSGFuZGxlKHRoaXMuc3RvcmVkUm91dGVzW2tdKTtcclxuXHRcdFx0XHRkZWxldGUgdGhpcy5zdG9yZWRSb3V0ZXNba107XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignYXJpbDpyZW1vdGUtY2xlYW51cCcsIGNsZWFudXBIYW5kbGVyKTtcclxuXHRcdC8vIFN0cmF0ZWd5IGBwcm92aWRlZEluOiAncm9vdCdgIOKGkiByb290IGluamVjdG9yIGRlc3Ryb3kgZWRpbGRpxJ9pbmRlIGxpc3RlbmVyJ8SxIGthbGTEsXIuXHJcblx0XHQvLyBQcmF0aWt0ZSB1enVuIMO2bcO8cmzDvCBhbWEgdGVzdCBvcnRhbcSxIHZlIGdlbGVjZWt0ZWtpIGlmcmFtZSB2Yi4gbXVsdGktd2luZG93XHJcblx0XHQvLyBzZW5hcnlvbGFyZGEgb3JwaGFuIGxpc3RlbmVyIGLEsXJha21hbWFrIGnDp2luIGRlZmVuc2l2ZS5cclxuXHRcdGluamVjdChEZXN0cm95UmVmKS5vbkRlc3Ryb3koKCkgPT4gd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2FyaWw6cmVtb3RlLWNsZWFudXAnLCBjbGVhbnVwSGFuZGxlcikpO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogSW5qZWN0b3IgZGVzdHJveSBlZGlsZGnEn2luZGUg4oCUIGhpYnJpdCBtb2RkYSBoZXIgdGFiJ8SxbiBjaGlsZCBgRW52aXJvbm1lbnRJbmplY3RvcmAnxLEgdGFiXHJcblx0ICoga2FwYW7EsW5jYSBgZGVzdHJveSgpYCBlZGlsaXI7IGJ1IHN0cmF0ZWd5IGBwcm92aWRlZEluOidyb290J2Agb2xkdcSfdW5kYW4gY2hpbGQgaW5qZWN0b3InZGFcclxuXHQgKiAocm9vdC1zY29wZSkgbWF0ZXJpYWxpemUgb2xtdcWfIGluc3RhbmNlIGRhIHnEsWvEsWzEsXIg4oCUIGBzdG9yZWRSb3V0ZXNgJ3RhIGthbGFuIFTDnE0gZGV0YWNoZWRcclxuXHQgKiBoYW5kbGUnbGFyxLEgdGVtaXpsZXIuIEFrc2kgaGFsZGUgYHByZXNlcnZlQ3VzdG9tRWxlbWVudHNJblRyZWVgIGlsZSBoaWRkZW4gY29udGFpbmVyJ2FcclxuXHQgKiB0YcWfxLFubcSxxZ8gcGx1Z2luIGN1c3RvbSBlbGVtZW50J2xlcmkgKMO2cm4uIGA8bWQtc2VhcmNoPmApIG9ycGhhbiBrYWzEsXIgKG1lbW9yeSBsZWFrKTogTUZFXHJcblx0ICogaW50ZXJuYWwgcm91dGUga2V5J2xlcmkgYF90YWJgIHRhxZ/EsW1hZMSxxJ/EsSBpw6dpbiBgYXJpbDpyZW1vdGUtY2xlYW51cGAgY3Jvc3MtaW5qZWN0b3IgdGVtaXpsacSfaVxyXG5cdCAqIG9ubGFyYSB1bGHFn2FtYXouIEluamVjdG9yLWRlc3Ryb3kgYmF6bMSxIGJ1IHPDvHDDvHJtZSBnYXJhbnRpbGkgdmUgaWRlbXBvdGVudHRpci5cclxuXHQgKi9cclxuXHRuZ09uRGVzdHJveSgpOiB2b2lkIHtcclxuXHRcdGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyh0aGlzLnN0b3JlZFJvdXRlcyk7XHJcblx0XHRpZiAoa2V5cy5sZW5ndGgpIGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIG5nT25EZXN0cm95IOKGkiAke2tleXMubGVuZ3RofSBzdG9yZWQgaGFuZGxlIHRlbWl6bGVuaXlvciAodGFiIGthcGFuZMSxKWApO1xyXG5cdFx0Zm9yIChjb25zdCBrZXkgb2Yga2V5cykge1xyXG5cdFx0XHR0aGlzLmNsZWFudXBTdG9yZWRIYW5kbGUodGhpcy5zdG9yZWRSb3V0ZXNba2V5XSk7XHJcblx0XHRcdGRlbGV0ZSB0aGlzLnN0b3JlZFJvdXRlc1trZXldO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogVmVyaWxlbiBrZXkgacOnaW4gZGlyZWN0ICsgY2hpbGQgY2xlYW51cCB1eWd1bGFyLiBFZmZlY3QgacOnaW5kZW4gYXlyxLHFn3TEsXLEsWxtxLHFnyBoZWxwZXIuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBwcm9jZXNzQ2xlYW51cEtleShrZXk6IHN0cmluZyk6IHZvaWQge1xyXG5cdFx0Ly8gRG/En3J1ZGFuIGXFn2xlxZ9lbiBrZXkgdmFyc2Egb251IHNpbC5cclxuXHRcdGlmICh0aGlzLnN0b3JlZFJvdXRlc1trZXldKSB7XHJcblx0XHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIGNsZWFudXAg4oaSIGRpcmVjdCBtYXRjaCBcIiR7a2V5fVwiYCk7XHJcblx0XHRcdHRoaXMuY2xlYW51cFN0b3JlZEhhbmRsZSh0aGlzLnN0b3JlZFJvdXRlc1trZXldKTtcclxuXHRcdFx0ZGVsZXRlIHRoaXMuc3RvcmVkUm91dGVzW2tleV07XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gTWF0Y2hlciBrZXkgc2lsaW5kacSfaW5kZSAoYG1mZTp3ZG06YWJjYCBnaWJpKSBvIHJlbW90ZSdhIGFpdCDigJQgdmFyc2EgbyB0YWInYSBhaXQg4oCUXHJcblx0XHQvLyBpbnRlcm5hbCByb3V0ZSBoYW5kbGUnbGFyxLFuxLEgZGEgdGVtaXpsZS4gQ29udmVudGlvbjogbWF0Y2hlciBrZXlcclxuXHRcdC8vIGBtZmU6JHtyZW1vdGVOYW1lfWAgdmV5YSBgbWZlOiR7cmVtb3RlTmFtZX06JHt0YWJJZH1gLCBub3JtYWwga2V5J2xlclxyXG5cdFx0Ly8gYCMwLyR7cmVtb3RlTmFtZX0vLi4uPy4uLiZfdGFiPSR7dGFiSWR9YCBmb3JtYXTEsW5kYS5cclxuXHRcdGlmIChrZXkuc3RhcnRzV2l0aCgnbWZlOicpKSB7XHJcblx0XHRcdGNvbnN0IHBhcnNlZCA9IHRoaXMucGFyc2VNYXRjaGVyS2V5KGtleSk7XHJcblx0XHRcdGlmICghcGFyc2VkKSByZXR1cm47XHJcblx0XHRcdGNvbnN0IHsgcmVtb3RlTmFtZSwgdGFiSWQgfSA9IHBhcnNlZDtcclxuXHRcdFx0Y29uc3QgcHJlZml4ID0gYCMwLyR7cmVtb3RlTmFtZX1gO1xyXG5cdFx0XHRmb3IgKGNvbnN0IGsgb2YgT2JqZWN0LmtleXModGhpcy5zdG9yZWRSb3V0ZXMpKSB7XHJcblx0XHRcdFx0aWYgKCFrLnN0YXJ0c1dpdGgocHJlZml4KSkgY29udGludWU7XHJcblx0XHRcdFx0Ly8gdGFiSWQgdmVyaWxkaXlzZSBzYWRlY2UgbyB0YWInYSBhaXQgY2hpbGQgaGFuZGxlJ2xhcsSxIHRlbWl6bGVcclxuXHRcdFx0XHRpZiAodGFiSWQgJiYgIWsuaW5jbHVkZXMoYF90YWI9JHt0YWJJZH1gKSkgY29udGludWU7XHJcblx0XHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gY2xlYW51cCDihpIgY2hpbGQgbWF0Y2ggXCIke2t9XCJgKTtcclxuXHRcdFx0XHR0aGlzLmNsZWFudXBTdG9yZWRIYW5kbGUodGhpcy5zdG9yZWRSb3V0ZXNba10pO1xyXG5cdFx0XHRcdGRlbGV0ZSB0aGlzLnN0b3JlZFJvdXRlc1trXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogYG1mZToke3JlbW90ZU5hbWV9YCB2ZXlhIGBtZmU6JHtyZW1vdGVOYW1lfToke3RhYklkfWAgZm9ybWF0xLFuZGFraSBtYXRjaGVyIGtleSdpIHBhcnNlIGVkZXIuXHJcblx0ICogdGFiSWQgeW9rc2EgYG51bGxgIGTDtm5lciAoZ2VyaXllIGTDtm7DvGsgdXl1bWx1bHVrOiBsZWdhY3kgw6dhxJ9yxLFsYXIgdGFiSWQgdmVybWV5ZWJpbGlyKS5cclxuXHQgKi9cclxuXHRwcml2YXRlIHBhcnNlTWF0Y2hlcktleShrZXk6IHN0cmluZyk6IHsgcmVtb3RlTmFtZTogc3RyaW5nOyB0YWJJZDogc3RyaW5nIHwgbnVsbCB9IHwgbnVsbCB7XHJcblx0XHRpZiAoIWtleS5zdGFydHNXaXRoKCdtZmU6JykpIHJldHVybiBudWxsO1xyXG5cdFx0Y29uc3QgdGFpbCA9IGtleS5zdWJzdHJpbmcoNCk7XHJcblx0XHRjb25zdCBjb2xvbklkeCA9IHRhaWwuaW5kZXhPZignOicpO1xyXG5cdFx0cmV0dXJuIHtcclxuXHRcdFx0cmVtb3RlTmFtZTogY29sb25JZHggPT09IC0xID8gdGFpbCA6IHRhaWwuc3Vic3RyaW5nKDAsIGNvbG9uSWR4KSxcclxuXHRcdFx0dGFiSWQ6IGNvbG9uSWR4ID09PSAtMSA/IG51bGwgOiB0YWlsLnN1YnN0cmluZyhjb2xvbklkeCArIDEpXHJcblx0XHR9O1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogU3RvcmVkIGhhbmRsZSfEsSB0YW1hbWVuIHRlbWl6bGU6IGhpZGRlbiBjb250YWluZXInZGFraSBwcmVzZXJ2ZWQgZWxlbWVudCdsZXJpIChwbHVnaW5cclxuXHQgKiBjdXN0b20gZWxlbWVudCdsZXJpICsgQXJpbFdlYkNvbXBvbmVudFdyYXBwZXInxLFuIGA8YXBwLXh4eD5gIGVsZW1lbnQnaSkgRE9NJ2RhbiBrYWxkxLFyLFxyXG5cdCAqIHNvbnJhIEFuZ3VsYXIgYGNvbXBvbmVudFJlZi5kZXN0cm95KClgIGlsZSB2aWV3IHRyZWUnc2luaSBkZXN0cm95IGV0LlxyXG5cdCAqXHJcblx0ICogYGNvbXBvbmVudFJlZi5kZXN0cm95KClgIHRlayBiYcWfxLFuYSB5ZXRlcmxpIGRlxJ9pbDogQW5ndWxhciBzYWRlY2UgdmlldyB0cmVlJ3NpbmkgYmlsaXIsXHJcblx0ICogYml6aW0gYHByZXNlcnZlQ3VzdG9tRWxlbWVudHNJblRyZWVgIHZlIGBwcmVzZXJ2ZVdlYkNvbXBvbmVudEVsZW1lbnRgIGlsZSBoaWRkZW5cclxuXHQgKiBjb250YWluZXInYSBtYW51ZWwgdGHFn8SxbmFuIGVsZW1lbnQnbGVyZGVuIGhhYmVyaSB5b2t0dXIg4oaSIG9ubGFyIG9ycGhhbiBrYWzEsXIuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBjbGVhbnVwU3RvcmVkSGFuZGxlKGhhbmRsZTogRGV0YWNoZWRSb3V0ZUhhbmRsZSB8IG51bGwgfCB1bmRlZmluZWQpOiB2b2lkIHtcclxuXHRcdGlmICghaGFuZGxlKSByZXR1cm47IC8vIEFuZ3VsYXIgUm91dGVyJ8SxbiBgc3RvcmUocm91dGUsIG51bGwpYCBpbGUgYsSxcmFrdMSxxJ/EsSBvcnBoYW4ga2V5J2xlciBpw6dpbiBkZWZlbnNpdmUgZ3VhcmQuXHJcblx0XHRjb25zdCBoaWRkZW5Db250YWluZXIgPSB0aGlzLmdldEhpZGRlbkNvbnRhaW5lcigpO1xyXG5cclxuXHRcdC8vIDEuIHByZXNlcnZlQ3VzdG9tRWxlbWVudHNJblRyZWUgaWxlIHRhxZ/EsW5hbiBwbHVnaW4gZWxlbWVudCdsZXJpICjDtnJuLiA8bWQtc2VhcmNoPilcclxuXHRcdGNvbnN0IHByZXNlcnZlZCA9IHRoaXMucHJlc2VydmVkQ3VzdG9tRWxzLmdldChoYW5kbGUpO1xyXG5cdFx0aWYgKHByZXNlcnZlZCkge1xyXG5cdFx0XHRmb3IgKGNvbnN0IHsgZWwgfSBvZiBwcmVzZXJ2ZWQpIHtcclxuXHRcdFx0XHRpZiAoaGlkZGVuQ29udGFpbmVyLmNvbnRhaW5zKGVsKSkge1xyXG5cdFx0XHRcdFx0aGlkZGVuQ29udGFpbmVyLnJlbW92ZUNoaWxkKGVsKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5wcmVzZXJ2ZWRDdXN0b21FbHMuZGVsZXRlKGhhbmRsZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gMi4gcHJlc2VydmVXZWJDb21wb25lbnRFbGVtZW50IGlsZSB0YcWfxLFuYW4gd3JhcHBlciBlbGVtZW50J2kgKMO2cm4uIDxhcHAtd2RtPilcclxuXHRcdGNvbnN0IGNvbXBvbmVudFJlZiA9IChoYW5kbGUgYXMgSW50ZXJuYWxEZXRhY2hlZFJvdXRlSGFuZGxlKS5jb21wb25lbnRSZWY7XHJcblx0XHRjb25zdCBpbnN0YW5jZSA9IGNvbXBvbmVudFJlZj8uaW5zdGFuY2UgYXMgV3JhcHBlckluc3RhbmNlIHwgdW5kZWZpbmVkO1xyXG5cdFx0aWYgKGluc3RhbmNlPy5lbGVtZW50KSB7XHJcblx0XHRcdC8vIEZsYWcnaSB0ZW1pemxlIGtpIHJlbW92ZUNoaWxkIHNvbnJhc8SxIHRldGlrbGVuZW4gZGlzY29ubmVjdGVkQ2FsbGJhY2sgQXBwbGljYXRpb24nxLFcclxuXHRcdFx0Ly8gZ2Vyw6dla3RlbiBkZXN0cm95IGV0c2luIOKAlCBha3NpIGhhbGRlIE1GRUFwcEVsZW1lbnQgb3JwaGFuIGthbMSxciAobWVtb3J5IGxlYWspLlxyXG5cdFx0XHRkZWxldGUgKGluc3RhbmNlLmVsZW1lbnQgYXMgSFRNTEVsZW1lbnQgJiB7IF9fYXJpbFByZXNlcnZlRHVyaW5nRGV0YWNoPzogYm9vbGVhbiB9KS5fX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaDtcclxuXHRcdFx0aWYgKGhpZGRlbkNvbnRhaW5lci5jb250YWlucyhpbnN0YW5jZS5lbGVtZW50KSkge1xyXG5cdFx0XHRcdGhpZGRlbkNvbnRhaW5lci5yZW1vdmVDaGlsZChpbnN0YW5jZS5lbGVtZW50KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIDMuIEFuZ3VsYXIgdmlldyB0cmVlJ3NpbmkgZGVzdHJveSBldFxyXG5cdFx0Y29tcG9uZW50UmVmPy5kZXN0cm95KCk7XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBNYXRjaGVyIChNRkUpIHJvdXRlJ2xhciBpw6dpbiB0YWJJZCB6b3J1bmx1LiBgZ2V0VGFiSWRGcm9tUm91dGVgIFVSTCBxdWVyeVBhcmFtcyArXHJcblx0ICogaGlzdG9yeS5zdGF0ZSdpIG9rdXIuIEhpw6diaXIga2F5bmFrIHRhYklkIHZlcm1lenNlIChpbGsgbmF2aWdhdGlvbiBpbnRlcmltJ2kpIGRldGFjaFxyXG5cdCAqIGVkaWxtZXo7IGBmaXJzdENoZWNrRm9yUm91dGVgIGBfdGFiYCBla2xleWlwIHllbmlkZW4gbmF2aWdhdGUgZWRlY2VrLlxyXG5cdCAqL1xyXG5cdHNob3VsZERldGFjaChyb3V0ZTogQWN0aXZhdGVkUm91dGVTbmFwc2hvdCk6IGJvb2xlYW4ge1xyXG5cdFx0aWYgKHRoaXMuaXNNYXRjaGVyUm91dGUocm91dGUpICYmICF0aGlzLmdldFRhYklkRnJvbVJvdXRlKHJvdXRlKSkge1xyXG5cdFx0XHRjb25zb2xlLmxvZyhgW1N0cmF0ZWd5XSBzaG91bGREZXRhY2gga2V5PVwiJHt0aGlzLmdldFJvdXRlS2V5KHJvdXRlKX1cIiDihpIgZmFsc2UgKG1hdGNoZXIgcm91dGUsIHRhYklkIHlvaylgKTtcclxuXHRcdFx0cmV0dXJuIGZhbHNlO1xyXG5cdFx0fVxyXG5cdFx0Y29uc3Qgcm91dGVLZXkgPSB0aGlzLmdldFJvdXRlS2V5KHJvdXRlKTtcclxuXHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIHNob3VsZERldGFjaCBrZXk9XCIke3JvdXRlS2V5fVwiIOKGkiB0cnVlYCk7XHJcblx0XHRyZXR1cm4gdHJ1ZTtcclxuXHR9XHJcblxyXG5cdHN0b3JlKHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90LCBkZXRhY2hlZFRyZWU6IERldGFjaGVkUm91dGVIYW5kbGUpOiB2b2lkIHtcclxuXHRcdGNvbnN0IHJvdXRlS2V5ID0gdGhpcy5nZXRSb3V0ZUtleShyb3V0ZSk7XHJcblx0XHRjb25zb2xlLmxvZyhgW1N0cmF0ZWd5XSBzdG9yZSBrZXk9XCIke3JvdXRlS2V5fVwiIGhhbmRsZT1gLCAhIWRldGFjaGVkVHJlZSk7XHJcblx0XHRpZiAoIWRldGFjaGVkVHJlZSkge1xyXG5cdFx0XHRkZWxldGUgdGhpcy5zdG9yZWRSb3V0ZXNbcm91dGVLZXldO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblx0XHRpZiAodGhpcy5pc01hdGNoZXJSb3V0ZShyb3V0ZSkgJiYgIXRoaXMuZ2V0VGFiSWRGcm9tUm91dGUocm91dGUpKSB7XHJcblx0XHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIHN0b3JlIFNLSVAga2V5PVwiJHtyb3V0ZUtleX1cIiAobWF0Y2hlciByb3V0ZSwgdGFiSWQgeW9rKWApO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblx0XHR0aGlzLnN0b3JlZFJvdXRlc1tyb3V0ZUtleV0gPSBkZXRhY2hlZFRyZWU7XHJcblx0XHR0aGlzLnByZXNlcnZlV2ViQ29tcG9uZW50RWxlbWVudChkZXRhY2hlZFRyZWUpO1xyXG5cdFx0dGhpcy5wcmVzZXJ2ZUN1c3RvbUVsZW1lbnRzSW5UcmVlKGRldGFjaGVkVHJlZSk7XHJcblx0fVxyXG5cclxuXHRzaG91bGRBdHRhY2gocm91dGU6IEFjdGl2YXRlZFJvdXRlU25hcHNob3QpOiBib29sZWFuIHtcclxuXHRcdGlmICh0aGlzLmlzTWF0Y2hlclJvdXRlKHJvdXRlKSAmJiAhdGhpcy5nZXRUYWJJZEZyb21Sb3V0ZShyb3V0ZSkpIHtcclxuXHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gc2hvdWxkQXR0YWNoIGtleT1cIiR7dGhpcy5nZXRSb3V0ZUtleShyb3V0ZSl9XCIg4oaSIGZhbHNlIChtYXRjaGVyIHJvdXRlLCB0YWJJZCB5b2spYCk7XHJcblx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdH1cclxuXHRcdGNvbnN0IHJvdXRlS2V5ID0gdGhpcy5nZXRSb3V0ZUtleShyb3V0ZSk7XHJcblx0XHRjb25zdCBzaG91bGRBdHRhY2ggPSAhIXRoaXMuc3RvcmVkUm91dGVzW3JvdXRlS2V5XTtcclxuXHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIHNob3VsZEF0dGFjaCBrZXk9XCIke3JvdXRlS2V5fVwiIOKGkiAke3Nob3VsZEF0dGFjaH0gKHN0b3JlZEtleXM9WyR7T2JqZWN0LmtleXModGhpcy5zdG9yZWRSb3V0ZXMpLmpvaW4oJywnKX1dKWApO1xyXG5cdFx0cmV0dXJuIHNob3VsZEF0dGFjaDtcclxuXHR9XHJcblxyXG5cdHJldHJpZXZlKHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90KTogRGV0YWNoZWRSb3V0ZUhhbmRsZSB8IG51bGwge1xyXG5cdFx0aWYgKHRoaXMuaXNNYXRjaGVyUm91dGUocm91dGUpICYmICF0aGlzLmdldFRhYklkRnJvbVJvdXRlKHJvdXRlKSkge1xyXG5cdFx0XHRyZXR1cm4gbnVsbDtcclxuXHRcdH1cclxuXHRcdGNvbnN0IHJvdXRlS2V5ID0gdGhpcy5nZXRSb3V0ZUtleShyb3V0ZSk7XHJcblx0XHRjb25zdCBoYW5kbGUgPSB0aGlzLnN0b3JlZFJvdXRlc1tyb3V0ZUtleV07XHJcblx0XHRjb25zb2xlLmxvZyhgW1N0cmF0ZWd5XSByZXRyaWV2ZSBrZXk9XCIke3JvdXRlS2V5fVwiIOKGkiAke2hhbmRsZSA/ICdIQVMnIDogJ05VTEwnfWApO1xyXG5cdFx0aWYgKGhhbmRsZSkge1xyXG5cdFx0XHR0aGlzLnJlc3RvcmVXZWJDb21wb25lbnRFbGVtZW50KGhhbmRsZSk7XHJcblx0XHRcdHRoaXMucmVzdG9yZUN1c3RvbUVsZW1lbnRzSW5UcmVlKGhhbmRsZSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gaGFuZGxlIHx8IG51bGw7XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBXZWIgY29tcG9uZW50IGVsZW1lbnQnaW5pIGhpZGRlbiBjb250YWluZXInYSB0YcWfxLFyIChzdGF0ZSBrb3J1bm1hc8SxIGnDp2luKS5cclxuXHQgKlxyXG5cdCAqIEVsZW1lbnQnZSBgX19hcmlsUHJlc2VydmVEdXJpbmdEZXRhY2hgIGZsYWcnaSB5YXrEsWzEsXI6IGByZW1vdmVDaGlsZGAgKyBgYXBwZW5kQ2hpbGRgXHJcblx0ICogYXJhc8SxbmRhIE1GRSBjdXN0b20gZWxlbWVudCdpbiBgZGlzY29ubmVjdGVkQ2FsbGJhY2tgJ2kgdGV0aWtsZW5peW9yOyBmbGFnIG9yYWRha2lcclxuXHQgKiBkZXN0cm95IHRpbWVyJ8SxbsSxICoqZGV0ZXJtaW5pc3RpYyoqIMWfZWtpbGRlIGJ5cGFzcyBlZGVyLiBFc2tpZGVuIDUwbXMgaGFyZGNvZGVkIHRpbWVyXHJcblx0ICogeWFyxLHFnyBrb8WfdWx1IMO8cmV0aXlvcmR1ICh5YXZhxZ8gY2loYXpsYXJkYSBBcHBsaWNhdGlvbiBpc3RlbXNpeiBkZXN0cm95IG9sdXlvcmR1KS5cclxuXHQgKi9cclxuXHRwcml2YXRlIHByZXNlcnZlV2ViQ29tcG9uZW50RWxlbWVudChkZXRhY2hlZFRyZWU6IERldGFjaGVkUm91dGVIYW5kbGUpOiB2b2lkIHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdGNvbnN0IGNvbXBvbmVudFJlZiA9IChkZXRhY2hlZFRyZWUgYXMgSW50ZXJuYWxEZXRhY2hlZFJvdXRlSGFuZGxlKS5jb21wb25lbnRSZWY7XHJcblx0XHRcdGlmICghY29tcG9uZW50UmVmKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBjb21wb25lbnRJbnN0YW5jZSA9IGNvbXBvbmVudFJlZi5pbnN0YW5jZSBhcyBXcmFwcGVySW5zdGFuY2UgfCB1bmRlZmluZWQ7XHJcblx0XHRcdGlmICghY29tcG9uZW50SW5zdGFuY2U/LmVsZW1lbnQpIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IGVsZW1lbnQgPSBjb21wb25lbnRJbnN0YW5jZS5lbGVtZW50IGFzIEhUTUxFbGVtZW50ICYgeyBfX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaD86IGJvb2xlYW4gfTtcclxuXHRcdFx0Y29uc3QgaGlkZGVuQ29udGFpbmVyID0gdGhpcy5nZXRIaWRkZW5Db250YWluZXIoKTtcclxuXHJcblx0XHRcdC8vIEZsYWcg4oCUIGRpc2Nvbm5lY3RlZENhbGxiYWNrIGJ1bnUgb2t1eXVwIGRlc3Ryb3knxLEgYXRsYXlhY2FrLiByZW1vdmVDaGlsZCd0YW5cclxuXHRcdFx0Ly8gw5ZOQ0Ugc2V0IGVkaWxpeW9yIGtpIGNhbGxiYWNrIGlsayB0ZXRpa2xlbmRpxJ9pbmRlIGZsYWcgeWVyaW5kZSBvbHN1bi5cclxuXHRcdFx0ZWxlbWVudC5fX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaCA9IHRydWU7XHJcblxyXG5cdFx0XHQvLyBFxJ9lciBlbGVtZW50IHphdGVuIGJpciBwYXJlbnQnYSBzYWhpcHNlLCBvbmRhbiBhecSxclxyXG5cdFx0XHRpZiAoZWxlbWVudC5wYXJlbnROb2RlICYmIGVsZW1lbnQucGFyZW50Tm9kZSAhPT0gaGlkZGVuQ29udGFpbmVyKSB7XHJcblx0XHRcdFx0ZWxlbWVudC5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGVsZW1lbnQpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBIaWRkZW4gY29udGFpbmVyJ2EgZWtsZSAoZcSfZXIgemF0ZW4gb3JhZGEgZGXEn2lsc2UpXHJcblx0XHRcdGlmICghaGlkZGVuQ29udGFpbmVyLmNvbnRhaW5zKGVsZW1lbnQpKSB7XHJcblx0XHRcdFx0aGlkZGVuQ29udGFpbmVyLmFwcGVuZENoaWxkKGVsZW1lbnQpO1xyXG5cdFx0XHRcdGNvbnNvbGUubG9nKCdXZWIgY29tcG9uZW50IGVsZW1lbnQgaGlkZGVuIGNvbnRhaW5lclxcJ2EgdGHFn8SxbmTEsSAoc3RhdGUga29ydW51eW9yKScpO1xyXG5cdFx0XHR9XHJcblx0XHR9IGNhdGNoIChlcnJvcikge1xyXG5cdFx0XHRjb25zb2xlLndhcm4oJ1dlYiBjb21wb25lbnQgZWxlbWVudCBwcmVzZXJ2ZSBlZGlsaXJrZW4gaGF0YTonLCBlcnJvcik7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBXZWIgY29tcG9uZW50IGVsZW1lbnQnaW5pIGhpZGRlbiBjb250YWluZXInZGFuIMOnxLFrYXIgdmUgY29tcG9uZW50IHZpZXcnYSBla2xlbWV5aSBkZW5lLlxyXG5cdCAqXHJcblx0ICogYF9fYXJpbFByZXNlcnZlRHVyaW5nRGV0YWNoYCBmbGFnJ2kgdGVtaXpsZW5pciDigJQgZcSfZXIgYnUgcmV0cmlldmUgc29ucmFzxLEgZWxlbWVudFxyXG5cdCAqIGdlcsOnZWsgYW5sYW1kYSBkZXN0cm95IGVkaWxpcnNlIGBkaXNjb25uZWN0ZWRDYWxsYmFja2AgZmxhZyB5b2sga2FidWwgZWRpcCBBcHBsaWNhdGlvblxyXG5cdCAqIGNsZWFudXAnxLFuxLEgecO8csO8dGVjZWsuXHJcblx0ICovXHJcblx0cHJpdmF0ZSByZXN0b3JlV2ViQ29tcG9uZW50RWxlbWVudChkZXRhY2hlZFRyZWU6IERldGFjaGVkUm91dGVIYW5kbGUpOiB2b2lkIHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdGNvbnN0IGNvbXBvbmVudFJlZiA9IChkZXRhY2hlZFRyZWUgYXMgSW50ZXJuYWxEZXRhY2hlZFJvdXRlSGFuZGxlKS5jb21wb25lbnRSZWY7XHJcblx0XHRcdGlmICghY29tcG9uZW50UmVmKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBjb21wb25lbnRJbnN0YW5jZSA9IGNvbXBvbmVudFJlZi5pbnN0YW5jZSBhcyBXcmFwcGVySW5zdGFuY2UgfCB1bmRlZmluZWQ7XHJcblx0XHRcdGlmICghY29tcG9uZW50SW5zdGFuY2U/LmVsZW1lbnQpIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IGVsZW1lbnQgPSBjb21wb25lbnRJbnN0YW5jZS5lbGVtZW50IGFzIEhUTUxFbGVtZW50ICYgeyBfX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaD86IGJvb2xlYW4gfTtcclxuXHRcdFx0Y29uc3QgaGlkZGVuQ29udGFpbmVyID0gdGhpcy5nZXRIaWRkZW5Db250YWluZXIoKTtcclxuXHJcblx0XHRcdC8vIEXEn2VyIGVsZW1lbnQgaGlkZGVuIGNvbnRhaW5lcidkYSBpc2UsIMOnxLFrYXJcclxuXHRcdFx0aWYgKGhpZGRlbkNvbnRhaW5lci5jb250YWlucyhlbGVtZW50KSkge1xyXG5cdFx0XHRcdGhpZGRlbkNvbnRhaW5lci5yZW1vdmVDaGlsZChlbGVtZW50KTtcclxuXHRcdFx0XHRkZWxldGUgZWxlbWVudC5fX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaDtcclxuXHRcdFx0XHRjb25zb2xlLmxvZygnV2ViIGNvbXBvbmVudCBlbGVtZW50IGhpZGRlbiBjb250YWluZXJcXCdkYW4gw6fEsWthcsSxbGTEsScpO1xyXG5cclxuXHRcdFx0XHQvLyBDb21wb25lbnQgdmlldyfEsW4gbmF0aXZlIGVsZW1lbnQnaW5lIGVrbGVtZXlpIGRlbmVcclxuXHRcdFx0XHRjb25zdCB0cnlSZXN0b3JlID0gKCk6IGJvb2xlYW4gPT4ge1xyXG5cdFx0XHRcdFx0Y29uc3QgdmMgPSBjb21wb25lbnRJbnN0YW5jZS52YztcclxuXHRcdFx0XHRcdGlmICh2Yz8ubmF0aXZlRWxlbWVudCkge1xyXG5cdFx0XHRcdFx0XHR2Yy5uYXRpdmVFbGVtZW50LmFwcGVuZENoaWxkKGVsZW1lbnQpO1xyXG5cdFx0XHRcdFx0XHRjb25zb2xlLmxvZygnV2ViIGNvbXBvbmVudCBlbGVtZW50IGNvbXBvbmVudCB2aWV3XFwnYSBla2xlbmRpIChyZXRyaWV2ZSBzxLFyYXPEsW5kYSknKTtcclxuXHRcdFx0XHRcdFx0Ly8gUHJvcHMnbGFyxLEgZ8O8bmNlbGxlbWVrIGnDp2luIGZsYWcgc2V0IGV0IChjb21wb25lbnQga2VuZGkgcG9wdWxhdGVQcm9wcyd1bnUgw6dhxJ/EsXJhY2FrKVxyXG5cdFx0XHRcdFx0XHRjb21wb25lbnRJbnN0YW5jZS5wcm9wc1VwZGF0ZWQgPSBmYWxzZTtcclxuXHRcdFx0XHRcdFx0cmV0dXJuIHRydWU7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdFx0fTtcclxuXHJcblx0XHRcdFx0Ly8gSGVtZW4gZGVuZVxyXG5cdFx0XHRcdGlmICghdHJ5UmVzdG9yZSgpKSB7XHJcblx0XHRcdFx0XHQvLyBWaWV3IGhlbsO8eiBoYXrEsXIgZGXEn2lsc2UsIGJpciBzb25yYWtpIHRpY2sndGUgdGVrcmFyIGRlbmVcclxuXHRcdFx0XHRcdHNldFRpbWVvdXQoKCkgPT4ge1xyXG5cdFx0XHRcdFx0XHRpZiAoIXRyeVJlc3RvcmUoKSkge1xyXG5cdFx0XHRcdFx0XHRcdC8vIEhhbGEgYmHFn2FyxLFzxLF6c2EsIGZsYWcgc2V0IGV0IChuZ0FmdGVyVmlld0luaXQndGUgdGVrcmFyIGRlbmVuZWNlaylcclxuXHRcdFx0XHRcdFx0XHRjb21wb25lbnRJbnN0YW5jZS5uZWVkc1Jlc3RvcmUgPSB0cnVlO1xyXG5cdFx0XHRcdFx0XHRcdGNvbnNvbGUubG9nKCdXZWIgY29tcG9uZW50IGVsZW1lbnQgcmVzdG9yZSBlZGlsZWNlayAodmlldyBoYXrEsXIgZGXEn2lsLCBuZ0FmdGVyVmlld0luaXRcXCd0ZSB0ZWtyYXIgZGVuZW5lY2VrKScpO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9LCAwKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XHJcblx0XHRcdGNvbnNvbGUud2FybignV2ViIGNvbXBvbmVudCBlbGVtZW50IHJlc3RvcmUgZWRpbGlya2VuIGhhdGE6JywgZXJyb3IpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogR2xvYmFsIGhpZGRlbiBjb250YWluZXInxLEgb2x1xZ90dXJ1ciB2ZXlhIGTDtm5kw7xyw7xyXHJcblx0ICovXHJcblx0cHJpdmF0ZSBnZXRIaWRkZW5Db250YWluZXIoKTogSFRNTEVsZW1lbnQge1xyXG5cdFx0Y29uc3Qgc2xvdCA9IGdsb2JhbFRoaXMgYXMgdHlwZW9mIGdsb2JhbFRoaXMgJiB7IF9fYXJpbFdlYkNvbXBvbmVudEhpZGRlbkNvbnRhaW5lcj86IEhUTUxFbGVtZW50IH07XHJcblx0XHRsZXQgY29udGFpbmVyID0gc2xvdC5fX2FyaWxXZWJDb21wb25lbnRIaWRkZW5Db250YWluZXI7XHJcblx0XHRpZiAoIWNvbnRhaW5lcikge1xyXG5cdFx0XHRjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHRcdFx0Y29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XHJcblx0XHRcdGNvbnRhaW5lci5zdHlsZS5wb3NpdGlvbiA9ICdhYnNvbHV0ZSc7XHJcblx0XHRcdGNvbnRhaW5lci5zdHlsZS5sZWZ0ID0gJy05OTk5cHgnO1xyXG5cdFx0XHRjb250YWluZXIuc3R5bGUudG9wID0gJy05OTk5cHgnO1xyXG5cdFx0XHRjb250YWluZXIuc3R5bGUudmlzaWJpbGl0eSA9ICdoaWRkZW4nO1xyXG5cdFx0XHRkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XHJcblx0XHRcdHNsb3QuX19hcmlsV2ViQ29tcG9uZW50SGlkZGVuQ29udGFpbmVyID0gY29udGFpbmVyO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGNvbnRhaW5lcjtcclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIERldGFjaGVkIHJvdXRlIHZpZXcnxLEgacOnaW5kZWtpIHBsdWdpbi9NRkUgY3VzdG9tIGVsZW1lbnQnbGVyaW5pICh0YWcgYWTEsW5kYSBgLWBcclxuXHQgKiBidWx1bmFuIHZlIGBjdXN0b21FbGVtZW50c2AnYSBrYXnEsXRsxLEgb2xhbmxhcsSxKSBoaWRkZW4gY29udGFpbmVyJ2EgdGHFn8SxciB2ZVxyXG5cdCAqIHllcmxlcmluZSBiaXJlciBDb21tZW50IHBsYWNlaG9sZGVyIGLEsXJha8Sxci4gRWxlbWVudCBkb2N1bWVudCd0YW4gYXlyxLFsbWF6LFxyXG5cdCAqIEFuZ3VsYXIgRWxlbWVudHMgYGRpc2Nvbm5lY3RlZENhbGxiYWNrYCAxMG1zIGRlc3Ryb3kgdGltZXInxLEga2FuYXQgw6fEsXJwbWF6IOKAlFxyXG5cdCAqIGRvbGF5xLFzxLF5bGEgcGx1Z2luJ2luIGBDb21wb25lbnRSZWZgIHZlIGRhaGlsaSBzdGF0ZSdpIChmb3JtIGRlxJ9lcmxlcmksIHNlYXJjaFxyXG5cdCAqIHNvbnXDp2xhcsSxIHZzLikgc29ucmFraSByZXRyaWV2ZSdlIGthZGFyIGtvcnVudXIuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBwcmVzZXJ2ZUN1c3RvbUVsZW1lbnRzSW5UcmVlKGRldGFjaGVkVHJlZTogRGV0YWNoZWRSb3V0ZUhhbmRsZSk6IHZvaWQge1xyXG5cdFx0dHJ5IHtcclxuXHRcdFx0Y29uc3QgY29tcG9uZW50UmVmID0gKGRldGFjaGVkVHJlZSBhcyBJbnRlcm5hbERldGFjaGVkUm91dGVIYW5kbGUpLmNvbXBvbmVudFJlZjtcclxuXHRcdFx0Y29uc3Qgcm9vdEVsID0gY29tcG9uZW50UmVmPy5sb2NhdGlvbj8ubmF0aXZlRWxlbWVudCBhcyBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZDtcclxuXHRcdFx0aWYgKCFyb290RWwpIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IGhpZGRlbkNvbnRhaW5lciA9IHRoaXMuZ2V0SGlkZGVuQ29udGFpbmVyKCk7XHJcblx0XHRcdGNvbnN0IGFsbCA9IHJvb3RFbC5xdWVyeVNlbGVjdG9yQWxsKCcqJyk7XHJcblx0XHRcdGNvbnN0IHByZXNlcnZlZExpc3Q6IFByZXNlcnZlZEN1c3RvbUVsW10gPSBbXTtcclxuXHJcblx0XHRcdGZvciAoY29uc3QgZWwgb2YgQXJyYXkuZnJvbShhbGwpKSB7XHJcblx0XHRcdFx0Y29uc3QgdGFnID0gZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpO1xyXG5cdFx0XHRcdGlmICghdGFnLmluY2x1ZGVzKCctJykpIGNvbnRpbnVlO1xyXG5cdFx0XHRcdGlmICghd2luZG93LmN1c3RvbUVsZW1lbnRzPy5nZXQodGFnKSkgY29udGludWU7XHJcblxyXG5cdFx0XHRcdGNvbnN0IG5vZGUgPSBlbCBhcyBIVE1MRWxlbWVudDtcclxuXHRcdFx0XHRjb25zdCBwbGFjZWhvbGRlciA9IGRvY3VtZW50LmNyZWF0ZUNvbW1lbnQoYGFyaWwtcHJlc2VydmVkOiR7dGFnfWApO1xyXG5cdFx0XHRcdG5vZGUucGFyZW50Tm9kZT8uaW5zZXJ0QmVmb3JlKHBsYWNlaG9sZGVyLCBub2RlKTtcclxuXHRcdFx0XHRoaWRkZW5Db250YWluZXIuYXBwZW5kQ2hpbGQobm9kZSk7XHJcblx0XHRcdFx0cHJlc2VydmVkTGlzdC5wdXNoKHsgZWw6IG5vZGUsIHBsYWNlaG9sZGVyIH0pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRpZiAocHJlc2VydmVkTGlzdC5sZW5ndGgpIHtcclxuXHRcdFx0XHR0aGlzLnByZXNlcnZlZEN1c3RvbUVscy5zZXQoZGV0YWNoZWRUcmVlLCBwcmVzZXJ2ZWRMaXN0KTtcclxuXHRcdFx0XHRjb25zb2xlLmxvZyhgW1N0cmF0ZWd5XSBwcmVzZXJ2ZUN1c3RvbUVsZW1lbnRzSW5UcmVlOiAke3ByZXNlcnZlZExpc3QubGVuZ3RofSBlbGVtZW50IHRhxZ/EsW5kxLFgKTtcclxuXHRcdFx0fVxyXG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcclxuXHRcdFx0Y29uc29sZS53YXJuKCdDdXN0b20gZWxlbWVudCBwcmVzZXJ2ZSBlZGlsaXJrZW4gaGF0YTonLCBlcnJvcik7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBwcmVzZXJ2ZUN1c3RvbUVsZW1lbnRzSW5UcmVlIGlsZSB0YcWfxLFuYW4gY3VzdG9tIGVsZW1lbnQnbGVyaSBvcmlqaW5hbFxyXG5cdCAqIHBsYWNlaG9sZGVyJ2xhcsSxbsSxbiB5ZXJpbmUgZ2VyaSBrb3lhci5cclxuXHQgKi9cclxuXHRwcml2YXRlIHJlc3RvcmVDdXN0b21FbGVtZW50c0luVHJlZShkZXRhY2hlZFRyZWU6IERldGFjaGVkUm91dGVIYW5kbGUpOiB2b2lkIHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdGNvbnN0IHByZXNlcnZlZExpc3QgPSB0aGlzLnByZXNlcnZlZEN1c3RvbUVscy5nZXQoZGV0YWNoZWRUcmVlKTtcclxuXHRcdFx0aWYgKCFwcmVzZXJ2ZWRMaXN0Py5sZW5ndGgpIHJldHVybjtcclxuXHJcblx0XHRcdGZvciAoY29uc3QgeyBlbCwgcGxhY2Vob2xkZXIgfSBvZiBwcmVzZXJ2ZWRMaXN0KSB7XHJcblx0XHRcdFx0aWYgKHBsYWNlaG9sZGVyLnBhcmVudE5vZGUpIHtcclxuXHRcdFx0XHRcdHBsYWNlaG9sZGVyLnBhcmVudE5vZGUucmVwbGFjZUNoaWxkKGVsLCBwbGFjZWhvbGRlcik7XHJcblx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdC8vIFBsYWNlaG9sZGVyIGHEn2HDp3RhbiBrb3BhcsSxbG3EscWfIOKAlCBwYXJlbnQgdmlldyB0YW1hbWVuIGRlc3Ryb3kgZWRpbG1pxZ8gb2xhYmlsaXJcclxuXHRcdFx0XHRcdC8vICjDtnJuLiByZW1vdGUgdGFiIGthcGFubcSxxZ8pLiBFbGVtZW50J2kgaGlkZGVuIGNvbnRhaW5lcidkYSBixLFyYWttYW1hayBpw6dpbiBjbGVhbnVwLlxyXG5cdFx0XHRcdFx0Y29uc29sZS53YXJuKCdbU3RyYXRlZ3ldIHJlc3RvcmU6IHBsYWNlaG9sZGVyIHBhcmVudCB5b2ssIGN1c3RvbSBlbGVtZW50IGNsZWFudXAgZWRpbGl5b3InLCBlbC50YWdOYW1lKTtcclxuXHRcdFx0XHRcdGlmIChlbC5wYXJlbnROb2RlID09PSB0aGlzLmdldEhpZGRlbkNvbnRhaW5lcigpKSB7XHJcblx0XHRcdFx0XHRcdHRoaXMuZ2V0SGlkZGVuQ29udGFpbmVyKCkucmVtb3ZlQ2hpbGQoZWwpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHR0aGlzLnByZXNlcnZlZEN1c3RvbUVscy5kZWxldGUoZGV0YWNoZWRUcmVlKTtcclxuXHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gcmVzdG9yZUN1c3RvbUVsZW1lbnRzSW5UcmVlOiAke3ByZXNlcnZlZExpc3QubGVuZ3RofSBlbGVtZW50IGdlcmkgeWVybGXFn3RpcmlsZGlgKTtcclxuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XHJcblx0XHRcdGNvbnNvbGUud2FybignQ3VzdG9tIGVsZW1lbnQgcmVzdG9yZSBlZGlsaXJrZW4gaGF0YTonLCBlcnJvcik7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHRwcml2YXRlIGlzTWF0Y2hlclJvdXRlKHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90IHwgbnVsbCk6IGJvb2xlYW4ge1xyXG5cdFx0cmV0dXJuICEhcm91dGU/LnJvdXRlQ29uZmlnPy5tYXRjaGVyO1xyXG5cdH1cclxuXHJcblx0cHJpdmF0ZSBnZXRSZW1vdGVOYW1lKHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90IHwgbnVsbCk6IHN0cmluZyB8IG51bGwge1xyXG5cdFx0cmV0dXJuIChyb3V0ZT8ucm91dGVDb25maWc/LmRhdGEgYXMgUGx1Z2luQ29uZmlnIHwgdW5kZWZpbmVkKT8ucmVtb3RlTmFtZSA/PyBudWxsO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogVVJMIGA/X3RhYj08dGFiSWQ+YCBxdWVyeSBwYXJhbSfEsW7EsSBva3Ug4oCUIGBOYXZTZXJ2aWNlYCBoZXIgdGFiIGnDp2luIHVuaXF1ZSBkZcSfZXIgw7xyZXRpci5cclxuXHQgKiBgSXNvbGF0ZWRMb2NhdGlvblN0cmF0ZWd5YCBzYXllc2luZGUgaG9zdCBSb3V0ZXInYSB0YWJzxLF6IG5hdmlnYXRpb24gZ2VsbWV6LCBzYWRlY2VcclxuXHQgKiBgdGFiU3RhdGUoe190YWJ9KWAnbMSxIHRhYiB0xLFrbGFtYWxhcsSxIGdlbGlyIOKGkiBgcm91dGUucXVlcnlQYXJhbXNbJ190YWInXWAgaGVyIHphbWFuIGRvbHUuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBnZXRUYWJJZEZyb21Sb3V0ZShyb3V0ZTogQWN0aXZhdGVkUm91dGVTbmFwc2hvdCB8IG51bGwpOiBzdHJpbmcgfCBudWxsIHtcclxuXHRcdGNvbnN0IHYgPSByb3V0ZT8ucXVlcnlQYXJhbXM/LlsnX3RhYiddO1xyXG5cdFx0cmV0dXJuIHR5cGVvZiB2ID09PSAnc3RyaW5nJyAmJiB2Lmxlbmd0aCA+IDAgPyB2IDogbnVsbDtcclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIFJvdXRlIGtleSdpbmkgb2x1xZ90dXJ1ci4gTWF0Y2hlciAod2ViIGNvbXBvbmVudCkgcm91dGUnbGFyIGnDp2luIHJlbW90ZU5hbWUgKyB0YWJJZFxyXG5cdCAqIGJhemzEsSBrZXkga3VsbGFuxLFsxLFyIOKAlCBheW7EsSByZW1vdGUndW4gaGVyIHRhYidpIGF5csSxIE1GRSBjdXN0b20gZWxlbWVudCBpbnN0YW5jZSfEsW5hXHJcblx0ICogc2FoaXB0aXIgKGJhxJ/EsW1zxLF6IHN0YXRlKS5cclxuXHQgKlxyXG5cdCAqIE5vcm1hbCByb3V0ZSdsYXIgacOnaW4gYHBhdGhGcm9tUm9vdGAgw7x6ZXJpbmRlbiB0YW0geW9sIMO8cmV0aWxpciwgYXJkxLFuZGFuIHF1ZXJ5IHBhcmFtc1xyXG5cdCAqIGVrbGVuaXIgKHphdGVuIGBfdGFiYCBkYWhpbCkg4oCUIG5lc3RlZCBib8WfLXBhdGggKGBwYXRoOiAnJ2ApIGNoaWxkIHJvdXRlJ2xhcsSxbsSxbiBheW7EsSBgXCJcImBcclxuXHQgKiBrZXknaXlsZSDDp2FrxLHFn21hc8SxbsSxIGVuZ2VsbGVyIHZlIGF5bsSxIHBhdGgnaW4gaWtpIHRhYidpIGZhcmtsxLEga2V5IGFsxLFyLlxyXG5cdCAqL1xyXG5cdHByaXZhdGUgZ2V0Um91dGVLZXkocm91dGU6IEFjdGl2YXRlZFJvdXRlU25hcHNob3QpOiBzdHJpbmcge1xyXG5cdFx0aWYgKHRoaXMuaXNNYXRjaGVyUm91dGUocm91dGUpKSB7XHJcblx0XHRcdGNvbnN0IHJlbW90ZU5hbWUgPSB0aGlzLmdldFJlbW90ZU5hbWUocm91dGUpO1xyXG5cdFx0XHRpZiAocmVtb3RlTmFtZSkge1xyXG5cdFx0XHRcdGNvbnN0IHRhYklkID0gdGhpcy5nZXRUYWJJZEZyb21Sb3V0ZShyb3V0ZSk7XHJcblx0XHRcdFx0cmV0dXJuIHRhYklkID8gYG1mZToke3JlbW90ZU5hbWV9OiR7dGFiSWR9YCA6IGBtZmU6JHtyZW1vdGVOYW1lfWA7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdC8vIFRhbSBwYXRoOiBrw7ZrIOKGkiBpbnRlcm1lZGlhdGUg4oaSIHNlbGYuIEJvxZ8gc2VnbWVudGxlcmkgaW5kZXggaWxlIGtvcnV5YXJhayBoaXllcmFyxZ9pXHJcblx0XHQvLyBwb3ppc3lvbnVudSBrZXknZSBkYWhpbCBlZGVyaXogKGthcmRlxZ8gZW1wdHktcGF0aCByb3V0ZSdsYXIgZmFya2zEsSBrZXkgYWzEsXIpLlxyXG5cdFx0Y29uc3QgZnVsbFBhdGggPSByb3V0ZS5wYXRoRnJvbVJvb3RcclxuXHRcdFx0Lm1hcCgociwgaSkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IHNlZyA9IHIudXJsLm1hcCgocykgPT4gcy5wYXRoKS5qb2luKCcvJyk7XHJcblx0XHRcdFx0cmV0dXJuIHNlZy5sZW5ndGggPiAwID8gc2VnIDogYCMke2l9YDtcclxuXHRcdFx0fSlcclxuXHRcdFx0LmpvaW4oJy8nKTtcclxuXHRcdGNvbnN0IHF1ZXJ5UGFyYW1zID0gdGhpcy5zZXJpYWxpemVRdWVyeVBhcmFtcyhyb3V0ZS5xdWVyeVBhcmFtcyk7XHJcblx0XHRjb25zdCBmcmFnbWVudCA9IHJvdXRlLmZyYWdtZW50ID8gJyMnICsgcm91dGUuZnJhZ21lbnQgOiAnJztcclxuXHJcblx0XHRyZXR1cm4gZnVsbFBhdGggKyBxdWVyeVBhcmFtcyArIGZyYWdtZW50O1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogUXVlcnkgcGFyYW0nbGFyxLEgVVJMLXNhZmUgxZ9la2lsZGUgc2VyaWxlxZ90aXJpci4gYFVSTFNlYXJjaFBhcmFtc2AgY29uc3RydWN0b3IndW5hXHJcblx0ICogZG/En3J1ZGFuIG9iamVjdCB2ZXJpcnNlayBhcnJheSBkZcSfZXJsZXIgYD9pZHM9MSwyLDNgIGdpYmkgdGVrIGtleSBvbGFyYWsgeWF6xLFsxLFyO1xyXG5cdCAqIEFuZ3VsYXIgUm91dGVyJ8SxbiBgP2lkcz0xJmlkcz0yJmlkcz0zYCBkYXZyYW7EscWfxLF5bGEgdXl1bXN1eiBvbGFiaWxpci4gYGFwcGVuZGAgaWxlXHJcblx0ICogaGVyIGFycmF5IGVsZW1hbsSxIGF5csSxIGVudHJ5IG9sdXIgdmUgbnVsbC91bmRlZmluZWQgZGXEn2VybGVyIGF0bGFuxLFyLlxyXG5cdCAqL1xyXG5cdHByaXZhdGUgc2VyaWFsaXplUXVlcnlQYXJhbXMocGFyYW1zOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90WydxdWVyeVBhcmFtcyddKTogc3RyaW5nIHtcclxuXHRcdC8vIGBzb3J0KClgIGlsZSBkZXRlcm1pbmlzbSDigJQgYXluxLEgc2F5ZmFuxLFuIGZhcmtsxLEgaW5zZXJ0aW9uLW9yZGVyJ2zEsSBxdWVyeVBhcmFtcyfEsVxyXG5cdFx0Ly8gKGRlZXAgbGluayBzaGFyZSwgc2VydmVyLXNpZGUgcmVuZGVyLCBKUy1nZW5lcmF0ZWQgVVJMJ2xlcikgYXluxLEgcm91dGUga2V5IMO8cmV0aXIuXHJcblx0XHQvLyBBa3NpIHRha2RpcmRlIGA/Yj0xJmE9MmAgdmUgYD9hPTImYj0xYCBpa2kgYXlyxLEgaGFuZGxlIG9sYXJhayBjYWNoZSdsZW5pciDihpIgdGFiXHJcblx0XHQvLyBpem9sYXN5b251IGF5bsSxIHNheWZhbsSxbiBpa2kgYXlyxLEgaW5zdGFuY2UnxLFuxLEgYcOnYXIuXHJcblx0XHRjb25zdCBrZXlzID0gT2JqZWN0LmtleXMocGFyYW1zKS5zb3J0KCk7XHJcblx0XHRpZiAoIWtleXMubGVuZ3RoKSByZXR1cm4gJyc7XHJcblx0XHRjb25zdCBvdXQgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKCk7XHJcblx0XHRmb3IgKGNvbnN0IGsgb2Yga2V5cykge1xyXG5cdFx0XHRjb25zdCB2ID0gcGFyYW1zW2tdO1xyXG5cdFx0XHRpZiAodiA9PSBudWxsKSBjb250aW51ZTtcclxuXHRcdFx0aWYgKEFycmF5LmlzQXJyYXkodikpIHtcclxuXHRcdFx0XHR2LmZvckVhY2goKHZ2KSA9PiB2diAhPSBudWxsICYmIG91dC5hcHBlbmQoaywgU3RyaW5nKHZ2KSkpO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdG91dC5hcHBlbmQoaywgU3RyaW5nKHYpKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0Y29uc3Qgc3RyID0gb3V0LnRvU3RyaW5nKCk7XHJcblx0XHRyZXR1cm4gc3RyID8gJz8nICsgc3RyIDogJyc7XHJcblx0fVxyXG5cclxuXHRzaG91bGRSZXVzZVJvdXRlKGZ1dHVyZTogQWN0aXZhdGVkUm91dGVTbmFwc2hvdCwgY3VycjogQWN0aXZhdGVkUm91dGVTbmFwc2hvdCk6IGJvb2xlYW4ge1xyXG5cdFx0Y29uc3QgZnV0dXJlTWF0Y2hlciA9IHRoaXMuaXNNYXRjaGVyUm91dGUoZnV0dXJlKTtcclxuXHRcdGNvbnN0IGN1cnJNYXRjaGVyID0gdGhpcy5pc01hdGNoZXJSb3V0ZShjdXJyKTtcclxuXHJcblx0XHRpZiAoZnV0dXJlTWF0Y2hlciAmJiBjdXJyTWF0Y2hlcikge1xyXG5cdFx0XHRjb25zdCBmciA9IHRoaXMuZ2V0UmVtb3RlTmFtZShmdXR1cmUpO1xyXG5cdFx0XHRjb25zdCBjciA9IHRoaXMuZ2V0UmVtb3RlTmFtZShjdXJyKTtcclxuXHRcdFx0Y29uc3QgZnQgPSB0aGlzLmdldFRhYklkRnJvbVJvdXRlKGZ1dHVyZSk7XHJcblx0XHRcdGNvbnN0IGN0ID0gdGhpcy5nZXRUYWJJZEZyb21Sb3V0ZShjdXJyKTtcclxuXHRcdFx0Ly8gQXluxLEgcmVtb3RlIG9sc2EgYmlsZSBmYXJrbMSxIHRhYidsZXJzZSBmcmVzaCBjb21wb25lbnQgaW5zdGFudGlhdGUgZWRpbG1lbGkg4oCUXHJcblx0XHRcdC8vIGFrc2kgdGFrZGlyZGUgaWtpIHRhYiB0ZWsgY29tcG9uZW50UmVmJ2kgcGF5bGHFn8SxciB2ZSBzdGF0ZSBpem9sYXN5b251IGtheWJvbHVyLlxyXG5cdFx0XHRjb25zdCBzaG91bGRSZXVzZSA9ICEhZnIgJiYgZnIgPT09IGNyICYmIGZ0ID09PSBjdDtcclxuXHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gc2hvdWxkUmV1c2VSb3V0ZSBtYXRjaGVyIOKGkiAke3Nob3VsZFJldXNlfS4gZnV0dXJlPSR7ZnJ9LyR7ZnR9IGN1cnI9JHtjcn0vJHtjdH1gKTtcclxuXHRcdFx0cmV0dXJuIHNob3VsZFJldXNlO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChmdXR1cmVNYXRjaGVyICE9PSBjdXJyTWF0Y2hlcikge1xyXG5cdFx0XHRjb25zb2xlLmxvZygnW1N0cmF0ZWd5XSBzaG91bGRSZXVzZVJvdXRlIG1peGVkIOKGkiBmYWxzZScpO1xyXG5cdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHR9XHJcblxyXG5cdFx0Y29uc3Qgc2hvdWxkUmV1c2UgPSBmdXR1cmUucm91dGVDb25maWcgPT09IGN1cnIucm91dGVDb25maWc7XHJcblx0XHRjb25zdCBmdXR1cmVLZXkgPSB0aGlzLmdldFJvdXRlS2V5KGZ1dHVyZSk7XHJcblx0XHRjb25zdCBjdXJyS2V5ID0gdGhpcy5nZXRSb3V0ZUtleShjdXJyKTtcclxuXHRcdGNvbnN0IGZ1dHVyZUNvbXAgPSBmdXR1cmUucm91dGVDb25maWc/LmNvbXBvbmVudD8ubmFtZSA/PyBmdXR1cmUuY29tcG9uZW50Py5uYW1lID8/ICdub25lJztcclxuXHRcdGNvbnN0IGN1cnJDb21wID0gY3Vyci5yb3V0ZUNvbmZpZz8uY29tcG9uZW50Py5uYW1lID8/IGN1cnIuY29tcG9uZW50Py5uYW1lID8/ICdub25lJztcclxuXHRcdGNvbnNvbGUubG9nKFxyXG5cdFx0XHRgW1N0cmF0ZWd5XSBzaG91bGRSZXVzZVJvdXRlIG5vcm1hbCDihpIgJHtzaG91bGRSZXVzZX0uIGZ1dHVyZT1cIiR7ZnV0dXJlS2V5fVwiKCR7ZnV0dXJlQ29tcH0pIGN1cnI9XCIke2N1cnJLZXl9XCIoJHtjdXJyQ29tcH0pYFxyXG5cdFx0KTtcclxuXHRcdHJldHVybiBzaG91bGRSZXVzZTtcclxuXHR9XHJcbn1cclxuIl19