vite-plugin-react-server 1.4.2 → 1.4.4

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 (74) hide show
  1. package/README.md +48 -313
  2. package/dist/package.json +123 -13
  3. package/dist/plugin/bundle/deferredStaticGeneration.js +14 -39
  4. package/dist/plugin/bundle/manifests.js +30 -48
  5. package/dist/plugin/config/autoDiscover/resolveAutoDiscover.d.ts.map +1 -1
  6. package/dist/plugin/config/autoDiscover/resolveAutoDiscover.js +4 -1
  7. package/dist/plugin/config/envPrefixFromConfig.js +12 -7
  8. package/dist/plugin/config/getCondition.d.ts.map +1 -1
  9. package/dist/plugin/config/getCondition.js +7 -5
  10. package/dist/plugin/dev-server/virtualRscHmrPlugin.js +23 -23
  11. package/dist/plugin/environments/createBuildEventPlugin.js +88 -98
  12. package/dist/plugin/environments/createEnvironmentPlugin.js +222 -250
  13. package/dist/plugin/helpers/createRscRenderHelpers.js +33 -34
  14. package/dist/plugin/helpers/createSharedLoader.d.ts.map +1 -1
  15. package/dist/plugin/helpers/createSharedLoader.js +4 -2
  16. package/dist/plugin/helpers/headlessStreamReuseHandler.js +30 -22
  17. package/dist/plugin/helpers/headlessStreamState.js +15 -28
  18. package/dist/plugin/helpers/resolveComponent.d.ts.map +1 -1
  19. package/dist/plugin/helpers/resolveComponent.js +4 -2
  20. package/dist/plugin/index.client.d.ts +5 -0
  21. package/dist/plugin/index.client.d.ts.map +1 -0
  22. package/dist/plugin/index.client.js +4 -0
  23. package/dist/plugin/index.d.ts +4 -3
  24. package/dist/plugin/index.d.ts.map +1 -1
  25. package/dist/plugin/index.js +10 -5
  26. package/dist/plugin/index.server.d.ts +5 -0
  27. package/dist/plugin/index.server.d.ts.map +1 -0
  28. package/dist/plugin/index.server.js +4 -0
  29. package/dist/plugin/metrics/createWorkerStartupMetrics.js +31 -13
  30. package/dist/plugin/orchestrator/createPluginOrchestrator.client.js +41 -38
  31. package/dist/plugin/orchestrator/createPluginOrchestrator.server.js +43 -46
  32. package/dist/plugin/plugin.client.js +2 -2
  33. package/dist/plugin/plugin.server.js +2 -2
  34. package/dist/plugin/react-static/createBuildLoader.client.js +12 -6
  35. package/dist/plugin/react-static/createBuildLoader.server.js +255 -235
  36. package/dist/plugin/react-static/plugin.client.js +684 -770
  37. package/dist/plugin/react-static/plugin.server.js +517 -603
  38. package/dist/plugin/react-static/processCssFilesForPages.js +103 -88
  39. package/dist/plugin/react-static/renderPage.client.js +455 -529
  40. package/dist/plugin/react-static/renderPage.server.js +485 -508
  41. package/dist/plugin/react-static/renderPagesBatched.js +277 -275
  42. package/dist/plugin/react-static/rscToHtmlStream.client.js +48 -29
  43. package/dist/plugin/react-static/rscToHtmlStream.server.js +62 -37
  44. package/dist/plugin/react-static/temporaryReferences.server.js +11 -2
  45. package/dist/plugin/stream/createMainThreadHandlers.js +40 -31
  46. package/dist/plugin/stream/renderRscStream.server.d.ts.map +1 -1
  47. package/dist/plugin/stream/renderRscStream.server.js +127 -144
  48. package/dist/plugin/transformer/createTransformerPlugin.js +226 -265
  49. package/dist/plugin/utils/checkReactVersion.d.ts +7 -0
  50. package/dist/plugin/utils/checkReactVersion.d.ts.map +1 -0
  51. package/dist/plugin/utils/checkReactVersion.js +23 -0
  52. package/dist/plugin/utils/envUrls.node.js +12 -11
  53. package/dist/plugin/vendor/vendor-alias.js +84 -114
  54. package/dist/plugin/vendor/vendor.client.d.ts.map +1 -1
  55. package/dist/plugin/vendor/vendor.client.js +1 -3
  56. package/dist/plugin/worker/rsc/handleRscRender.d.ts.map +1 -1
  57. package/dist/plugin/worker/rsc/handleRscRender.js +3 -1
  58. package/dist/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +123 -13
  60. package/plugin/config/autoDiscover/resolveAutoDiscover.ts +4 -0
  61. package/plugin/config/getCondition.ts +6 -4
  62. package/plugin/helpers/createRscRenderHelpers.ts +1 -1
  63. package/plugin/helpers/createSharedLoader.ts +6 -1
  64. package/plugin/helpers/resolveComponent.ts +6 -1
  65. package/plugin/index.client.ts +4 -0
  66. package/plugin/index.server.ts +4 -0
  67. package/plugin/index.ts +12 -5
  68. package/plugin/plugin.client.ts +1 -1
  69. package/plugin/plugin.server.ts +1 -1
  70. package/plugin/stream/renderRscStream.server.ts +3 -0
  71. package/plugin/utils/checkReactVersion.ts +28 -0
  72. package/plugin/vendor/vendor.client.ts +0 -2
  73. package/plugin/worker/rsc/handleRscRender.ts +2 -0
  74. package/scripts/generate-toc.mjs +27 -294
@@ -1,555 +1,481 @@
1
1
  /**
2
- * renderPage.client.ts
3
- *
4
- * PURPOSE: Client-side static page rendering for React Server Components
5
- *
6
- * ARCHITECTURE OVERVIEW:
7
- *
8
- * CLIENT-SIDE vs SERVER-SIDE:
9
- * - Server-side: RSC generation in main thread, HTML generation in worker
10
- * - Client-side: RSC generation in worker, HTML generation in main thread
11
- *
12
- * FLOW:
13
- * 1. RSC Worker generates RSC content with HTML wrapper
14
- * 2. RSC content is buffered to allow dual consumption
15
- * 3. Buffered RSC stream is consumed twice:
16
- * - For RSC file writing (index.rsc)
17
- * - For HTML transformation (index.html)
18
- * 4. HTML transform processes RSC content in main thread
19
- * 5. Both files are written to filesystem
20
- *
21
- * KEY INSIGHT: Node.js streams can only be consumed once, so we buffer the RSC
22
- * content to allow it to be used for both RSC file generation and HTML transformation.
23
- * This follows the pattern from collectRscContent.ts.
24
- *
25
- * HELPER FUNCTIONS:
26
- * - createBufferedRscStream: Creates a buffered stream for dual consumption
27
- * - createRscToHtmlStream: Transforms RSC content to HTML in main thread
28
- *
29
- * USAGE:
30
- * ```typescript
31
- * const result = await renderPage({
32
- * route: "/",
33
- * pagePath: "src/page/page.tsx",
34
- * // ... other options
35
- * });
36
- *
37
- * // result.html.pipe(htmlFileWriter);
38
- * // result.rsc.pipe(rscFileWriter);
39
- * ```
2
+ * vite-plugin-react-server
3
+ * Copyright (c) Nico Brinkkemper
4
+ * MIT License
40
5
  */
41
- import { createRenderMetrics } from "../metrics/createRenderMetrics.js";
42
- import { routeToURL } from "../utils/routeToURL.js";
43
- import { handleError } from "../error/handleError.js";
44
- import { assertNonReactServer } from "../config/getCondition.js";
45
- import { createRscStream } from "../stream/createRscStream.client.js";
46
- import { resolveComponents } from "../helpers/resolveComponents.client.js";
47
- import { join } from "node:path";
48
- import { createStreamMetrics } from "../metrics/createStreamMetrics.js";
49
- import { performance } from "node:perf_hooks";
50
- import { createRscToHtmlStream } from "./rscToHtmlStream.client.js";
6
+ import { createRenderMetrics } from '../metrics/createRenderMetrics.js';
7
+ import { routeToURL } from '../utils/routeToURL.js';
8
+ import { handleError } from '../error/handleError.js';
9
+ import { assertNonReactServer } from '../config/getCondition.js';
10
+ import { createRscStream } from '../stream/createRscStream.client.js';
11
+ import { resolveComponents } from '../helpers/resolveComponents.client.js';
12
+ import { join } from 'node:path';
13
+ import { createStreamMetrics } from '../metrics/createStreamMetrics.js';
14
+ import { performance } from 'node:perf_hooks';
15
+ import { createRscToHtmlStream } from './rscToHtmlStream.client.js';
16
+
51
17
  assertNonReactServer();
