@decaf-ts/integrations 0.3.12 → 0.4.1-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 (196) hide show
  1. package/README.md +11 -3
  2. package/dist/integrations.cjs +445 -7
  3. package/dist/integrations.js +479 -8
  4. package/jest.config.cjs +1 -1
  5. package/lib/cjs/feature-flags/constants.cjs +16 -0
  6. package/lib/cjs/feature-flags/decorators.cjs +65 -0
  7. package/lib/cjs/feature-flags/environment.cjs +26 -0
  8. package/lib/cjs/feature-flags/index.cjs +31 -0
  9. package/lib/cjs/feature-flags/models/FeatureFlag.model.cjs +81 -0
  10. package/lib/cjs/feature-flags/models/FeatureFlagAccess.model.cjs +78 -0
  11. package/lib/cjs/feature-flags/readers/FeatureFlagReader.cjs +25 -0
  12. package/lib/cjs/feature-flags/readers/index.cjs +19 -0
  13. package/lib/cjs/feature-flags/services/FeatureFlag.service.cjs +259 -0
  14. package/lib/cjs/feature-flags/types.cjs +4 -0
  15. package/lib/cjs/feature-flags/utils.cjs +141 -0
  16. package/lib/cjs/graph/engine/execution/SwitchGraphNodeExecutor.cjs +9 -9
  17. package/lib/cjs/graph/shared/nodes/flow-control.cjs +4 -3
  18. package/lib/cjs/graph/shared/types.cjs +2 -2
  19. package/lib/cjs/index.cjs +6 -5
  20. package/lib/cjs/kibana/builders/KibanaIndexBuilder.cjs +329 -0
  21. package/lib/cjs/kibana/builders/KibanaIndexBuilderCollection.cjs +37 -0
  22. package/lib/cjs/kibana/builders/index.cjs +20 -0
  23. package/lib/cjs/kibana/helpers.cjs +20 -16
  24. package/lib/cjs/kibana/index.cjs +3 -2
  25. package/lib/cjs/kibana/types.cjs +11 -2
  26. package/lib/cjs/loader/ObjectLoader.cjs +154 -0
  27. package/lib/cjs/loader/families.cjs +78 -0
  28. package/lib/cjs/loader/index.cjs +28 -0
  29. package/lib/cjs/loader/types.cjs +4 -0
  30. package/lib/cjs/nest/decorators.cjs +17 -0
  31. package/lib/cjs/nest/index.cjs +3 -3
  32. package/lib/cjs/nest/keycloakAuthHandler.cjs +74 -35
  33. package/lib/cjs/nest/keycloakModule.cjs +7 -10
  34. package/lib/cjs/nest/types.cjs +2 -2
  35. package/lib/cjs/nest/utils.cjs +58 -50
  36. package/lib/cjs/plugins/contract.cjs +76 -0
  37. package/lib/cjs/plugins/index.cjs +35 -0
  38. package/lib/cjs/plugins/kibana/host.cjs +24 -0
  39. package/lib/cjs/plugins/kibana/index.cjs +38 -0
  40. package/lib/cjs/plugins/kibana/installer.cjs +180 -0
  41. package/lib/cjs/plugins/kibana/manifest.cjs +33 -0
  42. package/lib/cjs/plugins/kibana/templates.cjs +503 -0
  43. package/lib/cjs/plugins/kibana/types.cjs +33 -0
  44. package/lib/cjs/plugins/superset/host.cjs +26 -0
  45. package/lib/cjs/plugins/superset/index.cjs +40 -0
  46. package/lib/cjs/plugins/superset/installer.cjs +232 -0
  47. package/lib/cjs/plugins/superset/manifest.cjs +35 -0
  48. package/lib/cjs/plugins/superset/templates.cjs +844 -0
  49. package/lib/cjs/plugins/superset/types.cjs +39 -0
  50. package/lib/esm/feature-flags/constants.js +12 -0
  51. package/lib/esm/feature-flags/decorators.js +49 -0
  52. package/lib/esm/feature-flags/environment.js +18 -0
  53. package/lib/esm/feature-flags/index.js +14 -0
  54. package/lib/esm/feature-flags/models/FeatureFlag.model.js +72 -0
  55. package/lib/esm/feature-flags/models/FeatureFlagAccess.model.js +69 -0
  56. package/lib/esm/feature-flags/readers/FeatureFlagReader.js +19 -0
  57. package/lib/esm/feature-flags/readers/index.js +2 -0
  58. package/lib/esm/feature-flags/services/FeatureFlag.service.js +250 -0
  59. package/lib/esm/feature-flags/types.js +2 -0
  60. package/lib/esm/feature-flags/utils.js +126 -0
  61. package/lib/esm/graph/engine/execution/SwitchGraphNodeExecutor.js +8 -8
  62. package/lib/esm/graph/shared/nodes/flow-control.js +3 -2
  63. package/lib/esm/graph/shared/types.js +1 -1
  64. package/lib/esm/index.js +5 -4
  65. package/lib/esm/kibana/builders/KibanaIndexBuilder.js +319 -0
  66. package/lib/esm/kibana/builders/KibanaIndexBuilderCollection.js +32 -0
  67. package/lib/esm/kibana/builders/index.js +3 -0
  68. package/lib/esm/kibana/helpers.js +19 -15
  69. package/lib/esm/kibana/index.js +2 -1
  70. package/lib/esm/kibana/types.js +13 -2
  71. package/lib/esm/loader/ObjectLoader.js +115 -0
  72. package/lib/esm/loader/families.js +66 -0
  73. package/lib/esm/loader/index.js +11 -0
  74. package/lib/esm/loader/types.js +2 -0
  75. package/lib/esm/nest/decorators.js +12 -0
  76. package/lib/esm/nest/index.js +2 -2
  77. package/lib/esm/nest/keycloakAuthHandler.js +67 -35
  78. package/lib/esm/nest/keycloakModule.js +7 -10
  79. package/lib/esm/nest/types.js +1 -1
  80. package/lib/esm/nest/utils.js +55 -46
  81. package/lib/esm/plugins/contract.js +68 -0
  82. package/lib/esm/plugins/index.js +18 -0
  83. package/lib/esm/plugins/kibana/host.js +19 -0
  84. package/lib/esm/plugins/kibana/index.js +14 -0
  85. package/lib/esm/plugins/kibana/installer.js +140 -0
  86. package/lib/esm/plugins/kibana/manifest.js +32 -0
  87. package/lib/esm/plugins/kibana/templates.js +499 -0
  88. package/lib/esm/plugins/kibana/types.js +10 -0
  89. package/lib/esm/plugins/superset/host.js +21 -0
  90. package/lib/esm/plugins/superset/index.js +16 -0
  91. package/lib/esm/plugins/superset/installer.js +192 -0
  92. package/lib/esm/plugins/superset/manifest.js +30 -0
  93. package/lib/esm/plugins/superset/templates.js +840 -0
  94. package/lib/esm/plugins/superset/types.js +11 -0
  95. package/lib/types/feature-flags/constants.d.cts +11 -0
  96. package/lib/types/feature-flags/constants.d.mts +11 -0
  97. package/lib/types/feature-flags/decorators.d.cts +11 -0
  98. package/lib/types/feature-flags/decorators.d.mts +11 -0
  99. package/lib/types/feature-flags/environment.d.cts +11 -0
  100. package/lib/types/feature-flags/environment.d.mts +11 -0
  101. package/lib/types/feature-flags/index.d.cts +13 -0
  102. package/lib/types/feature-flags/index.d.mts +13 -0
  103. package/lib/types/feature-flags/models/FeatureFlag.model.d.cts +16 -0
  104. package/lib/types/feature-flags/models/FeatureFlag.model.d.mts +16 -0
  105. package/lib/types/feature-flags/models/FeatureFlagAccess.model.d.cts +15 -0
  106. package/lib/types/feature-flags/models/FeatureFlagAccess.model.d.mts +15 -0
  107. package/lib/types/feature-flags/readers/FeatureFlagReader.d.cts +10 -0
  108. package/lib/types/feature-flags/readers/FeatureFlagReader.d.mts +10 -0
  109. package/lib/types/feature-flags/readers/index.d.cts +1 -0
  110. package/lib/types/feature-flags/readers/index.d.mts +1 -0
  111. package/lib/types/feature-flags/services/FeatureFlag.service.d.cts +43 -0
  112. package/lib/types/feature-flags/services/FeatureFlag.service.d.mts +43 -0
  113. package/lib/types/feature-flags/types.d.cts +39 -0
  114. package/lib/types/feature-flags/types.d.mts +39 -0
  115. package/lib/types/feature-flags/utils.d.cts +13 -0
  116. package/lib/types/feature-flags/utils.d.mts +13 -0
  117. package/lib/types/graph/engine/execution/SwitchGraphNodeExecutor.d.cts +2 -2
  118. package/lib/types/graph/engine/execution/SwitchGraphNodeExecutor.d.mts +2 -2
  119. package/lib/types/graph/shared/types.d.cts +3 -3
  120. package/lib/types/graph/shared/types.d.mts +3 -3
  121. package/lib/types/index.d.cts +3 -2
  122. package/lib/types/index.d.mts +3 -2
  123. package/lib/types/kibana/builders/KibanaIndexBuilder.d.cts +67 -0
  124. package/lib/types/kibana/builders/KibanaIndexBuilder.d.mts +67 -0
  125. package/lib/types/kibana/builders/KibanaIndexBuilderCollection.d.cts +17 -0
  126. package/lib/types/kibana/builders/KibanaIndexBuilderCollection.d.mts +17 -0
  127. package/lib/types/kibana/builders/index.d.cts +2 -0
  128. package/lib/types/kibana/builders/index.d.mts +2 -0
  129. package/lib/types/kibana/index.d.cts +1 -0
  130. package/lib/types/kibana/index.d.mts +1 -0
  131. package/lib/types/kibana/types.d.cts +11 -0
  132. package/lib/types/kibana/types.d.mts +11 -0
  133. package/lib/types/loader/ObjectLoader.d.cts +27 -0
  134. package/lib/types/loader/ObjectLoader.d.mts +27 -0
  135. package/lib/types/loader/families.d.cts +34 -0
  136. package/lib/types/loader/families.d.mts +34 -0
  137. package/lib/types/loader/index.d.cts +10 -0
  138. package/lib/types/loader/index.d.mts +10 -0
  139. package/lib/types/loader/types.d.cts +24 -0
  140. package/lib/types/loader/types.d.mts +24 -0
  141. package/lib/types/nest/decorators.d.cts +8 -0
  142. package/lib/types/nest/decorators.d.mts +8 -0
  143. package/lib/types/nest/index.d.cts +1 -1
  144. package/lib/types/nest/index.d.mts +1 -1
  145. package/lib/types/nest/keycloakAuthHandler.d.cts +19 -42
  146. package/lib/types/nest/keycloakAuthHandler.d.mts +19 -42
  147. package/lib/types/nest/keycloakModule.d.cts +3 -5
  148. package/lib/types/nest/keycloakModule.d.mts +3 -5
  149. package/lib/types/nest/types.d.cts +2 -0
  150. package/lib/types/nest/types.d.mts +2 -0
  151. package/lib/types/nest/utils.d.cts +3 -4
  152. package/lib/types/nest/utils.d.mts +3 -4
  153. package/lib/types/plugins/contract.d.cts +180 -0
  154. package/lib/types/plugins/contract.d.mts +180 -0
  155. package/lib/types/plugins/index.d.cts +17 -0
  156. package/lib/types/plugins/index.d.mts +17 -0
  157. package/lib/types/plugins/kibana/host.d.cts +18 -0
  158. package/lib/types/plugins/kibana/host.d.mts +18 -0
  159. package/lib/types/plugins/kibana/index.d.cts +13 -0
  160. package/lib/types/plugins/kibana/index.d.mts +13 -0
  161. package/lib/types/plugins/kibana/installer.d.cts +45 -0
  162. package/lib/types/plugins/kibana/installer.d.mts +45 -0
  163. package/lib/types/plugins/kibana/manifest.d.cts +31 -0
  164. package/lib/types/plugins/kibana/manifest.d.mts +31 -0
  165. package/lib/types/plugins/kibana/templates.d.cts +12 -0
  166. package/lib/types/plugins/kibana/templates.d.mts +12 -0
  167. package/lib/types/plugins/kibana/types.d.cts +9 -0
  168. package/lib/types/plugins/kibana/types.d.mts +9 -0
  169. package/lib/types/plugins/superset/host.d.cts +20 -0
  170. package/lib/types/plugins/superset/host.d.mts +20 -0
  171. package/lib/types/plugins/superset/index.d.cts +15 -0
  172. package/lib/types/plugins/superset/index.d.mts +15 -0
  173. package/lib/types/plugins/superset/installer.d.cts +70 -0
  174. package/lib/types/plugins/superset/installer.d.mts +70 -0
  175. package/lib/types/plugins/superset/manifest.d.cts +28 -0
  176. package/lib/types/plugins/superset/manifest.d.mts +28 -0
  177. package/lib/types/plugins/superset/templates.d.cts +12 -0
  178. package/lib/types/plugins/superset/templates.d.mts +12 -0
  179. package/lib/types/plugins/superset/types.d.cts +10 -0
  180. package/lib/types/plugins/superset/types.d.mts +10 -0
  181. package/package.json +62 -3
  182. package/plugins/kibana/.gitignore +16 -0
  183. package/plugins/kibana/Dockerfile +19 -0
  184. package/plugins/kibana/docker-compose.yml +50 -0
  185. package/plugins/kibana/host/index.html +101 -0
  186. package/plugins/kibana/setup-dashboards.sh +84 -0
  187. package/plugins/superset/.gitignore +17 -0
  188. package/plugins/superset/Dockerfile +79 -0
  189. package/plugins/superset/docker-compose.yml +62 -0
  190. package/plugins/superset/host/index.html +168 -0
  191. package/plugins/superset/setup-dashboards.sh +106 -0
  192. package/plugins/superset/superset_config.py +49 -0
  193. package/lib/cjs/nest/authService.cjs +0 -91
  194. package/lib/esm/nest/authService.js +0 -86
  195. package/lib/types/nest/authService.d.cts +0 -63
  196. package/lib/types/nest/authService.d.mts +0 -63
