vite-plugin-react-server 1.4.2 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -313
- package/dist/package.json +123 -13
- package/dist/plugin/bundle/deferredStaticGeneration.js +14 -39
- package/dist/plugin/bundle/manifests.js +30 -48
- package/dist/plugin/config/autoDiscover/resolveAutoDiscover.d.ts.map +1 -1
- package/dist/plugin/config/autoDiscover/resolveAutoDiscover.js +4 -1
- package/dist/plugin/config/envPrefixFromConfig.js +12 -7
- package/dist/plugin/config/getCondition.d.ts.map +1 -1
- package/dist/plugin/config/getCondition.js +7 -5
- package/dist/plugin/dev-server/virtualRscHmrPlugin.js +23 -23
- package/dist/plugin/environments/createBuildEventPlugin.js +88 -98
- package/dist/plugin/environments/createEnvironmentPlugin.js +222 -250
- package/dist/plugin/helpers/createRscRenderHelpers.js +33 -34
- package/dist/plugin/helpers/createSharedLoader.d.ts.map +1 -1
- package/dist/plugin/helpers/createSharedLoader.js +4 -2
- package/dist/plugin/helpers/headlessStreamReuseHandler.js +30 -22
- package/dist/plugin/helpers/headlessStreamState.js +15 -28
- package/dist/plugin/helpers/resolveComponent.d.ts.map +1 -1
- package/dist/plugin/helpers/resolveComponent.js +4 -2
- package/dist/plugin/index.client.d.ts +5 -0
- package/dist/plugin/index.client.d.ts.map +1 -0
- package/dist/plugin/index.client.js +4 -0
- package/dist/plugin/index.d.ts +4 -3
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +10 -5
- package/dist/plugin/index.server.d.ts +5 -0
- package/dist/plugin/index.server.d.ts.map +1 -0
- package/dist/plugin/index.server.js +4 -0
- package/dist/plugin/metrics/createWorkerStartupMetrics.js +31 -13
- package/dist/plugin/orchestrator/createPluginOrchestrator.client.js +41 -38
- package/dist/plugin/orchestrator/createPluginOrchestrator.server.js +43 -46
- package/dist/plugin/plugin.client.js +2 -2
- package/dist/plugin/plugin.server.js +2 -2
- package/dist/plugin/react-static/createBuildLoader.client.js +12 -6
- package/dist/plugin/react-static/createBuildLoader.server.js +255 -235
- package/dist/plugin/react-static/plugin.client.js +684 -770
- package/dist/plugin/react-static/plugin.server.js +517 -603
- package/dist/plugin/react-static/processCssFilesForPages.js +103 -88
- package/dist/plugin/react-static/renderPage.client.js +455 -529
- package/dist/plugin/react-static/renderPage.server.js +485 -508
- package/dist/plugin/react-static/renderPagesBatched.js +277 -275
- package/dist/plugin/react-static/rscToHtmlStream.client.js +48 -29
- package/dist/plugin/react-static/rscToHtmlStream.server.js +62 -37
- package/dist/plugin/react-static/temporaryReferences.server.js +11 -2
- package/dist/plugin/stream/createMainThreadHandlers.js +40 -31
- package/dist/plugin/stream/renderRscStream.server.d.ts.map +1 -1
- package/dist/plugin/stream/renderRscStream.server.js +127 -144
- package/dist/plugin/transformer/createTransformerPlugin.js +226 -265
- package/dist/plugin/utils/checkReactVersion.d.ts +7 -0
- package/dist/plugin/utils/checkReactVersion.d.ts.map +1 -0
- package/dist/plugin/utils/checkReactVersion.js +23 -0
- package/dist/plugin/utils/envUrls.node.js +12 -11
- package/dist/plugin/vendor/vendor-alias.js +84 -114
- package/dist/plugin/vendor/vendor.client.d.ts.map +1 -1
- package/dist/plugin/vendor/vendor.client.js +1 -3
- package/dist/plugin/worker/rsc/handleRscRender.d.ts.map +1 -1
- package/dist/plugin/worker/rsc/handleRscRender.js +3 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +123 -13
- package/plugin/config/autoDiscover/resolveAutoDiscover.ts +4 -0
- package/plugin/config/getCondition.ts +6 -4
- package/plugin/helpers/createSharedLoader.ts +6 -1
- package/plugin/helpers/resolveComponent.ts +6 -1
- package/plugin/index.client.ts +4 -0
- package/plugin/index.server.ts +4 -0
- package/plugin/index.ts +12 -5
- package/plugin/plugin.client.ts +1 -1
- package/plugin/plugin.server.ts +1 -1
- package/plugin/stream/renderRscStream.server.ts +3 -0
- package/plugin/utils/checkReactVersion.ts +28 -0
- package/plugin/vendor/vendor.client.ts +0 -2
- package/plugin/worker/rsc/handleRscRender.ts +2 -0
- package/scripts/generate-toc.mjs +27 -294
|
@@ -1,555 +1,481 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
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
|
|
42
|
-
import { routeToURL } from
|
|
43
|
-
import { handleError } from
|
|
44
|
-
import { assertNonReactServer } from
|
|
45
|
-
import { createRscStream } from
|
|
46
|
-
import { resolveComponents } from
|
|
47
|
-
import { join } from
|
|
48
|
-
import { createStreamMetrics } from
|
|
49
|
-
import { performance } from
|
|
50
|
-
import { createRscToHtmlStream } from
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
61
|
-
|
|
153
|
+
handlerOptions.logger?.info(
|
|
154
|
+
`[renderPage.client] Client-side rendering for route: ${handlerOptions.route}`
|
|
155
|
+
);
|
|
62
156
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
|
|
356
|
+
handlerOptions.logger?.info(
|
|
357
|
+
`[renderPage.client] Piping HTML stream to destination for route: ${handlerOptions.route}`
|
|
358
|
+
);
|
|
368
359
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
466
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
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
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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+IHtcbiAgICAgICAgICAvLyBObyBSU0MgY29udGVudCBmb3Igc2tpcHBlZCByb3V0ZXNcbiAgICAgICAgICBkZXN0aW5hdGlvbi5lbmQoKTtcbiAgICAgICAgICByZXR1cm4gZGVzdGluYXRpb247XG4gICAgICAgIH0sXG4gICAgICAgIGFib3J0OiAoKSA9PiB7XG4gICAgICAgICAgLy8gTm8gY2xlYW51cCBuZWVkZWRcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgICBcbiAgICAgIHlpZWxkIHtcbiAgICAgICAgdHlwZTogXCJza2lwXCIsXG4gICAgICAgIHJlYXNvbjogZXJyb3IsXG4gICAgICAgIGh0bWw6IGNsaWVudE9ubHlIdG1sU3RyZWFtV3JhcHBlcixcbiAgICAgICAgcnNjOiBlbXB0eVJzY1N0cmVhbVdyYXBwZXIsXG4gICAgICAgIG1ldHJpY3M6IHtcbiAgICAgICAgICByc2NGdWxsOiByc2NGdWxsTWV0cmljcyxcbiAgICAgICAgICByc2NIZWFkbGVzczogcnNjSGVhZGxlc3NNZXRyaWNzLFxuICAgICAgICAgIGh0bWw6IGh0bWxNZXRyaWNzLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICB9XG4gIH1cbn07ICJdLCJuYW1lcyI6WyJtYW5pZmVzdCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQTJEQSxvQkFBcUIsRUFBQTtBQVFSLE1BQUEsVUFBQSxHQUEyQixnQkFBZ0IsaUJBQUEsQ0FDdEQsY0FDQSxFQUFBO0FBQ0EsRUFBQSxJQUFJLGVBQWUsT0FBUyxFQUFBO0FBQzFCLElBQUEsY0FBQSxD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
|