52
- /**
53
- * Client version of renderPage that uses the react-client pattern
54
- * This works in REVERSE from the server plugin:
55
- * - Server: Main thread (RSC) + HTML worker (HTML)
56
- * - Client: RSC worker (RSC) + Main thread (HTML)
57
- */
58
- export const renderPage = async function* _renderPageClient(handlerOptions) {
18
+ const renderPage = async function* _renderPageClient(handlerOptions) {
19
+ if (handlerOptions.verbose) {
20
+ handlerOptions.logger?.info(
21
+ `[renderPage.client] onEvent callback exists: ${!!handlerOptions.onEvent}`
22
+ );
23
+ handlerOptions.logger?.info(
24
+ `[renderPage.client] onMetrics callback exists: ${!!handlerOptions.onMetrics}`
25
+ );
26
+ }
27
+ let hasYielded = false;
28
+ let errorResult = null;
29
+ const wrappedOnEvent = (event) => {
30
+ if (handlerOptions.onEvent) {
31
+ handlerOptions.onEvent(event);
32
+ }
33
+ if (event.type === "route.error" && !hasYielded) {
34
+ hasYielded = true;
35
+ const panicError = handleError({
36
+ error: event.data.error,
37
+ logger: handlerOptions.logger,
38
+ panicThreshold: event.data.panicThreshold,
39
+ context: `route.error (${event.data.route})`
40
+ });
41
+ if (panicError != null) {
42
+ errorResult = {
43
+ type: "error",
44
+ error: panicError,
45
+ metrics: {
46
+ rscHeadless: { duration: 0, chunks: 0, bytes: 0 },
47
+ html: { duration: 0, chunks: 0, bytes: 0 }
48
+ }
49
+ };
50
+ } else {
51
+ errorResult = {
52
+ type: "skip",
53
+ reason: event.data.error.message || "Non-panic error occurred",
54
+ html: { duration: 0, chunks: 0, bytes: 0 },
55
+ rsc: { duration: 0, chunks: 0, bytes: 0 },
56
+ metrics: {
57
+ rscHeadless: { duration: 0, chunks: 0, bytes: 0 },
58
+ html: { duration: 0, chunks: 0, bytes: 0 }
59
+ }
60
+ };
61
+ }
62
+ }
63
+ };
64
+ if (!handlerOptions.pagePath && !handlerOptions.PageComponent) {
65
+ const emptyStreamWrapper = {
66
+ pipe: (destination) => {
67
+ destination.end();
68
+ return destination;
69
+ },
70
+ abort: () => {
71
+ }
72
+ };
73
+ yield {
74
+ type: "skip",
75
+ reason: "No pagePath and no PageComponent provided",
76
+ html: emptyStreamWrapper,
77
+ rsc: emptyStreamWrapper,
78
+ metrics: {
79
+ rscFull: createRenderMetrics({
80
+ route: handlerOptions.route,
81
+ type: "rsc-full",
82
+ fromMainThread: false,
83
+ fromRscWorker: true,
84
+ fromHtmlWorker: false
85
+ }),
86
+ rscHeadless: createRenderMetrics({
87
+ route: handlerOptions.route,
88
+ type: "rsc-headless",
89
+ fromMainThread: false,
90
+ fromRscWorker: true,
91
+ fromHtmlWorker: false
92
+ }),
93
+ html: createRenderMetrics({
94
+ route: handlerOptions.route,
95
+ type: "html",
96
+ fromMainThread: true,
97
+ fromRscWorker: false,
98
+ fromHtmlWorker: false
99
+ })
100
+ }
101
+ };
102
+ return;
103
+ }
104
+ if (!handlerOptions.url) {
105
+ handlerOptions.url = routeToURL(
106
+ handlerOptions.route,
107
+ handlerOptions.moduleBaseURL,
108
+ handlerOptions.build.rscOutputPath
109
+ );
110
+ }
111
+ const baseDir = join(
112
+ handlerOptions.build.outDir,
113
+ handlerOptions.build.static
114
+ );
115
+ const routePath = handlerOptions.route.replace(/^\//, "");
116
+ const htmlMetrics = createRenderMetrics({
117
+ route: handlerOptions.route,
118
+ type: "html",
119
+ fromMainThread: true,
120
+ // Client: HTML rendered on main thread
121
+ fromRscWorker: false,
122
+ fromHtmlWorker: false,
123
+ baseDir,
124
+ routePath,
125
+ fileName: handlerOptions.build.htmlOutputPath,
126
+ outputPath: join(baseDir, routePath, handlerOptions.build.htmlOutputPath)
127
+ });
128
+ const rscFullMetrics = createRenderMetrics({
129
+ route: handlerOptions.route,
130
+ type: "rsc-full",
131
+ fromMainThread: false,
132
+ fromRscWorker: true,
133
+ // Client: RSC rendered on RSC worker
134
+ fromHtmlWorker: false
135
+ });
136
+ const rscHeadlessMetrics = createRenderMetrics({
137
+ route: handlerOptions.route,
138
+ type: "rsc-headless",
139
+ fromMainThread: false,
140
+ fromRscWorker: true,
141
+ // Client: RSC rendered on RSC worker
142
+ fromHtmlWorker: false,
143
+ baseDir,
144
+ routePath,
145
+ fileName: handlerOptions.build.rscOutputPath,
146
+ outputPath: join(baseDir, routePath, handlerOptions.build.rscOutputPath)
147
+ });
148
+ let headlessRscStream = null;
149
+ let fullRscStream = null;
150
+ let htmlHandler = null;
151
+ try {
59
152
  if (handlerOptions.verbose) {
60
- handlerOptions.logger?.info(`[renderPage.client] onEvent callback exists: ${!!handlerOptions.onEvent}`);
61
- handlerOptions.logger?.info(`[renderPage.client] onMetrics callback exists: ${!!handlerOptions.onMetrics}`);
153
+ handlerOptions.logger?.info(
154
+ `[renderPage.client] Client-side rendering for route: ${handlerOptions.route}`
155
+ );
62
156
  }
63
- // Track if we've yielded a result to prevent multiple yields
64
- let hasYielded = false;
65
- let errorResult = null;
66
- // Create a wrapper around onEvent to handle route.error events
67
- const wrappedOnEvent = (event) => {
68
- // Call the original onEvent first
69
- if (handlerOptions.onEvent) {
70
- handlerOptions.onEvent(event);
71
- }
72
- // Handle route.error events by storing result for later yielding
73
- if (event.type === "route.error" && !hasYielded) {
74
- hasYielded = true;
75
- // Check if this should cause a panic
76
- const panicError = handleError({
77
- error: event.data.error,
78
- logger: handlerOptions.logger,
79
- panicThreshold: event.data.panicThreshold,
80
- context: `route.error (${event.data.route})`,
81
- });
82
- if (panicError != null) {
83
- // This is a panic error, store error result
84
- errorResult = {
85
- type: "error",
86
- error: panicError,
87
- metrics: {
88
- rscHeadless: { duration: 0, chunks: 0, bytes: 0 },
89
- html: { duration: 0, chunks: 0, bytes: 0 },
90
- },
91
- };
92
- }
93
- else {
94
- // This is a non-panic error, store skip result
95
- errorResult = {
96
- type: "skip",
97
- reason: event.data.error.message || "Non-panic error occurred",
98
- html: { duration: 0, chunks: 0, bytes: 0 },
99
- rsc: { duration: 0, chunks: 0, bytes: 0 },
100
- metrics: {
101
- rscHeadless: { duration: 0, chunks: 0, bytes: 0 },
102
- html: { duration: 0, chunks: 0, bytes: 0 },
103
- },
104
- };
105
- }
106
- }
157
+ const resolvePathWithManifest = (path, manifest2) => {
158
+ const entry = manifest2[path];
159
+ if (entry && entry.file) {
160
+ return entry.file;
161
+ }
162
+ return path;
107
163
  };
108
- // Skip if no pagePath AND no PageComponent provided (fallback case)
109
- if (!handlerOptions.pagePath && !handlerOptions.PageComponent) {
110
- // Create empty stream wrappers for skip case
111
- const emptyStreamWrapper = {
112
- pipe: (destination) => {
113
- destination.end();
114
- return destination;
115
- },
116
- abort: () => {
117
- // No cleanup needed
118
- },
119
- };
164
+ const manifest = handlerOptions.manifest || {};
165
+ const resolvedPagePath = handlerOptions.pagePath ? resolvePathWithManifest(handlerOptions.pagePath, manifest) : void 0;
166
+ const resolvedPropsPath = handlerOptions.propsPath ? resolvePathWithManifest(handlerOptions.propsPath, manifest) : void 0;
167
+ const resolvedRootPath = handlerOptions.rootPath ? resolvePathWithManifest(handlerOptions.rootPath, manifest) : void 0;
168
+ const resolvedHtmlPath = handlerOptions.htmlPath ? resolvePathWithManifest(handlerOptions.htmlPath, manifest) : void 0;
169
+ if (handlerOptions.verbose) {
170
+ handlerOptions.logger?.info(`[renderPage.client] Resolved paths for route ${handlerOptions.route}:`);
171
+ handlerOptions.logger?.info(` page: ${handlerOptions.pagePath} -> ${resolvedPagePath}`);
172
+ handlerOptions.logger?.info(` props: ${handlerOptions.propsPath} -> ${resolvedPropsPath}`);
173
+ handlerOptions.logger?.info(` root: ${handlerOptions.rootPath} -> ${resolvedRootPath}`);
174
+ handlerOptions.logger?.info(` html: ${handlerOptions.htmlPath} -> ${resolvedHtmlPath}`);
175
+ handlerOptions.logger?.info(` manifest keys: ${Object.keys(manifest).join(", ")}`);
176
+ handlerOptions.logger?.info(` HTML path issue: htmlPath='${handlerOptions.htmlPath}', resolved='${resolvedHtmlPath}', manifest has Html entry: ${!!manifest[handlerOptions.htmlPath || ""]}`);
177
+ handlerOptions.logger?.info(` About to pass htmlPath='${resolvedHtmlPath}' to RSC stream`);
178
+ }
179
+ const worker = handlerOptions.worker ?? handlerOptions.rscWorker;
180
+ if (!worker) {
181
+ throw new Error("RSC worker is required for client-side component resolution");
182
+ }
183
+ try {
184
+ await resolveComponents({
185
+ route: handlerOptions.route,
186
+ pagePath: resolvedPagePath,
187
+ propsPath: resolvedPropsPath,
188
+ rootPath: resolvedRootPath,
189
+ htmlPath: resolvedHtmlPath,
190
+ pageExportName: handlerOptions.pageExportName,
191
+ propsExportName: handlerOptions.propsExportName,
192
+ rootExportName: handlerOptions.rootExportName,
193
+ htmlExportName: handlerOptions.htmlExportName,
194
+ worker,
195
+ rscWorker: worker,
196
+ onMetrics: handlerOptions.onMetrics,
197
+ logger: handlerOptions.logger,
198
+ verbose: handlerOptions.verbose
199
+ });
200
+ } catch (componentResolutionError) {
201
+ const error = componentResolutionError instanceof Error ? componentResolutionError : new Error(String(componentResolutionError));
202
+ const panicError = handleError({
203
+ error,
204
+ critical: false,
205
+ logger: handlerOptions.logger,
206
+ panicThreshold: handlerOptions.panicThreshold,
207
+ context: `Component resolution failed for route ${handlerOptions.route}`
208
+ });
209
+ if (panicError) {
120
210
  yield {
121
- type: "skip",
122
- reason: "No pagePath and no PageComponent provided",
123
- html: emptyStreamWrapper,
124
- rsc: emptyStreamWrapper,
125
- metrics: {
126
- rscFull: createRenderMetrics({
127
- route: handlerOptions.route,
128
- type: "rsc-full",
129
- fromMainThread: false,
130
- fromRscWorker: true,
131
- fromHtmlWorker: false,
132
- }),
133
- rscHeadless: createRenderMetrics({
134
- route: handlerOptions.route,
135
- type: "rsc-headless",
136
- fromMainThread: false,
137
- fromRscWorker: true,
138
- fromHtmlWorker: false,
139
- }),
140
- html: createRenderMetrics({
141
- route: handlerOptions.route,
142
- type: "html",
143
- fromMainThread: true,
144
- fromRscWorker: false,
145
- fromHtmlWorker: false,
146
- }),
147
- },
211
+ type: "error",
212
+ error: panicError,
213
+ metrics: {
214
+ rscFull: rscFullMetrics,
215
+ rscHeadless: rscHeadlessMetrics,
216
+ html: htmlMetrics
217
+ }
148
218
  };
149
219
  return;
220
+ }
221
+ handlerOptions.logger?.warn(
222
+ `[renderPage.client] Component resolution failed for route ${handlerOptions.route}, continuing with client-only HTML: ${error.message}`
223
+ );
224
+ const clientOnlyHtmlStreamWrapper = {
225
+ pipe: (destination) => {
226
+ const minimalHtml = `<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body><div id="root"></div><template id="«R»"></template></body></html>`;
227
+ destination.write(minimalHtml);
228
+ destination.end();
229
+ return destination;
230
+ },
231
+ abort: () => {
232
+ }
233
+ };
234
+ const emptyRscStreamWrapper = {
235
+ pipe: (destination) => {
236
+ destination.end();
237
+ return destination;
238
+ },
239
+ abort: () => {
240
+ }
241
+ };
242
+ yield {
243
+ type: "skip",
244
+ reason: error,
245
+ html: clientOnlyHtmlStreamWrapper,
246
+ rsc: emptyRscStreamWrapper,
247
+ metrics: {
248
+ rscFull: rscFullMetrics,
249
+ rscHeadless: rscHeadlessMetrics,
250
+ html: htmlMetrics
251
+ }
252
+ };
253
+ return;
150
254
  }
151
- if (!handlerOptions.url) {
152
- handlerOptions.url = routeToURL(handlerOptions.route, handlerOptions.moduleBaseURL, handlerOptions.build.rscOutputPath);
255
+ const newHandlerOptions = {
256
+ ...handlerOptions,
257
+ // Pass page paths to the RSC worker so it knows what to render
258
+ pagePath: resolvedPagePath,
259
+ propsPath: resolvedPropsPath,
260
+ rootPath: resolvedRootPath,
261
+ htmlPath: resolvedHtmlPath
262
+ };
263
+ if (handlerOptions.verbose) {
264
+ handlerOptions.logger?.info(
265
+ `[renderPage.client] handlerOptions.clientPipeableStreamOptions: ${JSON.stringify(handlerOptions.clientPipeableStreamOptions)}`
266
+ );
267
+ handlerOptions.logger?.info(
268
+ `[renderPage.client] newHandlerOptions.clientPipeableStreamOptions: ${JSON.stringify(newHandlerOptions.clientPipeableStreamOptions)}`
269
+ );
270
+ handlerOptions.logger?.info(
271
+ `[renderPage.client] newHandlerOptions page paths: pagePath=${newHandlerOptions.pagePath}, propsPath=${newHandlerOptions.propsPath}, rootPath=${newHandlerOptions.rootPath}, htmlPath=${newHandlerOptions.htmlPath}`
272
+ );
153
273
  }
154
- const baseDir = join(handlerOptions.build.outDir, handlerOptions.build.static);
155
- const routePath = handlerOptions.route.replace(/^\//, "");
156
- // Create metrics upfront with proper types - REVERSE from server
157
- const htmlMetrics = createRenderMetrics({
158
- route: handlerOptions.route,
159
- type: "html",
160
- fromMainThread: true, // Client: HTML rendered on main thread
161
- fromRscWorker: false,
162
- fromHtmlWorker: false,
163
- baseDir,
164
- routePath,
165
- fileName: handlerOptions.build.htmlOutputPath,
166
- outputPath: join(baseDir, routePath, handlerOptions.build.htmlOutputPath),
274
+ const uniqueId = handlerOptions.id ?? `${handlerOptions.route}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
275
+ const headlessRscStreamLocal = createRscStream({
276
+ ...newHandlerOptions,
277
+ id: `${handlerOptions.route}-headless-${uniqueId}`,
278
+ rscTimeout: handlerOptions.rscTimeout || 5e3,
279
+ onMetrics: handlerOptions.onMetrics,
280
+ // Headless RSC stream: page content only (for .rsc file)
281
+ htmlPath: "",
282
+ // No HTML wrapper - just page content
283
+ pagePath: newHandlerOptions.pagePath || "",
284
+ // Ensure pagePath is always a string
285
+ url: newHandlerOptions.url || "",
286
+ // Ensure url is always a string
287
+ pageProps: newHandlerOptions.pageProps || {},
288
+ // Ensure pageProps is always an object
289
+ onEvent: wrappedOnEvent
167
290
  });
168
- const rscFullMetrics = createRenderMetrics({
169
- route: handlerOptions.route,
170
- type: "rsc-full",
171
- fromMainThread: false,
172
- fromRscWorker: true, // Client: RSC rendered on RSC worker
173
- fromHtmlWorker: false,
291
+ const fullRscStreamLocal = createRscStream({
292
+ ...newHandlerOptions,
293
+ id: `${handlerOptions.route}-full-${uniqueId}`,
294
+ rscTimeout: handlerOptions.rscTimeout || 5e3,
295
+ onMetrics: handlerOptions.onMetrics,
296
+ // Full RSC stream: include HTML wrapper (for HTML generation)
297
+ // Pass through the resolved htmlPath so custom Html components work in client mode
298
+ htmlPath: resolvedHtmlPath,
299
+ pagePath: newHandlerOptions.pagePath || "",
300
+ // Ensure pagePath is always a string
301
+ url: newHandlerOptions.url || "",
302
+ // Ensure url is always a string
303
+ pageProps: newHandlerOptions.pageProps || {},
304
+ // Ensure pageProps is always an object
305
+ // Reuse headless stream elements - the worker will handle this with the unique ID
306
+ reuseHeadlessStreamId: headlessRscStreamLocal.id,
307
+ onEvent: wrappedOnEvent
174
308
  });
175
- const rscHeadlessMetrics = createRenderMetrics({
176
- route: handlerOptions.route,
177
- type: "rsc-headless",
178
- fromMainThread: false,
179
- fromRscWorker: true, // Client: RSC rendered on RSC worker
180
- fromHtmlWorker: false,
181
- baseDir,
182
- routePath,
183
- fileName: handlerOptions.build.rscOutputPath,
184
- outputPath: join(baseDir, routePath, handlerOptions.build.rscOutputPath),
309
+ headlessRscStream = headlessRscStreamLocal;
310
+ fullRscStream = fullRscStreamLocal;
311
+ if (handlerOptions.verbose) {
312
+ handlerOptions.logger?.info(
313
+ `[renderPage.client] Creating HTML transform stream with clientPipeableStreamOptions: ${JSON.stringify(newHandlerOptions.clientPipeableStreamOptions)}`
314
+ );
315
+ }
316
+ const htmlTransformStream = createRscToHtmlStream({
317
+ ...newHandlerOptions,
318
+ htmlTimeout: handlerOptions.htmlTimeout || 15e3,
319
+ route: handlerOptions.route,
320
+ logger: handlerOptions.logger,
321
+ verbose: handlerOptions.verbose,
322
+ rscStream: fullRscStreamLocal.rscStream
185
323
  });
186
- // Declare variables outside try block so they can be accessed in catch block
187
- let headlessRscStream = null;
188
- let fullRscStream = null;
189
- let htmlHandler = null;
190
- try {
191
- if (handlerOptions.verbose) {
192
- handlerOptions.logger?.info(`[renderPage.client] Client-side rendering for route: ${handlerOptions.route}`);
193
- }
194
- // Step 1: Resolve paths to built paths using the server manifest
195
- // The client version needs to use the server manifest to get the built paths
196
- // for the page components, not the static manifest
197
- const resolvePathWithManifest = (path, manifest) => {
198
- const entry = manifest[path];
199
- if (entry && entry.file) {
200
- return entry.file;
201
- }
202
- return path;
203
- };
204
- // Use manifest for page component resolution (client version works in reverse)
205
- const manifest = handlerOptions.manifest || {};
206
- const resolvedPagePath = handlerOptions.pagePath ? resolvePathWithManifest(handlerOptions.pagePath, manifest) : undefined;
207
- const resolvedPropsPath = handlerOptions.propsPath ? resolvePathWithManifest(handlerOptions.propsPath, manifest) : undefined;
208
- const resolvedRootPath = handlerOptions.rootPath ? resolvePathWithManifest(handlerOptions.rootPath, manifest) : undefined;
209
- const resolvedHtmlPath = handlerOptions.htmlPath ? resolvePathWithManifest(handlerOptions.htmlPath, manifest) : undefined;
210
- if (handlerOptions.verbose) {
211
- handlerOptions.logger?.info(`[renderPage.client] Resolved paths for route ${handlerOptions.route}:`);
212
- handlerOptions.logger?.info(` page: ${handlerOptions.pagePath} -> ${resolvedPagePath}`);
213
- handlerOptions.logger?.info(` props: ${handlerOptions.propsPath} -> ${resolvedPropsPath}`);
214
- handlerOptions.logger?.info(` root: ${handlerOptions.rootPath} -> ${resolvedRootPath}`);
215
- handlerOptions.logger?.info(` html: ${handlerOptions.htmlPath} -> ${resolvedHtmlPath}`);
216
- handlerOptions.logger?.info(` manifest keys: ${Object.keys(manifest).join(', ')}`);
217
- handlerOptions.logger?.info(` HTML path issue: htmlPath='${handlerOptions.htmlPath}', resolved='${resolvedHtmlPath}', manifest has Html entry: ${!!manifest[handlerOptions.htmlPath || '']}`);
218
- handlerOptions.logger?.info(` About to pass htmlPath='${resolvedHtmlPath}' to RSC stream`);
219
- }
220
- const worker = handlerOptions.worker ?? handlerOptions.rscWorker;
221
- // Step 2: Resolve components using the RSC worker with built paths
222
- // This separates component resolution from RSC generation, making the
223
- // subsequent RSC render completely synchronous
224
- if (!worker) {
225
- throw new Error("RSC worker is required for client-side component resolution");
226
- }
227
- // Preload components in the worker for faster subsequent RSC stream generation
228
- try {
229
- await resolveComponents({
230
- route: handlerOptions.route,
231
- pagePath: resolvedPagePath,
232
- propsPath: resolvedPropsPath,
233
- rootPath: resolvedRootPath,
234
- htmlPath: resolvedHtmlPath,
235
- pageExportName: handlerOptions.pageExportName,
236
- propsExportName: handlerOptions.propsExportName,
237
- rootExportName: handlerOptions.rootExportName,
238
- htmlExportName: handlerOptions.htmlExportName,
239
- worker: worker,
240
- rscWorker: worker,
241
- onMetrics: handlerOptions.onMetrics,
242
- logger: handlerOptions.logger,
243
- verbose: handlerOptions.verbose,
244
- });
245
- }
246
- catch (componentResolutionError) {
247
- // Handle component resolution failures gracefully
248
- const error = componentResolutionError instanceof Error
249
- ? componentResolutionError
250
- : new Error(String(componentResolutionError));
251
- // Check if this component resolution error should cause a panic based on panicThreshold
252
- const panicError = handleError({
253
- error,
254
- critical: false,
255
- logger: handlerOptions.logger,
256
- panicThreshold: handlerOptions.panicThreshold,
257
- context: `Component resolution failed for route ${handlerOptions.route}`,
258
- });
259
- // If this should cause a panic, yield error and return
260
- if (panicError) {
261
- yield {
262
- type: "error",
263
- error: panicError,
264
- metrics: {
265
- rscFull: rscFullMetrics,
266
- rscHeadless: rscHeadlessMetrics,
267
- html: htmlMetrics,
268
- },
269
- };
270
- return;
271
- }
272
- // Otherwise, treat this as a non-critical error and continue with client-only HTML
273
- // This allows the build to complete with a client-only page
274
- handlerOptions.logger?.warn(`[renderPage.client] Component resolution failed for route ${handlerOptions.route}, continuing with client-only HTML: ${error.message}`);
275
- // Create a client-only HTML stream wrapper with minimal HTML
276
- const clientOnlyHtmlStreamWrapper = {
277
- pipe: (destination) => {
278
- // Write a minimal client-only HTML structure
279
- const minimalHtml = `<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body><div id="root"></div><template id="«R»"></template></body></html>`;
280
- destination.write(minimalHtml);
281
- destination.end();
282
- return destination;
283
- },
284
- abort: () => {
285
- // No cleanup needed for simple HTML string
286
- },
287
- };
288
- // Create an empty RSC stream wrapper
289
- const emptyRscStreamWrapper = {
290
- pipe: (destination) => {
291
- // No RSC content for failed component resolution
292
- destination.end();
293
- return destination;
294
- },
295
- abort: () => {
296
- // No cleanup needed
297
- },
298
- };
299
- // Yield skip result with client-only HTML and empty RSC
300
- yield {
301
- type: "skip",
302
- reason: error,
303
- html: clientOnlyHtmlStreamWrapper,
304
- rsc: emptyRscStreamWrapper,
305
- metrics: {
306
- rscFull: rscFullMetrics,
307
- rscHeadless: rscHeadlessMetrics,
308
- html: htmlMetrics,
309
- },
310
- };
311
- return;
312
- }
313
- // Step 2: Create handler options
314
- // Components are now preloaded in the worker, so we can use the original handler options
315
- const newHandlerOptions = {
316
- ...handlerOptions,
317
- // Pass page paths to the RSC worker so it knows what to render
318
- pagePath: resolvedPagePath,
319
- propsPath: resolvedPropsPath,
320
- rootPath: resolvedRootPath,
321
- htmlPath: resolvedHtmlPath,
322
- };
323
- if (handlerOptions.verbose) {
324
- handlerOptions.logger?.info(`[renderPage.client] handlerOptions.clientPipeableStreamOptions: ${JSON.stringify(handlerOptions.clientPipeableStreamOptions)}`);
325
- handlerOptions.logger?.info(`[renderPage.client] newHandlerOptions.clientPipeableStreamOptions: ${JSON.stringify(newHandlerOptions.clientPipeableStreamOptions)}`);
326
- handlerOptions.logger?.info(`[renderPage.client] newHandlerOptions page paths: pagePath=${newHandlerOptions.pagePath}, propsPath=${newHandlerOptions.propsPath}, rootPath=${newHandlerOptions.rootPath}, htmlPath=${newHandlerOptions.htmlPath}`);
327
- }
328
- // Component resolution is already measured in resolveComponents
329
- // No need to measure module resolution time here anymore
330
- // Create headless RSC stream first (for .rsc file)
331
- const uniqueId = handlerOptions.id ?? `${handlerOptions.route}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
332
- const headlessRscStreamLocal = createRscStream({
333
- ...newHandlerOptions,
334
- id: `${handlerOptions.route}-headless-${uniqueId}`,
335
- rscTimeout: handlerOptions.rscTimeout || 5000,
336
- onMetrics: handlerOptions.onMetrics,
337
- // Headless RSC stream: page content only (for .rsc file)
338
- htmlPath: '', // No HTML wrapper - just page content
339
- pagePath: newHandlerOptions.pagePath || '', // Ensure pagePath is always a string
340
- url: newHandlerOptions.url || '', // Ensure url is always a string
341
- pageProps: newHandlerOptions.pageProps || {}, // Ensure pageProps is always an object
342
- onEvent: wrappedOnEvent,
324
+ htmlHandler = {
325
+ htmlStream: htmlTransformStream,
326
+ abort: () => {
327
+ htmlTransformStream.abort();
328
+ }
329
+ };
330
+ const rscStreamWrapper = {
331
+ pipe: (destination) => {
332
+ const streamMetrics = createStreamMetrics();
333
+ streamMetrics.startTime = performance.now();
334
+ const rscFileStream = headlessRscStream.rscStream;
335
+ rscFileStream.on("data", (chunk) => {
336
+ streamMetrics.chunks++;
337
+ streamMetrics.bytes += chunk.length;
343
338
  });
344
- // Create full RSC stream that reuses the headless stream elements
345
- const fullRscStreamLocal = createRscStream({
346
- ...newHandlerOptions,
347
- id: `${handlerOptions.route}-full-${uniqueId}`,
348
- rscTimeout: handlerOptions.rscTimeout || 5000,
349
- onMetrics: handlerOptions.onMetrics,
350
- // Full RSC stream: include HTML wrapper (for HTML generation)
351
- // Pass through the resolved htmlPath so custom Html components work in client mode
352
- htmlPath: resolvedHtmlPath,
353
- pagePath: newHandlerOptions.pagePath || '', // Ensure pagePath is always a string
354
- url: newHandlerOptions.url || '', // Ensure url is always a string
355
- pageProps: newHandlerOptions.pageProps || {}, // Ensure pageProps is always an object
356
- // Reuse headless stream elements - the worker will handle this with the unique ID
357
- reuseHeadlessStreamId: headlessRscStreamLocal.id,
358
- onEvent: wrappedOnEvent,
339
+ rscFileStream.on("end", () => {
340
+ streamMetrics.duration = performance.now() - streamMetrics.startTime;
341
+ streamMetrics.endTime = performance.now();
342
+ rscHeadlessMetrics.streamMetrics = streamMetrics;
343
+ rscHeadlessMetrics.chunkRate = streamMetrics.chunks / (streamMetrics.duration / 1e3);
344
+ rscHeadlessMetrics.processingTime = streamMetrics.duration;
345
+ rscHeadlessMetrics.memoryUsage = process.memoryUsage();
346
+ rscHeadlessMetrics.chunks = streamMetrics.chunks;
359
347
  });
360
- // Assign to the outer variables
361
- headlessRscStream = headlessRscStreamLocal;
362
- fullRscStream = fullRscStreamLocal;
363
- // The headless stream will be consumed naturally by the file writing
364
- // The full stream will reuse the headless stream elements for HTML generation
365
- // Step 3: Create HTML transform stream
348
+ rscFileStream.pipe(destination);
349
+ return destination;
350
+ },
351
+ abort: () => headlessRscStream.abort()
352
+ };
353
+ const htmlStreamWrapper = {
354
+ pipe: (destination) => {
366
355
  if (handlerOptions.verbose) {
367
- handlerOptions.logger?.info(`[renderPage.client] Creating HTML transform stream with clientPipeableStreamOptions: ${JSON.stringify(newHandlerOptions.clientPipeableStreamOptions)}`);
356
+ handlerOptions.logger?.info(
357
+ `[renderPage.client] Piping HTML stream to destination for route: ${handlerOptions.route}`
358
+ );
368
359
  }
369
- // Create HTML stream using the full RSC stream (which reuses headless stream elements)
370
- const htmlTransformStream = createRscToHtmlStream({
371
- ...newHandlerOptions,
372
- htmlTimeout: handlerOptions.htmlTimeout || 15000,
373
- route: handlerOptions.route,
374
- logger: handlerOptions.logger,
375
- verbose: handlerOptions.verbose,
376
- rscStream: fullRscStreamLocal.rscStream,
377
- });
378
- htmlHandler = {
379
- htmlStream: htmlTransformStream,
380
- abort: () => {
381
- htmlTransformStream.abort();
382
- }
383
- };
384
- // Create stream wrappers for file writing
385
- const rscStreamWrapper = {
386
- pipe: (destination) => {
387
- const streamMetrics = createStreamMetrics();
388
- streamMetrics.startTime = performance.now();
389
- // Use the headless RSC stream directly for the .rsc file
390
- const rscFileStream = headlessRscStream.rscStream;
391
- rscFileStream.on("data", (chunk) => {
392
- streamMetrics.chunks++;
393
- streamMetrics.bytes += chunk.length;
394
- });
395
- rscFileStream.on("end", () => {
396
- streamMetrics.duration = performance.now() - streamMetrics.startTime;
397
- streamMetrics.endTime = performance.now();
398
- rscHeadlessMetrics.streamMetrics = streamMetrics;
399
- rscHeadlessMetrics.chunkRate = streamMetrics.chunks / (streamMetrics.duration / 1000);
400
- rscHeadlessMetrics.processingTime = streamMetrics.duration;
401
- rscHeadlessMetrics.memoryUsage = process.memoryUsage();
402
- rscHeadlessMetrics.chunks = streamMetrics.chunks;
403
- });
404
- rscFileStream.pipe(destination);
405
- return destination;
406
- },
407
- abort: () => headlessRscStream.abort(),
408
- };
409
- const htmlStreamWrapper = {
410
- pipe: (destination) => {
411
- if (handlerOptions.verbose) {
412
- handlerOptions.logger?.info(`[renderPage.client] Piping HTML stream to destination for route: ${handlerOptions.route}`);
413
- }
414
- // Use the HTML transform stream's pipe method directly (same as server side)
415
- return htmlTransformStream.pipe(destination);
416
- },
417
- abort: () => {
418
- fullRscStream.abort();
419
- if (htmlHandler.abort) {
420
- htmlHandler.abort();
421
- }
422
- },
423
- on: (event, listener) => {
424
- // Forward error events from the HTML transform stream to the wrapper
425
- if (event === 'error') {
426
- // Access the actual stream from the transform result
427
- const htmlStream = htmlTransformStream.htmlStream;
428
- if (htmlStream && typeof htmlStream.on === 'function') {
429
- htmlStream.on('error', listener);
430
- }
431
- }
432
- return htmlStreamWrapper;
433
- },
434
- };
435
- // Don't emit initial metrics - wait for file writes to complete
436
- // The onMetrics callback will be called after both file.write.done events
437
- // Check if we have an error result to yield (with timeout protection)
438
- // Wait a short time for any pending route.error events
439
- await new Promise(resolve => setTimeout(resolve, 100));
440
- if (errorResult) {
441
- yield errorResult;
442
- return;
360
+ return htmlTransformStream.pipe(destination);
361
+ },
362
+ abort: () => {
363
+ fullRscStream.abort();
364
+ if (htmlHandler.abort) {
365
+ htmlHandler.abort();
443
366
  }
444
- yield {
445
- type: "success",
446
- html: htmlStreamWrapper,
447
- rsc: rscStreamWrapper,
448
- metrics: {
449
- rscFull: rscFullMetrics,
450
- rscHeadless: rscHeadlessMetrics,
451
- html: htmlMetrics,
452
- },
453
- };
367
+ },
368
+ on: (event, listener) => {
369
+ if (event === "error") {
370
+ const htmlStream = htmlTransformStream.htmlStream;
371
+ if (htmlStream && typeof htmlStream.on === "function") {
372
+ htmlStream.on("error", listener);
373
+ }
374
+ }
375
+ return htmlStreamWrapper;
376
+ }
377
+ };
378
+ await new Promise((resolve) => setTimeout(resolve, 100));
379
+ if (errorResult) {
380
+ yield errorResult;
381
+ return;
454
382
  }
455
- catch (error) {
456
- // Clean up resources
457
- try {
458
- if (headlessRscStream)
459
- headlessRscStream.abort();
460
- if (fullRscStream)
461
- fullRscStream.abort();
462
- if (htmlHandler?.abort)
463
- htmlHandler.abort();
383
+ yield {
384
+ type: "success",
385
+ html: htmlStreamWrapper,
386
+ rsc: rscStreamWrapper,
387
+ metrics: {
388
+ rscFull: rscFullMetrics,
389
+ rscHeadless: rscHeadlessMetrics,
390
+ html: htmlMetrics
391
+ }
392
+ };
393
+ } catch (error) {
394
+ try {
395
+ if (headlessRscStream) headlessRscStream.abort();
396
+ if (fullRscStream) fullRscStream.abort();
397
+ if (htmlHandler?.abort) htmlHandler.abort();
398
+ } catch (cleanupError) {
399
+ handlerOptions.logger?.warn(`Failed to cleanup streams on error: ${cleanupError}`);
400
+ }
401
+ const panicError = handleError({
402
+ error,
403
+ logger: handlerOptions.logger,
404
+ panicThreshold: handlerOptions.panicThreshold
405
+ });
406
+ if (panicError != null) {
407
+ yield {
408
+ type: "error",
409
+ error: panicError,
410
+ metrics: {
411
+ rscFull: rscFullMetrics,
412
+ rscHeadless: rscHeadlessMetrics,
413
+ html: htmlMetrics
464
414
  }
465
- catch (cleanupError) {
466
- handlerOptions.logger?.warn(`Failed to cleanup streams on error: ${cleanupError}`);
415
+ };
416
+ } else {
417
+ const fallbackRscStream = createRscStream({
418
+ ...handlerOptions,
419
+ url: `${handlerOptions.url}`,
420
+ route: `${handlerOptions.route}`,
421
+ cssFiles: handlerOptions.cssFiles || /* @__PURE__ */ new Map(),
422
+ globalCss: handlerOptions.globalCss || /* @__PURE__ */ new Map(),
423
+ id: `${handlerOptions.route}-fallback-${Date.now()}`,
424
+ rscTimeout: handlerOptions.rscTimeout || 5e3,
425
+ onMetrics: handlerOptions.onMetrics,
426
+ // Use React.Fragment as fallback (same as server environment)
427
+ pagePath: "",
428
+ // This will cause the default page to be used, but we'll override it
429
+ pageProps: {}
430
+ // Ensure pageProps is always an object
431
+ });
432
+ const fallbackHtmlStream = createRscToHtmlStream({
433
+ id: handlerOptions.id,
434
+ route: handlerOptions.route,
435
+ url: handlerOptions.url,
436
+ moduleRootPath: handlerOptions.moduleRootPath,
437
+ moduleBasePath: handlerOptions.moduleBasePath,
438
+ moduleBaseURL: handlerOptions.moduleBaseURL,
439
+ projectRoot: handlerOptions.projectRoot,
440
+ panicThreshold: handlerOptions.panicThreshold,
441
+ verbose: handlerOptions.verbose,
442
+ signal: handlerOptions.signal,
443
+ logger: handlerOptions.logger,
444
+ htmlTimeout: handlerOptions.htmlTimeout,
445
+ clientPipeableStreamOptions: handlerOptions.clientPipeableStreamOptions,
446
+ onMetrics: handlerOptions.onMetrics,
447
+ build: handlerOptions.build
448
+ });
449
+ const clientOnlyHtmlStreamWrapper = {
450
+ pipe: (destination) => {
451
+ return fallbackHtmlStream.pipe(destination);
452
+ },
453
+ abort: () => {
454
+ fallbackRscStream.abort();
467
455
  }
468
- const panicError = handleError({
469
- error,
470
- logger: handlerOptions.logger,
471
- context: "renderPageClient",
472
- panicThreshold: handlerOptions.panicThreshold,
473
- });
474
- if (panicError != null) {
475
- yield {
476
- type: "error",
477
- error: panicError,
478
- metrics: {
479
- rscFull: rscFullMetrics,
480
- rscHeadless: rscHeadlessMetrics,
481
- html: htmlMetrics,
482
- },
483
- };
456
+ };
457
+ const emptyRscStreamWrapper = {
458
+ pipe: (destination) => {
459
+ destination.end();
460
+ return destination;
461
+ },
462
+ abort: () => {
484
463
  }
485
- else {
486
- // For non-panic errors, we still want to write the HTML file (client-only)
487
- // but skip the RSC file since there was a server error
488
- // Create a fallback RSC stream with React.Fragment (same as server environment)
489
- const fallbackRscStream = createRscStream({
490
- ...handlerOptions,
491
- url: `${handlerOptions.url}`,
492
- route: `${handlerOptions.route}`,
493
- cssFiles: handlerOptions.cssFiles || new Map(),
494
- globalCss: handlerOptions.globalCss || new Map(),
495
- id: `${handlerOptions.route}-fallback-${Date.now()}`,
496
- rscTimeout: handlerOptions.rscTimeout || 5000,
497
- onMetrics: handlerOptions.onMetrics,
498
- // Use React.Fragment as fallback (same as server environment)
499
- pagePath: '', // This will cause the default page to be used, but we'll override it
500
- pageProps: {}, // Ensure pageProps is always an object
501
- });
502
- // Create HTML stream that processes the fallback RSC stream to ensure performance timing script is injected
503
- const fallbackHtmlStream = createRscToHtmlStream({
504
- id: handlerOptions.id,
505
- route: handlerOptions.route,
506
- url: handlerOptions.url,
507
- moduleRootPath: handlerOptions.moduleRootPath,
508
- moduleBasePath: handlerOptions.moduleBasePath,
509
- moduleBaseURL: handlerOptions.moduleBaseURL,
510
- projectRoot: handlerOptions.projectRoot,
511
- panicThreshold: handlerOptions.panicThreshold,
512
- verbose: handlerOptions.verbose,
513
- signal: handlerOptions.signal,
514
- logger: handlerOptions.logger,
515
- htmlTimeout: handlerOptions.htmlTimeout,
516
- clientPipeableStreamOptions: handlerOptions.clientPipeableStreamOptions,
517
- onMetrics: handlerOptions.onMetrics,
518
- build: handlerOptions.build,
519
- });
520
- // Create a wrapper that pipes the fallback RSC stream through the HTML transform
521
- const clientOnlyHtmlStreamWrapper = {
522
- pipe: (destination) => {
523
- // Pipe the fallback RSC stream through the HTML transform to ensure performance timing script is injected
524
- return fallbackHtmlStream.pipe(destination);
525
- },
526
- abort: () => {
527
- // Clean up the fallback RSC stream
528
- fallbackRscStream.abort();
529
- },
530
- };
531
- // Create an empty RSC stream wrapper
532
- const emptyRscStreamWrapper = {
533
- pipe: (destination) => {
534
- // No RSC content for skipped routes
535
- destination.end();
536
- return destination;
537
- },
538
- abort: () => {
539
- // No cleanup needed
540
- },
541
- };
542
- yield {
543
- type: "skip",
544
- reason: error,
545
- html: clientOnlyHtmlStreamWrapper,
546
- rsc: emptyRscStreamWrapper,
547
- metrics: {
548
- rscFull: rscFullMetrics,
549
- rscHeadless: rscHeadlessMetrics,
550
- html: htmlMetrics,
551
- },
552
- };
464
+ };
465
+ yield {
466
+ type: "skip",
467
+ reason: error,
468
+ html: clientOnlyHtmlStreamWrapper,
469
+ rsc: emptyRscStreamWrapper,
470
+ metrics: {
471
+ rscFull: rscFullMetrics,
472
+ rscHeadless: rscHeadlessMetrics,
473
+ html: htmlMetrics
553
474
  }
475
+ };
554
476
  }
477
+ }
555
478
  };
479
+
480
+ export { renderPage };
481
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyUGFnZS5jbGllbnQuanMiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3BsdWdpbi9yZWFjdC1zdGF0aWMvcmVuZGVyUGFnZS5jbGllbnQudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiByZW5kZXJQYWdlLmNsaWVudC50c1xuICpcbiAqIFBVUlBPU0U6IENsaWVudC1zaWRlIHN0YXRpYyBwYWdlIHJlbmRlcmluZyBmb3IgUmVhY3QgU2VydmVyIENvbXBvbmVudHNcbiAqXG4gKiBBUkNISVRFQ1RVUkUgT1ZFUlZJRVc6XG4gKiBcbiAqIENMSUVOVC1TSURFIHZzIFNFUlZFUi1TSURFOlxuICogLSBTZXJ2ZXItc2lkZTogUlNDIGdlbmVyYXRpb24gaW4gbWFpbiB0aHJlYWQsIEhUTUwgZ2VuZXJhdGlvbiBpbiB3b3JrZXJcbiAqIC0gQ2xpZW50LXNpZGU6IFJTQyBnZW5lcmF0aW9uIGluIHdvcmtlciwgSFRNTCBnZW5lcmF0aW9uIGluIG1haW4gdGhyZWFkXG4gKiBcbiAqIEZMT1c6XG4gKiAxLiBSU0MgV29ya2VyIGdlbmVyYXRlcyBSU0MgY29udGVudCB3aXRoIEhUTUwgd3JhcHBlclxuICogMi4gUlNDIGNvbnRlbnQgaXMgYnVmZmVyZWQgdG8gYWxsb3cgZHVhbCBjb25zdW1wdGlvblxuICogMy4gQnVmZmVyZWQgUlNDIHN0cmVhbSBpcyBjb25zdW1lZCB0d2ljZTpcbiAqICAgIC0gRm9yIFJTQyBmaWxlIHdyaXRpbmcgKGluZGV4LnJzYylcbiAqICAgIC0gRm9yIEhUTUwgdHJhbnNmb3JtYXRpb24gKGluZGV4Lmh0bWwpXG4gKiA0LiBIVE1MIHRyYW5zZm9ybSBwcm9jZXNzZXMgUlNDIGNvbnRlbnQgaW4gbWFpbiB0aHJlYWRcbiAqIDUuIEJvdGggZmlsZXMgYXJlIHdyaXR0ZW4gdG8gZmlsZXN5c3RlbVxuICogXG4gKiBLRVkgSU5TSUdIVDogTm9kZS5qcyBzdHJlYW1zIGNhbiBvbmx5IGJlIGNvbnN1bWVkIG9uY2UsIHNvIHdlIGJ1ZmZlciB0aGUgUlNDXG4gKiBjb250ZW50IHRvIGFsbG93IGl0IHRvIGJlIHVzZWQgZm9yIGJvdGggUlNDIGZpbGUgZ2VuZXJhdGlvbiBhbmQgSFRNTCB0cmFuc2Zvcm1hdGlvbi5cbiAqIFRoaXMgZm9sbG93cyB0aGUgcGF0dGVybiBmcm9tIGNvbGxlY3RSc2NDb250ZW50LnRzLlxuICogXG4gKiBIRUxQRVIgRlVOQ1RJT05TOlxuICogLSBjcmVhdGVCdWZmZXJlZFJzY1N0cmVhbTogQ3JlYXRlcyBhIGJ1ZmZlcmVkIHN0cmVhbSBmb3IgZHVhbCBjb25zdW1wdGlvblxuICogLSBjcmVhdGVSc2NUb0h0bWxTdHJlYW06IFRyYW5zZm9ybXMgUlNDIGNvbnRlbnQgdG8gSFRNTCBpbiBtYWluIHRocmVhZFxuICogXG4gKiBVU0FHRTpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHJlbmRlclBhZ2Uoe1xuICogICByb3V0ZTogXCIvXCIsXG4gKiAgIHBhZ2VQYXRoOiBcInNyYy9wYWdlL3BhZ2UudHN4XCIsXG4gKiAgIC8vIC4uLiBvdGhlciBvcHRpb25zXG4gKiB9KTtcbiAqIFxuICogLy8gcmVzdWx0Lmh0bWwucGlwZShodG1sRmlsZVdyaXRlcik7XG4gKiAvLyByZXN1bHQucnNjLnBpcGUocnNjRmlsZVdyaXRlcik7XG4gKiBgYGBcbiAqL1xuXG5pbXBvcnQgeyBjcmVhdGVSZW5kZXJNZXRyaWNzIH0gZnJvbSBcIi4uL21ldHJpY3MvY3JlYXRlUmVuZGVyTWV0cmljcy5qc1wiO1xuaW1wb3J0IHR5cGUgeyBSZW5kZXJNZXRyaWNzIH0gZnJvbSBcIi4uL21ldHJpY3MvdHlwZXMuanNcIjtcbmltcG9ydCB7IHJvdXRlVG9VUkwgfSBmcm9tIFwiLi4vdXRpbHMvcm91dGVUb1VSTC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBSZW5kZXJQYWdlRm4gfSBmcm9tIFwiLi90eXBlcy5qc1wiO1xuaW1wb3J0IHsgaGFuZGxlRXJyb3IgfSBmcm9tIFwiLi4vZXJyb3IvaGFuZGxlRXJyb3IuanNcIjtcbmltcG9ydCB7IGFzc2VydE5vblJlYWN0U2VydmVyIH0gZnJvbSBcIi4uL2NvbmZpZy9nZXRDb25kaXRpb24uanNcIjtcblxuaW1wb3J0IHsgY3JlYXRlUnNjU3RyZWFtIH0gZnJvbSBcIi4uL3N0cmVhbS9jcmVhdGVSc2NTdHJlYW0uY2xpZW50LmpzXCI7XG5pbXBvcnQgeyByZXNvbHZlQ29tcG9uZW50cyB9IGZyb20gXCIuLi9oZWxwZXJzL3Jlc29sdmVDb21wb25lbnRzLmNsaWVudC5qc1wiO1xuXG5pbXBvcnQgeyBqb2luIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuXG5pbXBvcnQgeyBjcmVhdGVTdHJlYW1NZXRyaWNzIH0gZnJvbSBcIi4uL21ldHJpY3MvY3JlYXRlU3RyZWFtTWV0cmljcy5qc1wiO1xuaW1wb3J0IHsgcGVyZm9ybWFuY2UgfSBmcm9tIFwibm9kZTpwZXJmX2hvb2tzXCI7XG5pbXBvcnQgeyBjcmVhdGVSc2NUb0h0bWxTdHJlYW0gfSBmcm9tIFwiLi9yc2NUb0h0bWxTdHJlYW0uY2xpZW50LmpzXCI7XG5cblxuXG5hc3NlcnROb25SZWFjdFNlcnZlcigpO1xuXG4vKipcbiAqIENsaWVudCB2ZXJzaW9uIG9mIHJlbmRlclBhZ2UgdGhhdCB1c2VzIHRoZSByZWFjdC1jbGllbnQgcGF0dGVyblxuICogVGhpcyB3b3JrcyBpbiBSRVZFUlNFIGZyb20gdGhlIHNlcnZlciBwbHVnaW46XG4gKiAtIFNlcnZlcjogTWFpbiB0aHJlYWQgKFJTQykgKyBIVE1MIHdvcmtlciAoSFRNTClcbiAqIC0gQ2xpZW50OiBSU0Mgd29ya2VyIChSU0MpICsgTWFpbiB0aHJlYWQgKEhUTUwpXG4gKi9cbmV4cG9ydCBjb25zdCByZW5kZXJQYWdlOiBSZW5kZXJQYWdlRm4gPSBhc3luYyBmdW5jdGlvbiogX3JlbmRlclBhZ2VDbGllbnQoXG4gIGhhbmRsZXJPcHRpb25zXG4pIHtcbiAgaWYgKGhhbmRsZXJPcHRpb25zLnZlcmJvc2UpIHtcbiAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICBgW3JlbmRlclBhZ2UuY2xpZW50XSBvbkV2ZW50IGNhbGxiYWNrIGV4aXN0czogJHshIWhhbmRsZXJPcHRpb25zLm9uRXZlbnR9YFxuICAgICk7XG4gICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy5pbmZvKFxuICAgICAgYFtyZW5kZXJQYWdlLmNsaWVudF0gb25NZXRyaWNzIGNhbGxiYWNrIGV4aXN0czogJHshIWhhbmRsZXJPcHRpb25zLm9uTWV0cmljc31gXG4gICAgKTtcbiAgfVxuXG4gIC8vIFRyYWNrIGlmIHdlJ3ZlIHlpZWxkZWQgYSByZXN1bHQgdG8gcHJldmVudCBtdWx0aXBsZSB5aWVsZHNcbiAgbGV0IGhhc1lpZWxkZWQgPSBmYWxzZTtcbiAgbGV0IGVycm9yUmVzdWx0OiBhbnkgPSBudWxsO1xuXG4gIC8vIENyZWF0ZSBhIHdyYXBwZXIgYXJvdW5kIG9uRXZlbnQgdG8gaGFuZGxlIHJvdXRlLmVycm9yIGV2ZW50c1xuICBjb25zdCB3cmFwcGVkT25FdmVudCA9IChldmVudDogYW55KSA9PiB7XG4gICAgLy8gQ2FsbCB0aGUgb3JpZ2luYWwgb25FdmVudCBmaXJzdFxuICAgIGlmIChoYW5kbGVyT3B0aW9ucy5vbkV2ZW50KSB7XG4gICAgICBoYW5kbGVyT3B0aW9ucy5vbkV2ZW50KGV2ZW50KTtcbiAgICB9XG4gICAgXG4gICAgLy8gSGFuZGxlIHJvdXRlLmVycm9yIGV2ZW50cyBieSBzdG9yaW5nIHJlc3VsdCBmb3IgbGF0ZXIgeWllbGRpbmdcbiAgICBpZiAoZXZlbnQudHlwZSA9PT0gXCJyb3V0ZS5lcnJvclwiICYmICFoYXNZaWVsZGVkKSB7XG4gICAgICBoYXNZaWVsZGVkID0gdHJ1ZTtcbiAgICAgIFxuICAgICAgLy8gQ2hlY2sgaWYgdGhpcyBzaG91bGQgY2F1c2UgYSBwYW5pY1xuICAgICAgY29uc3QgcGFuaWNFcnJvciA9IGhhbmRsZUVycm9yKHtcbiAgICAgICAgZXJyb3I6IGV2ZW50LmRhdGEuZXJyb3IsXG4gICAgICAgIGxvZ2dlcjogaGFuZGxlck9wdGlvbnMubG9nZ2VyLFxuICAgICAgICBwYW5pY1RocmVzaG9sZDogZXZlbnQuZGF0YS5wYW5pY1RocmVzaG9sZCxcbiAgICAgICAgY29udGV4dDogYHJvdXRlLmVycm9yICgke2V2ZW50LmRhdGEucm91dGV9KWAsXG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgaWYgKHBhbmljRXJyb3IgIT0gbnVsbCkge1xuICAgICAgICAvLyBUaGlzIGlzIGEgcGFuaWMgZXJyb3IsIHN0b3JlIGVycm9yIHJlc3VsdFxuICAgICAgICBlcnJvclJlc3VsdCA9IHtcbiAgICAgICAgICB0eXBlOiBcImVycm9yXCIsXG4gICAgICAgICAgZXJyb3I6IHBhbmljRXJyb3IsXG4gICAgICAgICAgbWV0cmljczoge1xuICAgICAgICAgICAgcnNjSGVhZGxlc3M6IHsgZHVyYXRpb246IDAsIGNodW5rczogMCwgYnl0ZXM6IDAgfSxcbiAgICAgICAgICAgIGh0bWw6IHsgZHVyYXRpb246IDAsIGNodW5rczogMCwgYnl0ZXM6IDAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gVGhpcyBpcyBhIG5vbi1wYW5pYyBlcnJvciwgc3RvcmUgc2tpcCByZXN1bHRcbiAgICAgICAgZXJyb3JSZXN1bHQgPSB7XG4gICAgICAgICAgdHlwZTogXCJza2lwXCIsXG4gICAgICAgICAgcmVhc29uOiBldmVudC5kYXRhLmVycm9yLm1lc3NhZ2UgfHwgXCJOb24tcGFuaWMgZXJyb3Igb2NjdXJyZWRcIixcbiAgICAgICAgICBodG1sOiB7IGR1cmF0aW9uOiAwLCBjaHVua3M6IDAsIGJ5dGVzOiAwIH0sXG4gICAgICAgICAgcnNjOiB7IGR1cmF0aW9uOiAwLCBjaHVua3M6IDAsIGJ5dGVzOiAwIH0sXG4gICAgICAgICAgbWV0cmljczoge1xuICAgICAgICAgICAgcnNjSGVhZGxlc3M6IHsgZHVyYXRpb246IDAsIGNodW5rczogMCwgYnl0ZXM6IDAgfSxcbiAgICAgICAgICAgIGh0bWw6IHsgZHVyYXRpb246IDAsIGNodW5rczogMCwgYnl0ZXM6IDAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9O1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICAvLyBTa2lwIGlmIG5vIHBhZ2VQYXRoIEFORCBubyBQYWdlQ29tcG9uZW50IHByb3ZpZGVkIChmYWxsYmFjayBjYXNlKVxuICBpZiAoIWhhbmRsZXJPcHRpb25zLnBhZ2VQYXRoICYmICFoYW5kbGVyT3B0aW9ucy5QYWdlQ29tcG9uZW50KSB7XG4gICAgLy8gQ3JlYXRlIGVtcHR5IHN0cmVhbSB3cmFwcGVycyBmb3Igc2tpcCBjYXNlXG4gICAgY29uc3QgZW1wdHlTdHJlYW1XcmFwcGVyID0ge1xuICAgICAgcGlwZTogPFdyaXRhYmxlIGV4dGVuZHMgTm9kZUpTLldyaXRhYmxlU3RyZWFtPihkZXN0aW5hdGlvbjogV3JpdGFibGUpID0+IHtcbiAgICAgICAgZGVzdGluYXRpb24uZW5kKCk7XG4gICAgICAgIHJldHVybiBkZXN0aW5hdGlvbjtcbiAgICAgIH0sXG4gICAgICBhYm9ydDogKCkgPT4ge1xuICAgICAgICAvLyBObyBjbGVhbnVwIG5lZWRlZFxuICAgICAgfSxcbiAgICB9O1xuXG4gICAgeWllbGQge1xuICAgICAgdHlwZTogXCJza2lwXCIsXG4gICAgICByZWFzb246IFwiTm8gcGFnZVBhdGggYW5kIG5vIFBhZ2VDb21wb25lbnQgcHJvdmlkZWRcIixcbiAgICAgIGh0bWw6IGVtcHR5U3RyZWFtV3JhcHBlcixcbiAgICAgIHJzYzogZW1wdHlTdHJlYW1XcmFwcGVyLFxuICAgICAgbWV0cmljczoge1xuICAgICAgICByc2NGdWxsOiBjcmVhdGVSZW5kZXJNZXRyaWNzKHtcbiAgICAgICAgICByb3V0ZTogaGFuZGxlck9wdGlvbnMucm91dGUsXG4gICAgICAgICAgdHlwZTogXCJyc2MtZnVsbFwiLFxuICAgICAgICAgIGZyb21NYWluVGhyZWFkOiBmYWxzZSxcbiAgICAgICAgICBmcm9tUnNjV29ya2VyOiB0cnVlLFxuICAgICAgICAgIGZyb21IdG1sV29ya2VyOiBmYWxzZSxcbiAgICAgICAgfSkgYXMgUmVuZGVyTWV0cmljcyAmIHsgdHlwZTogXCJyc2MtZnVsbFwiIH0sXG4gICAgICAgIHJzY0hlYWRsZXNzOiBjcmVhdGVSZW5kZXJNZXRyaWNzKHtcbiAgICAgICAgICByb3V0ZTogaGFuZGxlck9wdGlvbnMucm91dGUsXG4gICAgICAgICAgdHlwZTogXCJyc2MtaGVhZGxlc3NcIixcbiAgICAgICAgICBmcm9tTWFpblRocmVhZDogZmFsc2UsXG4gICAgICAgICAgZnJvbVJzY1dvcmtlcjogdHJ1ZSxcbiAgICAgICAgICBmcm9tSHRtbFdvcmtlcjogZmFsc2UsXG4gICAgICAgIH0pIGFzIFJlbmRlck1ldHJpY3MgJiB7IHR5cGU6IFwicnNjLWhlYWRsZXNzXCIgfSxcbiAgICAgICAgaHRtbDogY3JlYXRlUmVuZGVyTWV0cmljcyh7XG4gICAgICAgICAgcm91dGU6IGhhbmRsZXJPcHRpb25zLnJvdXRlLFxuICAgICAgICAgIHR5cGU6IFwiaHRtbFwiLFxuICAgICAgICAgIGZyb21NYWluVGhyZWFkOiB0cnVlLFxuICAgICAgICAgIGZyb21Sc2NXb3JrZXI6IGZhbHNlLFxuICAgICAgICAgIGZyb21IdG1sV29ya2VyOiBmYWxzZSxcbiAgICAgICAgfSkgYXMgUmVuZGVyTWV0cmljcyAmIHsgdHlwZTogXCJodG1sXCIgfSxcbiAgICAgIH0sXG4gICAgfTtcbiAgICByZXR1cm47XG4gIH1cblxuICBpZiAoIWhhbmRsZXJPcHRpb25zLnVybCkge1xuICAgIGhhbmRsZXJPcHRpb25zLnVybCA9IHJvdXRlVG9VUkwoXG4gICAgICBoYW5kbGVyT3B0aW9ucy5yb3V0ZSxcbiAgICAgIGhhbmRsZXJPcHRpb25zLm1vZHVsZUJhc2VVUkwsXG4gICAgICBoYW5kbGVyT3B0aW9ucy5idWlsZC5yc2NPdXRwdXRQYXRoXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IGJhc2VEaXIgPSBqb2luKFxuICAgIGhhbmRsZXJPcHRpb25zLmJ1aWxkLm91dERpcixcbiAgICBoYW5kbGVyT3B0aW9ucy5idWlsZC5zdGF0aWNcbiAgKTtcbiAgY29uc3Qgcm91dGVQYXRoID0gaGFuZGxlck9wdGlvbnMucm91dGUucmVwbGFjZSgvXlxcLy8sIFwiXCIpO1xuXG4gIC8vIENyZWF0ZSBtZXRyaWNzIHVwZnJvbnQgd2l0aCBwcm9wZXIgdHlwZXMgLSBSRVZFUlNFIGZyb20gc2VydmVyXG4gIGNvbnN0IGh0bWxNZXRyaWNzID0gY3JlYXRlUmVuZGVyTWV0cmljcyh7XG4gICAgcm91dGU6IGhhbmRsZXJPcHRpb25zLnJvdXRlLFxuICAgIHR5cGU6IFwiaHRtbFwiLFxuICAgIGZyb21NYWluVGhyZWFkOiB0cnVlLCAvLyBDbGllbnQ6IEhUTUwgcmVuZGVyZWQgb24gbWFpbiB0aHJlYWRcbiAgICBmcm9tUnNjV29ya2VyOiBmYWxzZSxcbiAgICBmcm9tSHRtbFdvcmtlcjogZmFsc2UsXG4gICAgYmFzZURpcixcbiAgICByb3V0ZVBhdGgsXG4gICAgZmlsZU5hbWU6IGhhbmRsZXJPcHRpb25zLmJ1aWxkLmh0bWxPdXRwdXRQYXRoLFxuICAgIG91dHB1dFBhdGg6IGpvaW4oYmFzZURpciwgcm91dGVQYXRoLCBoYW5kbGVyT3B0aW9ucy5idWlsZC5odG1sT3V0cHV0UGF0aCksXG4gIH0pO1xuICBcbiAgY29uc3QgcnNjRnVsbE1ldHJpY3MgPSBjcmVhdGVSZW5kZXJNZXRyaWNzKHtcbiAgICByb3V0ZTogaGFuZGxlck9wdGlvbnMucm91dGUsXG4gICAgdHlwZTogXCJyc2MtZnVsbFwiLFxuICAgIGZyb21NYWluVGhyZWFkOiBmYWxzZSxcbiAgICBmcm9tUnNjV29ya2VyOiB0cnVlLCAvLyBDbGllbnQ6IFJTQyByZW5kZXJlZCBvbiBSU0Mgd29ya2VyXG4gICAgZnJvbUh0bWxXb3JrZXI6IGZhbHNlLFxuICB9KTtcbiAgXG4gIGNvbnN0IHJzY0hlYWRsZXNzTWV0cmljcyA9IGNyZWF0ZVJlbmRlck1ldHJpY3Moe1xuICAgIHJvdXRlOiBoYW5kbGVyT3B0aW9ucy5yb3V0ZSxcbiAgICB0eXBlOiBcInJzYy1oZWFkbGVzc1wiLFxuICAgIGZyb21NYWluVGhyZWFkOiBmYWxzZSxcbiAgICBmcm9tUnNjV29ya2VyOiB0cnVlLCAvLyBDbGllbnQ6IFJTQyByZW5kZXJlZCBvbiBSU0Mgd29ya2VyXG4gICAgZnJvbUh0bWxXb3JrZXI6IGZhbHNlLFxuICAgIGJhc2VEaXIsXG4gICAgcm91dGVQYXRoLFxuICAgIGZpbGVOYW1lOiBoYW5kbGVyT3B0aW9ucy5idWlsZC5yc2NPdXRwdXRQYXRoLFxuICAgIG91dHB1dFBhdGg6IGpvaW4oYmFzZURpciwgcm91dGVQYXRoLCBoYW5kbGVyT3B0aW9ucy5idWlsZC5yc2NPdXRwdXRQYXRoKSxcbiAgfSk7XG5cbiAgLy8gRGVjbGFyZSB2YXJpYWJsZXMgb3V0c2lkZSB0cnkgYmxvY2sgc28gdGhleSBjYW4gYmUgYWNjZXNzZWQgaW4gY2F0Y2ggYmxvY2tcbiAgbGV0IGhlYWRsZXNzUnNjU3RyZWFtOiBhbnkgPSBudWxsO1xuICBsZXQgZnVsbFJzY1N0cmVhbTogYW55ID0gbnVsbDtcbiAgbGV0IGh0bWxIYW5kbGVyOiBhbnkgPSBudWxsO1xuICBcblxuXG4gIHRyeSB7XG4gICAgaWYgKGhhbmRsZXJPcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8uaW5mbyhcbiAgICAgICAgYFtyZW5kZXJQYWdlLmNsaWVudF0gQ2xpZW50LXNpZGUgcmVuZGVyaW5nIGZvciByb3V0ZTogJHtoYW5kbGVyT3B0aW9ucy5yb3V0ZX1gXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIFN0ZXAgMTogUmVzb2x2ZSBwYXRocyB0byBidWlsdCBwYXRocyB1c2luZyB0aGUgc2VydmVyIG1hbmlmZXN0XG4gICAgLy8gVGhlIGNsaWVudCB2ZXJzaW9uIG5lZWRzIHRvIHVzZSB0aGUgc2VydmVyIG1hbmlmZXN0IHRvIGdldCB0aGUgYnVpbHQgcGF0aHNcbiAgICAvLyBmb3IgdGhlIHBhZ2UgY29tcG9uZW50cywgbm90IHRoZSBzdGF0aWMgbWFuaWZlc3RcbiAgICBjb25zdCByZXNvbHZlUGF0aFdpdGhNYW5pZmVzdCA9IChwYXRoOiBzdHJpbmcsIG1hbmlmZXN0OiBhbnkpOiBzdHJpbmcgPT4ge1xuICAgICAgY29uc3QgZW50cnkgPSBtYW5pZmVzdFtwYXRoXTtcbiAgICAgIGlmIChlbnRyeSAmJiBlbnRyeS5maWxlKSB7XG4gICAgICAgIHJldHVybiBlbnRyeS5maWxlO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHBhdGg7XG4gICAgfTtcblxuICAgIC8vIFVzZSBtYW5pZmVzdCBmb3IgcGFnZSBjb21wb25lbnQgcmVzb2x1dGlvbiAoY2xpZW50IHZlcnNpb24gd29ya3MgaW4gcmV2ZXJzZSlcbiAgICBjb25zdCBtYW5pZmVzdCA9IGhhbmRsZXJPcHRpb25zLm1hbmlmZXN0IHx8IHt9O1xuICAgIGNvbnN0IHJlc29sdmVkUGFnZVBhdGggPSBoYW5kbGVyT3B0aW9ucy5wYWdlUGF0aCA/IHJlc29sdmVQYXRoV2l0aE1hbmlmZXN0KGhhbmRsZXJPcHRpb25zLnBhZ2VQYXRoLCBtYW5pZmVzdCkgOiB1bmRlZmluZWQ7XG4gICAgY29uc3QgcmVzb2x2ZWRQcm9wc1BhdGggPSBoYW5kbGVyT3B0aW9ucy5wcm9wc1BhdGggPyByZXNvbHZlUGF0aFdpdGhNYW5pZmVzdChoYW5kbGVyT3B0aW9ucy5wcm9wc1BhdGgsIG1hbmlmZXN0KSA6IHVuZGVmaW5lZDtcbiAgICBjb25zdCByZXNvbHZlZFJvb3RQYXRoID0gaGFuZGxlck9wdGlvbnMucm9vdFBhdGggPyByZXNvbHZlUGF0aFdpdGhNYW5pZmVzdChoYW5kbGVyT3B0aW9ucy5yb290UGF0aCwgbWFuaWZlc3QpIDogdW5kZWZpbmVkO1xuICAgIGNvbnN0IHJlc29sdmVkSHRtbFBhdGggPSBoYW5kbGVyT3B0aW9ucy5odG1sUGF0aCA/IHJlc29sdmVQYXRoV2l0aE1hbmlmZXN0KGhhbmRsZXJPcHRpb25zLmh0bWxQYXRoLCBtYW5pZmVzdCkgOiB1bmRlZmluZWQ7XG5cbiAgICBpZiAoaGFuZGxlck9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy5pbmZvKGBbcmVuZGVyUGFnZS5jbGllbnRdIFJlc29sdmVkIHBhdGhzIGZvciByb3V0ZSAke2hhbmRsZXJPcHRpb25zLnJvdXRlfTpgKTtcbiAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8uaW5mbyhgICBwYWdlOiAke2hhbmRsZXJPcHRpb25zLnBhZ2VQYXRofSAtPiAke3Jlc29sdmVkUGFnZVBhdGh9YCk7XG4gICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oYCAgcHJvcHM6ICR7aGFuZGxlck9wdGlvbnMucHJvcHNQYXRofSAtPiAke3Jlc29sdmVkUHJvcHNQYXRofWApO1xuICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy5pbmZvKGAgIHJvb3Q6ICR7aGFuZGxlck9wdGlvbnMucm9vdFBhdGh9IC0+ICR7cmVzb2x2ZWRSb290UGF0aH1gKTtcbiAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8uaW5mbyhgICBodG1sOiAke2hhbmRsZXJPcHRpb25zLmh0bWxQYXRofSAtPiAke3Jlc29sdmVkSHRtbFBhdGh9YCk7XG4gICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oYCAgbWFuaWZlc3Qga2V5czogJHtPYmplY3Qua2V5cyhtYW5pZmVzdCkuam9pbignLCAnKX1gKTtcbiAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8uaW5mbyhgICBIVE1MIHBhdGggaXNzdWU6IGh0bWxQYXRoPScke2hhbmRsZXJPcHRpb25zLmh0bWxQYXRofScsIHJlc29sdmVkPScke3Jlc29sdmVkSHRtbFBhdGh9JywgbWFuaWZlc3QgaGFzIEh0bWwgZW50cnk6ICR7ISFtYW5pZmVzdFtoYW5kbGVyT3B0aW9ucy5odG1sUGF0aCB8fCAnJ119YCk7XG4gICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oYCAgQWJvdXQgdG8gcGFzcyBodG1sUGF0aD0nJHtyZXNvbHZlZEh0bWxQYXRofScgdG8gUlNDIHN0cmVhbWApO1xuICAgIH1cbiAgICBjb25zdCB3b3JrZXIgPSBoYW5kbGVyT3B0aW9ucy53b3JrZXIgPz8gaGFuZGxlck9wdGlvbnMucnNjV29ya2VyO1xuXG4gICAgLy8gU3RlcCAyOiBSZXNvbHZlIGNvbXBvbmVudHMgdXNpbmcgdGhlIFJTQyB3b3JrZXIgd2l0aCBidWlsdCBwYXRoc1xuICAgIC8vIFRoaXMgc2VwYXJhdGVzIGNvbXBvbmVudCByZXNvbHV0aW9uIGZyb20gUlNDIGdlbmVyYXRpb24sIG1ha2luZyB0aGVcbiAgICAvLyBzdWJzZXF1ZW50IFJTQyByZW5kZXIgY29tcGxldGVseSBzeW5jaHJvbm91c1xuICAgIGlmICghd29ya2VyKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJSU0Mgd29ya2VyIGlzIHJlcXVpcmVkIGZvciBjbGllbnQtc2lkZSBjb21wb25lbnQgcmVzb2x1dGlvblwiKTtcbiAgICB9XG4gICAgXG4gICAgLy8gUHJlbG9hZCBjb21wb25lbnRzIGluIHRoZSB3b3JrZXIgZm9yIGZhc3RlciBzdWJzZXF1ZW50IFJTQyBzdHJlYW0gZ2VuZXJhdGlvblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCByZXNvbHZlQ29tcG9uZW50cyh7XG4gICAgICAgIHJvdXRlOiBoYW5kbGVyT3B0aW9ucy5yb3V0ZSxcbiAgICAgICAgcGFnZVBhdGg6IHJlc29sdmVkUGFnZVBhdGgsXG4gICAgICAgIHByb3BzUGF0aDogcmVzb2x2ZWRQcm9wc1BhdGgsXG4gICAgICAgIHJvb3RQYXRoOiByZXNvbHZlZFJvb3RQYXRoLFxuICAgICAgICBodG1sUGF0aDogcmVzb2x2ZWRIdG1sUGF0aCxcbiAgICAgICAgcGFnZUV4cG9ydE5hbWU6IGhhbmRsZXJPcHRpb25zLnBhZ2VFeHBvcnROYW1lLFxuICAgICAgICBwcm9wc0V4cG9ydE5hbWU6IGhhbmRsZXJPcHRpb25zLnByb3BzRXhwb3J0TmFtZSxcbiAgICAgICAgcm9vdEV4cG9ydE5hbWU6IGhhbmRsZXJPcHRpb25zLnJvb3RFeHBvcnROYW1lLFxuICAgICAgICBodG1sRXhwb3J0TmFtZTogaGFuZGxlck9wdGlvbnMuaHRtbEV4cG9ydE5hbWUsXG4gICAgICAgIHdvcmtlcjogd29ya2VyLFxuICAgICAgICByc2NXb3JrZXI6IHdvcmtlcixcbiAgICAgICAgb25NZXRyaWNzOiBoYW5kbGVyT3B0aW9ucy5vbk1ldHJpY3MsXG4gICAgICAgIGxvZ2dlcjogaGFuZGxlck9wdGlvbnMubG9nZ2VyLFxuICAgICAgICB2ZXJib3NlOiBoYW5kbGVyT3B0aW9ucy52ZXJib3NlLFxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoY29tcG9uZW50UmVzb2x1dGlvbkVycm9yKSB7XG4gICAgICAvLyBIYW5kbGUgY29tcG9uZW50IHJlc29sdXRpb24gZmFpbHVyZXMgZ3JhY2VmdWxseVxuICAgICAgY29uc3QgZXJyb3IgPSBjb21wb25lbnRSZXNvbHV0aW9uRXJyb3IgaW5zdGFuY2VvZiBFcnJvciBcbiAgICAgICAgPyBjb21wb25lbnRSZXNvbHV0aW9uRXJyb3IgXG4gICAgICAgIDogbmV3IEVycm9yKFN0cmluZyhjb21wb25lbnRSZXNvbHV0aW9uRXJyb3IpKTtcbiAgICAgIFxuICAgICAgLy8gQ2hlY2sgaWYgdGhpcyBjb21wb25lbnQgcmVzb2x1dGlvbiBlcnJvciBzaG91bGQgY2F1c2UgYSBwYW5pYyBiYXNlZCBvbiBwYW5pY1RocmVzaG9sZFxuICAgICAgY29uc3QgcGFuaWNFcnJvciA9IGhhbmRsZUVycm9yKHtcbiAgICAgICAgZXJyb3IsXG4gICAgICAgIGNyaXRpY2FsOiBmYWxzZSxcbiAgICAgICAgbG9nZ2VyOiBoYW5kbGVyT3B0aW9ucy5sb2dnZXIsXG4gICAgICAgIHBhbmljVGhyZXNob2xkOiBoYW5kbGVyT3B0aW9ucy5wYW5pY1RocmVzaG9sZCxcbiAgICAgICAgY29udGV4dDogYENvbXBvbmVudCByZXNvbHV0aW9uIGZhaWxlZCBmb3Igcm91dGUgJHtoYW5kbGVyT3B0aW9ucy5yb3V0ZX1gLFxuICAgICAgfSk7XG4gICAgICBcbiAgICAgICAgICAgICAvLyBJZiB0aGlzIHNob3VsZCBjYXVzZSBhIHBhbmljLCB5aWVsZCBlcnJvciBhbmQgcmV0dXJuXG4gICAgICAgaWYgKHBhbmljRXJyb3IpIHtcbiAgICAgICAgIHlpZWxkIHtcbiAgICAgICAgICAgdHlwZTogXCJlcnJvclwiLFxuICAgICAgICAgICBlcnJvcjogcGFuaWNFcnJvcixcbiAgICAgICAgICAgbWV0cmljczoge1xuICAgICAgICAgICAgIHJzY0Z1bGw6IHJzY0Z1bGxNZXRyaWNzLFxuICAgICAgICAgICAgIHJzY0hlYWRsZXNzOiByc2NIZWFkbGVzc01ldHJpY3MsXG4gICAgICAgICAgICAgaHRtbDogaHRtbE1ldHJpY3MsXG4gICAgICAgICAgIH0sXG4gICAgICAgICB9O1xuICAgICAgICAgcmV0dXJuO1xuICAgICAgIH1cbiAgICAgICBcbiAgICAgICAvLyBPdGhlcndpc2UsIHRyZWF0IHRoaXMgYXMgYSBub24tY3JpdGljYWwgZXJyb3IgYW5kIGNvbnRpbnVlIHdpdGggY2xpZW50LW9ubHkgSFRNTFxuICAgICAgIC8vIFRoaXMgYWxsb3dzIHRoZSBidWlsZCB0byBjb21wbGV0ZSB3aXRoIGEgY2xpZW50LW9ubHkgcGFnZVxuICAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8ud2FybihcbiAgICAgICAgIGBbcmVuZGVyUGFnZS5jbGllbnRdIENvbXBvbmVudCByZXNvbHV0aW9uIGZhaWxlZCBmb3Igcm91dGUgJHtoYW5kbGVyT3B0aW9ucy5yb3V0ZX0sIGNvbnRpbnVpbmcgd2l0aCBjbGllbnQtb25seSBIVE1MOiAke2Vycm9yLm1lc3NhZ2V9YFxuICAgICAgICk7XG4gICAgICAgXG4gICAgICAgLy8gQ3JlYXRlIGEgY2xpZW50LW9ubHkgSFRNTCBzdHJlYW0gd3JhcHBlciB3aXRoIG1pbmltYWwgSFRNTFxuICAgICAgIGNvbnN0IGNsaWVudE9ubHlIdG1sU3RyZWFtV3JhcHBlciA9IHtcbiAgICAgICAgIHBpcGU6IDxXcml0YWJsZSBleHRlbmRzIE5vZGVKUy5Xcml0YWJsZVN0cmVhbT4oZGVzdGluYXRpb246IFdyaXRhYmxlKSA9PiB7XG4gICAgICAgICAgIC8vIFdyaXRlIGEgbWluaW1hbCBjbGllbnQtb25seSBIVE1MIHN0cnVjdHVyZVxuICAgICAgICAgICBjb25zdCBtaW5pbWFsSHRtbCA9IGA8IURPQ1RZUEUgaHRtbD48aHRtbD48aGVhZD48bGluayByZWw9XCJleHBlY3RcIiBocmVmPVwiI8KrUsK7XCIgYmxvY2tpbmc9XCJyZW5kZXJcIi8+PC9oZWFkPjxib2R5PjxkaXYgaWQ9XCJyb290XCI+PC9kaXY+PHRlbXBsYXRlIGlkPVwiwqtSwrtcIj48L3RlbXBsYXRlPjwvYm9keT48L2h0bWw+YDtcbiAgICAgICAgICAgZGVzdGluYXRpb24ud3JpdGUobWluaW1hbEh0bWwpO1xuICAgICAgICAgICBkZXN0aW5hdGlvbi5lbmQoKTtcbiAgICAgICAgICAgcmV0dXJuIGRlc3RpbmF0aW9uO1xuICAgICAgICAgfSxcbiAgICAgICAgIGFib3J0OiAoKSA9PiB7XG4gICAgICAgICAgIC8vIE5vIGNsZWFudXAgbmVlZGVkIGZvciBzaW1wbGUgSFRNTCBzdHJpbmdcbiAgICAgICAgIH0sXG4gICAgICAgfTtcbiAgICAgICBcbiAgICAgICAvLyBDcmVhdGUgYW4gZW1wdHkgUlNDIHN0cmVhbSB3cmFwcGVyXG4gICAgICAgY29uc3QgZW1wdHlSc2NTdHJlYW1XcmFwcGVyID0ge1xuICAgICAgICAgcGlwZTogPFdyaXRhYmxlIGV4dGVuZHMgTm9kZUpTLldyaXRhYmxlU3RyZWFtPihkZXN0aW5hdGlvbjogV3JpdGFibGUpID0+IHtcbiAgICAgICAgICAgLy8gTm8gUlNDIGNvbnRlbnQgZm9yIGZhaWxlZCBjb21wb25lbnQgcmVzb2x1dGlvblxuICAgICAgICAgICBkZXN0aW5hdGlvbi5lbmQoKTtcbiAgICAgICAgICAgcmV0dXJuIGRlc3RpbmF0aW9uO1xuICAgICAgICAgfSxcbiAgICAgICAgIGFib3J0OiAoKSA9PiB7XG4gICAgICAgICAgIC8vIE5vIGNsZWFudXAgbmVlZGVkXG4gICAgICAgICB9LFxuICAgICAgIH07XG4gICAgICAgXG4gICAgICAgLy8gWWllbGQgc2tpcCByZXN1bHQgd2l0aCBjbGllbnQtb25seSBIVE1MIGFuZCBlbXB0eSBSU0NcbiAgICAgICB5aWVsZCB7XG4gICAgICAgICB0eXBlOiBcInNraXBcIixcbiAgICAgICAgIHJlYXNvbjogZXJyb3IsXG4gICAgICAgICBodG1sOiBjbGllbnRPbmx5SHRtbFN0cmVhbVdyYXBwZXIsXG4gICAgICAgICByc2M6IGVtcHR5UnNjU3RyZWFtV3JhcHBlcixcbiAgICAgICAgIG1ldHJpY3M6IHtcbiAgICAgICAgICAgcnNjRnVsbDogcnNjRnVsbE1ldHJpY3MsXG4gICAgICAgICAgIHJzY0hlYWRsZXNzOiByc2NIZWFkbGVzc01ldHJpY3MsXG4gICAgICAgICAgIGh0bWw6IGh0bWxNZXRyaWNzLFxuICAgICAgICAgfSxcbiAgICAgICB9O1xuICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBTdGVwIDI6IENyZWF0ZSBoYW5kbGVyIG9wdGlvbnNcbiAgICAvLyBDb21wb25lbnRzIGFyZSBub3cgcHJlbG9hZGVkIGluIHRoZSB3b3JrZXIsIHNvIHdlIGNhbiB1c2UgdGhlIG9yaWdpbmFsIGhhbmRsZXIgb3B0aW9uc1xuICAgIGNvbnN0IG5ld0hhbmRsZXJPcHRpb25zID0ge1xuICAgICAgLi4uaGFuZGxlck9wdGlvbnMsXG4gICAgICAvLyBQYXNzIHBhZ2UgcGF0aHMgdG8gdGhlIFJTQyB3b3JrZXIgc28gaXQga25vd3Mgd2hhdCB0byByZW5kZXJcbiAgICAgIHBhZ2VQYXRoOiByZXNvbHZlZFBhZ2VQYXRoLFxuICAgICAgcHJvcHNQYXRoOiByZXNvbHZlZFByb3BzUGF0aCxcbiAgICAgIHJvb3RQYXRoOiByZXNvbHZlZFJvb3RQYXRoLFxuICAgICAgaHRtbFBhdGg6IHJlc29sdmVkSHRtbFBhdGgsXG4gICAgfTtcblxuICAgIGlmIChoYW5kbGVyT3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgIGBbcmVuZGVyUGFnZS5jbGllbnRdIGhhbmRsZXJPcHRpb25zLmNsaWVudFBpcGVhYmxlU3RyZWFtT3B0aW9uczogJHtKU09OLnN0cmluZ2lmeShoYW5kbGVyT3B0aW9ucy5jbGllbnRQaXBlYWJsZVN0cmVhbU9wdGlvbnMpfWBcbiAgICAgICk7XG4gICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgIGBbcmVuZGVyUGFnZS5jbGllbnRdIG5ld0hhbmRsZXJPcHRpb25zLmNsaWVudFBpcGVhYmxlU3RyZWFtT3B0aW9uczogJHtKU09OLnN0cmluZ2lmeShuZXdIYW5kbGVyT3B0aW9ucy5jbGllbnRQaXBlYWJsZVN0cmVhbU9wdGlvbnMpfWBcbiAgICAgICk7XG4gICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgIGBbcmVuZGVyUGFnZS5jbGllbnRdIG5ld0hhbmRsZXJPcHRpb25zIHBhZ2UgcGF0aHM6IHBhZ2VQYXRoPSR7bmV3SGFuZGxlck9wdGlvbnMucGFnZVBhdGh9LCBwcm9wc1BhdGg9JHtuZXdIYW5kbGVyT3B0aW9ucy5wcm9wc1BhdGh9LCByb290UGF0aD0ke25ld0hhbmRsZXJPcHRpb25zLnJvb3RQYXRofSwgaHRtbFBhdGg9JHtuZXdIYW5kbGVyT3B0aW9ucy5odG1sUGF0aH1gXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIENvbXBvbmVudCByZXNvbHV0aW9uIGlzIGFscmVhZHkgbWVhc3VyZWQgaW4gcmVzb2x2ZUNvbXBvbmVudHNcbiAgICAvLyBObyBuZWVkIHRvIG1lYXN1cmUgbW9kdWxlIHJlc29sdXRpb24gdGltZSBoZXJlIGFueW1vcmVcblxuICAgIC8vIENyZWF0ZSBoZWFkbGVzcyBSU0Mgc3RyZWFtIGZpcnN0IChmb3IgLnJzYyBmaWxlKVxuICAgIGNvbnN0IHVuaXF1ZUlkID0gaGFuZGxlck9wdGlvbnMuaWQgPz8gYCR7aGFuZGxlck9wdGlvbnMucm91dGV9LSR7RGF0ZS5ub3coKX0tJHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHJpbmcoMiwgMTEpfWA7XG4gICAgXG4gICAgY29uc3QgaGVhZGxlc3NSc2NTdHJlYW1Mb2NhbCA9IGNyZWF0ZVJzY1N0cmVhbSh7XG4gICAgICAuLi5uZXdIYW5kbGVyT3B0aW9ucyxcbiAgICAgIGlkOiBgJHtoYW5kbGVyT3B0aW9ucy5yb3V0ZX0taGVhZGxlc3MtJHt1bmlxdWVJZH1gLFxuICAgICAgcnNjVGltZW91dDogaGFuZGxlck9wdGlvbnMucnNjVGltZW91dCB8fCA1MDAwLFxuICAgICAgb25NZXRyaWNzOiBoYW5kbGVyT3B0aW9ucy5vbk1ldHJpY3MsXG4gICAgICAvLyBIZWFkbGVzcyBSU0Mgc3RyZWFtOiBwYWdlIGNvbnRlbnQgb25seSAoZm9yIC5yc2MgZmlsZSlcbiAgICAgIGh0bWxQYXRoOiAnJywgLy8gTm8gSFRNTCB3cmFwcGVyIC0ganVzdCBwYWdlIGNvbnRlbnRcbiAgICAgIHBhZ2VQYXRoOiBuZXdIYW5kbGVyT3B0aW9ucy5wYWdlUGF0aCB8fCAnJywgLy8gRW5zdXJlIHBhZ2VQYXRoIGlzIGFsd2F5cyBhIHN0cmluZ1xuICAgICAgdXJsOiBuZXdIYW5kbGVyT3B0aW9ucy51cmwgfHwgJycsIC8vIEVuc3VyZSB1cmwgaXMgYWx3YXlzIGEgc3RyaW5nXG4gICAgICBwYWdlUHJvcHM6IG5ld0hhbmRsZXJPcHRpb25zLnBhZ2VQcm9wcyB8fCB7fSwgLy8gRW5zdXJlIHBhZ2VQcm9wcyBpcyBhbHdheXMgYW4gb2JqZWN0XG4gICAgICBvbkV2ZW50OiB3cmFwcGVkT25FdmVudCxcbiAgICB9KTtcblxuICAgIC8vIENyZWF0ZSBmdWxsIFJTQyBzdHJlYW0gdGhhdCByZXVzZXMgdGhlIGhlYWRsZXNzIHN0cmVhbSBlbGVtZW50c1xuICAgIGNvbnN0IGZ1bGxSc2NTdHJlYW1Mb2NhbCA9IGNyZWF0ZVJzY1N0cmVhbSh7XG4gICAgICAuLi5uZXdIYW5kbGVyT3B0aW9ucyxcbiAgICAgIGlkOiBgJHtoYW5kbGVyT3B0aW9ucy5yb3V0ZX0tZnVsbC0ke3VuaXF1ZUlkfWAsXG4gICAgICByc2NUaW1lb3V0OiBoYW5kbGVyT3B0aW9ucy5yc2NUaW1lb3V0IHx8IDUwMDAsXG4gICAgICBvbk1ldHJpY3M6IGhhbmRsZXJPcHRpb25zLm9uTWV0cmljcyxcbiAgICAgIC8vIEZ1bGwgUlNDIHN0cmVhbTogaW5jbHVkZSBIVE1MIHdyYXBwZXIgKGZvciBIVE1MIGdlbmVyYXRpb24pXG4gICAgICAvLyBQYXNzIHRocm91Z2ggdGhlIHJlc29sdmVkIGh0bWxQYXRoIHNvIGN1c3RvbSBIdG1sIGNvbXBvbmVudHMgd29yayBpbiBjbGllbnQgbW9kZVxuICAgICAgaHRtbFBhdGg6IHJlc29sdmVkSHRtbFBhdGgsXG4gICAgICBwYWdlUGF0aDogbmV3SGFuZGxlck9wdGlvbnMucGFnZVBhdGggfHwgJycsIC8vIEVuc3VyZSBwYWdlUGF0aCBpcyBhbHdheXMgYSBzdHJpbmdcbiAgICAgIHVybDogbmV3SGFuZGxlck9wdGlvbnMudXJsIHx8ICcnLCAvLyBFbnN1cmUgdXJsIGlzIGFsd2F5cyBhIHN0cmluZ1xuICAgICAgcGFnZVByb3BzOiBuZXdIYW5kbGVyT3B0aW9ucy5wYWdlUHJvcHMgfHwge30sIC8vIEVuc3VyZSBwYWdlUHJvcHMgaXMgYWx3YXlzIGFuIG9iamVjdFxuICAgICAgLy8gUmV1c2UgaGVhZGxlc3Mgc3RyZWFtIGVsZW1lbnRzIC0gdGhlIHdvcmtlciB3aWxsIGhhbmRsZSB0aGlzIHdpdGggdGhlIHVuaXF1ZSBJRFxuICAgICAgcmV1c2VIZWFkbGVzc1N0cmVhbUlkOiBoZWFkbGVzc1JzY1N0cmVhbUxvY2FsLmlkLFxuICAgICAgb25FdmVudDogd3JhcHBlZE9uRXZlbnQsXG4gICAgfSk7XG5cbiAgICAvLyBBc3NpZ24gdG8gdGhlIG91dGVyIHZhcmlhYmxlc1xuICAgIGhlYWRsZXNzUnNjU3RyZWFtID0gaGVhZGxlc3NSc2NTdHJlYW1Mb2NhbDtcbiAgICBmdWxsUnNjU3RyZWFtID0gZnVsbFJzY1N0cmVhbUxvY2FsO1xuXG4gICAgLy8gVGhlIGhlYWRsZXNzIHN0cmVhbSB3aWxsIGJlIGNvbnN1bWVkIG5hdHVyYWxseSBieSB0aGUgZmlsZSB3cml0aW5nXG4gICAgLy8gVGhlIGZ1bGwgc3RyZWFtIHdpbGwgcmV1c2UgdGhlIGhlYWRsZXNzIHN0cmVhbSBlbGVtZW50cyBmb3IgSFRNTCBnZW5lcmF0aW9uXG5cbiAgICAvLyBTdGVwIDM6IENyZWF0ZSBIVE1MIHRyYW5zZm9ybSBzdHJlYW1cbiAgICBpZiAoaGFuZGxlck9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy5pbmZvKFxuICAgICAgICBgW3JlbmRlclBhZ2UuY2xpZW50XSBDcmVhdGluZyBIVE1MIHRyYW5zZm9ybSBzdHJlYW0gd2l0aCBjbGllbnRQaXBlYWJsZVN0cmVhbU9wdGlvbnM6ICR7SlNPTi5zdHJpbmdpZnkobmV3SGFuZGxlck9wdGlvbnMuY2xpZW50UGlwZWFibGVTdHJlYW1PcHRpb25zKX1gXG4gICAgICApO1xuICAgIH1cbiAgICAvLyBDcmVhdGUgSFRNTCBzdHJlYW0gdXNpbmcgdGhlIGZ1bGwgUlNDIHN0cmVhbSAod2hpY2ggcmV1c2VzIGhlYWRsZXNzIHN0cmVhbSBlbGVtZW50cylcbiAgICBjb25zdCBodG1sVHJhbnNmb3JtU3RyZWFtID0gY3JlYXRlUnNjVG9IdG1sU3RyZWFtKHtcbiAgICAgIC4uLm5ld0hhbmRsZXJPcHRpb25zLFxuICAgICAgaHRtbFRpbWVvdXQ6IGhhbmRsZXJPcHRpb25zLmh0bWxUaW1lb3V0IHx8IDE1MDAwLFxuICAgICAgcm91dGU6IGhhbmRsZXJPcHRpb25zLnJvdXRlLFxuICAgICAgbG9nZ2VyOiBoYW5kbGVyT3B0aW9ucy5sb2dnZXIsXG4gICAgICB2ZXJib3NlOiBoYW5kbGVyT3B0aW9ucy52ZXJib3NlLFxuICAgICAgcnNjU3RyZWFtOiBmdWxsUnNjU3RyZWFtTG9jYWwucnNjU3RyZWFtLFxuICAgIH0pO1xuXG4gICAgaHRtbEhhbmRsZXIgPSB7XG4gICAgICBodG1sU3RyZWFtOiBodG1sVHJhbnNmb3JtU3RyZWFtLFxuICAgICAgYWJvcnQ6ICgpID0+IHtcbiAgICAgICAgaHRtbFRyYW5zZm9ybVN0cmVhbS5hYm9ydCgpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICAvLyBDcmVhdGUgc3RyZWFtIHdyYXBwZXJzIGZvciBmaWxlIHdyaXRpbmdcbiAgICBjb25zdCByc2NTdHJlYW1XcmFwcGVyID0ge1xuICAgICAgcGlwZTogPFdyaXRhYmxlIGV4dGVuZHMgTm9kZUpTLldyaXRhYmxlU3RyZWFtPihkZXN0aW5hdGlvbjogV3JpdGFibGUpID0+IHtcbiAgICAgICAgY29uc3Qgc3RyZWFtTWV0cmljcyA9IGNyZWF0ZVN0cmVhbU1ldHJpY3MoKTtcbiAgICAgICAgc3RyZWFtTWV0cmljcy5zdGFydFRpbWUgPSBwZXJmb3JtYW5jZS5ub3coKTtcblxuICAgICAgICAvLyBVc2UgdGhlIGhlYWRsZXNzIFJTQyBzdHJlYW0gZGlyZWN0bHkgZm9yIHRoZSAucnNjIGZpbGVcbiAgICAgICAgY29uc3QgcnNjRmlsZVN0cmVhbSA9IGhlYWRsZXNzUnNjU3RyZWFtLnJzY1N0cmVhbTtcblxuICAgICAgICByc2NGaWxlU3RyZWFtLm9uKFwiZGF0YVwiLCAoY2h1bms6IEJ1ZmZlcikgPT4ge1xuICAgICAgICAgIHN0cmVhbU1ldHJpY3MuY2h1bmtzKys7XG4gICAgICAgICAgc3RyZWFtTWV0cmljcy5ieXRlcyArPSBjaHVuay5sZW5ndGg7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJzY0ZpbGVTdHJlYW0ub24oXCJlbmRcIiwgKCkgPT4ge1xuICAgICAgICAgIHN0cmVhbU1ldHJpY3MuZHVyYXRpb24gPSBwZXJmb3JtYW5jZS5ub3coKSAtIHN0cmVhbU1ldHJpY3Muc3RhcnRUaW1lO1xuICAgICAgICAgIHN0cmVhbU1ldHJpY3MuZW5kVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpO1xuXG4gICAgICAgICAgcnNjSGVhZGxlc3NNZXRyaWNzLnN0cmVhbU1ldHJpY3MgPSBzdHJlYW1NZXRyaWNzO1xuICAgICAgICAgIHJzY0hlYWRsZXNzTWV0cmljcy5jaHVua1JhdGUgPSBzdHJlYW1NZXRyaWNzLmNodW5rcyAvIChzdHJlYW1NZXRyaWNzLmR1cmF0aW9uIC8gMTAwMCk7XG4gICAgICAgICAgcnNjSGVhZGxlc3NNZXRyaWNzLnByb2Nlc3NpbmdUaW1lID0gc3RyZWFtTWV0cmljcy5kdXJhdGlvbjtcbiAgICAgICAgICByc2NIZWFkbGVzc01ldHJpY3MubWVtb3J5VXNhZ2UgPSBwcm9jZXNzLm1lbW9yeVVzYWdlKCk7XG4gICAgICAgICAgcnNjSGVhZGxlc3NNZXRyaWNzLmNodW5rcyA9IHN0cmVhbU1ldHJpY3MuY2h1bmtzO1xuICAgICAgICB9KTtcblxuICAgICAgICByc2NGaWxlU3RyZWFtLnBpcGUoZGVzdGluYXRpb24pO1xuICAgICAgICByZXR1cm4gZGVzdGluYXRpb247XG4gICAgICB9LFxuICAgICAgYWJvcnQ6ICgpID0+IGhlYWRsZXNzUnNjU3RyZWFtLmFib3J0KCksXG4gICAgfTtcblxuICAgIGNvbnN0IGh0bWxTdHJlYW1XcmFwcGVyID0ge1xuICAgICAgcGlwZTogPFdyaXRhYmxlIGV4dGVuZHMgTm9kZUpTLldyaXRhYmxlU3RyZWFtPihkZXN0aW5hdGlvbjogV3JpdGFibGUpID0+IHtcbiAgICAgICAgaWYgKGhhbmRsZXJPcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgICAgICBgW3JlbmRlclBhZ2UuY2xpZW50XSBQaXBpbmcgSFRNTCBzdHJlYW0gdG8gZGVzdGluYXRpb24gZm9yIHJvdXRlOiAke2hhbmRsZXJPcHRpb25zLnJvdXRlfWBcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAvLyBVc2UgdGhlIEhUTUwgdHJhbnNmb3JtIHN0cmVhbSdzIHBpcGUgbWV0aG9kIGRpcmVjdGx5IChzYW1lIGFzIHNlcnZlciBzaWRlKVxuICAgICAgICByZXR1cm4gaHRtbFRyYW5zZm9ybVN0cmVhbS5waXBlKGRlc3RpbmF0aW9uKTtcbiAgICAgIH0sXG4gICAgICBhYm9ydDogKCkgPT4ge1xuICAgICAgICBmdWxsUnNjU3RyZWFtLmFib3J0KCk7XG4gICAgICAgIGlmIChodG1sSGFuZGxlci5hYm9ydCkge1xuICAgICAgICAgIGh0bWxIYW5kbGVyLmFib3J0KCk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICBvbjogKGV2ZW50OiBzdHJpbmcsIGxpc3RlbmVyOiAoLi4uYXJnczogYW55W10pID0+IHZvaWQpID0+IHtcbiAgICAgICAgLy8gRm9yd2FyZCBlcnJvciBldmVudHMgZnJvbSB0aGUgSFRNTCB0cmFuc2Zvcm0gc3RyZWFtIHRvIHRoZSB3cmFwcGVyXG4gICAgICAgIGlmIChldmVudCA9PT0gJ2Vycm9yJykge1xuICAgICAgICAgIC8vIEFjY2VzcyB0aGUgYWN0dWFsIHN0cmVhbSBmcm9tIHRoZSB0cmFuc2Zvcm0gcmVzdWx0XG4gICAgICAgICAgY29uc3QgaHRtbFN0cmVhbSA9IChodG1sVHJhbnNmb3JtU3RyZWFtIGFzIGFueSkuaHRtbFN0cmVhbTtcbiAgICAgICAgICBpZiAoaHRtbFN0cmVhbSAmJiB0eXBlb2YgaHRtbFN0cmVhbS5vbiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgaHRtbFN0cmVhbS5vbignZXJyb3InLCBsaXN0ZW5lcik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBodG1sU3RyZWFtV3JhcHBlcjtcbiAgICAgIH0sXG4gICAgfTtcblxuICAgIC8vIERvbid0IGVtaXQgaW5pdGlhbCBtZXRyaWNzIC0gd2FpdCBmb3IgZmlsZSB3cml0ZXMgdG8gY29tcGxldGVcbiAgICAvLyBUaGUgb25NZXRyaWNzIGNhbGxiYWNrIHdpbGwgYmUgY2FsbGVkIGFmdGVyIGJvdGggZmlsZS53cml0ZS5kb25lIGV2ZW50c1xuXG4gICAgLy8gQ2hlY2sgaWYgd2UgaGF2ZSBhbiBlcnJvciByZXN1bHQgdG8geWllbGQgKHdpdGggdGltZW91dCBwcm90ZWN0aW9uKVxuICAgIC8vIFdhaXQgYSBzaG9ydCB0aW1lIGZvciBhbnkgcGVuZGluZyByb3V0ZS5lcnJvciBldmVudHNcbiAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMTAwKSk7XG4gICAgXG4gICAgaWYgKGVycm9yUmVzdWx0KSB7XG4gICAgICB5aWVsZCBlcnJvclJlc3VsdDtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB5aWVsZCB7XG4gICAgICB0eXBlOiBcInN1Y2Nlc3NcIixcbiAgICAgIGh0bWw6IGh0bWxTdHJlYW1XcmFwcGVyLFxuICAgICAgcnNjOiByc2NTdHJlYW1XcmFwcGVyLFxuICAgICAgbWV0cmljczoge1xuICAgICAgICByc2NGdWxsOiByc2NGdWxsTWV0cmljcyxcbiAgICAgICAgcnNjSGVhZGxlc3M6IHJzY0hlYWRsZXNzTWV0cmljcyxcbiAgICAgICAgaHRtbDogaHRtbE1ldHJpY3MsXG4gICAgICB9LFxuICAgIH0gYXMgY29uc3Q7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgLy8gQ2xlYW4gdXAgcmVzb3VyY2VzXG4gICAgdHJ5IHtcbiAgICAgIGlmIChoZWFkbGVzc1JzY1N0cmVhbSkgaGVhZGxlc3NSc2NTdHJlYW0uYWJvcnQoKTtcbiAgICAgIGlmIChmdWxsUnNjU3RyZWFtKSBmdWxsUnNjU3RyZWFtLmFib3J0KCk7XG4gICAgICBpZiAoaHRtbEhhbmRsZXI/LmFib3J0KSBodG1sSGFuZGxlci5hYm9ydCgpO1xuICAgIH0gY2F0Y2ggKGNsZWFudXBFcnJvcjogdW5rbm93bikge1xuICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy53YXJuKGBGYWlsZWQgdG8gY2xlYW51cCBzdHJlYW1zIG9uIGVycm9yOiAke2NsZWFudXBFcnJvcn1gKTtcbiAgICB9XG5cbiAgICBjb25zdCBwYW5pY0Vycm9yID0gaGFuZGxlRXJyb3Ioe1xuICAgICAgZXJyb3IsXG4gICAgICBsb2dnZXI6IGhhbmRsZXJPcHRpb25zLmxvZ2dlcixcbiAgICAgIGNvbnRleHQ6IFwicmVuZGVyUGFnZUNsaWVudFwiLFxuICAgICAgcGFuaWNUaHJlc2hvbGQ6IGhhbmRsZXJPcHRpb25zLnBhbmljVGhyZXNob2xkLFxuICAgIH0pO1xuXG4gICAgaWYgKHBhbmljRXJyb3IgIT0gbnVsbCkge1xuICAgICAgeWllbGQge1xuICAgICAgICB0eXBlOiBcImVycm9yXCIsXG4gICAgICAgIGVycm9yOiBwYW5pY0Vycm9yLFxuICAgICAgICBtZXRyaWNzOiB7XG4gICAgICAgICAgcnNjRnVsbDogcnNjRnVsbE1ldHJpY3MsXG4gICAgICAgICAgcnNjSGVhZGxlc3M6IHJzY0hlYWRsZXNzTWV0cmljcyxcbiAgICAgICAgICBodG1sOiBodG1sTWV0cmljcyxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEZvciBub24tcGFuaWMgZXJyb3JzLCB3ZSBzdGlsbCB3YW50IHRvIHdyaXRlIHRoZSBIVE1MIGZpbGUgKGNsaWVudC1vbmx5KVxuICAgICAgLy8gYnV0IHNraXAgdGhlIFJTQyBmaWxlIHNpbmNlIHRoZXJlIHdhcyBhIHNlcnZlciBlcnJvclxuICAgICAgXG4gICAgICAvLyBDcmVhdGUgYSBmYWxsYmFjayBSU0Mgc3RyZWFtIHdpdGggUmVhY3QuRnJhZ21lbnQgKHNhbWUgYXMgc2VydmVyIGVudmlyb25tZW50KVxuICAgICAgY29uc3QgZmFsbGJhY2tSc2NTdHJlYW0gPSBjcmVhdGVSc2NTdHJlYW0oe1xuICAgICAgICAuLi5oYW5kbGVyT3B0aW9ucyxcbiAgICAgICAgdXJsOiBgJHtoYW5kbGVyT3B0aW9ucy51cmx9YCxcbiAgICAgICAgcm91dGU6IGAke2hhbmRsZXJPcHRpb25zLnJvdXRlfWAsXG4gICAgICAgIGNzc0ZpbGVzOiBoYW5kbGVyT3B0aW9ucy5jc3NGaWxlcyB8fCBuZXcgTWFwKCksXG4gICAgICAgIGdsb2JhbENzczogaGFuZGxlck9wdGlvbnMuZ2xvYmFsQ3NzIHx8IG5ldyBNYXAoKSxcbiAgICAgICAgaWQ6IGAke2hhbmRsZXJPcHRpb25zLnJvdXRlfS1mYWxsYmFjay0ke0RhdGUubm93KCl9YCxcbiAgICAgICAgcnNjVGltZW91dDogaGFuZGxlck9wdGlvbnMucnNjVGltZW91dCB8fCA1MDAwLFxuICAgICAgICBvbk1ldHJpY3M6IGhhbmRsZXJPcHRpb25zLm9uTWV0cmljcyxcbiAgICAgICAgLy8gVXNlIFJlYWN0LkZyYWdtZW50IGFzIGZhbGxiYWNrIChzYW1lIGFzIHNlcnZlciBlbnZpcm9ubWVudClcbiAgICAgICAgcGFnZVBhdGg6ICcnLCAvLyBUaGlzIHdpbGwgY2F1c2UgdGhlIGRlZmF1bHQgcGFnZSB0byBiZSB1c2VkLCBidXQgd2UnbGwgb3ZlcnJpZGUgaXRcbiAgICAgICAgcGFnZVByb3BzOiB7fSwgLy8gRW5zdXJlIHBhZ2VQcm9wcyBpcyBhbHdheXMgYW4gb2JqZWN0XG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgLy8gQ3JlYXRlIEhUTUwgc3RyZWFtIHRoYXQgcHJvY2Vzc2VzIHRoZSBmYWxsYmFjayBSU0Mgc3RyZWFtIHRvIGVuc3VyZSBwZXJmb3JtYW5jZSB0aW1pbmcgc2NyaXB0IGlzIGluamVjdGVkXG4gICAgICBjb25zdCBmYWxsYmFja0h0bWxTdHJlYW0gPSBjcmVhdGVSc2NUb0h0bWxTdHJlYW0oe1xuICAgICAgICBpZDogaGFuZGxlck9wdGlvbnMuaWQsXG4gICAgICAgIHJvdXRlOiBoYW5kbGVyT3B0aW9ucy5yb3V0ZSxcbiAgICAgICAgdXJsOiBoYW5kbGVyT3B0aW9ucy51cmwsXG4gICAgICAgIG1vZHVsZVJvb3RQYXRoOiBoYW5kbGVyT3B0aW9ucy5tb2R1bGVSb290UGF0aCxcbiAgICAgICAgbW9kdWxlQmFzZVBhdGg6IGhhbmRsZXJPcHRpb25zLm1vZHVsZUJhc2VQYXRoLFxuICAgICAgICBtb2R1bGVCYXNlVVJMOiBoYW5kbGVyT3B0aW9ucy5tb2R1bGVCYXNlVVJMLFxuICAgICAgICBwcm9qZWN0Um9vdDogaGFuZGxlck9wdGlvbnMucHJvamVjdFJvb3QsXG4gICAgICAgIHBhbmljVGhyZXNob2xkOiBoYW5kbGVyT3B0aW9ucy5wYW5pY1RocmVzaG9sZCxcbiAgICAgICAgdmVyYm9zZTogaGFuZGxlck9wdGlvbnMudmVyYm9zZSxcbiAgICAgICAgc2lnbmFsOiBoYW5kbGVyT3B0aW9ucy5zaWduYWwsXG4gICAgICAgIGxvZ2dlcjogaGFuZGxlck9wdGlvbnMubG9nZ2VyLFxuICAgICAgICBodG1sVGltZW91dDogaGFuZGxlck9wdGlvbnMuaHRtbFRpbWVvdXQsXG4gICAgICAgIGNsaWVudFBpcGVhYmxlU3RyZWFtT3B0aW9uczogaGFuZGxlck9wdGlvbnMuY2xpZW50UGlwZWFibGVTdHJlYW1PcHRpb25zLFxuICAgICAgICBvbk1ldHJpY3M6IGhhbmRsZXJPcHRpb25zLm9uTWV0cmljcyxcbiAgICAgICAgYnVpbGQ6IGhhbmRsZXJPcHRpb25zLmJ1aWxkLFxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIC8vIENyZWF0ZSBhIHdyYXBwZXIgdGhhdCBwaXBlcyB0aGUgZmFsbGJhY2sgUlNDIHN0cmVhbSB0aHJvdWdoIHRoZSBIVE1MIHRyYW5zZm9ybVxuICAgICAgY29uc3QgY2xpZW50T25seUh0bWxTdHJlYW1XcmFwcGVyID0ge1xuICAgICAgICBwaXBlOiA8V3JpdGFibGUgZXh0ZW5kcyBOb2RlSlMuV3JpdGFibGVTdHJlYW0+KGRlc3RpbmF0aW9uOiBXcml0YWJsZSkgPT4ge1xuICAgICAgICAgIC8vIFBpcGUgdGhlIGZhbGxiYWNrIFJTQyBzdHJlYW0gdGhyb3VnaCB0aGUgSFRNTCB0cmFuc2Zvcm0gdG8gZW5zdXJlIHBlcmZvcm1hbmNlIHRpbWluZyBzY3JpcHQgaXMgaW5qZWN0ZWRcbiAgICAgICAgICByZXR1cm4gZmFsbGJhY2tIdG1sU3RyZWFtLnBpcGUoZGVzdGluYXRpb24pO1xuICAgICAgICB9LFxuICAgICAgICBhYm9ydDogKCkgPT4ge1xuICAgICAgICAgIC8vIENsZWFuIHVwIHRoZSBmYWxsYmFjayBSU0Mgc3RyZWFtXG4gICAgICAgICAgZmFsbGJhY2tSc2NTdHJlYW0uYWJvcnQoKTtcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgICBcbiAgICAgIC8vIENyZWF0ZSBhbiBlbXB0eSBSU0Mgc3RyZWFtIHdyYXBwZXJcbiAgICAgIGNvbnN0IGVtcHR5UnNjU3RyZWFtV3JhcHBlciA9IHtcbiAgICAgICAgcGlwZTogPFdyaXRhYmxlIGV4dGVuZHMgTm9kZUpTLldyaXRhYmxlU3RyZWFtPihkZXN0aW5hdGlvbjogV3JpdGFibGUpID0+IHtcbiAgICAgICAgICAvLyBObyBSU0MgY29udGVudCBmb3Igc2tpcHBlZCByb3V0ZXNcbiAgICAgICAgICBkZXN0aW5hdGlvbi5lbmQoKTtcbiAgICAgICAgICByZXR1cm4gZGVzdGluYXRpb247XG4gICAgICAgIH0sXG4gICAgICAgIGFib3J0OiAoKSA9PiB7XG4gICAgICAgICAgLy8gTm8gY2xlYW51cCBuZWVkZWRcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgICBcbiAgICAgIHlpZWxkIHtcbiAgICAgICAgdHlwZTogXCJza2lwXCIsXG4gICAgICAgIHJlYXNvbjogZXJyb3IsXG4gICAgICAgIGh0bWw6IGNsaWVudE9ubHlIdG1sU3RyZWFtV3JhcHBlcixcbiAgICAgICAgcnNjOiBlbXB0eVJzY1N0cmVhbVdyYXBwZXIsXG4gICAgICAgIG1ldHJpY3M6IHtcbiAgICAgICAgICByc2NGdWxsOiByc2NGdWxsTWV0cmljcyxcbiAgICAgICAgICByc2NIZWFkbGVzczogcnNjSGVhZGxlc3NNZXRyaWNzLFxuICAgICAgICAgIGh0bWw6IGh0bWxNZXRyaWNzLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICB9XG4gIH1cbn07ICJdLCJuYW1lcyI6WyJtYW5pZmVzdCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQTJEQSxvQkFBcUIsRUFBQTtBQVFSLE1BQUEsVUFBQSxHQUEyQixnQkFBZ0IsaUJBQUEsQ0FDdEQsY0FDQSxFQUFBO0FBQ0EsRUFBQSxJQUFJLGVBQWUsT0FBUyxFQUFBO0FBQzFCLElBQUEsY0FBQSxDQUFlLE1BQVEsRUFBQSxJQUFBO0FBQUEsTUFDckIsQ0FBZ0QsNkNBQUEsRUFBQSxDQUFDLENBQUMsY0FBQSxDQUFlLE9BQU8sQ0FBQTtBQUFBLEtBQzFFO0FBQ0EsSUFBQSxjQUFBLENBQWUsTUFBUSxFQUFBLElBQUE7QUFBQSxNQUNyQixDQUFrRCwrQ0FBQSxFQUFBLENBQUMsQ0FBQyxjQUFBLENBQWUsU0FBUyxDQUFBO0FBQUEsS0FDOUU7QUFBQTtBQUlGLEVBQUEsSUFBSSxVQUFhLEdBQUEsS0FBQTtBQUNqQixFQUFBLElBQUksV0FBbUIsR0FBQSxJQUFBO0FBR3ZCLEVBQU0sTUFBQSxjQUFBLEdBQWlCLENBQUMsS0FBZSxLQUFBO0FBRXJDLElBQUEsSUFBSSxlQUFlLE9BQVMsRUFBQTtBQUMxQixNQUFBLGNBQUEsQ0FBZSxRQUFRLEtBQUssQ0FBQTtBQUFBO0FBSTlCLElBQUEsSUFBSSxLQUFNLENBQUEsSUFBQSxLQUFTLGFBQWlCLElBQUEsQ0FBQyxVQUFZLEVBQUE7QUFDL0MsTUFBYSxVQUFBLEdBQUEsSUFBQTtBQUdiLE1BQUEsTUFBTSxhQUFhLFdBQVksQ0FBQTtBQUFBLFFBQzdCLEtBQUEsRUFBTyxNQUFNLElBQUssQ0FBQSxLQUFBO0FBQUEsUUFDbEIsUUFBUSxjQUFlLENBQUEsTUFBQTtBQUFBLFFBQ3ZCLGNBQUEsRUFBZ0IsTUFBTSxJQUFLLENBQUEsY0FBQTtBQUFBLFFBQzNCLE9BQVMsRUFBQSxDQUFBLGFBQUEsRUFBZ0IsS0FBTSxDQUFBLElBQUEsQ0FBSyxLQUFLLENBQUEsQ0FBQTtBQUFBLE9BQzFDLENBQUE7QUFFRCxNQUFBLElBQUksY0FBYyxJQUFNLEVBQUE7QUFFdEIsUUFBYyxXQUFBLEdBQUE7QUFBQSxVQUNaLElBQU0sRUFBQSxPQUFBO0FBQUEsVUFDTixLQUFPLEVBQUEsVUFBQTtBQUFBLFVBQ1AsT0FBUyxFQUFBO0FBQUEsWUFDUCxhQUFhLEVBQUUsUUFBQSxFQUFVLEdBQUcsTUFBUSxFQUFBLENBQUEsRUFBRyxPQUFPLENBQUUsRUFBQTtBQUFBLFlBQ2hELE1BQU0sRUFBRSxRQUFBLEVBQVUsR0FBRyxNQUFRLEVBQUEsQ0FBQSxFQUFHLE9BQU8sQ0FBRTtBQUFBO0FBQzNDLFNBQ0Y7QUFBQSxPQUNLLE1BQUE7QUFFTCxRQUFjLFdBQUEsR0FBQTtBQUFBLFVBQ1osSUFBTSxFQUFBLE1BQUE7QUFBQSxVQUNOLE1BQVEsRUFBQSxLQUFBLENBQU0sSUFBSyxDQUFBLEtBQUEsQ0FBTSxPQUFXLElBQUEsMEJBQUE7QUFBQSxVQUNwQyxNQUFNLEVBQUUsUUFBQSxFQUFVLEdBQUcsTUFBUSxFQUFBLENBQUEsRUFBRyxPQUFPLENBQUUsRUFBQTtBQUFBLFVBQ3pDLEtBQUssRUFBRSxRQUFBLEVBQVUsR0FBRyxNQUFRLEVBQUEsQ0FBQSxFQUFHLE9BQU8sQ0FBRSxFQUFBO0FBQUEsVUFDeEMsT0FBUyxFQUFBO0FBQUEsWUFDUCxhQUFhLEVBQUUsUUFBQSxFQUFVLEdBQUcsTUFBUSxFQUFBLENBQUEsRUFBRyxPQUFPLENBQUUsRUFBQTtBQUFBLFlBQ2hELE1BQU0sRUFBRSxRQUFBLEVBQVUsR0FBRyxNQUFRLEVBQUEsQ0FBQSxFQUFHLE9BQU8sQ0FBRTtBQUFBO0FBQzNDLFNBQ0Y7QUFBQTtBQUNGO0FBQ0YsR0FDRjtBQUdBLEVBQUEsSUFBSSxDQUFDLGNBQUEsQ0FBZSxRQUFZLElBQUEsQ0FBQyxlQUFlLGFBQWUsRUFBQTtBQUU3RCxJQUFBLE1BQU0sa0JBQXFCLEdBQUE7QUFBQSxNQUN6QixJQUFBLEVBQU0sQ0FBeUMsV0FBMEIsS0FBQTtBQUN2RSxRQUFBLFdBQUEsQ0FBWSxHQUFJLEVBQUE7QUFDaEIsUUFBTyxPQUFBLFdBQUE7QUFBQSxPQUNUO0FBQUEsTUFDQSxPQUFPLE1BQU07QUFBQTtBQUViLEtBQ0Y7QUFFQSxJQUFNLE1BQUE7QUFBQSxNQUNKLElBQU0sRUFBQSxNQUFBO0FBQUEsTUFDTixNQUFRLEVBQUEsMkNBQUE7QUFBQSxNQUNSLElBQU0sRUFBQSxrQkFBQTtBQUFBLE1BQ04sR0FBSyxFQUFBLGtCQUFBO0FBQUEsTUFDTCxPQUFTLEVBQUE7QUFBQSxRQUNQLFNBQVMsbUJBQW9CLENBQUE7QUFBQSxVQUMzQixPQUFPLGNBQWUsQ0FBQSxLQUFBO0FBQUEsVUFDdEIsSUFBTSxFQUFBLFVBQUE7QUFBQSxVQUNOLGNBQWdCLEVBQUEsS0FBQTtBQUFBLFVBQ2hCLGFBQWUsRUFBQSxJQUFBO0FBQUEsVUFDZixjQUFnQixFQUFBO0FBQUEsU0FDakIsQ0FBQTtBQUFBLFFBQ0QsYUFBYSxtQkFBb0IsQ0FBQTtBQUFBLFVBQy9CLE9BQU8sY0FBZSxDQUFBLEtBQUE7QUFBQSxVQUN0QixJQUFNLEVBQUEsY0FBQTtBQUFBLFVBQ04sY0FBZ0IsRUFBQSxLQUFBO0FBQUEsVUFDaEIsYUFBZSxFQUFBLElBQUE7QUFBQSxVQUNmLGNBQWdCLEVBQUE7QUFBQSxTQUNqQixDQUFBO0FBQUEsUUFDRCxNQUFNLG1CQUFvQixDQUFBO0FBQUEsVUFDeEIsT0FBTyxjQUFlLENBQUEsS0FBQTtBQUFBLFVBQ3RCLElBQU0sRUFBQSxNQUFBO0FBQUEsVUFDTixjQUFnQixFQUFBLElBQUE7QUFBQSxVQUNoQixhQUFlLEVBQUEsS0FBQTtBQUFBLFVBQ2YsY0FBZ0IsRUFBQTtBQUFBLFNBQ2pCO0FBQUE7QUFDSCxLQUNGO0FBQ0EsSUFBQTtBQUFBO0FBR0YsRUFBSSxJQUFBLENBQUMsZUFBZSxHQUFLLEVBQUE7QUFDdkIsSUFBQSxjQUFBLENBQWUsR0FBTSxHQUFBLFVBQUE7QUFBQSxNQUNuQixjQUFlLENBQUEsS0FBQTtBQUFBLE1BQ2YsY0FBZSxDQUFBLGFBQUE7QUFBQSxNQUNmLGVBQWUsS0FBTSxDQUFBO0FBQUEsS0FDdkI7QUFBQTtBQUdGLEVBQUEsTUFBTSxPQUFVLEdBQUEsSUFBQTtBQUFBLElBQ2QsZUFBZSxLQUFNLENBQUEsTUFBQTtBQUFBLElBQ3JCLGVBQWUsS0FBTSxDQUFBO0FBQUEsR0FDdkI7QUFDQSxFQUFBLE1BQU0sU0FBWSxHQUFBLGNBQUEsQ0FBZSxLQUFNLENBQUEsT0FBQSxDQUFRLE9BQU8sRUFBRSxDQUFBO0FBR3hELEVBQUEsTUFBTSxjQUFjLG1CQUFvQixDQUFBO0FBQUEsSUFDdEMsT0FBTyxjQUFlLENBQUEsS0FBQTtBQUFBLElBQ3RCLElBQU0sRUFBQSxNQUFBO0FBQUEsSUFDTixjQUFnQixFQUFBLElBQUE7QUFBQTtBQUFBLElBQ2hCLGFBQWUsRUFBQSxLQUFBO0FBQUEsSUFDZixjQUFnQixFQUFBLEtBQUE7QUFBQSxJQUNoQixPQUFBO0FBQUEsSUFDQSxTQUFBO0FBQUEsSUFDQSxRQUFBLEVBQVUsZUFBZSxLQUFNLENBQUEsY0FBQTtBQUFBLElBQy9CLFlBQVksSUFBSyxDQUFBLE9BQUEsRUFBUyxTQUFXLEVBQUEsY0FBQSxDQUFlLE1BQU0sY0FBYztBQUFBLEdBQ3pFLENBQUE7QUFFRCxFQUFBLE1BQU0saUJBQWlCLG1CQUFvQixDQUFBO0FBQUEsSUFDekMsT0FBTyxjQUFlLENBQUEsS0FBQTtBQUFBLElBQ3RCLElBQU0sRUFBQSxVQUFBO0FBQUEsSUFDTixjQUFnQixFQUFBLEtBQUE7QUFBQSxJQUNoQixhQUFlLEVBQUEsSUFBQTtBQUFBO0FBQUEsSUFDZixjQUFnQixFQUFBO0FBQUEsR0FDakIsQ0FBQTtBQUVELEVBQUEsTUFBTSxxQkFBcUIsbUJBQW9CLENBQUE7QUFBQSxJQUM3QyxPQUFPLGNBQWUsQ0FBQSxLQUFBO0FBQUEsSUFDdEIsSUFBTSxFQUFBLGNBQUE7QUFBQSxJQUNOLGNBQWdCLEVBQUEsS0FBQTtBQUFBLElBQ2hCLGFBQWUsRUFBQSxJQUFBO0FBQUE7QUFBQSxJQUNmLGNBQWdCLEVBQUEsS0FBQTtBQUFBLElBQ2hCLE9BQUE7QUFBQSxJQUNBLFNBQUE7QUFBQSxJQUNBLFFBQUEsRUFBVSxlQUFlLEtBQU0sQ0FBQSxhQUFBO0FBQUEsSUFDL0IsWUFBWSxJQUFLLENBQUEsT0FBQSxFQUFTLFNBQVcsRUFBQSxjQUFBLENBQWUsTUFBTSxhQUFhO0FBQUEsR0FDeEUsQ0FBQTtBQUdELEVBQUEsSUFBSSxpQkFBeUIsR0FBQSxJQUFBO0FBQzdCLEVBQUEsSUFBSSxhQUFxQixHQUFBLElBQUE7QUFDekIsRUFBQSxJQUFJLFdBQW1CLEdBQUEsSUFBQTtBQUl2QixFQUFJLElBQUE7QUFDRixJQUFBLElBQUksZUFBZSxPQUFTLEVBQUE7QUFDMUIsTUFBQSxjQUFBLENBQWUsTUFBUSxFQUFBLElBQUE7QUFBQSxRQUNyQixDQUFBLHFEQUFBLEVBQXdELGVBQWUsS0FBSyxDQUFBO0FBQUEsT0FDOUU7QUFBQTtBQU1GLElBQU0sTUFBQSx1QkFBQSxHQUEwQixDQUFDLElBQUEsRUFBY0EsU0FBMEIsS0FBQTtBQUN2RSxNQUFNLE1BQUEsS0FBQSxHQUFRQSxVQUFTLElBQUksQ0FBQTtBQUMzQixNQUFJLElBQUEsS0FBQSxJQUFTLE1BQU0sSUFBTSxFQUFBO0FBQ3ZCLFFBQUEsT0FBTyxLQUFNLENBQUEsSUFBQTtBQUFBO0FBRWYsTUFBTyxPQUFBLElBQUE7QUFBQSxLQUNUO0FBR0EsSUFBTSxNQUFBLFFBQUEsR0FBVyxjQUFlLENBQUEsUUFBQSxJQUFZLEVBQUM7QUFDN0MsSUFBQSxNQUFNLG1CQUFtQixjQUFlLENBQUEsUUFBQSxHQUFXLHdCQUF3QixjQUFlLENBQUEsUUFBQSxFQUFVLFFBQVEsQ0FBSSxHQUFBLEtBQUEsQ0FBQTtBQUNoSCxJQUFBLE1BQU0sb0JBQW9CLGNBQWUsQ0FBQSxTQUFBLEdBQVksd0JBQXdCLGNBQWUsQ0FBQSxTQUFBLEVBQVcsUUFBUSxDQUFJLEdBQUEsS0FBQSxDQUFBO0FBQ25ILElBQUEsTUFBTSxtQkFBbUIsY0FBZSxDQUFBLFFBQUEsR0FBVyx3QkFBd0IsY0FBZSxDQUFBLFFBQUEsRUFBVSxRQUFRLENBQUksR0FBQSxLQUFBLENBQUE7QUFDaEgsSUFBQSxNQUFNLG1CQUFtQixjQUFlLENBQUEsUUFBQSxHQUFXLHdCQUF3QixjQUFlLENBQUEsUUFBQSxFQUFVLFFBQVEsQ0FBSSxHQUFBLEtBQUEsQ0FBQTtBQUVoSCxJQUFBLElBQUksZUFBZSxPQUFTLEVBQUE7QUFDMUIsTUFBQSxjQUFBLENBQWUsTUFBUSxFQUFBLElBQUEsQ0FBSyxDQUFnRCw2Q0FBQSxFQUFBLGNBQUEsQ0FBZSxLQUFLLENBQUcsQ0FBQSxDQUFBLENBQUE7QUFDbkcsTUFBQSxjQUFBLENBQWUsUUFBUSxJQUFLLENBQUEsQ0FBQSxRQUFBLEVBQVcsZUFBZSxRQUFRLENBQUEsSUFBQSxFQUFPLGdCQUFnQixDQUFFLENBQUEsQ0FBQTtBQUN2RixNQUFBLGNBQUEsQ0FBZSxRQUFRLElBQUssQ0FBQSxDQUFBLFNBQUEsRUFBWSxlQUFlLFNBQVMsQ0FBQSxJQUFBLEVBQU8saUJBQWlCLENBQUUsQ0FBQSxDQUFBO0FBQzFGLE1BQUEsY0FBQSxDQUFlLFFBQVEsSUFBSyxDQUFBLENBQUEsUUFBQSxFQUFXLGVBQWUsUUFBUSxDQUFBLElBQUEsRUFBTyxnQkFBZ0IsQ0FBRSxDQUFBLENBQUE7QUFDdkYsTUFBQSxjQUFBLENBQWUsUUFBUSxJQUFLLENBQUEsQ0FBQSxRQUFBLEVBQVcsZUFBZSxRQUFRLENBQUEsSUFBQSxFQUFPLGdCQUFnQixDQUFFLENBQUEsQ0FBQTtBQUN2RixNQUFlLGNBQUEsQ0FBQSxNQUFBLEVBQVEsSUFBSyxDQUFBLENBQUEsaUJBQUEsRUFBb0IsTUFBTyxDQUFBLElBQUEsQ0FBSyxRQUFRLENBQUUsQ0FBQSxJQUFBLENBQUssSUFBSSxDQUFDLENBQUUsQ0FBQSxDQUFBO0FBQ2xGLE1BQUEsY0FBQSxDQUFlLE1BQVEsRUFBQSxJQUFBLENBQUssQ0FBZ0MsNkJBQUEsRUFBQSxjQUFBLENBQWUsUUFBUSxDQUFnQixhQUFBLEVBQUEsZ0JBQWdCLENBQStCLDRCQUFBLEVBQUEsQ0FBQyxDQUFDLFFBQVMsQ0FBQSxjQUFBLENBQWUsUUFBWSxJQUFBLEVBQUUsQ0FBQyxDQUFFLENBQUEsQ0FBQTtBQUM3TCxNQUFBLGNBQUEsQ0FBZSxNQUFRLEVBQUEsSUFBQSxDQUFLLENBQTZCLDBCQUFBLEVBQUEsZ0JBQWdCLENBQWlCLGVBQUEsQ0FBQSxDQUFBO0FBQUE7QUFFNUYsSUFBTSxNQUFBLE1BQUEsR0FBUyxjQUFlLENBQUEsTUFBQSxJQUFVLGNBQWUsQ0FBQSxTQUFBO0FBS3ZELElBQUEsSUFBSSxDQUFDLE1BQVEsRUFBQTtBQUNYLE1BQU0sTUFBQSxJQUFJLE1BQU0sNkRBQTZELENBQUE7QUFBQTtBQUkvRSxJQUFJLElBQUE7QUFDRixNQUFBLE1BQU0saUJBQWtCLENBQUE7QUFBQSxRQUN0QixPQUFPLGNBQWUsQ0FBQSxLQUFBO0FBQUEsUUFDdEIsUUFBVSxFQUFBLGdCQUFBO0FBQUEsUUFDVixTQUFXLEVBQUEsaUJBQUE7QUFBQSxRQUNYLFFBQVUsRUFBQSxnQkFBQTtBQUFBLFFBQ1YsUUFBVSxFQUFBLGdCQUFBO0FBQUEsUUFDVixnQkFBZ0IsY0FBZSxDQUFBLGNBQUE7QUFBQSxRQUMvQixpQkFBaUIsY0FBZSxDQUFBLGVBQUE7QUFBQSxRQUNoQyxnQkFBZ0IsY0FBZSxDQUFBLGNBQUE7QUFBQSxRQUMvQixnQkFBZ0IsY0FBZSxDQUFBLGNBQUE7QUFBQSxRQUMvQixNQUFBO0FBQUEsUUFDQSxTQUFXLEVBQUEsTUFBQTtBQUFBLFFBQ1gsV0FBVyxjQUFlLENBQUEsU0FBQTtBQUFBLFFBQzFCLFFBQVEsY0FBZSxDQUFBLE1BQUE7QUFBQSxRQUN2QixTQUFTLGNBQWUsQ0FBQTtBQUFBLE9BQ3pCLENBQUE7QUFBQSxhQUNNLHdCQUEwQixFQUFBO0FBRWpDLE1BQU0sTUFBQSxLQUFBLEdBQVEsb0NBQW9DLEtBQzlDLEdBQUEsd0JBQUEsR0FDQSxJQUFJLEtBQU0sQ0FBQSxNQUFBLENBQU8sd0JBQXdCLENBQUMsQ0FBQTtBQUc5QyxNQUFBLE1BQU0sYUFBYSxXQUFZLENBQUE7QUFBQSxRQUM3QixLQUFBO0FBQUEsUUFDQSxRQUFVLEVBQUEsS0FBQTtBQUFBLFFBQ1YsUUFBUSxjQUFlLENBQUEsTUFBQTtBQUFBLFFBQ3ZCLGdCQUFnQixjQUFlLENBQUEsY0FBQTtBQUFBLFFBQy9CLE9BQUEsRUFBUyxDQUF5QyxzQ0FBQSxFQUFBLGNBQUEsQ0FBZSxLQUFLLENBQUE7QUFBQSxPQUN2RSxDQUFBO0FBR0EsTUFBQSxJQUFJLFVBQVksRUFBQTtBQUNkLFFBQU0sTUFBQTtBQUFBLFVBQ0osSUFBTSxFQUFBLE9BQUE7QUFBQSxVQUNOLEtBQU8sRUFBQSxVQUFBO0FBQUEsVUFDUCxPQUFTLEVBQUE7QUFBQSxZQUNQLE9BQVMsRUFBQSxjQUFBO0FBQUEsWUFDVCxXQUFhLEVBQUEsa0JBQUE7QUFBQSxZQUNiLElBQU0sRUFBQTtBQUFBO0FBQ1IsU0FDRjtBQUNBLFFBQUE7QUFBQTtBQUtGLE1BQUEsY0FBQSxDQUFlLE1BQVEsRUFBQSxJQUFBO0FBQUEsUUFDckIsQ0FBNkQsMERBQUEsRUFBQSxjQUFBLENBQWUsS0FBSyxDQUFBLG9DQUFBLEVBQXVDLE1BQU0sT0FBTyxDQUFBO0FBQUEsT0FDdkk7QUFHQSxNQUFBLE1BQU0sMkJBQThCLEdBQUE7QUFBQSxRQUNsQyxJQUFBLEVBQU0sQ0FBeUMsV0FBMEIsS0FBQTtBQUV2RSxVQUFBLE1BQU0sV0FBYyxHQUFBLENBQUEsMkpBQUEsQ0FBQTtBQUNwQixVQUFBLFdBQUEsQ0FBWSxNQUFNLFdBQVcsQ0FBQTtBQUM3QixVQUFBLFdBQUEsQ0FBWSxHQUFJLEVBQUE7QUFDaEIsVUFBTyxPQUFBLFdBQUE7QUFBQSxTQUNUO0FBQUEsUUFDQSxPQUFPLE1BQU07QUFBQTtBQUViLE9BQ0Y7QUFHQSxNQUFBLE1BQU0scUJBQXdCLEdBQUE7QUFBQSxRQUM1QixJQUFBLEVBQU0sQ0FBeUMsV0FBMEIsS0FBQTtBQUV2RSxVQUFBLFdBQUEsQ0FBWSxHQUFJLEVBQUE7QUFDaEIsVUFBTyxPQUFBLFdBQUE7QUFBQSxTQUNUO0FBQUEsUUFDQSxPQUFPLE1BQU07QUFBQTtBQUViLE9BQ0Y7QUFHQSxNQUFNLE1BQUE7QUFBQSxRQUNKLElBQU0sRUFBQSxNQUFBO0FBQUEsUUFDTixNQUFRLEVBQUEsS0FBQTtBQUFBLFFBQ1IsSUFBTSxFQUFBLDJCQUFBO0FBQUEsUUFDTixHQUFLLEVBQUEscUJBQUE7QUFBQSxRQUNMLE9BQVMsRUFBQTtBQUFBLFVBQ1AsT0FBUyxFQUFBLGNBQUE7QUFBQSxVQUNULFdBQWEsRUFBQSxrQkFBQTtBQUFBLFVBQ2IsSUFBTSxFQUFBO0FBQUE7QUFDUixPQUNGO0FBQ0EsTUFBQTtBQUFBO0FBS0gsSUFBQSxNQUFNLGlCQUFvQixHQUFBO0FBQUEsTUFDeEIsR0FBRyxjQUFBO0FBQUE7QUFBQSxNQUVILFFBQVUsRUFBQSxnQkFBQTtBQUFBLE1BQ1YsU0FBVyxFQUFBLGlCQUFBO0FBQUEsTUFDWCxRQUFVLEVBQUEsZ0JBQUE7QUFBQSxNQUNWLFFBQVUsRUFBQTtBQUFBLEtBQ1o7QUFFQSxJQUFBLElBQUksZUFBZSxPQUFTLEVBQUE7QUFDMUIsTUFBQSxjQUFBLENBQWUsTUFBUSxFQUFBLElBQUE7QUFBQSxRQUNyQixDQUFtRSxnRUFBQSxFQUFBLElBQUEsQ0FBSyxTQUFVLENBQUEsY0FBQSxDQUFlLDJCQUEyQixDQUFDLENBQUE7QUFBQSxPQUMvSDtBQUNBLE1BQUEsY0FBQSxDQUFlLE1BQVEsRUFBQSxJQUFBO0FBQUEsUUFDckIsQ0FBc0UsbUVBQUEsRUFBQSxJQUFBLENBQUssU0FBVSxDQUFBLGlCQUFBLENBQWtCLDJCQUEyQixDQUFDLENBQUE7QUFBQSxPQUNySTtBQUNBLE1BQUEsY0FBQSxDQUFlLE1BQVEsRUFBQSxJQUFBO0FBQUEsUUFDckIsQ0FBQSwyREFBQSxFQUE4RCxpQkFBa0IsQ0FBQSxRQUFRLENBQWUsWUFBQSxFQUFBLGlCQUFBLENBQWtCLFNBQVMsQ0FBQSxXQUFBLEVBQWMsaUJBQWtCLENBQUEsUUFBUSxDQUFjLFdBQUEsRUFBQSxpQkFBQSxDQUFrQixRQUFRLENBQUE7QUFBQSxPQUNwTjtBQUFBO0FBT0YsSUFBTSxNQUFBLFFBQUEsR0FBVyxlQUFlLEVBQU0sSUFBQSxDQUFBLEVBQUcsZUFBZSxLQUFLLENBQUEsQ0FBQSxFQUFJLEtBQUssR0FBSSxFQUFDLElBQUksSUFBSyxDQUFBLE1BQUEsR0FBUyxRQUFTLENBQUEsRUFBRSxFQUFFLFNBQVUsQ0FBQSxDQUFBLEVBQUcsRUFBRSxDQUFDLENBQUEsQ0FBQTtBQUUxSCxJQUFBLE1BQU0seUJBQXlCLGVBQWdCLENBQUE7QUFBQSxNQUM3QyxHQUFHLGlCQUFBO0FBQUEsTUFDSCxFQUFJLEVBQUEsQ0FBQSxFQUFHLGNBQWUsQ0FBQSxLQUFLLGFBQWEsUUFBUSxDQUFBLENBQUE7QUFBQSxNQUNoRCxVQUFBLEVBQVksZUFBZSxVQUFjLElBQUEsR0FBQTtBQUFBLE1BQ3pDLFdBQVcsY0FBZSxDQUFBLFNBQUE7QUFBQTtBQUFBLE1BRTFCLFFBQVUsRUFBQSxFQUFBO0FBQUE7QUFBQSxNQUNWLFFBQUEsRUFBVSxrQkFBa0IsUUFBWSxJQUFBLEVBQUE7QUFBQTtBQUFBLE1BQ3hDLEdBQUEsRUFBSyxrQkFBa0IsR0FBTyxJQUFBLEVBQUE7QUFBQTtBQUFBLE1BQzlCLFNBQUEsRUFBVyxpQkFBa0IsQ0FBQSxTQUFBLElBQWEsRUFBQztBQUFBO0FBQUEsTUFDM0MsT0FBUyxFQUFBO0FBQUEsS0FDVixDQUFBO0FBR0QsSUFBQSxNQUFNLHFCQUFxQixlQUFnQixDQUFBO0FBQUEsTUFDekMsR0FBRyxpQkFBQTtBQUFBLE1BQ0gsRUFBSSxFQUFBLENBQUEsRUFBRyxjQUFlLENBQUEsS0FBSyxTQUFTLFFBQVEsQ0FBQSxDQUFBO0FBQUEsTUFDNUMsVUFBQSxFQUFZLGVBQWUsVUFBYyxJQUFBLEdBQUE7QUFBQSxNQUN6QyxXQUFXLGNBQWUsQ0FBQSxTQUFBO0FBQUE7QUFBQTtBQUFBLE1BRzFCLFFBQVUsRUFBQSxnQkFBQTtBQUFBLE1BQ1YsUUFBQSxFQUFVLGtCQUFrQixRQUFZLElBQUEsRUFBQTtBQUFBO0FBQUEsTUFDeEMsR0FBQSxFQUFLLGtCQUFrQixHQUFPLElBQUEsRUFBQTtBQUFBO0FBQUEsTUFDOUIsU0FBQSxFQUFXLGlCQUFrQixDQUFBLFNBQUEsSUFBYSxFQUFDO0FBQUE7QUFBQTtBQUFBLE1BRTNDLHVCQUF1QixzQkFBdUIsQ0FBQSxFQUFBO0FBQUEsTUFDOUMsT0FBUyxFQUFBO0FBQUEsS0FDVixDQUFBO0FBR0QsSUFBb0IsaUJBQUEsR0FBQSxzQkFBQTtBQUNwQixJQUFnQixhQUFBLEdBQUEsa0JBQUE7QUFNaEIsSUFBQSxJQUFJLGVBQWUsT0FBUyxFQUFBO0FBQzFCLE1BQUEsY0FBQSxDQUFlLE1BQVEsRUFBQSxJQUFBO0FBQUEsUUFDckIsQ0FBd0YscUZBQUEsRUFBQSxJQUFBLENBQUssU0FBVSxDQUFBLGlCQUFBLENBQWtCLDJCQUEyQixDQUFDLENBQUE7QUFBQSxPQUN2SjtBQUFBO0FBR0YsSUFBQSxNQUFNLHNCQUFzQixxQkFBc0IsQ0FBQTtBQUFBLE1BQ2hELEdBQUcsaUJBQUE7QUFBQSxNQUNILFdBQUEsRUFBYSxlQUFlLFdBQWUsSUFBQSxJQUFBO0FBQUEsTUFDM0MsT0FBTyxjQUFlLENBQUEsS0FBQTtBQUFBLE1BQ3RCLFFBQVEsY0FBZSxDQUFBLE1BQUE7QUFBQSxNQUN2QixTQUFTLGNBQWUsQ0FBQSxPQUFBO0FBQUEsTUFDeEIsV0FBVyxrQkFBbUIsQ0FBQTtBQUFBLEtBQy9CLENBQUE7QUFFRCxJQUFjLFdBQUEsR0FBQTtBQUFBLE1BQ1osVUFBWSxFQUFBLG1CQUFBO0FBQUEsTUFDWixPQUFPLE1BQU07QUFDWCxRQUFBLG1CQUFBLENBQW9CLEtBQU0sRUFBQTtBQUFBO0FBQzVCLEtBQ0Y7QUFHQSxJQUFBLE1BQU0sZ0JBQW1CLEdBQUE7QUFBQSxNQUN2QixJQUFBLEVBQU0sQ0FBeUMsV0FBMEIsS0FBQTtBQUN2RSxRQUFBLE1BQU0sZ0JBQWdCLG1CQUFvQixFQUFBO0FBQzFDLFFBQWMsYUFBQSxDQUFBLFNBQUEsR0FBWSxZQUFZLEdBQUksRUFBQTtBQUcxQyxRQUFBLE1BQU0sZ0JBQWdCLGlCQUFrQixDQUFBLFNBQUE7QUFFeEMsUUFBYyxhQUFBLENBQUEsRUFBQSxDQUFHLE1BQVEsRUFBQSxDQUFDLEtBQWtCLEtBQUE7QUFDMUMsVUFBYyxhQUFBLENBQUEsTUFBQSxFQUFBO0FBQ2QsVUFBQSxhQUFBLENBQWMsU0FBUyxLQUFNLENBQUEsTUFBQTtBQUFBLFNBQzlCLENBQUE7QUFFRCxRQUFjLGFBQUEsQ0FBQSxFQUFBLENBQUcsT0FBTyxNQUFNO0FBQzVCLFVBQUEsYUFBQSxDQUFjLFFBQVcsR0FBQSxXQUFBLENBQVksR0FBSSxFQUFBLEdBQUksYUFBYyxDQUFBLFNBQUE7QUFDM0QsVUFBYyxhQUFBLENBQUEsT0FBQSxHQUFVLFlBQVksR0FBSSxFQUFBO0FBRXhDLFVBQUEsa0JBQUEsQ0FBbUIsYUFBZ0IsR0FBQSxhQUFBO0FBQ25DLFVBQUEsa0JBQUEsQ0FBbUIsU0FBWSxHQUFBLGFBQUEsQ0FBYyxNQUFVLElBQUEsYUFBQSxDQUFjLFFBQVcsR0FBQSxHQUFBLENBQUE7QUFDaEYsVUFBQSxrQkFBQSxDQUFtQixpQkFBaUIsYUFBYyxDQUFBLFFBQUE7QUFDbEQsVUFBbUIsa0JBQUEsQ0FBQSxXQUFBLEdBQWMsUUFBUSxXQUFZLEVBQUE7QUFDckQsVUFBQSxrQkFBQSxDQUFtQixTQUFTLGFBQWMsQ0FBQSxNQUFBO0FBQUEsU0FDM0MsQ0FBQTtBQUVELFFBQUEsYUFBQSxDQUFjLEtBQUssV0FBVyxDQUFBO0FBQzlCLFFBQU8sT0FBQSxXQUFBO0FBQUEsT0FDVDtBQUFBLE1BQ0EsS0FBQSxFQUFPLE1BQU0saUJBQUEsQ0FBa0IsS0FBTTtBQUFBLEtBQ3ZDO0FBRUEsSUFBQSxNQUFNLGlCQUFvQixHQUFBO0FBQUEsTUFDeEIsSUFBQSxFQUFNLENBQXlDLFdBQTBCLEtBQUE7QUFDdkUsUUFBQSxJQUFJLGVBQWUsT0FBUyxFQUFBO0FBQzFCLFVBQUEsY0FBQSxDQUFlLE1BQVEsRUFBQSxJQUFBO0FBQUEsWUFDckIsQ0FBQSxpRUFBQSxFQUFvRSxlQUFlLEtBQUssQ0FBQTtBQUFBLFdBQzFGO0FBQUE7QUFJRixRQUFPLE9BQUEsbUJBQUEsQ0FBb0IsS0FBSyxXQUFXLENBQUE7QUFBQSxPQUM3QztBQUFBLE1BQ0EsT0FBTyxNQUFNO0FBQ1gsUUFBQSxhQUFBLENBQWMsS0FBTSxFQUFBO0FBQ3BCLFFBQUEsSUFBSSxZQUFZLEtBQU8sRUFBQTtBQUNyQixVQUFBLFdBQUEsQ0FBWSxLQUFNLEVBQUE7QUFBQTtBQUNwQixPQUNGO0FBQUEsTUFDQSxFQUFBLEVBQUksQ0FBQyxLQUFBLEVBQWUsUUFBdUMsS0FBQTtBQUV6RCxRQUFBLElBQUksVUFBVSxPQUFTLEVBQUE7QUFFckIsVUFBQSxNQUFNLGFBQWMsbUJBQTRCLENBQUEsVUFBQTtBQUNoRCxVQUFBLElBQUksVUFBYyxJQUFBLE9BQU8sVUFBVyxDQUFBLEVBQUEsS0FBTyxVQUFZLEVBQUE7QUFDckQsWUFBVyxVQUFBLENBQUEsRUFBQSxDQUFHLFNBQVMsUUFBUSxDQUFBO0FBQUE7QUFDakM7QUFFRixRQUFPLE9BQUEsaUJBQUE7QUFBQTtBQUNULEtBQ0Y7QUFPQSxJQUFBLE1BQU0sSUFBSSxPQUFRLENBQUEsQ0FBQSxPQUFBLEtBQVcsVUFBVyxDQUFBLE9BQUEsRUFBUyxHQUFHLENBQUMsQ0FBQTtBQUVyRCxJQUFBLElBQUksV0FBYSxFQUFBO0FBQ2YsTUFBTSxNQUFBLFdBQUE7QUFDTixNQUFBO0FBQUE7QUFHRixJQUFNLE1BQUE7QUFBQSxNQUNKLElBQU0sRUFBQSxTQUFBO0FBQUEsTUFDTixJQUFNLEVBQUEsaUJBQUE7QUFBQSxNQUNOLEdBQUssRUFBQSxnQkFBQTtBQUFBLE1BQ0wsT0FBUyxFQUFBO0FBQUEsUUFDUCxPQUFTLEVBQUEsY0FBQTtBQUFBLFFBQ1QsV0FBYSxFQUFBLGtCQUFBO0FBQUEsUUFDYixJQUFNLEVBQUE7QUFBQTtBQUNSLEtBQ0Y7QUFBQSxXQUNPLEtBQU8sRUFBQTtBQUVkLElBQUksSUFBQTtBQUNGLE1BQUksSUFBQSxpQkFBQSxvQkFBcUMsS0FBTSxFQUFBO0FBQy9DLE1BQUksSUFBQSxhQUFBLGdCQUE2QixLQUFNLEVBQUE7QUFDdkMsTUFBSSxJQUFBLFdBQUEsRUFBYSxLQUFPLEVBQUEsV0FBQSxDQUFZLEtBQU0sRUFBQTtBQUFBLGFBQ25DLFlBQXVCLEVBQUE7QUFDOUIsTUFBQSxjQUFBLENBQWUsTUFBUSxFQUFBLElBQUEsQ0FBSyxDQUF1QyxvQ0FBQSxFQUFBLFlBQVksQ0FBRSxDQUFBLENBQUE7QUFBQTtBQUduRixJQUFBLE1BQU0sYUFBYSxXQUFZLENBQUE7QUFBQSxNQUM3QixLQUFBO0FBQUEsTUFDQSxRQUFRLGNBQWUsQ0FBQSxNQUFBO0FBQUEsTUFFdkIsZ0JBQWdCLGNBQWUsQ0FBQTtBQUFBLEtBQ2hDLENBQUE7QUFFRCxJQUFBLElBQUksY0FBYyxJQUFNLEVBQUE7QUFDdEIsTUFBTSxNQUFBO0FBQUEsUUFDSixJQUFNLEVBQUEsT0FBQTtBQUFBLFFBQ04sS0FBTyxFQUFBLFVBQUE7QUFBQSxRQUNQLE9BQVMsRUFBQTtBQUFBLFVBQ1AsT0FBUyxFQUFBLGNBQUE7QUFBQSxVQUNULFdBQWEsRUFBQSxrQkFBQTtBQUFBLFVBQ2IsSUFBTSxFQUFBO0FBQUE7QUFDUixPQUNGO0FBQUEsS0FDSyxNQUFBO0FBS0wsTUFBQSxNQUFNLG9CQUFvQixlQUFnQixDQUFBO0FBQUEsUUFDeEMsR0FBRyxjQUFBO0FBQUEsUUFDSCxHQUFBLEVBQUssQ0FBRyxFQUFBLGNBQUEsQ0FBZSxHQUFHLENBQUEsQ0FBQTtBQUFBLFFBQzFCLEtBQUEsRUFBTyxDQUFHLEVBQUEsY0FBQSxDQUFlLEtBQUssQ0FBQSxDQUFBO0FBQUEsUUFDOUIsUUFBVSxFQUFBLGNBQUEsQ0FBZSxRQUFZLG9CQUFBLElBQUksR0FBSSxFQUFBO0FBQUEsUUFDN0MsU0FBVyxFQUFBLGNBQUEsQ0FBZSxTQUFhLG9CQUFBLElBQUksR0FBSSxFQUFBO0FBQUEsUUFDL0MsSUFBSSxDQUFHLEVBQUEsY0FBQSxDQUFlLEtBQUssQ0FBYSxVQUFBLEVBQUEsSUFBQSxDQUFLLEtBQUssQ0FBQSxDQUFBO0FBQUEsUUFDbEQsVUFBQSxFQUFZLGVBQWUsVUFBYyxJQUFBLEdBQUE7QUFBQSxRQUN6QyxXQUFXLGNBQWUsQ0FBQSxTQUFBO0FBQUE7QUFBQSxRQUUxQixRQUFVLEVBQUEsRUFBQTtBQUFBO0FBQUEsUUFDVixXQUFXO0FBQUM7QUFBQSxPQUNiLENBQUE7QUFHRCxNQUFBLE1BQU0scUJBQXFCLHFCQUFzQixDQUFBO0FBQUEsUUFDL0MsSUFBSSxjQUFlLENBQUEsRUFBQTtBQUFBLFFBQ25CLE9BQU8sY0FBZSxDQUFBLEtBQUE7QUFBQSxRQUN0QixLQUFLLGNBQWUsQ0FBQSxHQUFBO0FBQUEsUUFDcEIsZ0JBQWdCLGNBQWUsQ0FBQSxjQUFBO0FBQUEsUUFDL0IsZ0JBQWdCLGNBQWUsQ0FBQSxjQUFBO0FBQUEsUUFDL0IsZUFBZSxjQUFlLENBQUEsYUFBQTtBQUFBLFFBQzlCLGFBQWEsY0FBZSxDQUFBLFdBQUE7QUFBQSxRQUM1QixnQkFBZ0IsY0FBZSxDQUFBLGNBQUE7QUFBQSxRQUMvQixTQUFTLGNBQWUsQ0FBQSxPQUFBO0FBQUEsUUFDeEIsUUFBUSxjQUFlLENBQUEsTUFBQTtBQUFBLFFBQ3ZCLFFBQVEsY0FBZSxDQUFBLE1BQUE7QUFBQSxRQUN2QixhQUFhLGNBQWUsQ0FBQSxXQUFBO0FBQUEsUUFDNUIsNkJBQTZCLGNBQWUsQ0FBQSwyQkFBQTtBQUFBLFFBQzVDLFdBQVcsY0FBZSxDQUFBLFNBQUE7QUFBQSxRQUMxQixPQUFPLGNBQWUsQ0FBQTtBQUFBLE9BQ3ZCLENBQUE7QUFHRCxNQUFBLE1BQU0sMkJBQThCLEdBQUE7QUFBQSxRQUNsQyxJQUFBLEVBQU0sQ0FBeUMsV0FBMEIsS0FBQTtBQUV2RSxVQUFPLE9BQUEsa0JBQUEsQ0FBbUIsS0FBSyxXQUFXLENBQUE7QUFBQSxTQUM1QztBQUFBLFFBQ0EsT0FBTyxNQUFNO0FBRVgsVUFBQSxpQkFBQSxDQUFrQixLQUFNLEVBQUE7QUFBQTtBQUMxQixPQUNGO0FBR0EsTUFBQSxNQUFNLHFCQUF3QixHQUFBO0FBQUEsUUFDNUIsSUFBQSxFQUFNLENBQXlDLFdBQTBCLEtBQUE7QUFFdkUsVUFBQSxXQUFBLENBQVksR0FBSSxFQUFBO0FBQ2hCLFVBQU8sT0FBQSxXQUFBO0FBQUEsU0FDVDtBQUFBLFFBQ0EsT0FBTyxNQUFNO0FBQUE7QUFFYixPQUNGO0FBRUEsTUFBTSxNQUFBO0FBQUEsUUFDSixJQUFNLEVBQUEsTUFBQTtBQUFBLFFBQ04sTUFBUSxFQUFBLEtBQUE7QUFBQSxRQUNSLElBQU0sRUFBQSwyQkFBQTtBQUFBLFFBQ04sR0FBSyxFQUFBLHFCQUFBO0FBQUEsUUFDTCxPQUFTLEVBQUE7QUFBQSxVQUNQLE9BQVMsRUFBQSxjQUFBO0FBQUEsVUFDVCxXQUFhLEVBQUEsa0JBQUE7QUFBQSxVQUNiLElBQU0sRUFBQTtBQUFBO0FBQ1IsT0FDRjtBQUFBO0FBQ0Y7QUFFSjs7OzsifQ==