@@ -0,0 +1,840 @@
1
+ /**
2
+ * @module integrations/plugins/superset/templates
3
+ * @summary Patch and build script templates for the Superset embed plugin.
4
+ * @description Holds all patch scripts (Python + Bash) and build scripts as
5
+ * string templates. These are materialized onto disk by the installer. Decaf
6
+ * does not generate new Superset source — it patches existing internal files
7
+ * (`superset-frontend/src/embedded/index.tsx` and
8
+ * `superset-embedded-sdk/src/index.ts`) following the reference implementation
9
+ * from DECAF-40 §9.
10
+ *
11
+ * The patch adds a `switchDashboard` method to Superset's existing Switchboard
12
+ * channel, replacing only `DashboardPage` while keeping the iframe element,
13
+ * document, contentWindow, React runtime, and message channel alive.
14
+ */
15
+ import { SUPERSET_PLUGIN_ID, SUPERSET_PLUGIN_VERSION } from "./manifest.js";
16
+ const APPLY_PATCH_PY = `#!/usr/bin/env python3
17
+
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ import re
22
+ import sys
23
+ from pathlib import Path
24
+
25
+
26
+ FRONTEND_FILE = Path("superset-frontend/src/embedded/index.tsx")
27
+ SDK_FILE = Path("superset-embedded-sdk/src/index.ts")
28
+
29
+
30
+ class PatchError(RuntimeError):
31
+ pass
32
+
33
+
34
+ def replace_once(
35
+ content: str,
36
+ old: str,
37
+ new: str,
38
+ description: str,
39
+ ) -> str:
40
+ count = content.count(old)
41
+
42
+ if count != 1:
43
+ raise PatchError(
44
+ f"{description}: expected exactly one occurrence, found {count}"
45
+ )
46
+
47
+ return content.replace(old, new, 1)
48
+
49
+
50
+ def replace_regex_once(
51
+ content: str,
52
+ pattern: re.Pattern[str],
53
+ replacement: str,
54
+ description: str,
55
+ ) -> str:
56
+ updated, count = pattern.subn(replacement, content, count=1)
57
+
58
+ if count != 1:
59
+ raise PatchError(
60
+ f"{description}: expected exactly one match, found {count}"
61
+ )
62
+
63
+ return updated
64
+
65
+
66
+ def patch_react_import(content: str) -> str:
67
+ react_import_pattern = re.compile(
68
+ r"import\\s*\\{\\s*([^}]+)\\s*\\}\\s*from\\s*'react';"
69
+ )
70
+
71
+ match = react_import_pattern.search(content)
72
+
73
+ if not match:
74
+ raise PatchError("Could not locate the named React import")
75
+
76
+ imports = [
77
+ value.strip()
78
+ for value in match.group(1).replace("\\n", " ").split(",")
79
+ if value.strip()
80
+ ]
81
+
82
+ required_imports = ["lazy", "Suspense", "useEffect", "useState"]
83
+
84
+ for required_import in required_imports:
85
+ if required_import not in imports:
86
+ imports.append(required_import)
87
+
88
+ replacement = (
89
+ "import {\\n "
90
+ + ",\\n ".join(imports)
91
+ + ",\\n} from 'react';"
92
+ )
93
+
94
+ return (
95
+ content[: match.start()]
96
+ + replacement
97
+ + content[match.end() :]
98
+ )
99
+
100
+
101
+ def locate_embedded_component(
102
+ content: str,
103
+ ) -> tuple[re.Match[str], str]:
104
+ names = [
105
+ "EmbeddedLazyDashboardPage",
106
+ "EmbededLazyDashboardPage",
107
+ ]
108
+
109
+ for name in names:
110
+ pattern = re.compile(
111
+ rf"const {name} = \\(\\) => \\{{.*?\\n\\}};\\n"
112
+ rf"(?=const EmbeddedRoute)",
113
+ re.DOTALL,
114
+ )
115
+
116
+ match = pattern.search(content)
117
+
118
+ if match:
119
+ return match, name
120
+
121
+ raise PatchError(
122
+ "Could not find EmbeddedLazyDashboardPage before EmbeddedRoute"
123
+ )
124
+
125
+
126
+ def patch_frontend(frontend_path: Path) -> None:
127
+ content = frontend_path.read_text(encoding="utf-8")
128
+
129
+ if "SUPERSET_SWITCHABLE_EMBED_PATCH" in content:
130
+ print(f"Already patched: {frontend_path}")
131
+ return
132
+
133
+ content = patch_react_import(content)
134
+
135
+ component_match, component_name = locate_embedded_component(
136
+ content
137
+ )
138
+
139
+ replacement_component = \`// SUPERSET_SWITCHABLE_EMBED_PATCH
140
+ //
141
+ // Allows the existing embedded Superset document to replace DashboardPage
142
+ // without navigating or recreating the iframe.
143
+ //
144
+ // Security:
145
+ // - This method is exposed only through Superset's existing Switchboard
146
+ // MessageChannel.
147
+ // - The host application must obtain a guest token for the target dashboard.
148
+ // - Superset's normal Allowed Domains validation remains in effect.
149
+
150
+ interface SwitchDashboardPayload {
151
+ dashboardId: string;
152
+ guestToken: string;
153
+ }
154
+
155
+ interface SwitchDashboardResult {
156
+ dashboardId: string;
157
+ accepted: true;
158
+ }
159
+
160
+ type DashboardSwitchHandler = (
161
+ payload: SwitchDashboardPayload,
162
+ ) => Promise<SwitchDashboardResult>;
163
+
164
+ let dashboardSwitchHandler: DashboardSwitchHandler | undefined;
165
+
166
+ const \${component_name} = () => {
167
+ const uiConfig = useUiConfig();
168
+
169
+ const [dashboardId, setDashboardId] = useState<string | null>(
170
+ bootstrapData.embedded!.dashboard_id,
171
+ );
172
+
173
+ useEffect(() => {
174
+ dashboardSwitchHandler = async ({
175
+ dashboardId: requestedDashboardId,
176
+ guestToken,
177
+ }: SwitchDashboardPayload) => {
178
+ const nextDashboardId = requestedDashboardId.trim();
179
+
180
+ if (!nextDashboardId) {
181
+ throw new Error(
182
+ 'dashboardId must be a non-empty string',
183
+ );
184
+ }
185
+
186
+ if (
187
+ typeof guestToken !== 'string' ||
188
+ guestToken.trim().length === 0
189
+ ) {
190
+ throw new Error(
191
+ 'guestToken must be a non-empty string',
192
+ );
193
+ }
194
+
195
+ setDashboardId(null);
196
+
197
+ displayedUnauthorizedToast = false;
198
+
199
+ setupGuestClient(guestToken);
200
+
201
+ await new Promise<void>(resolve => {
202
+ window.requestAnimationFrame(() => resolve());
203
+ });
204
+
205
+ setDashboardId(nextDashboardId);
206
+
207
+ return {
208
+ dashboardId: nextDashboardId,
209
+ accepted: true,
210
+ };
211
+ };
212
+
213
+ return () => {
214
+ dashboardSwitchHandler = undefined;
215
+ };
216
+ }, []);
217
+
218
+ useEffect(() => {
219
+ if (!uiConfig?.emitDataMasks) {
220
+ return undefined;
221
+ }
222
+
223
+ let previousDataMask = store.getState().dataMask;
224
+
225
+ return store.subscribe(() => {
226
+ const currentDataMask = store.getState().dataMask;
227
+
228
+ if (previousDataMask !== currentDataMask) {
229
+ Switchboard.emit('observeDataMask', {
230
+ ...currentDataMask,
231
+ ...getDataMaskChangeTrigger(
232
+ currentDataMask,
233
+ previousDataMask,
234
+ ),
235
+ });
236
+
237
+ previousDataMask = currentDataMask;
238
+ }
239
+ });
240
+ }, [uiConfig?.emitDataMasks]);
241
+
242
+ if (!dashboardId) {
243
+ return <Loading />;
244
+ }
245
+
246
+ return (
247
+ <LazyDashboardPage
248
+ key={dashboardId}
249
+ idOrSlug={dashboardId}
250
+ />
251
+ );
252
+ };
253
+ \`
254
+
255
+ content = (
256
+ content[: component_match.start()]
257
+ + replacement_component
258
+ + content[component_match.end() :]
259
+ )
260
+
261
+ switchboard_anchor_candidates = [
262
+ (
263
+ " Switchboard.defineMethod("
264
+ "'getScrollSize', embeddedApi.getScrollSize);"
265
+ ),
266
+ (
267
+ " Switchboard.defineMethod(\\n"
268
+ " 'getScrollSize',\\n"
269
+ " embeddedApi.getScrollSize,\\n"
270
+ " );"
271
+ ),
272
+ ]
273
+
274
+ switchboard_method = """ Switchboard.defineMethod(
275
+ 'switchDashboard',
276
+ async (
277
+ payload: SwitchDashboardPayload,
278
+ ): Promise<SwitchDashboardResult> => {
279
+ if (!dashboardSwitchHandler) {
280
+ throw new Error(
281
+ 'The embedded dashboard is not ready to switch',
282
+ );
283
+ }
284
+
285
+ return dashboardSwitchHandler(payload);
286
+ },
287
+ );
288
+
289
+ """
290
+
291
+ anchor_found = False
292
+
293
+ for anchor in switchboard_anchor_candidates:
294
+ if content.count(anchor) == 1:
295
+ content = content.replace(
296
+ anchor,
297
+ switchboard_method + anchor,
298
+ 1,
299
+ )
300
+ anchor_found = True
301
+ break
302
+
303
+ if not anchor_found:
304
+ raise PatchError(
305
+ "Could not locate the getScrollSize Switchboard method"
306
+ )
307
+
308
+ frontend_path.write_text(content, encoding="utf-8")
309
+ print(f"Patched: {frontend_path}")
310
+
311
+
312
+ def patch_guest_token_fetch_type(content: str) -> str:
313
+ candidates = [
314
+ "export type GuestTokenFetchFn = () => Promise<string>;",
315
+ (
316
+ "export type GuestTokenFetchFn = "
317
+ "() => Promise<string>;"
318
+ ),
319
+ ]
320
+
321
+ replacement = """// SUPERSET_SWITCHABLE_EMBED_SDK_PATCH
322
+ //
323
+ // dashboardId is undefined for the initial dashboard bootstrap.
324
+ // After a successful dashboard switch, token refreshes receive the active
325
+ // dashboard UUID.
326
+ export type GuestTokenFetchFn = (
327
+ dashboardId?: string,
328
+ ) => Promise<string>;"""
329
+
330
+ for candidate in candidates:
331
+ if content.count(candidate) == 1:
332
+ return content.replace(
333
+ candidate,
334
+ replacement,
335
+ 1,
336
+ )
337
+
338
+ raise PatchError("Could not locate GuestTokenFetchFn")
339
+
340
+
341
+ def patch_embedded_dashboard_type(content: str) -> str:
342
+ type_pattern = re.compile(
343
+ r"(export\\s+(?:interface|type)\\s+EmbeddedDashboard"
344
+ r".*?\\{)(.*?)(\\n\\};)",
345
+ re.DOTALL,
346
+ )
347
+
348
+ match = type_pattern.search(content)
349
+
350
+ if not match:
351
+ raise PatchError(
352
+ "Could not locate the EmbeddedDashboard public type"
353
+ )
354
+
355
+ body = match.group(2)
356
+
357
+ if "switchDashboard:" in body:
358
+ return content
359
+
360
+ method = """
361
+ /**
362
+ * Replaces DashboardPage in the existing iframe document.
363
+ *
364
+ * The value must be the target dashboard UUID/id accepted by
365
+ * DashboardPage, not the embedded-configuration UUID used in the
366
+ * initial /embedded/:uuid URL.
367
+ */
368
+ switchDashboard: (
369
+ dashboardId: string,
370
+ ) => Promise<{
371
+ dashboardId: string;
372
+ accepted: true;
373
+ }>;
374
+ """
375
+
376
+ updated_type = (
377
+ match.group(1)
378
+ + body.rstrip()
379
+ + "\\n"
380
+ + method.rstrip()
381
+ + match.group(3)
382
+ )
383
+
384
+ return (
385
+ content[: match.start()]
386
+ + updated_type
387
+ + content[match.end() :]
388
+ )
389
+
390
+
391
+ def patch_initial_guest_token_fetch(content: str) -> str:
392
+ candidates = [
393
+ "fetchGuestToken(),",
394
+ "await fetchGuestToken(),",
395
+ ]
396
+
397
+ for candidate in candidates:
398
+ if content.count(candidate) >= 1:
399
+ return content.replace(
400
+ candidate,
401
+ candidate.replace(
402
+ "fetchGuestToken()",
403
+ "fetchGuestToken(undefined)",
404
+ ),
405
+ 1,
406
+ )
407
+
408
+ raise PatchError(
409
+ "Could not locate the initial fetchGuestToken call"
410
+ )
411
+
412
+
413
+ def patch_guest_token_refresh(content: str) -> str:
414
+ patterns = [
415
+ re.compile(
416
+ r"async function refreshGuestToken\\(\\) \\{\\s*"
417
+ r"const newGuestToken = await fetchGuestToken\\(\\);\\s*"
418
+ r"ourPort\\.emit\\("
419
+ r"'guestToken',\\s*\\{\\s*guestToken:\\s*newGuestToken\\s*\\}"
420
+ r"\\);\\s*"
421
+ r"setTimeout\\("
422
+ r"refreshGuestToken,\\s*"
423
+ r"getGuestTokenRefreshTiming\\(newGuestToken\\)"
424
+ r"\\);\\s*"
425
+ r"\\}",
426
+ re.DOTALL,
427
+ ),
428
+ re.compile(
429
+ r"const refreshGuestToken = async \\(\\) => \\{\\s*"
430
+ r"const newGuestToken = await fetchGuestToken\\(\\);\\s*"
431
+ r"ourPort\\.emit\\("
432
+ r"'guestToken',\\s*\\{\\s*guestToken:\\s*newGuestToken\\s*\\}"
433
+ r"\\);\\s*"
434
+ r"setTimeout\\("
435
+ r"refreshGuestToken,\\s*"
436
+ r"getGuestTokenRefreshTiming\\(newGuestToken\\)"
437
+ r"\\);\\s*"
438
+ r"\\};",
439
+ re.DOTALL,
440
+ ),
441
+ ]
442
+
443
+ replacement = """let activeDashboardId: string | undefined;
444
+
445
+ async function refreshGuestToken() {
446
+ const newGuestToken = await fetchGuestToken(
447
+ activeDashboardId,
448
+ );
449
+
450
+ ourPort.emit('guestToken', {
451
+ guestToken: newGuestToken,
452
+ });
453
+
454
+ setTimeout(
455
+ refreshGuestToken,
456
+ getGuestTokenRefreshTiming(newGuestToken),
457
+ );
458
+ }"""
459
+
460
+ for pattern in patterns:
461
+ updated, count = pattern.subn(
462
+ replacement,
463
+ content,
464
+ count=1,
465
+ )
466
+
467
+ if count == 1:
468
+ return updated
469
+
470
+ raise PatchError(
471
+ "Could not locate the guest-token refresh function"
472
+ )
473
+
474
+
475
+ def patch_sdk_functions(content: str) -> str:
476
+ anchor_candidates = [
477
+ (
478
+ " const getScrollSize = () => "
479
+ "ourPort.get<Size>('getScrollSize');"
480
+ ),
481
+ (
482
+ " const getScrollSize = () =>\\n"
483
+ " ourPort.get<Size>('getScrollSize');"
484
+ ),
485
+ ]
486
+
487
+ switch_function = """ const switchDashboard = async (
488
+ dashboardId: string,
489
+ ): Promise<{
490
+ dashboardId: string;
491
+ accepted: true;
492
+ }> => {
493
+ const normalizedDashboardId = dashboardId.trim();
494
+
495
+ if (!normalizedDashboardId) {
496
+ throw new Error(
497
+ 'dashboardId must be a non-empty string',
498
+ );
499
+ }
500
+
501
+ const guestToken = await fetchGuestToken(
502
+ normalizedDashboardId,
503
+ );
504
+
505
+ const result = await ourPort.get<{
506
+ dashboardId: string;
507
+ accepted: true;
508
+ }>('switchDashboard', {
509
+ dashboardId: normalizedDashboardId,
510
+ guestToken,
511
+ });
512
+
513
+ activeDashboardId = normalizedDashboardId;
514
+
515
+ return result;
516
+ };
517
+
518
+ """
519
+
520
+ for anchor in anchor_candidates:
521
+ if content.count(anchor) == 1:
522
+ return content.replace(
523
+ anchor,
524
+ switch_function + anchor,
525
+ 1,
526
+ )
527
+
528
+ raise PatchError(
529
+ "Could not locate the SDK getScrollSize function"
530
+ )
531
+
532
+
533
+ def patch_sdk_return_object(content: str) -> str:
534
+ candidates = [
535
+ " setThemeMode,\\n };",
536
+ " setThemeMode,\\n unmount,\\n };",
537
+ " unmount,\\n setThemeMode,\\n };",
538
+ ]
539
+
540
+ for candidate in candidates:
541
+ if content.count(candidate) == 1:
542
+ lines = candidate.splitlines()
543
+
544
+ closing = lines.pop()
545
+ body = "\\n".join(lines)
546
+
547
+ return content.replace(
548
+ candidate,
549
+ body
550
+ + "\\n"
551
+ + " switchDashboard,\\n"
552
+ + closing,
553
+ 1,
554
+ )
555
+
556
+ raise PatchError(
557
+ "Could not locate the EmbeddedDashboard return object"
558
+ )
559
+
560
+
561
+ def patch_sdk(sdk_path: Path) -> None:
562
+ content = sdk_path.read_text(encoding="utf-8")
563
+
564
+ if "SUPERSET_SWITCHABLE_EMBED_SDK_PATCH" in content:
565
+ print(f"Already patched: {sdk_path}")
566
+ return
567
+
568
+ content = patch_guest_token_fetch_type(content)
569
+ content = patch_embedded_dashboard_type(content)
570
+ content = patch_initial_guest_token_fetch(content)
571
+ content = patch_guest_token_refresh(content)
572
+ content = patch_sdk_functions(content)
573
+ content = patch_sdk_return_object(content)
574
+
575
+ sdk_path.write_text(content, encoding="utf-8")
576
+ print(f"Patched: {sdk_path}")
577
+
578
+
579
+ def main() -> int:
580
+ parser = argparse.ArgumentParser(
581
+ description=(
582
+ "Apply the switchable-dashboard patch to a "
583
+ "Superset 6.1.x source checkout."
584
+ )
585
+ )
586
+
587
+ parser.add_argument(
588
+ "superset_root",
589
+ type=Path,
590
+ help="Path to the Apache Superset source checkout",
591
+ )
592
+
593
+ args = parser.parse_args()
594
+ root = args.superset_root.resolve()
595
+
596
+ frontend_path = root / FRONTEND_FILE
597
+ sdk_path = root / SDK_FILE
598
+
599
+ missing = [
600
+ path
601
+ for path in [frontend_path, sdk_path]
602
+ if not path.exists()
603
+ ]
604
+
605
+ if missing:
606
+ print(
607
+ "The target does not appear to be a compatible "
608
+ "Superset source checkout.",
609
+ file=sys.stderr,
610
+ )
611
+
612
+ for missing_path in missing:
613
+ print(f"Missing: {missing_path}", file=sys.stderr)
614
+
615
+ return 2
616
+
617
+ try:
618
+ patch_frontend(frontend_path)
619
+ patch_sdk(sdk_path)
620
+ except PatchError as error:
621
+ print(f"Patch failed: {error}", file=sys.stderr)
622
+ return 1
623
+
624
+ print("Patch completed successfully.")
625
+ return 0
626
+
627
+
628
+ if __name__ == "__main__":
629
+ raise SystemExit(main())
630
+ `;
631
+ const VERIFY_PATCH_SH = `#!/usr/bin/env bash
632
+
633
+ set -euo pipefail
634
+
635
+ SUPERSET_ROOT=\${1:?Usage: verify_patch.sh /path/to/superset}
636
+
637
+ FRONTEND_FILE="$SUPERSET_ROOT/superset-frontend/src/embedded/index.tsx"
638
+ SDK_FILE="$SUPERSET_ROOT/superset-embedded-sdk/src/index.ts"
639
+
640
+ test -f "$FRONTEND_FILE"
641
+ test -f "$SDK_FILE"
642
+
643
+ grep -q \\
644
+ "SUPERSET_SWITCHABLE_EMBED_PATCH" \\
645
+ "$FRONTEND_FILE"
646
+
647
+ grep -q \\
648
+ "'switchDashboard'" \\
649
+ "$FRONTEND_FILE"
650
+
651
+ grep -q \\
652
+ "dashboardSwitchHandler" \\
653
+ "$FRONTEND_FILE"
654
+
655
+ grep -q \\
656
+ "SUPERSET_SWITCHABLE_EMBED_SDK_PATCH" \\
657
+ "$SDK_FILE"
658
+
659
+ grep -q \\
660
+ "switchDashboard:" \\
661
+ "$SDK_FILE"
662
+
663
+ grep -q \\
664
+ "activeDashboardId" \\
665
+ "$SDK_FILE"
666
+
667
+ grep -q \\
668
+ "fetchGuestToken(" \\
669
+ "$SDK_FILE"
670
+
671
+ echo "The switchable-dashboard patch is present."
672
+ `;
673
+ const BUILD_SDK_SH = `#!/usr/bin/env bash
674
+
675
+ set -euo pipefail
676
+
677
+ SUPERSET_ROOT=\${1:?Usage: build-sdk.sh /path/to/superset}
678
+
679
+ SDK_ROOT="$SUPERSET_ROOT/superset-embedded-sdk"
680
+
681
+ if [ ! -f "$SDK_ROOT/package.json" ]; then
682
+ echo "Missing SDK package: $SDK_ROOT" >&2
683
+ exit 1
684
+ fi
685
+
686
+ cd "$SDK_ROOT"
687
+
688
+ npm ci
689
+
690
+ npm run build
691
+
692
+ echo
693
+ echo "Embedded SDK build completed."
694
+ echo "Output directory:"
695
+ find . \\
696
+ -maxdepth 2 \\
697
+ -type d \\
698
+ \\( -name dist -o -name lib \\) \\
699
+ -print
700
+ `;
701
+ const BUILD_FRONTEND_SH = `#!/usr/bin/env bash
702
+
703
+ set -euo pipefail
704
+
705
+ SUPERSET_ROOT=\${1:?Usage: build-superset-frontend.sh /path/to/superset}
706
+
707
+ FRONTEND_ROOT="$SUPERSET_ROOT/superset-frontend"
708
+
709
+ if [ ! -f "$FRONTEND_ROOT/package.json" ]; then
710
+ echo "Missing Superset frontend: $FRONTEND_ROOT" >&2
711
+ exit 1
712
+ fi
713
+
714
+ cd "$FRONTEND_ROOT"
715
+
716
+ npm ci
717
+
718
+ npm run build
719
+
720
+ echo "Superset frontend build completed."
721
+ `;
722
+ const PATCH_AND_BUILD_SH = `#!/usr/bin/env bash
723
+
724
+ set -euo pipefail
725
+
726
+ SUPERSET_ROOT=\${1:?Usage: patch-and-build.sh /path/to/superset}
727
+
728
+ SCRIPT_DIR=$(
729
+ cd "$(dirname "\${BASH_SOURCE[0]}")"
730
+ pwd
731
+ )
732
+
733
+ PROJECT_ROOT=$(
734
+ cd "$SCRIPT_DIR/.."
735
+ pwd
736
+ )
737
+
738
+ python3 \\
739
+ "$PROJECT_ROOT/patches/apply_superset_6_1_patch.py" \\
740
+ "$SUPERSET_ROOT"
741
+
742
+ "$PROJECT_ROOT/patches/verify_patch.sh" \\
743
+ "$SUPERSET_ROOT"
744
+
745
+ "$SCRIPT_DIR/build-sdk.sh" \\
746
+ "$SUPERSET_ROOT"
747
+
748
+ "$SCRIPT_DIR/build-superset-frontend.sh" \\
749
+ "$SUPERSET_ROOT"
750
+
751
+ echo
752
+ echo "Patch and frontend builds completed."
753
+ `;
754
+ const BUILD_DOCKER_SH = `#!/usr/bin/env bash
755
+
756
+ set -euo pipefail
757
+
758
+ SUPERSET_ROOT=\${1:?Usage: build-docker-image.sh /path/to/superset [image-tag]}
759
+ IMAGE_TAG=\${2:-local/superset-switchable:6.1.0}
760
+
761
+ SCRIPT_DIR=$(
762
+ cd "$(dirname "\${BASH_SOURCE[0]}")"
763
+ pwd
764
+ )
765
+
766
+ PROJECT_ROOT=$(
767
+ cd "$SCRIPT_DIR/.."
768
+ pwd
769
+ )
770
+
771
+ python3 \\
772
+ "$PROJECT_ROOT/patches/apply_superset_6_1_patch.py" \\
773
+ "$SUPERSET_ROOT"
774
+
775
+ "$PROJECT_ROOT/patches/verify_patch.sh" \\
776
+ "$SUPERSET_ROOT"
777
+
778
+ cd "$SUPERSET_ROOT"
779
+
780
+ docker build \\
781
+ --target lean \\
782
+ --tag "$IMAGE_TAG" \\
783
+ .
784
+
785
+ echo "Built Docker image: $IMAGE_TAG"
786
+ `;
787
+ const README = `# ${SUPERSET_PLUGIN_ID}
788
+
789
+ Multi-tenant, org-agnostic Superset dashboard embed plugin (patch-and-build strategy).
790
+
791
+ This is not a generated plugin like the Kibana variant. Instead, Decaf holds
792
+ patch scripts that modify Superset's internal embedded frontend and embedded
793
+ SDK source files to add a \`switchDashboard\` method. The existing iframe
794
+ element, document, contentWindow, React runtime, and Switchboard MessageChannel
795
+ are preserved across dashboard switches.
796
+
797
+ ## What it patches
798
+
799
+ - \`superset-frontend/src/embedded/index.tsx\` — adds \`switchDashboard\` to the
800
+ Switchboard channel; replaces only \`DashboardPage\`.
801
+ - \`superset-embedded-sdk/src/index.ts\` — adds
802
+ \`embeddedDashboard.switchDashboard(dashboardId)\`; requests a guest token for
803
+ the selected dashboard; refreshes future guest tokens for the active dashboard.
804
+
805
+ ## Build
806
+
807
+ \`\`\`bash
808
+ # Apply patch + verify + build SDK + build frontend
809
+ build/patch-and-build.sh /path/to/superset
810
+
811
+ # Or build a Docker image
812
+ build/build-docker-image.sh /path/to/superset registry.example.com/superset-switchable:6.1.0
813
+ \`\`\`
814
+
815
+ The patched SDK must be packed and installed into the Angular application:
816
+
817
+ \`\`\`bash
818
+ cd /path/to/superset/superset-embedded-sdk
819
+ npm ci && npm run build && npm pack
820
+ cd /path/to/angular-app
821
+ npm install /path/to/superset/superset-embedded-sdk/superset-ui-embedded-sdk-*.tgz
822
+ \`\`\`
823
+
824
+ Version: ${SUPERSET_PLUGIN_VERSION}
825
+ `;
826
+ /**
827
+ * Returns the full set of patch and build script files.
828
+ */
829
+ export function supersetPatchFiles() {
830
+ return [
831
+ { path: "patches/apply_superset_6_1_patch.py", content: APPLY_PATCH_PY, executable: true },
832
+ { path: "patches/verify_patch.sh", content: VERIFY_PATCH_SH, executable: true },
833
+ { path: "build/build-sdk.sh", content: BUILD_SDK_SH, executable: true },
834
+ { path: "build/build-superset-frontend.sh", content: BUILD_FRONTEND_SH, executable: true },
835
+ { path: "build/patch-and-build.sh", content: PATCH_AND_BUILD_SH, executable: true },
836
+ { path: "build/build-docker-image.sh", content: BUILD_DOCKER_SH, executable: true },
837
+ { path: "README.md", content: README },
838
+ ];
839
+ }
840
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVtcGxhdGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3BsdWdpbnMvc3VwZXJzZXQvdGVtcGxhdGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsdUJBQXVCLEVBQUUsc0JBQW1CO0FBV3pFLE1BQU0sY0FBYyxHQUFHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztDQXNtQnRCLENBQUM7QUFFRixNQUFNLGVBQWUsR0FBRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Q0F5Q3ZCLENBQUM7QUFFRixNQUFNLFlBQVksR0FBRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBMkJwQixDQUFDO0FBRUYsTUFBTSxpQkFBaUIsR0FBRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Q0FvQnpCLENBQUM7QUFFRixNQUFNLGtCQUFrQixHQUFHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBK0IxQixDQUFDO0FBRUYsTUFBTSxlQUFlLEdBQUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBZ0N2QixDQUFDO0FBRUYsTUFBTSxNQUFNLEdBQUcsS0FBSyxrQkFBa0I7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7V0FxQzNCLHVCQUF1QjtDQUNqQyxDQUFDO0FBRUY7O0dBRUc7QUFDSCxNQUFNLFVBQVUsa0JBQWtCO0lBQ2hDLE9BQU87UUFDTCxFQUFFLElBQUksRUFBRSxxQ0FBcUMsRUFBRSxPQUFPLEVBQUUsY0FBYyxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUU7UUFDMUYsRUFBRSxJQUFJLEVBQUUseUJBQXlCLEVBQUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFO1FBQy9FLEVBQUUsSUFBSSxFQUFFLG9CQUFvQixFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRTtRQUN2RSxFQUFFLElBQUksRUFBRSxrQ0FBa0MsRUFBRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRTtRQUMxRixFQUFFLElBQUksRUFBRSwwQkFBMEIsRUFBRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRTtRQUNuRixFQUFFLElBQUksRUFBRSw2QkFBNkIsRUFBRSxPQUFPLEVBQUUsZUFBZSxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUU7UUFDbkYsRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUU7S0FDdkMsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBtb2R1bGUgaW50ZWdyYXRpb25zL3BsdWdpbnMvc3VwZXJzZXQvdGVtcGxhdGVzXG4gKiBAc3VtbWFyeSBQYXRjaCBhbmQgYnVpbGQgc2NyaXB0IHRlbXBsYXRlcyBmb3IgdGhlIFN1cGVyc2V0IGVtYmVkIHBsdWdpbi5cbiAqIEBkZXNjcmlwdGlvbiBIb2xkcyBhbGwgcGF0Y2ggc2NyaXB0cyAoUHl0aG9uICsgQmFzaCkgYW5kIGJ1aWxkIHNjcmlwdHMgYXNcbiAqIHN0cmluZyB0ZW1wbGF0ZXMuIFRoZXNlIGFyZSBtYXRlcmlhbGl6ZWQgb250byBkaXNrIGJ5IHRoZSBpbnN0YWxsZXIuIERlY2FmXG4gKiBkb2VzIG5vdCBnZW5lcmF0ZSBuZXcgU3VwZXJzZXQgc291cmNlIOKAlCBpdCBwYXRjaGVzIGV4aXN0aW5nIGludGVybmFsIGZpbGVzXG4gKiAoYHN1cGVyc2V0LWZyb250ZW5kL3NyYy9lbWJlZGRlZC9pbmRleC50c3hgIGFuZFxuICogYHN1cGVyc2V0LWVtYmVkZGVkLXNkay9zcmMvaW5kZXgudHNgKSBmb2xsb3dpbmcgdGhlIHJlZmVyZW5jZSBpbXBsZW1lbnRhdGlvblxuICogZnJvbSBERUNBRi00MCDCpzkuXG4gKlxuICogVGhlIHBhdGNoIGFkZHMgYSBgc3dpdGNoRGFzaGJvYXJkYCBtZXRob2QgdG8gU3VwZXJzZXQncyBleGlzdGluZyBTd2l0Y2hib2FyZFxuICogY2hhbm5lbCwgcmVwbGFjaW5nIG9ubHkgYERhc2hib2FyZFBhZ2VgIHdoaWxlIGtlZXBpbmcgdGhlIGlmcmFtZSBlbGVtZW50LFxuICogZG9jdW1lbnQsIGNvbnRlbnRXaW5kb3csIFJlYWN0IHJ1bnRpbWUsIGFuZCBtZXNzYWdlIGNoYW5uZWwgYWxpdmUuXG4gKi9cbmltcG9ydCB7IFNVUEVSU0VUX1BMVUdJTl9JRCwgU1VQRVJTRVRfUExVR0lOX1ZFUlNJT04gfSBmcm9tIFwiLi9tYW5pZmVzdFwiO1xuXG5leHBvcnQgaW50ZXJmYWNlIFN1cGVyc2V0UGF0Y2hGaWxlIHtcbiAgLyoqIFJlbGF0aXZlIHBhdGggd2l0aGluIHRoZSB0YXJnZXQgZGlyZWN0b3J5LiAqL1xuICBwYXRoOiBzdHJpbmc7XG4gIC8qKiBGaWxlIGNvbnRlbnRzLiAqL1xuICBjb250ZW50OiBzdHJpbmc7XG4gIC8qKiBXaGV0aGVyIHRvIG1hcmsgdGhlIGZpbGUgZXhlY3V0YWJsZSBvbiBQT1NJWC4gKi9cbiAgZXhlY3V0YWJsZT86IGJvb2xlYW47XG59XG5cbmNvbnN0IEFQUExZX1BBVENIX1BZID0gYCMhL3Vzci9iaW4vZW52IHB5dGhvbjNcblxuZnJvbSBfX2Z1dHVyZV9fIGltcG9ydCBhbm5vdGF0aW9uc1xuXG5pbXBvcnQgYXJncGFyc2VcbmltcG9ydCByZVxuaW1wb3J0IHN5c1xuZnJvbSBwYXRobGliIGltcG9ydCBQYXRoXG5cblxuRlJPTlRFTkRfRklMRSA9IFBhdGgoXCJzdXBlcnNldC1mcm9udGVuZC9zcmMvZW1iZWRkZWQvaW5kZXgudHN4XCIpXG5TREtfRklMRSA9IFBhdGgoXCJzdXBlcnNldC1lbWJlZGRlZC1zZGsvc3JjL2luZGV4LnRzXCIpXG5cblxuY2xhc3MgUGF0Y2hFcnJvcihSdW50aW1lRXJyb3IpOlxuICAgIHBhc3NcblxuXG5kZWYgcmVwbGFjZV9vbmNlKFxuICAgIGNvbnRlbnQ6IHN0cixcbiAgICBvbGQ6IHN0cixcbiAgICBuZXc6IHN0cixcbiAgICBkZXNjcmlwdGlvbjogc3RyLFxuKSAtPiBzdHI6XG4gICAgY291bnQgPSBjb250ZW50LmNvdW50KG9sZClcblxuICAgIGlmIGNvdW50ICE9IDE6XG4gICAgICAgIHJhaXNlIFBhdGNoRXJyb3IoXG4gICAgICAgICAgICBmXCJ7ZGVzY3JpcHRpb259OiBleHBlY3RlZCBleGFjdGx5IG9uZSBvY2N1cnJlbmNlLCBmb3VuZCB7Y291bnR9XCJcbiAgICAgICAgKVxuXG4gICAgcmV0dXJuIGNvbnRlbnQucmVwbGFjZShvbGQsIG5ldywgMSlcblxuXG5kZWYgcmVwbGFjZV9yZWdleF9vbmNlKFxuICAgIGNvbnRlbnQ6IHN0cixcbiAgICBwYXR0ZXJuOiByZS5QYXR0ZXJuW3N0cl0sXG4gICAgcmVwbGFjZW1lbnQ6IHN0cixcbiAgICBkZXNjcmlwdGlvbjogc3RyLFxuKSAtPiBzdHI6XG4gICAgdXBkYXRlZCwgY291bnQgPSBwYXR0ZXJuLnN1Ym4ocmVwbGFjZW1lbnQsIGNvbnRlbnQsIGNvdW50PTEpXG5cbiAgICBpZiBjb3VudCAhPSAxOlxuICAgICAgICByYWlzZSBQYXRjaEVycm9yKFxuICAgICAgICAgICAgZlwie2Rlc2NyaXB0aW9ufTogZXhwZWN0ZWQgZXhhY3RseSBvbmUgbWF0Y2gsIGZvdW5kIHtjb3VudH1cIlxuICAgICAgICApXG5cbiAgICByZXR1cm4gdXBkYXRlZFxuXG5cbmRlZiBwYXRjaF9yZWFjdF9pbXBvcnQoY29udGVudDogc3RyKSAtPiBzdHI6XG4gICAgcmVhY3RfaW1wb3J0X3BhdHRlcm4gPSByZS5jb21waWxlKFxuICAgICAgICByXCJpbXBvcnRcXFxccypcXFxce1xcXFxzKihbXn1dKylcXFxccypcXFxcfVxcXFxzKmZyb21cXFxccyoncmVhY3QnO1wiXG4gICAgKVxuXG4gICAgbWF0Y2ggPSByZWFjdF9pbXBvcnRfcGF0dGVybi5zZWFyY2goY29udGVudClcblxuICAgIGlmIG5vdCBtYXRjaDpcbiAgICAgICAgcmFpc2UgUGF0Y2hFcnJvcihcIkNvdWxkIG5vdCBsb2NhdGUgdGhlIG5hbWVkIFJlYWN0IGltcG9ydFwiKVxuXG4gICAgaW1wb3J0cyA9IFtcbiAgICAgICAgdmFsdWUuc3RyaXAoKVxuICAgICAgICBmb3IgdmFsdWUgaW4gbWF0Y2guZ3JvdXAoMSkucmVwbGFjZShcIlxcXFxuXCIsIFwiIFwiKS5zcGxpdChcIixcIilcbiAgICAgICAgaWYgdmFsdWUuc3RyaXAoKVxuICAgIF1cblxuICAgIHJlcXVpcmVkX2ltcG9ydHMgPSBbXCJsYXp5XCIsIFwiU3VzcGVuc2VcIiwgXCJ1c2VFZmZlY3RcIiwgXCJ1c2VTdGF0ZVwiXVxuXG4gICAgZm9yIHJlcXVpcmVkX2ltcG9ydCBpbiByZXF1aXJlZF9pbXBvcnRzOlxuICAgICAgICBpZiByZXF1aXJlZF9pbXBvcnQgbm90IGluIGltcG9ydHM6XG4gICAgICAgICAgICBpbXBvcnRzLmFwcGVuZChyZXF1aXJlZF9pbXBvcnQpXG5cbiAgICByZXBsYWNlbWVudCA9IChcbiAgICAgICAgXCJpbXBvcnQge1xcXFxuICBcIlxuICAgICAgICArIFwiLFxcXFxuICBcIi5qb2luKGltcG9ydHMpXG4gICAgICAgICsgXCIsXFxcXG59IGZyb20gJ3JlYWN0JztcIlxuICAgIClcblxuICAgIHJldHVybiAoXG4gICAgICAgIGNvbnRlbnRbOiBtYXRjaC5zdGFydCgpXVxuICAgICAgICArIHJlcGxhY2VtZW50XG4gICAgICAgICsgY29udGVudFttYXRjaC5lbmQoKSA6XVxuICAgIClcblxuXG5kZWYgbG9jYXRlX2VtYmVkZGVkX2NvbXBvbmVudChcbiAgICBjb250ZW50OiBzdHIsXG4pIC0+IHR1cGxlW3JlLk1hdGNoW3N0cl0sIHN0cl06XG4gICAgbmFtZXMgPSBbXG4gICAgICAgIFwiRW1iZWRkZWRMYXp5RGFzaGJvYXJkUGFnZVwiLFxuICAgICAgICBcIkVtYmVkZWRMYXp5RGFzaGJvYXJkUGFnZVwiLFxuICAgIF1cblxuICAgIGZvciBuYW1lIGluIG5hbWVzOlxuICAgICAgICBwYXR0ZXJuID0gcmUuY29tcGlsZShcbiAgICAgICAgICAgIHJmXCJjb25zdCB7bmFtZX0gPSBcXFxcKFxcXFwpID0+IFxcXFx7ey4qP1xcXFxuXFxcXH19O1xcXFxuXCJcbiAgICAgICAgICAgIHJmXCIoPz1jb25zdCBFbWJlZGRlZFJvdXRlKVwiLFxuICAgICAgICAgICAgcmUuRE9UQUxMLFxuICAgICAgICApXG5cbiAgICAgICAgbWF0Y2ggPSBwYXR0ZXJuLnNlYXJjaChjb250ZW50KVxuXG4gICAgICAgIGlmIG1hdGNoOlxuICAgICAgICAgICAgcmV0dXJuIG1hdGNoLCBuYW1lXG5cbiAgICByYWlzZSBQYXRjaEVycm9yKFxuICAgICAgICBcIkNvdWxkIG5vdCBmaW5kIEVtYmVkZGVkTGF6eURhc2hib2FyZFBhZ2UgYmVmb3JlIEVtYmVkZGVkUm91dGVcIlxuICAgIClcblxuXG5kZWYgcGF0Y2hfZnJvbnRlbmQoZnJvbnRlbmRfcGF0aDogUGF0aCkgLT4gTm9uZTpcbiAgICBjb250ZW50ID0gZnJvbnRlbmRfcGF0aC5yZWFkX3RleHQoZW5jb2Rpbmc9XCJ1dGYtOFwiKVxuXG4gICAgaWYgXCJTVVBFUlNFVF9TV0lUQ0hBQkxFX0VNQkVEX1BBVENIXCIgaW4gY29udGVudDpcbiAgICAgICAgcHJpbnQoZlwiQWxyZWFkeSBwYXRjaGVkOiB7ZnJvbnRlbmRfcGF0aH1cIilcbiAgICAgICAgcmV0dXJuXG5cbiAgICBjb250ZW50ID0gcGF0Y2hfcmVhY3RfaW1wb3J0KGNvbnRlbnQpXG5cbiAgICBjb21wb25lbnRfbWF0Y2gsIGNvbXBvbmVudF9uYW1lID0gbG9jYXRlX2VtYmVkZGVkX2NvbXBvbmVudChcbiAgICAgICAgY29udGVudFxuICAgIClcblxuICAgIHJlcGxhY2VtZW50X2NvbXBvbmVudCA9IFxcYC8vIFNVUEVSU0VUX1NXSVRDSEFCTEVfRU1CRURfUEFUQ0hcbi8vXG4vLyBBbGxvd3MgdGhlIGV4aXN0aW5nIGVtYmVkZGVkIFN1cGVyc2V0IGRvY3VtZW50IHRvIHJlcGxhY2UgRGFzaGJvYXJkUGFnZVxuLy8gd2l0aG91dCBuYXZpZ2F0aW5nIG9yIHJlY3JlYXRpbmcgdGhlIGlmcmFtZS5cbi8vXG4vLyBTZWN1cml0eTpcbi8vIC0gVGhpcyBtZXRob2QgaXMgZXhwb3NlZCBvbmx5IHRocm91Z2ggU3VwZXJzZXQncyBleGlzdGluZyBTd2l0Y2hib2FyZFxuLy8gICBNZXNzYWdlQ2hhbm5lbC5cbi8vIC0gVGhlIGhvc3QgYXBwbGljYXRpb24gbXVzdCBvYnRhaW4gYSBndWVzdCB0b2tlbiBmb3IgdGhlIHRhcmdldCBkYXNoYm9hcmQuXG4vLyAtIFN1cGVyc2V0J3Mgbm9ybWFsIEFsbG93ZWQgRG9tYWlucyB2YWxpZGF0aW9uIHJlbWFpbnMgaW4gZWZmZWN0LlxuXG5pbnRlcmZhY2UgU3dpdGNoRGFzaGJvYXJkUGF5bG9hZCB7XG4gIGRhc2hib2FyZElkOiBzdHJpbmc7XG4gIGd1ZXN0VG9rZW46IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIFN3aXRjaERhc2hib2FyZFJlc3VsdCB7XG4gIGRhc2hib2FyZElkOiBzdHJpbmc7XG4gIGFjY2VwdGVkOiB0cnVlO1xufVxuXG50eXBlIERhc2hib2FyZFN3aXRjaEhhbmRsZXIgPSAoXG4gIHBheWxvYWQ6IFN3aXRjaERhc2hib2FyZFBheWxvYWQsXG4pID0+IFByb21pc2U8U3dpdGNoRGFzaGJvYXJkUmVzdWx0PjtcblxubGV0IGRhc2hib2FyZFN3aXRjaEhhbmRsZXI6IERhc2hib2FyZFN3aXRjaEhhbmRsZXIgfCB1bmRlZmluZWQ7XG5cbmNvbnN0IFxcJHtjb21wb25lbnRfbmFtZX0gPSAoKSA9PiB7XG4gIGNvbnN0IHVpQ29uZmlnID0gdXNlVWlDb25maWcoKTtcblxuICBjb25zdCBbZGFzaGJvYXJkSWQsIHNldERhc2hib2FyZElkXSA9IHVzZVN0YXRlPHN0cmluZyB8IG51bGw+KFxuICAgIGJvb3RzdHJhcERhdGEuZW1iZWRkZWQhLmRhc2hib2FyZF9pZCxcbiAgKTtcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGRhc2hib2FyZFN3aXRjaEhhbmRsZXIgPSBhc3luYyAoe1xuICAgICAgZGFzaGJvYXJkSWQ6IHJlcXVlc3RlZERhc2hib2FyZElkLFxuICAgICAgZ3Vlc3RUb2tlbixcbiAgICB9OiBTd2l0Y2hEYXNoYm9hcmRQYXlsb2FkKSA9PiB7XG4gICAgICBjb25zdCBuZXh0RGFzaGJvYXJkSWQgPSByZXF1ZXN0ZWREYXNoYm9hcmRJZC50cmltKCk7XG5cbiAgICAgIGlmICghbmV4dERhc2hib2FyZElkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnZGFzaGJvYXJkSWQgbXVzdCBiZSBhIG5vbi1lbXB0eSBzdHJpbmcnLFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICBpZiAoXG4gICAgICAgIHR5cGVvZiBndWVzdFRva2VuICE9PSAnc3RyaW5nJyB8fFxuICAgICAgICBndWVzdFRva2VuLnRyaW0oKS5sZW5ndGggPT09IDBcbiAgICAgICkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ2d1ZXN0VG9rZW4gbXVzdCBiZSBhIG5vbi1lbXB0eSBzdHJpbmcnLFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICBzZXREYXNoYm9hcmRJZChudWxsKTtcblxuICAgICAgZGlzcGxheWVkVW5hdXRob3JpemVkVG9hc3QgPSBmYWxzZTtcblxuICAgICAgc2V0dXBHdWVzdENsaWVudChndWVzdFRva2VuKTtcblxuICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4ocmVzb2x2ZSA9PiB7XG4gICAgICAgIHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gcmVzb2x2ZSgpKTtcbiAgICAgIH0pO1xuXG4gICAgICBzZXREYXNoYm9hcmRJZChuZXh0RGFzaGJvYXJkSWQpO1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBkYXNoYm9hcmRJZDogbmV4dERhc2hib2FyZElkLFxuICAgICAgICBhY2NlcHRlZDogdHJ1ZSxcbiAgICAgIH07XG4gICAgfTtcblxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBkYXNoYm9hcmRTd2l0Y2hIYW5kbGVyID0gdW5kZWZpbmVkO1xuICAgIH07XG4gIH0sIFtdKTtcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghdWlDb25maWc/LmVtaXREYXRhTWFza3MpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgbGV0IHByZXZpb3VzRGF0YU1hc2sgPSBzdG9yZS5nZXRTdGF0ZSgpLmRhdGFNYXNrO1xuXG4gICAgcmV0dXJuIHN0b3JlLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICBjb25zdCBjdXJyZW50RGF0YU1hc2sgPSBzdG9yZS5nZXRTdGF0ZSgpLmRhdGFNYXNrO1xuXG4gICAgICBpZiAocHJldmlvdXNEYXRhTWFzayAhPT0gY3VycmVudERhdGFNYXNrKSB7XG4gICAgICAgIFN3aXRjaGJvYXJkLmVtaXQoJ29ic2VydmVEYXRhTWFzaycsIHtcbiAgICAgICAgICAuLi5jdXJyZW50RGF0YU1hc2ssXG4gICAgICAgICAgLi4uZ2V0RGF0YU1hc2tDaGFuZ2VUcmlnZ2VyKFxuICAgICAgICAgICAgY3VycmVudERhdGFNYXNrLFxuICAgICAgICAgICAgcHJldmlvdXNEYXRhTWFzayxcbiAgICAgICAgICApLFxuICAgICAgICB9KTtcblxuICAgICAgICBwcmV2aW91c0RhdGFNYXNrID0gY3VycmVudERhdGFNYXNrO1xuICAgICAgfVxuICAgIH0pO1xuICB9LCBbdWlDb25maWc/LmVtaXREYXRhTWFza3NdKTtcblxuICBpZiAoIWRhc2hib2FyZElkKSB7XG4gICAgcmV0dXJuIDxMb2FkaW5nIC8+O1xuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8TGF6eURhc2hib2FyZFBhZ2VcbiAgICAgIGtleT17ZGFzaGJvYXJkSWR9XG4gICAgICBpZE9yU2x1Zz17ZGFzaGJvYXJkSWR9XG4gICAgLz5cbiAgKTtcbn07XG5cXGBcblxuICAgIGNvbnRlbnQgPSAoXG4gICAgICAgIGNvbnRlbnRbOiBjb21wb25lbnRfbWF0Y2guc3RhcnQoKV1cbiAgICAgICAgKyByZXBsYWNlbWVudF9jb21wb25lbnRcbiAgICAgICAgKyBjb250ZW50W2NvbXBvbmVudF9tYXRjaC5lbmQoKSA6XVxuICAgIClcblxuICAgIHN3aXRjaGJvYXJkX2FuY2hvcl9jYW5kaWRhdGVzID0gW1xuICAgICAgICAoXG4gICAgICAgICAgICBcIiAgICBTd2l0Y2hib2FyZC5kZWZpbmVNZXRob2QoXCJcbiAgICAgICAgICAgIFwiJ2dldFNjcm9sbFNpemUnLCBlbWJlZGRlZEFwaS5nZXRTY3JvbGxTaXplKTtcIlxuICAgICAgICApLFxuICAgICAgICAoXG4gICAgICAgICAgICBcIiAgICBTd2l0Y2hib2FyZC5kZWZpbmVNZXRob2QoXFxcXG5cIlxuICAgICAgICAgICAgXCIgICAgICAnZ2V0U2Nyb2xsU2l6ZScsXFxcXG5cIlxuICAgICAgICAgICAgXCIgICAgICBlbWJlZGRlZEFwaS5nZXRTY3JvbGxTaXplLFxcXFxuXCJcbiAgICAgICAgICAgIFwiICAgICk7XCJcbiAgICAgICAgKSxcbiAgICBdXG5cbiAgICBzd2l0Y2hib2FyZF9tZXRob2QgPSBcIlwiXCIgICAgU3dpdGNoYm9hcmQuZGVmaW5lTWV0aG9kKFxuICAgICAgJ3N3aXRjaERhc2hib2FyZCcsXG4gICAgICBhc3luYyAoXG4gICAgICAgIHBheWxvYWQ6IFN3aXRjaERhc2hib2FyZFBheWxvYWQsXG4gICAgICApOiBQcm9taXNlPFN3aXRjaERhc2hib2FyZFJlc3VsdD4gPT4ge1xuICAgICAgICBpZiAoIWRhc2hib2FyZFN3aXRjaEhhbmRsZXIpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnVGhlIGVtYmVkZGVkIGRhc2hib2FyZCBpcyBub3QgcmVhZHkgdG8gc3dpdGNoJyxcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGRhc2hib2FyZFN3aXRjaEhhbmRsZXIocGF5bG9hZCk7XG4gICAgICB9LFxuICAgICk7XG5cblwiXCJcIlxuXG4gICAgYW5jaG9yX2ZvdW5kID0gRmFsc2VcblxuICAgIGZvciBhbmNob3IgaW4gc3dpdGNoYm9hcmRfYW5jaG9yX2NhbmRpZGF0ZXM6XG4gICAgICAgIGlmIGNvbnRlbnQuY291bnQoYW5jaG9yKSA9PSAxOlxuICAgICAgICAgICAgY29udGVudCA9IGNvbnRlbnQucmVwbGFjZShcbiAgICAgICAgICAgICAgICBhbmNob3IsXG4gICAgICAgICAgICAgICAgc3dpdGNoYm9hcmRfbWV0aG9kICsgYW5jaG9yLFxuICAgICAgICAgICAgICAgIDEsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBhbmNob3JfZm91bmQgPSBUcnVlXG4gICAgICAgICAgICBicmVha1xuXG4gICAgaWYgbm90IGFuY2hvcl9mb3VuZDpcbiAgICAgICAgcmFpc2UgUGF0Y2hFcnJvcihcbiAgICAgICAgICAgIFwiQ291bGQgbm90IGxvY2F0ZSB0aGUgZ2V0U2Nyb2xsU2l6ZSBTd2l0Y2hib2FyZCBtZXRob2RcIlxuICAgICAgICApXG5cbiAgICBmcm9udGVuZF9wYXRoLndyaXRlX3RleHQoY29udGVudCwgZW5jb2Rpbmc9XCJ1dGYtOFwiKVxuICAgIHByaW50KGZcIlBhdGNoZWQ6IHtmcm9udGVuZF9wYXRofVwiKVxuXG5cbmRlZiBwYXRjaF9ndWVzdF90b2tlbl9mZXRjaF90eXBlKGNvbnRlbnQ6IHN0cikgLT4gc3RyOlxuICAgIGNhbmRpZGF0ZXMgPSBbXG4gICAgICAgIFwiZXhwb3J0IHR5cGUgR3Vlc3RUb2tlbkZldGNoRm4gPSAoKSA9PiBQcm9taXNlPHN0cmluZz47XCIsXG4gICAgICAgIChcbiAgICAgICAgICAgIFwiZXhwb3J0IHR5cGUgR3Vlc3RUb2tlbkZldGNoRm4gPSBcIlxuICAgICAgICAgICAgXCIoKSA9PiBQcm9taXNlPHN0cmluZz47XCJcbiAgICAgICAgKSxcbiAgICBdXG5cbiAgICByZXBsYWNlbWVudCA9IFwiXCJcIi8vIFNVUEVSU0VUX1NXSVRDSEFCTEVfRU1CRURfU0RLX1BBVENIXG4vL1xuLy8gZGFzaGJvYXJkSWQgaXMgdW5kZWZpbmVkIGZvciB0aGUgaW5pdGlhbCBkYXNoYm9hcmQgYm9vdHN0cmFwLlxuLy8gQWZ0ZXIgYSBzdWNjZXNzZnVsIGRhc2hib2FyZCBzd2l0Y2gsIHRva2VuIHJlZnJlc2hlcyByZWNlaXZlIHRoZSBhY3RpdmVcbi8vIGRhc2hib2FyZCBVVUlELlxuZXhwb3J0IHR5cGUgR3Vlc3RUb2tlbkZldGNoRm4gPSAoXG4gIGRhc2hib2FyZElkPzogc3RyaW5nLFxuKSA9PiBQcm9taXNlPHN0cmluZz47XCJcIlwiXG5cbiAgICBmb3IgY2FuZGlkYXRlIGluIGNhbmRpZGF0ZXM6XG4gICAgICAgIGlmIGNvbnRlbnQuY291bnQoY2FuZGlkYXRlKSA9PSAxOlxuICAgICAgICAgICAgcmV0dXJuIGNvbnRlbnQucmVwbGFjZShcbiAgICAgICAgICAgICAgICBjYW5kaWRhdGUsXG4gICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQsXG4gICAgICAgICAgICAgICAgMSxcbiAgICAgICAgICAgIClcblxuICAgIHJhaXNlIFBhdGNoRXJyb3IoXCJDb3VsZCBub3QgbG9jYXRlIEd1ZXN0VG9rZW5GZXRjaEZuXCIpXG5cblxuZGVmIHBhdGNoX2VtYmVkZGVkX2Rhc2hib2FyZF90eXBlKGNvbnRlbnQ6IHN0cikgLT4gc3RyOlxuICAgIHR5cGVfcGF0dGVybiA9IHJlLmNvbXBpbGUoXG4gICAgICAgIHJcIihleHBvcnRcXFxccysoPzppbnRlcmZhY2V8dHlwZSlcXFxccytFbWJlZGRlZERhc2hib2FyZFwiXG4gICAgICAgIHJcIi4qP1xcXFx7KSguKj8pKFxcXFxuXFxcXH07KVwiLFxuICAgICAgICByZS5ET1RBTEwsXG4gICAgKVxuXG4gICAgbWF0Y2ggPSB0eXBlX3BhdHRlcm4uc2VhcmNoKGNvbnRlbnQpXG5cbiAgICBpZiBub3QgbWF0Y2g6XG4gICAgICAgIHJhaXNlIFBhdGNoRXJyb3IoXG4gICAgICAgICAgICBcIkNvdWxkIG5vdCBsb2NhdGUgdGhlIEVtYmVkZGVkRGFzaGJvYXJkIHB1YmxpYyB0eXBlXCJcbiAgICAgICAgKVxuXG4gICAgYm9keSA9IG1hdGNoLmdyb3VwKDIpXG5cbiAgICBpZiBcInN3aXRjaERhc2hib2FyZDpcIiBpbiBib2R5OlxuICAgICAgICByZXR1cm4gY29udGVudFxuXG4gICAgbWV0aG9kID0gXCJcIlwiXG4gIC8qKlxuICAgKiBSZXBsYWNlcyBEYXNoYm9hcmRQYWdlIGluIHRoZSBleGlzdGluZyBpZnJhbWUgZG9jdW1lbnQuXG4gICAqXG4gICAqIFRoZSB2YWx1ZSBtdXN0IGJlIHRoZSB0YXJnZXQgZGFzaGJvYXJkIFVVSUQvaWQgYWNjZXB0ZWQgYnlcbiAgICogRGFzaGJvYXJkUGFnZSwgbm90IHRoZSBlbWJlZGRlZC1jb25maWd1cmF0aW9uIFVVSUQgdXNlZCBpbiB0aGVcbiAgICogaW5pdGlhbCAvZW1iZWRkZWQvOnV1aWQgVVJMLlxuICAgKi9cbiAgc3dpdGNoRGFzaGJvYXJkOiAoXG4gICAgZGFzaGJvYXJkSWQ6IHN0cmluZyxcbiAgKSA9PiBQcm9taXNlPHtcbiAgICBkYXNoYm9hcmRJZDogc3RyaW5nO1xuICAgIGFjY2VwdGVkOiB0cnVlO1xuICB9PjtcblwiXCJcIlxuXG4gICAgdXBkYXRlZF90eXBlID0gKFxuICAgICAgICBtYXRjaC5ncm91cCgxKVxuICAgICAgICArIGJvZHkucnN0cmlwKClcbiAgICAgICAgKyBcIlxcXFxuXCJcbiAgICAgICAgKyBtZXRob2QucnN0cmlwKClcbiAgICAgICAgKyBtYXRjaC5ncm91cCgzKVxuICAgIClcblxuICAgIHJldHVybiAoXG4gICAgICAgIGNvbnRlbnRbOiBtYXRjaC5zdGFydCgpXVxuICAgICAgICArIHVwZGF0ZWRfdHlwZVxuICAgICAgICArIGNvbnRlbnRbbWF0Y2guZW5kKCkgOl1cbiAgICApXG5cblxuZGVmIHBhdGNoX2luaXRpYWxfZ3Vlc3RfdG9rZW5fZmV0Y2goY29udGVudDogc3RyKSAtPiBzdHI6XG4gICAgY2FuZGlkYXRlcyA9IFtcbiAgICAgICAgXCJmZXRjaEd1ZXN0VG9rZW4oKSxcIixcbiAgICAgICAgXCJhd2FpdCBmZXRjaEd1ZXN0VG9rZW4oKSxcIixcbiAgICBdXG5cbiAgICBmb3IgY2FuZGlkYXRlIGluIGNhbmRpZGF0ZXM6XG4gICAgICAgIGlmIGNvbnRlbnQuY291bnQoY2FuZGlkYXRlKSA+PSAxOlxuICAgICAgICAgICAgcmV0dXJuIGNvbnRlbnQucmVwbGFjZShcbiAgICAgICAgICAgICAgICBjYW5kaWRhdGUsXG4gICAgICAgICAgICAgICAgY2FuZGlkYXRlLnJlcGxhY2UoXG4gICAgICAgICAgICAgICAgICAgIFwiZmV0Y2hHdWVzdFRva2VuKClcIixcbiAgICAgICAgICAgICAgICAgICAgXCJmZXRjaEd1ZXN0VG9rZW4odW5kZWZpbmVkKVwiLFxuICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgMSxcbiAgICAgICAgICAgIClcblxuICAgIHJhaXNlIFBhdGNoRXJyb3IoXG4gICAgICAgIFwiQ291bGQgbm90IGxvY2F0ZSB0aGUgaW5pdGlhbCBmZXRjaEd1ZXN0VG9rZW4gY2FsbFwiXG4gICAgKVxuXG5cbmRlZiBwYXRjaF9ndWVzdF90b2tlbl9yZWZyZXNoKGNvbnRlbnQ6IHN0cikgLT4gc3RyOlxuICAgIHBhdHRlcm5zID0gW1xuICAgICAgICByZS5jb21waWxlKFxuICAgICAgICAgICAgclwiYXN5bmMgZnVuY3Rpb24gcmVmcmVzaEd1ZXN0VG9rZW5cXFxcKFxcXFwpIFxcXFx7XFxcXHMqXCJcbiAgICAgICAgICAgIHJcImNvbnN0IG5ld0d1ZXN0VG9rZW4gPSBhd2FpdCBmZXRjaEd1ZXN0VG9rZW5cXFxcKFxcXFwpO1xcXFxzKlwiXG4gICAgICAgICAgICByXCJvdXJQb3J0XFxcXC5lbWl0XFxcXChcIlxuICAgICAgICAgICAgclwiJ2d1ZXN0VG9rZW4nLFxcXFxzKlxcXFx7XFxcXHMqZ3Vlc3RUb2tlbjpcXFxccypuZXdHdWVzdFRva2VuXFxcXHMqXFxcXH1cIlxuICAgICAgICAgICAgclwiXFxcXCk7XFxcXHMqXCJcbiAgICAgICAgICAgIHJcInNldFRpbWVvdXRcXFxcKFwiXG4gICAgICAgICAgICByXCJyZWZyZXNoR3Vlc3RUb2tlbixcXFxccypcIlxuICAgICAgICAgICAgclwiZ2V0R3Vlc3RUb2tlblJlZnJlc2hUaW1pbmdcXFxcKG5ld0d1ZXN0VG9rZW5cXFxcKVwiXG4gICAgICAgICAgICByXCJcXFxcKTtcXFxccypcIlxuICAgICAgICAgICAgclwiXFxcXH1cIixcbiAgICAgICAgICAgIHJlLkRPVEFMTCxcbiAgICAgICAgKSxcbiAgICAgICAgcmUuY29tcGlsZShcbiAgICAgICAgICAgIHJcImNvbnN0IHJlZnJlc2hHdWVzdFRva2VuID0gYXN5bmMgXFxcXChcXFxcKSA9PiBcXFxce1xcXFxzKlwiXG4gICAgICAgICAgICByXCJjb25zdCBuZXdHdWVzdFRva2VuID0gYXdhaXQgZmV0Y2hHdWVzdFRva2VuXFxcXChcXFxcKTtcXFxccypcIlxuICAgICAgICAgICAgclwib3VyUG9ydFxcXFwuZW1pdFxcXFwoXCJcbiAgICAgICAgICAgIHJcIidndWVzdFRva2VuJyxcXFxccypcXFxce1xcXFxzKmd1ZXN0VG9rZW46XFxcXHMqbmV3R3Vlc3RUb2tlblxcXFxzKlxcXFx9XCJcbiAgICAgICAgICAgIHJcIlxcXFwpO1xcXFxzKlwiXG4gICAgICAgICAgICByXCJzZXRUaW1lb3V0XFxcXChcIlxuICAgICAgICAgICAgclwicmVmcmVzaEd1ZXN0VG9rZW4sXFxcXHMqXCJcbiAgICAgICAgICAgIHJcImdldEd1ZXN0VG9rZW5SZWZyZXNoVGltaW5nXFxcXChuZXdHdWVzdFRva2VuXFxcXClcIlxuICAgICAgICAgICAgclwiXFxcXCk7XFxcXHMqXCJcbiAgICAgICAgICAgIHJcIlxcXFx9O1wiLFxuICAgICAgICAgICAgcmUuRE9UQUxMLFxuICAgICAgICApLFxuICAgIF1cblxuICAgIHJlcGxhY2VtZW50ID0gXCJcIlwibGV0IGFjdGl2ZURhc2hib2FyZElkOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgYXN5bmMgZnVuY3Rpb24gcmVmcmVzaEd1ZXN0VG9rZW4oKSB7XG4gICAgY29uc3QgbmV3R3Vlc3RUb2tlbiA9IGF3YWl0IGZldGNoR3Vlc3RUb2tlbihcbiAgICAgIGFjdGl2ZURhc2hib2FyZElkLFxuICAgICk7XG5cbiAgICBvdXJQb3J0LmVtaXQoJ2d1ZXN0VG9rZW4nLCB7XG4gICAgICBndWVzdFRva2VuOiBuZXdHdWVzdFRva2VuLFxuICAgIH0pO1xuXG4gICAgc2V0VGltZW91dChcbiAgICAgIHJlZnJlc2hHdWVzdFRva2VuLFxuICAgICAgZ2V0R3Vlc3RUb2tlblJlZnJlc2hUaW1pbmcobmV3R3Vlc3RUb2tlbiksXG4gICAgKTtcbiAgfVwiXCJcIlxuXG4gICAgZm9yIHBhdHRlcm4gaW4gcGF0dGVybnM6XG4gICAgICAgIHVwZGF0ZWQsIGNvdW50ID0gcGF0dGVybi5zdWJuKFxuICAgICAgICAgICAgcmVwbGFjZW1lbnQsXG4gICAgICAgICAgICBjb250ZW50LFxuICAgICAgICAgICAgY291bnQ9MSxcbiAgICAgICAgKVxuXG4gICAgICAgIGlmIGNvdW50ID09IDE6XG4gICAgICAgICAgICByZXR1cm4gdXBkYXRlZFxuXG4gICAgcmFpc2UgUGF0Y2hFcnJvcihcbiAgICAgICAgXCJDb3VsZCBub3QgbG9jYXRlIHRoZSBndWVzdC10b2tlbiByZWZyZXNoIGZ1bmN0aW9uXCJcbiAgICApXG5cblxuZGVmIHBhdGNoX3Nka19mdW5jdGlvbnMoY29udGVudDogc3RyKSAtPiBzdHI6XG4gICAgYW5jaG9yX2NhbmRpZGF0ZXMgPSBbXG4gICAgICAgIChcbiAgICAgICAgICAgIFwiICBjb25zdCBnZXRTY3JvbGxTaXplID0gKCkgPT4gXCJcbiAgICAgICAgICAgIFwib3VyUG9ydC5nZXQ8U2l6ZT4oJ2dldFNjcm9sbFNpemUnKTtcIlxuICAgICAgICApLFxuICAgICAgICAoXG4gICAgICAgICAgICBcIiAgY29uc3QgZ2V0U2Nyb2xsU2l6ZSA9ICgpID0+XFxcXG5cIlxuICAgICAgICAgICAgXCIgICAgb3VyUG9ydC5nZXQ8U2l6ZT4oJ2dldFNjcm9sbFNpemUnKTtcIlxuICAgICAgICApLFxuICAgIF1cblxuICAgIHN3aXRjaF9mdW5jdGlvbiA9IFwiXCJcIiAgY29uc3Qgc3dpdGNoRGFzaGJvYXJkID0gYXN5bmMgKFxuICAgIGRhc2hib2FyZElkOiBzdHJpbmcsXG4gICk6IFByb21pc2U8e1xuICAgIGRhc2hib2FyZElkOiBzdHJpbmc7XG4gICAgYWNjZXB0ZWQ6IHRydWU7XG4gIH0+ID0+IHtcbiAgICBjb25zdCBub3JtYWxpemVkRGFzaGJvYXJkSWQgPSBkYXNoYm9hcmRJZC50cmltKCk7XG5cbiAgICBpZiAoIW5vcm1hbGl6ZWREYXNoYm9hcmRJZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnZGFzaGJvYXJkSWQgbXVzdCBiZSBhIG5vbi1lbXB0eSBzdHJpbmcnLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBjb25zdCBndWVzdFRva2VuID0gYXdhaXQgZmV0Y2hHdWVzdFRva2VuKFxuICAgICAgbm9ybWFsaXplZERhc2hib2FyZElkLFxuICAgICk7XG5cbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBvdXJQb3J0LmdldDx7XG4gICAgICBkYXNoYm9hcmRJZDogc3RyaW5nO1xuICAgICAgYWNjZXB0ZWQ6IHRydWU7XG4gICAgfT4oJ3N3aXRjaERhc2hib2FyZCcsIHtcbiAgICAgIGRhc2hib2FyZElkOiBub3JtYWxpemVkRGFzaGJvYXJkSWQsXG4gICAgICBndWVzdFRva2VuLFxuICAgIH0pO1xuXG4gICAgYWN0aXZlRGFzaGJvYXJkSWQgPSBub3JtYWxpemVkRGFzaGJvYXJkSWQ7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9O1xuXG5cIlwiXCJcblxuICAgIGZvciBhbmNob3IgaW4gYW5jaG9yX2NhbmRpZGF0ZXM6XG4gICAgICAgIGlmIGNvbnRlbnQuY291bnQoYW5jaG9yKSA9PSAxOlxuICAgICAgICAgICAgcmV0dXJuIGNvbnRlbnQucmVwbGFjZShcbiAgICAgICAgICAgICAgICBhbmNob3IsXG4gICAgICAgICAgICAgICAgc3dpdGNoX2Z1bmN0aW9uICsgYW5jaG9yLFxuICAgICAgICAgICAgICAgIDEsXG4gICAgICAgICAgICApXG5cbiAgICByYWlzZSBQYXRjaEVycm9yKFxuICAgICAgICBcIkNvdWxkIG5vdCBsb2NhdGUgdGhlIFNESyBnZXRTY3JvbGxTaXplIGZ1bmN0aW9uXCJcbiAgICApXG5cblxuZGVmIHBhdGNoX3Nka19yZXR1cm5fb2JqZWN0KGNvbnRlbnQ6IHN0cikgLT4gc3RyOlxuICAgIGNhbmRpZGF0ZXMgPSBbXG4gICAgICAgIFwiICAgIHNldFRoZW1lTW9kZSxcXFxcbiAgfTtcIixcbiAgICAgICAgXCIgICAgc2V0VGhlbWVNb2RlLFxcXFxuICAgIHVubW91bnQsXFxcXG4gIH07XCIsXG4gICAgICAgIFwiICAgIHVubW91bnQsXFxcXG4gICAgc2V0VGhlbWVNb2RlLFxcXFxuICB9O1wiLFxuICAgIF1cblxuICAgIGZvciBjYW5kaWRhdGUgaW4gY2FuZGlkYXRlczpcbiAgICAgICAgaWYgY29udGVudC5jb3VudChjYW5kaWRhdGUpID09IDE6XG4gICAgICAgICAgICBsaW5lcyA9IGNhbmRpZGF0ZS5zcGxpdGxpbmVzKClcblxuICAgICAgICAgICAgY2xvc2luZyA9IGxpbmVzLnBvcCgpXG4gICAgICAgICAgICBib2R5ID0gXCJcXFxcblwiLmpvaW4obGluZXMpXG5cbiAgICAgICAgICAgIHJldHVybiBjb250ZW50LnJlcGxhY2UoXG4gICAgICAgICAgICAgICAgY2FuZGlkYXRlLFxuICAgICAgICAgICAgICAgIGJvZHlcbiAgICAgICAgICAgICAgICArIFwiXFxcXG5cIlxuICAgICAgICAgICAgICAgICsgXCIgICAgc3dpdGNoRGFzaGJvYXJkLFxcXFxuXCJcbiAgICAgICAgICAgICAgICArIGNsb3NpbmcsXG4gICAgICAgICAgICAgICAgMSxcbiAgICAgICAgICAgIClcblxuICAgIHJhaXNlIFBhdGNoRXJyb3IoXG4gICAgICAgIFwiQ291bGQgbm90IGxvY2F0ZSB0aGUgRW1iZWRkZWREYXNoYm9hcmQgcmV0dXJuIG9iamVjdFwiXG4gICAgKVxuXG5cbmRlZiBwYXRjaF9zZGsoc2RrX3BhdGg6IFBhdGgpIC0+IE5vbmU6XG4gICAgY29udGVudCA9IHNka19wYXRoLnJlYWRfdGV4dChlbmNvZGluZz1cInV0Zi04XCIpXG5cbiAgICBpZiBcIlNVUEVSU0VUX1NXSVRDSEFCTEVfRU1CRURfU0RLX1BBVENIXCIgaW4gY29udGVudDpcbiAgICAgICAgcHJpbnQoZlwiQWxyZWFkeSBwYXRjaGVkOiB7c2RrX3BhdGh9XCIpXG4gICAgICAgIHJldHVyblxuXG4gICAgY29udGVudCA9IHBhdGNoX2d1ZXN0X3Rva2VuX2ZldGNoX3R5cGUoY29udGVudClcbiAgICBjb250ZW50ID0gcGF0Y2hfZW1iZWRkZWRfZGFzaGJvYXJkX3R5cGUoY29udGVudClcbiAgICBjb250ZW50ID0gcGF0Y2hfaW5pdGlhbF9ndWVzdF90b2tlbl9mZXRjaChjb250ZW50KVxuICAgIGNvbnRlbnQgPSBwYXRjaF9ndWVzdF90b2tlbl9yZWZyZXNoKGNvbnRlbnQpXG4gICAgY29udGVudCA9IHBhdGNoX3Nka19mdW5jdGlvbnMoY29udGVudClcbiAgICBjb250ZW50ID0gcGF0Y2hfc2RrX3JldHVybl9vYmplY3QoY29udGVudClcblxuICAgIHNka19wYXRoLndyaXRlX3RleHQoY29udGVudCwgZW5jb2Rpbmc9XCJ1dGYtOFwiKVxuICAgIHByaW50KGZcIlBhdGNoZWQ6IHtzZGtfcGF0aH1cIilcblxuXG5kZWYgbWFpbigpIC0+IGludDpcbiAgICBwYXJzZXIgPSBhcmdwYXJzZS5Bcmd1bWVudFBhcnNlcihcbiAgICAgICAgZGVzY3JpcHRpb249KFxuICAgICAgICAgICAgXCJBcHBseSB0aGUgc3dpdGNoYWJsZS1kYXNoYm9hcmQgcGF0Y2ggdG8gYSBcIlxuICAgICAgICAgICAgXCJTdXBlcnNldCA2LjEueCBzb3VyY2UgY2hlY2tvdXQuXCJcbiAgICAgICAgKVxuICAgIClcblxuICAgIHBhcnNlci5hZGRfYXJndW1lbnQoXG4gICAgICAgIFwic3VwZXJzZXRfcm9vdFwiLFxuICAgICAgICB0eXBlPVBhdGgsXG4gICAgICAgIGhlbHA9XCJQYXRoIHRvIHRoZSBBcGFjaGUgU3VwZXJzZXQgc291cmNlIGNoZWNrb3V0XCIsXG4gICAgKVxuXG4gICAgYXJncyA9IHBhcnNlci5wYXJzZV9hcmdzKClcbiAgICByb290ID0gYXJncy5zdXBlcnNldF9yb290LnJlc29sdmUoKVxuXG4gICAgZnJvbnRlbmRfcGF0aCA9IHJvb3QgLyBGUk9OVEVORF9GSUxFXG4gICAgc2RrX3BhdGggPSByb290IC8gU0RLX0ZJTEVcblxuICAgIG1pc3NpbmcgPSBbXG4gICAgICAgIHBhdGhcbiAgICAgICAgZm9yIHBhdGggaW4gW2Zyb250ZW5kX3BhdGgsIHNka19wYXRoXVxuICAgICAgICBpZiBub3QgcGF0aC5leGlzdHMoKVxuICAgIF1cblxuICAgIGlmIG1pc3Npbmc6XG4gICAgICAgIHByaW50KFxuICAgICAgICAgICAgXCJUaGUgdGFyZ2V0IGRvZXMgbm90IGFwcGVhciB0byBiZSBhIGNvbXBhdGlibGUgXCJcbiAgICAgICAgICAgIFwiU3VwZXJzZXQgc291cmNlIGNoZWNrb3V0LlwiLFxuICAgICAgICAgICAgZmlsZT1zeXMuc3RkZXJyLFxuICAgICAgICApXG5cbiAgICAgICAgZm9yIG1pc3NpbmdfcGF0aCBpbiBtaXNzaW5nOlxuICAgICAgICAgICAgcHJpbnQoZlwiTWlzc2luZzoge21pc3NpbmdfcGF0aH1cIiwgZmlsZT1zeXMuc3RkZXJyKVxuXG4gICAgICAgIHJldHVybiAyXG5cbiAgICB0cnk6XG4gICAgICAgIHBhdGNoX2Zyb250ZW5kKGZyb250ZW5kX3BhdGgpXG4gICAgICAgIHBhdGNoX3NkayhzZGtfcGF0aClcbiAgICBleGNlcHQgUGF0Y2hFcnJvciBhcyBlcnJvcjpcbiAgICAgICAgcHJpbnQoZlwiUGF0Y2ggZmFpbGVkOiB7ZXJyb3J9XCIsIGZpbGU9c3lzLnN0ZGVycilcbiAgICAgICAgcmV0dXJuIDFcblxuICAgIHByaW50KFwiUGF0Y2ggY29tcGxldGVkIHN1Y2Nlc3NmdWxseS5cIilcbiAgICByZXR1cm4gMFxuXG5cbmlmIF9fbmFtZV9fID09IFwiX19tYWluX19cIjpcbiAgICByYWlzZSBTeXN0ZW1FeGl0KG1haW4oKSlcbmA7XG5cbmNvbnN0IFZFUklGWV9QQVRDSF9TSCA9IGAjIS91c3IvYmluL2VudiBiYXNoXG5cbnNldCAtZXVvIHBpcGVmYWlsXG5cblNVUEVSU0VUX1JPT1Q9XFwkezE6P1VzYWdlOiB2ZXJpZnlfcGF0Y2guc2ggL3BhdGgvdG8vc3VwZXJzZXR9XG5cbkZST05URU5EX0ZJTEU9XCIkU1VQRVJTRVRfUk9PVC9zdXBlcnNldC1mcm9udGVuZC9zcmMvZW1iZWRkZWQvaW5kZXgudHN4XCJcblNES19GSUxFPVwiJFNVUEVSU0VUX1JPT1Qvc3VwZXJzZXQtZW1iZWRkZWQtc2RrL3NyYy9pbmRleC50c1wiXG5cbnRlc3QgLWYgXCIkRlJPTlRFTkRfRklMRVwiXG50ZXN0IC1mIFwiJFNES19GSUxFXCJcblxuZ3JlcCAtcSBcXFxcXG4gIFwiU1VQRVJTRVRfU1dJVENIQUJMRV9FTUJFRF9QQVRDSFwiIFxcXFxcbiAgXCIkRlJPTlRFTkRfRklMRVwiXG5cbmdyZXAgLXEgXFxcXFxuICBcIidzd2l0Y2hEYXNoYm9hcmQnXCIgXFxcXFxuICBcIiRGUk9OVEVORF9GSUxFXCJcblxuZ3JlcCAtcSBcXFxcXG4gIFwiZGFzaGJvYXJkU3dpdGNoSGFuZGxlclwiIFxcXFxcbiAgXCIkRlJPTlRFTkRfRklMRVwiXG5cbmdyZXAgLXEgXFxcXFxuICBcIlNVUEVSU0VUX1NXSVRDSEFCTEVfRU1CRURfU0RLX1BBVENIXCIgXFxcXFxuICBcIiRTREtfRklMRVwiXG5cbmdyZXAgLXEgXFxcXFxuICBcInN3aXRjaERhc2hib2FyZDpcIiBcXFxcXG4gIFwiJFNES19GSUxFXCJcblxuZ3JlcCAtcSBcXFxcXG4gIFwiYWN0aXZlRGFzaGJvYXJkSWRcIiBcXFxcXG4gIFwiJFNES19GSUxFXCJcblxuZ3JlcCAtcSBcXFxcXG4gIFwiZmV0Y2hHdWVzdFRva2VuKFwiIFxcXFxcbiAgXCIkU0RLX0ZJTEVcIlxuXG5lY2hvIFwiVGhlIHN3aXRjaGFibGUtZGFzaGJvYXJkIHBhdGNoIGlzIHByZXNlbnQuXCJcbmA7XG5cbmNvbnN0IEJVSUxEX1NES19TSCA9IGAjIS91c3IvYmluL2VudiBiYXNoXG5cbnNldCAtZXVvIHBpcGVmYWlsXG5cblNVUEVSU0VUX1JPT1Q9XFwkezE6P1VzYWdlOiBidWlsZC1zZGsuc2ggL3BhdGgvdG8vc3VwZXJzZXR9XG5cblNES19ST09UPVwiJFNVUEVSU0VUX1JPT1Qvc3VwZXJzZXQtZW1iZWRkZWQtc2RrXCJcblxuaWYgWyAhIC1mIFwiJFNES19ST09UL3BhY2thZ2UuanNvblwiIF07IHRoZW5cbiAgZWNobyBcIk1pc3NpbmcgU0RLIHBhY2thZ2U6ICRTREtfUk9PVFwiID4mMlxuICBleGl0IDFcbmZpXG5cbmNkIFwiJFNES19ST09UXCJcblxubnBtIGNpXG5cbm5wbSBydW4gYnVpbGRcblxuZWNob1xuZWNobyBcIkVtYmVkZGVkIFNESyBidWlsZCBjb21wbGV0ZWQuXCJcbmVjaG8gXCJPdXRwdXQgZGlyZWN0b3J5OlwiXG5maW5kIC4gXFxcXFxuICAtbWF4ZGVwdGggMiBcXFxcXG4gIC10eXBlIGQgXFxcXFxuICBcXFxcKCAtbmFtZSBkaXN0IC1vIC1uYW1lIGxpYiBcXFxcKSBcXFxcXG4gIC1wcmludFxuYDtcblxuY29uc3QgQlVJTERfRlJPTlRFTkRfU0ggPSBgIyEvdXNyL2Jpbi9lbnYgYmFzaFxuXG5zZXQgLWV1byBwaXBlZmFpbFxuXG5TVVBFUlNFVF9ST09UPVxcJHsxOj9Vc2FnZTogYnVpbGQtc3VwZXJzZXQtZnJvbnRlbmQuc2ggL3BhdGgvdG8vc3VwZXJzZXR9XG5cbkZST05URU5EX1JPT1Q9XCIkU1VQRVJTRVRfUk9PVC9zdXBlcnNldC1mcm9udGVuZFwiXG5cbmlmIFsgISAtZiBcIiRGUk9OVEVORF9ST09UL3BhY2thZ2UuanNvblwiIF07IHRoZW5cbiAgZWNobyBcIk1pc3NpbmcgU3VwZXJzZXQgZnJvbnRlbmQ6ICRGUk9OVEVORF9ST09UXCIgPiYyXG4gIGV4aXQgMVxuZmlcblxuY2QgXCIkRlJPTlRFTkRfUk9PVFwiXG5cbm5wbSBjaVxuXG5ucG0gcnVuIGJ1aWxkXG5cbmVjaG8gXCJTdXBlcnNldCBmcm9udGVuZCBidWlsZCBjb21wbGV0ZWQuXCJcbmA7XG5cbmNvbnN0IFBBVENIX0FORF9CVUlMRF9TSCA9IGAjIS91c3IvYmluL2VudiBiYXNoXG5cbnNldCAtZXVvIHBpcGVmYWlsXG5cblNVUEVSU0VUX1JPT1Q9XFwkezE6P1VzYWdlOiBwYXRjaC1hbmQtYnVpbGQuc2ggL3BhdGgvdG8vc3VwZXJzZXR9XG5cblNDUklQVF9ESVI9JChcbiAgY2QgXCIkKGRpcm5hbWUgXCJcXCR7QkFTSF9TT1VSQ0VbMF19XCIpXCJcbiAgcHdkXG4pXG5cblBST0pFQ1RfUk9PVD0kKFxuICBjZCBcIiRTQ1JJUFRfRElSLy4uXCJcbiAgcHdkXG4pXG5cbnB5dGhvbjMgXFxcXFxuICBcIiRQUk9KRUNUX1JPT1QvcGF0Y2hlcy9hcHBseV9zdXBlcnNldF82XzFfcGF0Y2gucHlcIiBcXFxcXG4gIFwiJFNVUEVSU0VUX1JPT1RcIlxuXG5cIiRQUk9KRUNUX1JPT1QvcGF0Y2hlcy92ZXJpZnlfcGF0Y2guc2hcIiBcXFxcXG4gIFwiJFNVUEVSU0VUX1JPT1RcIlxuXG5cIiRTQ1JJUFRfRElSL2J1aWxkLXNkay5zaFwiIFxcXFxcbiAgXCIkU1VQRVJTRVRfUk9PVFwiXG5cblwiJFNDUklQVF9ESVIvYnVpbGQtc3VwZXJzZXQtZnJvbnRlbmQuc2hcIiBcXFxcXG4gIFwiJFNVUEVSU0VUX1JPT1RcIlxuXG5lY2hvXG5lY2hvIFwiUGF0Y2ggYW5kIGZyb250ZW5kIGJ1aWxkcyBjb21wbGV0ZWQuXCJcbmA7XG5cbmNvbnN0IEJVSUxEX0RPQ0tFUl9TSCA9IGAjIS91c3IvYmluL2VudiBiYXNoXG5cbnNldCAtZXVvIHBpcGVmYWlsXG5cblNVUEVSU0VUX1JPT1Q9XFwkezE6P1VzYWdlOiBidWlsZC1kb2NrZXItaW1hZ2Uuc2ggL3BhdGgvdG8vc3VwZXJzZXQgW2ltYWdlLXRhZ119XG5JTUFHRV9UQUc9XFwkezI6LWxvY2FsL3N1cGVyc2V0LXN3aXRjaGFibGU6Ni4xLjB9XG5cblNDUklQVF9ESVI9JChcbiAgY2QgXCIkKGRpcm5hbWUgXCJcXCR7QkFTSF9TT1VSQ0VbMF19XCIpXCJcbiAgcHdkXG4pXG5cblBST0pFQ1RfUk9PVD0kKFxuICBjZCBcIiRTQ1JJUFRfRElSLy4uXCJcbiAgcHdkXG4pXG5cbnB5dGhvbjMgXFxcXFxuICBcIiRQUk9KRUNUX1JPT1QvcGF0Y2hlcy9hcHBseV9zdXBlcnNldF82XzFfcGF0Y2gucHlcIiBcXFxcXG4gIFwiJFNVUEVSU0VUX1JPT1RcIlxuXG5cIiRQUk9KRUNUX1JPT1QvcGF0Y2hlcy92ZXJpZnlfcGF0Y2guc2hcIiBcXFxcXG4gIFwiJFNVUEVSU0VUX1JPT1RcIlxuXG5jZCBcIiRTVVBFUlNFVF9ST09UXCJcblxuZG9ja2VyIGJ1aWxkIFxcXFxcbiAgLS10YXJnZXQgbGVhbiBcXFxcXG4gIC0tdGFnIFwiJElNQUdFX1RBR1wiIFxcXFxcbiAgLlxuXG5lY2hvIFwiQnVpbHQgRG9ja2VyIGltYWdlOiAkSU1BR0VfVEFHXCJcbmA7XG5cbmNvbnN0IFJFQURNRSA9IGAjICR7U1VQRVJTRVRfUExVR0lOX0lEfVxuXG5NdWx0aS10ZW5hbnQsIG9yZy1hZ25vc3RpYyBTdXBlcnNldCBkYXNoYm9hcmQgZW1iZWQgcGx1Z2luIChwYXRjaC1hbmQtYnVpbGQgc3RyYXRlZ3kpLlxuXG5UaGlzIGlzIG5vdCBhIGdlbmVyYXRlZCBwbHVnaW4gbGlrZSB0aGUgS2liYW5hIHZhcmlhbnQuIEluc3RlYWQsIERlY2FmIGhvbGRzXG5wYXRjaCBzY3JpcHRzIHRoYXQgbW9kaWZ5IFN1cGVyc2V0J3MgaW50ZXJuYWwgZW1iZWRkZWQgZnJvbnRlbmQgYW5kIGVtYmVkZGVkXG5TREsgc291cmNlIGZpbGVzIHRvIGFkZCBhIFxcYHN3aXRjaERhc2hib2FyZFxcYCBtZXRob2QuIFRoZSBleGlzdGluZyBpZnJhbWVcbmVsZW1lbnQsIGRvY3VtZW50LCBjb250ZW50V2luZG93LCBSZWFjdCBydW50aW1lLCBhbmQgU3dpdGNoYm9hcmQgTWVzc2FnZUNoYW5uZWxcbmFyZSBwcmVzZXJ2ZWQgYWNyb3NzIGRhc2hib2FyZCBzd2l0Y2hlcy5cblxuIyMgV2hhdCBpdCBwYXRjaGVzXG5cbi0gXFxgc3VwZXJzZXQtZnJvbnRlbmQvc3JjL2VtYmVkZGVkL2luZGV4LnRzeFxcYCDigJQgYWRkcyBcXGBzd2l0Y2hEYXNoYm9hcmRcXGAgdG8gdGhlXG4gIFN3aXRjaGJvYXJkIGNoYW5uZWw7IHJlcGxhY2VzIG9ubHkgXFxgRGFzaGJvYXJkUGFnZVxcYC5cbi0gXFxgc3VwZXJzZXQtZW1iZWRkZWQtc2RrL3NyYy9pbmRleC50c1xcYCDigJQgYWRkc1xuICBcXGBlbWJlZGRlZERhc2hib2FyZC5zd2l0Y2hEYXNoYm9hcmQoZGFzaGJvYXJkSWQpXFxgOyByZXF1ZXN0cyBhIGd1ZXN0IHRva2VuIGZvclxuICB0aGUgc2VsZWN0ZWQgZGFzaGJvYXJkOyByZWZyZXNoZXMgZnV0dXJlIGd1ZXN0IHRva2VucyBmb3IgdGhlIGFjdGl2ZSBkYXNoYm9hcmQuXG5cbiMjIEJ1aWxkXG5cblxcYFxcYFxcYGJhc2hcbiMgQXBwbHkgcGF0Y2ggKyB2ZXJpZnkgKyBidWlsZCBTREsgKyBidWlsZCBmcm9udGVuZFxuYnVpbGQvcGF0Y2gtYW5kLWJ1aWxkLnNoIC9wYXRoL3RvL3N1cGVyc2V0XG5cbiMgT3IgYnVpbGQgYSBEb2NrZXIgaW1hZ2VcbmJ1aWxkL2J1aWxkLWRvY2tlci1pbWFnZS5zaCAvcGF0aC90by9zdXBlcnNldCByZWdpc3RyeS5leGFtcGxlLmNvbS9zdXBlcnNldC1zd2l0Y2hhYmxlOjYuMS4wXG5cXGBcXGBcXGBcblxuVGhlIHBhdGNoZWQgU0RLIG11c3QgYmUgcGFja2VkIGFuZCBpbnN0YWxsZWQgaW50byB0aGUgQW5ndWxhciBhcHBsaWNhdGlvbjpcblxuXFxgXFxgXFxgYmFzaFxuY2QgL3BhdGgvdG8vc3VwZXJzZXQvc3VwZXJzZXQtZW1iZWRkZWQtc2RrXG5ucG0gY2kgJiYgbnBtIHJ1biBidWlsZCAmJiBucG0gcGFja1xuY2QgL3BhdGgvdG8vYW5ndWxhci1hcHBcbm5wbSBpbnN0YWxsIC9wYXRoL3RvL3N1cGVyc2V0L3N1cGVyc2V0LWVtYmVkZGVkLXNkay9zdXBlcnNldC11aS1lbWJlZGRlZC1zZGstKi50Z3pcblxcYFxcYFxcYFxuXG5WZXJzaW9uOiAke1NVUEVSU0VUX1BMVUdJTl9WRVJTSU9OfVxuYDtcblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBmdWxsIHNldCBvZiBwYXRjaCBhbmQgYnVpbGQgc2NyaXB0IGZpbGVzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc3VwZXJzZXRQYXRjaEZpbGVzKCk6IFN1cGVyc2V0UGF0Y2hGaWxlW10ge1xuICByZXR1cm4gW1xuICAgIHsgcGF0aDogXCJwYXRjaGVzL2FwcGx5X3N1cGVyc2V0XzZfMV9wYXRjaC5weVwiLCBjb250ZW50OiBBUFBMWV9QQVRDSF9QWSwgZXhlY3V0YWJsZTogdHJ1ZSB9LFxuICAgIHsgcGF0aDogXCJwYXRjaGVzL3ZlcmlmeV9wYXRjaC5zaFwiLCBjb250ZW50OiBWRVJJRllfUEFUQ0hfU0gsIGV4ZWN1dGFibGU6IHRydWUgfSxcbiAgICB7IHBhdGg6IFwiYnVpbGQvYnVpbGQtc2RrLnNoXCIsIGNvbnRlbnQ6IEJVSUxEX1NES19TSCwgZXhlY3V0YWJsZTogdHJ1ZSB9LFxuICAgIHsgcGF0aDogXCJidWlsZC9idWlsZC1zdXBlcnNldC1mcm9udGVuZC5zaFwiLCBjb250ZW50OiBCVUlMRF9GUk9OVEVORF9TSCwgZXhlY3V0YWJsZTogdHJ1ZSB9LFxuICAgIHsgcGF0aDogXCJidWlsZC9wYXRjaC1hbmQtYnVpbGQuc2hcIiwgY29udGVudDogUEFUQ0hfQU5EX0JVSUxEX1NILCBleGVjdXRhYmxlOiB0cnVlIH0sXG4gICAgeyBwYXRoOiBcImJ1aWxkL2J1aWxkLWRvY2tlci1pbWFnZS5zaFwiLCBjb250ZW50OiBCVUlMRF9ET0NLRVJfU0gsIGV4ZWN1dGFibGU6IHRydWUgfSxcbiAgICB7IHBhdGg6IFwiUkVBRE1FLm1kXCIsIGNvbnRlbnQ6IFJFQURNRSB9LFxuICBdO1xufVxuIl19